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 button
69 }
70}
71
72impl IconButton {
73 pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
74 Self {
75 id: id.into(),
76 icon,
77 color: Color::default(),
78 variant: ButtonVariant::default(),
79 state: InteractionState::default(),
80 selected: false,
81 tooltip: None,
82 on_mouse_down: None,
83 }
84 }
85
86 pub fn icon(mut self, icon: Icon) -> Self {
87 self.icon = icon;
88 self
89 }
90
91 pub fn color(mut self, color: Color) -> Self {
92 self.color = color;
93 self
94 }
95
96 pub fn variant(mut self, variant: ButtonVariant) -> Self {
97 self.variant = variant;
98 self
99 }
100
101 pub fn state(mut self, state: InteractionState) -> Self {
102 self.state = state;
103 self
104 }
105
106 pub fn selected(mut self, selected: bool) -> Self {
107 self.selected = selected;
108 self
109 }
110
111 pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
112 self.tooltip = Some(Box::new(tooltip));
113 self
114 }
115
116 pub fn on_click(
117 mut self,
118 handler: impl 'static + Fn(&MouseDownEvent, &mut WindowContext),
119 ) -> Self {
120 self.on_mouse_down = Some(Box::new(handler));
121 self
122 }
123
124 pub fn action(self, action: Box<dyn Action>) -> Self {
125 self.on_click(move |_event, cx| cx.dispatch_action(action.boxed_clone()))
126 }
127}