icon_button.rs

  1use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
  2use gpui::{prelude::*, Action, AnyView, Div, MouseButton, Stateful};
  3use std::sync::Arc;
  4
  5struct IconButtonHandlers<V: 'static> {
  6    click: Option<ClickHandler<V>>,
  7}
  8
  9impl<V: 'static> Default for IconButtonHandlers<V> {
 10    fn default() -> Self {
 11        Self { click: None }
 12    }
 13}
 14
 15#[derive(RenderOnce)]
 16pub struct IconButton<V: 'static> {
 17    id: ElementId,
 18    icon: Icon,
 19    color: TextColor,
 20    variant: ButtonVariant,
 21    state: InteractionState,
 22    selected: bool,
 23    tooltip: Option<Box<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>>,
 24    handlers: IconButtonHandlers<V>,
 25}
 26
 27impl<V: 'static> Component<V> for IconButton<V> {
 28    type Rendered = Stateful<V, Div<V>>;
 29
 30    fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
 31        let icon_color = match (self.state, self.color) {
 32            (InteractionState::Disabled, _) => TextColor::Disabled,
 33            (InteractionState::Active, _) => TextColor::Selected,
 34            _ => self.color,
 35        };
 36
 37        let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
 38            ButtonVariant::Filled => (
 39                cx.theme().colors().element_background,
 40                cx.theme().colors().element_hover,
 41                cx.theme().colors().element_active,
 42            ),
 43            ButtonVariant::Ghost => (
 44                cx.theme().colors().ghost_element_background,
 45                cx.theme().colors().ghost_element_hover,
 46                cx.theme().colors().ghost_element_active,
 47            ),
 48        };
 49
 50        if self.selected {
 51            bg_color = bg_hover_color;
 52        }
 53
 54        let mut button = h_stack()
 55            .id(self.id.clone())
 56            .justify_center()
 57            .rounded_md()
 58            .p_1()
 59            .bg(bg_color)
 60            .cursor_pointer()
 61            .hover(|style| style.bg(bg_hover_color))
 62            .active(|style| style.bg(bg_active_color))
 63            .child(IconElement::new(self.icon).color(icon_color));
 64
 65        if let Some(click_handler) = self.handlers.click.clone() {
 66            button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
 67                cx.stop_propagation();
 68                click_handler(state, cx);
 69            })
 70        }
 71
 72        if let Some(tooltip) = self.tooltip {
 73            if !self.selected {
 74                button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
 75            }
 76        }
 77
 78        button
 79    }
 80}
 81
 82impl<V: 'static> IconButton<V> {
 83    pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
 84        Self {
 85            id: id.into(),
 86            icon,
 87            color: TextColor::default(),
 88            variant: ButtonVariant::default(),
 89            state: InteractionState::default(),
 90            selected: false,
 91            tooltip: None,
 92            handlers: IconButtonHandlers::default(),
 93        }
 94    }
 95
 96    pub fn icon(mut self, icon: Icon) -> Self {
 97        self.icon = icon;
 98        self
 99    }
100
101    pub fn color(mut self, color: TextColor) -> Self {
102        self.color = color;
103        self
104    }
105
106    pub fn variant(mut self, variant: ButtonVariant) -> Self {
107        self.variant = variant;
108        self
109    }
110
111    pub fn state(mut self, state: InteractionState) -> Self {
112        self.state = state;
113        self
114    }
115
116    pub fn selected(mut self, selected: bool) -> Self {
117        self.selected = selected;
118        self
119    }
120
121    pub fn tooltip(
122        mut self,
123        tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
124    ) -> Self {
125        self.tooltip = Some(Box::new(tooltip));
126        self
127    }
128
129    pub fn on_click(mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext<V>)) -> Self {
130        self.handlers.click = Some(Arc::new(handler));
131        self
132    }
133
134    pub fn action(self, action: Box<dyn Action>) -> Self {
135        self.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
136    }
137}