icon_button.rs

  1use std::marker::PhantomData;
  2use std::sync::Arc;
  3
  4use gpui2::MouseButton;
  5
  6use crate::{h_stack, prelude::*};
  7use crate::{ClickHandler, Icon, IconColor, IconElement};
  8
  9struct IconButtonHandlers<S: 'static + Send + Sync> {
 10    click: Option<ClickHandler<S>>,
 11}
 12
 13impl<S: 'static + Send + Sync> Default for IconButtonHandlers<S> {
 14    fn default() -> Self {
 15        Self { click: None }
 16    }
 17}
 18
 19#[derive(Element)]
 20pub struct IconButton<S: 'static + Send + Sync> {
 21    state_type: PhantomData<S>,
 22    icon: Icon,
 23    color: IconColor,
 24    variant: ButtonVariant,
 25    state: InteractionState,
 26    handlers: IconButtonHandlers<S>,
 27}
 28
 29impl<S: 'static + Send + Sync> IconButton<S> {
 30    pub fn new(icon: Icon) -> Self {
 31        Self {
 32            state_type: PhantomData,
 33            icon,
 34            color: IconColor::default(),
 35            variant: ButtonVariant::default(),
 36            state: InteractionState::default(),
 37            handlers: IconButtonHandlers::default(),
 38        }
 39    }
 40
 41    pub fn icon(mut self, icon: Icon) -> Self {
 42        self.icon = icon;
 43        self
 44    }
 45
 46    pub fn color(mut self, color: IconColor) -> Self {
 47        self.color = color;
 48        self
 49    }
 50
 51    pub fn variant(mut self, variant: ButtonVariant) -> Self {
 52        self.variant = variant;
 53        self
 54    }
 55
 56    pub fn state(mut self, state: InteractionState) -> Self {
 57        self.state = state;
 58        self
 59    }
 60
 61    pub fn on_click(
 62        mut self,
 63        handler: impl Fn(&mut S, &mut ViewContext<S>) + 'static + Send + Sync,
 64    ) -> Self {
 65        self.handlers.click = Some(Arc::new(handler));
 66        self
 67    }
 68
 69    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 70        let color = ThemeColor::new(cx);
 71
 72        let icon_color = match (self.state, self.color) {
 73            (InteractionState::Disabled, _) => IconColor::Disabled,
 74            _ => self.color,
 75        };
 76
 77        let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
 78            ButtonVariant::Filled => (
 79                color.filled_element,
 80                color.filled_element_hover,
 81                color.filled_element_active,
 82            ),
 83            ButtonVariant::Ghost => (
 84                color.ghost_element,
 85                color.ghost_element_hover,
 86                color.ghost_element_active,
 87            ),
 88        };
 89
 90        let mut button = h_stack()
 91            // TODO: We probably need a more robust method for differentiating `IconButton`s from one another.
 92            .id(SharedString::from(format!("{:?}", self.icon)))
 93            .justify_center()
 94            .rounded_md()
 95            .py(ui_size(cx, 0.25))
 96            .px(ui_size(cx, 6. / 14.))
 97            .bg(bg_color)
 98            .hover(|style| style.bg(bg_hover_color))
 99            .active(|style| style.bg(bg_active_color))
100            .child(IconElement::new(self.icon).color(icon_color));
101
102        if let Some(click_handler) = self.handlers.click.clone() {
103            button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
104                click_handler(state, cx);
105            });
106        }
107
108        button
109    }
110}