use crate::Widget; use core::convert::{TryFrom, TryInto}; use core::ptr::NonNull; use embedded_graphics::pixelcolor::{Rgb565, Rgb888}; pub type LvResult = Result; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum LvError { InvalidReference, Uninitialized, LvOOMemory, AlreadyInUse, } #[derive(Clone)] pub struct Color { pub(crate) raw: lvgl_sys::lv_color_t, } impl Color { pub fn from_rgb((r, g, b): (u8, u8, u8)) -> Self { let raw = unsafe { lvgl_sys::_LV_COLOR_MAKE(r, g, b) }; Self { raw } } pub fn from_raw(raw: lvgl_sys::lv_color_t) -> Self { Self { raw } } pub fn r(&self) -> u8 { unsafe { lvgl_sys::_LV_COLOR_GET_R(self.raw) as u8 } } pub fn g(&self) -> u8 { unsafe { lvgl_sys::_LV_COLOR_GET_G(self.raw) as u8 } } pub fn b(&self) -> u8 { unsafe { lvgl_sys::_LV_COLOR_GET_B(self.raw) as u8 } } } impl From for Rgb888 { fn from(color: Color) -> Self { unsafe { Rgb888::new( lvgl_sys::_LV_COLOR_GET_R(color.raw) as u8, lvgl_sys::_LV_COLOR_GET_G(color.raw) as u8, lvgl_sys::_LV_COLOR_GET_B(color.raw) as u8, ) } } } impl From for Rgb565 { fn from(color: Color) -> Self { unsafe { Rgb565::new( lvgl_sys::_LV_COLOR_GET_R(color.raw) as u8, lvgl_sys::_LV_COLOR_GET_G(color.raw) as u8, lvgl_sys::_LV_COLOR_GET_B(color.raw) as u8, ) } } } /// Events are triggered in LVGL 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(()) // } // } } } } impl From> for lvgl_sys::lv_event_t { fn from(event: Event) -> Self { let native_event = match event { Event::Pressed => lvgl_sys::LV_EVENT_PRESSED, Event::Pressing => lvgl_sys::LV_EVENT_PRESSING, Event::PressLost => lvgl_sys::LV_EVENT_PRESS_LOST, Event::ShortClicked => lvgl_sys::LV_EVENT_SHORT_CLICKED, Event::Clicked => lvgl_sys::LV_EVENT_CLICKED, Event::LongPressed => lvgl_sys::LV_EVENT_LONG_PRESSED, Event::LongPressedRepeat => lvgl_sys::LV_EVENT_LONG_PRESSED_REPEAT, Event::Released => lvgl_sys::LV_EVENT_RELEASED, // TODO: handle all types... _ => lvgl_sys::LV_EVENT_CLICKED, }; native_event as lvgl_sys::lv_event_t } } /// 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: Widget + Sized, 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 = 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, InTopMid, InTopRight, InBottomLeft, InBottomMid, InBottomRight, InLeftMid, InRightMid, OutTopLeft, OutTopMid, OutTopRight, OutBottomLeft, OutBottomMid, OutBottomRight, OutLeftTop, OutLeftMid, OutLeftBottom, OutRightTop, OutRightMid, OutRightBottom, } impl Into for Align { fn into(self) -> u8 { let native = match self { Align::Center => lvgl_sys::LV_ALIGN_CENTER, Align::InTopLeft => lvgl_sys::LV_ALIGN_IN_TOP_LEFT, Align::InTopMid => lvgl_sys::LV_ALIGN_IN_TOP_MID, Align::InTopRight => lvgl_sys::LV_ALIGN_IN_TOP_RIGHT, Align::InBottomLeft => lvgl_sys::LV_ALIGN_IN_BOTTOM_LEFT, Align::InBottomMid => lvgl_sys::LV_ALIGN_IN_BOTTOM_MID, Align::InBottomRight => lvgl_sys::LV_ALIGN_IN_BOTTOM_RIGHT, Align::InLeftMid => lvgl_sys::LV_ALIGN_IN_LEFT_MID, Align::InRightMid => lvgl_sys::LV_ALIGN_IN_RIGHT_MID, Align::OutTopLeft => lvgl_sys::LV_ALIGN_OUT_TOP_LEFT, Align::OutTopMid => lvgl_sys::LV_ALIGN_OUT_TOP_MID, Align::OutTopRight => lvgl_sys::LV_ALIGN_OUT_TOP_RIGHT, Align::OutBottomLeft => lvgl_sys::LV_ALIGN_OUT_BOTTOM_LEFT, Align::OutBottomMid => lvgl_sys::LV_ALIGN_OUT_BOTTOM_MID, Align::OutBottomRight => lvgl_sys::LV_ALIGN_OUT_BOTTOM_RIGHT, Align::OutLeftTop => lvgl_sys::LV_ALIGN_OUT_LEFT_TOP, Align::OutLeftMid => lvgl_sys::LV_ALIGN_OUT_LEFT_MID, Align::OutLeftBottom => lvgl_sys::LV_ALIGN_OUT_LEFT_BOTTOM, Align::OutRightTop => lvgl_sys::LV_ALIGN_OUT_RIGHT_TOP, Align::OutRightMid => lvgl_sys::LV_ALIGN_OUT_RIGHT_MID, Align::OutRightBottom => lvgl_sys::LV_ALIGN_OUT_RIGHT_BOTTOM, }; native as u8 } } pub enum Animation { ON, OFF, } impl From for lvgl_sys::lv_anim_enable_t { fn from(anim: Animation) -> Self { match anim { Animation::ON => lvgl_sys::LV_ANIM_ON as u8, Animation::OFF => lvgl_sys::LV_ANIM_OFF as u8, } } } #[cfg(test)] mod test { use super::*; use lvgl_sys; #[test] fn color_properties_accessible() { let color = Color::from_rgb((206, 51, 255)); if lvgl_sys::LV_COLOR_DEPTH == 32 { assert_eq!(color.r(), 206); assert_eq!(color.g(), 51); assert_eq!(color.b(), 255); } else if lvgl_sys::LV_COLOR_DEPTH == 16 { assert_eq!(color.r(), 25); assert_eq!(color.g(), 12); assert_eq!(color.b(), 31); } } }