Skip to main content

guise/
actionicon.rs

1//! `ActionIcon` — a square icon-only button.
2
3use 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/// A compact, square icon button.
13#[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}