diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 79c158048e..eef3921a45 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -381,67 +381,59 @@ impl ProjectPanel { let is_local = project.is_local(); let is_read_only = project.is_read_only(); - let context_menu = ContextMenu::build(cx, |mut menu, cx| { - if is_read_only { - menu = menu.action("Copy Relative Path", Box::new(CopyRelativePath)); - if is_dir { - menu = menu.action("Search Inside", Box::new(NewSearchInDirectory)) - } - - return menu; - } - - if is_local { - menu = menu.action( - "Add Folder to Project", - Box::new(workspace::AddFolderToProject), - ); - if is_root { - menu = menu.entry( - "Remove from Project", - None, - cx.handler_for(&this, move |this, cx| { - this.project.update(cx, |project, cx| { - project.remove_worktree(worktree_id, cx) - }); - }), - ); - } - } - - menu = menu - .action("New File", Box::new(NewFile)) - .action("New Folder", Box::new(NewDirectory)) - .separator() - .action("Cut", Box::new(Cut)) - .action("Copy", Box::new(Copy)); - - if let Some(clipboard_entry) = self.clipboard_entry { - if clipboard_entry.worktree_id() == worktree_id { - menu = menu.action("Paste", Box::new(Paste)); - } - } - - menu = menu - .separator() - .action("Copy Path", Box::new(CopyPath)) - .action("Copy Relative Path", Box::new(CopyRelativePath)) - .separator() - .action("Reveal in Finder", Box::new(RevealInFinder)); - - if is_dir { - menu = menu - .action("Open in Terminal", Box::new(OpenInTerminal)) - .action("Search Inside", Box::new(NewSearchInDirectory)) - } - - menu = menu.separator().action("Rename", Box::new(Rename)); - - if !is_root { - menu = menu.action("Delete", Box::new(Delete)); - } - - menu + let context_menu = ContextMenu::build(cx, |menu, cx| { + menu.context(self.focus_handle.clone()).then_if_else( + is_read_only, + |menu| { + menu.action("Copy Relative Path", Box::new(CopyRelativePath)) + .then_if(is_dir, |menu| { + menu.action("Search Inside", Box::new(NewSearchInDirectory)) + }) + }, + |menu| { + menu.then_if(is_local, |menu| { + menu.action( + "Add Folder to Project", + Box::new(workspace::AddFolderToProject), + ) + .then_if(is_root, |menu| { + menu.entry( + "Remove from Project", + None, + cx.handler_for(&this, move |this, cx| { + this.project.update(cx, |project, cx| { + project.remove_worktree(worktree_id, cx) + }); + }), + ) + }) + }) + .action("New File", Box::new(NewFile)) + .action("New Folder", Box::new(NewDirectory)) + .separator() + .action("Cut", Box::new(Cut)) + .action("Copy", Box::new(Copy)) + .if_some(self.clipboard_entry, |menu, entry| { + menu.then_if(entry.worktree_id() == worktree_id, |menu| { + menu.action("Paste", Box::new(Paste)) + }) + }) + .separator() + .action("Copy Path", Box::new(CopyPath)) + .action("Copy Relative Path", Box::new(CopyRelativePath)) + .separator() + .action("Reveal in Finder", Box::new(RevealInFinder)) + .then_if(is_dir, |menu| { + menu + .action("Open in Terminal", Box::new(OpenInTerminal)) + .action("Search Inside", Box::new(NewSearchInDirectory)) + }) + .separator().action("Rename", Box::new(Rename)) + .then_if(!is_root, |menu| { + menu.action("Delete", Box::new(Delete)) + }) + }, + ) }); cx.focus_view(&context_menu); diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 470483cc0a..dbc0394a5b 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -27,6 +27,7 @@ enum ContextMenuItem { pub struct ContextMenu { items: Vec, focus_handle: FocusHandle, + action_context: Option, selected_index: Option, delayed: bool, clicked: bool, @@ -56,6 +57,7 @@ impl ContextMenu { Self { items: Default::default(), focus_handle, + action_context: None, selected_index: None, delayed: false, clicked: false, @@ -66,6 +68,39 @@ impl ContextMenu { }) } + pub fn if_some(self, condition: Option, f: impl FnOnce(Self, T) -> Self) -> Self { + if let Some(t) = condition { + f(self, t) + } else { + self + } + } + + pub fn then_if_else(self, condition: bool, then: impl FnOnce(Self) -> Self, otherwise: impl FnOnce(Self) -> Self) -> Self { + if condition { + then(self) + } else { + otherwise(self) + } + } + + pub fn then_if(self, condition: bool, f: impl FnOnce(Self) -> Self) -> Self { + if condition { + f(self) + } else { + self + } + } + + pub fn map(self, f: impl FnOnce(Self) -> Self) -> Self { + f(self) + } + + pub fn context(mut self, focus: FocusHandle) -> Self { + self.action_context = Some(focus); + self + } + pub fn header(mut self, title: impl Into) -> Self { self.items.push(ContextMenuItem::Header(title.into())); self @@ -305,7 +340,14 @@ impl Render for ContextMenu { .child(label_element) .debug_selector(|| format!("MENU_ITEM-{}", label)) .children(action.as_ref().and_then(|action| { - KeyBinding::for_action(&**action, cx) + self.action_context + .as_ref() + .map(|focus| { + KeyBinding::for_action_in(&**action, focus, cx) + }) + .unwrap_or_else(|| { + KeyBinding::for_action(&**action, cx) + }) .map(|binding| div().ml_1().child(binding)) })), )