file tree lazy update
This commit is contained in:
parent
df18044c64
commit
28bc4b4823
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue