From bada88c5b30b91cbba5fdf3e6ab6e4fd389d65d6 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Wed, 22 Oct 2025 20:57:01 -0300 Subject: [PATCH] Make the rules library window more consistent with the settings UI (#40948) Now that we have two surface areas that open as separate windows, it's important they're consistent with one another. This PR make the settings UI and rules library windows more similar by having them use the same minimum window size and similar styles for their navbar given they have fundamentally the same design (nav on the left and content on the right). Release Notes: - N/A --- crates/gpui/src/window.rs | 7 ++ crates/rules_library/src/rules_library.rs | 124 +++++++++++----------- crates/settings_ui/src/settings_ui.rs | 10 +- 3 files changed, 74 insertions(+), 67 deletions(-) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 88076f3af13b5d95e820e29a59913156eae07065..aa01e34bbc192cf63da94a9c2e4399ff40f7c9ff 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -60,6 +60,13 @@ pub use prompts::*; pub(crate) const DEFAULT_WINDOW_SIZE: Size = size(px(1536.), px(864.)); +/// A 6:5 aspect ratio minimum window size to be used for functional, +/// additional-to-main-Zed windows, like the settings and rules library windows. +pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size = Size { + width: Pixels(900.), + height: Pixels(750.), +}; + /// Represents the two different phases when dispatching events. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] pub enum DispatchPhase { diff --git a/crates/rules_library/src/rules_library.rs b/crates/rules_library/src/rules_library.rs index 1d3eb8b55690c2344a40820e3c5df472bcfc1e05..3de1786f4c747d162d554d34b06424ec6102fcd4 100644 --- a/crates/rules_library/src/rules_library.rs +++ b/crates/rules_library/src/rules_library.rs @@ -3,9 +3,9 @@ use collections::{HashMap, HashSet}; use editor::{CompletionProvider, SelectionEffects}; use editor::{CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle, actions::Tab}; use gpui::{ - Action, App, Bounds, Entity, EventEmitter, Focusable, PromptLevel, Subscription, Task, - TextStyle, TitlebarOptions, WindowBounds, WindowHandle, WindowOptions, actions, point, size, - transparent_black, + Action, App, Bounds, DEFAULT_ADDITIONAL_WINDOW_SIZE, Entity, EventEmitter, Focusable, + PromptLevel, Subscription, Task, TextStyle, TitlebarOptions, WindowBounds, WindowHandle, + WindowOptions, actions, point, size, transparent_black, }; use language::{Buffer, LanguageRegistry, language_settings::SoftWrap}; use language_model::{ @@ -129,13 +129,13 @@ pub fn open_rules_library( titlebar: Some(TitlebarOptions { title: Some("Rules Library".into()), appears_transparent: true, - traffic_light_position: Some(point(px(9.0), px(9.0))), + traffic_light_position: Some(point(px(12.0), px(12.0))), }), app_id: Some(app_id.to_owned()), window_bounds: Some(WindowBounds::Windowed(bounds)), window_background: cx.theme().window_background_appearance(), window_decorations: Some(window_decorations), - window_min_size: Some(size(px(800.), px(600.))), // 4:3 Aspect Ratio + window_min_size: Some(DEFAULT_ADDITIONAL_WINDOW_SIZE), kind: gpui::WindowKind::Floating, ..Default::default() }, @@ -369,10 +369,9 @@ impl PickerDelegate for RulePickerDelegate { .spacing(ListItemSpacing::Sparse) .toggle_state(selected) .child( - h_flex() - .h_5() - .line_height(relative(1.)) - .child(Label::new(rule.title.clone().unwrap_or("Untitled".into()))), + Label::new(rule.title.clone().unwrap_or("Untitled".into())) + .truncate() + .mr_10(), ) .end_slot::(default.then(|| { IconButton::new("toggle-default-rule", IconName::Paperclip) @@ -453,13 +452,15 @@ impl PickerDelegate for RulePickerDelegate { cx: &mut Context>, ) -> Div { h_flex() - .bg(cx.theme().colors().editor_background) - .rounded_sm() - .overflow_hidden() - .flex_none() .py_1() - .px_2() + .px_1p5() .mx_1() + .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(editor.clone()) } } @@ -1096,11 +1097,11 @@ impl RulesLibrary { v_flex() .id("rule-list") .capture_action(cx.listener(Self::focus_active_rule)) - .bg(cx.theme().colors().panel_background) + .px_1p5() .h_full() - .px_1() - .w_1_3() + .w_64() .overflow_x_hidden() + .bg(cx.theme().colors().panel_background) .child( h_flex() .p(DynamicSpacing::Base04.rems(cx)) @@ -1121,16 +1122,55 @@ impl RulesLibrary { .child(div().flex_grow().child(self.picker.clone())) } + fn render_active_rule_editor( + &self, + editor: &Entity, + cx: &mut Context, + ) -> impl IntoElement { + let settings = ThemeSettings::get_global(cx); + + div() + .w_full() + .on_action(cx.listener(Self::move_down_from_title)) + .pl_1() + .border_1() + .border_color(transparent_black()) + .rounded_sm() + .group_hover("active-editor-header", |this| { + this.border_color(cx.theme().colors().border_variant) + }) + .child(EditorElement::new( + &editor, + EditorStyle { + background: cx.theme().system().transparent, + local_player: cx.theme().players().local(), + text: TextStyle { + color: cx.theme().colors().editor_foreground, + font_family: settings.ui_font.family.clone(), + font_features: settings.ui_font.features.clone(), + font_size: HeadlineSize::Large.rems().into(), + font_weight: settings.ui_font.weight, + line_height: relative(settings.buffer_line_height.value()), + ..Default::default() + }, + scrollbar_width: Pixels::ZERO, + syntax: cx.theme().syntax().clone(), + status: cx.theme().status().clone(), + inlay_hints_style: editor::make_inlay_hints_style(cx), + edit_prediction_styles: editor::make_suggestion_styles(cx), + ..EditorStyle::default() + }, + )) + } + fn render_active_rule(&mut self, cx: &mut Context) -> gpui::Stateful
{ div() - .w_2_3() - .h_full() .id("rule-editor") + .h_full() + .flex_grow() .border_l_1() .border_color(cx.theme().colors().border) .bg(cx.theme().colors().editor_background) - .flex_none() - .min_w_64() .children(self.active_rule_id.and_then(|prompt_id| { let rule_metadata = self.store.read(cx).metadata(prompt_id)?; let rule_editor = &self.rule_editors[&prompt_id]; @@ -1138,7 +1178,6 @@ impl RulesLibrary { let model = LanguageModelRegistry::read_global(cx) .default_model() .map(|default| default.model); - let settings = ThemeSettings::get_global(cx); Some( v_flex() @@ -1158,46 +1197,7 @@ impl RulesLibrary { .gap_2() .justify_between() .child( - div() - .w_full() - .on_action(cx.listener(Self::move_down_from_title)) - .pl_1() - .border_1() - .border_color(transparent_black()) - .rounded_sm() - .group_hover("active-editor-header", |this| { - this.border_color(cx.theme().colors().border_variant) - }) - .child(EditorElement::new( - &rule_editor.title_editor, - EditorStyle { - background: cx.theme().system().transparent, - local_player: cx.theme().players().local(), - text: TextStyle { - color: cx.theme().colors().editor_foreground, - font_family: settings.ui_font.family.clone(), - font_features: settings - .ui_font - .features - .clone(), - font_size: HeadlineSize::Large.rems().into(), - font_weight: settings.ui_font.weight, - line_height: relative( - settings.buffer_line_height.value(), - ), - ..Default::default() - }, - scrollbar_width: Pixels::ZERO, - syntax: cx.theme().syntax().clone(), - status: cx.theme().status().clone(), - inlay_hints_style: editor::make_inlay_hints_style( - cx, - ), - edit_prediction_styles: - editor::make_suggestion_styles(cx), - ..EditorStyle::default() - }, - )), + self.render_active_rule_editor(&rule_editor.title_editor, cx), ) .child( h_flex() diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index d4c94b2b094fece6730b877f8a127b7451545ce2..e78f4458c464dec0fab69c81bc1dd51ee3f128f7 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -6,10 +6,10 @@ use editor::{Editor, EditorEvent}; use feature_flags::FeatureFlag; use fuzzy::StringMatchCandidate; use gpui::{ - Action, App, Div, Entity, FocusHandle, Focusable, Global, ListState, ReadGlobal as _, - ScrollHandle, Stateful, Subscription, Task, TitlebarOptions, UniformListScrollHandle, Window, - WindowBounds, WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, size, - uniform_list, + Action, App, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle, Focusable, Global, + ListState, ReadGlobal as _, ScrollHandle, Stateful, Subscription, Task, TitlebarOptions, + UniformListScrollHandle, Window, WindowBounds, WindowHandle, WindowOptions, actions, div, list, + point, prelude::*, px, uniform_list, }; use heck::ToTitleCase as _; use project::WorktreeId; @@ -575,7 +575,7 @@ pub fn open_settings_editor( cx.defer(move |cx| { let current_rem_size: f32 = theme::ThemeSettings::get_global(cx).ui_font_size(cx).into(); - let default_bounds = size(px(900.), px(750.)); // 4:3 Aspect Ratio + let default_bounds = DEFAULT_ADDITIONAL_WINDOW_SIZE; let default_rem_size = 16.0; let scale_factor = current_rem_size / default_rem_size; let scaled_bounds: gpui::Size = default_bounds.map(|axis| axis * scale_factor);