rlvgl-widgets 0.1.6

Built-in widgets for rlvgl.
Documentation
//! Binary on/off switch widget.

use rlvgl_core::draw::{draw_widget_bg, fill_rounded_rect};
use rlvgl_core::event::Event;
use rlvgl_core::renderer::Renderer;
use rlvgl_core::style::Style;
use rlvgl_core::widget::{Color, Rect, Widget};

/// Toggle switch with a sliding knob.
pub struct Switch {
    bounds: Rect,
    /// Visual styling for the track.
    pub style: Style,
    /// Color of the sliding knob.
    pub knob_color: Color,
    on: bool,
}

impl Switch {
    /// Create a new switch.
    pub fn new(bounds: Rect) -> Self {
        Self {
            bounds,
            style: Style::default(),
            knob_color: Color(0, 0, 0, 255),
            on: false,
        }
    }

    /// Return whether the switch is currently on.
    pub fn is_on(&self) -> bool {
        self.on
    }

    /// Set the switch state programmatically.
    pub fn set_on(&mut self, value: bool) {
        self.on = value;
    }
}

impl Widget for Switch {
    fn bounds(&self) -> Rect {
        self.bounds
    }

    fn draw(&self, renderer: &mut dyn Renderer) {
        let a = self.style.alpha;
        let r = self.style.radius;
        // Draw the background track.
        draw_widget_bg(renderer, self.bounds, &self.style);

        // Draw the knob on the left or right half depending on state.
        let knob_width = self.bounds.width / 2;
        let knob_rect = if self.on {
            Rect {
                x: self.bounds.x + self.bounds.width - knob_width,
                y: self.bounds.y,
                width: knob_width,
                height: self.bounds.height,
            }
        } else {
            Rect {
                x: self.bounds.x,
                y: self.bounds.y,
                width: knob_width,
                height: self.bounds.height,
            }
        };
        fill_rounded_rect(renderer, knob_rect, self.knob_color.with_alpha(a), r);
    }

    fn handle_event(&mut self, event: &Event) -> bool {
        if let Event::PressRelease { x, y } = event {
            let inside = *x >= self.bounds.x
                && *x < self.bounds.x + self.bounds.width
                && *y >= self.bounds.y
                && *y < self.bounds.y + self.bounds.height;
            if inside {
                self.on = !self.on;
                return true;
            }
        }
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rlvgl_core::{event::Event, widget::Widget};

    #[test]
    fn switch_toggle_and_bounds() {
        let rect = Rect {
            x: 0,
            y: 0,
            width: 40,
            height: 20,
        };
        let mut sw = Switch::new(rect);
        assert_eq!(sw.bounds().x, rect.x);
        assert_eq!(sw.bounds().y, rect.y);
        assert_eq!(sw.bounds().width, rect.width);
        assert_eq!(sw.bounds().height, rect.height);
        let evt = Event::PressRelease { x: 5, y: 5 };
        assert!(sw.handle_event(&evt));
        assert!(sw.is_on());
    }
}