icon_button.rs

  1use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement, TextTooltip};
  2use gpui::{prelude::*, MouseButton, VisualContext};
  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    tooltip: Option<SharedString>,
 23    handlers: IconButtonHandlers<V>,
 24}
 25
 26impl<V: 'static> IconButton<V> {
 27    pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
 28        Self {
 29            id: id.into(),
 30            icon,
 31            color: TextColor::default(),
 32            variant: ButtonVariant::default(),
 33            state: InteractionState::default(),
 34            tooltip: None,
 35            handlers: IconButtonHandlers::default(),
 36        }
 37    }
 38
 39    pub fn icon(mut self, icon: Icon) -> Self {
 40        self.icon = icon;
 41        self
 42    }
 43
 44    pub fn color(mut self, color: TextColor) -> Self {
 45        self.color = color;
 46        self
 47    }
 48
 49    pub fn variant(mut self, variant: ButtonVariant) -> Self {
 50        self.variant = variant;
 51        self
 52    }
 53
 54    pub fn state(mut self, state: InteractionState) -> Self {
 55        self.state = state;
 56        self
 57    }
 58
 59    pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
 60        self.tooltip = Some(tooltip.into());
 61        self
 62    }
 63
 64    pub fn on_click(
 65        mut self,
 66        handler: impl 'static + Fn(&mut V, &mut ViewContext<V>) + Send + Sync,
 67    ) -> Self {
 68        self.handlers.click = Some(Arc::new(handler));
 69        self
 70    }
 71
 72    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
 73        let icon_color = match (self.state, self.color) {
 74            (InteractionState::Disabled, _) => TextColor::Disabled,
 75            _ => self.color,
 76        };
 77
 78        let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
 79            ButtonVariant::Filled => (
 80                cx.theme().colors().element_background,
 81                cx.theme().colors().element_hover,
 82                cx.theme().colors().element_active,
 83            ),
 84            ButtonVariant::Ghost => (
 85                cx.theme().colors().ghost_element_background,
 86                cx.theme().colors().ghost_element_hover,
 87                cx.theme().colors().ghost_element_active,
 88            ),
 89        };
 90
 91        let mut button = h_stack()
 92            .id(self.id.clone())
 93            .justify_center()
 94            .rounded_md()
 95            .p_1()
 96            .bg(bg_color)
 97            .hover(|style| style.bg(bg_hover_color))
 98            .active(|style| style.bg(bg_active_color))
 99            .child(IconElement::new(self.icon).color(icon_color));
100
101        if let Some(click_handler) = self.handlers.click.clone() {
102            button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
103                cx.stop_propagation();
104                click_handler(state, cx);
105            });
106        }
107
108        if let Some(tooltip) = self.tooltip.clone() {
109            button =
110                button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone())));
111        }
112
113        button
114    }
115}