/ src / components / branding / button.rs
button.rs
  1  //! Heavily inspired on https://github.com/longbridge/gpui-component/blob/main/crates/ui/src/button/button.rs
  2  
  3  use std::rc::Rc;
  4  
  5  use gpui::{prelude::*, *};
  6  
  7  #[derive(IntoElement)]
  8  pub struct Button {
  9    base: Stateful<Div>,
 10    label: Option<SharedString>,
 11    on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
 12    loading: bool,
 13    disabled: bool,
 14    stop_propagation: bool,
 15    w_full: bool,
 16  }
 17  
 18  impl Button {
 19    pub fn new(id: impl Into<ElementId>) -> Self {
 20      Self {
 21        base: div().id(id.into()).flex_shrink_0(),
 22        label: None,
 23        on_click: None,
 24        loading: false,
 25        disabled: false,
 26        stop_propagation: true,
 27        w_full: false,
 28      }
 29    }
 30  
 31    /// Set label to the Button, if no label is set, the button will be in Icon Button mode.
 32    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
 33      self.label = Some(label.into());
 34      self
 35    }
 36  
 37    /// Add click handler.
 38    pub fn on_click(
 39      mut self,
 40      handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
 41    ) -> Self {
 42      self.on_click = Some(Rc::new(handler));
 43      self
 44    }
 45  
 46    /// Whether the button should extends to its fullest.
 47    pub fn w_full(mut self) -> Self {
 48      self.w_full = true;
 49      self
 50    }
 51  
 52    /// Set true to show the loading indicator.
 53    pub fn loading(mut self, loading: bool) -> Self {
 54      self.loading = loading;
 55      self
 56    }
 57  
 58    pub fn disabled(mut self, disabled: bool) -> Self {
 59      self.disabled = disabled;
 60      self
 61    }
 62  
 63    #[inline]
 64    fn clickable(&self) -> bool {
 65      !(self.disabled || self.loading) && self.on_click.is_some()
 66    }
 67  }
 68  
 69  impl RenderOnce for Button {
 70    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
 71      self
 72        .base
 73        .cursor_pointer()
 74        .flex()
 75        .items_center()
 76        .justify_center()
 77        .bg(rgb(0x5865f2))
 78        .rounded_lg()
 79        .border_1()
 80        .border_color(rgba(0xffffff14))
 81        .px_4()
 82        .py_3()
 83        .hover(|this| this.bg(rgb(0x4452BB)))
 84        .active(|this| this.bg(rgb(0x3A48A3)))
 85        .when(self.w_full, |this| this.w_full())
 86        .when_some(
 87          self.on_click.filter(|_| !self.disabled && !self.loading),
 88          |this, on_click| {
 89            let stop_propagation = self.stop_propagation;
 90            this
 91              .on_mouse_down(MouseButton::Left, move |_, window, cx| {
 92                window.prevent_default();
 93                if stop_propagation {
 94                  cx.stop_propagation();
 95                }
 96              })
 97              .on_click(move |event, window, cx| {
 98                (on_click)(event, window, cx);
 99              })
100          },
101        )
102        .when_some(self.label, |this, label| {
103          this.child(
104            div()
105              .text_color(gpui::white())
106              .text_size(px(16.))
107              .font_weight(FontWeight::MEDIUM)
108              .flex_none()
109              .line_height(relative(1.))
110              .child(label),
111          )
112        })
113    }
114  }