group_box: Update GroupBox style. (#1167)

- Removed padding x of the Group Title.
- Added `fill` variant and change default to use no background and
paddings style.
 
<img width="1120" height="1337" alt="image"
src="https://github.com/user-attachments/assets/958e902f-3481-424c-a923-62746175aabf"
/>
This commit is contained in:
Jason Lee 2025-08-21 18:17:58 +08:00 committed by GitHub
parent 554082b969
commit 4e5aca3842
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 114 additions and 36 deletions

View file

@ -7,7 +7,9 @@ use gpui_component::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
checkbox::Checkbox, checkbox::Checkbox,
group_box::GroupBox, group_box::GroupBox,
h_flex,
radio::{Radio, RadioGroup}, radio::{Radio, RadioGroup},
switch::Switch,
text::TextView, text::TextView,
v_flex, ActiveTheme as _, StyledExt, v_flex, ActiveTheme as _, StyledExt,
}; };
@ -24,7 +26,8 @@ impl super::Story for GroupBoxStory {
} }
fn description() -> &'static str { fn description() -> &'static str {
"A styled container element that with an optional title to groups related content together." "A styled container element that with an optional title \
to groups related content together."
} }
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable> { fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable> {
@ -58,21 +61,57 @@ impl Render for GroupBoxStory {
.justify_center() .justify_center()
.gap_4() .gap_4()
.child( .child(
section("A simple GroupBox").w_128().child( section("Default Style").w_128().child(
GroupBox::new() GroupBox::new()
.title(Checkbox::new("all").label("Subscribe All")) .child("Subscriptions")
.child(Checkbox::new("all").label("All"))
.child(Checkbox::new("news-letter").label("News Letter")) .child(Checkbox::new("news-letter").label("News Letter"))
.child(Checkbox::new("account-activity").label("Account Activity")) .child(Checkbox::new("account-activity").label("Account Activity"))
.child(Button::new("ok").primary().label("Send Mail")), .child(Button::new("ok").primary().label("Update Subscriptions")),
), ),
) )
.child( .child(
section("Outline style").w_128().child( section("Fill Style").w_128().child(
GroupBox::new().outline().title("Appearance").child( GroupBox::new()
RadioGroup::vertical("theme") .id("activity")
.child(Radio::new("light").label("Light")) .fill()
.child(Radio::new("dark").label("Dark")) .title("Contributions & activity")
.child(Radio::new("system").label("System")), .child(
h_flex()
.justify_between()
.child("Make profile private and hide activity")
.child(Switch::new("toggle-0").checked(true)),
)
.child(
h_flex()
.justify_between()
.child("Include private contributions on my profile")
.child(Switch::new("toggle-1").checked(false)),
)
.child(Button::new("btn-1").primary().label("Save")),
),
)
.child(
section("Outline Style").w_128().child(
GroupBox::new()
.id("appearance")
.outline()
.title("Appearance")
.child(
RadioGroup::vertical("theme")
.child(Radio::new("light").label("Light"))
.child(Radio::new("dark").label("Dark"))
.child(Radio::new("system").label("System")),
),
),
)
.child(
section("Without Title").w_128().child(
GroupBox::new().outline().child(
h_flex()
.justify_between()
.child("Make profile private and hide activity")
.child(Switch::new("toggle-1").checked(true)),
), ),
), ),
) )
@ -84,16 +123,23 @@ impl Render for GroupBoxStory {
.rounded_xl() .rounded_xl()
.p_5() .p_5()
.title("This is a custom style") .title("This is a custom style")
.title_style(StyleRefinement::default().font_semibold().line_height(relative(1.0)).px_3()) .title_style(
StyleRefinement::default()
.font_semibold()
.line_height(relative(1.0))
.px_3(),
)
.content_style( .content_style(
StyleRefinement::default().rounded_xl() StyleRefinement::default()
.rounded_xl()
.py_3() .py_3()
.px_4() .px_4()
.border_2() .border_2(),
) )
.child(TextView::markdown( .child(TextView::markdown(
"custom-style", "custom-style",
"You can use `title_style` to customize the style of the title. \n \ "You can use `title_style` to customize the style \
of the title. \n \
And any style in `GroupBox` will apply to the content container.", And any style in `GroupBox` will apply to the content container.",
)), )),
), ),

View file

@ -408,7 +408,6 @@ impl RenderOnce for StorySection {
.gap_4() .gap_4()
.child(self.title), .child(self.title),
) )
.title_style(StyleRefinement::default().px_2())
.content_style( .content_style(
StyleRefinement::default() StyleRefinement::default()
.rounded_lg() .rounded_lg()

View file

@ -1,35 +1,71 @@
use gpui::{ use gpui::{
div, prelude::FluentBuilder, relative, AnyElement, App, IntoElement, ParentElement, RenderOnce, div, prelude::FluentBuilder, relative, AnyElement, App, ElementId, InteractiveElement as _,
StyleRefinement, Styled, Window, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{v_flex, ActiveTheme, StyledExt as _}; use crate::{v_flex, ActiveTheme, StyledExt as _};
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
pub enum GroupBoxVariant {
#[default]
Normal,
Fill,
Outline,
}
/// GroupBox is a styled container element that with /// GroupBox is a styled container element that with
/// an optional title to groups related content together. /// an optional title to groups related content together.
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct GroupBox { pub struct GroupBox {
id: Option<ElementId>,
variant: GroupBoxVariant,
style: StyleRefinement, style: StyleRefinement,
title_style: StyleRefinement, title_style: StyleRefinement,
title: Option<AnyElement>, title: Option<AnyElement>,
content_style: StyleRefinement, content_style: StyleRefinement,
outline: bool,
children: SmallVec<[AnyElement; 1]>, children: SmallVec<[AnyElement; 1]>,
} }
impl GroupBox { impl GroupBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
id: None,
variant: GroupBoxVariant::default(),
style: StyleRefinement::default(), style: StyleRefinement::default(),
title_style: StyleRefinement::default(), title_style: StyleRefinement::default(),
content_style: StyleRefinement::default(), content_style: StyleRefinement::default(),
title: None, title: None,
outline: false,
children: SmallVec::new(), children: SmallVec::new(),
} }
} }
/// Set the variant of the group box.
pub fn variant(mut self, variant: GroupBoxVariant) -> Self {
self.variant = variant;
self
}
/// Set to use Fill variant.
pub fn fill(mut self) -> Self {
self.variant = GroupBoxVariant::Fill;
self
}
/// Set use outline style of the group box.
///
/// If true, the group box will have a border around it, and no background color.
pub fn outline(mut self) -> Self {
self.variant = GroupBoxVariant::Outline;
self
}
/// Set the id of the group box, default is None.
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
self.id = Some(id.into());
self
}
/// Set the title of the group box, default is None. /// Set the title of the group box, default is None.
pub fn title(mut self, title: impl IntoElement) -> Self { pub fn title(mut self, title: impl IntoElement) -> Self {
self.title = Some(title.into_any_element()); self.title = Some(title.into_any_element());
@ -47,14 +83,6 @@ impl GroupBox {
self.content_style = style; self.content_style = style;
self self
} }
/// Set use outline style of the group box, default is false.
///
/// If true, the group box will have a border around it, and no background color.
pub fn outline(mut self) -> Self {
self.outline = true;
self
}
} }
impl ParentElement for GroupBox { impl ParentElement for GroupBox {
@ -71,15 +99,22 @@ impl Styled for GroupBox {
impl RenderOnce for GroupBox { impl RenderOnce for GroupBox {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let (bg, border, has_paddings) = match self.variant {
GroupBoxVariant::Normal => (None, None, false),
GroupBoxVariant::Fill => (Some(cx.theme().group_box), None, true),
GroupBoxVariant::Outline => (None, Some(cx.theme().border), true),
};
v_flex() v_flex()
.id(self.id.unwrap_or("group-box".into()))
.size_full() .size_full()
.gap_2() .when(has_paddings, |this| this.gap_3())
.when(!has_paddings, |this| this.gap_4())
.refine_style(&self.style) .refine_style(&self.style)
.when_some(self.title, |this, title| { .when_some(self.title, |this, title| {
this.child( this.child(
div() div()
.px_4() .text_color(cx.theme().muted_foreground)
.text_color(cx.theme().group_box_foreground)
.line_height(relative(1.)) .line_height(relative(1.))
.refine_style(&self.title_style) .refine_style(&self.title_style)
.child(title), .child(title),
@ -87,13 +122,11 @@ impl RenderOnce for GroupBox {
}) })
.child( .child(
v_flex() v_flex()
.when(!self.outline, |this| this.bg(cx.theme().group_box)) .when_some(bg, |this, bg| this.bg(bg))
.when(self.outline, |this| { .when_some(border, |this, border| this.border_color(border).border_1())
this.border_color(cx.theme().border).border_1()
})
.text_color(cx.theme().group_box_foreground) .text_color(cx.theme().group_box_foreground)
.p_4() .when(has_paddings, |this| this.p_4())
.gap_3() .gap_4()
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.refine_style(&self.content_style) .refine_style(&self.content_style)
.children(self.children), .children(self.children),