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_hover_color, bg_active_color) = match self.variant {
27 ButtonVariant::Filled => (
28 cx.theme().colors().element_background,
29 cx.theme().colors().element_hover,
30 cx.theme().colors().element_active,
31 ),
32 ButtonVariant::Ghost => (
33 cx.theme().colors().ghost_element_background,
34 cx.theme().colors().ghost_element_hover,
35 cx.theme().colors().ghost_element_active,
36 ),
37 };
38
39 if self.selected {
40 bg_color = cx.theme().colors().element_selected;
41 }
42
43 let mut button = h_stack()
44 .id(self.id.clone())
45 .justify_center()
46 .rounded_md()
47 .p_1()
48 .bg(bg_color)
49 .cursor_pointer()
50 // Nate: Trying to figure out the right places we want to show a
51 // hover state here. I think it is a bit heavy to have it on every
52 // place we use an icon button.
53 // .hover(|style| style.bg(bg_hover_color))
54 .active(|style| style.bg(bg_active_color))
55 .child(IconElement::new(self.icon).color(icon_color));
56
57 if let Some(click_handler) = self.on_mouse_down {
58 button = button.on_mouse_down(MouseButton::Left, move |event, cx| {
59 cx.stop_propagation();
60 click_handler(event, cx);
61 })
62 }
63
64 if let Some(tooltip) = self.tooltip {
65 if !self.selected {
66 button = button.tooltip(move |cx| tooltip(cx))
67 }
68 }
69
70 button
71 }
72}
73
74impl IconButton {
75 pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
76 Self {
77 id: id.into(),
78 icon,
79 color: Color::default(),
80 variant: ButtonVariant::default(),
81 state: InteractionState::default(),
82 selected: false,
83 tooltip: None,
84 on_mouse_down: None,
85 }
86 }
87
88 pub fn icon(mut self, icon: Icon) -> Self {
89 self.icon = icon;
90 self
91 }
92
93 pub fn color(mut self, color: Color) -> Self {
94 self.color = color;
95 self
96 }
97
98 pub fn variant(mut self, variant: ButtonVariant) -> Self {
99 self.variant = variant;
100 self
101 }
102
103 pub fn state(mut self, state: InteractionState) -> Self {
104 self.state = state;
105 self
106 }
107
108 pub fn selected(mut self, selected: bool) -> Self {
109 self.selected = selected;
110 self
111 }
112
113 pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
114 self.tooltip = Some(Box::new(tooltip));
115 self
116 }
117
118 pub fn on_click(
119 mut self,
120 handler: impl 'static + Fn(&MouseDownEvent, &mut WindowContext),
121 ) -> Self {
122 self.on_mouse_down = Some(Box::new(handler));
123 self
124 }
125
126 pub fn action(self, action: Box<dyn Action>) -> Self {
127 self.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
128 }
129}