Skip to content

Instantly share code, notes, and snippets.

@rminderhoud
Created November 10, 2022 20:41
Show Gist options
  • Save rminderhoud/938ecebc4a49b75c93ffb3c345f84c52 to your computer and use it in GitHub Desktop.
Save rminderhoud/938ecebc4a49b75c93ffb3c345f84c52 to your computer and use it in GitHub Desktop.
Basic tree
// Some code that was written but never used for a project, keeping here as a reference for basic tree implementaiton in rust
#[derive(Debug, Default)]
struct FileTreeNode {
id: usize,
path: PathBuf,
parent: usize,
children: Vec<usize>,
}
impl FileTreeNode {
fn path_str(&self) -> String {
normalize_path_str(self.path.to_string_lossy())
}
fn is_leaf(&self) -> bool {
self.children.is_empty()
}
}
/// Tree of nodes representing files and directories in a root directory
#[derive(Debug, Default)]
struct FileTree {
root_dir: PathBuf,
nodes: Vec<FileTreeNode>,
file_node_ids: Vec<usize>,
}
impl FileTree {
fn new(root: &Path) -> FileTree {
FileTree {
root_dir: root.into(),
..Default::default()
}
}
fn new_node(&mut self, parent: usize, p: &Path) -> usize {
let id = self.nodes.len();
let node = FileTreeNode {
id,
path: p.to_path_buf(),
parent,
..Default::default()
};
self.nodes.push(node);
return id;
}
fn valid_node(&self, id: usize) -> bool {
id < self.nodes.len()
}
}
/// Recursively build a `FileTree` from the root path
fn build_tree_nodes(tree: &mut FileTree, path: &Path, parent: usize) -> anyhow::Result<usize> {
let node_id = tree.new_node(parent, path);
debug!(
path = path.display().to_string(),
"Reading directory contents"
);
let mut dirs = Vec::new();
let mut files = Vec::new();
// Collect into files and dirs seperately so we can control the order when
// building our tree (insert dirs first)
for entry in fs::read_dir(path)? {
let entry = entry?;
let file_type = entry.file_type()?;
if file_type.is_file() {
files.push(entry);
} else if file_type.is_dir() {
dirs.push(entry);
} else {
continue; // Skip symlinks
}
}
for entry in dirs {
let dir_node_id = build_tree_nodes(tree, &entry.path(), node_id)?;
tree.nodes[node_id].children.push(dir_node_id);
}
for entry in files {
let file_node_id = tree.new_node(node_id, &entry.path());
tree.nodes[node_id].children.push(file_node_id);
tree.file_node_ids.push(file_node_id);
}
debug!(
path = path.display().to_string(),
"Finished reading directory contents"
);
Ok(node_id)
}
fn build_tree(root: &Path) -> anyhow::Result<FileTree> {
let mut tree = FileTree::new(root);
build_tree_nodes(&mut tree, root, 0)?;
Ok(tree)
}
// egui rendering code
fn draw_tree_node(&self, ui: &mut egui::Ui, node_id: usize) {
let node = &self.file_tree.nodes[node_id];
let node_name = node.path_str();
// Node represents a file
if node.is_leaf() {
ui.indent(0, |ui| {
let mut checked = false;
ui.checkbox(&mut checked, node_name);
});
return;
}
let id = ui.make_persistent_id(&node_name);
egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, false)
.show_header(ui, |ui| {
let mut b = false;
ui.checkbox(&mut b, node_name);
})
.body(|ui| {
for child_node_id in node.children.iter() {
self.draw_tree_node(ui, *child_node_id)
}
});
}
fn draw_tree(&self, ui: &mut egui::Ui) {
if self.file_tree_task_state == TaskState::Running {
ui.add(egui::Spinner::new());
return;
}
if !self.file_tree.valid_node(0) {
debug!("No root node to draw");
return;
}
self.draw_tree_node(ui, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment