From b6cf576d66995e4b9753119c03cac79af4fac8c6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:44:21 +0200 Subject: [PATCH] project panel: Always show paste in context menu (and grey it out when it's disabled) (#17262) ![image](https://github.com/user-attachments/assets/df471567-bdb9-494b-96a5-84d1da47583f) Release Notes: - "Paste" is now always shown in project panel context menu. --- crates/project_panel/src/project_panel.rs | 8 +++- crates/ui/src/components/context_menu.rs | 53 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index d9ec5ee652ba8f373e0c958e8e413a6c6caf2676..36cdc482619692901098e7443a6c88033f164522 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -499,8 +499,12 @@ impl ProjectPanel { .action("Copy", Box::new(Copy)) .action("Duplicate", Box::new(Duplicate)) // TODO: Paste should always be visible, cbut disabled when clipboard is empty - .when(self.clipboard.as_ref().is_some(), |menu| { - menu.action("Paste", Box::new(Paste)) + .map(|menu| { + if self.clipboard.as_ref().is_some() { + menu.action("Paste", Box::new(Paste)) + } else { + menu.disabled_action("Paste", Box::new(Paste)) + } }) .separator() .action("Copy Path", Box::new(CopyPath)) diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index c93f4e1f3203eb9fdff125fabcb25137305aeed6..3bbe41fe5ee288dc05684de998fc882814e1ca1a 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -21,6 +21,7 @@ enum ContextMenuItem { icon: Option, handler: Rc, &mut WindowContext)>, action: Option>, + disabled: bool, }, CustomEntry { entry_render: Box AnyElement>, @@ -102,6 +103,7 @@ impl ContextMenu { handler: Rc::new(move |_, cx| handler(cx)), icon: None, action, + disabled: false, }); self } @@ -120,6 +122,7 @@ impl ContextMenu { handler: Rc::new(move |_, cx| handler(cx)), icon: None, action, + disabled: false, }); self } @@ -167,6 +170,29 @@ impl ContextMenu { cx.dispatch_action(action.boxed_clone()); }), icon: None, + disabled: false, + }); + self + } + + pub fn disabled_action( + mut self, + label: impl Into, + action: Box, + ) -> Self { + self.items.push(ContextMenuItem::Entry { + toggle: None, + label: label.into(), + action: Some(action.boxed_clone()), + + handler: Rc::new(move |context, cx| { + if let Some(context) = &context { + cx.focus(context); + } + cx.dispatch_action(action.boxed_clone()); + }), + icon: None, + disabled: true, }); self } @@ -179,6 +205,7 @@ impl ContextMenu { action: Some(action.boxed_clone()), handler: Rc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())), icon: Some(IconName::ArrowUpRight), + disabled: false, }); self } @@ -187,7 +214,11 @@ impl ContextMenu { let context = self.action_context.as_ref(); match self.selected_index.and_then(|ix| self.items.get(ix)) { Some( - ContextMenuItem::Entry { handler, .. } + ContextMenuItem::Entry { + handler, + disabled: false, + .. + } | ContextMenuItem::CustomEntry { handler, .. }, ) => (handler)(context, cx), _ => {} @@ -259,6 +290,7 @@ impl ContextMenu { if let Some(ix) = self.items.iter().position(|item| { if let ContextMenuItem::Entry { action: Some(action), + disabled: false, .. } = item { @@ -298,7 +330,7 @@ impl ContextMenuItem { ContextMenuItem::Header(_) | ContextMenuItem::Separator | ContextMenuItem::Label { .. } => false, - ContextMenuItem::Entry { .. } => true, + ContextMenuItem::Entry { disabled, .. } => !disabled, ContextMenuItem::CustomEntry { selectable, .. } => *selectable, } } @@ -328,6 +360,7 @@ impl Render for ContextMenu { for item in self.items.iter() { if let ContextMenuItem::Entry { action: Some(action), + disabled: false, .. } = item { @@ -360,22 +393,30 @@ impl Render for ContextMenu { handler, icon, action, + disabled, } => { let handler = handler.clone(); let menu = cx.view().downgrade(); - + let color = if *disabled { + Color::Muted + } else { + Color::Default + }; let label_element = if let Some(icon) = icon { h_flex() .gap_1() - .child(Label::new(label.clone())) - .child(Icon::new(*icon).size(IconSize::Small)) + .child(Label::new(label.clone()).color(color)) + .child( + Icon::new(*icon).size(IconSize::Small).color(color), + ) .into_any_element() } else { - Label::new(label.clone()).into_any_element() + Label::new(label.clone()).color(color).into_any_element() }; ListItem::new(ix) .inset(true) + .disabled(*disabled) .selected(Some(ix) == self.selected_index) .when_some(*toggle, |list_item, (position, toggled)| { let contents = if toggled {