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:
parent
adf6f70185
commit
83aecf1293
4 changed files with 175 additions and 11 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
98
crates/ui/src/button_group.rs
Normal file
98
crates/ui/src/button_group.rs
Normal 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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue