file tree lazy update

This commit is contained in:
Penwing 2024-01-31 11:04:36 +01:00
parent df18044c64
commit 28bc4b4823
3 changed files with 143 additions and 17 deletions

View file

@ -190,30 +190,35 @@ impl Calcifer {
&mut self, &mut self,
ui: &mut egui::Ui, ui: &mut egui::Ui,
file: &panels::FileEntry, file: &panels::FileEntry,
depth: isize,
n_files: &mut usize, n_files: &mut usize,
) { ) -> bool {
*n_files += 1; *n_files += 1;
if let Some(folder_content) = &file.folder_content { if let Some(folder_content) = &file.folder_content {
let mut check_for_update: bool = false;
let collapsing_response = egui::CollapsingHeader::new(file.name.clone()) let collapsing_response = egui::CollapsingHeader::new(file.name.clone())
.default_open(depth > 0) .default_open(self.tree_dir_opened.contains(&file.name))
.show(ui, |ui| { .show(ui, |ui| {
if !self.tree_dir_opened.contains(&file.name) { if !self.tree_dir_opened.contains(&file.name) {
return; return;
} }
for deeper_file in folder_content { for deeper_file in folder_content {
self.list_files(ui, deeper_file, depth - 1, n_files); if self.list_files(ui, deeper_file, n_files) {
check_for_update = true;
}
} }
}); });
if collapsing_response.fully_closed() { if collapsing_response.fully_closed() {
self.tree_dir_opened.retain(|s| s != &file.name); self.tree_dir_opened.retain(|s| s != &file.name);
} else if !self.tree_dir_opened.contains(&file.name) { } else if !self.tree_dir_opened.contains(&file.name) {
self.tree_dir_opened.push(file.name.clone()); self.tree_dir_opened.push(file.name.clone());
return !file.content_checked;
} }
return check_for_update;
} else if ui.button(&file.name).clicked() { } else if ui.button(&file.name).clicked() {
self.open_file(Some(&file.path)); self.open_file(Some(&file.path));
} }
return false;
} }
} }

View file

@ -54,20 +54,28 @@ impl Calcifer {
if !self.tree_visible { if !self.tree_visible {
return; return;
} }
if self.file_tree.is_none() {
self.file_tree = Some(panels::generate_folder_entry(self.home.as_path()));
}
let mut n_files: usize = 0;
egui::SidePanel::left("file_tree_panel").show(ctx, |ui| { egui::SidePanel::left("file_tree_panel").show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Bookshelf "); ui.label("Bookshelf ");
if ui.add(egui::Button::new("📖")).clicked() {
self.file_tree = panels::generate_file_tree(self.home.as_path(), 7);
}
}); });
ui.separator(); ui.separator();
let mut n_files: usize = 0; egui::ScrollArea::vertical().show(ui, |ui| {
if let Some(file_tree) = self.file_tree.clone() { if let Some(file_tree) = self.file_tree.clone() {
self.list_files(ui, &file_tree, 1, &mut n_files); let update_requested = self.list_files(ui, &file_tree, &mut n_files);
if update_requested {
self.file_tree = Some(panels::update_file_tree(
file_tree,
self.tree_dir_opened.clone(),
));
}
} else { } else {
ui.label("No book on the Bookshelf"); ui.label("No book on the Bookshelf");
} }
});
ui.separator(); ui.separator();
ui.label(format!("{} files displayed", n_files)); ui.label(format!("{} files displayed", n_files));
}); });

View file

@ -12,7 +12,7 @@ pub struct FileEntry {
pub name: String, pub name: String,
pub path: PathBuf, pub path: PathBuf,
pub folder_content: Option<Vec<FileEntry>>, pub folder_content: Option<Vec<FileEntry>>,
pub folder_open: bool, pub content_checked: bool,
} }
impl FileEntry { impl FileEntry {
@ -21,7 +21,15 @@ impl FileEntry {
name, name,
path, path,
folder_content: None, folder_content: None,
folder_open: false, content_checked: true,
}
}
pub fn end_of_branch(name: String, path: PathBuf) -> Self {
Self {
name,
path,
folder_content: Some(vec![]),
content_checked: false,
} }
} }
} }
@ -45,10 +53,14 @@ pub fn generate_file_tree(path: &Path, depth: isize) -> Option<FileEntry> {
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
if !path.is_dir() || depth < 0 { if !path.is_dir() {
return Some(FileEntry::new_entry(name, path.to_path_buf())); return Some(FileEntry::new_entry(name, path.to_path_buf()));
} }
if depth < 0 {
return Some(FileEntry::end_of_branch(name, path.to_path_buf()));
}
match fs::read_dir(path) { match fs::read_dir(path) {
Err(err) => Some(FileEntry::new_entry( Err(err) => Some(FileEntry::new_entry(
format!("Error reading directory: {}", err), format!("Error reading directory: {}", err),
@ -92,12 +104,113 @@ pub fn generate_file_tree(path: &Path, depth: isize) -> Option<FileEntry> {
name, name,
path: path.to_path_buf(), path: path.to_path_buf(),
folder_content: Some(folder_content), folder_content: Some(folder_content),
folder_open: false, content_checked: true,
}) })
} }
} }
} }
pub fn update_file_tree(file: FileEntry, opened_dirs: Vec<String>) -> FileEntry {
if opened_dirs.contains(&file.name) {
if let Some(folder_content) = &file.folder_content {
if !file.content_checked {
return generate_folder_entry(&file.path);
}
let updated_content: Vec<FileEntry> = folder_content
.iter()
.map(|entry| update_file_tree(entry.clone(), opened_dirs.clone()))
.collect();
return FileEntry {
name: file.name,
path: file.path,
folder_content: Some(updated_content),
content_checked: true,
};
} else {
return file;
}
} else {
return file;
}
}
pub fn generate_folder_entry(path: &Path) -> FileEntry {
if let Some(file_name) = path.file_name() {
let name = file_name.to_string_lossy().into_owned();
match fs::read_dir(path) {
Err(err) => FileEntry::new_entry(
format!("Error reading directory: {}", err),
path.to_path_buf(),
),
Ok(entries) => {
let mut paths: Vec<Result<fs::DirEntry, io::Error>> = entries
.map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e)))
.collect();
paths.sort_by(|a, b| match (a, b) {
(Ok(entry_a), Ok(entry_b)) => sort_directories_first(entry_a, entry_b),
(Err(_), Ok(_)) => std::cmp::Ordering::Greater,
(Ok(_), Err(_)) => std::cmp::Ordering::Less,
(Err(_), Err(_)) => std::cmp::Ordering::Equal,
});
let mut folder_content = Vec::new();
for result in paths {
match result {
Ok(entry) => {
if let Some(file) = generate_entry(&entry.path()) {
folder_content.push(file);
}
}
Err(err) => {
folder_content.push(FileEntry::new_entry(
format!("Error reading entry: {}", err),
path.to_path_buf(),
));
}
}
}
FileEntry {
name,
path: path.to_path_buf(),
folder_content: Some(folder_content),
content_checked: true,
}
}
}
} else {
FileEntry::new_entry(format!("Error reading directory name"), path.to_path_buf())
}
}
fn generate_entry(path: &Path) -> Option<FileEntry> {
if let Some(file_name) = path.file_name() {
if file_name.to_string_lossy().starts_with('.') {
return None;
}
let extension = path.extension().and_then(|ext| ext.to_str());
if !ALLOWED_FILE_EXTENSIONS.contains(&extension.unwrap_or_default()) {
return None;
}
} else {
return None;
}
let name = path
.file_name()
.unwrap_or_else(|| OsStr::new(""))
.to_string_lossy()
.into_owned();
if !path.is_dir() {
return Some(FileEntry::new_entry(name, path.to_path_buf()));
}
return Some(FileEntry::end_of_branch(name, path.to_path_buf()));
}
fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> Ordering { fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> Ordering {
let a_is_dir = a.path().is_dir(); let a_is_dir = a.path().is_dir();
let b_is_dir = b.path().is_dir(); let b_is_dir = b.path().is_dir();