1use gpui::prelude::*;
4use gpui::{div, px, App, ClickEvent, ElementId, IntoElement, Window};
5
6use crate::devtools::Probed;
7use crate::icon::Glyph;
8use crate::input::ClickHandler;
9use crate::style::{icon_size, surface, ColorValue, Variant};
10use crate::theme::{theme, ColorName, Size};
11
12#[derive(IntoElement)]
14pub struct ActionIcon {
15 id: ElementId,
16 icon: Glyph,
17 variant: Variant,
18 color: ColorValue,
19 size: Size,
20 radius: Option<Size>,
21 disabled: bool,
22 on_click: Option<ClickHandler>,
23}
24
25impl ActionIcon {
26 pub fn new(id: impl Into<ElementId>, icon: impl Into<Glyph>) -> Self {
27 ActionIcon {
28 id: id.into(),
29 icon: icon.into(),
30 variant: Variant::Subtle,
31 color: ColorValue::Named(ColorName::Gray),
32 size: Size::Md,
33 radius: None,
34 disabled: false,
35 on_click: None,
36 }
37 }
38
39 pub fn variant(mut self, variant: Variant) -> Self {
40 self.variant = variant;
41 self
42 }
43
44 pub fn color(mut self, color: impl Into<ColorValue>) -> Self {
45 self.color = color.into();
46 self
47 }
48
49 pub fn size(mut self, size: Size) -> Self {
50 self.size = size;
51 self
52 }
53
54 pub fn radius(mut self, radius: Size) -> Self {
55 self.radius = Some(radius);
56 self
57 }
58
59 pub fn disabled(mut self, disabled: bool) -> Self {
60 self.disabled = disabled;
61 self
62 }
63
64 pub fn on_click(
65 mut self,
66 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
67 ) -> Self {
68 self.on_click = Some(Box::new(handler));
69 self
70 }
71}
72
73impl RenderOnce for ActionIcon {
74 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
75 let t = theme(cx);
76 let s = surface(t, self.color, self.variant);
77 let dim = icon_size(self.size);
78 let radius = t.radius(self.radius.unwrap_or(t.default_radius));
79
80 let mut el = div()
81 .id(self.id)
82 .w(px(dim))
83 .h(px(dim))
84 .flex()
85 .items_center()
86 .justify_center()
87 .rounded(px(radius))
88 .bg(s.bg)
89 .text_color(s.fg)
90 .text_size(px(dim * 0.5))
91 .child(self.icon);
92 if let Some(border) = s.border {
93 el = el.border_1().border_color(border);
94 }
95
96 let element = if self.disabled {
97 el.opacity(0.5)
98 } else {
99 let hover_bg = s.bg_hover;
100 el = el.hover(move |st| st.bg(hover_bg));
101 if let Some(handler) = self.on_click {
102 el = el.on_click(handler);
103 }
104 el
105 };
106
107 element
108 .probe("ActionIcon")
109 .attr("variant", self.variant.label())
110 .attr("size", self.size.label())
111 .attr_if("disabled", self.disabled)
112 }
113}