1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use gpui2::MouseButton;
5
6use crate::{h_stack, prelude::*};
7use crate::{ClickHandler, Icon, IconColor, IconElement};
8
9struct IconButtonHandlers<S: 'static + Send + Sync> {
10 click: Option<ClickHandler<S>>,
11}
12
13impl<S: 'static + Send + Sync> Default for IconButtonHandlers<S> {
14 fn default() -> Self {
15 Self { click: None }
16 }
17}
18
19#[derive(Element)]
20pub struct IconButton<S: 'static + Send + Sync> {
21 state_type: PhantomData<S>,
22 icon: Icon,
23 color: IconColor,
24 variant: ButtonVariant,
25 state: InteractionState,
26 handlers: IconButtonHandlers<S>,
27}
28
29impl<S: 'static + Send + Sync> IconButton<S> {
30 pub fn new(icon: Icon) -> Self {
31 Self {
32 state_type: PhantomData,
33 icon,
34 color: IconColor::default(),
35 variant: ButtonVariant::default(),
36 state: InteractionState::default(),
37 handlers: IconButtonHandlers::default(),
38 }
39 }
40
41 pub fn icon(mut self, icon: Icon) -> Self {
42 self.icon = icon;
43 self
44 }
45
46 pub fn color(mut self, color: IconColor) -> Self {
47 self.color = color;
48 self
49 }
50
51 pub fn variant(mut self, variant: ButtonVariant) -> Self {
52 self.variant = variant;
53 self
54 }
55
56 pub fn state(mut self, state: InteractionState) -> Self {
57 self.state = state;
58 self
59 }
60
61 pub fn on_click(
62 mut self,
63 handler: impl Fn(&mut S, &mut ViewContext<S>) + 'static + Send + Sync,
64 ) -> Self {
65 self.handlers.click = Some(Arc::new(handler));
66 self
67 }
68
69 fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
70 let color = ThemeColor::new(cx);
71
72 let icon_color = match (self.state, self.color) {
73 (InteractionState::Disabled, _) => IconColor::Disabled,
74 _ => self.color,
75 };
76
77 let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
78 ButtonVariant::Filled => (
79 color.filled_element,
80 color.filled_element_hover,
81 color.filled_element_active,
82 ),
83 ButtonVariant::Ghost => (
84 color.ghost_element,
85 color.ghost_element_hover,
86 color.ghost_element_active,
87 ),
88 };
89
90 let mut button = h_stack()
91 // TODO: We probably need a more robust method for differentiating `IconButton`s from one another.
92 .id(SharedString::from(format!("{:?}", self.icon)))
93 .justify_center()
94 .rounded_md()
95 .py(ui_size(cx, 0.25))
96 .px(ui_size(cx, 6. / 14.))
97 .bg(bg_color)
98 .hover(|style| style.bg(bg_hover_color))
99 .active(|style| style.bg(bg_active_color))
100 .child(IconElement::new(self.icon).color(icon_color));
101
102 if let Some(click_handler) = self.handlers.click.clone() {
103 button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
104 click_handler(state, cx);
105 });
106 }
107
108 button
109 }
110}