icon_button.rs

  1use crate::{h_stack, prelude::*, Icon, IconElement};
  2use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Stateful};
  3
  4#[derive(IntoElement)]
  5pub struct IconButton {
  6    id: ElementId,
  7    icon: Icon,
  8    color: Color,
  9    variant: ButtonVariant,
 10    state: InteractionState,
 11    selected: bool,
 12    tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
 13    on_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
 14}
 15
 16impl RenderOnce for IconButton {
 17    type Rendered = Stateful<Div>;
 18
 19    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
 20        let icon_color = match (self.state, self.color) {
 21            (InteractionState::Disabled, _) => Color::Disabled,
 22            (InteractionState::Active, _) => Color::Selected,
 23            _ => self.color,
 24        };
 25
 26        let (mut bg_color, bg_active_color) = match self.variant {
 27            ButtonVariant::Filled => (
 28                cx.theme().colors().element_background,
 29                cx.theme().colors().element_active,
 30            ),
 31            ButtonVariant::Ghost => (
 32                cx.theme().colors().ghost_element_background,
 33                cx.theme().colors().ghost_element_active,
 34            ),
 35        };
 36
 37        if self.selected {
 38            bg_color = cx.theme().colors().element_selected;
 39        }
 40
 41        let mut button = h_stack()
 42            .id(self.id.clone())
 43            .justify_center()
 44            .rounded_md()
 45            .p_1()
 46            .bg(bg_color)
 47            .cursor_pointer()
 48            // Nate: Trying to figure out the right places we want to show a
 49            // hover state here. I think it is a bit heavy to have it on every
 50            // place we use an icon button.
 51            // .hover(|style| style.bg(bg_hover_color))
 52            .active(|style| style.bg(bg_active_color))
 53            .child(IconElement::new(self.icon).color(icon_color));
 54
 55        if let Some(click_handler) = self.on_mouse_down {
 56            button = button.on_mouse_down(MouseButton::Left, move |event, cx| {
 57                cx.stop_propagation();
 58                click_handler(event, cx);
 59            })
 60        }
 61
 62        if let Some(tooltip) = self.tooltip {
 63            if !self.selected {
 64                button = button.tooltip(move |cx| tooltip(cx))
 65            }
 66        }
 67
 68        // HACK: Add an additional identified element wrapper to fix tooltips not showing up.
 69        div().id(self.id.clone()).child(button)
 70    }
 71}
 72
 73impl IconButton {
 74    pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
 75        Self {
 76            id: id.into(),
 77            icon,
 78            color: Color::default(),
 79            variant: ButtonVariant::default(),
 80            state: InteractionState::default(),
 81            selected: false,
 82            tooltip: None,
 83            on_mouse_down: None,
 84        }
 85    }
 86
 87    pub fn icon(mut self, icon: Icon) -> Self {
 88        self.icon = icon;
 89        self
 90    }
 91
 92    pub fn color(mut self, color: Color) -> Self {
 93        self.color = color;
 94        self
 95    }
 96
 97    pub fn variant(mut self, variant: ButtonVariant) -> Self {
 98        self.variant = variant;
 99        self
100    }
101
102    pub fn state(mut self, state: InteractionState) -> Self {
103        self.state = state;
104        self
105    }
106
107    pub fn selected(mut self, selected: bool) -> Self {
108        self.selected = selected;
109        self
110    }
111
112    pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
113        self.tooltip = Some(Box::new(tooltip));
114        self
115    }
116
117    pub fn on_click(
118        mut self,
119        handler: impl 'static + Fn(&MouseDownEvent, &mut WindowContext),
120    ) -> Self {
121        self.on_mouse_down = Some(Box::new(handler));
122        self
123    }
124
125    pub fn action(self, action: Box<dyn Action>) -> Self {
126        self.on_click(move |_event, cx| cx.dispatch_action(action.boxed_clone()))
127    }
128}