From 8dd38517abb1ddf695cce7dce45715c85b8d9bc7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 27 Oct 2023 13:41:48 -0600 Subject: [PATCH] Add dialog for opening project, highlight active file in project menu --- Cargo.lock | 10 ++--- src/main.rs | 127 +++++++++++++++++++++++++++++++++++++--------------- src/menu.rs | 2 +- src/tab.rs | 16 ++++--- 4 files changed, 107 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 716a443..e65c524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -895,7 +895,7 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.10.0" -source = "git+https://github.com/pop-os/cosmic-text?branch=vi-editor#c1e40363ab576c90edb7b78f1f257b3845558b1a" +source = "git+https://github.com/pop-os/cosmic-text?branch=vi-editor#423fc2243930645036ff053e384a3ce64e70255e" dependencies = [ "fontdb 0.15.0", "libm", @@ -5854,18 +5854,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c552e97c5a9b90bc8ddc545b5106e798807376356688ebaa3aee36f44f8c4b9e" +checksum = "a1808c6e5aa42cb2b67578276190258f08679c275d92a19735aad5d359e1b154" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964bc0588d7ac1c0243d0427ef08482618313702bbb014806cb7ab3da34d3d99" +checksum = "ea597733532c780ce3e110401fc3b359b984cc78e17ac0917d3f371ffd1c1618" dependencies = [ "proc-macro2", "quote", diff --git a/src/main.rs b/src/main.rs index 850268f..e479a41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,8 +69,10 @@ pub struct App { #[derive(Clone, Debug)] pub enum Message { New, - OpenDialog, - Open(PathBuf), + OpenFileDialog, + OpenFile(PathBuf), + OpenProjectDialog, + OpenProject(PathBuf), Save, TabActivate(segmented_button::Entity), TabClose(segmented_button::Entity), @@ -194,10 +196,34 @@ impl App { } pub fn update_title(&mut self) -> Command { - let title = match self.active_tab() { - Some(tab) => tab.title(), - None => format!("No Open File"), + let (title, tab_path_opt) = match self.active_tab() { + Some(tab) => (tab.title(), tab.path_opt.clone()), + None => (format!("No Open File"), None), }; + + //TODO: is this the best place for this? + let mut active_id = segmented_button::Entity::default(); + match tab_path_opt { + Some(tab_path) => { + for id in self.nav_model.iter() { + match self.nav_model.data(id) { + Some(node) => match node { + ProjectNode::File { path, .. } => { + if path == &tab_path { + active_id = id; + break; + } + } + _ => {} + }, + None => {} + } + } + } + None => {} + } + self.nav_model.activate(active_id); + let window_title = format!("{title} - COSMIC Text Editor"); self.set_header_title(title.clone()); self.set_window_title(window_title) @@ -247,6 +273,10 @@ impl cosmic::Application for App { // Show nav bar only if project is provided if app.core.nav_bar_active() != app.nav_model.iter().next().is_some() { app.core.nav_bar_toggle(); + app.nav_model + .insert() + .icon(icon::from_name("folder-open-symbolic").size(16).icon()) + .text("Open project"); } // Open an empty file if no arguments provided @@ -263,7 +293,8 @@ impl cosmic::Application for App { } fn on_nav_select(&mut self, id: nav_bar::Id) -> Command { - let node = match self.nav_model.data_mut::(id) { + // Toggle open state and get clone of node data + let node_opt = match self.nav_model.data_mut::(id) { Some(node) => { match node { ProjectNode::Folder { open, .. } => { @@ -271,43 +302,50 @@ impl cosmic::Application for App { } _ => {} } - node.clone() - } - None => { - log::warn!("no path found for id {:?}", id); - return Command::none(); + Some(node.clone()) } + None => None, }; - self.nav_model - .icon_set(id, icon::from_name(node.icon_name()).size(16).icon()); + match node_opt { + Some(node) => { + // Update icon + self.nav_model + .icon_set(id, icon::from_name(node.icon_name()).size(16).icon()); - match node { - ProjectNode::Folder { path, open, .. } => { - let position = self.nav_model.position(id).unwrap_or(0); - let indent = self.nav_model.indent(id).unwrap_or(0); - if open { - self.open_folder(path, position + 1, indent + 1); - } else { - loop { - let child_id = match self.nav_model.entity_at(position + 1) { - Some(some) => some, - None => break, - }; - - if self.nav_model.indent(child_id).unwrap_or(0) > indent { - self.nav_model.remove(child_id); + match node { + ProjectNode::Folder { path, open, .. } => { + let position = self.nav_model.position(id).unwrap_or(0); + let indent = self.nav_model.indent(id).unwrap_or(0); + if open { + // Open folder + self.open_folder(path, position + 1, indent + 1); } else { - break; + // Close folder + loop { + let child_id = match self.nav_model.entity_at(position + 1) { + Some(some) => some, + None => break, + }; + + if self.nav_model.indent(child_id).unwrap_or(0) > indent { + self.nav_model.remove(child_id); + } else { + break; + } + } } + Command::none() + } + ProjectNode::File { path, .. } => { + //TODO: go to already open file if possible + self.update(Message::OpenFile(path)) } } - Command::none() } - ProjectNode::File { path, .. } => { - //TODO: go to already open file if possible - self.open_tab(Some(path.clone())); - self.update_title() + None => { + // Open project + self.update(Message::OpenProjectDialog) } } } @@ -318,11 +356,11 @@ impl cosmic::Application for App { self.open_tab(None); return self.update_title(); } - Message::OpenDialog => { + Message::OpenFileDialog => { return Command::perform( async { if let Some(handle) = rfd::AsyncFileDialog::new().pick_file().await { - message::app(Message::Open(handle.path().to_owned())) + message::app(Message::OpenFile(handle.path().to_owned())) } else { message::none() } @@ -330,10 +368,25 @@ impl cosmic::Application for App { |x| x, ); } - Message::Open(path) => { + Message::OpenFile(path) => { self.open_tab(Some(path)); return self.update_title(); } + Message::OpenProjectDialog => { + return Command::perform( + async { + if let Some(handle) = rfd::AsyncFileDialog::new().pick_folder().await { + message::app(Message::OpenProject(handle.path().to_owned())) + } else { + message::none() + } + }, + |x| x, + ); + } + Message::OpenProject(path) => { + self.open_project(path); + } Message::Save => { let mut title_opt = None; diff --git a/src/menu.rs b/src/menu.rs index bd9af8c..3860723 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -51,7 +51,7 @@ pub fn menu_bar<'a>(config: &Config) -> Element<'a, Message> { menu_key("New file", "Ctrl + N", Message::New), menu_key("New window", "Ctrl + Shift + N", Message::Todo), MenuTree::new(horizontal_rule(1)), - menu_key("Open file...", "Ctrl + O", Message::OpenDialog), + menu_key("Open file...", "Ctrl + O", Message::OpenFileDialog), MenuTree::with_children( menu_folder("Open recent"), vec![menu_item("TODO", Message::Todo)], diff --git a/src/tab.rs b/src/tab.rs index 42d7fb6..2cd86aa 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -56,11 +56,17 @@ impl Tab { let mut editor = editor.borrow_with(&mut font_system); match editor.load_text(&path, self.attrs) { Ok(()) => { - log::info!("opened '{}'", path.display()); - self.path_opt = Some(path); + log::info!("opened {:?}", path); + self.path_opt = match fs::canonicalize(&path) { + Ok(ok) => Some(ok), + Err(err) => { + log::error!("failed to canonicalize {:?}: {}", path, err); + Some(path) + } + }; } Err(err) => { - log::error!("failed to open '{}': {}", path.display(), err); + log::error!("failed to open {:?}: {}", path, err); self.path_opt = None; } } @@ -76,10 +82,10 @@ impl Tab { } match fs::write(path, text) { Ok(()) => { - log::info!("saved '{}'", path.display()); + log::info!("saved {:?}", path); } Err(err) => { - log::error!("failed to save '{}': {}", path.display(), err); + log::error!("failed to save {:?}: {}", path, err); } } } else {