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 {
205    fn id(&self) -> &ElementId;
206    fn style(self, style: ButtonStyle2) -> Self;
207    fn disabled(self, disabled: bool) -> Self;
208    fn size(self, size: ButtonSize2) -> Self;
209    fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
210    // fn width(&mut self, width: DefiniteLength) -> &mut Self;
211}
212
213// pub struct LabelButton {
214//     // Base properties...
215//     id: ElementId,
216//     appearance: ButtonAppearance,
217//     state: InteractionState,
218//     disabled: bool,
219//     size: ButtonSize,
220//     tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
221//     width: Option<DefiniteLength>,
222//     // Button-specific properties...
223//     label: Option<SharedString>,
224//     label_color: Option<Color>,
225//     icon: Option<Icon>,
226//     icon_color: Option<Color>,
227//     icon_position: Option<IconPosition>,
228//     // Define more fields for additional properties as needed
229// }
230
231// impl ButtonCommon for LabelButton {
232//     fn id(&self) -> &ElementId {
233//         &self.id
234//     }
235
236//     fn appearance(&mut self, appearance: ButtonAppearance) -> &mut Self {
237//         self.style= style;
238//         self
239//     }
240//     // implement methods from ButtonCommon trait...
241// }
242
243// impl LabelButton {
244//     pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
245//         Self {
246//             id: id.into(),
247//             label: Some(label.into()),
248//             // initialize other fields with default values...
249//         }
250//     }
251
252//     // ... Define other builder methods specific to Button type...
253// }
254
255// TODO: Icon Button
256
257#[derive(IntoElement)]
258pub struct ButtonLike {
259    id: ElementId,
260    style: ButtonStyle2,
261    disabled: bool,
262    size: ButtonSize2,
263    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
264    on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
265    children: SmallVec<[AnyElement; 2]>,
266}
267
268impl ButtonLike {
269    pub fn children(
270        &mut self,
271        children: impl IntoIterator<Item = impl Into<AnyElement>>,
272    ) -> &mut Self {
273        self.children = children.into_iter().map(Into::into).collect();
274        self
275    }
276
277    pub fn new(id: impl Into<ElementId>) -> Self {
278        Self {
279            id: id.into(),
280            style: ButtonStyle2::default(),
281            disabled: false,
282            size: ButtonSize2::Default,
283            tooltip: None,
284            children: SmallVec::new(),
285            on_click: None,
286        }
287    }
288}
289
290impl Clickable for ButtonLike {
291    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
292        self.on_click = Some(Box::new(handler));
293        self
294    }
295}
296
297// impl Selectable for ButtonLike {
298//     fn selected(&mut self, selected: bool) -> &mut Self {
299//         todo!()
300//     }
301
302//     fn selected_tooltip(
303//         &mut self,
304//         tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
305//     ) -> &mut Self {
306//         todo!()
307//     }
308// }
309
310impl ButtonCommon for ButtonLike {
311    fn id(&self) -> &ElementId {
312        &self.id
313    }
314
315    fn style(mut self, style: ButtonStyle2) -> Self {
316        self.style = style;
317        self
318    }
319
320    fn disabled(mut self, disabled: bool) -> Self {
321        self.disabled = disabled;
322        self
323    }
324
325    fn size(mut self, size: ButtonSize2) -> Self {
326        self.size = size;
327        self
328    }
329
330    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
331        self.tooltip = Some(Box::new(tooltip));
332        self
333    }
334}
335
336impl RenderOnce for ButtonLike {
337    type Rendered = Stateful<Div>;
338
339    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
340        h_stack()
341            .id(self.id.clone())
342            .h(self.size.height())
343            .rounded_md()
344            .cursor_pointer()
345            .gap_1()
346            .px_1()
347            .bg(self.style.enabled(cx).background)
348            .hover(|hover| hover.bg(self.style.hovered(cx).background))
349            .active(|active| active.bg(self.style.active(cx).background))
350            .when_some(
351                self.on_click.filter(|_| !self.disabled),
352                |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
353            )
354            .when_some(self.tooltip, |this, tooltip| {
355                this.tooltip(move |cx| tooltip(cx))
356            })
357            .children(self.children)
358    }
359}
360
361impl ParentElement for ButtonLike {
362    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
363        &mut self.children
364    }
365}
366
367// pub struct ToggleButton {
368//     // based on either IconButton2 or Button, with additional 'selected: bool' property
369// }
370
371// impl ButtonCommon for ToggleButton {
372//     fn id(&self) -> &ElementId {
373//         &self.id
374//     }
375//     // ... Implement other methods from ButtonCommon trait with builder patterns...
376// }
377
378// impl ToggleButton {
379//     pub fn new() -> Self {
380//         // Initialize with default values
381//         Self {
382//             // ... initialize fields, possibly with defaults or required parameters...
383//         }
384//     }
385
386//     // ... Define other builder methods specific to ToggleButton type...
387// }
388
389// pub struct SplitButton {
390//     // Base properties...
391//     id: ElementId,
392//     // Button-specific properties, possibly including a DefaultButton
393//     secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
394//     // More fields as necessary...
395// }
396
397// impl ButtonCommon for SplitButton {
398//     fn id(&self) -> &ElementId {
399//         &self.id
400//     }
401//     // ... Implement other methods from ButtonCommon trait with builder patterns...
402// }
403
404// impl SplitButton {
405//     pub fn new(id: impl Into<ElementId>) -> Self {
406//         Self {
407//             id: id.into(),
408//             // ... initialize other fields with default values...
409//         }
410//     }
411
412//     // ... Define other builder methods specific to SplitButton type...
413// }