From 6d9e6c1d2063a824689a1a65c39e012ce27ffcda Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 25 Jul 2025 14:11:39 -0400 Subject: [PATCH] fix(desktop): use the window id for context menus --- src/app.rs | 22 ++++++++++++------- src/dialog.rs | 9 ++++---- src/tab.rs | 61 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/app.rs b/src/app.rs index a87489a..8658031 100644 --- a/src/app.rs +++ b/src/app.rs @@ -960,6 +960,7 @@ impl App { location: Location, activate: bool, selection_paths: Option>, + window_id: Option, ) -> (Entity, Task) { #[cfg(feature = "gvfs")] if let Location::Network(ref uri, ref name, Some(ref path)) = location { @@ -1001,6 +1002,7 @@ impl App { location.clone(), self.config.tab, Some(&self.state.sort_names), + window_id, ); tab.mode = match self.mode { Mode::App => tab::Mode::App, @@ -1009,6 +1011,7 @@ impl App { tab::Mode::Desktop } }; + let entity = self .tab_model .insert() @@ -1038,7 +1041,8 @@ impl App { activate: bool, selection_paths: Option>, ) -> Task { - self.open_tab_entity(location, activate, selection_paths).1 + self.open_tab_entity(location, activate, selection_paths, None) + .1 } // This wrapper ensures that local folders use trash and remote folders permanently delete with a dialog @@ -2424,7 +2428,7 @@ impl Application for App { if tab.context_menu.is_some() { return self.update(Message::TabMessage( Some(entity), - tab::Message::ContextMenu(None), + tab::Message::ContextMenu(None, None), )); } @@ -3705,7 +3709,7 @@ impl Application for App { if tab.context_menu.is_some() { tasks.push(self.update(Message::TabMessage( Some(active), - tab::Message::ContextMenu(None), + tab::Message::ContextMenu(None, None), ))); } } @@ -3804,7 +3808,7 @@ impl Application for App { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); //TODO: move to Task? - if let tab::Message::ContextMenu(_point_opt) = tab_message { + if let tab::Message::ContextMenu(_point_opt, _) = tab_message { // Disable side context page self.set_show_context(false); } @@ -3852,7 +3856,7 @@ impl Application for App { self.update_tab(entity, tab_path, selection_paths), ])); } - tab::Command::ContextMenu(point_opt) => { + tab::Command::ContextMenu(point_opt, parent_id) => { #[cfg(feature = "wayland")] match point_opt { Some(point) => { @@ -3887,9 +3891,10 @@ impl Application for App { ..Default::default() }; SctkPopupSettings { - parent: app - .window_id_opt - .unwrap_or_else(|| WindowId::NONE), + parent: parent_id.unwrap_or( + app.window_id_opt + .unwrap_or_else(|| WindowId::NONE), + ), id: window_id, positioner, parent_size: None, @@ -4543,6 +4548,7 @@ impl Application for App { Location::Desktop(crate::desktop_dir(), display, self.config.desktop), false, None, + Some(surface_id), ); self.windows.insert(surface_id, WindowKind::Desktop(entity)); return Task::batch([ diff --git a/src/dialog.rs b/src/dialog.rs index 977184a..1cf0789 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -945,7 +945,7 @@ impl Application for App { }, }); - let mut tab = Tab::new(location, flags.config.dialog_tab(), None); + let mut tab = Tab::new(location, flags.config.dialog_tab(), None, None); tab.mode = tab::Mode::Dialog(flags.kind.clone()); tab.sort_name = tab::HeadingOptions::Modified; tab.sort_direction = false; @@ -1239,7 +1239,7 @@ impl Application for App { } if self.tab.context_menu.is_some() { - return self.update(Message::TabMessage(tab::Message::ContextMenu(None))); + return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None))); } if self.tab.edit_location.is_some() { @@ -1605,7 +1605,7 @@ impl Application for App { tab::Command::ChangeLocation(_tab_title, _tab_path, _selection_paths) => { commands.push(Task::batch([self.update_watcher(), self.rescan_tab()])); } - tab::Command::ContextMenu(point_opt) => { + tab::Command::ContextMenu(point_opt, parent_id) => { #[cfg(feature = "wayland")] match point_opt { Some(point) => { @@ -1639,7 +1639,8 @@ impl Application for App { ..Default::default() }; SctkPopupSettings { - parent: app.flags.window_id, + parent: parent_id + .unwrap_or(app.flags.window_id), id: window_id, positioner, parent_size: None, diff --git a/src/tab.rs b/src/tab.rs index 5529d8d..8e764cc 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -16,6 +16,7 @@ use cosmic::{ horizontal_rule, rule, scrollable::{self, AbsoluteOffset, Viewport}, }, + window, Alignment, Border, Color, @@ -1508,7 +1509,7 @@ pub enum Command { AddToSidebar(PathBuf), AutoScroll(Option), ChangeLocation(String, Location, Option>), - ContextMenu(Option), + ContextMenu(Option, Option), Delete(Vec), DropFiles(PathBuf, ClipboardPaste), EmptyTrash, @@ -1536,7 +1537,7 @@ pub enum Message { ClickRelease(Option), Config(TabConfig), ContextAction(Action), - ContextMenu(Option), + ContextMenu(Option, Option), LocationContextMenuPoint(Option), LocationContextMenuIndex(Option), LocationMenuAction(LocationMenuAction), @@ -2411,6 +2412,7 @@ pub struct Tab { date_time_formatter: DateTimeFormatter, time_formatter: DateTimeFormatter, watch_drag: bool, + window_id: Option, } async fn calculate_dir_size(path: &Path, controller: Controller) -> Result { @@ -2478,6 +2480,7 @@ impl Tab { location: Location, config: TabConfig, sorting_options: Option<&OrderMap>, + window_id: Option, ) -> Self { let location_str = location.to_string(); let (sort_name, sort_direction) = sorting_options @@ -2523,6 +2526,7 @@ impl Tab { date_time_formatter: date_time_formatter(config.military_time), time_formatter: time_formatter(config.military_time), watch_drag: true, + window_id, } } @@ -3087,7 +3091,7 @@ impl Tab { commands.push(Command::Action(action)); } - Message::ContextMenu(point_opt) => { + Message::ContextMenu(point_opt, _) => { self.edit_location = None; if point_opt.is_none() || !mod_shift { self.context_menu = point_opt; @@ -3926,12 +3930,13 @@ impl Tab { // Update context menu popup if self.context_menu != last_context_menu { if last_context_menu.is_some() { - commands.push(Command::ContextMenu(None)); + commands.push(Command::ContextMenu(None, self.window_id.clone())); } if let Some(point) = self.context_menu { - commands.push(Command::ContextMenu(Some( - point + self.offset_opt.unwrap_or_default(), - ))); + commands.push(Command::ContextMenu( + Some(point + self.offset_opt.unwrap_or_default()), + self.window_id.clone(), + )); } } @@ -5400,9 +5405,12 @@ impl Tab { .on_scroll(|delta| respond_to_scroll_direction(delta, self.modifiers)); if self.context_menu.is_some() { - mouse_area = mouse_area.on_right_press(move |_point_opt| Message::ContextMenu(None)); + mouse_area = mouse_area.on_right_press(move |_point_opt| { + Message::ContextMenu(None, self.window_id.clone()) + }); } else { - mouse_area = mouse_area.on_right_press(Message::ContextMenu); + let window_id = self.window_id.clone(); + mouse_area = mouse_area.on_right_press(move |p| Message::ContextMenu(p, window_id)); } let mut popover = widget::popover(mouse_area); @@ -6063,7 +6071,12 @@ mod tests { fn tab_history() -> io::Result<(TempDir, Tab, Vec)> { let fs = simple_fs(NUM_FILES, NUM_NESTED, NUM_DIRS, NUM_NESTED, NAME_LEN)?; let path = fs.path(); - let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default(), None); + let mut tab = Tab::new( + Location::Path(path.into()), + TabConfig::default(), + None, + None, + ); // All directories (simple_fs only produces one nested layer) let dirs: Vec = filter_dirs(path)? @@ -6160,7 +6173,12 @@ mod tests { .next() .expect("temp directory should have at least one directory"); - let mut tab = Tab::new(Location::Path(path.to_owned()), TabConfig::default(), None); + let mut tab = Tab::new( + Location::Path(path.to_owned()), + TabConfig::default(), + None, + None, + ); debug!( "Emitting Message::Location(Location::Path(\"{}\"))", next_dir.display() @@ -6292,7 +6310,12 @@ mod tests { fn tab_empty_history_does_nothing_on_prev_next() -> io::Result<()> { let fs = simple_fs(0, NUM_NESTED, NUM_DIRS, 0, NAME_LEN)?; let path = fs.path(); - let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default(), None); + let mut tab = Tab::new( + Location::Path(path.into()), + TabConfig::default(), + None, + None, + ); // Tab's location shouldn't change if GoPrev or GoNext is triggered debug!("Emitting Message::GoPrevious",); @@ -6314,7 +6337,12 @@ mod tests { .next() .expect("should be at least one directory"); - let mut tab = Tab::new(Location::Path(next_dir.clone()), TabConfig::default(), None); + let mut tab = Tab::new( + Location::Path(next_dir.clone()), + TabConfig::default(), + None, + None, + ); // This will eventually yield false once root is hit while next_dir.pop() { debug!("Emitting Message::LocationUp",); @@ -6347,7 +6375,12 @@ mod tests { } debug!("Creating tab for directory of long file names"); - Tab::new(Location::Path(path.into()), TabConfig::default(), None); + Tab::new( + Location::Path(path.into()), + TabConfig::default(), + None, + None, + ); Ok(()) }