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