diff --git a/crates/story/src/settings_story.rs b/crates/story/src/settings_story.rs index c84058ac..f8b21a90 100644 --- a/crates/story/src/settings_story.rs +++ b/crates/story/src/settings_story.rs @@ -1,6 +1,6 @@ use gpui::{ App, AppContext, Axis, Context, Element, Entity, FocusHandle, Focusable, Global, IntoElement, - ParentElement as _, Render, SharedString, Styled, Window, + ParentElement as _, Render, SharedString, Styled, Window, px, }; use gpui_component::{ @@ -99,6 +99,10 @@ impl super::Story for SettingsStory { fn new_view(window: &mut Window, cx: &mut App) -> Entity { Self::view(window, cx) } + + fn paddings() -> gpui::Pixels { + px(0.) + } } impl SettingsStory { diff --git a/crates/story/src/sidebar_story.rs b/crates/story/src/sidebar_story.rs index e88f9f23..8b284b87 100644 --- a/crates/story/src/sidebar_story.rs +++ b/crates/story/src/sidebar_story.rs @@ -281,7 +281,7 @@ impl Render for SidebarStory { Sidebar::new(self.side) .collapsed(self.collapsed) .w(px(220.)) - .p_3() + .gap_0() .header( SidebarHeader::new() .child( diff --git a/crates/ui/src/setting/page.rs b/crates/ui/src/setting/page.rs index 71b12716..ffbb45e4 100644 --- a/crates/ui/src/setting/page.rs +++ b/crates/ui/src/setting/page.rs @@ -1,14 +1,14 @@ use gpui::{ - list, prelude::FluentBuilder as _, px, App, Entity, InteractiveElement as _, IntoElement, - ParentElement as _, SharedString, StatefulInteractiveElement, Styled, Window, + div, list, prelude::FluentBuilder as _, px, App, Entity, InteractiveElement as _, IntoElement, + ListAlignment, ListState, ParentElement as _, SharedString, Styled, Window, }; use rust_i18n::t; use crate::{ button::{Button, ButtonVariants}, - divider::Divider, h_flex, label::Label, + scroll::{Scrollbar, ScrollbarState}, setting::{settings::SettingsState, RenderOptions, SettingGroup}, v_flex, ActiveTheme, IconName, Sizable, }; @@ -100,11 +100,16 @@ impl SettingPage { .collect::>(); let groups_count = groups.len(); - let list_state = window + let (scroll_state, list_state) = window .use_keyed_state( SharedString::from(format!("list-state:{}", ix)), cx, - |_, _| gpui::ListState::new(groups_count, gpui::ListAlignment::Top, px(0.)), + |_, _| { + ( + ScrollbarState::default(), + ListState::new(groups_count, ListAlignment::Top, px(100.)), + ) + }, ) .read(cx) .clone(); @@ -123,12 +128,13 @@ impl SettingPage { v_flex() .id(ix) - .p_4() .size_full() - .overflow_scroll() .child( v_flex() + .p_4() .gap_3() + .border_b_1() + .border_color(cx.theme().border) .child(h_flex().justify_between().child(self.title.clone()).when( self.is_resettable(cx), |this| { @@ -153,22 +159,37 @@ impl SettingPage { .text_sm() .text_color(cx.theme().muted_foreground), ) - }) - .child(Divider::horizontal()), + }), ) .child( - list(list_state.clone(), { - let query = query.clone(); - let options = *options; - move |ix, window, cx| { - let group = groups[ix].clone(); - group - .pt_6() - .render(ix, &query, &options, window, cx) - .into_any_element() - } - }) - .size_full(), + div() + .px_4() + .relative() + .flex_1() + .w_full() + .child( + list(list_state.clone(), { + let query = query.clone(); + let options = *options; + move |ix, window, cx| { + let group = groups[ix].clone(); + group + .py_4() + .render(ix, &query, &options, window, cx) + .into_any_element() + } + }) + .size_full(), + ) + .child( + div() + .absolute() + .top_0() + .left_0() + .right_0() + .bottom_0() + .child(Scrollbar::vertical(&scroll_state, &list_state)), + ), ) } } diff --git a/crates/ui/src/setting/settings.rs b/crates/ui/src/setting/settings.rs index fba9eee4..d039bff4 100644 --- a/crates/ui/src/setting/settings.rs +++ b/crates/ui/src/setting/settings.rs @@ -4,11 +4,11 @@ use crate::{ resizable::{h_resizable, resizable_panel}, setting::{SettingGroup, SettingPage}, sidebar::{Sidebar, SidebarMenu, SidebarMenuItem}, - IconName, Sizable, Size, + IconName, Sizable, Size, StyledExt, }; use gpui::{ div, prelude::FluentBuilder as _, px, relative, App, AppContext as _, Axis, ElementId, Entity, - IntoElement, ParentElement as _, Pixels, RenderOnce, Styled, Window, + IntoElement, ParentElement as _, Pixels, RenderOnce, StyleRefinement, Styled, Window, }; use rust_i18n::t; @@ -31,6 +31,7 @@ pub struct Settings { group_variant: GroupBoxVariant, size: Size, sidebar_width: Pixels, + sidebar_style: StyleRefinement, } impl Settings { @@ -42,6 +43,7 @@ impl Settings { group_variant: GroupBoxVariant::default(), size: Size::default(), sidebar_width: px(250.0), + sidebar_style: StyleRefinement::default(), } } @@ -71,6 +73,12 @@ impl Settings { self } + /// Set the style refinement for the sidebar. + pub fn sidebar_style(mut self, style: &StyleRefinement) -> Self { + self.sidebar_style = style.clone(); + self + } + fn filtered_pages(&self, query: &str) -> Vec { self.pages .iter() @@ -138,6 +146,7 @@ impl Settings { Sidebar::left() .w(relative(1.)) .border_0() + .refine_style(&self.sidebar_style) .collapsed(false) .header( div() @@ -145,57 +154,52 @@ impl Settings { .child(Input::new(&search_input).prefix(IconName::Search)), ) .child( - SidebarMenu::new() - .p_2() - .children(pages.iter().enumerate().map(|(page_ix, page)| { - let is_page_active = - selected_index.page_ix == page_ix && selected_index.group_ix.is_none(); - SidebarMenuItem::new(page.title.clone()) - .default_open(page.default_open) - .active(is_page_active) - .on_click({ - let state = state.clone(); - move |_, _, cx| { - state.update(cx, |state, cx| { - state.selected_index = SelectIndex { - page_ix, - ..Default::default() - }; - cx.notify(); - }) - } - }) - .when(page.groups.len() > 1, |this| { - this.children( - page.groups - .iter() - .filter(|g| g.title.is_some()) - .enumerate() - .map(|(group_ix, group)| { - let is_active = selected_index.page_ix == page_ix - && selected_index.group_ix == Some(group_ix); - let title = group.title.clone().unwrap_or_default(); + SidebarMenu::new().children(pages.iter().enumerate().map(|(page_ix, page)| { + let is_page_active = + selected_index.page_ix == page_ix && selected_index.group_ix.is_none(); + SidebarMenuItem::new(page.title.clone()) + .default_open(page.default_open) + .active(is_page_active) + .on_click({ + let state = state.clone(); + move |_, _, cx| { + state.update(cx, |state, cx| { + state.selected_index = SelectIndex { + page_ix, + ..Default::default() + }; + cx.notify(); + }) + } + }) + .when(page.groups.len() > 1, |this| { + this.children( + page.groups + .iter() + .filter(|g| g.title.is_some()) + .enumerate() + .map(|(group_ix, group)| { + let is_active = selected_index.page_ix == page_ix + && selected_index.group_ix == Some(group_ix); + let title = group.title.clone().unwrap_or_default(); - SidebarMenuItem::new(title).active(is_active).on_click( - { - let state = state.clone(); - move |_, _, cx| { - state.update(cx, |state, cx| { - state.selected_index = SelectIndex { - page_ix, - group_ix: Some(group_ix), - }; - state.deferred_scroll_group_ix = - Some(group_ix); - cx.notify(); - }) - } - }, - ) - }), - ) - }) - })), + SidebarMenuItem::new(title).active(is_active).on_click({ + let state = state.clone(); + move |_, _, cx| { + state.update(cx, |state, cx| { + state.selected_index = SelectIndex { + page_ix, + group_ix: Some(group_ix), + }; + state.deferred_scroll_group_ix = Some(group_ix); + cx.notify(); + }) + } + }) + }), + ) + }) + })), ) } } diff --git a/crates/ui/src/sidebar/mod.rs b/crates/ui/src/sidebar/mod.rs index 3569edf7..34e56e71 100644 --- a/crates/ui/src/sidebar/mod.rs +++ b/crates/ui/src/sidebar/mod.rs @@ -194,7 +194,6 @@ impl RenderOnce for Sidebar { .w(DEFAULT_WIDTH) .flex_shrink_0() .h_full() - .gap_3() .overflow_hidden() .relative() .bg(cx.theme().sidebar)