Skip to main content

rlvgl_widgets/
click_area.rs

1//! Transparent click-area widget used as the rlvgl-side analogue of
2//! QML's `MouseArea`. Renders nothing; reports clicks through an
3//! optional callback the caller registers with [`Self::set_on_click`].
4
5use alloc::boxed::Box;
6
7use rlvgl_core::event::Event;
8use rlvgl_core::renderer::Renderer;
9use rlvgl_core::widget::{Rect, Widget};
10
11type ClickHandler = Box<dyn FnMut(&mut ClickArea)>;
12
13/// Transparent rectangular hit area with an optional click callback.
14///
15/// Designed for the QML `MouseArea` lowering shipped by phase QT-04d
16/// (`docs/qt-support/04d-mousearea.md`). The widget intentionally
17/// has no visual: its [`Widget::draw`] is a no-op so the area never
18/// occludes its parent's pixels. Hit-testing happens on
19/// [`Event::PressRelease`].
20pub struct ClickArea {
21    bounds: Rect,
22    on_click: Option<ClickHandler>,
23}
24
25impl ClickArea {
26    /// Create a click area covering `bounds`.
27    pub fn new(bounds: Rect) -> Self {
28        Self {
29            bounds,
30            on_click: None,
31        }
32    }
33
34    /// Register a callback invoked when a click is released inside
35    /// `bounds`. The callback receives `&mut self` so it can mutate
36    /// the click area itself (e.g. resize after a click) — though
37    /// QT-04d's emitted closures typically only mutate `ScreenState`
38    /// captured by reference.
39    pub fn set_on_click<F: FnMut(&mut Self) + 'static>(&mut self, handler: F) {
40        self.on_click = Some(Box::new(handler));
41    }
42
43    fn inside_bounds(&self, x: i32, y: i32) -> bool {
44        let b = self.bounds;
45        x >= b.x && x < b.x + b.width && y >= b.y && y < b.y + b.height
46    }
47}
48
49impl Widget for ClickArea {
50    fn bounds(&self) -> Rect {
51        self.bounds
52    }
53
54    /// No-op — the click area is transparent.
55    fn draw(&self, _renderer: &mut dyn Renderer) {}
56
57    fn handle_event(&mut self, event: &Event) -> bool {
58        match event {
59            Event::PressRelease { x, y } if self.inside_bounds(*x, *y) => {
60                if let Some(mut cb) = self.on_click.take() {
61                    cb(self);
62                    self.on_click = Some(cb);
63                }
64                true
65            }
66            _ => false,
67        }
68    }
69}