use ::alloc::boxed::Box;
use ::core::{ffi::c_void, mem::MaybeUninit, ptr::NonNull, time::Duration};
use bevy_ecs::{component::Component, lifecycle::HookContext, world::DeferredWorld};
use crate::widgets::{Wdg, Widget};
use crate::{info, warn};
#[derive(Component)]
#[component(on_insert = add_animation)]
#[component(storage = "SparseSet")]
pub struct Animation {
raw: lightvgl_sys::lv_anim_t,
}
impl Animation {
pub fn new<F>(duration: Duration, start: i32, end: i32, animator: F) -> Self
where
F: FnMut(&mut Wdg, i32),
{
let mut raw = unsafe {
let mut anim = MaybeUninit::<lightvgl_sys::lv_anim_t>::uninit();
lightvgl_sys::lv_anim_init(anim.as_mut_ptr());
anim.assume_init()
};
raw.duration = duration.as_millis().try_into().unwrap_or(0);
raw.start_value = start;
raw.current_value = start;
raw.end_value = end;
raw.user_data = Box::<F>::into_raw(Box::new(animator)) as *mut _;
raw.exec_cb = Some(animator_trampoline::<F>);
Self { raw }
}
#[cfg(feature = "no_ecs")]
pub fn set_widget(&mut self, widget: &mut Wdg) {
self.raw_mut().var = widget.raw_mut() as *mut _;
}
pub fn start(&mut self) {
unsafe {
let old_ptr = &mut self.raw;
let new_ptr = lightvgl_sys::lv_anim_start(old_ptr);
self.raw = *new_ptr;
}
}
pub fn raw(&self) -> &lightvgl_sys::lv_anim_t {
&self.raw
}
pub fn raw_mut(&mut self) -> &mut lightvgl_sys::lv_anim_t {
&mut self.raw
}
}
unsafe impl Send for Animation {}
unsafe impl Sync for Animation {}
impl Drop for Animation {
fn drop(&mut self) {
info!("Dropping Animation");
}
}
fn add_animation(mut world: DeferredWorld, ctx: HookContext) {
let obj = world
.get_mut::<Widget>(ctx.entity)
.expect("Animation components must be added entities with a Widget component")
.as_mut()
.raw();
let mut anim = world.get_mut::<Animation>(ctx.entity).unwrap();
anim.raw.var = obj as *mut _;
anim.start();
info!("Added Animation");
}
unsafe extern "C" fn animator_trampoline<F>(obj: *mut c_void, val: i32)
where
F: FnMut(&mut Wdg, i32),
{
unsafe {
let ptr = lightvgl_sys::lv_anim_get(obj, None) as *mut lightvgl_sys::lv_anim_t;
let anim = NonNull::new(ptr).unwrap();
let obj = obj as *mut lightvgl_sys::lv_obj_t;
if anim.as_ref().user_data.is_null() {
warn!("Animation user data was null, this should never happen!");
return;
}
let mut obj_wdg = Wdg::from_ptr(obj);
let callback = &mut *(anim.as_ref().user_data as *mut F);
callback(&mut obj_wdg, val);
}
}