From 136468a4df71d31806276f56f9e0448cea6c13e6 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Tue, 9 Sep 2025 09:36:12 -0300 Subject: [PATCH] keymap editor: Add some adjustments to the UI (#37819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Makes the keymap editor search container more consistent with the project & file search corresponding elements - Changes the keymap editor menu item in the user menu be called "Keymap Editor", as opposed to "Key Binding", to match with the tab and action name Design note: Still a bit unsure about the extra space on the right for the keymap editor. This makes it way more consistent with the other search views, but it also just feels like space that could be used. On the other hand, though, it's very unlikely anyone will ever use more than 30% of the search bar width as search queries here are likely pretty short; definitely much shorter than project search queries. Screenshot 2025-09-09 at 1  02@2x Release Notes: - N/A --- crates/keymap_editor/src/keymap_editor.rs | 283 ++++++++++-------- .../src/ui_components/keystroke_input.rs | 30 +- crates/search/src/project_search.rs | 5 +- crates/search/src/search_bar.rs | 4 +- crates/title_bar/src/title_bar.rs | 2 +- crates/ui_input/src/ui_input.rs | 2 +- 6 files changed, 178 insertions(+), 148 deletions(-) diff --git a/crates/keymap_editor/src/keymap_editor.rs b/crates/keymap_editor/src/keymap_editor.rs index a8e356276b..7e0a96d47a 100644 --- a/crates/keymap_editor/src/keymap_editor.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -1576,33 +1576,6 @@ impl Render for KeymapEditor { .child( h_flex() .gap_2() - .child( - right_click_menu("open-keymap-menu") - .menu(|window, cx| { - ContextMenu::build(window, cx, |menu, _, _| { - menu.header("Open Keymap JSON") - .action("User", zed_actions::OpenKeymap.boxed_clone()) - .action("Zed Default", zed_actions::OpenDefaultKeymap.boxed_clone()) - .action("Vim Default", vim::OpenDefaultKeymap.boxed_clone()) - }) - }) - .anchor(gpui::Corner::TopLeft) - .trigger(|open, _, _| - IconButton::new( - "OpenKeymapJsonButton", - IconName::Json - ) - .shape(ui::IconButtonShape::Square) - .when(!open, |this| - this.tooltip(move |window, cx| { - Tooltip::with_meta("Open Keymap JSON", Some(&zed_actions::OpenKeymap),"Right click to view more options", window, cx) - }) - ) - .on_click(|_, window, cx| { - window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx); - }) - ) - ) .child( div() .key_context({ @@ -1617,73 +1590,139 @@ impl Render for KeymapEditor { .py_1() .border_1() .border_color(theme.colors().border) - .rounded_lg() + .rounded_md() .child(self.filter_editor.clone()), ) .child( - IconButton::new( - "KeymapEditorToggleFiltersIcon", - IconName::Keyboard, - ) - .shape(ui::IconButtonShape::Square) - .tooltip({ - let focus_handle = focus_handle.clone(); - - move |window, cx| { - Tooltip::for_action_in( - "Search by Keystroke", - &ToggleKeystrokeSearch, - &focus_handle.clone(), - window, - cx, + h_flex() + .gap_1() + .min_w_64() + .child( + IconButton::new( + "KeymapEditorToggleFiltersIcon", + IconName::Keyboard, ) - } - }) - .toggle_state(matches!( - self.search_mode, - SearchMode::KeyStroke { .. } - )) - .on_click(|_, window, cx| { - window.dispatch_action(ToggleKeystrokeSearch.boxed_clone(), cx); - }), - ) - .child( - IconButton::new("KeymapEditorConflictIcon", IconName::Warning) - .shape(ui::IconButtonShape::Square) - .when( - self.keybinding_conflict_state.any_user_binding_conflicts(), - |this| { - this.indicator(Indicator::dot().color(Color::Warning)) - }, - ) - .tooltip({ - let filter_state = self.filter_state; - let focus_handle = focus_handle.clone(); + .icon_size(IconSize::Small) + .tooltip({ + let focus_handle = focus_handle.clone(); - move |window, cx| { - Tooltip::for_action_in( - match filter_state { - FilterState::All => "Show Conflicts", - FilterState::Conflicts => "Hide Conflicts", - }, - &ToggleConflictFilter, - &focus_handle.clone(), - window, + move |window, cx| { + Tooltip::for_action_in( + "Search by Keystroke", + &ToggleKeystrokeSearch, + &focus_handle.clone(), + window, + cx, + ) + } + }) + .toggle_state(matches!( + self.search_mode, + SearchMode::KeyStroke { .. } + )) + .on_click(|_, window, cx| { + window.dispatch_action( + ToggleKeystrokeSearch.boxed_clone(), cx, + ); + }), + ) + .child( + IconButton::new("KeymapEditorConflictIcon", IconName::Warning) + .icon_size(IconSize::Small) + .when( + self.keybinding_conflict_state + .any_user_binding_conflicts(), + |this| { + this.indicator( + Indicator::dot().color(Color::Warning), + ) + }, ) - } - }) - .selected_icon_color(Color::Warning) - .toggle_state(matches!( - self.filter_state, - FilterState::Conflicts - )) - .on_click(|_, window, cx| { - window.dispatch_action( - ToggleConflictFilter.boxed_clone(), - cx, - ); - }), + .tooltip({ + let filter_state = self.filter_state; + let focus_handle = focus_handle.clone(); + + move |window, cx| { + Tooltip::for_action_in( + match filter_state { + FilterState::All => "Show Conflicts", + FilterState::Conflicts => { + "Hide Conflicts" + } + }, + &ToggleConflictFilter, + &focus_handle.clone(), + window, + cx, + ) + } + }) + .selected_icon_color(Color::Warning) + .toggle_state(matches!( + self.filter_state, + FilterState::Conflicts + )) + .on_click(|_, window, cx| { + window.dispatch_action( + ToggleConflictFilter.boxed_clone(), + cx, + ); + }), + ) + .child( + div() + .ml_1() + .pl_2() + .border_l_1() + .border_color(cx.theme().colors().border_variant) + .child( + right_click_menu("open-keymap-menu") + .menu(|window, cx| { + ContextMenu::build(window, cx, |menu, _, _| { + menu.header("Open Keymap JSON") + .action( + "User", + zed_actions::OpenKeymap.boxed_clone(), + ) + .action( + "Zed Default", + zed_actions::OpenDefaultKeymap + .boxed_clone(), + ) + .action( + "Vim Default", + vim::OpenDefaultKeymap.boxed_clone(), + ) + }) + }) + .anchor(gpui::Corner::TopLeft) + .trigger(|open, _, _| { + IconButton::new( + "OpenKeymapJsonButton", + IconName::Json, + ) + .icon_size(IconSize::Small) + .when(!open, |this| { + this.tooltip(move |window, cx| { + Tooltip::with_meta( + "Open keymap.json", + Some(&zed_actions::OpenKeymap), + "Right click to view more options", + window, + cx, + ) + }) + }) + .on_click(|_, window, cx| { + window.dispatch_action( + zed_actions::OpenKeymap.boxed_clone(), + cx, + ); + }) + }), + ), + ) ), ) .when_some( @@ -1694,48 +1733,42 @@ impl Render for KeymapEditor { |this, exact_match| { this.child( h_flex() - .map(|this| { - if self - .keybinding_conflict_state - .any_user_binding_conflicts() - { - this.pr(rems_from_px(54.)) - } else { - this.pr_7() - } - }) .gap_2() .child(self.keystroke_editor.clone()) .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, - window, - cx, + h_flex() + .min_w_64() + .child( + IconButton::new( + "keystrokes-exact-match", + IconName::CaseSensitive, ) - } - }) - .shape(IconButtonShape::Square) - .toggle_state(exact_match) - .on_click( - cx.listener(|_, _, window, cx| { - window.dispatch_action( - ToggleExactKeystrokeMatching.boxed_clone(), - cx, - ); - }), - ), - ), + .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, + window, + cx, + ) + } + }) + .shape(IconButtonShape::Square) + .toggle_state(exact_match) + .on_click( + cx.listener(|_, _, window, cx| { + window.dispatch_action( + ToggleExactKeystrokeMatching.boxed_clone(), + cx, + ); + }), + ), + ), + ) ) }, ), diff --git a/crates/keymap_editor/src/ui_components/keystroke_input.rs b/crates/keymap_editor/src/ui_components/keystroke_input.rs index e6b2ff7105..e264df3b62 100644 --- a/crates/keymap_editor/src/ui_components/keystroke_input.rs +++ b/crates/keymap_editor/src/ui_components/keystroke_input.rs @@ -461,7 +461,7 @@ impl Render for KeystrokeInput { let is_focused = self.outer_focus_handle.contains_focused(window, cx); let is_recording = self.is_recording(window); - let horizontal_padding = rems_from_px(64.); + let width = rems_from_px(64.); let recording_bg_color = colors .editor_background @@ -528,6 +528,9 @@ impl Render for KeystrokeInput { h_flex() .id("keystroke-input") .track_focus(&self.outer_focus_handle) + .key_context(Self::key_context()) + .on_action(cx.listener(Self::start_recording)) + .on_action(cx.listener(Self::clear_keystrokes)) .py_2() .px_3() .gap_2() @@ -535,7 +538,7 @@ impl Render for KeystrokeInput { .w_full() .flex_1() .justify_between() - .rounded_sm() + .rounded_md() .overflow_hidden() .map(|this| { if is_recording { @@ -545,16 +548,16 @@ impl Render for KeystrokeInput { } }) .border_1() - .border_color(colors.border_variant) - .when(is_focused, |parent| { - parent.border_color(colors.border_focused) + .map(|this| { + if is_focused { + this.border_color(colors.border_focused) + } else { + this.border_color(colors.border_variant) + } }) - .key_context(Self::key_context()) - .on_action(cx.listener(Self::start_recording)) - .on_action(cx.listener(Self::clear_keystrokes)) .child( h_flex() - .w(horizontal_padding) + .w(width) .gap_0p5() .justify_start() .flex_none() @@ -573,14 +576,13 @@ impl Render for KeystrokeInput { .id("keystroke-input-inner") .track_focus(&self.inner_focus_handle) .on_modifiers_changed(cx.listener(Self::on_modifiers_changed)) - .size_full() .when(!self.search, |this| { this.focus(|mut style| { style.border_color = Some(colors.border_focused); style }) }) - .w_full() + .size_full() .min_w_0() .justify_center() .flex_wrap() @@ -589,7 +591,7 @@ impl Render for KeystrokeInput { ) .child( h_flex() - .w(horizontal_padding) + .w(width) .gap_0p5() .justify_end() .flex_none() @@ -641,9 +643,7 @@ impl Render for KeystrokeInput { "Clear Keystrokes", &ClearKeystrokes, )) - .when(!is_recording || !is_focused, |this| { - this.icon_color(Color::Muted) - }) + .when(!is_focused, |this| this.icon_color(Color::Muted)) .on_click(cx.listener(|this, _event, window, cx| { this.clear_keystrokes(&ClearKeystrokes, window, cx); })), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 33458a3a88..80ed0958b7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -36,10 +36,7 @@ use std::{ pin::pin, sync::Arc, }; -use ui::{ - Icon, IconButton, IconButtonShape, IconName, KeyBinding, Label, LabelCommon, LabelSize, - Toggleable, Tooltip, h_flex, prelude::*, utils::SearchInputWidth, v_flex, -}; +use ui::{IconButtonShape, KeyBinding, Toggleable, Tooltip, prelude::*, utils::SearchInputWidth}; use util::{ResultExt as _, paths::PathMatcher}; use workspace::{ DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation, diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 44f6b3fdd2..631b96b69f 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -41,15 +41,15 @@ pub(super) fn render_action_button( pub(crate) fn input_base_styles(border_color: Hsla, map: impl FnOnce(Div) -> Div) -> Div { h_flex() - .min_w_32() .map(map) + .min_w_32() .h_8() .pl_2() .pr_1() .py_1() .border_1() .border_color(border_color) - .rounded_lg() + .rounded_md() } pub(crate) fn render_text_input( diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 0090929937..e15e7ad46d 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -693,7 +693,7 @@ impl TitleBar { "Settings Profiles", zed_actions::settings_profile_selector::Toggle.boxed_clone(), ) - .action("Key Bindings", Box::new(keymap_editor::OpenKeymapEditor)) + .action("Keymap Editor", Box::new(keymap_editor::OpenKeymapEditor)) .action( "Themes…", zed_actions::theme_selector::Toggle::default().boxed_clone(), diff --git a/crates/ui_input/src/ui_input.rs b/crates/ui_input/src/ui_input.rs index 02f8ef89f3..39a701c8e8 100644 --- a/crates/ui_input/src/ui_input.rs +++ b/crates/ui_input/src/ui_input.rs @@ -168,7 +168,7 @@ impl Render for SingleLineInput { .py_1p5() .flex_grow() .text_color(style.text_color) - .rounded_sm() + .rounded_md() .bg(style.background_color) .border_1() .border_color(style.border_color)