icon_button.rs

  1use std::sync::Arc;
  2
  3use gpui::{rems, MouseButton, VisualContext};
  4
  5use crate::{h_stack, prelude::*, TextTooltip};
  6use crate::{ClickHandler, Icon, IconColor, IconElement};
  7
  8struct IconButtonHandlers<V: 'static> {
  9    click: Option<ClickHandler<V>>,
 10}
 11
 12impl<V: 'static> Default for IconButtonHandlers<V> {
 13    fn default() -> Self {
 14        Self { click: None }
 15    }
 16}
 17
 18#[derive(Component)]
 19pub struct IconButton<V: 'static> {
 20    id: ElementId,
 21    icon: Icon,
 22    color: IconColor,
 23    variant: ButtonVariant,
 24    state: InteractionState,
 25    tooltip: Option<SharedString>,
 26    handlers: IconButtonHandlers<V>,
 27}
 28
 29impl<V: 'static> IconButton<V> {
 30    pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
 31        Self {
 32            id: id.into(),
 33            icon,
 34            color: IconColor::default(),
 35            variant: ButtonVariant::default(),
 36            state: InteractionState::default(),
 37            tooltip: None,
 38            handlers: IconButtonHandlers::default(),
 39        }
 40    }
 41
 42    pub fn icon(mut self, icon: Icon) -> Self {
 43        self.icon = icon;
 44        self
 45    }
 46
 47    pub fn color(mut self, color: IconColor) -> Self {
 48        self.color = color;
 49        self
 50    }
 51
 52    pub fn variant(mut self, variant: ButtonVariant) -> Self {
 53        self.variant = variant;
 54        self
 55    }
 56
 57    pub fn state(mut self, state: InteractionState) -> Self {
 58        self.state = state;
 59        self
 60    }
 61
 62    pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
 63        self.tooltip = Some(tooltip.into());
 64        self
 65    }
 66
 67    pub fn on_click(
 68        mut self,
 69        handler: impl 'static + Fn(&mut V, &mut ViewContext<V>) + Send + Sync,
 70    ) -> Self {
 71        self.handlers.click = Some(Arc::new(handler));
 72        self
 73    }
 74
 75    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
 76        let icon_color = match (self.state, self.color) {
 77            (InteractionState::Disabled, _) => IconColor::Disabled,
 78            _ => self.color,
 79        };
 80
 81        let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
 82            ButtonVariant::Filled => (
 83                cx.theme().colors().element_background,
 84                cx.theme().colors().element_hover,
 85                cx.theme().colors().element_active,
 86            ),
 87            ButtonVariant::Ghost => (
 88                cx.theme().colors().ghost_element_background,
 89                cx.theme().colors().ghost_element_hover,
 90                cx.theme().colors().ghost_element_active,
 91            ),
 92        };
 93
 94        let mut button = h_stack()
 95            .id(self.id.clone())
 96            .justify_center()
 97            .rounded_md()
 98            // todo!("Where do these numbers come from?")
 99            .py(rems(0.21875))
100            .px(rems(0.375))
101            .bg(bg_color)
102            .hover(|style| style.bg(bg_hover_color))
103            .active(|style| style.bg(bg_active_color))
104            .child(IconElement::new(self.icon).color(icon_color));
105
106        if let Some(click_handler) = self.handlers.click.clone() {
107            button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
108                cx.stop_propagation();
109                click_handler(state, cx);
110            });
111        }
112
113        if let Some(tooltip) = self.tooltip.clone() {
114            button =
115                button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone())));
116        }
117
118        button
119    }
120}