settings ui: Add some UX adjustments (#39615)

Danilo Leal created

Release Notes:

- N/A

Change summary

Cargo.lock                                 |   1 
assets/keymaps/default-linux.json          |   3 
assets/keymaps/default-macos.json          |   3 
assets/keymaps/default-windows.json        |   3 
crates/settings_ui/Cargo.toml              |   1 
crates/settings_ui/src/settings_ui.rs      | 116 +++++++++++++----------
crates/title_bar/src/title_bar.rs          |   6 -
crates/ui/src/components/tree_view_item.rs |  14 ++
crates/ui_input/src/numeric_stepper.rs     |   4 
crates/zed_actions/src/lib.rs              |   4 
10 files changed, 93 insertions(+), 62 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -14384,6 +14384,7 @@ dependencies = [
  "workspace",
  "workspace-hack",
  "zed-util",
+ "zed_actions",
  "zlog",
 ]
 

assets/keymaps/default-linux.json πŸ”—

@@ -30,7 +30,8 @@
       "ctrl-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
       "ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
       "ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
-      "ctrl-,": "zed::OpenSettings",
+      "ctrl-,": "zed::OpenSettingsEditor",
+      "ctrl-alt-,": "zed::OpenSettings",
       "ctrl-q": "zed::Quit",
       "f4": "debugger::Start",
       "shift-f5": "debugger::Stop",

assets/keymaps/default-macos.json πŸ”—

@@ -39,7 +39,8 @@
       "cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
       "cmd--": ["zed::DecreaseBufferFontSize", { "persist": false }],
       "cmd-0": ["zed::ResetBufferFontSize", { "persist": false }],
-      "cmd-,": "zed::OpenSettings",
+      "cmd-,": "zed::OpenSettingsEditor",
+      "cmd-alt-,": "zed::OpenSettings",
       "cmd-q": "zed::Quit",
       "cmd-h": "zed::Hide",
       "alt-cmd-h": "zed::HideOthers",

assets/keymaps/default-windows.json πŸ”—

@@ -29,7 +29,8 @@
       "ctrl-shift-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
       "ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
       "ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
-      "ctrl-,": "zed::OpenSettings",
+      "ctrl-,": "zed::OpenSettingsEditor",
+      "ctrl-alt-,": "zed::OpenSettings",
       "ctrl-q": "zed::Quit",
       "f4": "debugger::Start",
       "shift-f5": "debugger::Stop",

crates/settings_ui/Cargo.toml πŸ”—

@@ -35,6 +35,7 @@ ui_input.workspace = true
 util.workspace = true
 workspace-hack.workspace = true
 workspace.workspace = true
+zed_actions.workspace = true
 
 [dev-dependencies]
 assets.workspace = true

crates/settings_ui/src/settings_ui.rs πŸ”—

@@ -5,9 +5,9 @@ use editor::{Editor, EditorEvent};
 use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
 use fuzzy::StringMatchCandidate;
 use gpui::{
-    App, AppContext as _, Context, Div, Entity, FontWeight, Global, IntoElement, ReadGlobal as _,
-    Render, ScrollHandle, Task, TitlebarOptions, UniformListScrollHandle, Window, WindowHandle,
-    WindowOptions, actions, div, point, px, size, uniform_list,
+    App, Div, Entity, Focusable, FontWeight, Global, ReadGlobal as _, ScrollHandle, Task,
+    TitlebarOptions, UniformListScrollHandle, Window, WindowHandle, WindowOptions, div, point,
+    prelude::*, px, size, uniform_list,
 };
 use project::WorktreeId;
 use settings::{
@@ -27,8 +27,9 @@ use ui::{
     ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, Switch, SwitchColor,
     TreeViewItem, WithScrollbar, prelude::*,
 };
-use ui_input::{NumericStepper, NumericStepperType};
+use ui_input::{NumericStepper, NumericStepperStyle, NumericStepperType};
 use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath};
+use zed_actions::OpenSettingsEditor;
 
 use crate::components::SettingsEditor;
 
@@ -2704,14 +2705,6 @@ impl FeatureFlag for SettingsUiFeatureFlag {
     const NAME: &'static str = "settings-ui";
 }
 
-actions!(
-    zed,
-    [
-        /// Opens Settings Editor.
-        OpenSettingsEditor
-    ]
-);
-
 pub fn init(cx: &mut App) {
     init_renderers(cx);
 
@@ -2987,17 +2980,18 @@ impl SettingsPageItem {
                             .child(
                                 h_flex()
                                     .w_full()
-                                    .gap_4()
+                                    .gap_1()
                                     .child(Label::new(SharedString::new_static(setting_item.title)))
                                     .when_some(
                                         file_set_in.filter(|file_set_in| file_set_in != &file),
-                                        |elem, file_set_in| {
-                                            elem.child(
+                                        |this, file_set_in| {
+                                            this.child(
                                                 Label::new(format!(
-                                                    "set in {}",
+                                                    "β€” set in {}",
                                                     file_set_in.name()
                                                 ))
-                                                .color(Color::Muted),
+                                                .color(Color::Muted)
+                                                .size(LabelSize::Small),
                                             )
                                         },
                                     ),
@@ -3172,6 +3166,10 @@ impl SettingsWindow {
         this.fetch_files(cx);
         this.build_ui(cx);
 
+        this.search_bar.update(cx, |editor, cx| {
+            editor.focus_handle(cx).focus(window);
+        });
+
         this
     }
 
@@ -3397,13 +3395,15 @@ impl SettingsWindow {
             .gap_1()
             .children(self.files.iter().enumerate().map(|(ix, file)| {
                 Button::new(ix, file.name())
+                    .toggle_state(file == &self.current_file)
+                    .selected_style(ButtonStyle::Tinted(ui::TintColor::Accent))
                     .on_click(cx.listener(move |this, _, _window, cx| this.change_file(ix, cx)))
             }))
     }
 
     fn render_search(&self, _window: &mut Window, cx: &mut App) -> Div {
         h_flex()
-            .pt_1()
+            .py_1()
             .px_1p5()
             .gap_1p5()
             .rounded_sm()
@@ -3414,7 +3414,14 @@ impl SettingsWindow {
             .child(self.search_bar.clone())
     }
 
-    fn render_nav(&self, window: &mut Window, cx: &mut Context<SettingsWindow>) -> Div {
+    fn render_nav(
+        &self,
+        window: &mut Window,
+        cx: &mut Context<SettingsWindow>,
+    ) -> impl IntoElement {
+        let visible_entries: Vec<_> = self.visible_navbar_entries().collect();
+        let visible_count = visible_entries.len();
+
         v_flex()
             .w_64()
             .p_2p5()
@@ -3424,39 +3431,46 @@ impl SettingsWindow {
             .border_r_1()
             .border_color(cx.theme().colors().border)
             .bg(cx.theme().colors().panel_background)
-            .child(self.render_search(window, cx).pb_1())
+            .child(self.render_search(window, cx))
             .child(
-                uniform_list(
-                    "settings-ui-nav-bar",
-                    self.navbar_entries.len(),
-                    cx.processor(|this, range: Range<usize>, _, cx| {
-                        this.visible_navbar_entries()
-                            .skip(range.start.saturating_sub(1))
-                            .take(range.len())
-                            .map(|(ix, entry)| {
-                                TreeViewItem::new(("settings-ui-navbar-entry", ix), entry.title)
-                                    .root_item(entry.is_root)
-                                    .toggle_state(this.is_navbar_entry_selected(ix))
-                                    .when(entry.is_root, |item| {
-                                        item.expanded(entry.expanded).on_toggle(cx.listener(
-                                            move |this, _, _, cx| {
-                                                this.toggle_navbar_entry(ix);
-                                                cx.notify();
-                                            },
-                                        ))
+                v_flex()
+                    .size_full()
+                    .child(
+                        uniform_list(
+                            "settings-ui-nav-bar",
+                            visible_count,
+                            cx.processor(move |this, range: Range<usize>, _, cx| {
+                                let entries: Vec<_> = this.visible_navbar_entries().collect();
+                                range
+                                    .filter_map(|ix| entries.get(ix).copied())
+                                    .map(|(ix, entry)| {
+                                        TreeViewItem::new(
+                                            ("settings-ui-navbar-entry", ix),
+                                            entry.title,
+                                        )
+                                        .root_item(entry.is_root)
+                                        .toggle_state(this.is_navbar_entry_selected(ix))
+                                        .when(entry.is_root, |item| {
+                                            item.expanded(entry.expanded).on_toggle(cx.listener(
+                                                move |this, _, _, cx| {
+                                                    this.toggle_navbar_entry(ix);
+                                                    cx.notify();
+                                                },
+                                            ))
+                                        })
+                                        .on_click(cx.listener(move |this, _, _, cx| {
+                                            this.navbar_entry = ix;
+                                            cx.notify();
+                                        }))
+                                        .into_any_element()
                                     })
-                                    .on_click(cx.listener(move |this, _, _, cx| {
-                                        this.navbar_entry = ix;
-                                        cx.notify();
-                                    }))
-                                    .into_any_element()
-                            })
-                            .collect()
-                    }),
-                )
-                .track_scroll(self.list_handle.clone())
-                .size_full()
-                .flex_grow(),
+                                    .collect()
+                            }),
+                        )
+                        .track_scroll(self.list_handle.clone())
+                        .flex_grow(),
+                    )
+                    .vertical_scrollbar_for(self.list_handle.clone(), window, cx),
             )
     }
 
@@ -3638,6 +3652,7 @@ impl Render for SettingsWindow {
         let ui_font = theme::setup_ui_font(window, cx);
 
         div()
+            .key_context("SettingsWindow")
             .flex()
             .flex_row()
             .size_full()
@@ -3773,6 +3788,7 @@ fn render_numeric_stepper<T: NumericStepperType + Send + Sync>(
                 .log_err(); // todo(settings_ui) don't log err
             }
         })
+        .style(NumericStepperStyle::Outlined)
         .into_any_element()
 }
 

crates/title_bar/src/title_bar.rs πŸ”—

@@ -724,11 +724,7 @@ impl TitleBar {
                             },
                         )
                         .separator()
-                        .action("Settings", zed_actions::OpenSettings.boxed_clone())
-                        .action(
-                            "Settings Profiles",
-                            zed_actions::settings_profile_selector::Toggle.boxed_clone(),
-                        )
+                        .action("Settings", zed_actions::OpenSettingsEditor.boxed_clone())
                         .action("Keymap Editor", Box::new(zed_actions::OpenKeymapEditor))
                         .action(
                             "Themes…",

crates/ui/src/components/tree_view_item.rs πŸ”—

@@ -139,6 +139,7 @@ impl RenderOnce for TreeViewItem {
                 h_flex()
                     .id("inner_tree_view_item")
                     .group("tree_view_item")
+                    .cursor_pointer()
                     .size_full()
                     .relative()
                     .map(|this| {
@@ -207,7 +208,18 @@ impl RenderOnce for TreeViewItem {
                     .when_some(self.on_hover, |this, on_hover| this.on_hover(on_hover))
                     .when_some(
                         self.on_click.filter(|_| !self.disabled),
-                        |this, on_click| this.cursor_pointer().on_click(on_click),
+                        |this, on_click| {
+                            if self.root_item && self.on_toggle.is_some() {
+                                let on_toggle = self.on_toggle.clone().unwrap();
+
+                                this.on_click(move |event, window, cx| {
+                                    on_click(event, window, cx);
+                                    on_toggle(event, window, cx);
+                                })
+                            } else {
+                                this.on_click(on_click)
+                            }
+                        },
                     )
                     .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
                         this.on_mouse_down(MouseButton::Right, move |event, window, cx| {

crates/ui_input/src/numeric_stepper.rs πŸ”—

@@ -417,7 +417,6 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
                     })
                     .child(
                         h_flex()
-                            .h_8()
                             .min_w_16()
                             .w_full()
                             .border_1()
@@ -426,9 +425,10 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
                             .child(match *self.mode.read(cx) {
                                 NumericStepperMode::Read => h_flex()
                                     .id("numeric_stepper_label")
+                                    .px_1()
                                     .flex_1()
                                     .justify_center()
-                                    .child(Label::new((self.format)(&self.value)).mx_3())
+                                    .child(Label::new((self.format)(&self.value)))
                                     .when_some(tab_index.as_mut(), |this, tab_index| {
                                         *tab_index += 1;
                                         this.tab_index(*tab_index - 1).focus(|style| {

crates/zed_actions/src/lib.rs πŸ”—

@@ -30,8 +30,10 @@ pub struct OpenZedUrl {
 actions!(
     zed,
     [
-        /// Opens the settings editor.
+        /// Opens the settings JSON file.
         OpenSettings,
+        /// Opens the settings editor.
+        OpenSettingsEditor,
         /// Opens the default keymap file.
         OpenDefaultKeymap,
         /// Opens account settings.