diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6338aa7..0084cd4 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -18,3 +18,7 @@ path = "demo.rs" [[example]] name = "bar" path = "bar.rs" + +[[example]] +name = "button_click" +path = "button_click.rs" diff --git a/examples/bar.rs b/examples/bar.rs index 78d7ea1..bc12850 100644 --- a/examples/bar.rs +++ b/examples/bar.rs @@ -58,7 +58,8 @@ fn main() -> Result<(), String> { loading_lbl.set_text("Loading..."); loading_lbl.set_align(&mut bar, Align::OutTopMid, 0, -10); - loading_lbl.on_event(|_, event| { + loading_lbl.on_event(|mut this, event| { + this.set_text("Loaded!"); if let lvgl::Event::Clicked = event { println!("Loaded!"); } diff --git a/examples/button_click.rs b/examples/button_click.rs new file mode 100644 index 0000000..830e8cc --- /dev/null +++ b/examples/button_click.rs @@ -0,0 +1,94 @@ +use embedded_graphics::pixelcolor::Rgb565; +use embedded_graphics::prelude::*; +use embedded_graphics_simulator::{ + OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window, +}; +use lvgl::widgets::{Bar, Button, Label}; +use lvgl::{self, Align, Animation, Color, DisplayDriver, Event, Object, Style, UI}; +use lvgl_sys; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread::sleep; +use std::time::Duration; + +fn main() -> Result<(), String> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new( + lvgl_sys::LV_HOR_RES_MAX, + lvgl_sys::LV_VER_RES_MAX, + )); + + let output_settings = OutputSettingsBuilder::new().scale(2).build(); + let mut window = Window::new("Bar Example", &output_settings); + + let mut ui = UI::init().unwrap(); + + // Implement and register your display: + let display_driver = DisplayDriver::new(&mut display); + ui.disp_drv_register(display_driver); + + // Create screen and widgets + let mut screen = ui.scr_act(); + + let mut screen_style = Style::new(); + screen_style.set_body_main_color(Color::from_rgb((0, 0, 0))); + screen_style.set_body_grad_color(Color::from_rgb((0, 0, 0))); + screen_style.set_body_radius(0); + screen.set_style(screen_style); + + // Create the button + let mut button = Button::new(&mut screen); + button.set_align(&mut screen, Align::InLeftMid, 30, 0); + button.set_size(180, 80); + let mut btn_lbl = Label::new(&mut button); + btn_lbl.set_text("Click me!"); + button.on_event(|_, event| { + if let lvgl::Event::Clicked = event { + println!("Clicked!"); + } + }); + + let threaded_ui = Arc::new(Mutex::new(ui)); + + let (stop_ch, read_ch) = mpsc::channel(); + let closure_ui = threaded_ui.clone(); + let tick_thr = std::thread::spawn(move || loop { + let period = Duration::from_millis(5); + + // Needs to be called periodically for LittlevGL internal timing calculations. + closure_ui.lock().unwrap().tick_inc(period); + + sleep(period); + if read_ch.try_recv().is_ok() { + break; + } + }); + + 'running: loop { + threaded_ui.lock().unwrap().task_handler(); + + window.update(&display); + for event in window.events() { + match event { + SimulatorEvent::MouseButtonUp { + mouse_btn: _, + point, + } => { + println!("Clicked on: {:?}", point); + // Send a event to the button directly + threaded_ui + .lock() + .unwrap() + .event_send(&mut button, Event::Clicked); + } + SimulatorEvent::Quit => break 'running, + _ => {} + } + } + + sleep(Duration::from_millis(25)); + } + + stop_ch.send(true).unwrap(); + tick_thr.join().unwrap(); + + Ok(()) +} diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index d8daa03..1cc605c 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -6,20 +6,30 @@ use core::ptr::NonNull; use embedded_graphics::pixelcolor::{Rgb565, Rgb888}; use lvgl_sys; +const PANIC_MESSAGE: &str = "Value was dropped by LittlevGL"; + /// Represents a native LittlevGL object pub trait NativeObject { /// Provide common way to access to the underlying native object pointer. fn raw(&self) -> ptr::NonNull; } -/// Stores the native LittlevGL raw pointer +/// Stores the native LittlevGL raw pointer. +/// +/// This is the parent object of all widget objects in `lvgl-rs`. +/// +/// # Panics +/// +/// Panics if LittlevGL internally deallocated the original object. pub struct ObjectX { - raw: ptr::NonNull, + // We use a raw pointer here because we do not control this memory address, it is controlled + // by LittlevGL's C code. + raw: *mut lvgl_sys::lv_obj_t, } impl NativeObject for ObjectX { fn raw(&self) -> ptr::NonNull { - unsafe { ptr::NonNull::new_unchecked(self.raw.as_ptr()) } + ptr::NonNull::new(self.raw).expect(PANIC_MESSAGE) } } @@ -111,7 +121,7 @@ impl Object for ObjectX { type SpecialEvent = (); unsafe fn from_raw(raw: ptr::NonNull) -> Self { - Self { raw } + Self { raw: raw.as_ptr() } } } @@ -125,7 +135,7 @@ macro_rules! define_object { pub fn on_event(&mut self, f: F) where F: FnMut( - alloc::boxed::Box, + Self, $crate::support::Event<::SpecialEvent>, ), { @@ -167,7 +177,7 @@ macro_rules! define_object { pub fn on_event(&mut self, f: F) where F: FnMut( - Box, + Self, $crate::support::Event<::SpecialEvent>, ), { @@ -404,12 +414,12 @@ pub(crate) unsafe extern "C" fn event_callback( event: lvgl_sys::lv_event_t, ) where T: Object + Sized, - F: FnMut(Box, Event), + F: FnMut(T, Event), { // convert the lv_event_t to lvgl-rs Event type if let Ok(event) = event.try_into() { if let Some(obj_ptr) = NonNull::new(obj) { - let object = Box::new(T::from_raw(obj_ptr)); + let object = T::from_raw(obj_ptr); // get the pointer from the Rust callback closure FnMut provided by users let user_closure = &mut *((*obj).user_data as *mut F); // call user callback closure