diff --git a/src/app.rs b/src/app.rs index d9fc9b7..4a88bc8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -28,7 +28,7 @@ use cosmic::{ event, futures::{self, SinkExt}, keyboard::{Event as KeyEvent, Key, Modifiers}, - stream, + mouse, stream, widget::scrollable, window::{self, Event as WindowEvent, Id as WindowId}, }, @@ -369,6 +369,7 @@ pub enum Message { ModifiersChanged(window::Id, Modifiers), MounterItems(MounterKey, MounterItems), MountResult(MounterKey, MounterItem, Result), + Mouse(window::Id, mouse::Button), MoveTo(Option), MoveToResult(DialogResult), NavBarClose(Entity), @@ -1554,6 +1555,21 @@ impl App { } } + fn close_context_menus(&mut self) -> Task { + let active = self.tab_model.active(); + if let Some(tab) = self.tab_model.data_mut::(active) { + tab.location_context_menu_index = None; + if tab.context_menu.is_some() { + return self.update(Message::TabMessage( + Some(active), + tab::Message::ContextMenu(None, None), + )); + } + } + + Task::none() + } + fn update_nav_model(&mut self) { let mut nav_model = segmented_button::ModelBuilder::default(); @@ -2594,11 +2610,12 @@ impl Application for App { self.set_show_context(false); return cosmic::task::message(cosmic::action::app(Message::SetShowDetails(false))); } - if self.search_get().is_some() { - // Close search if open - return self.search_set_active(None); - } if let Some(tab) = self.tab_model.data_mut::(entity) { + if tab.location_context_menu_index.is_some() { + tab.location_context_menu_index = None; + return Task::none(); + } + if tab.context_menu.is_some() { return self.update(Message::TabMessage( Some(entity), @@ -2621,6 +2638,11 @@ impl Application for App { } } + if self.search_get().is_some() { + // Close search if open + return self.search_set_active(None); + } + Task::none() } @@ -3304,6 +3326,12 @@ impl Application for App { ); } }, + Message::Mouse(window_id, _button) => { + // Close context menu when clicking outside. + if self.core.main_window_id() == Some(window_id) { + return self.close_context_menus(); + } + } Message::MoveTo(entity_opt) => { let selected_paths: Box<[_]> = self.selected_paths(entity_opt).collect(); return self.move_to(&selected_paths); @@ -4046,14 +4074,18 @@ impl Application for App { )); } Message::SearchActivate => { - return if self.search_get().is_none() { - self.search_set_active(Some(String::new())) + let mut tasks = vec![self.close_context_menus()]; + + if self.search_get().is_none() { + tasks.push(self.search_set_active(Some(String::new()))); } else { - widget::text_input::focus(self.search_id.clone()) + tasks.push(widget::text_input::focus(self.search_id.clone())); }; + + return Task::batch(tasks); } Message::SearchClear => { - return self.search_set_active(None); + return Task::batch([self.close_context_menus(), self.search_set_active(None)]); } Message::SearchInput(input) => { return self.search_set_active(Some(input)); @@ -4074,18 +4106,7 @@ impl Application for App { return self.update_config(); } Message::TabActivate(entity) => { - let mut tasks = Vec::new(); - - // Close old context menu - let active = self.tab_model.active(); - if let Some(tab) = self.tab_model.data_mut::(active) - && tab.context_menu.is_some() - { - tasks.push(self.update(Message::TabMessage( - Some(active), - tab::Message::ContextMenu(None, None), - ))); - } + let mut tasks = vec![self.close_context_menus()]; // Activate new tab self.tab_model.activate(entity); @@ -4316,6 +4337,7 @@ impl Application for App { } tab::Command::OpenFile(paths) => commands.push(self.open_file(&paths)), tab::Command::OpenInNewTab(path) => { + commands.push(self.close_context_menus()); commands.push(self.open_tab(Location::Path(path), false, None)); } tab::Command::OpenInNewWindow(path) => match env::current_exe() { @@ -4772,25 +4794,21 @@ impl Application for App { } } NavMenuAction::OpenInNewTab(entity) => { - match self.nav_model.data::(entity) { - Some(Location::Network(uri, display_name, path)) => { - return self.open_tab( - Location::Network(uri.clone(), display_name.clone(), path.clone()), - false, - None, - ); - } + let open_task = match self.nav_model.data::(entity) { + Some(Location::Network(uri, display_name, path)) => self.open_tab( + Location::Network(uri.clone(), display_name.clone(), path.clone()), + false, + None, + ), Some(Location::Path(path)) => { - return self.open_tab(Location::Path(path.clone()), false, None); + self.open_tab(Location::Path(path.clone()), false, None) } - Some(Location::Recents) => { - return self.open_tab(Location::Recents, false, None); - } - Some(Location::Trash) => { - return self.open_tab(Location::Trash, false, None); - } - _ => {} - } + Some(Location::Recents) => self.open_tab(Location::Recents, false, None), + Some(Location::Trash) => self.open_tab(Location::Trash, false, None), + _ => Task::none(), + }; + + return Task::batch([self.close_context_menus(), open_task]); } // Open the selected path in a new cosmic-files window. @@ -6185,6 +6203,10 @@ impl Application for App { let mut subscriptions = vec![ //TODO: filter more events by window id event::listen_with(|event, status, window_id| match event { + Event::Mouse(mouse::Event::ButtonPressed(button)) => match status { + event::Status::Ignored => Some(Message::Mouse(window_id, button)), + event::Status::Captured => None, + }, Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, diff --git a/src/dialog.rs b/src/dialog.rs index bede7d5..ff64889 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -11,7 +11,7 @@ use cosmic::{ event, futures::{self, SinkExt}, keyboard::{Event as KeyEvent, Key, Modifiers, key::Named}, - stream, + mouse, stream, widget::scrollable, window, }, @@ -468,6 +468,7 @@ enum Message { Key(Modifiers, Key, Option), ModifiersChanged(Modifiers), MounterItems(MounterKey, MounterItems), + Mouse(window::Id, mouse::Button), NewFolder, NotifyEvents(Vec), NotifyWatcher(WatcherWrapper), @@ -854,6 +855,15 @@ impl App { } } + fn close_context_menus(&mut self) -> Task { + self.tab.location_context_menu_index = None; + if self.tab.context_menu.is_some() { + return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None))); + } + + Task::none() + } + fn update_nav_model(&mut self) { let mut nav_model = segmented_button::ModelBuilder::default(); @@ -1300,9 +1310,9 @@ impl Application for App { return Task::none(); } - if self.search_get().is_some() { - // Close search if open - return self.search_set(None); + if self.tab.location_context_menu_index.is_some() { + self.tab.location_context_menu_index = None; + return Task::none(); } if self.tab.context_menu.is_some() { @@ -1315,6 +1325,11 @@ impl Application for App { return Task::none(); } + if self.search_get().is_some() { + // Close search if open + return self.search_set(None); + } + let had_focused_button = self.tab.select_focus_id().is_some(); if self.tab.select_none() { if had_focused_button { @@ -1529,6 +1544,12 @@ impl Application for App { return Task::batch(commands); } + Message::Mouse(window_id, _button) => { + // Close context menu when clicking outside. + if self.core.main_window_id() == Some(window_id) { + return self.close_context_menus(); + } + } Message::NewFolder => { if let Some(path) = self.tab.location.path_opt() { self.dialog_pages.push_back(DialogPage::NewFolder { @@ -1686,14 +1707,18 @@ impl Application for App { ))); } Message::SearchActivate => { - return if self.search_get().is_none() { - self.search_set(Some(String::new())) + let mut tasks = vec![self.close_context_menus()]; + + if self.search_get().is_none() { + tasks.push(self.search_set(Some(String::new()))); } else { - widget::text_input::focus(self.search_id.clone()) - }; + tasks.push(widget::text_input::focus(self.search_id.clone())); + } + + return Task::batch(tasks); } Message::SearchClear => { - return self.search_set(None); + return Task::batch([self.close_context_menus(), self.search_set(None)]); } Message::SearchInput(input) => { return self.search_set(Some(input)); @@ -1974,7 +1999,11 @@ impl Application for App { struct WatcherSubscription; struct TimeSubscription; let mut subscriptions = vec![ - event::listen_with(|event, status, _window_id| match event { + event::listen_with(|event, status, window_id| match event { + Event::Mouse(mouse::Event::ButtonPressed(button)) => match status { + event::Status::Ignored => Some(Message::Mouse(window_id, button)), + event::Status::Captured => None, + }, Event::Keyboard(KeyEvent::KeyPressed { key, modifiers,