use core::marker::PhantomData;
use oxivgl_sys::*;
use crate::{
draw::{
Area, DrawArcDsc, DrawImageDsc, DrawLabelDscOwned, DrawLetterDsc, DrawLineDsc, DrawRectDsc, DrawTriangleDsc,
},
draw_buf::DrawBuf,
widgets::{AsLvHandle, Obj, WidgetError},
};
#[derive(Debug)]
pub struct Canvas<'p> {
obj: Obj<'p>,
draw_buf: *mut DrawBuf,
}
unsafe extern "C" fn canvas_delete_cb(e: *mut lv_event_t) {
let ptr = unsafe { lv_event_get_user_data(e) } as *mut DrawBuf;
if !ptr.is_null() {
unsafe { drop(alloc::boxed::Box::from_raw(ptr)) };
}
}
impl<'p> Canvas<'p> {
pub fn new(parent: &impl AsLvHandle, buf: DrawBuf) -> Result<Self, WidgetError> {
let handle = parent.lv_handle();
if handle.is_null() {
return Err(WidgetError::LvglNullPointer);
}
let obj_ptr = unsafe { lv_canvas_create(handle) };
if obj_ptr.is_null() {
return Err(WidgetError::LvglNullPointer);
}
let buf_box = alloc::boxed::Box::new(buf);
let buf_ptr = alloc::boxed::Box::into_raw(buf_box);
unsafe { lv_canvas_set_draw_buf(obj_ptr, (*buf_ptr).as_ptr()) };
unsafe {
lv_obj_add_event_cb(
obj_ptr,
Some(canvas_delete_cb),
lv_event_code_t_LV_EVENT_DELETE,
buf_ptr as *mut core::ffi::c_void,
)
};
Ok(Self { obj: Obj::from_raw(obj_ptr), draw_buf: buf_ptr })
}
pub fn draw_buf(&self) -> &DrawBuf {
unsafe { &*self.draw_buf }
}
pub fn fill_bg(&self, color: lv_color_t, opa: u8) -> &Self {
unsafe { lv_canvas_fill_bg(self.lv_handle(), color, opa) };
self
}
pub fn set_px(&self, x: i32, y: i32, color: lv_color_t, opa: u8) -> &Self {
unsafe { lv_canvas_set_px(self.lv_handle(), x, y, color, opa) };
self
}
pub fn get_px(&self, x: i32, y: i32) -> lv_color32_t {
unsafe { lv_canvas_get_px(self.lv_handle(), x, y) }
}
pub fn init_layer(&self) -> CanvasLayer<'_> {
CanvasLayer::new(self.lv_handle())
}
pub fn draw_to_buf(buf: &DrawBuf, f: impl FnOnce(&TempCanvas<'_>)) {
let screen = unsafe { lv_screen_active() };
assert!(!screen.is_null(), "no active screen");
let canvas_ptr = unsafe { lv_canvas_create(screen) };
assert!(!canvas_ptr.is_null(), "canvas creation failed");
unsafe { lv_canvas_set_draw_buf(canvas_ptr, buf.as_ptr()) };
struct Guard(*mut lv_obj_t);
impl Drop for Guard {
fn drop(&mut self) {
unsafe { lv_obj_delete(self.0) };
}
}
let _guard = Guard(canvas_ptr);
let temp = TempCanvas { ptr: canvas_ptr, _life: PhantomData };
f(&temp);
}
}
pub struct TempCanvas<'a> {
ptr: *mut lv_obj_t,
_life: PhantomData<&'a ()>,
}
impl<'a> TempCanvas<'a> {
pub fn fill_bg(&self, color: lv_color_t, opa: u8) {
unsafe { lv_canvas_fill_bg(self.ptr, color, opa) };
}
pub fn set_px(&self, x: i32, y: i32, color: lv_color_t, opa: u8) {
unsafe { lv_canvas_set_px(self.ptr, x, y, color, opa) };
}
pub fn init_layer(&self) -> CanvasLayer<'_> {
CanvasLayer::new(self.ptr)
}
}
impl<'p> AsLvHandle for Canvas<'p> {
fn lv_handle(&self) -> *mut lv_obj_t {
self.obj.lv_handle()
}
}
impl<'p> core::ops::Deref for Canvas<'p> {
type Target = Obj<'p>;
fn deref(&self) -> &Self::Target {
&self.obj
}
}
pub struct CanvasLayer<'c> {
canvas: *mut lv_obj_t,
layer: lv_layer_t,
_canvas_lifetime: PhantomData<&'c ()>,
}
impl<'c> CanvasLayer<'c> {
pub(crate) fn new(canvas: *mut lv_obj_t) -> Self {
let mut layer = unsafe { core::mem::zeroed::<lv_layer_t>() };
unsafe { lv_canvas_init_layer(canvas, &mut layer) };
Self { canvas, layer, _canvas_lifetime: PhantomData }
}
pub fn draw_rect(&mut self, dsc: &DrawRectDsc, area: Area) {
let lv_area = lv_area_t::from(area);
unsafe { lv_draw_rect(&mut self.layer, dsc.as_ptr(), &lv_area) };
}
pub fn draw_arc(&mut self, dsc: &DrawArcDsc) {
unsafe { lv_draw_arc(&mut self.layer, dsc.as_ptr()) };
}
pub fn draw_line(&mut self, dsc: &DrawLineDsc) {
unsafe { lv_draw_line(&mut self.layer, dsc.as_ptr()) };
}
pub fn draw_triangle(&mut self, dsc: &DrawTriangleDsc) {
unsafe { lv_draw_triangle(&mut self.layer, dsc.as_ptr()) };
}
pub fn draw_image<'i>(&mut self, dsc: &DrawImageDsc<'i>, area: Area) {
let lv_area = lv_area_t::from(area);
unsafe { lv_draw_image(&mut self.layer, dsc.as_ptr(), &lv_area) };
}
pub fn draw_letter(&mut self, dsc: &DrawLetterDsc, x: i32, y: i32) {
let pt = lv_point_t { x, y };
let mut inner = unsafe { *dsc.as_ptr() };
unsafe { lv_draw_letter(&mut self.layer, &mut inner, &pt) };
}
pub fn draw_label(&mut self, dsc: &DrawLabelDscOwned, area: Area, text: &str) {
let mut buf = [0u8; 64];
let len = text.len().min(buf.len() - 1);
buf[..len].copy_from_slice(&text.as_bytes()[..len]);
buf[len] = 0;
let mut local_dsc = dsc.inner;
local_dsc.text = buf.as_ptr() as *const _;
local_dsc.set_text_local(1);
let lv_area = lv_area_t::from(area);
unsafe { lv_draw_label(&mut self.layer, &local_dsc, &lv_area) };
}
}
impl Drop for CanvasLayer<'_> {
fn drop(&mut self) {
unsafe { lv_canvas_finish_layer(self.canvas, &mut self.layer) };
}
}