diff --git a/crates/onboarding/src/welcome.rs b/crates/onboarding/src/welcome.rs index 0bc4bd94b33d1faa31ef6a347ae553e23a6d6f2e..9c4714c6424569c6416051505d8aeca5b026128e 100644 --- a/crates/onboarding/src/welcome.rs +++ b/crates/onboarding/src/welcome.rs @@ -9,7 +9,7 @@ use workspace::{ item::{Item, ItemEvent}, with_active_or_new_workspace, }; -use zed_actions::{Extensions, OpenSettings, agent, command_palette}; +use zed_actions::{Extensions, OpenSettingsEditor, agent, command_palette}; use crate::{Onboarding, OpenOnboarding}; @@ -53,7 +53,7 @@ const CONTENT: (Section<4>, Section<3>) = ( SectionEntry { icon: IconName::Settings, title: "Open Settings", - action: &OpenSettings, + action: &OpenSettingsEditor, }, SectionEntry { icon: IconName::ZedAssistant, diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 6f8cc8eb5e8dd0f177cb1fffd5b8916d4e257e73..330de12f32002970b9b46f9cb1098f7295e8dae3 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -131,7 +131,7 @@ pub(crate) fn settings_data() -> Vec { SettingsPageItem::SettingItem(SettingItem { files: USER, title: "Settings Profiles", - description: "Any number of settings profiles that are temporarily applied on top of your existing user settings.", + description: "Any number of settings profiles that are temporarily applied on top of your existing user settings", field: Box::new( SettingField { pick: |settings_content| &settings_content.workspace.use_system_prompts, @@ -295,6 +295,28 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel UI Font Size", + description: "Font size for agent response text in the agent panel. Falls back to the regular UI font size.", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.theme.agent_ui_font_size, + pick_mut: |settings_content| &mut settings_content.theme.agent_ui_font_size, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel Buffer Font Size", + description: "Font size for user messages text in the agent panel", + field: Box::new(SettingField { + pick: |settings_content| &settings_content.theme.agent_buffer_font_size, + pick_mut: |settings_content| { + &mut settings_content.theme.agent_buffer_font_size + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Keymap"), SettingsPageItem::SettingItem(SettingItem { title: "Base Keymap", @@ -1020,7 +1042,7 @@ pub(crate) fn settings_data() -> Vec { }), SettingsPageItem::SettingItem(SettingItem { title: "Min Line Number Digits", - description: "Minimum number of characters to reserve space for in the gutter.", + description: "Minimum number of characters to reserve space for in the gutter", field: Box::new(SettingField { pick: |settings_content| { if let Some(gutter) = &settings_content.editor.gutter { @@ -1638,6 +1660,27 @@ pub(crate) fn settings_data() -> Vec { title: "Workbench & Window", items: vec![ SettingsPageItem::SectionHeader("Status Bar"), + SettingsPageItem::SettingItem(SettingItem { + title: "Project Panel Button", + description: "Whether to show the project panel button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(project_panel) = &settings_content.project_panel { + &project_panel.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .project_panel + .get_or_insert_default() + .button + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SettingItem(SettingItem { title: "Active Language Button", description: "Whether to show the active language button in the status bar", @@ -1738,6 +1781,24 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Debugger Button", + description: "Whether to show the debugger button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.debugger.get_or_insert_default().button + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Tab Bar"), SettingsPageItem::SettingItem(SettingItem { title: "Editor Tabs", @@ -1844,7 +1905,7 @@ pub(crate) fn settings_data() -> Vec { }), SettingsPageItem::SettingItem(SettingItem { title: "Show Onboarding Banner", - description: "Whether to show onboarding banners in the titlebar", + description: "Whether to show banners announcing new features in the titlebar", field: Box::new(SettingField { pick: |settings_content| { if let Some(title_bar) = &settings_content.title_bar { @@ -2255,27 +2316,6 @@ pub(crate) fn settings_data() -> Vec { title: "Panels", items: vec![ SettingsPageItem::SectionHeader("Project Panel"), - SettingsPageItem::SettingItem(SettingItem { - title: "Project Panel Button", - description: "Whether to show the project panel button in the status bar", - field: Box::new(SettingField { - pick: |settings_content| { - if let Some(project_panel) = &settings_content.project_panel { - &project_panel.button - } else { - &None - } - }, - pick_mut: |settings_content| { - &mut settings_content - .project_panel - .get_or_insert_default() - .button - }, - }), - metadata: None, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "Project Panel Dock", description: "Where to dock the project panel", @@ -2884,8 +2924,8 @@ pub(crate) fn settings_data() -> Vec { }), SettingsPageItem::SectionHeader("Git Panel"), SettingsPageItem::SettingItem(SettingItem { - title: "Button", - description: "Whether to show the git panel button in the status bar", + title: "Git Panel Button", + description: "Whether to show the Git panel button in the status bar", field: Box::new(SettingField { pick: |settings_content| { if let Some(git_panel) = &settings_content.git_panel { @@ -2902,8 +2942,8 @@ pub(crate) fn settings_data() -> Vec { files: USER, }), SettingsPageItem::SettingItem(SettingItem { - title: "Dock", - description: "Where to dock the git panel", + title: "Git Panel Dock", + description: "Where to dock the Git panel", field: Box::new(SettingField { pick: |settings_content| { if let Some(git_panel) = &settings_content.git_panel { @@ -2920,8 +2960,8 @@ pub(crate) fn settings_data() -> Vec { files: USER, }), SettingsPageItem::SettingItem(SettingItem { - title: "Default Width", - description: "Default width of the git panel in pixels", + title: "Git Panel Default Width", + description: "Default width of the Git panel in pixels", field: Box::new(SettingField { pick: |settings_content| { if let Some(git_panel) = &settings_content.git_panel { @@ -2940,6 +2980,25 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Debugger Panel"), + SettingsPageItem::SettingItem(SettingItem { + title: "Debugger Panel Dock", + description: "The dock position of the debug panel", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(debugger) = &settings_content.debugger { + &debugger.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.debugger.get_or_insert_default().dock + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SectionHeader("Notification Panel"), SettingsPageItem::SettingItem(SettingItem { title: "Notification Panel Button", @@ -3636,24 +3695,6 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), - SettingsPageItem::SettingItem(SettingItem { - title: "Dock", - description: "The dock position of the debug panel", - field: Box::new(SettingField { - pick: |settings_content| { - if let Some(debugger) = &settings_content.debugger { - &debugger.dock - } else { - &None - } - }, - pick_mut: |settings_content| { - &mut settings_content.debugger.get_or_insert_default().dock - }, - }), - metadata: None, - files: USER, - }), SettingsPageItem::SettingItem(SettingItem { title: "Log DAP Communications", description: "Whether to log messages between active debug adapters and Zed", @@ -3696,24 +3737,6 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), - SettingsPageItem::SettingItem(SettingItem { - title: "Button", - description: "Whether to show the debug button in the status bar", - field: Box::new(SettingField { - pick: |settings_content| { - if let Some(debugger) = &settings_content.debugger { - &debugger.button - } else { - &None - } - }, - pick_mut: |settings_content| { - &mut settings_content.debugger.get_or_insert_default().button - }, - }), - metadata: None, - files: USER, - }), ], }, SettingsPage { @@ -4006,7 +4029,7 @@ fn language_settings_data() -> Vec { }), SettingsPageItem::SettingItem(SettingItem { title: "Show Wrap Guides", - description: "Whether to show wrap guides in the editor. Setting this to true will show a guide at the 'preferred_line_length' value if softwrap is set to 'preferred_line_length', and will show any additional guides as specified by the 'wrap_guides' setting", + description: "Whether to show wrap guides in the editor", field: Box::new(SettingField { pick: |settings_content| { language_settings_field(settings_content, |language| &language.show_wrap_guides) @@ -4438,7 +4461,7 @@ fn language_settings_data() -> Vec { }), SettingsPageItem::SettingItem(SettingItem { title: "Always Treat Brackets As Autoclosed", - description: "Controls how the editor handles the autoclosed characters. When set to `false`(default), skipping over and auto-removing of the closing characters happen only for auto-inserted characters. Otherwise(when `true`), the closing characters are always skipped over and auto-removed no matter how they were inserted", + description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted", field: Box::new(SettingField { pick: |settings_content| { language_settings_field(settings_content, |language| { @@ -4518,7 +4541,7 @@ fn language_settings_data() -> Vec { }), SettingsPageItem::SettingItem(SettingItem { title: "Linked Edits", - description: "Whether to perform linked edits of associated ranges, if the language server supports it. For example, when editing opening tag, the contents of the closing tag will be edited as well", + description: "Whether to perform linked edits of associated ranges, if the LS supports it. For example, when editing opening tag, the contents of the closing tag will be edited as well", field: Box::new(SettingField { pick: |settings_content| { language_settings_field(settings_content, |language| &language.linked_edits) diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index b5d76d4c3e0cc0750fd7001f6d5343d9f8b98964..0307b5f27d9ce2f2976f649b193f83048f593d0c 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -29,8 +29,9 @@ use std::{ sync::{Arc, LazyLock, RwLock, atomic::AtomicBool}, }; use ui::{ - ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding, KeybindingHint, - PopoverMenu, Switch, SwitchColor, TreeViewItem, WithScrollbar, prelude::*, + ContextMenu, Divider, DividerColor, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding, + KeybindingHint, PopoverMenu, Switch, SwitchColor, Tooltip, TreeViewItem, WithScrollbar, + prelude::*, }; use ui_input::{NumberField, NumberFieldType}; use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath}; @@ -242,6 +243,9 @@ fn init_renderers(cx: &mut App) { .icon_color(Color::Error) .icon_size(IconSize::Small) .style(ButtonStyle::Outlined) + .tooltip(Tooltip::text( + "This warning is only displayed in dev builds.", + )) .into_any_element() }) .add_renderer::(|settings_field, file, _, _, cx| { @@ -554,14 +558,14 @@ impl SettingsPageItem { match self { SettingsPageItem::SectionHeader(header) => v_flex() .w_full() - .gap_1() + .gap_1p5() .child( Label::new(SharedString::new_static(header)) - .size(LabelSize::XSmall) + .size(LabelSize::Small) .color(Color::Muted) .buffer_font(cx), ) - .child(Divider::horizontal().color(ui::DividerColor::BorderVariant)) + .child(Divider::horizontal().color(DividerColor::BorderFaded)) .into_any_element(), SettingsPageItem::SettingItem(setting_item) => { let renderer = cx.default_global::().clone(); @@ -571,8 +575,8 @@ impl SettingsPageItem { h_flex() .id(setting_item.title) .w_full() + .min_w_0() .gap_2() - .flex_wrap() .justify_between() .map(|this| { if is_last { @@ -585,8 +589,8 @@ impl SettingsPageItem { }) .child( v_flex() + .w_full() .max_w_1_2() - .flex_shrink() .child( h_flex() .w_full() @@ -620,6 +624,9 @@ impl SettingsPageItem { .icon_color(Color::Error) .icon_size(IconSize::Small) .style(ButtonStyle::Outlined) + .tooltip(Tooltip::text( + "This warning is only displayed in dev builds.", + )) .into_any_element() } else { renderer.render( @@ -651,7 +658,6 @@ impl SettingsPageItem { ) .child( Button::new(("sub-page".into(), sub_page_link.title), "Configure") - .size(ButtonSize::Medium) .icon(IconName::ChevronRight) .icon_position(IconPosition::End) .icon_color(Color::Muted) @@ -1218,6 +1224,7 @@ impl SettingsWindow { h_flex() .py_1() .px_1p5() + .mb_3() .gap_1p5() .rounded_sm() .bg(cx.theme().colors().editor_background) @@ -1233,7 +1240,7 @@ impl SettingsWindow { cx: &mut Context, ) -> impl IntoElement { let visible_count = self.visible_navbar_entries().count(); - let nav_background = cx.theme().colors().panel_background; + let focus_keybind_label = if self.navbar_focus_handle.contains_focused(window, cx) { "Focus Content" } else { @@ -1244,15 +1251,14 @@ impl SettingsWindow { .w_64() .p_2p5() .pt_10() - .gap_3() .flex_none() .border_r_1() .border_color(cx.theme().colors().border) - .bg(nav_background) + .bg(cx.theme().colors().panel_background) .child(self.render_search(window, cx)) .child( v_flex() - .flex_grow() + .size_full() .track_focus(&self.navbar_focus_handle) .tab_group() .tab_index(NAVBAR_GROUP_TAB_INDEX) @@ -1280,53 +1286,46 @@ impl SettingsWindow { }, )) }) - .on_click(cx.listener( - move |this, evt: &gpui::ClickEvent, window, cx| { - this.navbar_entry = ix; - - if !this.navbar_entries[ix].is_root { - let mut selected_page_ix = ix; - - while !this.navbar_entries[selected_page_ix] - .is_root - { - selected_page_ix -= 1; - } - - let section_header = ix - selected_page_ix; - - if let Some(section_index) = this - .page_items() - .enumerate() - .filter(|item| { - matches!( - item.1, - SettingsPageItem::SectionHeader(_) - ) - }) - .take(section_header) - .last() - .map(|pair| pair.0) - { - this.scroll_handle - .scroll_to_top_of_item(section_index); - } + .on_click(cx.listener(move |this, _, _, cx| { + this.navbar_entry = ix; + + if !this.navbar_entries[ix].is_root { + let mut selected_page_ix = ix; + + while !this.navbar_entries[selected_page_ix].is_root + { + selected_page_ix -= 1; } - if evt.is_keyboard() { - // todo(settings_ui): Focus the actual item and scroll to it - this.focus_first_content_item(window, cx); + let section_header = ix - selected_page_ix; + + if let Some(section_index) = this + .page_items() + .enumerate() + .filter(|item| { + matches!( + item.1, + SettingsPageItem::SectionHeader(_) + ) + }) + .take(section_header) + .last() + .map(|pair| pair.0) + { + this.scroll_handle + .scroll_to_top_of_item(section_index); } - cx.notify(); - }, - )) + } + + cx.notify(); + })) .into_any_element() }) .collect() }), ) - .track_scroll(self.list_handle.clone()) - .flex_grow(), + .size_full() + .track_scroll(self.list_handle.clone()), ) .vertical_scrollbar_for(self.list_handle.clone(), window, cx), ) @@ -1335,6 +1334,7 @@ impl SettingsWindow { .w_full() .p_2() .pb_0p5() + .flex_none() .border_t_1() .border_color(cx.theme().colors().border_variant) .children( @@ -1804,7 +1804,6 @@ fn render_number_field( .log_err(); // todo(settings_ui) don't log err } }) - .tab_index(0) .into_any_element() } diff --git a/crates/ui/src/components/tree_view_item.rs b/crates/ui/src/components/tree_view_item.rs index 575cce4817799fc2039cefa5a6f053fe0fbce76a..babc52029af25d4d7babedd9c0a1ffa0aecabf4e 100644 --- a/crates/ui/src/components/tree_view_item.rs +++ b/crates/ui/src/components/tree_view_item.rs @@ -126,11 +126,12 @@ impl Toggleable for TreeViewItem { impl RenderOnce for TreeViewItem { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let selected_bg = cx.theme().colors().element_active.opacity(0.5); + let selected_border = cx.theme().colors().border.opacity(0.6); let focused_border = cx.theme().colors().border_focused; let transparent_border = cx.theme().colors().border_transparent; - let item_size = rems_from_px(28.); + let item_size = rems_from_px(28.); let indentation_line = h_flex().size(item_size).flex_none().justify_center().child( div() .w_px() @@ -149,14 +150,12 @@ impl RenderOnce for TreeViewItem { .cursor_pointer() .size_full() .relative() - .when_some(self.tab_index, |this, index| this.tab_index(index)) .map(|this| { let label = self.label; if self.root_item { this.h(item_size) .px_1() - .mb_1() .gap_2p5() .rounded_sm() .border_1() @@ -166,6 +165,7 @@ impl RenderOnce for TreeViewItem { }) .focus(|s| s.border_color(focused_border)) .hover(|s| s.bg(cx.theme().colors().element_hover)) + .when_some(self.tab_index, |this, index| this.tab_index(index)) .child( Disclosure::new("toggle", self.expanded) .when_some( @@ -190,13 +190,13 @@ impl RenderOnce for TreeViewItem { .px_1() .rounded_sm() .border_1() - .focusable() .border_color(transparent_border) .when(self.selected, |this| { this.border_color(selected_border).bg(selected_bg) }) - .in_focus(|s| s.border_color(focused_border)) + .focus(|s| s.border_color(focused_border)) .hover(|s| s.bg(cx.theme().colors().element_hover)) + .when_some(self.tab_index, |this, index| this.tab_index(index)) .child( Label::new(label) .when(!self.selected, |this| this.color(Color::Muted)), diff --git a/crates/ui_input/src/number_field.rs b/crates/ui_input/src/number_field.rs index 1823932c7d6248d55f35d835c37b26bb1b9b826f..3be609bbe41ac45dac4179ad46594f731d6ac88b 100644 --- a/crates/ui_input/src/number_field.rs +++ b/crates/ui_input/src/number_field.rs @@ -6,7 +6,7 @@ use std::{ }; use editor::{Editor, EditorStyle}; -use gpui::{ClickEvent, Entity, FocusHandle, Focusable, FontWeight, Modifiers}; +use gpui::{ClickEvent, CursorStyle, Entity, FocusHandle, Focusable, FontWeight, Modifiers}; use settings::{CodeFade, MinimumContrast}; use ui::prelude::*; @@ -344,6 +344,27 @@ impl RenderOnce for NumberField { } }; + let bg_color = cx.theme().colors().surface_background; + let hover_bg_color = cx.theme().colors().element_hover; + + let border_color = cx.theme().colors().border_variant; + let focus_border_color = cx.theme().colors().border_focused; + + let base_button = |icon: IconName| { + h_flex() + .cursor(CursorStyle::PointingHand) + .p_1p5() + .size_full() + .justify_center() + .overflow_hidden() + .border_1() + .border_color(border_color) + .bg(bg_color) + .hover(|s| s.bg(hover_bg_color)) + .focus(|s| s.border_color(focus_border_color).bg(hover_bg_color)) + .child(Icon::new(icon).size(IconSize::Small)) + }; + h_flex() .id(self.id.clone()) .track_focus(&self.focus_handle) @@ -376,28 +397,19 @@ impl RenderOnce for NumberField { }; decrement.child( - h_flex() + base_button(IconName::Dash) .id("decrement_button") - .cursor(gpui::CursorStyle::PointingHand) - .p_1p5() - .size_full() - .justify_center() - .overflow_hidden() .rounded_tl_sm() .rounded_bl_sm() - .border_1() - .border_color(cx.theme().colors().border_variant) - .bg(cx.theme().colors().surface_background) - .hover(|s| s.bg(cx.theme().colors().element_hover)) - .child(Icon::new(IconName::Dash).size(IconSize::Small)) - .when_some(tab_index.as_mut(), |this, tab_index| { - *tab_index += 1; - this.tab_index(*tab_index - 1).focus(|style| { - style - .border_color(cx.theme().colors().border_focused) - .bg(cx.theme().colors().element_hover) - }) - }) + .tab_index( + tab_index + .as_mut() + .map(|tab_index| { + *tab_index += 1; + *tab_index - 1 + }) + .unwrap_or(0), + ) .on_click(decrement_handler), ) }) @@ -406,34 +418,23 @@ impl RenderOnce for NumberField { .min_w_16() .size_full() .border_y_1() - .border_color(cx.theme().colors().border_variant) - .bg(cx.theme().colors().surface_background) - .in_focus(|this| this.border_color(cx.theme().colors().border_focused)) + .border_color(border_color) + .bg(bg_color) + .in_focus(|this| this.border_color(focus_border_color)) .child(match *self.mode.read(cx) { NumberFieldMode::Read => h_flex() - .id("numeric_stepper_label") .px_1() .flex_1() .justify_center() .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| { - style - .border_color(cx.theme().colors().border_focused) - .bg(cx.theme().colors().element_hover) - }) - }) - .on_click({ - let _mode = self.mode.clone(); - move |click, _, _cx| { - if click.click_count() == 2 || click.is_keyboard() { - // Edit mode is disabled until we implement center text alignment for editor - // mode.write(cx, NumberFieldMode::Edit); - } - } - }) .into_any_element(), + // Edit mode is disabled until we implement center text alignment for editor + // mode.write(cx, NumberFieldMode::Edit); + // + // When we get to making Edit mode work, we shouldn't even focus the decrement/increment buttons. + // Focus should go instead straight to the editor, avoiding any double-step focus. + // In this world, the buttons become a mouse-only interaction, given users should be able + // to do everything they'd do with the buttons straight in the editor anyway. NumberFieldMode::Edit => h_flex() .flex_1() .child(window.use_state(cx, { @@ -501,28 +502,19 @@ impl RenderOnce for NumberField { }; increment.child( - h_flex() + base_button(IconName::Plus) .id("increment_button") - .cursor(gpui::CursorStyle::PointingHand) - .p_1p5() - .size_full() - .justify_center() - .overflow_hidden() .rounded_tr_sm() .rounded_br_sm() - .border_1() - .border_color(cx.theme().colors().border_variant) - .bg(cx.theme().colors().surface_background) - .hover(|s| s.bg(cx.theme().colors().element_hover)) - .child(Icon::new(IconName::Plus).size(IconSize::Small)) - .when_some(tab_index.as_mut(), |this, tab_index| { - *tab_index += 1; - this.tab_index(*tab_index - 1).focus(|style| { - style - .border_color(cx.theme().colors().border_focused) - .bg(cx.theme().colors().element_hover) - }) - }) + .tab_index( + tab_index + .as_mut() + .map(|tab_index| { + *tab_index += 1; + *tab_index - 1 + }) + .unwrap_or(0), + ) .on_click(increment_handler), ) }),