Add dialog for opening project, highlight active file in project menu

This commit is contained in:
Jeremy Soller 2023-10-27 13:41:48 -06:00
parent 0117a7bc02
commit 8dd38517ab
No known key found for this signature in database
GPG key ID: DCFCA852D3906975
4 changed files with 107 additions and 48 deletions

10
Cargo.lock generated
View file

@ -895,7 +895,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-text" name = "cosmic-text"
version = "0.10.0" 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 = [ dependencies = [
"fontdb 0.15.0", "fontdb 0.15.0",
"libm", "libm",
@ -5854,18 +5854,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.16" version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c552e97c5a9b90bc8ddc545b5106e798807376356688ebaa3aee36f44f8c4b9e" checksum = "a1808c6e5aa42cb2b67578276190258f08679c275d92a19735aad5d359e1b154"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.7.16" version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "964bc0588d7ac1c0243d0427ef08482618313702bbb014806cb7ab3da34d3d99" checksum = "ea597733532c780ce3e110401fc3b359b984cc78e17ac0917d3f371ffd1c1618"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -69,8 +69,10 @@ pub struct App {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
New, New,
OpenDialog, OpenFileDialog,
Open(PathBuf), OpenFile(PathBuf),
OpenProjectDialog,
OpenProject(PathBuf),
Save, Save,
TabActivate(segmented_button::Entity), TabActivate(segmented_button::Entity),
TabClose(segmented_button::Entity), TabClose(segmented_button::Entity),
@ -194,10 +196,34 @@ impl App {
} }
pub fn update_title(&mut self) -> Command<Message> { pub fn update_title(&mut self) -> Command<Message> {
let title = match self.active_tab() { let (title, tab_path_opt) = match self.active_tab() {
Some(tab) => tab.title(), Some(tab) => (tab.title(), tab.path_opt.clone()),
None => format!("No Open File"), 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"); let window_title = format!("{title} - COSMIC Text Editor");
self.set_header_title(title.clone()); self.set_header_title(title.clone());
self.set_window_title(window_title) self.set_window_title(window_title)
@ -247,6 +273,10 @@ impl cosmic::Application for App {
// Show nav bar only if project is provided // Show nav bar only if project is provided
if app.core.nav_bar_active() != app.nav_model.iter().next().is_some() { if app.core.nav_bar_active() != app.nav_model.iter().next().is_some() {
app.core.nav_bar_toggle(); 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 // 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<Message> { fn on_nav_select(&mut self, id: nav_bar::Id) -> Command<Message> {
let node = match self.nav_model.data_mut::<ProjectNode>(id) { // Toggle open state and get clone of node data
let node_opt = match self.nav_model.data_mut::<ProjectNode>(id) {
Some(node) => { Some(node) => {
match node { match node {
ProjectNode::Folder { open, .. } => { ProjectNode::Folder { open, .. } => {
@ -271,43 +302,50 @@ impl cosmic::Application for App {
} }
_ => {} _ => {}
} }
node.clone() Some(node.clone())
}
None => {
log::warn!("no path found for id {:?}", id);
return Command::none();
} }
None => None,
}; };
self.nav_model match node_opt {
.icon_set(id, icon::from_name(node.icon_name()).size(16).icon()); Some(node) => {
// Update icon
self.nav_model
.icon_set(id, icon::from_name(node.icon_name()).size(16).icon());
match node { match node {
ProjectNode::Folder { path, open, .. } => { ProjectNode::Folder { path, open, .. } => {
let position = self.nav_model.position(id).unwrap_or(0); let position = self.nav_model.position(id).unwrap_or(0);
let indent = self.nav_model.indent(id).unwrap_or(0); let indent = self.nav_model.indent(id).unwrap_or(0);
if open { if open {
self.open_folder(path, position + 1, indent + 1); // Open folder
} else { self.open_folder(path, position + 1, indent + 1);
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 { } 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, .. } => { None => {
//TODO: go to already open file if possible // Open project
self.open_tab(Some(path.clone())); self.update(Message::OpenProjectDialog)
self.update_title()
} }
} }
} }
@ -318,11 +356,11 @@ impl cosmic::Application for App {
self.open_tab(None); self.open_tab(None);
return self.update_title(); return self.update_title();
} }
Message::OpenDialog => { Message::OpenFileDialog => {
return Command::perform( return Command::perform(
async { async {
if let Some(handle) = rfd::AsyncFileDialog::new().pick_file().await { 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 { } else {
message::none() message::none()
} }
@ -330,10 +368,25 @@ impl cosmic::Application for App {
|x| x, |x| x,
); );
} }
Message::Open(path) => { Message::OpenFile(path) => {
self.open_tab(Some(path)); self.open_tab(Some(path));
return self.update_title(); 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 => { Message::Save => {
let mut title_opt = None; let mut title_opt = None;

View file

@ -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 file", "Ctrl + N", Message::New),
menu_key("New window", "Ctrl + Shift + N", Message::Todo), menu_key("New window", "Ctrl + Shift + N", Message::Todo),
MenuTree::new(horizontal_rule(1)), MenuTree::new(horizontal_rule(1)),
menu_key("Open file...", "Ctrl + O", Message::OpenDialog), menu_key("Open file...", "Ctrl + O", Message::OpenFileDialog),
MenuTree::with_children( MenuTree::with_children(
menu_folder("Open recent"), menu_folder("Open recent"),
vec![menu_item("TODO", Message::Todo)], vec![menu_item("TODO", Message::Todo)],

View file

@ -56,11 +56,17 @@ impl Tab {
let mut editor = editor.borrow_with(&mut font_system); let mut editor = editor.borrow_with(&mut font_system);
match editor.load_text(&path, self.attrs) { match editor.load_text(&path, self.attrs) {
Ok(()) => { Ok(()) => {
log::info!("opened '{}'", path.display()); log::info!("opened {:?}", path);
self.path_opt = Some(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) => { Err(err) => {
log::error!("failed to open '{}': {}", path.display(), err); log::error!("failed to open {:?}: {}", path, err);
self.path_opt = None; self.path_opt = None;
} }
} }
@ -76,10 +82,10 @@ impl Tab {
} }
match fs::write(path, text) { match fs::write(path, text) {
Ok(()) => { Ok(()) => {
log::info!("saved '{}'", path.display()); log::info!("saved {:?}", path);
} }
Err(err) => { Err(err) => {
log::error!("failed to save '{}': {}", path.display(), err); log::error!("failed to save {:?}: {}", path, err);
} }
} }
} else { } else {