egui_macview/controls/
toggle.rs1use crate::theme::MacTheme;
6use egui::{CornerRadius, Pos2, Response, Sense, Stroke, StrokeKind, Ui, Vec2, Widget};
7
8pub struct MacToggle<'a> {
28 value: &'a mut bool,
29 enabled: bool,
30 theme: Option<&'a MacTheme>,
31}
32
33impl<'a> MacToggle<'a> {
34 pub fn new(value: &'a mut bool) -> Self {
36 Self {
37 value,
38 enabled: true,
39 theme: None,
40 }
41 }
42
43 pub fn enabled(mut self, enabled: bool) -> Self {
45 self.enabled = enabled;
46 self
47 }
48
49 pub fn theme(mut self, theme: &'a MacTheme) -> Self {
51 self.theme = Some(theme);
52 self
53 }
54}
55
56impl<'a> Widget for MacToggle<'a> {
57 fn ui(self, ui: &mut Ui) -> Response {
58 let theme_cow = MacTheme::resolve(self.theme, ui.ctx());
59 let theme: &MacTheme = &theme_cow;
60
61 let metrics = theme.toggle();
62 let height = metrics.height.points();
63 let width = metrics.width.points();
64 let radius = metrics.corner_radius.points() as u8;
65
66 let desired_size = Vec2::new(width, height);
67 let sense = if self.enabled {
68 Sense::click()
69 } else {
70 Sense::hover()
71 };
72 let (rect, mut response) = ui.allocate_exact_size(desired_size, sense);
73 if self.enabled && response.clicked() {
74 *self.value = !*self.value;
75 response.mark_changed();
76 }
77
78 let spring = theme.motion().swiftui_bouncy.to_spring();
80 let spring_key = response.id.with("toggle_caspring");
81 let target_val = if *self.value { 1.0f32 } else { 0.0f32 };
82 let anim_progress = spring
83 .animate_f32(ui.ctx(), spring_key, target_val)
84 .clamp(0.0, 1.0);
85
86 let alpha_factor = if self.enabled {
87 1.0
88 } else {
89 theme.disabled_opacity()
90 };
91 let painter = ui.painter_at(rect);
92 let bg_color = theme
93 .control()
94 .lerp_to_gamma(theme.system_green(), anim_progress)
95 .gamma_multiply(alpha_factor);
96
97 painter.rect(
98 rect,
99 CornerRadius::same(radius),
100 bg_color,
101 Stroke::new(
102 metrics.border_width.points(),
103 theme.separator().gamma_multiply(alpha_factor),
104 ),
105 StrokeKind::Inside,
106 );
107
108 let knob_radius = metrics.knob_size.points() * 0.5;
109 let x_off = rect.min.x + metrics.knob_offset_off.points() + knob_radius;
110 let x_on = rect.min.x + metrics.knob_offset_on.points() + knob_radius;
111 let knob_x = x_off + (x_on - x_off) * anim_progress;
112 let knob_center = Pos2::new(knob_x, rect.center().y);
113
114 painter.circle_filled(
115 knob_center + Vec2::new(0.0, metrics.border_width.points()),
116 knob_radius,
117 theme.drop_shadow().gamma_multiply(alpha_factor),
118 );
119 painter.circle_filled(
120 knob_center,
121 knob_radius,
122 theme.knob_fill().gamma_multiply(alpha_factor),
123 );
124
125 response
126 }
127}