Skip to main content

rlvgl_widgets/
switch.rs

1//! Binary on/off switch widget.
2
3use rlvgl_core::draw::{draw_widget_bg, fill_rounded_rect};
4use rlvgl_core::event::Event;
5use rlvgl_core::renderer::Renderer;
6use rlvgl_core::style::Style;
7use rlvgl_core::widget::{Color, Rect, Widget};
8
9/// Toggle switch with a sliding knob.
10pub struct Switch {
11    bounds: Rect,
12    /// Visual styling for the track.
13    pub style: Style,
14    /// Color of the sliding knob.
15    pub knob_color: Color,
16    on: bool,
17}
18
19impl Switch {
20    /// Create a new switch.
21    pub fn new(bounds: Rect) -> Self {
22        Self {
23            bounds,
24            style: Style::default(),
25            knob_color: Color(0, 0, 0, 255),
26            on: false,
27        }
28    }
29
30    /// Return whether the switch is currently on.
31    pub fn is_on(&self) -> bool {
32        self.on
33    }
34
35    /// Set the switch state programmatically.
36    pub fn set_on(&mut self, value: bool) {
37        self.on = value;
38    }
39}
40
41impl Widget for Switch {
42    fn bounds(&self) -> Rect {
43        self.bounds
44    }
45
46    fn draw(&self, renderer: &mut dyn Renderer) {
47        let a = self.style.alpha;
48        let r = self.style.radius;
49        // Draw the background track.
50        draw_widget_bg(renderer, self.bounds, &self.style);
51
52        // Draw the knob on the left or right half depending on state.
53        let knob_width = self.bounds.width / 2;
54        let knob_rect = if self.on {
55            Rect {
56                x: self.bounds.x + self.bounds.width - knob_width,
57                y: self.bounds.y,
58                width: knob_width,
59                height: self.bounds.height,
60            }
61        } else {
62            Rect {
63                x: self.bounds.x,
64                y: self.bounds.y,
65                width: knob_width,
66                height: self.bounds.height,
67            }
68        };
69        fill_rounded_rect(renderer, knob_rect, self.knob_color.with_alpha(a), r);
70    }
71
72    fn handle_event(&mut self, event: &Event) -> bool {
73        if let Event::PressRelease { x, y } = event {
74            let inside = *x >= self.bounds.x
75                && *x < self.bounds.x + self.bounds.width
76                && *y >= self.bounds.y
77                && *y < self.bounds.y + self.bounds.height;
78            if inside {
79                self.on = !self.on;
80                return true;
81            }
82        }
83        false
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use rlvgl_core::{event::Event, widget::Widget};
91
92    #[test]
93    fn switch_toggle_and_bounds() {
94        let rect = Rect {
95            x: 0,
96            y: 0,
97            width: 40,
98            height: 20,
99        };
100        let mut sw = Switch::new(rect);
101        assert_eq!(sw.bounds().x, rect.x);
102        assert_eq!(sw.bounds().y, rect.y);
103        assert_eq!(sw.bounds().width, rect.width);
104        assert_eq!(sw.bounds().height, rect.height);
105        let evt = Event::PressRelease { x: 5, y: 5 };
106        assert!(sw.handle_event(&evt));
107        assert!(sw.is_on());
108    }
109}