diff --git a/examples/bar.rs b/examples/bar.rs index 0bc6d77..78636e2 100644 --- a/examples/bar.rs +++ b/examples/bar.rs @@ -45,6 +45,12 @@ fn main() -> Result<(), String> { loading_lbl.set_text("Loading..."); loading_lbl.set_align(&mut bar, Align::OutTopMid, 0, -10); + loading_lbl.on_event(|mut this, event| { + if let lvgl::Event::Clicked = event { + this.set_text("Clicked!"); + } + }); + let mut loading_style = Style::new(); loading_style.set_text_color(Color::from_rgb((255, 255, 255))); loading_lbl.set_style(loading_style); diff --git a/lvgl/src/global.rs b/lvgl/src/global.rs index 521af32..2081913 100644 --- a/lvgl/src/global.rs +++ b/lvgl/src/global.rs @@ -1,4 +1,4 @@ -use crate::{DisplayDriver, ObjectX}; +use crate::{DisplayDriver, Object, ObjectX}; use alloc::boxed::Box; use core::marker::PhantomData; use core::ptr; diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index 84f40f6..d5179e0 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -1,30 +1,34 @@ use alloc::boxed::Box; +use core::convert::{TryFrom, TryInto}; use core::mem; use core::ptr; +use core::ptr::NonNull; use embedded_graphics::pixelcolor::{Rgb565, Rgb888}; use lvgl_sys; +/// 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 pub struct ObjectX { raw: ptr::NonNull, } -impl ObjectX { - pub(crate) fn from_raw(raw: ptr::NonNull) -> Self { - Self { raw } - } -} - impl NativeObject for ObjectX { fn raw(&self) -> ptr::NonNull { unsafe { ptr::NonNull::new_unchecked(self.raw.as_ptr()) } } } +/// A wrapper for all LittlevGL common operations on generic objects. pub trait Object: NativeObject { + type SpecialEvent; + + unsafe fn from_raw(raw_pointer: ptr::NonNull) -> Self; + fn set_pos(&mut self, x: i16, y: i16) { unsafe { lvgl_sys::lv_obj_set_pos( @@ -103,7 +107,13 @@ pub trait Object: NativeObject { } } -impl Object for ObjectX {} +impl Object for ObjectX { + type SpecialEvent = (); + + unsafe fn from_raw(raw: ptr::NonNull) -> Self { + Self { raw } + } +} macro_rules! define_object { ($item:ident) => { @@ -111,13 +121,84 @@ macro_rules! define_object { core: $crate::support::ObjectX, } + impl $item { + pub fn on_event(&mut self, f: F) + where + F: FnMut( + alloc::boxed::Box, + $crate::support::Event<::SpecialEvent>, + ), + { + unsafe { + let mut raw = self.raw(); + let obj = raw.as_mut(); + let user_closure = alloc::boxed::Box::new(f); + obj.user_data = alloc::boxed::Box::into_raw(user_closure) as *mut cty::c_void; + lvgl_sys::lv_obj_set_event_cb( + obj, + lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::), + ); + } + } + } + impl $crate::support::NativeObject for $item { fn raw(&self) -> core::ptr::NonNull { self.core.raw() } } - impl $crate::support::Object for $item {} + impl $crate::support::Object for $item { + type SpecialEvent = (); + + unsafe fn from_raw(raw_pointer: core::ptr::NonNull) -> Self { + Self { + core: $crate::support::ObjectX::from_raw(raw_pointer), + } + } + } + }; + ($item:ident, $event_type:ident) => { + pub struct $item { + core: $crate::support::ObjectX, + } + + impl $item { + pub fn on_event(&mut self, f: F) + where + F: FnMut( + Box, + $crate::support::Event<::SpecialEvent>, + ), + { + unsafe { + let mut raw = self.raw(); + let obj = raw.as_mut(); + let user_closure = alloc::boxed::Box::new(f); + obj.user_data = alloc::boxed::Box::into_raw(user_closure) as *mut cty::c_void; + lvgl_sys::lv_obj_set_event_cb( + obj, + lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::), + ); + } + } + } + + impl $crate::support::NativeObject for $item { + fn raw(&self) -> core::ptr::NonNull { + self.core.raw() + } + } + + impl $crate::support::Object for $item { + type SpecialEvent = $event_type; + + unsafe fn from_raw(raw_pointer: core::ptr::NonNull) -> Self { + Self { + core: $crate::support::ObjectX::from_raw(raw_pointer), + } + } + } }; } @@ -225,6 +306,100 @@ impl From for Rgb565 { } } +/// Events are triggered in LittlevGL when something happens which might be interesting to +/// the user, e.g. if an object: +/// - is clicked +/// - is dragged +/// - its value has changed, etc. +/// +/// All objects (such as Buttons/Labels/Sliders etc.) receive these generic events +/// regardless of their type. +pub enum Event { + /// The object has been pressed + Pressed, + + /// The object is being pressed (sent continuously while pressing) + Pressing, + + /// The input device is still being pressed but is no longer on the object + PressLost, + + /// Released before `long_press_time` config time. Not called if dragged. + ShortClicked, + + /// Called on release if not dragged (regardless to long press) + Clicked, + + /// Pressing for `long_press_time` config time. Not called if dragged. + LongPressed, + + /// Called after `long_press_time` config in every `long_press_rep_time` ms. Not + /// called if dragged. + LongPressedRepeat, + + /// Called in every case when the object has been released even if it was dragged. Not called + /// if slid from the object while pressing and released outside of the object. In this + /// case, `Event<_>::PressLost` is sent. + Released, + + /// Pointer-like input devices events (E.g. mouse or touchpad) + Pointer(PointerEvent), + + /// Special event for the object type + Special(T), +} + +impl TryFrom for Event { + type Error = (); + + fn try_from(value: u8) -> Result { + match value as u32 { + lvgl_sys::LV_EVENT_PRESSED => Ok(Event::Pressed), + lvgl_sys::LV_EVENT_PRESSING => Ok(Event::Pressing), + lvgl_sys::LV_EVENT_PRESS_LOST => Ok(Event::PressLost), + lvgl_sys::LV_EVENT_SHORT_CLICKED => Ok(Event::ShortClicked), + lvgl_sys::LV_EVENT_CLICKED => Ok(Event::Clicked), + lvgl_sys::LV_EVENT_LONG_PRESSED => Ok(Event::LongPressed), + lvgl_sys::LV_EVENT_LONG_PRESSED_REPEAT => Ok(Event::LongPressedRepeat), + lvgl_sys::LV_EVENT_RELEASED => Ok(Event::Released), + _ => Err(()), + // _ => { + // if let Ok(special_event_type) = S::try_from(value) { + // Ok(Event::Special(special_event_type)) + // } else { + // Err(()) + // } + // } + } + } +} + +/// These events are sent only by pointer-like input devices (E.g. mouse or touchpad) +pub enum PointerEvent { + DragBegin, + DragEnd, + DragThrowBegin, +} + +pub(crate) unsafe extern "C" fn event_callback( + obj: *mut lvgl_sys::lv_obj_t, + event: lvgl_sys::lv_event_t, +) where + T: Object + Sized, + F: FnMut(Box, 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)); + // 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 + user_closure(object, event); + } + } +} + pub enum Align { Center, InTopLeft, diff --git a/lvgl/src/widgets/bar.rs b/lvgl/src/widgets/bar.rs index 7315efa..41f741d 100644 --- a/lvgl/src/widgets/bar.rs +++ b/lvgl/src/widgets/bar.rs @@ -1,5 +1,6 @@ use crate::support::Animation; use crate::support::{NativeObject, ObjectX}; +use crate::Object; use core::ptr; use lvgl_sys; @@ -10,12 +11,12 @@ impl Bar { where C: NativeObject, { - let raw = unsafe { + unsafe { let ptr = lvgl_sys::lv_bar_create(parent.raw().as_mut(), ptr::null_mut()); - ptr::NonNull::new_unchecked(ptr) - }; - let core = ObjectX::from_raw(raw); - Self { core } + let raw = ptr::NonNull::new_unchecked(ptr); + let core = ObjectX::from_raw(raw); + Self { core } + } } /// Set minimum and the maximum values of the bar diff --git a/lvgl/src/widgets/button.rs b/lvgl/src/widgets/button.rs index 00ceed9..6e79071 100644 --- a/lvgl/src/widgets/button.rs +++ b/lvgl/src/widgets/button.rs @@ -1,4 +1,4 @@ -use crate::{NativeObject, ObjectX}; +use crate::{NativeObject, Object, ObjectX}; use core::ptr; define_object!(Button); @@ -8,11 +8,11 @@ impl Button { where C: NativeObject, { - let raw = unsafe { + unsafe { let ptr = lvgl_sys::lv_btn_create(parent.raw().as_mut(), ptr::null_mut()); - ptr::NonNull::new_unchecked(ptr) - }; - let core = ObjectX::from_raw(raw); - Self { core } + let raw = ptr::NonNull::new_unchecked(ptr); + let core = ObjectX::from_raw(raw); + Self { core } + } } } diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index c8c573e..861d1fe 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -1,4 +1,4 @@ -use crate::{NativeObject, ObjectX}; +use crate::{NativeObject, Object, ObjectX}; use core::ptr; use cstr_core::CString; @@ -16,12 +16,12 @@ impl Label { where C: NativeObject, { - let raw = unsafe { + unsafe { let ptr = lvgl_sys::lv_label_create(parent.raw().as_mut(), ptr::null_mut()); - ptr::NonNull::new_unchecked(ptr) - }; - let core = ObjectX::from_raw(raw); - Self { core } + let raw = ptr::NonNull::new_unchecked(ptr); + let core = ObjectX::from_raw(raw); + Self { core } + } } pub fn set_text(&mut self, text: &str) {