1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use gpui3::{MouseButton, StatelesslyInteractive};
5
6use crate::{h_stack, prelude::*};
7use crate::{theme, 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 theme = theme(cx);
71 let color = ThemeColor::new(cx);
72
73 let icon_color = match (self.state, self.color) {
74 (InteractionState::Disabled, _) => IconColor::Disabled,
75 _ => self.color,
76 };
77
78 let mut button = h_stack()
79 .justify_center()
80 .rounded_md()
81 .py(ui_size(cx, 0.25))
82 .px(ui_size(cx, 6. / 14.))
83 .when(self.variant == ButtonVariant::Filled, |this| {
84 this.bg(color.filled_element)
85 })
86 .hover(|style| style.bg(theme.highest.base.hovered.background))
87 .child(IconElement::new(self.icon).color(icon_color));
88
89 if let Some(click_handler) = self.handlers.click.clone() {
90 button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
91 click_handler(state, cx);
92 });
93 }
94
95 button
96 }
97}