API re-design #9
2 changed files with 212 additions and 1 deletions
207
lvgl/src/api.rs
Normal file
207
lvgl/src/api.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::time::Duration;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
// There can only be a single reference to LittlevGL library.
|
||||||
|
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
|
pub enum LvError {
|
||||||
|
AlreadyInUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UI {
|
||||||
|
// LittlevGL is not thread-safe by default.
|
||||||
|
_not_send: PhantomData<*const ()>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UI {
|
||||||
|
pub fn init() -> Result<Self, LvError> {
|
||||||
|
if LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) == false {
|
||||||
|
// lvgl_sys::lv_init();
|
||||||
|
Ok(Self {
|
||||||
|
_not_send: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(LvError::AlreadyInUse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disp_drv_register(&mut self, _display: &mut DisplayDriver) {
|
||||||
|
// register it
|
||||||
|
// lvgl_sys::lv_disp_drv_register(&mut disp_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick_inc(&mut self, _tick_period: Duration) {
|
||||||
|
// lvgl_sys::lv_tick_inc(tick_period);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn task_handler(&mut self) {
|
||||||
|
// lvgl_sys::lv_task_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DisplayDriver {
|
||||||
|
refresh_lines: u32,
|
||||||
|
current_screen: Screen,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayDriver {
|
||||||
|
pub fn new(refresh_lines: u32) -> Self {
|
||||||
|
Self{ refresh_lines, current_screen: Screen::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure cannot delete the current loaded screen
|
||||||
|
pub fn load_scr(&mut self, screen: Screen) {
|
||||||
|
self.current_screen = screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scr_act(&mut self) -> &mut Screen {
|
||||||
|
&mut self.current_screen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LvObject {
|
||||||
|
fn label_create(&self) -> Label where Self: Sized {
|
||||||
|
Label{ parent: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn btn_create<F>(&self) -> Button<F> where Self: Sized, F: FnMut() {
|
||||||
|
Button{ parent: self, event_callback: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Screen {
|
||||||
|
_not_send: PhantomData<*const ()>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Screen {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { _not_send: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LvObject for Screen {}
|
||||||
|
|
||||||
|
pub struct Label<'a> {
|
||||||
|
parent: &'a dyn LvObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Label<'a> {
|
||||||
|
pub fn set_text(&mut self, _text: &str) {
|
||||||
|
// set text call to unsafe...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LvObject for Label<'a> {}
|
||||||
|
|
||||||
|
pub struct Button<'a, F> where F: FnMut() {
|
||||||
|
parent: &'a dyn LvObject,
|
||||||
|
event_callback: Option<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, F> Button<'a, F> where F: FnMut() {
|
||||||
|
pub fn on_event(&mut self, callback: F) {
|
||||||
|
// add callback
|
||||||
|
self.event_callback = Some(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, F> LvObject for Button<'a, F> where F: FnMut() {}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::api::{UI, DisplayDriver, LvObject};
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::sync::{Mutex, Arc};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_usage() {
|
||||||
|
let mut ui = UI::init().unwrap();
|
||||||
|
|
||||||
|
let refresh_lines = 10;
|
||||||
|
let mut display = DisplayDriver::new(refresh_lines);
|
||||||
|
ui.disp_drv_register(&mut display);
|
||||||
|
let display = Arc::new(Mutex::new(display));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut disp = display.clone();
|
||||||
|
let mut d = disp.lock().unwrap();
|
||||||
|
let screen = d.scr_act();
|
||||||
|
|
||||||
|
let mut button = screen.btn_create();
|
||||||
|
|
||||||
|
let mut inner_disp = display.clone();
|
||||||
|
button.on_event(|| {
|
||||||
|
// something
|
||||||
|
let mut disp = inner_disp.lock().unwrap();
|
||||||
|
let screen = disp.scr_act();
|
||||||
|
let mut label = screen.label_create();
|
||||||
|
label.set_text("Clicked");
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut label = button.label_create();
|
||||||
|
label.set_text("Click me!");
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut disp = display.clone();
|
||||||
|
let mut d = disp.lock().unwrap();
|
||||||
|
let screen = d.scr_act();
|
||||||
|
let mut button2 = screen.btn_create();
|
||||||
|
button2.on_event(|| {
|
||||||
|
// else
|
||||||
|
});
|
||||||
|
let mut label2 = button2.label_create();
|
||||||
|
label2.set_text("Else");
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.tick_inc(Duration::from_millis(5));
|
||||||
|
ui.task_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_usage() {
|
||||||
|
// // can use Arc<Mutex<lvgl::App>> to share between threads
|
||||||
|
// let mut app = lvgl::App::init().unwrap();
|
||||||
|
//
|
||||||
|
// let disp = DisplayDriver::new();
|
||||||
|
// app.register_display_driver(&disp); // takes (&mut self, ...)
|
||||||
|
//
|
||||||
|
// let screen: &mut lvgl::Screen = disp.new_screen(); // takes (&mut self)
|
||||||
|
// disp.load_screen(&screen); // takes (&self) because it just need the ref to
|
||||||
|
// // the screen to load, as all the screens are already internal.
|
||||||
|
//
|
||||||
|
// let button = screen.new_button(); // takes (&self)
|
||||||
|
// button.on_event(|&mut app, &mut btn, ev| {
|
||||||
|
// let mut label: &mut Label = app.cur_screen().new_label();
|
||||||
|
// if let lvgl::Event::Clicked = ev {
|
||||||
|
// btn.set_text("clicked!");
|
||||||
|
// }
|
||||||
|
// app.load_screen(&screen);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// let mut lbl_click = button.new_label();
|
||||||
|
// lbl_click.set_text("Click me!");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// // lvgl::Timer returns reference to same internal object
|
||||||
|
// app.tick(Duration::from_millis(1)); // takes (&mut self)
|
||||||
|
//
|
||||||
|
// app.task_handler(); // takes (&mut self)
|
||||||
|
//
|
||||||
|
// // Multiple Displays can be instantiated
|
||||||
|
// // Multiple Screen's in each Display
|
||||||
|
// // Screen can be instantiated independently
|
||||||
|
// // Any specific object impl Screenable(trait) (can be used anywhere Screen is used)
|
||||||
|
// // To get active screen from a Display use `lv_disp_get_scr_act(disp)`
|
||||||
|
// let screen = disp.get_active_screen();
|
||||||
|
// // To set an active screen in a Display use `lv_disp_load_scr(disp, scr)`
|
||||||
|
// disp.load_screen(screen);
|
||||||
|
//
|
||||||
|
// let mut screen = Screen::new();
|
||||||
|
// let mut button = screen.add_button();
|
||||||
|
// let mut btn_lbl = button.create_label();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
#![no_std]
|
// #![no_std]
|
||||||
|
|
||||||
pub mod display;
|
pub mod display;
|
||||||
mod objx;
|
mod objx;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
|
||||||
pub use display::DisplayDriver;
|
pub use display::DisplayDriver;
|
||||||
pub use objx::*;
|
pub use objx::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue