From 297f53198fe137d10639b36723a096b735f50a0e Mon Sep 17 00:00:00 2001 From: WanderingPenwing Date: Mon, 22 Jul 2024 15:33:24 +0200 Subject: [PATCH] reworked file tree --- Cargo.toml | 2 +- calcifer.project | 2 +- src/core/app.rs | 16 ++++----- src/core/ui.rs | 49 ++++++++++++++++++++------- src/main.rs | 5 --- src/panels/file_tree.rs | 73 +++++++++++++++++++++++++++-------------- 6 files changed, 97 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0078c20..bfa4841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcifer" -version = "1.3.2" +version = "1.4.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/calcifer.project b/calcifer.project index be32f9f..2c4cb7a 100644 --- a/calcifer.project +++ b/calcifer.project @@ -1 +1 @@ -{"categories":[{"name":"to do","content":[{"name":"bug project","description":"when switching tabs between two project file, if item window is open it crashes","id":1},{"name":"update workflow .yml","description":"make a workflow compiling the calcifer and put the linux in calcifer-{version}\nand the windows in calcifer_windows_{version}\n\nupdate nix\nupdate jiji","id":5},{"name":"open dir in tree ?","description":"// Hello there","id":2},{"name":"fix tab title","description":"// Hello there","id":2}]},{"name":"in progress","content":[{"name":"export copy paste fix","description":"// Hello there","id":1}]},{"name":"done","content":[{"name":"move .project file","description":"// Hello there","id":4},{"name":"move config","description":"config from .calcifer/save.json\nto .config/calcifer/state.json","id":1},{"name":"add id to textarea per tab","description":"to improve undo, make each code area of each tab have a unique id (no more undo into another tab)","id":1},{"name":"file tree id ?","description":"// Hello there","id":1}]},{"name":"+","content":[]}]} \ No newline at end of file +{"categories":[{"name":"to do","content":[{"name":"bug project","description":"when switching tabs between two project file, if item window is open it crashes","id":1},{"name":"update workflow .yml","description":"make a workflow compiling the calcifer and put the linux in calcifer-{version}\nand the windows in calcifer_windows_{version}\n\nupdate nix\nupdate jiji","id":5},{"name":"fix tab title","description":"// Hello there","id":2}]},{"name":"in progress","content":[{"name":"export copy paste fix","description":"// Hello there","id":1}]},{"name":"done","content":[{"name":"move .project file","description":"// Hello there","id":4},{"name":"move config","description":"config from .calcifer/save.json\nto .config/calcifer/state.json","id":1},{"name":"add id to textarea per tab","description":"to improve undo, make each code area of each tab have a unique id (no more undo into another tab)","id":1},{"name":"file tree id ?","description":"// Hello there","id":1},{"name":"open dir in tree ?","description":"// Hello there","id":2}]},{"name":"+","content":[]}]} \ No newline at end of file diff --git a/src/core/app.rs b/src/core/app.rs index 549a5bc..e4b7f55 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -209,13 +209,13 @@ impl Calcifer { if let Some(folder_content) = &file.folder_content { let mut check_for_update: bool = false; - let file_id = panels::get_file_id(&file); + let file_path_id = panels::get_file_path_id(&file.path); - let collapsing_response = egui::CollapsingHeader::new(&file.name.clone()) - .id_source(&file_id) - .default_open(self.tree_dir_opened.contains(&file_id)) + let collapsing_response = egui::CollapsingHeader::new(file.name.clone()) + .id_source(&file.id) + .default_open(self.tree_dir_opened.contains(&file_path_id)) .show(ui, |ui| { - if !self.tree_dir_opened.contains(&file_id) { + if !self.tree_dir_opened.contains(&file_path_id) { return; } for deeper_file in folder_content { @@ -225,9 +225,9 @@ impl Calcifer { } }); if collapsing_response.fully_closed() { - self.tree_dir_opened.retain(|s| s != &file_id); - } else if !self.tree_dir_opened.contains(&file_id) { - self.tree_dir_opened.push(file_id); + self.tree_dir_opened.retain(|s| s != &file_path_id); + } else if !self.tree_dir_opened.contains(&file_path_id) { + self.tree_dir_opened.push(file_path_id); return !file.content_checked; } return check_for_update; diff --git a/src/core/ui.rs b/src/core/ui.rs index 8532f82..f94a119 100644 --- a/src/core/ui.rs +++ b/src/core/ui.rs @@ -53,19 +53,26 @@ impl Calcifer { if !self.tree_visible { return; } - let mut init_update : bool = false; - if self.file_tree.is_none() { - self.file_tree = Some(panels::generate_folder_entry(self.home.as_path())); - init_update = true - } - let mut n_files: usize = 0; egui::SidePanel::left("file_tree_panel").show(ctx, |ui| { ui.horizontal(|ui| { - ui.label("Bookshelf "); + ui.label(format!("Bookshelf ({} files) ", self.n_file_displayed)); + if ui.button("↺").clicked() { + self.file_tree = None; + } + if ui.button("🗙").clicked() { + self.file_tree = None; + self.tree_dir_opened = vec![]; + } }); ui.separator(); - ui.label(format!("{} files displayed", self.n_file_displayed)); - ui.separator(); + + let mut init_update : bool = false; + if self.file_tree.is_none() { + self.file_tree = Some(panels::generate_folder_entry(self.home.as_path())); + init_update = true + } + let mut n_files: usize = 0; + egui::ScrollArea::vertical().show(ui, |ui| { if let Some(file_tree) = self.file_tree.clone() { let update_requested = self.list_files(ui, &file_tree, &mut n_files); @@ -80,8 +87,9 @@ impl Calcifer { } ui.separator(); }); + + self.n_file_displayed = n_files; }); - self.n_file_displayed = n_files; } pub fn draw_bottom_tray(&mut self, ctx: &egui::Context) { @@ -211,7 +219,7 @@ impl Calcifer { if ui .add( egui::Label::new( - egui::RichText::new(" X ").color(color), + egui::RichText::new(" ❌ ").color(color), ) .sense(egui::Sense::click()), ) @@ -281,6 +289,25 @@ impl Calcifer { path.pop(); panels::send_command(format!("cd {}", path.display())); } + + if ui + .add(egui::Button::new("expand tree")) + .clicked() + { + let mut current_path = self.tabs[self.selected_tab].path.clone(); + current_path.pop(); + + while let Some(parent) = current_path.parent() { + let dir_id = panels::get_file_path_id(¤t_path); + if !self.tree_dir_opened.contains(&dir_id) { + self.tree_dir_opened.push(dir_id); + } + current_path = parent.to_path_buf(); + } + + self.tree_visible = true; + self.file_tree = None; + } ui.label("Picked file:"); ui.monospace( diff --git a/src/main.rs b/src/main.rs index e149721..5b6c8e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -211,11 +211,6 @@ impl eframe::App for Calcifer { self.handle_save_file(self.save_tab()); } - if ctx.input(|i| i.key_pressed(egui::Key::T) && i.modifiers.ctrl) { - self.file_tree = None; - self.tree_dir_opened = vec![]; - } - if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl && i.modifiers.shift) { self.handle_save_file(self.save_tab_as()); } diff --git a/src/panels/file_tree.rs b/src/panels/file_tree.rs index 5b0302a..4a9a7c1 100644 --- a/src/panels/file_tree.rs +++ b/src/panels/file_tree.rs @@ -4,6 +4,11 @@ use std::{ fs, io, path::{Path, PathBuf}, }; +use std::hash::DefaultHasher; +use std::time::UNIX_EPOCH; +use std::time::SystemTime; +use std::hash::Hasher; +use std::hash::Hash; //use crate::ALLOWED_FILE_EXTENSIONS; @@ -13,53 +18,72 @@ pub struct FileEntry { pub path: PathBuf, pub folder_content: Option>, pub content_checked: bool, + pub id: String, } impl FileEntry { pub fn new_entry(name: String, path: PathBuf) -> Self { Self { name, - path, + path: path.clone(), folder_content: None, content_checked: true, + id: Self::generate_unique_id(path), } } pub fn end_of_branch(name: String, path: PathBuf) -> Self { Self { name, - path, + path: path.clone(), folder_content: Some(vec![]), content_checked: false, + id: Self::generate_unique_id(path), } } + + fn generate_unique_id(path: PathBuf) -> String { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + + let mut hasher = DefaultHasher::new(); + now.hash(&mut hasher); + + let hash = hasher.finish(); + + format!("{}#{}", path.display(), hash) + } } pub fn update_file_tree(file: FileEntry, opened_dirs: Vec) -> FileEntry { - if opened_dirs.contains(&get_file_id(&file)) { - if let Some(folder_content) = &file.folder_content { - if !file.content_checked { - return generate_folder_entry(&file.path); - } - let updated_content: Vec = folder_content - .iter() - .map(|entry| update_file_tree(entry.clone(), opened_dirs.clone())) - .collect(); - FileEntry { - name: file.name, - path: file.path, - folder_content: Some(updated_content), - content_checked: true, - } - } else { - file - } - } else { - file + if !opened_dirs.contains(&get_file_path_id(&file.path)) { + return file; + } + + if !file.content_checked { + let folder = generate_folder_entry(&file.path); + return update_file_tree(folder, opened_dirs); + } + + if file.folder_content.is_none() { + return file; + } + + let folder_content = file.folder_content.expect("should have checked if none"); + + let updated_content: Vec = folder_content + .iter() + .map(|entry| update_file_tree(entry.clone(), opened_dirs.clone())) + .collect(); + FileEntry { + name: file.name, + path: file.path.clone(), + folder_content: Some(updated_content), + content_checked: true, + id: FileEntry::generate_unique_id(file.path), } } -pub fn get_file_id(file: &FileEntry) -> String { - format!("#{}",file.path.clone().display()) +pub fn get_file_path_id(path: &PathBuf) -> String { + format!("#{}", path.clone().display()) } pub fn generate_folder_entry(path: &Path) -> FileEntry { @@ -106,6 +130,7 @@ pub fn generate_folder_entry(path: &Path) -> FileEntry { path: path.to_path_buf(), folder_content: Some(folder_content), content_checked: true, + id: FileEntry::generate_unique_id(path.to_path_buf()), } } }