icon_button.rs

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