extern crate alloc;
use alloc::boxed::Box;
use core::marker::PhantomData;
use crate::{
alloc::PebbleAlloc,
sys,
ui::{
UiGuard, WindowCtx,
graphics::{GContext, Rectangle, grect, rect},
},
};
pub unsafe trait AsLayer {
fn as_ptr(&self) -> *mut sys::Layer;
}
pub trait Layer: AsLayer {
fn add_child(&mut self, child: &mut impl AsLayer);
fn mark_dirty(&mut self);
fn bounds(&self) -> Rectangle;
fn frame(&self) -> Rectangle;
fn set_frame(&mut self, frame: Rectangle) -> &mut Self;
fn set_hidden(&mut self, hidden: bool) -> &mut Self;
}
impl<T: AsLayer> Layer for T {
fn add_child(&mut self, child: &mut impl AsLayer) {
unsafe { sys::layer_add_child(self.as_ptr(), child.as_ptr()) };
}
fn mark_dirty(&mut self) {
unsafe { sys::layer_mark_dirty(self.as_ptr()) };
}
fn bounds(&self) -> Rectangle {
rect(unsafe { sys::layer_get_bounds(self.as_ptr()) })
}
fn frame(&self) -> Rectangle {
rect(unsafe { sys::layer_get_frame(self.as_ptr()) })
}
fn set_frame(&mut self, frame: Rectangle) -> &mut Self {
unsafe { sys::layer_set_frame(self.as_ptr(), grect(frame)) };
self
}
fn set_hidden(&mut self, hidden: bool) -> &mut Self {
unsafe { sys::layer_set_hidden(self.as_ptr(), hidden) };
self
}
}
pub type DrawFn<T> = fn(&T, Rectangle, &mut GContext<'_>);
type ErasedDrawFn = fn(*const (), Rectangle, &mut GContext<'_>);
pub struct RootLayer {
ptr: *mut sys::Layer,
}
pub struct DataLayer<T> {
ptr: *mut sys::Layer,
_data: PhantomData<Box<LayerData<T>, PebbleAlloc>>,
}
#[repr(C)]
struct LayerData<T> {
draw: Option<ErasedDrawFn>,
val: T,
}
const VAL_OFFSET: usize = size_of::<Option<ErasedDrawFn>>();
impl RootLayer {
pub fn from_ctx<C, D>(win: &mut WindowCtx<'_, C, D>) -> Self {
let ptr = unsafe { sys::window_get_root_layer(win.ptr()) };
RootLayer { ptr }
}
}
unsafe impl AsLayer for RootLayer {
fn as_ptr(&self) -> *mut gneiss_sys::Layer {
self.ptr
}
}
impl<T> DataLayer<T> {
pub unsafe fn from_raw(ptr: *mut sys::Layer) -> Self {
Self {
ptr,
_data: PhantomData,
}
}
pub fn new_with_data(frame: Rectangle, data: T) -> Option<Self> {
let _: () = const { assert!(align_of::<LayerData<T>>() <= 4) };
let _: () = const { assert!(core::mem::offset_of!(LayerData<T>, val) == VAL_OFFSET) };
let ptr = unsafe {
sys::layer_create_with_data(grect(frame), size_of::<LayerData<T>>()).as_mut()?
};
unsafe {
core::ptr::write(sys::layer_get_data(ptr).cast(), LayerData {
draw: None,
val: data,
});
};
Some(Self {
ptr,
_data: PhantomData,
})
}
fn layer_data(&mut self) -> &mut LayerData<T> {
unsafe {
sys::layer_get_data(self.ptr)
.cast::<LayerData<T>>()
.as_mut()
.unwrap_unchecked()
}
}
pub fn data(&mut self) -> &mut T {
&mut self.layer_data().val
}
pub fn set_draw(&mut self, draw: DrawFn<T>) -> &mut Self {
self.layer_data().draw =
Some(unsafe { core::mem::transmute::<DrawFn<T>, ErasedDrawFn>(draw) });
unsafe { sys::layer_set_update_proc(self.ptr, Some(draw_trampoline)) };
self.mark_dirty();
self
}
pub fn clear_draw(&mut self) -> &mut Self {
self.layer_data().draw = None;
unsafe { sys::layer_set_update_proc(self.ptr, None) };
self.mark_dirty();
self
}
}
unsafe extern "C" fn draw_trampoline(layer: *mut sys::Layer, ctx: *mut sys::GContext) {
let _ui = UiGuard::enter();
let (draw, val) = unsafe {
let data = sys::layer_get_data(layer);
(
*data.cast::<Option<ErasedDrawFn>>(),
data.byte_add(VAL_OFFSET).cast::<()>(),
)
};
let Some(draw) = draw else {
return;
};
let mut ctx = unsafe { GContext::new(ctx, layer) };
let bounds = rect(unsafe { sys::layer_get_bounds(layer) });
draw(val, bounds, &mut ctx);
}
impl DataLayer<()> {
pub fn new(frame: Rectangle) -> Option<Self> {
Self::new_with_data(frame, ())
}
}
unsafe impl<T> AsLayer for DataLayer<T> {
fn as_ptr(&self) -> *mut sys::Layer {
self.ptr
}
}
impl<T> Drop for DataLayer<T> {
fn drop(&mut self) {
unsafe {
core::ptr::drop_in_place(self.data());
sys::layer_remove_child_layers(self.ptr);
sys::layer_destroy(self.ptr);
}
}
}