setting: Add scrollbar to SettingPage. (#1660)

This commit is contained in:
Jason Lee 2025-11-21 23:04:56 +08:00 committed by GitHub
parent 857f7973e3
commit fc36451a27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 104 additions and 76 deletions

View file

@ -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<impl Render> {
Self::view(window, cx)
}
fn paddings() -> gpui::Pixels {
px(0.)
}
}
impl SettingsStory {

View file

@ -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(

View file

@ -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::<Vec<_>>();
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)),
),
)
}
}

View file

@ -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<SettingPage> {
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();
})
}
})
}),
)
})
})),
)
}
}

View file

@ -194,7 +194,6 @@ impl<E: Collapsible + IntoElement> RenderOnce for Sidebar<E> {
.w(DEFAULT_WIDTH)
.flex_shrink_0()
.h_full()
.gap_3()
.overflow_hidden()
.relative()
.bg(cx.theme().sidebar)