icon_button.rs

  1use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
  2use gpui::{prelude::*, Action, AnyView, MouseButton};
  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(Component)]
 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> IconButton<V> {
 28    pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
 29        Self {
 30            id: id.into(),
 31            icon,
 32            color: TextColor::default(),
 33            variant: ButtonVariant::default(),
 34            state: InteractionState::default(),
 35            selected: false,
 36            tooltip: None,
 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: TextColor) -> 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 selected(mut self, selected: bool) -> Self {
 62        self.selected = selected;
 63        self
 64    }
 65
 66    pub fn tooltip(
 67        mut self,
 68        tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
 69    ) -> Self {
 70        self.tooltip = Some(Box::new(tooltip));
 71        self
 72    }
 73
 74    pub fn on_click(mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext<V>)) -> Self {
 75        self.handlers.click = Some(Arc::new(handler));
 76        self
 77    }
 78
 79    pub fn action(self, action: Box<dyn Action>) -> Self {
 80        self.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
 81    }
 82
 83    fn render(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
 84        let icon_color = match (self.state, self.color) {
 85            (InteractionState::Disabled, _) => TextColor::Disabled,
 86            (InteractionState::Active, _) => TextColor::Selected,
 87            _ => self.color,
 88        };
 89
 90        let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
 91            ButtonVariant::Filled => (
 92                cx.theme().colors().element_background,
 93                cx.theme().colors().element_hover,
 94                cx.theme().colors().element_active,
 95            ),
 96            ButtonVariant::Ghost => (
 97                cx.theme().colors().ghost_element_background,
 98                cx.theme().colors().ghost_element_hover,
 99                cx.theme().colors().ghost_element_active,
100            ),
101        };
102
103        if self.selected {
104            bg_color = bg_hover_color;
105        }
106
107        let mut button = h_stack()
108            .id(self.id.clone())
109            .justify_center()
110            .rounded_md()
111            .p_1()
112            .bg(bg_color)
113            .cursor_pointer()
114            // Nate: Trying to figure out the right places we want to show a
115            // hover state here. I think it is a bit heavy to have it on every
116            // place we use an icon button.
117            // .hover(|style| style.bg(bg_hover_color))
118            .active(|style| style.bg(bg_active_color))
119            .child(IconElement::new(self.icon).color(icon_color));
120
121        if let Some(click_handler) = self.handlers.click.clone() {
122            button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
123                cx.stop_propagation();
124                click_handler(state, cx);
125            })
126        }
127
128        if let Some(tooltip) = self.tooltip.take() {
129            if !self.selected {
130                button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
131            }
132        }
133
134        button
135    }
136}