1use std::sync::Arc;
2
3use gpui::{rems, MouseButton, VisualContext};
4
5use crate::{h_stack, prelude::*, TextTooltip};
6use crate::{ClickHandler, Icon, IconColor, IconElement};
7
8struct IconButtonHandlers<V: 'static> {
9 click: Option<ClickHandler<V>>,
10}
11
12impl<V: 'static> Default for IconButtonHandlers<V> {
13 fn default() -> Self {
14 Self { click: None }
15 }
16}
17
18#[derive(Component)]
19pub struct IconButton<V: 'static> {
20 id: ElementId,
21 icon: Icon,
22 color: IconColor,
23 variant: ButtonVariant,
24 state: InteractionState,
25 tooltip: Option<SharedString>,
26 handlers: IconButtonHandlers<V>,
27}
28
29impl<V: 'static> IconButton<V> {
30 pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
31 Self {
32 id: id.into(),
33 icon,
34 color: IconColor::default(),
35 variant: ButtonVariant::default(),
36 state: InteractionState::default(),
37 tooltip: None,
38 handlers: IconButtonHandlers::default(),
39 }
40 }
41
42 pub fn icon(mut self, icon: Icon) -> Self {
43 self.icon = icon;
44 self
45 }
46
47 pub fn color(mut self, color: IconColor) -> Self {
48 self.color = color;
49 self
50 }
51
52 pub fn variant(mut self, variant: ButtonVariant) -> Self {
53 self.variant = variant;
54 self
55 }
56
57 pub fn state(mut self, state: InteractionState) -> Self {
58 self.state = state;
59 self
60 }
61
62 pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
63 self.tooltip = Some(tooltip.into());
64 self
65 }
66
67 pub fn on_click(
68 mut self,
69 handler: impl 'static + Fn(&mut V, &mut ViewContext<V>) + Send + Sync,
70 ) -> Self {
71 self.handlers.click = Some(Arc::new(handler));
72 self
73 }
74
75 fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
76 let icon_color = match (self.state, self.color) {
77 (InteractionState::Disabled, _) => IconColor::Disabled,
78 _ => self.color,
79 };
80
81 let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
82 ButtonVariant::Filled => (
83 cx.theme().colors().element_background,
84 cx.theme().colors().element_hover,
85 cx.theme().colors().element_active,
86 ),
87 ButtonVariant::Ghost => (
88 cx.theme().colors().ghost_element_background,
89 cx.theme().colors().ghost_element_hover,
90 cx.theme().colors().ghost_element_active,
91 ),
92 };
93
94 let mut button = h_stack()
95 .id(self.id.clone())
96 .justify_center()
97 .rounded_md()
98 // todo!("Where do these numbers come from?")
99 .py(rems(0.21875))
100 .px(rems(0.375))
101 .bg(bg_color)
102 .hover(|style| style.bg(bg_hover_color))
103 .active(|style| style.bg(bg_active_color))
104 .child(IconElement::new(self.icon).color(icon_color));
105
106 if let Some(click_handler) = self.handlers.click.clone() {
107 button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
108 cx.stop_propagation();
109 click_handler(state, cx);
110 });
111 }
112
113 if let Some(tooltip) = self.tooltip.clone() {
114 button =
115 button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone())));
116 }
117
118 button
119 }
120}