Add ButtonGroup (#221)

<img width="849" alt="image"
src="https://github.com/user-attachments/assets/5f809b40-9d4c-4934-828d-98f77a3f4b29">

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
xda 2024-09-06 10:32:10 +08:00 committed by GitHub
parent adf6f70185
commit 83aecf1293
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 175 additions and 11 deletions

View file

@ -5,6 +5,7 @@ use gpui::{
use ui::{
button::{Button, ButtonCustomStyle},
button_group::ButtonGroup,
checkbox::Checkbox,
h_flex,
prelude::FluentBuilder,
@ -405,6 +406,38 @@ impl Render for ButtonStory {
),
),
)
.child(
section("Button Group", cx).child(
ButtonGroup::new("button-group")
.child(
Button::new("button-one", cx)
.label("One")
.disabled(disabled)
.selected(selected)
.loading(loading)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
)
.child(
Button::new("button-two", cx)
.label("Two")
.disabled(disabled)
.selected(selected)
.loading(loading)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
)
.child(
Button::new("button-three", cx)
.label("Three")
.disabled(disabled)
.selected(selected)
.loading(loading)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
),
),
)
.child(
section("Icon Button", cx)
.child(

View file

@ -6,9 +6,9 @@ use crate::{
Disableable, Icon, Selectable, Sizable, Size,
};
use gpui::{
div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Div, ElementId,
FocusHandle, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, WindowContext,
div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Corners, Div, Edges,
ElementId, FocusHandle, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement,
Pixels, RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, WindowContext,
};
pub enum ButtonRounded {
@ -109,6 +109,8 @@ pub struct Button {
selected: bool,
style: ButtonStyle,
rounded: ButtonRounded,
border_corners: Corners<bool>,
border_edges: Edges<bool>,
size: Size,
compact: bool,
tooltip: Option<SharedString>,
@ -134,6 +136,8 @@ impl Button {
selected: false,
style: ButtonStyle::Secondary,
rounded: ButtonRounded::Medium,
border_corners: Corners::all(true),
border_edges: Edges::all(true),
size: Size::Medium,
tooltip: None,
on_click: None,
@ -191,6 +195,18 @@ impl Button {
self
}
/// Set the border corners side of the Button.
pub(crate) fn border_corners(mut self, corners: impl Into<Corners<bool>>) -> Self {
self.border_corners = corners.into();
self
}
/// Set the border edges of the Button.
pub(crate) fn border_edges(mut self, edges: impl Into<Edges<bool>>) -> Self {
self.border_edges = edges.into();
self
}
/// Set label to the Button, if no label is set, the button will be in Icon Button mode.
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
@ -310,13 +326,30 @@ impl RenderOnce for Button {
}
}
})
.map(|this| match self.rounded {
ButtonRounded::Small => this.rounded(px(cx.theme().radius * 0.5)),
ButtonRounded::Medium => this.rounded(px(cx.theme().radius)),
ButtonRounded::Large => this.rounded(px(cx.theme().radius * 2.0)),
ButtonRounded::Size(px) => this.rounded(px),
ButtonRounded::None => this.rounded_none(),
})
.when(
self.border_corners.top_left && self.border_corners.bottom_left,
|this| match self.rounded {
ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)),
ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)),
ButtonRounded::Large => this.rounded_l(px(cx.theme().radius * 2.0)),
ButtonRounded::Size(px) => this.rounded_l(px),
ButtonRounded::None => this.rounded_none(),
},
)
.when(
self.border_corners.top_right && self.border_corners.bottom_right,
|this| match self.rounded {
ButtonRounded::Small => this.rounded_r(px(cx.theme().radius * 0.5)),
ButtonRounded::Medium => this.rounded_r(px(cx.theme().radius)),
ButtonRounded::Large => this.rounded_r(px(cx.theme().radius * 2.0)),
ButtonRounded::Size(px) => this.rounded_r(px),
ButtonRounded::None => this.rounded_none(),
},
)
.when(self.border_edges.left, |this| this.border_l_1())
.when(self.border_edges.right, |this| this.border_r_1())
.when(self.border_edges.top, |this| this.border_t_1())
.when(self.border_edges.bottom, |this| this.border_b_1())
.text_color(normal_style.fg)
.when(self.selected, |this| {
let selected_style = style.selected(cx);
@ -361,7 +394,6 @@ impl RenderOnce for Button {
.text_color(disabled_style.fg)
.border_color(disabled_style.border)
})
.border_1()
.child({
h_flex()
.id("label")

View file

@ -0,0 +1,98 @@
use gpui::{
div, Corners, Div, Edges, ElementId, InteractiveElement, IntoElement, ParentElement,
RenderOnce, Styled, WindowContext,
};
use crate::button::Button;
#[derive(IntoElement)]
pub struct ButtonGroup {
pub base: Div,
id: ElementId,
children: Vec<Button>,
}
impl ButtonGroup {
/// Creates a new ButtonGroup.
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
base: div(),
children: Vec::new(),
id: id.into(),
}
}
/// Adds a button as a child to the ButtonGroup.
pub fn child(mut self, child: Button) -> Self {
self.children.push(child);
self
}
}
impl Styled for ButtonGroup {
fn style(&mut self) -> &mut gpui::StyleRefinement {
self.base.style()
}
}
impl RenderOnce for ButtonGroup {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let children_len = self.children.len();
// Render a div container with a flex layout to group buttons horizontally.
self.base
.id(self.id)
.flex()
.items_center()
.children(if children_len > 1 {
self.children
.into_iter()
.enumerate()
.map(|(index, button)| {
if index == 0 {
// First
button
.border_corners(Corners {
top_left: true,
top_right: false,
bottom_left: true,
bottom_right: false,
})
.border_edges(Edges {
left: true,
top: true,
right: true,
bottom: true,
})
} else if index == children_len - 1 {
// Last
button
.border_edges(Edges {
left: false,
top: true,
right: true,
bottom: true,
})
.border_corners(Corners {
top_left: false,
top_right: true,
bottom_left: false,
bottom_right: true,
})
} else {
// Middle
button
.border_corners(Corners::all(false))
.border_edges(Edges {
left: false,
top: true,
right: true,
bottom: true,
})
}
})
.collect()
} else {
self.children
})
}
}

View file

@ -9,6 +9,7 @@ mod time;
pub mod animation;
pub mod button;
pub mod button_group;
pub mod checkbox;
pub mod clipboard;
pub mod color_picker;