button: Improve ButtonGroup to add style, size methods. (#241)

- Add `ButtonStyled` trait to apply the style for Button and
ButtonGroup.
This commit is contained in:
Jason Lee 2024-09-13 12:00:47 +08:00 committed by GitHub
parent b83ece3cd8
commit a8d4abea69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 211 additions and 148 deletions

View file

@ -9,7 +9,7 @@ use story::{
SwitchStory, TableStory, TextStory, TooltipStory,
};
use ui::{
button::Button,
button::{Button, ButtonStyled as _},
color_picker::{ColorPicker, ColorPickerEvent},
dock::{DockArea, DockEvent, DockItem, DockItemState},
h_flex,

View file

@ -4,7 +4,7 @@ use gpui::{
};
use ui::{
button::{Button, ButtonCustomStyle},
button::{Button, ButtonCustomStyle, ButtonStyled as _},
button_group::ButtonGroup,
checkbox::Checkbox,
h_flex,
@ -21,7 +21,7 @@ pub struct ButtonStory {
loading: bool,
selected: bool,
compact: bool,
multiple: bool,
toggle_multiple: bool,
}
impl ButtonStory {
@ -32,7 +32,7 @@ impl ButtonStory {
loading: false,
selected: false,
compact: false,
multiple: false,
toggle_multiple: false,
})
}
@ -71,7 +71,7 @@ impl Render for ButtonStory {
let loading = self.loading;
let selected = self.selected;
let compact = self.compact;
let multiple = self.multiple;
let toggle_multiple = self.toggle_multiple;
v_flex()
.gap_6()
@ -436,79 +436,81 @@ impl Render for ButtonStory {
),
)
.child(
section("Button Group", cx).child(
ButtonGroup::new("button-group")
.disabled(disabled)
.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("Toggle Button Group", cx)
section("Button Group", cx)
.child(
Checkbox::new("multiple-button")
.label("Multiple")
.checked(self.multiple)
.on_click(cx.listener(|view, _, cx| {
view.multiple = !view.multiple;
cx.notify();
})),
ButtonGroup::new("button-group")
.small()
.disabled(disabled)
.child(
Button::new("button-one", cx)
.label("One")
.disabled(disabled)
.selected(selected)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
)
.child(
Button::new("button-two", cx)
.label("Two")
.disabled(disabled)
.selected(selected)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
)
.child(
Button::new("button-three", cx)
.label("Three")
.disabled(disabled)
.selected(selected)
.when(compact, |this| this.compact())
.on_click(Self::on_click),
),
)
.child(
ButtonGroup::new("toggle-button-group")
.multiple(multiple)
h_flex()
.gap_2()
.child(
Button::new("disabled-toggle-button", cx)
.label("Disabled")
.selected(disabled),
Checkbox::new("multiple-button")
.label("Multiple")
.checked(toggle_multiple)
.on_click(cx.listener(|view, _, cx| {
view.toggle_multiple = !view.toggle_multiple;
cx.notify();
})),
)
.child(
Button::new("loading-toggle-button", cx)
.label("Loading")
.selected(loading),
)
.child(
Button::new("selected-toggle-button", cx)
.label("Selected")
.selected(selected),
)
.child(
Button::new("compact-toggle-button", cx)
.label("Compact")
.selected(compact),
)
.on_click(cx.listener(|view, selected: &Vec<usize>, cx| {
view.disabled = selected.contains(&0);
view.loading = selected.contains(&1);
view.selected = selected.contains(&2);
view.compact = selected.contains(&3);
cx.notify();
})),
ButtonGroup::new("toggle-button-group")
.primary()
.compact()
.multiple(toggle_multiple)
.child(
Button::new("disabled-toggle-button", cx)
.label("Disabled")
.selected(disabled),
)
.child(
Button::new("loading-toggle-button", cx)
.label("Loading")
.selected(loading),
)
.child(
Button::new("selected-toggle-button", cx)
.label("Selected")
.selected(selected),
)
.child(
Button::new("compact-toggle-button", cx)
.label("Compact")
.selected(compact),
)
.on_click(cx.listener(|view, selected: &Vec<usize>, cx| {
view.disabled = selected.contains(&0);
view.loading = selected.contains(&1);
view.selected = selected.contains(&2);
view.compact = selected.contains(&3);
cx.notify();
})),
),
),
)
.child(

View file

@ -8,7 +8,7 @@ use gpui::{
};
use ui::{
button::{Button, ButtonStyle},
button::{Button, ButtonStyle, ButtonStyled as _},
checkbox::Checkbox,
date_picker::DatePicker,
h_flex,

View file

@ -6,7 +6,7 @@ use gpui::{
};
use serde::Deserialize;
use ui::{
button::Button,
button::{Button, ButtonStyled as _},
context_menu::ContextMenuExt,
divider::Divider,
h_flex,

View file

@ -34,6 +34,45 @@ pub struct ButtonCustomStyle {
active: Hsla,
}
pub trait ButtonStyled: Sized {
fn with_style(self, style: ButtonStyle) -> Self;
/// With the primary style for the Button.
fn primary(self) -> Self {
self.with_style(ButtonStyle::Primary)
}
/// With the danger style for the Button.
fn danger(self) -> Self {
self.with_style(ButtonStyle::Danger)
}
/// With the outline style for the Button.
fn outline(self) -> Self {
self.with_style(ButtonStyle::Outline)
}
/// With the ghost style for the Button.
fn ghost(self) -> Self {
self.with_style(ButtonStyle::Ghost)
}
/// With the link style for the Button.
fn link(self) -> Self {
self.with_style(ButtonStyle::Link)
}
/// With the text style for the Button, it will no padding look like a normal text.
fn text(self) -> Self {
self.with_style(ButtonStyle::Text)
}
/// With the custom style for the Button.
fn custom(self, style: ButtonCustomStyle) -> Self {
self.with_style(ButtonStyle::Custom(style))
}
}
impl ButtonCustomStyle {
pub fn new(cx: &WindowContext) -> Self {
Self {
@ -149,48 +188,6 @@ impl Button {
}
}
/// With the primary style for the Button.
pub fn primary(mut self) -> Self {
self.style = ButtonStyle::Primary;
self
}
/// With the secondary style for the Button.
pub fn danger(mut self) -> Self {
self.style = ButtonStyle::Danger;
self
}
/// With the ghost style for the Button.
pub fn ghost(mut self) -> Self {
self.style = ButtonStyle::Ghost;
self
}
/// With the outline style for the Button.
pub fn outline(mut self) -> Self {
self.style = ButtonStyle::Outline;
self
}
/// With the link style for the Button.
pub fn link(mut self) -> Self {
self.style = ButtonStyle::Link;
self
}
/// With the text style for the Button, it will no padding look like a normal text.
pub fn text(mut self) -> Self {
self.style = ButtonStyle::Text;
self
}
/// With the custom style for the Button.
pub fn custom(mut self, custom: ButtonCustomStyle) -> Self {
self.style = ButtonStyle::Custom(custom);
self
}
/// Set the border radius of the Button.
pub fn rounded(mut self, rounded: impl Into<ButtonRounded>) -> Self {
self.rounded = rounded.into();
@ -277,6 +274,13 @@ impl Sizable for Button {
}
}
impl ButtonStyled for Button {
fn with_style(mut self, style: ButtonStyle) -> Self {
self.style = style;
self
}
}
impl Styled for Button {
fn style(&mut self) -> &mut gpui::StyleRefinement {
self.base.style()

View file

@ -4,7 +4,10 @@ use gpui::{
};
use std::{cell::Cell, rc::Rc};
use crate::{button::Button, Disableable};
use crate::{
button::{Button, ButtonStyle, ButtonStyled},
Disableable, Sizable, Size,
};
#[derive(IntoElement)]
pub struct ButtonGroup {
@ -13,6 +16,12 @@ pub struct ButtonGroup {
children: Vec<Button>,
multiple: bool,
disabled: bool,
// The button props
compact: Option<bool>,
style: Option<ButtonStyle>,
size: Option<Size>,
on_click: Option<Box<dyn Fn(&Vec<usize>, &mut WindowContext) + 'static>>,
}
@ -30,6 +39,9 @@ impl ButtonGroup {
base: div(),
children: Vec::new(),
id: id.into(),
style: None,
size: None,
compact: None,
multiple: false,
disabled: false,
on_click: None,
@ -42,34 +54,56 @@ impl ButtonGroup {
self
}
/// Sets the multiple selection mode.
/// With the multiple selection mode.
pub fn multiple(mut self, multiple: bool) -> Self {
self.multiple = multiple;
self
}
/// With the compact mode for the ButtonGroup.
pub fn compact(mut self) -> Self {
self.compact = Some(true);
self
}
/// Sets the on_click handler for the ButtonGroup.
///
/// The handler first argument is a vector of the selected button indices.
pub fn on_click(mut self, handler: impl Fn(&Vec<usize>, &mut WindowContext) + 'static) -> Self {
self.on_click = Some(Box::new(handler));
self
}
}
impl Sizable for ButtonGroup {
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.size = Some(size.into());
self
}
}
impl Styled for ButtonGroup {
fn style(&mut self) -> &mut gpui::StyleRefinement {
self.base.style()
}
}
impl ButtonStyled for ButtonGroup {
fn with_style(mut self, style: ButtonStyle) -> Self {
self.style = Some(style);
self
}
}
impl RenderOnce for ButtonGroup {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let children_len = self.children.len();
let mut selected: Vec<usize> = Vec::new();
let shared_state = Rc::new(Cell::new(None)); // Shared state to store the child index
let mut selected_ixs: Vec<usize> = Vec::new();
let state = Rc::new(Cell::new(None));
for (child_index, child) in self.children.iter().enumerate() {
for (ix, child) in self.children.iter().enumerate() {
if child.selected {
selected.push(child_index);
selected_ixs.push(ix);
}
}
@ -82,7 +116,7 @@ impl RenderOnce for ButtonGroup {
.into_iter()
.enumerate()
.map(|(child_index, child)| {
let shared_state_clone = Rc::clone(&shared_state);
let state = Rc::clone(&state);
let child = if children_len == 1 {
child
} else if child_index == 0 {
@ -127,8 +161,11 @@ impl RenderOnce for ButtonGroup {
})
}
.stop_propagation(false)
.on_click(move |_, cx| {
shared_state_clone.set(Some(child_index));
.when_some(self.size, |this, size| this.with_size(size))
.when_some(self.style, |this, style| this.style(style))
.when_some(self.compact, |this, _| this.compact())
.on_click(move |_, _| {
state.set(Some(child_index));
});
child
@ -138,21 +175,21 @@ impl RenderOnce for ButtonGroup {
self.on_click.filter(|_| !self.disabled),
move |this, on_click| {
this.on_click(move |_, cx| {
let mut selected = selected.clone();
if let Some(index) = shared_state.get() {
let mut selected_ixs = selected_ixs.clone();
if let Some(ix) = state.get() {
if self.multiple {
if let Some(pos) = selected.iter().position(|&i| i == index) {
selected.remove(pos); // Toggle off if already selected
if let Some(pos) = selected_ixs.iter().position(|&i| i == ix) {
selected_ixs.remove(pos);
} else {
selected.push(index); // Toggle on if not selected
selected_ixs.push(ix);
}
} else {
selected.clear(); // Clear the existing selection
selected.push(index); // Replace with the new selection
selected_ixs.clear();
selected_ixs.push(ix);
}
}
on_click(&selected, cx);
on_click(&selected_ixs, cx);
})
},
)

View file

@ -3,7 +3,10 @@ use gpui::{
RenderOnce, SharedString, Styled, WindowContext,
};
use crate::{button::Button, h_flex, IconName, Sizable};
use crate::{
button::{Button, ButtonStyled as _},
h_flex, IconName, Sizable as _,
};
#[derive(IntoElement)]
pub struct Clipboard {

View file

@ -9,7 +9,7 @@ use gpui::{
use rust_i18n::t;
use crate::{
button::Button,
button::{Button, ButtonStyled as _},
dock::DockItemInfo,
h_flex,
popup_menu::{PopupMenu, PopupMenuExt},

View file

@ -8,8 +8,13 @@ use gpui::{
};
use crate::{
button::Button, h_flex, modal::overlay_color, root::ContextModal as _, scroll::ScrollbarAxis,
theme::ActiveTheme, v_flex, IconName, Placement, Sizable, StyledExt as _,
button::{Button, ButtonStyled as _},
h_flex,
modal::overlay_color,
root::ContextModal as _,
scroll::ScrollbarAxis,
theme::ActiveTheme,
v_flex, IconName, Placement, Sizable, StyledExt as _,
};
#[derive(IntoElement)]

View file

@ -1,6 +1,9 @@
use gpui::{px, WindowContext};
use crate::{button::Button, IconName, Sizable as _};
use crate::{
button::{Button, ButtonStyled as _},
IconName, Sizable as _,
};
pub(crate) struct ClearButton {}

View file

@ -8,8 +8,10 @@ use gpui::{
};
use crate::{
animation::cubic_bezier, button::Button, theme::ActiveTheme as _, v_flex, ContextModal,
IconName, Sizable as _,
animation::cubic_bezier,
button::{Button, ButtonStyled as _},
theme::ActiveTheme as _,
v_flex, ContextModal, IconName, Sizable as _,
};
actions!(modal, [Escape]);

View file

@ -8,8 +8,11 @@ use gpui::{
use smol::Timer;
use crate::{
animation::cubic_bezier, button::Button, h_flex, theme::ActiveTheme as _, v_flex, Icon,
IconName, Sizable as _, StyledExt,
animation::cubic_bezier,
button::{Button, ButtonStyled as _},
h_flex,
theme::ActiveTheme as _,
v_flex, Icon, IconName, Sizable as _, StyledExt,
};
pub enum NotificationType {

View file

@ -182,8 +182,8 @@ impl Colors {
popover: hsl(0.0, 0.0, 100.0),
popover_foreground: hsl(240.0, 10.0, 3.9),
primary: hsl(223.0, 5.9, 10.0),
primary_hover: hsl(223.0, 5.9, 30.0),
primary_active: hsl(223.0, 5.9, 45.0),
primary_hover: hsl(223.0, 5.9, 15.0),
primary_active: hsl(223.0, 1.9, 25.0),
primary_foreground: hsl(223.0, 0.0, 98.0),
secondary: hsl(240.0, 4.8, 95.9),
secondary_hover: hsl(240.0, 5.8, 10.).opacity(0.05),
@ -224,8 +224,8 @@ impl Colors {
popover: hsl(240.0, 10.0, 3.9),
popover_foreground: hsl(0.0, 0.0, 98.0),
primary: hsl(223.0, 0.0, 98.0),
primary_hover: hsl(223.0, 0.0, 85.0),
primary_active: hsl(223.0, 0.0, 60.0),
primary_hover: hsl(223.0, 0.0, 90.0),
primary_active: hsl(223.0, 0.0, 80.0),
primary_foreground: hsl(223.0, 5.9, 10.0),
secondary: hsl(240.0, 3.7, 15.9),
secondary_hover: hsl(240.0, 3.7, 20.9).opacity(0.5),

View file

@ -9,7 +9,10 @@ use gpui::{
use rust_i18n::t;
use crate::{
button::Button, h_flex, theme::ActiveTheme, v_flex, Disableable as _, IconName, Selectable,
button::{Button, ButtonStyled as _},
h_flex,
theme::ActiveTheme,
v_flex, Disableable as _, IconName, Selectable,
};
use super::utils::days_in_month;
@ -593,7 +596,8 @@ impl Calendar {
let active = (ix + 1) as u8 == self.current_month;
self.item_button(ix, month.to_string(), active, false, false, cx)
.w(relative(0.3)).text_sm()
.w(relative(0.3))
.text_sm()
.on_click(cx.listener(move |view, _, cx| {
view.current_month = (ix + 1) as u8;
view.set_view_mode(ViewMode::Day, cx);