From b1528601cc1a716ceb7b724892b25528a9f62cbf Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:22:57 -0300 Subject: [PATCH] settings ui: Add some light design tweaks (#38934) Release Notes: - N/A --- crates/settings_ui/src/settings_ui.rs | 152 ++++++++++++++------------ 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 8f2052acf04557236507be3ef49e4af0dc920e66..c5f9258998c7ca9a778e6046c741db7cba7f122b 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -4,8 +4,8 @@ use editor::Editor; use feature_flags::{FeatureFlag, FeatureFlagAppExt as _}; use gpui::{ App, AppContext as _, Context, Div, Entity, Global, IntoElement, ReadGlobal as _, Render, - UniformListScrollHandle, Window, WindowHandle, WindowOptions, actions, div, px, size, - uniform_list, + TitlebarOptions, UniformListScrollHandle, Window, WindowHandle, WindowOptions, actions, div, + point, px, size, uniform_list, }; use project::WorktreeId; use settings::{CursorShape, SaturatingBool, SettingsContent, SettingsStore}; @@ -17,12 +17,7 @@ use std::{ rc::Rc, sync::Arc, }; -use ui::{ - ActiveTheme as _, AnyElement, BorrowAppContext as _, Button, Clickable as _, Color, Divider, - DropdownMenu, FluentBuilder as _, Icon, IconName, InteractiveElement as _, Label, - LabelCommon as _, LabelSize, ListItem, ParentElement, SharedString, - StatefulInteractiveElement as _, Styled, StyledTypography, Switch, h_flex, v_flex, -}; +use ui::{Divider, DropdownMenu, ListItem, Switch, prelude::*}; use util::{paths::PathStyle, rel_path::RelPath}; use crate::components::SettingsEditor; @@ -292,11 +287,16 @@ fn init_renderers(cx: &mut App) { pub fn open_settings_editor(cx: &mut App) -> anyhow::Result> { cx.open_window( WindowOptions { - titlebar: None, + titlebar: Some(TitlebarOptions { + title: Some("Settings Window".into()), + appears_transparent: true, + traffic_light_position: Some(point(px(12.0), px(12.0))), + }), focus: true, show: true, kind: gpui::WindowKind::Normal, - window_min_size: Some(size(px(300.), px(500.))), // todo(settings_ui): Does this min_size make sense? + window_background: cx.theme().window_background_appearance(), + window_min_size: Some(size(px(800.), px(600.))), // 4:3 Aspect Ratio ..Default::default() }, |window, cx| cx.new(|cx| SettingsWindow::new(window, cx)), @@ -342,40 +342,40 @@ enum SettingsPageItem { impl SettingsPageItem { fn render(&self, _file: SettingsFile, window: &mut Window, cx: &mut App) -> AnyElement { match self { - SettingsPageItem::SectionHeader(header) => div() + SettingsPageItem::SectionHeader(header) => v_flex() .w_full() + .gap_0p5() .child(Label::new(SharedString::new_static(header)).size(LabelSize::Large)) .child(Divider::horizontal().color(ui::DividerColor::BorderVariant)) .into_any_element(), SettingsPageItem::SettingItem(setting_item) => { let renderer = cx.default_global::().clone(); - div() + h_flex() .id(setting_item.title) + .w_full() + .gap_2() + .flex_wrap() + .justify_between() .child( - Label::new(SharedString::new_static(setting_item.title)) - .size(LabelSize::Default), - ) - .child( - h_flex() - .justify_between() + v_flex() + .max_w_1_2() + .flex_shrink() .child( - div() - .child( - Label::new(SharedString::new_static( - setting_item.description, - )) - .size(LabelSize::Small) - .color(Color::Muted), - ) - .max_w_1_2(), + Label::new(SharedString::new_static(setting_item.title)) + .size(LabelSize::Default), ) - .child(renderer.render( - setting_item.field.as_ref(), - setting_item.metadata.as_deref(), - window, - cx, - )), + .child( + Label::new(SharedString::new_static(setting_item.description)) + .size(LabelSize::Small) + .color(Color::Muted), + ), ) + .child(renderer.render( + setting_item.field.as_ref(), + setting_item.metadata.as_deref(), + window, + cx, + )) .into_any_element() } } @@ -423,7 +423,7 @@ impl SettingsWindow { let current_file = SettingsFile::User; let search = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); - editor.set_placeholder_text("Search Settings", window, cx); + editor.set_placeholder_text("Search settings…", window, cx); editor }); let mut this = Self { @@ -526,9 +526,7 @@ impl SettingsWindow { } fn render_files(&self, _window: &mut Window, cx: &mut Context) -> Div { - div() - .flex() - .flex_row() + h_flex() .gap_1() .children(self.files.iter().enumerate().map(|(ix, file)| { Button::new(ix, file.name()) @@ -536,19 +534,30 @@ impl SettingsWindow { })) } - fn render_search(&self, _window: &mut Window, _cx: &mut App) -> Div { + fn render_search(&self, _window: &mut Window, cx: &mut App) -> Div { h_flex() - .child(Icon::new(IconName::MagnifyingGlass)) + .pt_1() + .px_1p5() + .gap_1p5() + .rounded_sm() + .bg(cx.theme().colors().editor_background) + .border_1() + .border_color(cx.theme().colors().border) + .child(Icon::new(IconName::MagnifyingGlass).color(Color::Muted)) .child(self.search.clone()) } fn render_nav(&self, window: &mut Window, cx: &mut Context) -> Div { v_flex() + .w_64() + .p_2p5() + .pt_10() + .gap_3() + .flex_none() + .border_r_1() + .border_color(cx.theme().colors().border) .bg(cx.theme().colors().panel_background) - .p_3() - .child(div().h_10()) // Files spacer; .child(self.render_search(window, cx).pb_1()) - .gap_3() .child( uniform_list( "settings-ui-nav-bar", @@ -559,12 +568,25 @@ impl SettingsWindow { .map(|ix| { let entry = &this.navbar_entries[ix]; - div() + h_flex() .id(("settings-ui-section", ix)) + .w_full() + .pl_2p5() + .py_0p5() + .rounded_sm() + .border_1() + .border_color(cx.theme().colors().border_transparent) + .text_color(cx.theme().colors().text_muted) + .when(this.is_navbar_entry_selected(ix), |this| { + this.text_color(cx.theme().colors().text) + .bg(cx.theme().colors().element_selected.opacity(0.2)) + .border_color(cx.theme().colors().border) + }) .child( ListItem::new(("settings-ui-navbar-entry", ix)) .selectable(true) - .indent_step_size(px(10.)) + .inset(true) + .indent_step_size(px(1.)) .indent_level(if entry.is_root { 1 } else { 3 }) .when(entry.is_root, |item| { item.toggle( @@ -579,26 +601,13 @@ impl SettingsWindow { })) }) .child( - div() + h_flex() .text_ui(cx) - .size_full() - .child(entry.title) - .hover(|style| { - style.bg(cx.theme().colors().element_hover) + .truncate() + .hover(|s| { + s.bg(cx.theme().colors().element_hover) }) - .when(!entry.is_root, |this| { - this.text_color( - cx.theme().colors().text_muted, - ) - }) - .when( - this.is_navbar_entry_selected(ix), - |this| { - this.text_color( - Color::Selected.color(cx), - ) - }, - ), + .child(entry.title), ), ) .on_click(cx.listener(move |this, _, _, cx| { @@ -610,7 +619,6 @@ impl SettingsWindow { }), ) .track_scroll(self.list_handle.clone()) - .gap_1_5() .size_full() .flex_grow(), ) @@ -622,7 +630,7 @@ impl SettingsWindow { window: &mut Window, cx: &mut Context, ) -> Div { - v_flex().gap_4().py_4().children( + v_flex().gap_4().children( page.items .iter() .map(|item| item.render(self.current_file.clone(), window, cx)), @@ -655,21 +663,21 @@ impl SettingsWindow { impl Render for SettingsWindow { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { div() - .size_full() - .bg(cx.theme().colors().background) .flex() .flex_row() + .size_full() + .bg(cx.theme().colors().background) .text_color(cx.theme().colors().text) - .child(self.render_nav(window, cx).w(px(300.0))) - .child(Divider::vertical().color(ui::DividerColor::BorderVariant)) + .child(self.render_nav(window, cx)) .child( v_flex() - .bg(cx.theme().colors().editor_background) + .w_full() + .pt_4() .px_6() - .py_2() + .gap_4() + .bg(cx.theme().colors().editor_background) .child(self.render_files(window, cx)) - .child(self.render_page(self.current_page(), window, cx)) - .w_full(), + .child(self.render_page(self.current_page(), window, cx)), ) } }