From 30e562c8c2323c0b1cfd424d2bbb60e12ab2462f Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sat, 25 Jul 2026 22:53:11 +0200 Subject: [PATCH 1/2] fix: nav_context_menu must produce one submenu per nav item The segmented_button widget asserts that context_menu children count matches model length. The yoda fork was building a single flat menu for the clicked item instead of iterating over all nav_model items like upstream, causing an assertion panic on right-click in the sidebar (trash, folders, etc.). Align with upstream: iterate self.nav_model.iter() and use menu::nav_context() instead of menu::items(). --- src/app.rs | 169 +++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 82 deletions(-) diff --git a/src/app.rs b/src/app.rs index 0e572a0..ebd32d9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2833,94 +2833,99 @@ impl Application for App { fn nav_context_menu( &self, ) -> Option>>> { - let entity = self.nav_bar_context_id; - let favorite_index_opt = self.nav_model.data::(entity); - let location_opt = self.nav_model.data::(entity); + let items = self.nav_model.iter().map(|entity| { + let favorite_index_opt = self.nav_model.data::(entity); + let location_opt = self.nav_model.data::(entity); - let mut items = Vec::with_capacity(7); + let mut items = Vec::with_capacity(7); - if location_opt - .and_then(Location::path_opt) - .is_some_and(|x| x.is_file()) - { - items.push(cosmic::widget::menu::Item::Button( - fl!("open"), - None, - NavMenuAction::Open(entity), - )); - items.push(cosmic::widget::menu::Item::Button( - fl!("menu-open-with"), - None, - NavMenuAction::OpenWith(entity), - )); - } else { - items.push(cosmic::widget::menu::Item::Button( - fl!("open-in-new-tab"), - None, - NavMenuAction::OpenInNewTab(entity), - )); - items.push(cosmic::widget::menu::Item::Button( - fl!("open-in-new-window"), - None, - NavMenuAction::OpenInNewWindow(entity), - )); - } - if let Some(path) = location_opt.and_then(Location::path_opt) { - let selected_dir = usize::from(path.is_dir()); - let action_items: Vec<_> = self - .config - .context_actions - .iter() - .enumerate() - .filter(|(_, action)| action.matches_selection(1, selected_dir)) - .map(|(i, action)| { - cosmic::widget::menu::Item::Button( - action.name.clone(), - None, - NavMenuAction::RunContextAction(entity, i), - ) - }) - .collect(); - - if !action_items.is_empty() { - items.push(cosmic::widget::menu::Item::Divider); - items.extend(action_items); + if location_opt + .and_then(Location::path_opt) + .is_some_and(|x| x.is_file()) + { + items.push(cosmic::widget::menu::Item::Button( + fl!("open"), + None, + NavMenuAction::Open(entity), + )); + items.push(cosmic::widget::menu::Item::Button( + fl!("menu-open-with"), + None, + NavMenuAction::OpenWith(entity), + )); + } else { + items.push(cosmic::widget::menu::Item::Button( + fl!("open-in-new-tab"), + None, + NavMenuAction::OpenInNewTab(entity), + )); + items.push(cosmic::widget::menu::Item::Button( + fl!("open-in-new-window"), + None, + NavMenuAction::OpenInNewWindow(entity), + )); } - } - items.push(cosmic::widget::menu::Item::Divider); - if matches!(location_opt, Some(Location::Path(..))) { - items.push(cosmic::widget::menu::Item::Button( - fl!("show-details"), - None, - NavMenuAction::Preview(entity), - )); - } - items.push(cosmic::widget::menu::Item::Divider); - if favorite_index_opt.is_some() { - items.push(cosmic::widget::menu::Item::Button( - fl!("remove-from-sidebar"), - None, - NavMenuAction::RemoveFromSidebar(entity), - )); - } + if let Some(path) = location_opt.and_then(Location::path_opt) { + let selected_dir = usize::from(path.is_dir()); + let action_items: Vec<_> = self + .config + .context_actions + .iter() + .enumerate() + .filter(|(_, action)| action.matches_selection(1, selected_dir)) + .map(|(i, action)| { + cosmic::widget::menu::Item::Button( + action.name.clone(), + None, + NavMenuAction::RunContextAction(entity, i), + ) + }) + .collect(); - if matches!(location_opt, Some(Location::Recents)) && tab::has_recents() { - items.push(cosmic::widget::menu::Item::Button( - fl!("clear-recents-history"), - None, - NavMenuAction::ClearRecents, - )); - } + if !action_items.is_empty() { + items.push(cosmic::widget::menu::Item::Divider); + items.extend(action_items); + } + } + items.push(cosmic::widget::menu::Item::Divider); + if matches!(location_opt, Some(Location::Path(..))) { + items.push(cosmic::widget::menu::Item::Button( + fl!("show-details"), + None, + NavMenuAction::Preview(entity), + )); + } + items.push(cosmic::widget::menu::Item::Divider); + if favorite_index_opt.is_some() { + items.push(cosmic::widget::menu::Item::Button( + fl!("remove-from-sidebar"), + None, + NavMenuAction::RemoveFromSidebar(entity), + )); + } - if matches!(location_opt, Some(Location::Trash)) && !Trash::is_empty() { - items.push(cosmic::widget::menu::Item::Button( - fl!("empty-trash"), - None, - NavMenuAction::EmptyTrash, - )); - } + if matches!(location_opt, Some(Location::Recents)) && tab::has_recents() { + items.push(cosmic::widget::menu::Item::Button( + fl!("clear-recents-history"), + None, + NavMenuAction::ClearRecents, + )); + } - Some(cosmic::widget::menu::items(&HashMap::new(), items)) + if matches!(location_opt, Some(Location::Trash)) && !Trash::is_empty() { + items.push(cosmic::widget::menu::Item::Button( + fl!("empty-trash"), + None, + NavMenuAction::EmptyTrash, + )); + } + items + }); + + Some(cosmic::widget::menu::nav_context( + &HashMap::new(), + items.collect(), + )) } fn nav_model(&self) -> Option<&segmented_button::SingleSelectModel> { From 9e0c2726efb37b4116e782b51d5e637f114271a2 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sat, 25 Jul 2026 22:54:15 +0200 Subject: [PATCH 2/2] feat: add empty-trash action to trash view context menu Right-clicking inside the trash view now shows 'Empty trash' when the trash is not empty, in addition to the existing per-item restore/delete actions and sort options. --- src/menu.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/menu.rs b/src/menu.rs index 0937595..8439655 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -418,6 +418,12 @@ pub fn context_menu<'a>( if tab.mode.multiple() { children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } + if !Trash::is_empty() { + if !children.is_empty() { + children.push(divider::horizontal::light().into()); + } + children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash).into()); + } if !children.is_empty() { children.push(divider::horizontal::light().into()); }