1use crate::{
2 div::div,
3 element::{Element, ParentElement},
4 interactive::Interactive,
5 style::StyleHelpers,
6 text::ArcCow,
7 themes::rose_pine,
8};
9use gpui::{platform::MouseButton, ViewContext};
10use playground_macros::Element;
11use std::{marker::PhantomData, rc::Rc};
12
13struct ButtonHandlers<V, D> {
14 click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
15}
16
17impl<V, D> Default for ButtonHandlers<V, D> {
18 fn default() -> Self {
19 Self { click: None }
20 }
21}
22
23use crate as playground;
24#[derive(Element)]
25pub struct Button<V: 'static, D: 'static> {
26 handlers: ButtonHandlers<V, D>,
27 label: Option<ArcCow<'static, str>>,
28 icon: Option<ArcCow<'static, str>>,
29 data: Rc<D>,
30 view_type: PhantomData<V>,
31}
32
33// Impl block for buttons without data.
34// See below for an impl block for any button.
35impl<V: 'static> Button<V, ()> {
36 fn new() -> Self {
37 Self {
38 handlers: ButtonHandlers::default(),
39 label: None,
40 icon: None,
41 data: Rc::new(()),
42 view_type: PhantomData,
43 }
44 }
45
46 pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
47 Button {
48 handlers: ButtonHandlers::default(),
49 label: self.label,
50 icon: self.icon,
51 data: Rc::new(data),
52 view_type: PhantomData,
53 }
54 }
55}
56
57// Impl block for button regardless of its data type.
58impl<V: 'static, D: 'static> Button<V, D> {
59 pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
60 self.label = Some(label.into());
61 self
62 }
63
64 pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
65 self.icon = Some(icon.into());
66 self
67 }
68
69 pub fn on_click(mut self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
70 self.handlers.click = Some(Rc::new(handler));
71 self
72 }
73}
74
75pub fn button<V>() -> Button<V, ()> {
76 Button::new()
77}
78
79impl<V: 'static, D: 'static> Button<V, D> {
80 fn render(
81 &mut self,
82 view: &mut V,
83 cx: &mut ViewContext<V>,
84 ) -> impl Element<V> + Interactive<V> {
85 // TODO: Drive theme from the context
86 let button = div()
87 .fill(rose_pine::dawn().error(0.5))
88 .h_4()
89 .children(self.label.clone());
90
91 if let Some(handler) = self.handlers.click.clone() {
92 let data = self.data.clone();
93 button.on_mouse_down(MouseButton::Left, move |view, event, cx| {
94 handler(view, data.as_ref(), cx)
95 })
96 } else {
97 button
98 }
99 }
100}