From c523b3485bd0638e580df9fa7f1a884ad3d900e4 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 21 Nov 2025 22:44:35 +0800 Subject: [PATCH] sidebar: Refactor Sidebar to support `Styled`. (#1658) ## Break Change - The `width`, `border_width` method has been removed from Sidebar, use `Styled` trait to use `w`, `border` method from GPUI instead. ```diff Sidebar::left() - .width(relative(1.)) - .border_width(px(0.)) + .w(relative(1.)) + .border_0() .collapsed(false) ``` --- crates/story/src/main.rs | 4 +- crates/story/src/sidebar_story.rs | 5 ++- crates/ui/src/setting/settings.rs | 4 +- crates/ui/src/sidebar/group.rs | 1 - crates/ui/src/sidebar/header.rs | 28 +++++++++---- crates/ui/src/sidebar/mod.rs | 67 +++++++++++++++++++------------ 6 files changed, 68 insertions(+), 41 deletions(-) diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index f35b0481..1a02f8ff 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -158,8 +158,8 @@ impl Render for Gallery { .size_range(px(200.)..px(320.)) .child( Sidebar::left() - .width(relative(1.)) - .border_width(px(0.)) + .w(relative(1.)) + .border_0() .collapsed(self.collapsed) .header( v_flex() diff --git a/crates/story/src/sidebar_story.rs b/crates/story/src/sidebar_story.rs index b32b0d2c..e88f9f23 100644 --- a/crates/story/src/sidebar_story.rs +++ b/crates/story/src/sidebar_story.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use gpui::{ Action, App, AppContext, ClickEvent, Context, Entity, Focusable, IntoElement, ParentElement, - Render, SharedString, Styled, Window, div, prelude::FluentBuilder, relative, + Render, SharedString, Styled, Window, div, prelude::FluentBuilder, px, relative, }; use gpui_component::{ @@ -280,9 +280,10 @@ impl Render for SidebarStory { .child( Sidebar::new(self.side) .collapsed(self.collapsed) + .w(px(220.)) + .p_3() .header( SidebarHeader::new() - .w_full() .child( div() .flex() diff --git a/crates/ui/src/setting/settings.rs b/crates/ui/src/setting/settings.rs index 823b938e..fba9eee4 100644 --- a/crates/ui/src/setting/settings.rs +++ b/crates/ui/src/setting/settings.rs @@ -136,8 +136,8 @@ impl Settings { let search_input = state.read(cx).search_input.clone(); Sidebar::left() - .width(relative(1.)) - .border_width(px(0.)) + .w(relative(1.)) + .border_0() .collapsed(false) .header( div() diff --git a/crates/ui/src/sidebar/group.rs b/crates/ui/src/sidebar/group.rs index feb5a9fc..43b1578e 100644 --- a/crates/ui/src/sidebar/group.rs +++ b/crates/ui/src/sidebar/group.rs @@ -54,7 +54,6 @@ impl RenderOnce for SidebarGroup { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { v_flex() .relative() - .p_2() .when(!self.collapsed, |this| { this.child( div() diff --git a/crates/ui/src/sidebar/header.rs b/crates/ui/src/sidebar/header.rs index 951073ef..3e1aff6b 100644 --- a/crates/ui/src/sidebar/header.rs +++ b/crates/ui/src/sidebar/header.rs @@ -1,14 +1,16 @@ use gpui::{ - prelude::FluentBuilder as _, Div, InteractiveElement, IntoElement, ParentElement, RenderOnce, - Styled, + div, prelude::FluentBuilder as _, AnyElement, Div, InteractiveElement, IntoElement, + ParentElement, RenderOnce, StyleRefinement, Styled, }; -use crate::{h_flex, menu::DropdownMenu, ActiveTheme as _, Collapsible, Selectable}; +use crate::{menu::DropdownMenu, ActiveTheme as _, Collapsible, Selectable, StyledExt}; /// Header for the [`super::Sidebar`] #[derive(IntoElement)] pub struct SidebarHeader { base: Div, + style: StyleRefinement, + children: Vec, selected: bool, collapsed: bool, } @@ -17,13 +19,21 @@ impl SidebarHeader { /// Create a new [`SidebarHeader`]. pub fn new() -> Self { Self { - base: h_flex().gap_2().w_full(), + base: div(), + style: StyleRefinement::default(), + children: Vec::new(), selected: false, collapsed: false, } } } +impl Default for SidebarHeader { + fn default() -> Self { + Self::new() + } +} + impl Selectable for SidebarHeader { fn selected(mut self, selected: bool) -> Self { self.selected = selected; @@ -48,13 +58,13 @@ impl Collapsible for SidebarHeader { impl ParentElement for SidebarHeader { fn extend(&mut self, elements: impl IntoIterator) { - self.base.extend(elements); + self.children.extend(elements); } } impl Styled for SidebarHeader { fn style(&mut self) -> &mut gpui::StyleRefinement { - self.base.style() + &mut self.style } } @@ -68,13 +78,15 @@ impl DropdownMenu for SidebarHeader {} impl RenderOnce for SidebarHeader { fn render(self, _: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement { - h_flex() + self.base .id("sidebar-header") + .h_flex() .gap_2() .p_2() .w_full() .justify_between() .rounded(cx.theme().radius) + .refine_style(&self.style) .hover(|this| { this.bg(cx.theme().sidebar_accent) .text_color(cx.theme().sidebar_accent_foreground) @@ -83,6 +95,6 @@ impl RenderOnce for SidebarHeader { this.bg(cx.theme().sidebar_accent) .text_color(cx.theme().sidebar_accent_foreground) }) - .child(self.base) + .children(self.children) } } diff --git a/crates/ui/src/sidebar/mod.rs b/crates/ui/src/sidebar/mod.rs index 9a03ccbe..3569edf7 100644 --- a/crates/ui/src/sidebar/mod.rs +++ b/crates/ui/src/sidebar/mod.rs @@ -5,8 +5,9 @@ use crate::{ v_flex, ActiveTheme, Collapsible, Icon, IconName, Side, Sizable, StyledExt, }; use gpui::{ - div, prelude::FluentBuilder, px, AnyElement, App, ClickEvent, DefiniteLength, - InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, Styled, Window, + div, prelude::FluentBuilder, px, AnyElement, App, ClickEvent, EdgesRefinement, + InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, StyleRefinement, + Styled, Window, }; use std::rc::Rc; @@ -25,6 +26,7 @@ const COLLAPSED_WIDTH: Pixels = px(48.); /// A Sidebar element that can contain collapsible child elements. #[derive(IntoElement)] pub struct Sidebar { + style: StyleRefinement, content: Vec, /// header view header: Option, @@ -33,8 +35,6 @@ pub struct Sidebar { /// The side of the sidebar side: Side, collapsible: bool, - width: DefiniteLength, - border_width: Pixels, collapsed: bool, } @@ -42,13 +42,12 @@ impl Sidebar { /// Create a new Sidebar on the given [`Side`]. pub fn new(side: Side) -> Self { Self { + style: StyleRefinement::default(), content: vec![], header: None, footer: None, side, collapsible: true, - width: DEFAULT_WIDTH.into(), - border_width: px(1.), collapsed: false, } } @@ -63,18 +62,6 @@ impl Sidebar { Self::new(Side::Right) } - /// Set the width of the sidebar - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self - } - - /// Set border width of the sidebar - pub fn border_width(mut self, border_width: impl Into) -> Self { - self.border_width = border_width.into(); - self - } - /// Set the sidebar to be collapsible, default is true pub fn collapsible(mut self, collapsible: bool) -> Self { self.collapsible = collapsible; @@ -192,41 +179,69 @@ impl RenderOnce for SidebarToggleButton { } } +impl Styled for Sidebar { + fn style(&mut self) -> &mut StyleRefinement { + &mut self.style + } +} + impl RenderOnce for Sidebar { fn render(mut self, _: &mut Window, cx: &mut App) -> impl IntoElement { + self.style.padding = EdgesRefinement::default(); + v_flex() .id("sidebar") - .w(self.width) - .when(self.collapsed, |this| this.w(COLLAPSED_WIDTH)) + .w(DEFAULT_WIDTH) .flex_shrink_0() .h_full() + .gap_3() .overflow_hidden() .relative() .bg(cx.theme().sidebar) .text_color(cx.theme().sidebar_foreground) .border_color(cx.theme().sidebar_border) .map(|this| match self.side { - Side::Left => this.border_r(self.border_width), - Side::Right => this.border_l(self.border_width), + Side::Left => this.border_r_1(), + Side::Right => this.border_l_1(), }) + .refine_style(&self.style) + .when(self.collapsed, |this| this.w(COLLAPSED_WIDTH).gap_2()) .when_some(self.header.take(), |this, header| { - this.child(h_flex().id("header").p_2().gap_2().child(header)) + this.child( + h_flex() + .id("header") + .pt_3() + .px_3() + .gap_2() + .when(self.collapsed, |this| this.pt_2().px_2()) + .child(header), + ) }) .child( v_flex().id("content").flex_1().min_h_0().child( - div() + v_flex() + .gap_3() + .p_3() + .when(self.collapsed, |this| this.p_2()) .children( self.content .into_iter() .enumerate() .map(|(ix, c)| div().id(ix).child(c.collapsed(self.collapsed))), ) - .gap_2() .scrollable(ScrollbarAxis::Vertical), ), ) .when_some(self.footer.take(), |this, footer| { - this.child(h_flex().id("footer").gap_2().p_2().child(footer)) + this.child( + h_flex() + .id("footer") + .pb_3() + .px_3() + .gap_2() + .when(self.collapsed, |this| this.pt_2().px_2()) + .child(footer), + ) }) } }