components.rs

  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}