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}