Skip to main content

egui_components/
switch.rs

1//! `Switch` widget (toggle).
2
3use egui::{vec2, Color32, Response, Sense, Stroke, Ui, Widget};
4use egui_components_theme::{mix, Theme};
5
6pub struct Switch<'a> {
7    on: &'a mut bool,
8    disabled: bool,
9}
10
11impl<'a> Switch<'a> {
12    pub fn new(on: &'a mut bool) -> Self {
13        Self { on, disabled: false }
14    }
15    pub fn disabled(mut self, d: bool) -> Self {
16        self.disabled = d;
17        self
18    }
19}
20
21impl<'a> Widget for Switch<'a> {
22    fn ui(self, ui: &mut Ui) -> Response {
23        let theme = Theme::get(ui.ctx());
24        let m = theme.metrics;
25        let c = theme.colors;
26
27        let desired = vec2(m.switch_width, m.switch_height);
28        let sense = if self.disabled { Sense::hover() } else { Sense::click() };
29        let (rect, mut response) = ui.allocate_exact_size(desired, sense);
30
31        if response.clicked() && !self.disabled {
32            *self.on = !*self.on;
33            response.mark_changed();
34        }
35
36        if ui.is_rect_visible(rect) {
37            // Animate the thumb.
38            let t = ui
39                .ctx()
40                .animate_bool_with_time(response.id, *self.on, 0.12);
41
42            let track_color = if *self.on {
43                c.primary_background
44            } else {
45                c.switch_background
46            };
47            let track_color = if self.disabled { fade(track_color) } else { track_color };
48
49            let painter = ui.painter();
50            let radius = egui::CornerRadius::same((m.switch_height * 0.5) as u8);
51            painter.rect_filled(rect, radius, track_color);
52
53            let thumb_d = m.switch_height - m.switch_thumb_padding * 2.0;
54            let thumb_y = rect.center().y;
55            let off_x = rect.left() + m.switch_thumb_padding + thumb_d * 0.5;
56            let on_x = rect.right() - m.switch_thumb_padding - thumb_d * 0.5;
57            let thumb_x = off_x + (on_x - off_x) * t;
58            let thumb_color = if *self.on {
59                c.primary_foreground
60            } else {
61                Color32::WHITE
62            };
63            let thumb_color = if self.disabled { fade(thumb_color) } else { thumb_color };
64
65            painter.circle(
66                egui::pos2(thumb_x, thumb_y),
67                thumb_d * 0.5,
68                thumb_color,
69                Stroke::NONE,
70            );
71
72            if response.has_focus() {
73                painter.rect_stroke(
74                    rect.expand(2.0),
75                    egui::CornerRadius::same((m.switch_height * 0.5 + 2.0) as u8),
76                    theme.focus_ring(),
77                    egui::StrokeKind::Outside,
78                );
79            }
80
81            if !self.disabled && response.hovered() {
82                ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
83            }
84        }
85
86        response
87    }
88}
89
90fn fade(c: Color32) -> Color32 {
91    mix(c, Color32::from_rgba_unmultiplied(0, 0, 0, 0), 0.4)
92}