Skip to main content

rlvgl_widgets/
button.rs

1//! Interactive button widget with callback support.
2use alloc::{boxed::Box, string::String};
3use rlvgl_core::event::Event;
4use rlvgl_core::renderer::Renderer;
5use rlvgl_core::widget::{Rect, Widget};
6
7use crate::label::Label;
8use rlvgl_core::style::Style;
9
10type ClickHandler = Box<dyn FnMut(&mut Button)>;
11
12/// Clickable button widget.
13pub struct Button {
14    /// Bounding rectangle defining the clickable area.
15    bounds: Rect,
16    label: Label,
17    on_click: Option<ClickHandler>,
18}
19
20impl Button {
21    /// Create a new button with the provided label text.
22    pub fn new(text: impl Into<String>, bounds: Rect) -> Self {
23        Self {
24            bounds,
25            label: Label::new(text, bounds),
26            on_click: None,
27        }
28    }
29
30    /// Immutable access to the button's style.
31    pub fn style(&self) -> &Style {
32        &self.label.style
33    }
34
35    /// Mutable access to the button's style.
36    pub fn style_mut(&mut self) -> &mut Style {
37        &mut self.label.style
38    }
39
40    /// Update the label displayed on the button.
41    pub fn set_text(&mut self, text: impl Into<String>) {
42        self.label.set_text(text);
43    }
44
45    /// Retrieve the current button label.
46    pub fn text(&self) -> &str {
47        self.label.text()
48    }
49
50    /// Register a callback invoked when the button is released.
51    pub fn set_on_click<F: FnMut(&mut Self) + 'static>(&mut self, handler: F) {
52        self.on_click = Some(Box::new(handler));
53    }
54
55    /// Check if the given coordinates are inside the button's bounds.
56    fn inside_bounds(&self, x: i32, y: i32) -> bool {
57        let b = self.bounds;
58        x >= b.x && x < b.x + b.width && y >= b.y && y < b.y + b.height
59    }
60}
61
62impl Widget for Button {
63    fn bounds(&self) -> Rect {
64        self.bounds
65    }
66
67    fn draw(&self, renderer: &mut dyn Renderer) {
68        self.label.draw(renderer);
69    }
70
71    /// Delegate pointer events and invoke the click handler when released.
72    fn handle_event(&mut self, event: &Event) -> bool {
73        match event {
74            Event::PressRelease { x, y } if self.inside_bounds(*x, *y) => {
75                if let Some(mut cb) = self.on_click.take() {
76                    cb(self);
77                    self.on_click = Some(cb);
78                }
79                return true;
80            }
81            _ => {}
82        }
83        false
84    }
85}