文章目录
-
- 目录树
- 目录结构
- UI树
- 主程序
- egui初步教程
- egui基础组件的使用
目录树
egui并没有提供树结构,但实现起来并不复杂,如下图所示

想要实现这个功能可以分
3
3
3步走
目录结构
在目录树中,每个节点都代表一个路径,如想递归式地实现节点结构,那么需要为当前节点,关联其所有子节点,即所有子路径。这种结构可被存储为字典形式,其key和value分别是父路径和子路径列表。为此,实现【FileTreeState】结构,其内部成员【dir_cache】就是这个路径字典,代码如下。
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use eframe::egui;
struct FileTreeState {
dir_cache: HashMap<PathBuf, Vec<PathBuf>>,
}
impl FileTreeState {
fn get_dir_children(&mut self, dir: &Path) -> Vec<PathBuf> {
self.dir_cache
.entry(dir.to_path_buf())
.or_insert_with(|| {
let mut children = Vec::new();
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.filter_map(|e| e.ok()) {
children.push(entry.path());
}
}
children.sort_by(|a, b| {
match (a.is_dir(), b.is_dir()) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => a.file_name()
.unwrap_or_default()
.cmp(b.file_name().unwrap_or_default()),
}});
children
}).clone()
}
}
【dir_cache】中,用PathBuf来表示路径。
【get_dir_children】用于获取父路径的子路径。首先用【entry】添加父路径key,然后用【or_insert_with】插入父路径的所有子路径。生成子路径的过程分为两个步骤,先用read_dir获取所有子路径,并添加到childern列表;在通过sort_by对其进行排序,将文件夹放在前面,文件放在后面。
UI树
egui并没有提供树结构,因此需要自行实现其展开逻辑。而其基本组件,采用【CollapsingHeader】和【label】来实现。即每个目录都是一个可以展开的【CollapsingHeader】,而文件作为叶节点,只需一个label即可,代码如下
fn render_tree_node(ui: &mut egui::Ui, state: &mut FileTreeState, path: &Path) {
let display_name = path
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| path.to_string_lossy().to_string());
if path.is_dir() {
let id = egui::Id::new(path.to_string_lossy().to_string());
egui::CollapsingHeader::new(&display_name)
.id_salt(id)
.default_open(false)
.show(ui, |ui| {
let children = state.get_dir_children(path);
for child_path in children {
render_tree_node(ui, state, &child_path);
}
});
} else {
ui.label(display_name);
}
}
上述代码中,先获取输入路径的最后一截名字,然后判断该路径是文件还是文件夹,如果是文件夹,就递归式地渲染【CollapsingHeader】,否则添加一个label作为叶节点。
主程序
在实现了文件系统的基础结构以及GUI的节点渲染方案后,事实上就已经实现了目录树组件,接下来要做的,就是和其他egui程序一样,将其塞进一个App中,代码如下。
struct FileTreeApp {
state: FileTreeState,
root_path: PathBuf,
}
impl FileTreeApp {
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
Self {
state: FileTreeState {
dir_cache: HashMap::new(),
},
root_path: std::env::current_dir().unwrap_or_default(),
}
}
}
impl eframe::App for FileTreeApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
render_tree_node(ui, &mut self.state, &self.root_path);
});
});
}
}
fn main() -> eframe::Result<()> {
let options = eframe::NativeOptions::default();
eframe::run_native(
"Egui File Tree",
options,
Box::new(|cc| Ok(Box::new(FileTreeApp::new(cc)))),
)
}
在【new】中,将当前所在文件夹设为根目录;在update中,调用了render_tree_node,递归式地渲染文件管理系统。
本文介绍了如何使用Rust的egui库实现一个目录树组件。主要分为三个步骤:1) 构建目录树数据结构,使用HashMap缓存父子路径关系;2) 实现UI渲染逻辑,通过CollapsingHeader和label组件递归显示目录结构;3) 集成到主程序中。核心代码包括路径缓存处理、目录排序以及递归渲染UI节点。最终实现了一个可展开/折叠的图形化目录树,支持显示当前工作目录下的所有文件和子目录。



