Skip to main content

egui_charts/ui_kit/buttons/
icon_button.rs

1//! Icon Button - simple icon-only button
2//!
3//! The most common button type, used for tool selection, toggles, and actions.
4
5use super::{ButtonSize, ButtonVariant, finish};
6use crate::icons::Icon;
7use crate::theme::ThemeExt;
8use egui::{Rect, Response, Sense, Ui, Vec2};
9
10/// Icon button with theme-aware styling
11///
12/// A square button containing a single icon. Supports hover, pressed, selected
13/// and disabled states with proper theme colors; a selected button is filled with the accent.
14///
15/// # Example
16///
17/// ```
18/// use egui_charts::icons::icons;
19/// use egui_charts::ui_kit::buttons::{ButtonSize, IconButton};
20///
21/// # egui::__run_test_ui(|ui| {
22/// # let is_active = false;
23/// let response = IconButton::new(&icons::TREND_LINE)
24///     .size(ButtonSize::MD)
25///     .tooltip("Trend Line (Alt+T)")
26///     .selected(is_active)
27///     .show(ui);
28///
29/// if response.clicked() {
30///     // select the trend line tool
31/// }
32/// # });
33/// ```
34pub struct IconButton<'a> {
35    icon: &'a Icon,
36    size: ButtonSize,
37    tooltip: Option<String>,
38    selected: Option<bool>,
39    enabled: bool,
40    icon_scale: Option<f32>,
41}
42
43impl<'a> IconButton<'a> {
44    /// Create a new icon button
45    pub fn new(icon: &'a Icon) -> Self {
46        Self {
47            icon,
48            size: ButtonSize::MD,
49            tooltip: None,
50            selected: None,
51            enabled: true,
52            icon_scale: None,
53        }
54    }
55
56    /// Set the button size
57    #[must_use]
58    pub fn size(mut self, size: ButtonSize) -> Self {
59        self.size = size;
60        self
61    }
62
63    /// Set the tooltip text
64    #[must_use]
65    pub fn tooltip(mut self, text: impl Into<String>) -> Self {
66        self.tooltip = Some(text.into());
67        self
68    }
69
70    /// Set whether the button is in selected/active state
71    #[must_use]
72    pub fn selected(mut self, selected: bool) -> Self {
73        self.selected = Some(selected);
74        self
75    }
76
77    /// Set whether the button is enabled
78    #[must_use]
79    pub fn enabled(mut self, enabled: bool) -> Self {
80        self.enabled = enabled;
81        self
82    }
83
84    /// Set the icon size as a fraction of the button size (0.1-1.0).
85    ///
86    /// By default the icon is 16, 20 or 24 px for small, medium and larger buttons.
87    #[must_use]
88    pub fn icon_scale(mut self, scale: f32) -> Self {
89        self.icon_scale = Some(scale.clamp(0.1, 1.0));
90        self
91    }
92
93    /// Show the button and return the response
94    pub fn show(self, ui: &mut Ui) -> Response {
95        if !self.enabled {
96            // `enabled(false)` looks exactly like a button inside `add_enabled_ui(false)`.
97            return ui.add_enabled(false, |ui: &mut Ui| {
98                Self {
99                    enabled: true,
100                    ..self
101                }
102                .show(ui)
103            });
104        }
105
106        let button_size = Vec2::splat(self.size.pixels());
107        let (rect, response) = ui.allocate_exact_size(button_size, Sense::click());
108        crate::styles::focus::draw_focus_ring(ui, &response, ui.active_theme().radius.sm);
109
110        if ui.is_rect_visible(rect) {
111            let hovered = response.hovered();
112            let pressed = response.is_pointer_button_down_on();
113            let variant = if self.selected == Some(true) {
114                ButtonVariant::Primary
115            } else {
116                ButtonVariant::Ghost
117            };
118            ui.painter().rect_filled(
119                rect,
120                ui.active_theme().radius.sm,
121                variant.bg_color(ui, hovered, pressed),
122            );
123
124            let icon_color = if self.selected == Some(true) {
125                variant.fg_color(ui)
126            } else {
127                ui.style().interact(&response).fg_stroke.color
128            };
129            let icon_size = Vec2::splat(self.size.icon_pixels(self.icon_scale));
130            self.icon
131                .as_image_tinted(icon_size, icon_color)
132                .paint_at(ui, Rect::from_center_size(rect.center(), icon_size));
133        }
134
135        let tooltip = self.tooltip.as_deref();
136        finish(
137            response,
138            self.selected,
139            tooltip.unwrap_or_default(),
140            tooltip,
141        )
142    }
143}