Add dialog for opening project, highlight active file in project menu
This commit is contained in:
parent
0117a7bc02
commit
8dd38517ab
4 changed files with 107 additions and 48 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -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",
|
||||||
|
|
|
||||||
127
src/main.rs
127
src/main.rs
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)],
|
||||||
|
|
|
||||||
16
src/tab.rs
16
src/tab.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue