use std::{any::Any, fmt, sync::Arc, time::Duration};
use parking_lot::Mutex;
use smallbox::SmallBox;
use zng_app_context::context_local;
use zng_handle::{Handle, HandleOwner, WeakHandle};
use zng_time::{DInstant, Deadline};
use zng_unit::Factor;
use crate::{
Var, VarHandle, VarHandlerOwner, VarValue,
animation::easing::{EasingStep, EasingTime},
};
pub mod easing;
pub use zng_var_proc_macros::Transitionable;
pub trait AnimationTimer {
fn elapsed(&mut self, deadline: Deadline) -> bool;
fn register(&mut self, deadline: Deadline);
fn now(&self) -> DInstant;
}
pub trait AnimationController: Send + Sync + Any {
fn on_start(&self, animation: &Animation) {
let _ = animation;
}
fn on_stop(&self, animation: &Animation) {
let _ = animation;
}
}
impl AnimationController for () {}
pub struct ForceAnimationController;
impl AnimationController for ForceAnimationController {
fn on_start(&self, animation: &Animation) {
animation.force_enable();
}
}
context_local! {
pub(crate) static VARS_ANIMATION_CTRL_CTX: Box<dyn AnimationController> = {
let r: Box<dyn AnimationController> = Box::new(());
r
};
}
#[derive(Clone)]
pub struct Animation(Arc<Mutex<AnimationData>>);
struct AnimationData {
start_time: DInstant,
restarted_count: usize,
stop: bool,
sleep: Option<Deadline>,
restart_next: bool,
animations_enabled: bool,
force_enabled: bool,
now: DInstant,
time_scale: Factor,
}
impl Animation {
pub(super) fn new(animations_enabled: bool, now: DInstant, time_scale: Factor) -> Self {
Animation(Arc::new(Mutex::new(AnimationData {
start_time: now,
restarted_count: 0,
stop: false,
now,
sleep: None,
restart_next: false,
animations_enabled,
force_enabled: false,
time_scale,
})))
}
pub fn start_time(&self) -> DInstant {
self.0.lock().start_time
}
pub fn now(&self) -> DInstant {
self.0.lock().now
}
pub fn time_scale(&self) -> Factor {
self.0.lock().time_scale
}
pub(crate) fn reset_state(&self, enabled: bool, now: DInstant, time_scale: Factor) {
let mut m = self.0.lock();
if !m.force_enabled {
m.animations_enabled = enabled;
}
m.now = now;
m.time_scale = time_scale;
m.sleep = None;
if std::mem::take(&mut m.restart_next) {
m.start_time = now;
m.restarted_count += 1;
}
}
pub(crate) fn reset_sleep(&self) {
self.0.lock().sleep = None;
}
pub fn sleep(&self, duration: Duration, restart: bool) {
let mut me = self.0.lock();
me.sleep = Some(Deadline(me.now + duration));
me.restart_next = restart;
}
pub(crate) fn sleep_deadline(&self) -> Option<Deadline> {
self.0.lock().sleep
}
pub fn animations_enabled(&self) -> bool {
self.0.lock().animations_enabled
}
pub fn force_enable(&self) {
let mut me = self.0.lock();
me.force_enabled = true;
me.animations_enabled = true;
}
pub fn elapsed_dur(&self) -> Duration {
let me = self.0.lock();
me.now - me.start_time
}
pub fn elapsed(&self, duration: Duration) -> EasingTime {
let me = self.0.lock();
if me.animations_enabled {
EasingTime::elapsed(duration, me.now - me.start_time, me.time_scale)
} else {
EasingTime::end()
}
}
pub fn elapsed_stop(&self, duration: Duration) -> EasingTime {
let t = self.elapsed(duration);
if t.is_end() {
self.stop()
}
t
}
pub fn elapsed_restart(&self, duration: Duration) -> EasingTime {
let t = self.elapsed(duration);
if t.is_end() {
self.restart()
}
t
}
pub fn elapsed_restart_stop(&self, duration: Duration, max_restarts: usize) -> EasingTime {
let t = self.elapsed(duration);
if t.is_end() {
if self.count() < max_restarts {
self.restart();
} else {
self.stop();
}
}
t
}
pub fn stop(&self) {
self.0.lock().stop = true;
}
pub fn stop_requested(&self) -> bool {
self.0.lock().stop
}
pub fn restart(&self) {
let mut me = self.0.lock();
me.start_time = me.now;
me.restarted_count += 1;
}
pub fn count(&self) -> usize {
self.0.lock().restarted_count
}
pub fn set_start_time(&self, instant: DInstant) {
self.0.lock().start_time = instant;
}
pub fn set_elapsed(&self, elapsed: EasingTime, duration: Duration) {
let now = self.0.lock().now;
self.set_start_time(now.checked_sub(duration * elapsed.fct()).unwrap());
}
pub fn set_count(&self, count: usize) {
self.0.lock().restarted_count = count;
}
}
#[derive(Clone)]
pub struct ModifyInfo {
pub(crate) handle: Option<WeakAnimationHandle>,
pub(crate) importance: usize,
}
impl ModifyInfo {
pub fn never() -> Self {
ModifyInfo {
handle: None,
importance: 0,
}
}
pub fn importance(&self) -> usize {
self.importance
}
pub fn is_animating(&self) -> bool {
self.handle.as_ref().map(|h| h.upgrade().is_some()).unwrap_or(false)
}
pub fn animation_eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
pub fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle {
if let Some(h) = &self.handle
&& let Some(h) = h.upgrade()
{
return h.hook_animation_stop(handler);
}
VarHandle::dummy()
}
}
impl fmt::Debug for ModifyInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModifyInfo")
.field("is_animating()", &self.is_animating())
.field("importance()", &self.importance)
.finish()
}
}
pub(crate) type AnimationStopFn = SmallBox<dyn FnMut() + Send + 'static, smallbox::space::S4>;
#[derive(Default)]
pub(super) struct AnimationHandleData {
on_drop: Mutex<Vec<(AnimationStopFn, VarHandlerOwner)>>,
}
impl Drop for AnimationHandleData {
fn drop(&mut self) {
for (mut f, h) in self.on_drop.get_mut().drain(..) {
if h.is_alive() {
f()
}
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
#[must_use = "the animation stops if the handle is dropped"]
pub struct AnimationHandle(Handle<AnimationHandleData>);
impl Default for AnimationHandle {
fn default() -> Self {
Self::dummy()
}
}
impl AnimationHandle {
pub(super) fn new() -> (HandleOwner<AnimationHandleData>, Self) {
let (owner, handle) = Handle::new(AnimationHandleData::default());
(owner, AnimationHandle(handle))
}
pub fn dummy() -> Self {
AnimationHandle(Handle::dummy(AnimationHandleData::default()))
}
pub fn perm(self) {
self.0.perm();
}
pub fn is_permanent(&self) -> bool {
self.0.is_permanent()
}
pub fn stop(self) {
self.0.force_drop();
}
pub fn is_stopped(&self) -> bool {
self.0.is_dropped()
}
pub fn downgrade(&self) -> WeakAnimationHandle {
WeakAnimationHandle(self.0.downgrade())
}
pub fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle {
if !self.is_stopped() {
let (owner, handle) = VarHandle::new();
self.0.data().on_drop.lock().push((handler, owner));
handle
} else {
VarHandle::dummy()
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
pub struct WeakAnimationHandle(pub(super) WeakHandle<AnimationHandleData>);
impl WeakAnimationHandle {
pub fn new() -> Self {
Self(WeakHandle::new())
}
pub fn upgrade(&self) -> Option<AnimationHandle> {
self.0.upgrade().map(AnimationHandle)
}
}
pub trait Transitionable: VarValue {
fn lerp(self, to: &Self, step: EasingStep) -> Self;
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transition<T> {
pub from: T,
pub to: T,
}
impl<T> Transition<T>
where
T: Transitionable,
{
pub fn new(from: T, to: T) -> Self {
Self { from, to }
}
pub fn sample(&self, step: EasingStep) -> T {
self.from.clone().lerp(&self.to, step)
}
}
#[derive(Clone, Debug)]
pub struct TransitionKeyed<T> {
keys: Vec<(Factor, T)>,
}
impl<T> TransitionKeyed<T>
where
T: Transitionable,
{
pub fn new(mut keys: Vec<(Factor, T)>) -> Option<Self> {
if keys.is_empty() {
return None;
}
for i in 1..keys.len() {
if keys[i].0 < keys[i - 1].0 {
keys[i].0 = keys[i - 1].0;
}
}
Some(TransitionKeyed { keys })
}
pub fn keys(&self) -> &[(Factor, T)] {
&self.keys
}
pub fn sample(&self, step: EasingStep) -> T {
if let Some(i) = self.keys.iter().position(|(f, _)| *f > step) {
if i == 0 {
self.keys[0].1.clone()
} else {
let (from_step, from_value) = self.keys[i - 1].clone();
if from_step == step {
from_value
} else {
let (_, to_value) = &self.keys[i];
let step = step - from_step;
from_value.lerp(to_value, step)
}
}
} else {
self.keys[self.keys.len() - 1].1.clone()
}
}
}
pub struct ChaseAnimation<T: VarValue + Transitionable> {
pub(super) target: T,
pub(super) var: Var<T>,
pub(super) handle: AnimationHandle,
}
impl<T> fmt::Debug for ChaseAnimation<T>
where
T: VarValue + Transitionable,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChaseAnimation")
.field("target", &self.target)
.finish_non_exhaustive()
}
}
impl<T> ChaseAnimation<T>
where
T: VarValue + Transitionable,
{
pub fn target(&self) -> &T {
&self.target
}
pub fn modify(&mut self, modify: impl FnOnce(&mut T), duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
if self.handle.is_stopped() {
self.target = self.var.get();
}
modify(&mut self.target);
self.handle = self.var.ease(self.target.clone(), duration, easing);
}
pub fn set(&mut self, value: impl Into<T>, duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
self.target = value.into();
self.handle = self.var.ease(self.target.clone(), duration, easing);
}
}
pub fn slerp_sampler<T: Transitionable>(t: &Transition<T>, step: EasingStep) -> T {
slerp_enabled(true, || t.sample(step))
}
pub fn is_slerp_enabled() -> bool {
SLERP_ENABLED.get_clone()
}
pub fn slerp_enabled<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
SLERP_ENABLED.with_context(&mut Some(Arc::new(enabled)), f)
}
context_local! {
static SLERP_ENABLED: bool = false;
}
#[expect(non_camel_case_types)]
pub struct TRANSITIONABLE_APP;