button_like.rs

  1use gpui::{rems, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
  2use smallvec::SmallVec;
  3
  4use crate::h_stack;
  5use crate::prelude::*;
  6
  7pub trait ButtonCommon: Clickable + Disableable {
  8    fn id(&self) -> &ElementId;
  9    fn style(self, style: ButtonStyle2) -> Self;
 10    fn size(self, size: ButtonSize2) -> Self;
 11    fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
 12}
 13
 14#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
 15pub enum ButtonStyle2 {
 16    #[default]
 17    Filled,
 18    // Tinted,
 19    Subtle,
 20    Transparent,
 21}
 22
 23#[derive(Debug, Clone)]
 24pub struct ButtonStyle {
 25    pub background: Hsla,
 26    pub border_color: Hsla,
 27    pub label_color: Hsla,
 28    pub icon_color: Hsla,
 29}
 30
 31impl ButtonStyle2 {
 32    pub fn enabled(self, cx: &mut WindowContext) -> ButtonStyle {
 33        match self {
 34            ButtonStyle2::Filled => ButtonStyle {
 35                background: cx.theme().colors().element_background,
 36                border_color: gpui::transparent_black(),
 37                label_color: Color::Default.color(cx),
 38                icon_color: Color::Default.color(cx),
 39            },
 40            ButtonStyle2::Subtle => ButtonStyle {
 41                background: cx.theme().colors().ghost_element_background,
 42                border_color: gpui::transparent_black(),
 43                label_color: Color::Default.color(cx),
 44                icon_color: Color::Default.color(cx),
 45            },
 46            ButtonStyle2::Transparent => ButtonStyle {
 47                background: gpui::transparent_black(),
 48                border_color: gpui::transparent_black(),
 49                label_color: Color::Default.color(cx),
 50                icon_color: Color::Default.color(cx),
 51            },
 52        }
 53    }
 54
 55    pub fn hovered(self, cx: &mut WindowContext) -> ButtonStyle {
 56        match self {
 57            ButtonStyle2::Filled => ButtonStyle {
 58                background: cx.theme().colors().element_hover,
 59                border_color: gpui::transparent_black(),
 60                label_color: Color::Default.color(cx),
 61                icon_color: Color::Default.color(cx),
 62            },
 63            ButtonStyle2::Subtle => ButtonStyle {
 64                background: cx.theme().colors().ghost_element_hover,
 65                border_color: gpui::transparent_black(),
 66                label_color: Color::Default.color(cx),
 67                icon_color: Color::Default.color(cx),
 68            },
 69            ButtonStyle2::Transparent => ButtonStyle {
 70                background: gpui::transparent_black(),
 71                border_color: gpui::transparent_black(),
 72                // TODO: These are not great
 73                label_color: Color::Muted.color(cx),
 74                // TODO: These are not great
 75                icon_color: Color::Muted.color(cx),
 76            },
 77        }
 78    }
 79
 80    pub fn active(self, cx: &mut WindowContext) -> ButtonStyle {
 81        match self {
 82            ButtonStyle2::Filled => ButtonStyle {
 83                background: cx.theme().colors().element_active,
 84                border_color: gpui::transparent_black(),
 85                label_color: Color::Default.color(cx),
 86                icon_color: Color::Default.color(cx),
 87            },
 88            ButtonStyle2::Subtle => ButtonStyle {
 89                background: cx.theme().colors().ghost_element_active,
 90                border_color: gpui::transparent_black(),
 91                label_color: Color::Default.color(cx),
 92                icon_color: Color::Default.color(cx),
 93            },
 94            ButtonStyle2::Transparent => ButtonStyle {
 95                background: gpui::transparent_black(),
 96                border_color: gpui::transparent_black(),
 97                // TODO: These are not great
 98                label_color: Color::Muted.color(cx),
 99                // TODO: These are not great
100                icon_color: Color::Muted.color(cx),
101            },
102        }
103    }
104
105    pub fn focused(self, cx: &mut WindowContext) -> ButtonStyle {
106        match self {
107            ButtonStyle2::Filled => ButtonStyle {
108                background: cx.theme().colors().element_background,
109                border_color: cx.theme().colors().border_focused,
110                label_color: Color::Default.color(cx),
111                icon_color: Color::Default.color(cx),
112            },
113            ButtonStyle2::Subtle => ButtonStyle {
114                background: cx.theme().colors().ghost_element_background,
115                border_color: cx.theme().colors().border_focused,
116                label_color: Color::Default.color(cx),
117                icon_color: Color::Default.color(cx),
118            },
119            ButtonStyle2::Transparent => ButtonStyle {
120                background: gpui::transparent_black(),
121                border_color: cx.theme().colors().border_focused,
122                label_color: Color::Accent.color(cx),
123                icon_color: Color::Accent.color(cx),
124            },
125        }
126    }
127
128    pub fn disabled(self, cx: &mut WindowContext) -> ButtonStyle {
129        match self {
130            ButtonStyle2::Filled => ButtonStyle {
131                background: cx.theme().colors().element_disabled,
132                border_color: cx.theme().colors().border_disabled,
133                label_color: Color::Disabled.color(cx),
134                icon_color: Color::Disabled.color(cx),
135            },
136            ButtonStyle2::Subtle => ButtonStyle {
137                background: cx.theme().colors().ghost_element_disabled,
138                border_color: cx.theme().colors().border_disabled,
139                label_color: Color::Disabled.color(cx),
140                icon_color: Color::Disabled.color(cx),
141            },
142            ButtonStyle2::Transparent => ButtonStyle {
143                background: gpui::transparent_black(),
144                border_color: gpui::transparent_black(),
145                label_color: Color::Disabled.color(cx),
146                icon_color: Color::Disabled.color(cx),
147            },
148        }
149    }
150}
151
152#[derive(Default, PartialEq, Clone, Copy)]
153pub enum ButtonSize2 {
154    #[default]
155    Default,
156    Compact,
157    None,
158}
159
160impl ButtonSize2 {
161    fn height(self) -> Rems {
162        match self {
163            ButtonSize2::Default => rems(22. / 16.),
164            ButtonSize2::Compact => rems(18. / 16.),
165            ButtonSize2::None => rems(16. / 16.),
166        }
167    }
168}
169
170#[derive(IntoElement)]
171pub struct ButtonLike {
172    id: ElementId,
173    pub(super) style: ButtonStyle2,
174    pub(super) disabled: bool,
175    pub(super) selected: bool,
176    size: ButtonSize2,
177    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
178    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
179    children: SmallVec<[AnyElement; 2]>,
180}
181
182impl ButtonLike {
183    pub fn new(id: impl Into<ElementId>) -> Self {
184        Self {
185            id: id.into(),
186            style: ButtonStyle2::default(),
187            disabled: false,
188            selected: false,
189            size: ButtonSize2::Default,
190            tooltip: None,
191            children: SmallVec::new(),
192            on_click: None,
193        }
194    }
195}
196
197impl Disableable for ButtonLike {
198    fn disabled(mut self, disabled: bool) -> Self {
199        self.disabled = disabled;
200        self
201    }
202}
203
204impl Selectable for ButtonLike {
205    fn selected(mut self, selected: bool) -> Self {
206        self.selected = selected;
207        self
208    }
209}
210
211impl Clickable for ButtonLike {
212    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
213        self.on_click = Some(Box::new(handler));
214        self
215    }
216}
217
218impl ButtonCommon for ButtonLike {
219    fn id(&self) -> &ElementId {
220        &self.id
221    }
222
223    fn style(mut self, style: ButtonStyle2) -> Self {
224        self.style = style;
225        self
226    }
227
228    fn size(mut self, size: ButtonSize2) -> Self {
229        self.size = size;
230        self
231    }
232
233    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
234        self.tooltip = Some(Box::new(tooltip));
235        self
236    }
237}
238
239impl ParentElement for ButtonLike {
240    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
241        &mut self.children
242    }
243}
244
245impl RenderOnce for ButtonLike {
246    type Rendered = Stateful<Div>;
247
248    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
249        h_stack()
250            .id(self.id.clone())
251            .h(self.size.height())
252            .rounded_md()
253            .cursor_pointer()
254            .gap_1()
255            .px_1()
256            .bg(self.style.enabled(cx).background)
257            .hover(|hover| hover.bg(self.style.hovered(cx).background))
258            .active(|active| active.bg(self.style.active(cx).background))
259            .when_some(
260                self.on_click.filter(|_| !self.disabled),
261                |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
262            )
263            .when_some(self.tooltip, |this, tooltip| {
264                this.tooltip(move |cx| tooltip(cx))
265            })
266            .children(self.children)
267    }
268}