egui_components/
switch.rs1use 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 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}