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},
checkbox::Checkbox,
group_box::GroupBox,
h_flex,
radio::{Radio, RadioGroup},
switch::Switch,
text::TextView,
v_flex, ActiveTheme as _, StyledExt,
};
@ -24,7 +26,8 @@ impl super::Story for GroupBoxStory {
}
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> {
@ -58,21 +61,57 @@ impl Render for GroupBoxStory {
.justify_center()
.gap_4()
.child(
section("A simple GroupBox").w_128().child(
section("Default Style").w_128().child(
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("account-activity").label("Account Activity"))
.child(Button::new("ok").primary().label("Send Mail")),
.child(Button::new("ok").primary().label("Update Subscriptions")),
),
)
.child(
section("Outline style").w_128().child(
GroupBox::new().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")),
section("Fill Style").w_128().child(
GroupBox::new()
.id("activity")
.fill()
.title("Contributions & activity")
.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()
.p_5()
.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(
StyleRefinement::default().rounded_xl()
StyleRefinement::default()
.rounded_xl()
.py_3()
.px_4()
.border_2()
.border_2(),
)
.child(TextView::markdown(
"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.",
)),
),

View file

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

View file

@ -1,35 +1,71 @@
use gpui::{
div, prelude::FluentBuilder, relative, AnyElement, App, IntoElement, ParentElement, RenderOnce,
StyleRefinement, Styled, Window,
div, prelude::FluentBuilder, relative, AnyElement, App, ElementId, InteractiveElement as _,
IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window,
};
use smallvec::SmallVec;
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
/// an optional title to groups related content together.
#[derive(IntoElement)]
pub struct GroupBox {
id: Option<ElementId>,
variant: GroupBoxVariant,
style: StyleRefinement,
title_style: StyleRefinement,
title: Option<AnyElement>,
content_style: StyleRefinement,
outline: bool,
children: SmallVec<[AnyElement; 1]>,
}
impl GroupBox {
pub fn new() -> Self {
Self {
id: None,
variant: GroupBoxVariant::default(),
style: StyleRefinement::default(),
title_style: StyleRefinement::default(),
content_style: StyleRefinement::default(),
title: None,
outline: false,
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.
pub fn title(mut self, title: impl IntoElement) -> Self {
self.title = Some(title.into_any_element());
@ -47,14 +83,6 @@ impl GroupBox {
self.content_style = style;
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 {
@ -71,15 +99,22 @@ impl Styled for GroupBox {
impl RenderOnce for GroupBox {
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()
.id(self.id.unwrap_or("group-box".into()))
.size_full()
.gap_2()
.when(has_paddings, |this| this.gap_3())
.when(!has_paddings, |this| this.gap_4())
.refine_style(&self.style)
.when_some(self.title, |this, title| {
this.child(
div()
.px_4()
.text_color(cx.theme().group_box_foreground)
.text_color(cx.theme().muted_foreground)
.line_height(relative(1.))
.refine_style(&self.title_style)
.child(title),
@ -87,13 +122,11 @@ impl RenderOnce for GroupBox {
})
.child(
v_flex()
.when(!self.outline, |this| this.bg(cx.theme().group_box))
.when(self.outline, |this| {
this.border_color(cx.theme().border).border_1()
})
.when_some(bg, |this, bg| this.bg(bg))
.when_some(border, |this, border| this.border_color(border).border_1())
.text_color(cx.theme().group_box_foreground)
.p_4()
.gap_3()
.when(has_paddings, |this| this.p_4())
.gap_4()
.rounded(cx.theme().radius)
.refine_style(&self.content_style)
.children(self.children),