egui_charts/ui/drawing_toolbar/components/
icon_button.rs1use crate::icons::Icon;
7use crate::localization as l10n;
8use crate::theme::ThemeExt;
9use egui::{
10 Color32, Rect, RectAlign, Response, Role, Sense, Stroke, StrokeKind, Tooltip, Ui, Vec2,
11 WidgetInfo,
12};
13
14use super::svg_helpers::{rail_icon_color, rail_icon_size, render_svg_at_rect_themed};
15use crate::tokens::DESIGN_TOKENS;
16
17pub fn draw_icon_btn(
36 ui: &mut Ui,
37 icon: &Icon,
38 tooltip: &'static str,
39 is_active: bool,
40 size: f32,
41) -> Response {
42 let (rect, response) = ui.allocate_exact_size(Vec2::splat(size), Sense::click());
43 crate::styles::focus::draw_focus_ring(ui, &response, ui.active_theme().radius.sm);
44
45 let bg_color = if is_active && response.hovered() {
46 super::active_hover_color(ui)
47 } else if is_active {
48 ui.visuals().widgets.active.bg_fill
49 } else if response.hovered() {
50 ui.visuals().widgets.hovered.bg_fill
51 } else {
52 Color32::TRANSPARENT };
54
55 ui.painter()
56 .rect_filled(rect, ui.active_theme().radius.sm, bg_color);
57
58 render_svg_at_rect_themed(ui, icon, rect, response.hovered(), is_active);
59
60 let translated_label = l10n::text(ui.ctx(), tooltip);
61 response.widget_info(|| {
62 WidgetInfo::selected(
63 Role::Button,
64 response.enabled(),
65 is_active,
66 &translated_label,
67 )
68 });
69 response.on_hover_text(l10n::text(ui.ctx(), tooltip))
70}
71
72pub fn draw_toggle_btn(
88 ui: &mut Ui,
89 icon: &Icon,
90 tooltip: &'static str,
91 is_toggled: bool,
92 size: f32,
93) -> Response {
94 draw_icon_btn(ui, icon, tooltip, is_toggled, size)
95}
96
97pub fn draw_icon_button_padded(
115 ui: &mut Ui,
116 icon: &Icon,
117 tooltip: &str,
118 is_active: bool,
119 icon_size: f32,
120 padding: f32,
121) -> Response {
122 let size = Vec2::splat(icon_size + padding * 2.0);
124 let (rect, response) = ui.allocate_exact_size(size, Sense::click());
125 crate::styles::focus::draw_focus_ring(ui, &response, ui.active_theme().radius.sm);
126
127 let bg_color = if is_active {
129 ui.visuals().widgets.active.bg_fill
130 } else if response.is_pointer_button_down_on() {
131 super::active_hover_color(ui)
132 } else if response.hovered() {
133 ui.visuals().widgets.hovered.bg_fill
134 } else {
135 Color32::TRANSPARENT };
137
138 let icon_button_rect = egui::Rect::from_center_size(rect.center(), Vec2::splat(icon_size));
140 ui.painter()
141 .rect_filled(icon_button_rect, ui.active_theme().radius.sm, bg_color);
142
143 let is_hovered = response.hovered() || response.is_pointer_button_down_on();
144 render_svg_at_rect_themed(ui, icon, icon_button_rect, is_hovered, is_active);
145
146 let translated_label = l10n::text(ui.ctx(), tooltip);
147 response.widget_info(|| {
148 WidgetInfo::selected(
149 Role::Button,
150 response.enabled(),
151 is_active,
152 &translated_label,
153 )
154 });
155 response.on_hover_text(l10n::text(ui.ctx(), tooltip))
156}
157
158pub fn draw_tool_button(ui: &mut Ui, icon: &Icon, tooltip: &str, is_selected: bool) -> Response {
180 draw_rail_button(ui, icon, tooltip, is_selected, false)
181}
182
183pub(crate) fn draw_latched_button(ui: &mut Ui, icon: &Icon, tooltip: &str, on: bool) -> Response {
185 draw_rail_button(ui, icon, tooltip, on, true)
186}
187
188fn draw_rail_button(
189 ui: &mut Ui,
190 icon: &Icon,
191 tooltip: &str,
192 is_selected: bool,
193 latched: bool,
194) -> Response {
195 let tokens = &DESIGN_TOKENS.sizing.toolbar;
196 let margin = Vec2::new(tokens.hover_margin_h - 0.5, tokens.hover_margin_v - 0.5);
197
198 let (rect, response) = ui.allocate_exact_size(
199 Vec2::new(tokens.button_width, tokens.button_height),
200 Sense::click(),
201 );
202 let translated_label = l10n::text(ui.ctx(), tooltip);
203 response.widget_info(|| {
204 WidgetInfo::selected(
205 Role::Button,
206 response.enabled(),
207 is_selected,
208 &translated_label,
209 )
210 });
211
212 if ui.is_rect_visible(rect) {
213 let theme = ui.active_theme();
214 let inner_rect = rect.shrink2(margin);
215
216 let selected_fill = if latched {
217 if theme.dark {
218 theme.ui.text
219 } else {
220 Color32::from_gray(26)
221 }
222 } else if theme.dark {
223 theme.ui.btn_bg_active
224 } else {
225 Color32::from_gray(235)
226 };
227 let selected_ink = if latched {
228 if theme.dark {
229 theme.ui.panel_bg
230 } else {
231 Color32::WHITE
232 }
233 } else {
234 rail_icon_color(ui)
235 };
236 let bg = match (is_selected, response.hovered()) {
237 (true, _) => Some(selected_fill),
238 (false, true) => Some(ui.visuals().widgets.hovered.bg_fill),
239 (false, false) => None,
240 };
241 if let Some(bg) = bg {
242 ui.painter().rect_filled(inner_rect, theme.radius.md, bg);
243 }
244 if response.has_focus() {
247 let (ring, ring_rect) = if is_selected {
248 (selected_ink, inner_rect.shrink(DESIGN_TOKENS.stroke.thick))
249 } else {
250 (crate::styles::focus::focus_ring_color(ui), inner_rect)
251 };
252 ui.painter().rect_stroke(
253 ring_rect,
254 theme.radius.md,
255 Stroke::new(DESIGN_TOKENS.stroke.thick, ring),
256 StrokeKind::Inside,
257 );
258 }
259
260 let icon_rect = Rect::from_center_size(rect.center(), rail_icon_size(icon));
262 let icon_color = if !ui.is_enabled() {
263 theme.ui.text_disabled
264 } else if is_selected {
265 selected_ink
266 } else {
267 rail_icon_color(ui)
268 };
269 icon.as_image_tinted(icon_rect.size(), icon_color)
270 .paint_at(ui, icon_rect);
271 }
272
273 let mut tip = Tooltip::for_enabled(&response);
274 tip.popup = tip.popup.align(RectAlign::RIGHT);
275 tip.show(|ui| ui.label(l10n::text(ui.ctx(), tooltip)));
276 response
277}