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::{
|
use ui::{
|
||||||
button::{Button, ButtonCustomStyle},
|
button::{Button, ButtonCustomStyle},
|
||||||
|
button_group::ButtonGroup,
|
||||||
checkbox::Checkbox,
|
checkbox::Checkbox,
|
||||||
h_flex,
|
h_flex,
|
||||||
prelude::FluentBuilder,
|
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(
|
.child(
|
||||||
section("Icon Button", cx)
|
section("Icon Button", cx)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ use crate::{
|
||||||
Disableable, Icon, Selectable, Sizable, Size,
|
Disableable, Icon, Selectable, Sizable, Size,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Div, ElementId,
|
div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Corners, Div, Edges,
|
||||||
FocusHandle, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
|
ElementId, FocusHandle, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement,
|
||||||
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, WindowContext,
|
Pixels, RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum ButtonRounded {
|
pub enum ButtonRounded {
|
||||||
|
|
@ -109,6 +109,8 @@ pub struct Button {
|
||||||
selected: bool,
|
selected: bool,
|
||||||
style: ButtonStyle,
|
style: ButtonStyle,
|
||||||
rounded: ButtonRounded,
|
rounded: ButtonRounded,
|
||||||
|
border_corners: Corners<bool>,
|
||||||
|
border_edges: Edges<bool>,
|
||||||
size: Size,
|
size: Size,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
tooltip: Option<SharedString>,
|
tooltip: Option<SharedString>,
|
||||||
|
|
@ -134,6 +136,8 @@ impl Button {
|
||||||
selected: false,
|
selected: false,
|
||||||
style: ButtonStyle::Secondary,
|
style: ButtonStyle::Secondary,
|
||||||
rounded: ButtonRounded::Medium,
|
rounded: ButtonRounded::Medium,
|
||||||
|
border_corners: Corners::all(true),
|
||||||
|
border_edges: Edges::all(true),
|
||||||
size: Size::Medium,
|
size: Size::Medium,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
|
|
@ -191,6 +195,18 @@ impl Button {
|
||||||
self
|
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.
|
/// 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 {
|
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||||
self.label = Some(label.into());
|
self.label = Some(label.into());
|
||||||
|
|
@ -310,13 +326,30 @@ impl RenderOnce for Button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|this| match self.rounded {
|
.when(
|
||||||
ButtonRounded::Small => this.rounded(px(cx.theme().radius * 0.5)),
|
self.border_corners.top_left && self.border_corners.bottom_left,
|
||||||
ButtonRounded::Medium => this.rounded(px(cx.theme().radius)),
|
|this| match self.rounded {
|
||||||
ButtonRounded::Large => this.rounded(px(cx.theme().radius * 2.0)),
|
ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)),
|
||||||
ButtonRounded::Size(px) => this.rounded(px),
|
ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)),
|
||||||
ButtonRounded::None => this.rounded_none(),
|
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)
|
.text_color(normal_style.fg)
|
||||||
.when(self.selected, |this| {
|
.when(self.selected, |this| {
|
||||||
let selected_style = style.selected(cx);
|
let selected_style = style.selected(cx);
|
||||||
|
|
@ -361,7 +394,6 @@ impl RenderOnce for Button {
|
||||||
.text_color(disabled_style.fg)
|
.text_color(disabled_style.fg)
|
||||||
.border_color(disabled_style.border)
|
.border_color(disabled_style.border)
|
||||||
})
|
})
|
||||||
.border_1()
|
|
||||||
.child({
|
.child({
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("label")
|
.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 animation;
|
||||||
pub mod button;
|
pub mod button;
|
||||||
|
pub mod button_group;
|
||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
pub mod color_picker;
|
pub mod color_picker;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue