diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 4780f1f565..cbb6791a2f 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -233,31 +233,25 @@ pub fn deploy_context_menu( .action("Copy and Trim", Box::new(CopyAndTrim)) .action("Paste", Box::new(Paste)) .separator() - .map(|builder| { - let reveal_in_finder_label = if cfg!(target_os = "macos") { + .action_disabled_when( + !has_reveal_target, + if cfg!(target_os = "macos") { "Reveal in Finder" } else { "Reveal in File Manager" - }; - const OPEN_IN_TERMINAL_LABEL: &str = "Open in Terminal"; - if has_reveal_target { - builder - .action(reveal_in_finder_label, Box::new(RevealInFileManager)) - .action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) - } else { - builder - .disabled_action(reveal_in_finder_label, Box::new(RevealInFileManager)) - .disabled_action(OPEN_IN_TERMINAL_LABEL, Box::new(OpenInTerminal)) - } - }) - .map(|builder| { - const COPY_PERMALINK_LABEL: &str = "Copy Permalink"; - if has_git_repo { - builder.action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) - } else { - builder.disabled_action(COPY_PERMALINK_LABEL, Box::new(CopyPermalinkToLine)) - } - }); + }, + Box::new(RevealInFileManager), + ) + .action_disabled_when( + !has_reveal_target, + "Open in Terminal", + Box::new(OpenInTerminal), + ) + .action_disabled_when( + !has_git_repo, + "Copy Permalink", + Box::new(CopyPermalinkToLine), + ); match focus { Some(focus) => builder.context(focus), None => builder, diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 51ef90fd38..86a67fcc59 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -122,40 +122,29 @@ fn git_panel_context_menu( ContextMenu::build(window, cx, move |context_menu, _, _| { context_menu .context(focus_handle) - .map(|menu| { - if state.has_unstaged_changes { - menu.action("Stage All", StageAll.boxed_clone()) - } else { - menu.disabled_action("Stage All", StageAll.boxed_clone()) - } - }) - .map(|menu| { - if state.has_staged_changes { - menu.action("Unstage All", UnstageAll.boxed_clone()) - } else { - menu.disabled_action("Unstage All", UnstageAll.boxed_clone()) - } - }) + .action_disabled_when( + !state.has_unstaged_changes, + "Stage All", + StageAll.boxed_clone(), + ) + .action_disabled_when( + !state.has_staged_changes, + "Unstage All", + UnstageAll.boxed_clone(), + ) .separator() .action("Open Diff", project_diff::Diff.boxed_clone()) .separator() - .map(|menu| { - if state.has_tracked_changes { - menu.action("Discard Tracked Changes", RestoreTrackedFiles.boxed_clone()) - } else { - menu.disabled_action( - "Discard Tracked Changes", - RestoreTrackedFiles.boxed_clone(), - ) - } - }) - .map(|menu| { - if state.has_new_changes { - menu.action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone()) - } else { - menu.disabled_action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone()) - } - }) + .action_disabled_when( + !state.has_tracked_changes, + "Discard Tracked Changes", + RestoreTrackedFiles.boxed_clone(), + ) + .action_disabled_when( + !state.has_new_changes, + "Trash Untracked Files", + TrashUntrackedFiles.boxed_clone(), + ) }) } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 4db83bcf4c..657cccf98a 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -820,13 +820,11 @@ impl ProjectPanel { .action("Copy", Box::new(Copy)) .action("Duplicate", Box::new(Duplicate)) // TODO: Paste should always be visible, cbut disabled when clipboard is empty - .map(|menu| { - if self.clipboard.as_ref().is_some() { - menu.action("Paste", Box::new(Paste)) - } else { - menu.disabled_action("Paste", Box::new(Paste)) - } - }) + .action_disabled_when( + self.clipboard.as_ref().is_none(), + "Paste", + Box::new(Paste), + ) .separator() .action("Copy Path", Box::new(zed_actions::workspace::CopyPath)) .action( diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 73b5d06ba0..4adac417bf 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -9,7 +9,7 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ AppContext as _, AsyncApp, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Global, KeyContext, Keystroke, ModifiersChangedEvent, ScrollStrategy, StyledText, - Subscription, WeakEntity, actions, div, + Subscription, WeakEntity, actions, div, transparent_black, }; use language::{Language, LanguageConfig}; use settings::KeybindSource; @@ -17,8 +17,8 @@ use settings::KeybindSource; use util::ResultExt; use ui::{ - ActiveTheme as _, App, BorrowAppContext, ParentElement as _, Render, SharedString, Styled as _, - Window, prelude::*, + ActiveTheme as _, App, BorrowAppContext, ContextMenu, ParentElement as _, Render, SharedString, + Styled as _, Window, prelude::*, right_click_menu, }; use workspace::{Item, ModalView, SerializableItem, Workspace, register_serializable_item}; @@ -30,6 +30,9 @@ use crate::{ actions!(zed, [OpenKeymapEditor]); +const KEYMAP_EDITOR_NAMESPACE: &'static str = "keymap_editor"; +actions!(keymap_editor, [EditBinding, CopyAction, CopyContext]); + pub fn init(cx: &mut App) { let keymap_event_channel = KeymapEventChannel::new(); cx.set_global(keymap_event_channel); @@ -59,6 +62,7 @@ pub fn init(cx: &mut App) { command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _cx| { filter.hide_action_types(&keymap_ui_actions); + filter.hide_namespace(KEYMAP_EDITOR_NAMESPACE); }); cx.observe_flag::( @@ -69,6 +73,7 @@ pub fn init(cx: &mut App) { cx, |filter, _cx| { filter.show_action_types(keymap_ui_actions.iter()); + filter.show_namespace(KEYMAP_EDITOR_NAMESPACE); }, ); } else { @@ -76,6 +81,7 @@ pub fn init(cx: &mut App) { cx, |filter, _cx| { filter.hide_action_types(&keymap_ui_actions); + filter.hide_namespace(KEYMAP_EDITOR_NAMESPACE); }, ); } @@ -231,8 +237,8 @@ impl KeymapEditor { let context = key_binding .predicate() - .map(|predicate| predicate.to_string()) - .unwrap_or_else(|| "".to_string()); + .map(|predicate| KeybindContextString::Local(predicate.to_string().into())) + .unwrap_or(KeybindContextString::Global); let source = source.map(|source| (source, source.name().into())); @@ -249,7 +255,7 @@ impl KeymapEditor { ui_key_binding, action: action_name.into(), action_input, - context: context.into(), + context: Some(context), source, }); string_match_candidates.push(string_match_candidate); @@ -264,7 +270,7 @@ impl KeymapEditor { ui_key_binding: None, action: (*action_name).into(), action_input: None, - context: empty.clone(), + context: None, source: None, }); string_match_candidates.push(string_match_candidate); @@ -345,6 +351,33 @@ impl KeymapEditor { }); } + fn focus_search( + &mut self, + _: &search::FocusSearch, + window: &mut Window, + cx: &mut Context, + ) { + if !self + .filter_editor + .focus_handle(cx) + .contains_focused(window, cx) + { + window.focus(&self.filter_editor.focus_handle(cx)); + } else { + self.filter_editor.update(cx, |editor, cx| { + editor.select_all(&Default::default(), window, cx); + }); + } + self.selected_index.take(); + } + + fn selected_binding(&self) -> Option<&ProcessedKeybinding> { + self.selected_index + .and_then(|match_index| self.matches.get(match_index)) + .map(|r#match| r#match.candidate_id) + .and_then(|keybind_index| self.keybindings.get(keybind_index)) + } + fn select_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context) { if let Some(selected) = self.selected_index { let selected = selected + 1; @@ -408,25 +441,18 @@ impl KeymapEditor { } fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context) { - let Some(index) = self.selected_index else { - return; - }; - let keybind = self.keybindings[self.matches[index].candidate_id].clone(); - - self.edit_keybinding(keybind, window, cx); + self.edit_selected_keybinding(window, cx); } - fn edit_keybinding( - &mut self, - keybind: ProcessedKeybinding, - window: &mut Window, - cx: &mut Context, - ) { + fn edit_selected_keybinding(&mut self, window: &mut Window, cx: &mut Context) { + let Some(keybind) = self.selected_binding() else { + return; + }; self.workspace .update(cx, |workspace, cx| { let fs = workspace.app_state().fs.clone(); workspace.toggle_modal(window, cx, |window, cx| { - let modal = KeybindingEditorModal::new(keybind, fs, window, cx); + let modal = KeybindingEditorModal::new(keybind.clone(), fs, window, cx); window.focus(&modal.focus_handle(cx)); modal }); @@ -434,24 +460,40 @@ impl KeymapEditor { .log_err(); } - fn focus_search( + fn edit_binding(&mut self, _: &EditBinding, window: &mut Window, cx: &mut Context) { + self.edit_selected_keybinding(window, cx); + } + + fn copy_context_to_clipboard( &mut self, - _: &search::FocusSearch, - window: &mut Window, + _: &CopyContext, + _window: &mut Window, cx: &mut Context, ) { - if !self - .filter_editor - .focus_handle(cx) - .contains_focused(window, cx) - { - window.focus(&self.filter_editor.focus_handle(cx)); - } else { - self.filter_editor.update(cx, |editor, cx| { - editor.select_all(&Default::default(), window, cx); - }); - } - self.selected_index.take(); + let context = self + .selected_binding() + .and_then(|binding| binding.context.as_ref()) + .and_then(KeybindContextString::local_str) + .map(|context| context.to_string()); + let Some(context) = context else { + return; + }; + cx.write_to_clipboard(gpui::ClipboardItem::new_string(context.clone())); + } + + fn copy_action_to_clipboard( + &mut self, + _: &CopyAction, + _window: &mut Window, + cx: &mut Context, + ) { + let action = self + .selected_binding() + .map(|binding| binding.action.to_string()); + let Some(action) = action else { + return; + }; + cx.write_to_clipboard(gpui::ClipboardItem::new_string(action.clone())); } } @@ -461,10 +503,43 @@ struct ProcessedKeybinding { ui_key_binding: Option, action: SharedString, action_input: Option, - context: SharedString, + context: Option, source: Option<(KeybindSource, SharedString)>, } +#[derive(Clone, Debug, IntoElement)] +enum KeybindContextString { + Global, + Local(SharedString), +} + +impl KeybindContextString { + const GLOBAL: SharedString = SharedString::new_static(""); + + pub fn local(&self) -> Option<&SharedString> { + match self { + KeybindContextString::Global => None, + KeybindContextString::Local(name) => Some(name), + } + } + + pub fn local_str(&self) -> Option<&str> { + match self { + KeybindContextString::Global => None, + KeybindContextString::Local(name) => Some(name), + } + } +} + +impl RenderOnce for KeybindContextString { + fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { + match self { + KeybindContextString::Global => KeybindContextString::GLOBAL.clone(), + KeybindContextString::Local(name) => name, + } + } +} + impl Item for KeymapEditor { type Event = (); @@ -486,6 +561,9 @@ impl Render for KeymapEditor { .on_action(cx.listener(Self::select_last)) .on_action(cx.listener(Self::focus_search)) .on_action(cx.listener(Self::confirm)) + .on_action(cx.listener(Self::edit_binding)) + .on_action(cx.listener(Self::copy_action_to_clipboard)) + .on_action(cx.listener(Self::copy_context_to_clipboard)) .size_full() .bg(theme.colors().editor_background) .id("keymap-editor") @@ -514,10 +592,6 @@ impl Render for KeymapEditor { .striped() .column_widths([rems(16.), rems(16.), rems(16.), rems(32.), rems(8.)]) .header(["Action", "Arguments", "Keystrokes", "Context", "Source"]) - .selected_item_index(self.selected_index) - .on_click_row(cx.processor(|this, row_index, _window, _cx| { - this.selected_index = Some(row_index); - })) .uniform_list( "keymap-editor-table", row_count, @@ -538,7 +612,12 @@ impl Render for KeymapEditor { .map_or(gpui::Empty.into_any_element(), |input| { input.into_any_element() }); - let context = binding.context.clone().into_any_element(); + let context = binding + .context + .clone() + .map_or(gpui::Empty.into_any_element(), |context| { + context.into_any_element() + }); let source = binding .source .clone() @@ -549,6 +628,43 @@ impl Render for KeymapEditor { }) .collect() }), + ) + .map_row( + cx.processor(|this, (row_index, row): (usize, Div), _window, cx| { + let is_selected = this.selected_index == Some(row_index); + let row = row + .id(("keymap-table-row", row_index)) + .on_click(cx.listener(move |this, _event, _window, _cx| { + this.selected_index = Some(row_index); + })) + .border_2() + .border_color(transparent_black()) + .when(is_selected, |row| { + row.border_color(cx.theme().colors().panel_focused_border) + }); + + right_click_menu(("keymap-table-row-menu", row_index)) + .trigger({ + let this = cx.weak_entity(); + move |is_menu_open: bool, _window, cx| { + if is_menu_open { + this.update(cx, |this, cx| { + if this.selected_index != Some(row_index) { + this.selected_index = Some(row_index); + cx.notify(); + } + }) + .ok(); + } + row + } + }) + .menu({ + let this = cx.weak_entity(); + move |window, cx| build_keybind_context_menu(&this, window, cx) + }) + .into_any_element() + }), ), ) } @@ -712,7 +828,7 @@ impl Render for KeybindingEditorModal { .await { this.update(cx, |this, cx| { - this.error = Some(err); + this.error = Some(err.to_string()); cx.notify(); }) .log_err(); @@ -741,54 +857,55 @@ async fn save_keybinding_update( new_keystrokes: &[Keystroke], fs: &Arc, tab_size: usize, -) -> Result<(), String> { +) -> anyhow::Result<()> { let keymap_contents = settings::KeymapFile::load_keymap_file(fs) .await - .map_err(|err| format!("Failed to load keymap file: {}", err))?; + .context("Failed to load keymap file")?; let existing_keystrokes = existing .ui_key_binding .as_ref() .map(|keybinding| keybinding.key_binding.keystrokes()) .unwrap_or_default(); + let context = existing + .context + .as_ref() + .and_then(KeybindContextString::local_str); + + let input = existing + .action_input + .as_ref() + .map(|input| input.text.as_ref()); + let operation = if existing.ui_key_binding.is_some() { settings::KeybindUpdateOperation::Replace { target: settings::KeybindUpdateTarget { - context: Some(existing.context.as_ref()).filter(|context| !context.is_empty()), + context, keystrokes: existing_keystrokes, action_name: &existing.action, use_key_equivalents: false, - input: existing - .action_input - .as_ref() - .map(|input| input.text.as_ref()), + input, }, target_source: existing .source .map(|(source, _name)| source) .unwrap_or(KeybindSource::User), source: settings::KeybindUpdateTarget { - context: Some(existing.context.as_ref()).filter(|context| !context.is_empty()), + context, keystrokes: new_keystrokes, action_name: &existing.action, use_key_equivalents: false, - input: existing - .action_input - .as_ref() - .map(|input| input.text.as_ref()), + input, }, } } else { - return Err( - "Not Implemented: Creating new bindings from unbound actions is not supported yet." - .to_string(), - ); + anyhow::bail!("Adding new bindings not implemented yet"); }; let updated_keymap_contents = settings::KeymapFile::update_keybinding(operation, keymap_contents, tab_size) - .map_err(|err| format!("Failed to update keybinding: {}", err))?; + .context("Failed to update keybinding")?; fs.atomic_write(paths::keymap_file().clone(), updated_keymap_contents) .await - .map_err(|err| format!("Failed to write keymap file: {}", err))?; + .context("Failed to write keymap file")?; Ok(()) } @@ -903,6 +1020,36 @@ impl Render for KeybindInput { } } +fn build_keybind_context_menu( + this: &WeakEntity, + window: &mut Window, + cx: &mut App, +) -> Entity { + ContextMenu::build(window, cx, |menu, _window, cx| { + let Some(this) = this.upgrade() else { + return menu; + }; + let selected_binding = this.read_with(cx, |this, _cx| this.selected_binding().cloned()); + let Some(selected_binding) = selected_binding else { + return menu; + }; + + let selected_binding_has_context = selected_binding + .context + .as_ref() + .and_then(KeybindContextString::local) + .is_some(); + + menu.action("Edit Binding", Box::new(EditBinding)) + .action("Copy action", Box::new(CopyAction)) + .action_disabled_when( + !selected_binding_has_context, + "Copy Context", + Box::new(CopyContext), + ) + }) +} + impl SerializableItem for KeymapEditor { fn serialized_item_kind() -> &'static str { "KeymapEditor" diff --git a/crates/settings_ui/src/ui_components/table.rs b/crates/settings_ui/src/ui_components/table.rs index 62f597e148..bce131e481 100644 --- a/crates/settings_ui/src/ui_components/table.rs +++ b/crates/settings_ui/src/ui_components/table.rs @@ -155,8 +155,6 @@ impl TableInteractionState { self.vertical_scrollbar.hide(window, cx); } - // fn listener(this: Entity, fn: F) -> - pub fn listener( this: &Entity, f: impl Fn(&mut Self, &E, &mut Window, &mut Context) + 'static, @@ -353,9 +351,8 @@ pub struct Table { headers: Option<[AnyElement; COLS]>, rows: TableContents, interaction_state: Option>, - selected_item_index: Option, column_widths: Option<[Length; COLS]>, - on_click_row: Option>, + map_row: Option AnyElement>>, } impl Table { @@ -367,9 +364,8 @@ impl Table { headers: None, rows: TableContents::Vec(Vec::new()), interaction_state: None, - selected_item_index: None, column_widths: None, - on_click_row: None, + map_row: None, } } @@ -418,11 +414,6 @@ impl Table { self } - pub fn selected_item_index(mut self, selected_item_index: Option) -> Self { - self.selected_item_index = selected_item_index; - self - } - pub fn header(mut self, headers: [impl IntoElement; COLS]) -> Self { self.headers = Some(headers.map(IntoElement::into_any_element)); self @@ -440,11 +431,11 @@ impl Table { self } - pub fn on_click_row( + pub fn map_row( mut self, - callback: impl Fn(usize, &mut Window, &mut App) + 'static, + callback: impl Fn((usize, Div), &mut Window, &mut App) -> AnyElement + 'static, ) -> Self { - self.on_click_row = Some(Rc::new(callback)); + self.map_row = Some(Rc::new(callback)); self } } @@ -465,7 +456,8 @@ pub fn render_row( row_index: usize, items: [impl IntoElement; COLS], table_context: TableRenderContext, - cx: &App, + window: &mut Window, + cx: &mut App, ) -> AnyElement { let is_striped = table_context.striped; let is_last = row_index == table_context.total_row_count - 1; @@ -477,43 +469,33 @@ pub fn render_row( let column_widths = table_context .column_widths .map_or([None; COLS], |widths| widths.map(Some)); - let is_selected = table_context.selected_item_index == Some(row_index); - let row = div() - .w_full() - .border_2() - .border_color(transparent_black()) - .when(is_selected, |row| { - row.border_color(cx.theme().colors().panel_focused_border) - }) - .child( - div() - .w_full() - .flex() - .flex_row() - .items_center() - .justify_between() - .px_1p5() - .py_1() - .when_some(bg, |row, bg| row.bg(bg)) - .when(!is_striped, |row| { - row.border_b_1() - .border_color(transparent_black()) - .when(!is_last, |row| row.border_color(cx.theme().colors().border)) - }) - .children( - items - .map(IntoElement::into_any_element) - .into_iter() - .zip(column_widths) - .map(|(cell, width)| base_cell_style(width, cx).child(cell)), - ), - ); + let row = div().w_full().child( + div() + .w_full() + .flex() + .flex_row() + .items_center() + .justify_between() + .px_1p5() + .py_1() + .when_some(bg, |row, bg| row.bg(bg)) + .when(!is_striped, |row| { + row.border_b_1() + .border_color(transparent_black()) + .when(!is_last, |row| row.border_color(cx.theme().colors().border)) + }) + .children( + items + .map(IntoElement::into_any_element) + .into_iter() + .zip(column_widths) + .map(|(cell, width)| base_cell_style(width, cx).child(cell)), + ), + ); - if let Some(on_click) = table_context.on_click_row { - row.id(("table-row", row_index)) - .on_click(move |_, window, cx| on_click(row_index, window, cx)) - .into_any_element() + if let Some(map_row) = table_context.map_row { + map_row((row_index, row), window, cx) } else { row.into_any_element() } @@ -547,9 +529,8 @@ pub fn render_header( pub struct TableRenderContext { pub striped: bool, pub total_row_count: usize, - pub selected_item_index: Option, pub column_widths: Option<[Length; COLS]>, - pub on_click_row: Option>, + pub map_row: Option AnyElement>>, } impl TableRenderContext { @@ -558,14 +539,13 @@ impl TableRenderContext { striped: table.striped, total_row_count: table.rows.len(), column_widths: table.column_widths, - selected_item_index: table.selected_item_index, - on_click_row: table.on_click_row.clone(), + map_row: table.map_row.clone(), } } } impl RenderOnce for Table { - fn render(mut self, _window: &mut Window, cx: &mut App) -> impl IntoElement { + fn render(mut self, window: &mut Window, cx: &mut App) -> impl IntoElement { let table_context = TableRenderContext::new(&self); let interaction_state = self.interaction_state.and_then(|state| state.upgrade()); @@ -598,7 +578,7 @@ impl RenderOnce for Table { .map(|parent| match self.rows { TableContents::Vec(items) => { parent.children(items.into_iter().enumerate().map(|(index, row)| { - render_row(index, row, table_context.clone(), cx) + render_row(index, row, table_context.clone(), window, cx) })) } TableContents::UniformList(uniform_list_data) => parent.child( @@ -617,6 +597,7 @@ impl RenderOnce for Table { row_index, row, table_context.clone(), + window, cx, ) }) diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 91b2dc8fd4..d7080f21f4 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -503,8 +503,9 @@ impl ContextMenu { self } - pub fn disabled_action( + pub fn action_disabled_when( mut self, + disabled: bool, label: impl Into, action: Box, ) -> Self { @@ -522,7 +523,7 @@ impl ContextMenu { icon_size: IconSize::Small, icon_position: IconPosition::End, icon_color: None, - disabled: true, + disabled, documentation_aside: None, end_slot_icon: None, end_slot_title: None, diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index 3328644e8e..85ef549bc0 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -9,7 +9,7 @@ use gpui::{ pub struct RightClickMenu { id: ElementId, - child_builder: Option AnyElement + 'static>>, + child_builder: Option AnyElement + 'static>>, menu_builder: Option Entity + 'static>>, anchor: Option, attach: Option, @@ -23,11 +23,11 @@ impl RightClickMenu { pub fn trigger(mut self, e: F) -> Self where - F: FnOnce(bool) -> E + 'static, + F: FnOnce(bool, &mut Window, &mut App) -> E + 'static, E: IntoElement + 'static, { - self.child_builder = Some(Box::new(move |is_menu_active| { - e(is_menu_active).into_any_element() + self.child_builder = Some(Box::new(move |is_menu_active, window, cx| { + e(is_menu_active, window, cx).into_any_element() })); self } @@ -149,10 +149,9 @@ impl Element for RightClickMenu { element }); - let mut child_element = this - .child_builder - .take() - .map(|child_builder| (child_builder)(element_state.menu.borrow().is_some())); + let mut child_element = this.child_builder.take().map(|child_builder| { + (child_builder)(element_state.menu.borrow().is_some(), window, cx) + }); let child_layout_id = child_element .as_mut() diff --git a/crates/ui/src/components/stories/context_menu.rs b/crates/ui/src/components/stories/context_menu.rs index b34c65a89b..197964adc8 100644 --- a/crates/ui/src/components/stories/context_menu.rs +++ b/crates/ui/src/components/stories/context_menu.rs @@ -47,12 +47,12 @@ impl Render for ContextMenuStory { .justify_between() .child( right_click_menu("test2") - .trigger(|_| Label::new("TOP LEFT")) + .trigger(|_, _, _| Label::new("TOP LEFT")) .menu(move |window, cx| build_menu(window, cx, "top left")), ) .child( right_click_menu("test1") - .trigger(|_| Label::new("BOTTOM LEFT")) + .trigger(|_, _, _| Label::new("BOTTOM LEFT")) .anchor(Corner::BottomLeft) .attach(Corner::TopLeft) .menu(move |window, cx| build_menu(window, cx, "bottom left")), @@ -65,13 +65,13 @@ impl Render for ContextMenuStory { .justify_between() .child( right_click_menu("test3") - .trigger(|_| Label::new("TOP RIGHT")) + .trigger(|_, _, _| Label::new("TOP RIGHT")) .anchor(Corner::TopRight) .menu(move |window, cx| build_menu(window, cx, "top right")), ) .child( right_click_menu("test4") - .trigger(|_| Label::new("BOTTOM RIGHT")) + .trigger(|_, _, _| Label::new("BOTTOM RIGHT")) .anchor(Corner::BottomRight) .attach(Corner::TopRight) .menu(move |window, cx| build_menu(window, cx, "bottom right")), diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 66336c7be6..8fcd55b784 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -902,7 +902,7 @@ impl Render for PanelButtons { }) .anchor(menu_anchor) .attach(menu_attach) - .trigger(move |is_active| { + .trigger(move |is_active, _window, _cx| { IconButton::new(name, icon) .icon_size(IconSize::Small) .toggle_state(is_active_button) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 5c04912d6b..cb2dd99f5e 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2521,7 +2521,7 @@ impl Pane { let pane = cx.entity().downgrade(); let menu_context = item.item_focus_handle(cx); right_click_menu(ix) - .trigger(|_| tab) + .trigger(|_, _, _| tab) .menu(move |window, cx| { let pane = pane.clone(); let menu_context = menu_context.clone(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 333282611b..944e6b26af 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4311,6 +4311,7 @@ mod tests { "icon_theme_selector", "jj", "journal", + "keymap_editor", "language_selector", "lsp_tool", "markdown", diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index 85e28c6ae8..c998ac1075 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -258,18 +258,12 @@ impl Render for QuickActionBar { .action("Next Problem", Box::new(GoToDiagnostic)) .action("Previous Problem", Box::new(GoToPreviousDiagnostic)) .separator() - .map(|menu| { - if has_diff_hunks { - menu.action("Next Hunk", Box::new(GoToHunk)) - .action("Previous Hunk", Box::new(GoToPreviousHunk)) - } else { - menu.disabled_action("Next Hunk", Box::new(GoToHunk)) - .disabled_action( - "Previous Hunk", - Box::new(GoToPreviousHunk), - ) - } - }) + .action_disabled_when(!has_diff_hunks, "Next Hunk", Box::new(GoToHunk)) + .action_disabled_when( + !has_diff_hunks, + "Previous Hunk", + Box::new(GoToPreviousHunk), + ) .separator() .action("Move Line Up", Box::new(MoveLineUp)) .action("Move Line Down", Box::new(MoveLineDown))