button2.rs

  1use gpui::{
  2    rems, AnyElement, AnyView, ClickEvent, Div, Hsla, IntoElement, Rems, Stateful,
  3    StatefulInteractiveElement, WindowContext,
  4};
  5use smallvec::SmallVec;
  6
  7use crate::{h_stack, prelude::*};
  8
  9// 🚧 Heavily WIP 🚧
 10
 11// #[derive(Default, PartialEq, Clone, Copy)]
 12// pub enum ButtonType2 {
 13//     #[default]
 14//     DefaultButton,
 15//     IconButton,
 16//     ButtonLike,
 17//     SplitButton,
 18//     ToggleButton,
 19// }
 20
 21#[derive(Default, PartialEq, Clone, Copy)]
 22pub enum IconPosition2 {
 23    #[default]
 24    Before,
 25    After,
 26}
 27
 28#[derive(Default, PartialEq, Clone, Copy)]
 29pub enum ButtonStyle2 {
 30    #[default]
 31    Filled,
 32    // Tinted,
 33    Subtle,
 34    Transparent,
 35}
 36
 37#[derive(Debug, Clone, Copy)]
 38pub struct ButtonStyle {
 39    pub background: Hsla,
 40    pub border_color: Hsla,
 41    pub label_color: Hsla,
 42    pub icon_color: Hsla,
 43}
 44
 45impl ButtonStyle2 {
 46    pub fn enabled(self, cx: &mut WindowContext) -> ButtonStyle {
 47        match self {
 48            ButtonStyle2::Filled => ButtonStyle {
 49                background: cx.theme().colors().element_background,
 50                border_color: gpui::transparent_black(),
 51                label_color: Color::Default.color(cx),
 52                icon_color: Color::Default.color(cx),
 53            },
 54            ButtonStyle2::Subtle => ButtonStyle {
 55                background: cx.theme().colors().ghost_element_background,
 56                border_color: gpui::transparent_black(),
 57                label_color: Color::Default.color(cx),
 58                icon_color: Color::Default.color(cx),
 59            },
 60            ButtonStyle2::Transparent => ButtonStyle {
 61                background: gpui::transparent_black(),
 62                border_color: gpui::transparent_black(),
 63                label_color: Color::Default.color(cx),
 64                icon_color: Color::Default.color(cx),
 65            },
 66        }
 67    }
 68
 69    pub fn hovered(self, cx: &mut WindowContext) -> ButtonStyle {
 70        match self {
 71            ButtonStyle2::Filled => ButtonStyle {
 72                background: cx.theme().colors().element_hover,
 73                border_color: gpui::transparent_black(),
 74                label_color: Color::Default.color(cx),
 75                icon_color: Color::Default.color(cx),
 76            },
 77            ButtonStyle2::Subtle => ButtonStyle {
 78                background: cx.theme().colors().ghost_element_hover,
 79                border_color: gpui::transparent_black(),
 80                label_color: Color::Default.color(cx),
 81                icon_color: Color::Default.color(cx),
 82            },
 83            ButtonStyle2::Transparent => ButtonStyle {
 84                background: gpui::transparent_black(),
 85                border_color: gpui::transparent_black(),
 86                // TODO: These are not great
 87                label_color: Color::Muted.color(cx),
 88                // TODO: These are not great
 89                icon_color: Color::Muted.color(cx),
 90            },
 91        }
 92    }
 93
 94    pub fn active(self, cx: &mut WindowContext) -> ButtonStyle {
 95        match self {
 96            ButtonStyle2::Filled => ButtonStyle {
 97                background: cx.theme().colors().element_active,
 98                border_color: gpui::transparent_black(),
 99                label_color: Color::Default.color(cx),
100                icon_color: Color::Default.color(cx),
101            },
102            ButtonStyle2::Subtle => ButtonStyle {
103                background: cx.theme().colors().ghost_element_active,
104                border_color: gpui::transparent_black(),
105                label_color: Color::Default.color(cx),
106                icon_color: Color::Default.color(cx),
107            },
108            ButtonStyle2::Transparent => ButtonStyle {
109                background: gpui::transparent_black(),
110                border_color: gpui::transparent_black(),
111                // TODO: These are not great
112                label_color: Color::Muted.color(cx),
113                // TODO: These are not great
114                icon_color: Color::Muted.color(cx),
115            },
116        }
117    }
118
119    pub fn focused(self, cx: &mut WindowContext) -> ButtonStyle {
120        match self {
121            ButtonStyle2::Filled => ButtonStyle {
122                background: cx.theme().colors().element_background,
123                border_color: cx.theme().colors().border_focused,
124                label_color: Color::Default.color(cx),
125                icon_color: Color::Default.color(cx),
126            },
127            ButtonStyle2::Subtle => ButtonStyle {
128                background: cx.theme().colors().ghost_element_background,
129                border_color: cx.theme().colors().border_focused,
130                label_color: Color::Default.color(cx),
131                icon_color: Color::Default.color(cx),
132            },
133            ButtonStyle2::Transparent => ButtonStyle {
134                background: gpui::transparent_black(),
135                border_color: cx.theme().colors().border_focused,
136                label_color: Color::Accent.color(cx),
137                icon_color: Color::Accent.color(cx),
138            },
139        }
140    }
141
142    pub fn disabled(self, cx: &mut WindowContext) -> ButtonStyle {
143        match self {
144            ButtonStyle2::Filled => ButtonStyle {
145                background: cx.theme().colors().element_disabled,
146                border_color: cx.theme().colors().border_disabled,
147                label_color: Color::Disabled.color(cx),
148                icon_color: Color::Disabled.color(cx),
149            },
150            ButtonStyle2::Subtle => ButtonStyle {
151                background: cx.theme().colors().ghost_element_disabled,
152                border_color: cx.theme().colors().border_disabled,
153                label_color: Color::Disabled.color(cx),
154                icon_color: Color::Disabled.color(cx),
155            },
156            ButtonStyle2::Transparent => ButtonStyle {
157                background: gpui::transparent_black(),
158                border_color: gpui::transparent_black(),
159                label_color: Color::Disabled.color(cx),
160                icon_color: Color::Disabled.color(cx),
161            },
162        }
163    }
164}
165
166#[derive(Default, PartialEq, Clone, Copy)]
167pub enum ButtonSize2 {
168    #[default]
169    Default,
170    Compact,
171    None,
172}
173
174impl ButtonSize2 {
175    fn height(self) -> Rems {
176        match self {
177            ButtonSize2::Default => rems(22. / 16.),
178            ButtonSize2::Compact => rems(18. / 16.),
179            ButtonSize2::None => rems(16. / 16.),
180        }
181    }
182}
183
184// pub struct Button {
185//     id: ElementId,
186//     icon: Option<Icon>,
187//     icon_color: Option<Color>,
188//     icon_position: Option<IconPosition2>,
189//     label: Option<Label>,
190//     label_color: Option<Color>,
191//     appearance: ButtonAppearance2,
192//     state: InteractionState,
193//     selected: bool,
194//     disabled: bool,
195//     tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
196//     width: Option<DefiniteLength>,
197//     action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
198//     secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
199//     /// Used to pass down some content to the button
200//     /// to enable creating custom buttons.
201//     children: SmallVec<[AnyElement; 2]>,
202// }
203
204pub trait ButtonCommon: Clickable + Disableable {
205    fn id(&self) -> &ElementId;
206    fn style(self, style: ButtonStyle2) -> Self;
207    fn size(self, size: ButtonSize2) -> Self;
208    fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
209}
210
211// pub struct LabelButton {
212//     // Base properties...
213//     id: ElementId,
214//     appearance: ButtonAppearance,
215//     state: InteractionState,
216//     disabled: bool,
217//     size: ButtonSize,
218//     tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
219//     width: Option<DefiniteLength>,
220//     // Button-specific properties...
221//     label: Option<SharedString>,
222//     label_color: Option<Color>,
223//     icon: Option<Icon>,
224//     icon_color: Option<Color>,
225//     icon_position: Option<IconPosition>,
226//     // Define more fields for additional properties as needed
227// }
228
229// impl ButtonCommon for LabelButton {
230//     fn id(&self) -> &ElementId {
231//         &self.id
232//     }
233
234//     fn appearance(&mut self, appearance: ButtonAppearance) -> &mut Self {
235//         self.style= style;
236//         self
237//     }
238//     // implement methods from ButtonCommon trait...
239// }
240
241// impl LabelButton {
242//     pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
243//         Self {
244//             id: id.into(),
245//             label: Some(label.into()),
246//             // initialize other fields with default values...
247//         }
248//     }
249
250//     // ... Define other builder methods specific to Button type...
251// }
252
253// TODO: Icon Button
254
255#[derive(IntoElement)]
256pub struct ButtonLike {
257    id: ElementId,
258    style: ButtonStyle2,
259    disabled: bool,
260    size: ButtonSize2,
261    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
262    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
263    children: SmallVec<[AnyElement; 2]>,
264}
265
266impl ButtonLike {
267    pub fn new(id: impl Into<ElementId>) -> Self {
268        Self {
269            id: id.into(),
270            style: ButtonStyle2::default(),
271            disabled: false,
272            size: ButtonSize2::Default,
273            tooltip: None,
274            children: SmallVec::new(),
275            on_click: None,
276        }
277    }
278}
279
280impl Disableable for ButtonLike {
281    fn disabled(mut self, disabled: bool) -> Self {
282        self.disabled = disabled;
283        self
284    }
285}
286
287impl Clickable for ButtonLike {
288    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
289        self.on_click = Some(Box::new(handler));
290        self
291    }
292}
293
294// impl Selectable for ButtonLike {
295//     fn selected(&mut self, selected: bool) -> &mut Self {
296//         todo!()
297//     }
298
299//     fn selected_tooltip(
300//         &mut self,
301//         tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
302//     ) -> &mut Self {
303//         todo!()
304//     }
305// }
306
307impl ButtonCommon for ButtonLike {
308    fn id(&self) -> &ElementId {
309        &self.id
310    }
311
312    fn style(mut self, style: ButtonStyle2) -> Self {
313        self.style = style;
314        self
315    }
316
317    fn size(mut self, size: ButtonSize2) -> Self {
318        self.size = size;
319        self
320    }
321
322    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
323        self.tooltip = Some(Box::new(tooltip));
324        self
325    }
326}
327
328impl RenderOnce for ButtonLike {
329    type Rendered = Stateful<Div>;
330
331    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
332        h_stack()
333            .id(self.id.clone())
334            .h(self.size.height())
335            .rounded_md()
336            .cursor_pointer()
337            .gap_1()
338            .px_1()
339            .bg(self.style.enabled(cx).background)
340            .hover(|hover| hover.bg(self.style.hovered(cx).background))
341            .active(|active| active.bg(self.style.active(cx).background))
342            .when_some(
343                self.on_click.filter(|_| !self.disabled),
344                |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
345            )
346            .when_some(self.tooltip, |this, tooltip| {
347                this.tooltip(move |cx| tooltip(cx))
348            })
349            .children(self.children)
350    }
351}
352
353impl ParentElement for ButtonLike {
354    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
355        &mut self.children
356    }
357}
358
359// pub struct ToggleButton {
360//     // based on either IconButton2 or Button, with additional 'selected: bool' property
361// }
362
363// impl ButtonCommon for ToggleButton {
364//     fn id(&self) -> &ElementId {
365//         &self.id
366//     }
367//     // ... Implement other methods from ButtonCommon trait with builder patterns...
368// }
369
370// impl ToggleButton {
371//     pub fn new() -> Self {
372//         // Initialize with default values
373//         Self {
374//             // ... initialize fields, possibly with defaults or required parameters...
375//         }
376//     }
377
378//     // ... Define other builder methods specific to ToggleButton type...
379// }
380
381// pub struct SplitButton {
382//     // Base properties...
383//     id: ElementId,
384//     // Button-specific properties, possibly including a DefaultButton
385//     secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
386//     // More fields as necessary...
387// }
388
389// impl ButtonCommon for SplitButton {
390//     fn id(&self) -> &ElementId {
391//         &self.id
392//     }
393//     // ... Implement other methods from ButtonCommon trait with builder patterns...
394// }
395
396// impl SplitButton {
397//     pub fn new(id: impl Into<ElementId>) -> Self {
398//         Self {
399//             id: id.into(),
400//             // ... initialize other fields with default values...
401//         }
402//     }
403
404//     // ... Define other builder methods specific to SplitButton type...
405// }