Skip to main content

minifb_ui/ui/
switch.rs

1pub struct Switch {
2    pub pos_x: usize,
3    pub pos_y: usize,
4    pub width: usize,
5    pub height: usize,
6
7    pub on: bool,
8    anim_t: f32,
9    pub anim_speed: f32,
10
11    pub track_color_off: crate::color::Color,
12    pub track_color_on: crate::color::Color,
13    pub thumb_color: crate::color::Color,
14    pub thumb_padding: f32,
15
16    pub label: Option<crate::ui::text::Text>,
17    pub label_size: f32,
18    pub label_color: crate::color::Color,
19    pub label_gap: usize,
20
21    lmb_was_down: bool,
22    toggled_this_frame: bool,
23}
24
25impl Default for Switch {
26    fn default() -> Self {
27        Self {
28            pos_x: 0,
29            pos_y: 0,
30            width: 50,
31            height: 26,
32            on: false,
33            anim_t: 0.0,
34            anim_speed: 0.08,
35            track_color_off: crate::color::Color::new(120, 120, 130),
36            track_color_on: crate::color::Color::new(52, 199, 89),
37            thumb_color: crate::color::Color::new(255, 255, 255),
38            thumb_padding: 2.0,
39            label: None,
40            label_size: 14.0,
41            label_color: crate::color::Color::new(200, 200, 200),
42            label_gap: 10,
43            lmb_was_down: false,
44            toggled_this_frame: false,
45        }
46    }
47}
48
49impl Switch {
50    pub fn position(mut self, x: usize, y: usize) -> Self {
51        self.pos_x = x;
52        self.pos_y = y;
53        self
54    }
55
56    pub fn size(mut self, width: usize, height: usize) -> Self {
57        self.width = width;
58        self.height = height;
59        self
60    }
61
62    pub fn default_on(mut self, on: bool) -> Self {
63        self.on = on;
64        self.anim_t = if on { 1.0 } else { 0.0 };
65        self
66    }
67
68    pub fn anim_speed(mut self, speed: f32) -> Self {
69        self.anim_speed = speed;
70        self
71    }
72
73    pub fn track_color_off(mut self, color: crate::color::Color) -> Self {
74        self.track_color_off = color;
75        self
76    }
77
78    pub fn track_color_on(mut self, color: crate::color::Color) -> Self {
79        self.track_color_on = color;
80        self
81    }
82
83    pub fn thumb_color(mut self, color: crate::color::Color) -> Self {
84        self.thumb_color = color;
85        self
86    }
87
88    pub fn thumb_padding(mut self, padding: f32) -> Self {
89        self.thumb_padding = padding;
90        self
91    }
92
93    pub fn label(mut self, text: &str, font: crate::ttf::Font, size: f32) -> Self {
94        self.label = Some(crate::ui::text::Text::new(text, font));
95        self.label_size = size;
96        self
97    }
98
99    pub fn label_color(mut self, color: crate::color::Color) -> Self {
100        self.label_color = color;
101        self
102    }
103
104    pub fn label_gap(mut self, gap: usize) -> Self {
105        self.label_gap = gap;
106        self
107    }
108
109    pub fn is_on(&self) -> bool {
110        self.on
111    }
112
113    pub fn set_on(&mut self, on: bool) {
114        self.on = on;
115    }
116
117    pub fn just_toggled(&self) -> bool {
118        self.toggled_this_frame
119    }
120
121    fn update(&mut self, window: &mut crate::window::Window) {
122        self.toggled_this_frame = false;
123
124        let mouse = window.get_mouse_state();
125        let lmb = mouse.lmb_clicked;
126        let lmb_just_pressed = lmb && !self.lmb_was_down;
127
128        if lmb_just_pressed {
129            let mx = mouse.pos_x;
130            let my = mouse.pos_y;
131            let in_bounds = mx >= self.pos_x as f32
132                && mx < (self.pos_x + self.width) as f32
133                && my >= self.pos_y as f32
134                && my < (self.pos_y + self.height) as f32;
135
136            if in_bounds {
137                self.on = !self.on;
138                self.toggled_this_frame = true;
139            }
140        }
141
142        self.lmb_was_down = lmb;
143
144        // Animate toward target
145        let target = if self.on { 1.0 } else { 0.0 };
146        let diff = target - self.anim_t;
147        if diff.abs() > 0.001 {
148            self.anim_t += diff.signum() * self.anim_speed;
149            self.anim_t = self.anim_t.clamp(0.0, 1.0);
150        } else {
151            self.anim_t = target;
152        }
153    }
154
155    pub fn draw(&mut self, window: &mut crate::window::Window) {
156        self.update(window);
157
158        let track_color = self.track_color_off.lerp(&self.track_color_on, self.anim_t);
159        let radius = self.height / 2;
160
161        // Draw track
162        window.draw_rect_f(
163            self.pos_x, self.pos_y,
164            self.width, self.height,
165            radius,
166            &track_color,
167            0,
168        );
169
170        // Compute thumb position
171        let thumb_radius = (self.height as f32 / 2.0) - self.thumb_padding;
172        let left_cx = self.pos_x as f32 + self.thumb_padding + thumb_radius;
173        let right_cx = self.pos_x as f32 + self.width as f32 - self.thumb_padding - thumb_radius;
174        let thumb_cx = left_cx + self.anim_t * (right_cx - left_cx);
175        let thumb_cy = self.pos_y as f32 + self.height as f32 / 2.0;
176
177        // Thumb shadow (subtle offset down)
178        window.draw_ellipse_f(
179            thumb_cx as isize,
180            (thumb_cy + 1.0) as isize,
181            thumb_radius as usize, thumb_radius as usize,
182            &crate::color::Color::rgba(0, 0, 0, 30),
183            0,
184        );
185
186        // Thumb
187        window.draw_ellipse_f(
188            thumb_cx as isize,
189            thumb_cy as isize,
190            thumb_radius as usize, thumb_radius as usize,
191            &self.thumb_color,
192            0,
193        );
194
195        // Label
196        if let Some(ref label) = self.label {
197            let label_x = self.pos_x + self.width + self.label_gap;
198            let label_h = self.label_size;
199            let label_y = self.pos_y as f32 + (self.height as f32 - label_h) / 2.0;
200            window.draw_text(label_x, label_y as usize, label, self.label_size, &self.label_color);
201        }
202    }
203}