From d77ab99ab1a427964b1313b7fbeb55c7dfbaff7a Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:43:15 -0300 Subject: [PATCH] keymap_editor: Make "toggle exact match mode" the default for binding search (#42883) I think having the "exact mode" turned on by default is usually what users will expect when searching for a specific keybinding. When it's turned off, it's very odd to search for a super common binding like "command-enter" and get no results. That happens because without that mode, we're trying to match for subsequent matches, which I'm betting it's an edge case. Hopefully, this change will make the keymap editor feel more like it works well. I'm also adding the toggle icon button inside the keystroke input for consistency with the project search input. Making this change very inspired by [Sam Rose's feedback](https://bsky.app/profile/samwho.dev/post/3m5juszqyd22w). Release Notes: - keymap editor: Made the "toggle exact match mode" the default keystroke search mode so that whatever you search for matches exactly to results. --- crates/keymap_editor/src/keymap_editor.rs | 71 ++++++++----------- .../src/ui_components/keystroke_input.rs | 10 ++- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/crates/keymap_editor/src/keymap_editor.rs b/crates/keymap_editor/src/keymap_editor.rs index 58a2a5d044f208d967b58e275a5e908ff0c63418..5e39e80b4bd7e9b91c9419290a48b39d060aa267 100644 --- a/crates/keymap_editor/src/keymap_editor.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -184,7 +184,7 @@ enum SearchMode { impl SearchMode { fn invert(&self) -> Self { match self { - SearchMode::Normal => SearchMode::KeyStroke { exact_match: false }, + SearchMode::Normal => SearchMode::KeyStroke { exact_match: true }, SearchMode::KeyStroke { .. } => SearchMode::Normal, } } @@ -1600,9 +1600,33 @@ impl Item for KeymapEditor { impl Render for KeymapEditor { fn render(&mut self, _window: &mut Window, cx: &mut ui::Context) -> impl ui::IntoElement { + if let SearchMode::KeyStroke { exact_match } = self.search_mode { + let button = IconButton::new("keystrokes-exact-match", IconName::CaseSensitive) + .tooltip(move |_window, cx| { + Tooltip::for_action( + "Toggle Exact Match Mode", + &ToggleExactKeystrokeMatching, + cx, + ) + }) + .shape(IconButtonShape::Square) + .toggle_state(exact_match) + .on_click(cx.listener(|_, _, window, cx| { + window.dispatch_action(ToggleExactKeystrokeMatching.boxed_clone(), cx); + })); + + self.keystroke_editor.update(cx, |editor, _| { + editor.actions_slot = Some(button.into_any_element()); + }); + } else { + self.keystroke_editor.update(cx, |editor, _| { + editor.actions_slot = None; + }); + } + let row_count = self.matches.len(); - let theme = cx.theme(); let focus_handle = &self.focus_handle; + let theme = cx.theme(); v_flex() .id("keymap-editor") @@ -1786,49 +1810,14 @@ impl Render for KeymapEditor { ) ), ) - .when_some( - match self.search_mode { - SearchMode::Normal => None, - SearchMode::KeyStroke { exact_match } => Some(exact_match), - }, - |this, exact_match| { + .when( + matches!(self.search_mode, SearchMode::KeyStroke { .. }), + |this| { this.child( h_flex() .gap_2() .child(self.keystroke_editor.clone()) - .child( - h_flex() - .min_w_64() - .child( - IconButton::new( - "keystrokes-exact-match", - IconName::CaseSensitive, - ) - .tooltip({ - let keystroke_focus_handle = - self.keystroke_editor.read(cx).focus_handle(cx); - - move |_window, cx| { - Tooltip::for_action_in( - "Toggle Exact Match Mode", - &ToggleExactKeystrokeMatching, - &keystroke_focus_handle, - cx, - ) - } - }) - .shape(IconButtonShape::Square) - .toggle_state(exact_match) - .on_click( - cx.listener(|_, _, window, cx| { - window.dispatch_action( - ToggleExactKeystrokeMatching.boxed_clone(), - cx, - ); - }), - ), - ), - ) + .child(div().min_w_64()), // Spacer div to align with the search input ) }, ), diff --git a/crates/keymap_editor/src/ui_components/keystroke_input.rs b/crates/keymap_editor/src/ui_components/keystroke_input.rs index f1f583be7a6bb0aa1796c7c03d9aea084b3bdd3b..6936de784f9d5c16b218d0952c41d6336299a0f9 100644 --- a/crates/keymap_editor/src/ui_components/keystroke_input.rs +++ b/crates/keymap_editor/src/ui_components/keystroke_input.rs @@ -64,6 +64,7 @@ pub struct KeystrokeInput { clear_close_keystrokes_timer: Option>, #[cfg(test)] recording: bool, + pub actions_slot: Option, } impl KeystrokeInput { @@ -94,6 +95,7 @@ impl KeystrokeInput { clear_close_keystrokes_timer: None, #[cfg(test)] recording: false, + actions_slot: None, } } @@ -445,6 +447,11 @@ impl KeystrokeInput { // not get de-synced self.inner_focus_handle.is_focused(window) } + + pub fn actions_slot(mut self, action: impl IntoElement) -> Self { + self.actions_slot = Some(action.into_any_element()); + self + } } impl EventEmitter<()> for KeystrokeInput {} @@ -586,7 +593,7 @@ impl Render for KeystrokeInput { .min_w_0() .justify_center() .flex_wrap() - .gap(ui::DynamicSpacing::Base04.rems(cx)) + .gap_1() .children(self.render_keystrokes(is_recording)), ) .child( @@ -636,6 +643,7 @@ impl Render for KeystrokeInput { ) } }) + .when_some(self.actions_slot.take(), |this, action| this.child(action)) .when(is_recording, |this| { this.child( IconButton::new("clear-btn", IconName::Backspace)