zng_var/
animation.rs

1//! Var animation types and functions.
2
3use std::{mem, time::Duration};
4
5use zng_app_context::context_local;
6use zng_clone_move::clmv;
7
8use zng_handle::{Handle, HandleOwner, WeakHandle};
9use zng_time::{DInstant, Deadline, INSTANT};
10use zng_unit::TimeUnits as _;
11
12pub use zng_var_proc_macros::Transitionable;
13
14use self::types::ArcCowVar;
15
16use super::*;
17
18pub mod easing;
19
20#[derive(Default)]
21pub(super) struct AnimationHandleData {
22    on_drop: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
23}
24impl Drop for AnimationHandleData {
25    fn drop(&mut self) {
26        for f in self.on_drop.get_mut().drain(..) {
27            f()
28        }
29    }
30}
31/// Represents a running animation.
32///
33/// Drop all clones of this handle to stop the animation, or call [`perm`] to drop the handle
34/// but keep the animation alive until it is stopped from the inside.
35///
36/// [`perm`]: AnimationHandle::perm
37#[derive(Clone, PartialEq, Eq, Hash, Debug)]
38#[repr(transparent)]
39#[must_use = "the animation stops if the handle is dropped"]
40pub struct AnimationHandle(Handle<AnimationHandleData>);
41impl Default for AnimationHandle {
42    /// `dummy`.
43    fn default() -> Self {
44        Self::dummy()
45    }
46}
47impl AnimationHandle {
48    pub(super) fn new() -> (HandleOwner<AnimationHandleData>, Self) {
49        let (owner, handle) = Handle::new(AnimationHandleData::default());
50        (owner, AnimationHandle(handle))
51    }
52
53    /// Create dummy handle that is always in the *stopped* state.
54    ///
55    /// Note that `Option<AnimationHandle>` takes up the same space as `AnimationHandle` and avoids an allocation.
56    pub fn dummy() -> Self {
57        AnimationHandle(Handle::dummy(AnimationHandleData::default()))
58    }
59
60    /// Drops the handle but does **not** stop.
61    ///
62    /// The animation stays in memory for the duration of the app or until another handle calls [`stop`](Self::stop).
63    pub fn perm(self) {
64        self.0.perm();
65    }
66
67    /// If another handle has called [`perm`](Self::perm).
68    ///
69    /// If `true` the animation will stay active until the app exits, unless [`stop`](Self::stop) is called.
70    pub fn is_permanent(&self) -> bool {
71        self.0.is_permanent()
72    }
73
74    /// Drops the handle and forces the animation to drop.
75    pub fn stop(self) {
76        self.0.force_drop();
77    }
78
79    /// If another handle has called [`stop`](Self::stop).
80    ///
81    /// The animation is already dropped or will be dropped in the next app update, this is irreversible.
82    pub fn is_stopped(&self) -> bool {
83        self.0.is_dropped()
84    }
85
86    /// Create a weak handle.
87    pub fn downgrade(&self) -> WeakAnimationHandle {
88        WeakAnimationHandle(self.0.downgrade())
89    }
90
91    /// Register a `handler` to be called once when the animation stops.
92    ///
93    /// Returns the `handler` if the animation has already stopped.
94    ///
95    /// [`importance`]: ModifyInfo::importance
96    pub fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
97        if !self.is_stopped() {
98            self.0.data().on_drop.lock().push(handler);
99            Ok(())
100        } else {
101            Err(handler)
102        }
103    }
104}
105
106/// Weak [`AnimationHandle`].
107#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
108pub struct WeakAnimationHandle(pub(super) WeakHandle<AnimationHandleData>);
109impl WeakAnimationHandle {
110    /// New weak handle that does not upgrade.
111    pub fn new() -> Self {
112        Self(WeakHandle::new())
113    }
114
115    /// Get the animation handle if it is still animating.
116    pub fn upgrade(&self) -> Option<AnimationHandle> {
117        self.0.upgrade().map(AnimationHandle)
118    }
119}
120
121struct AnimationData {
122    start_time: DInstant,
123    restart_count: usize,
124    stop: bool,
125    sleep: Option<Deadline>,
126    animations_enabled: bool,
127    force_enabled: bool,
128    now: DInstant,
129    time_scale: Factor,
130}
131
132/// Represents an animation in its closure.
133///
134/// See the [`VARS.animate`] method for more details.
135///
136/// [`VARS.animate`]: VARS::animate
137#[derive(Clone)]
138pub struct Animation(Arc<Mutex<AnimationData>>);
139impl Animation {
140    pub(super) fn new(animations_enabled: bool, now: DInstant, time_scale: Factor) -> Self {
141        Animation(Arc::new(Mutex::new(AnimationData {
142            start_time: now,
143            restart_count: 0,
144            stop: false,
145            now,
146            sleep: None,
147            animations_enabled,
148            force_enabled: false,
149            time_scale,
150        })))
151    }
152
153    /// The instant this animation (re)started.
154    pub fn start_time(&self) -> DInstant {
155        self.0.lock().start_time
156    }
157
158    /// The instant the current animation update started.
159    ///
160    /// Use this value instead of [`INSTANT.now`], animations update sequentially, but should behave as if
161    /// they are updating exactly in parallel, using this timestamp ensures that.
162    ///
163    /// [`INSTANT.now`]: zng_time::INSTANT::now
164    pub fn now(&self) -> DInstant {
165        self.0.lock().now
166    }
167
168    /// Global time scale for animations.
169    pub fn time_scale(&self) -> Factor {
170        self.0.lock().time_scale
171    }
172
173    pub(crate) fn reset_state(&self, enabled: bool, now: DInstant, time_scale: Factor) {
174        let mut m = self.0.lock();
175        if !m.force_enabled {
176            m.animations_enabled = enabled;
177        }
178        m.now = now;
179        m.time_scale = time_scale;
180        m.sleep = None;
181    }
182
183    pub(crate) fn reset_sleep(&self) {
184        self.0.lock().sleep = None;
185    }
186
187    /// Set the duration to the next animation update. The animation will *sleep* until `duration` elapses.
188    ///
189    /// The animation awakes in the next [`VARS.frame_duration`] after the `duration` elapses. The minimum
190    /// possible `duration` is the frame duration, shorter durations behave the same as if not set.
191    ///
192    /// [`VARS.frame_duration`]: VARS::frame_duration
193    pub fn sleep(&self, duration: Duration) {
194        let mut me = self.0.lock();
195        me.sleep = Some(Deadline(me.now + duration));
196    }
197
198    pub(crate) fn sleep_deadline(&self) -> Option<Deadline> {
199        self.0.lock().sleep
200    }
201
202    /// Returns a value that indicates if animations are enabled in the operating system.
203    ///
204    /// If `false` all animations must be skipped to the end, users with photo-sensitive epilepsy disable animations system wide.
205    pub fn animations_enabled(&self) -> bool {
206        self.0.lock().animations_enabled
207    }
208
209    /// Set [`animations_enabled`] to `true`.
210    ///
211    /// This should only be used for animations that are component of an app feature, cosmetic animations must not force enable.
212    ///
213    /// [`animations_enabled`]: VARS::animations_enabled
214    pub fn force_enable(&self) {
215        let mut me = self.0.lock();
216        me.force_enabled = true;
217        me.animations_enabled = true;
218    }
219
220    /// Compute the time elapsed from [`start_time`] to [`now`].
221    ///
222    /// [`start_time`]: Self::start_time
223    /// [`now`]: Self::now
224    pub fn elapsed_dur(&self) -> Duration {
225        let me = self.0.lock();
226        me.now - me.start_time
227    }
228
229    /// Compute the elapsed [`EasingTime`], in the span of the total `duration`, if [`animations_enabled`].
230    ///
231    /// If animations are disabled, returns [`EasingTime::end`], the returned time is scaled.
232    ///
233    /// [`animations_enabled`]: Self::animations_enabled
234    pub fn elapsed(&self, duration: Duration) -> EasingTime {
235        let me = self.0.lock();
236        if me.animations_enabled {
237            EasingTime::elapsed(duration, me.now - me.start_time, me.time_scale)
238        } else {
239            EasingTime::end()
240        }
241    }
242
243    /// Compute the elapsed [`EasingTime`], if the time [`is_end`] requests animation stop.
244    ///
245    /// [`is_end`]: EasingTime::is_end
246    pub fn elapsed_stop(&self, duration: Duration) -> EasingTime {
247        let t = self.elapsed(duration);
248        if t.is_end() {
249            self.stop()
250        }
251        t
252    }
253
254    /// Compute the elapsed [`EasingTime`], if the time [`is_end`] restarts the animation.
255    ///
256    /// [`is_end`]: EasingTime::is_end
257    pub fn elapsed_restart(&self, duration: Duration) -> EasingTime {
258        let t = self.elapsed(duration);
259        if t.is_end() {
260            self.restart()
261        }
262        t
263    }
264
265    /// Compute the elapsed [`EasingTime`], if the time [`is_end`] restarts the animation, repeats until has
266    /// restarted `max_restarts` inclusive, then stops the animation.
267    ///
268    /// [`is_end`]: EasingTime::is_end
269    pub fn elapsed_restart_stop(&self, duration: Duration, max_restarts: usize) -> EasingTime {
270        let t = self.elapsed(duration);
271        if t.is_end() {
272            if self.restart_count() < max_restarts {
273                self.restart();
274            } else {
275                self.stop();
276            }
277        }
278        t
279    }
280
281    /// Drop the animation after applying the current update.
282    pub fn stop(&self) {
283        self.0.lock().stop = true;
284    }
285
286    /// If the animation will be dropped after applying the update.
287    pub fn stop_requested(&self) -> bool {
288        self.0.lock().stop
289    }
290
291    /// Set the animation start time to now.
292    pub fn restart(&self) {
293        let now = self.0.lock().now;
294        self.set_start_time(now);
295        let mut me = self.0.lock();
296        me.restart_count += 1;
297    }
298
299    /// Number of times the animation restarted.
300    pub fn restart_count(&self) -> usize {
301        self.0.lock().restart_count
302    }
303
304    /// Change the start time to an arbitrary value.
305    ///
306    /// Note that this does not affect the restart count.
307    pub fn set_start_time(&self, instant: DInstant) {
308        self.0.lock().start_time = instant;
309    }
310
311    /// Change the start to an instant that computes the `elapsed` for the `duration` at the moment
312    /// this method is called.
313    ///
314    /// Note that this does not affect the restart count.
315    pub fn set_elapsed(&self, elapsed: EasingTime, duration: Duration) {
316        let now = self.0.lock().now;
317        self.set_start_time(now.checked_sub(duration * elapsed.fct()).unwrap());
318    }
319}
320
321/// Represents a type that can be animated between two values.
322///
323/// This trait is auto-implemented for all [`Copy`] types that can add, subtract and multiply by [`Factor`], [`Clone`]
324/// only types must implement this trait manually.
325///
326/// [`Factor`]: zng_unit::Factor
327pub trait Transitionable: VarValue {
328    /// Sample the linear interpolation from `self` -> `to` by `step`.  
329    fn lerp(self, to: &Self, step: EasingStep) -> Self;
330}
331
332/// Represents a simple transition between two values.
333pub struct Transition<T> {
334    /// Value sampled at the `0.fct()` step.
335    pub from: T,
336    ///
337    /// Value sampled at the `1.fct()` step.
338    pub to: T,
339}
340impl<T> Transition<T>
341where
342    T: Transitionable,
343{
344    /// New transition.
345    pub fn new(from: T, to: T) -> Self {
346        Self { from, to }
347    }
348
349    /// Compute the transition value at the `step`.
350    pub fn sample(&self, step: EasingStep) -> T {
351        self.from.clone().lerp(&self.to, step)
352    }
353}
354
355/// Represents a transition across multiple keyed values that can be sampled using [`EasingStep`].
356#[derive(Clone, Debug)]
357pub struct TransitionKeyed<T> {
358    keys: Vec<(Factor, T)>,
359}
360impl<T> TransitionKeyed<T>
361where
362    T: Transitionable,
363{
364    /// New transition.
365    ///
366    /// Returns `None` if `keys` is empty.
367    pub fn new(mut keys: Vec<(Factor, T)>) -> Option<Self> {
368        if keys.is_empty() {
369            return None;
370        }
371
372        // correct backtracking keyframes.
373        for i in 1..keys.len() {
374            if keys[i].0 < keys[i - 1].0 {
375                keys[i].0 = keys[i - 1].0;
376            }
377        }
378
379        Some(TransitionKeyed { keys })
380    }
381
382    /// Keyed values.
383    pub fn keys(&self) -> &[(Factor, T)] {
384        &self.keys
385    }
386
387    /// Compute the transition value at the `step`.
388    pub fn sample(&self, step: EasingStep) -> T {
389        if let Some(i) = self.keys.iter().position(|(f, _)| *f > step) {
390            if i == 0 {
391                // step before first
392                self.keys[0].1.clone()
393            } else {
394                let (from_step, from_value) = self.keys[i - 1].clone();
395                if from_step == step {
396                    // step exact key
397                    from_value
398                } else {
399                    // linear interpolate between steps
400
401                    let (_, to_value) = &self.keys[i];
402                    let step = step - from_step;
403
404                    from_value.lerp(to_value, step)
405                }
406            }
407        } else {
408            // step is after last
409            self.keys[self.keys.len() - 1].1.clone()
410        }
411    }
412}
413
414pub(super) struct Animations {
415    animations: Mutex<Vec<AnimationFn>>,
416    animation_imp: usize,
417    pub(super) current_modify: ModifyInfo,
418    pub(super) animation_start_time: Option<DInstant>,
419    next_frame: Option<Deadline>,
420    pub(super) animations_enabled: ArcCowVar<bool, ArcVar<bool>>,
421    pub(super) sys_animations_enabled: ArcVar<bool>,
422    pub(super) frame_duration: ArcVar<Duration>,
423    pub(super) animation_time_scale: ArcVar<Factor>,
424}
425impl Animations {
426    pub(crate) fn new() -> Self {
427        let sys_animations_enabled = var(true);
428        Self {
429            animations: Mutex::default(),
430            animation_imp: 1,
431            current_modify: ModifyInfo {
432                handle: None,
433                importance: 1,
434            },
435            animation_start_time: None,
436            next_frame: None,
437            animations_enabled: sys_animations_enabled.cow(),
438            sys_animations_enabled,
439            frame_duration: var((1.0 / 60.0).secs()),
440            animation_time_scale: var(1.fct()),
441        }
442    }
443
444    pub(super) fn update_animations(timer: &mut impl AnimationTimer) {
445        let mut vars = VARS_SV.write();
446        if let Some(next_frame) = vars.ans.next_frame {
447            if timer.elapsed(next_frame) {
448                let mut animations = mem::take(vars.ans.animations.get_mut());
449                debug_assert!(!animations.is_empty());
450
451                let info = AnimationUpdateInfo {
452                    animations_enabled: vars.ans.animations_enabled.get(),
453                    time_scale: vars.ans.animation_time_scale.get(),
454                    now: timer.now(),
455                    next_frame: next_frame + vars.ans.frame_duration.get(),
456                };
457
458                let mut min_sleep = Deadline(info.now + Duration::from_secs(60 * 60));
459
460                drop(vars);
461
462                animations.retain_mut(|animate| {
463                    if let Some(sleep) = animate(info) {
464                        min_sleep = min_sleep.min(sleep);
465                        true
466                    } else {
467                        false
468                    }
469                });
470
471                let mut vars = VARS_SV.write();
472
473                let self_animations = vars.ans.animations.get_mut();
474                if !self_animations.is_empty() {
475                    min_sleep = Deadline(info.now);
476                }
477                animations.append(self_animations);
478                *self_animations = animations;
479
480                if !self_animations.is_empty() {
481                    vars.ans.next_frame = Some(min_sleep);
482                    timer.register(min_sleep);
483                } else {
484                    vars.ans.next_frame = None;
485                }
486            }
487        }
488    }
489
490    pub(super) fn next_deadline(timer: &mut impl AnimationTimer) {
491        if let Some(next_frame) = VARS_SV.read().ans.next_frame {
492            timer.register(next_frame);
493        }
494    }
495
496    pub(crate) fn animate<A>(mut animation: A) -> AnimationHandle
497    where
498        A: FnMut(&Animation) + Send + 'static,
499    {
500        let mut vars = VARS_SV.write();
501
502        // # Modify Importance
503        //
504        // Variables only accept modifications from an importance (IMP) >= the previous IM that modified it.
505        //
506        // Direct modifications always overwrite previous animations, so we advance the IMP for each call to
507        // this method **and then** advance the IMP again for all subsequent direct modifications.
508        //
509        // Example sequence of events:
510        //
511        // |IM| Modification  | Accepted
512        // |--|---------------|----------
513        // | 1| Var::set      | YES
514        // | 2| Var::ease     | YES
515        // | 2| ease update   | YES
516        // | 3| Var::set      | YES
517        // | 3| Var::set      | YES
518        // | 2| ease update   | NO
519        // | 4| Var::ease     | YES
520        // | 2| ease update   | NO
521        // | 4| ease update   | YES
522        // | 5| Var::set      | YES
523        // | 2| ease update   | NO
524        // | 4| ease update   | NO
525
526        // ensure that all animations started in this update have the same exact time, we update then with the same `now`
527        // timestamp also, this ensures that synchronized animations match perfectly.
528        let start_time = if let Some(t) = vars.ans.animation_start_time {
529            t
530        } else {
531            let t = INSTANT.now();
532            vars.ans.animation_start_time = Some(t);
533            t
534        };
535
536        let mut anim_imp = None;
537        if let Some(c) = VARS_MODIFY_CTX.get_clone() {
538            if c.is_animating() {
539                // nested animation uses parent importance.
540                anim_imp = Some(c.importance);
541            }
542        }
543        let anim_imp = match anim_imp {
544            Some(i) => i,
545            None => {
546                // not nested, advance base imp
547                let mut imp = vars.ans.animation_imp.wrapping_add(1);
548                if imp == 0 {
549                    imp = 1;
550                }
551
552                let mut next_imp = imp.wrapping_add(1);
553                if next_imp == 0 {
554                    next_imp = 1;
555                }
556
557                vars.ans.animation_imp = next_imp;
558                vars.ans.current_modify.importance = next_imp;
559
560                imp
561            }
562        };
563
564        let (handle_owner, handle) = AnimationHandle::new();
565        let weak_handle = handle.downgrade();
566
567        let controller = VARS_ANIMATION_CTRL_CTX.get();
568
569        let anim = Animation::new(vars.ans.animations_enabled.get(), start_time, vars.ans.animation_time_scale.get());
570
571        drop(vars);
572
573        controller.on_start(&anim);
574        let mut controller = Some(controller);
575        let mut anim_modify_info = Some(Arc::new(Some(ModifyInfo {
576            handle: Some(weak_handle.clone()),
577            importance: anim_imp,
578        })));
579
580        let mut vars = VARS_SV.write();
581
582        vars.ans.animations.get_mut().push(Box::new(move |info| {
583            let _handle_owner = &handle_owner; // capture and own the handle owner.
584
585            if weak_handle.upgrade().is_some() {
586                if anim.stop_requested() {
587                    // drop
588                    controller.as_ref().unwrap().on_stop(&anim);
589                    return None;
590                }
591
592                if let Some(sleep) = anim.sleep_deadline() {
593                    if sleep > info.next_frame {
594                        // retain sleep
595                        return Some(sleep);
596                    } else if sleep.0 > info.now {
597                        // sync-up to frame rate after sleep
598                        anim.reset_sleep();
599                        return Some(info.next_frame);
600                    }
601                }
602
603                anim.reset_state(info.animations_enabled, info.now, info.time_scale);
604
605                VARS_ANIMATION_CTRL_CTX.with_context(&mut controller, || {
606                    VARS_MODIFY_CTX.with_context(&mut anim_modify_info, || animation(&anim))
607                });
608
609                // retain until next frame
610                //
611                // stop or sleep may be requested after this (during modify apply),
612                // these updates are applied on the next frame.
613                Some(info.next_frame)
614            } else {
615                // drop
616                controller.as_ref().unwrap().on_stop(&anim);
617                None
618            }
619        }));
620
621        vars.ans.next_frame = Some(Deadline(DInstant::EPOCH));
622
623        vars.wake_app();
624
625        handle
626    }
627}
628
629type AnimationFn = Box<dyn FnMut(AnimationUpdateInfo) -> Option<Deadline> + Send>;
630
631#[derive(Clone, Copy)]
632struct AnimationUpdateInfo {
633    animations_enabled: bool,
634    now: DInstant,
635    time_scale: Factor,
636    next_frame: Deadline,
637}
638
639pub(super) fn var_animate<T: VarValue>(
640    target: &impl Var<T>,
641    animate: impl FnMut(&Animation, &mut VarModify<T>) + Send + 'static,
642) -> AnimationHandle {
643    if !target.capabilities().is_always_read_only() {
644        let target = target.clone().actual_var();
645        if !target.capabilities().is_always_read_only() {
646            // target var can be animated.
647
648            let wk_target = target.downgrade();
649            let animate = Arc::new(Mutex::new(animate));
650
651            return VARS.animate(move |args| {
652                // animation
653
654                if let Some(target) = wk_target.upgrade() {
655                    // target still exists
656
657                    if target.modify_importance() > VARS.current_modify().importance {
658                        // var modified by a more recent animation or directly, this animation cannot
659                        // affect it anymore.
660                        args.stop();
661                        return;
662                    }
663
664                    // try update
665                    let r = target.modify(clmv!(animate, args, |value| {
666                        (animate.lock())(&args, value);
667                    }));
668
669                    if let Err(VarIsReadOnlyError { .. }) = r {
670                        // var can maybe change to allow write again, but we wipe all animations anyway.
671                        args.stop();
672                    }
673                } else {
674                    // target dropped.
675                    args.stop();
676                }
677            });
678        }
679    }
680    AnimationHandle::dummy()
681}
682
683pub(super) fn var_sequence<T: VarValue, V: Var<T>>(
684    target: &V,
685    animate: impl FnMut(&<<V::ActualVar as Var<T>>::Downgrade as WeakVar<T>>::Upgrade) -> AnimationHandle + Send + 'static,
686) -> VarHandle {
687    if !target.capabilities().is_always_read_only() {
688        let target = target.clone().actual_var();
689        if !target.capabilities().is_always_read_only() {
690            // target var can be animated.
691
692            let (handle, handle_hook) = VarHandle::new(Box::new(|_| true));
693
694            let wk_target = target.downgrade();
695
696            #[derive(Clone)]
697            struct SequenceController(Arc<dyn Fn() + Send + Sync + 'static>);
698            impl AnimationController for SequenceController {
699                fn on_stop(&self, _: &Animation) {
700                    let ctrl = self.clone();
701                    VARS.with_animation_controller(ctrl, || (self.0)());
702                }
703            }
704            let animate = Mutex::new(animate);
705            let animate = Arc::new(move || {
706                if let Some(target) = wk_target.upgrade() {
707                    if target.modify_importance() <= VARS.current_modify().importance()
708                        && handle_hook.is_alive()
709                        && VARS.animations_enabled().get()
710                    {
711                        (animate.lock())(&target).perm();
712                    }
713                }
714            });
715            VARS.with_animation_controller(SequenceController(animate.clone()), || {
716                animate();
717            });
718
719            return handle;
720        }
721    }
722    VarHandle::dummy()
723}
724
725pub(super) fn var_set_ease_with<T>(
726    start_value: T,
727    end_value: T,
728    duration: Duration,
729    easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
730    init_step: EasingStep, // set to 0 skips first frame, set to 999 includes first frame.
731    sampler: impl Fn(&Transition<T>, EasingStep) -> T + Send + 'static,
732) -> impl FnMut(&Animation, &mut VarModify<T>) + Send
733where
734    T: VarValue + Transitionable,
735{
736    let transition = Transition::new(start_value, end_value);
737    let mut prev_step = init_step;
738    move |a, vm| {
739        let step = easing(a.elapsed_stop(duration));
740
741        if prev_step != step {
742            vm.set(sampler(&transition, step));
743            prev_step = step;
744        }
745    }
746}
747
748pub(super) fn var_set_ease_oci_with<T>(
749    start_value: T,
750    end_value: T,
751    duration: Duration,
752    easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
753    init_step: EasingStep, // set to 0 skips first frame, set to 999 includes first frame.
754    sampler: impl Fn(&Transition<T>, EasingStep) -> T + Send + 'static,
755) -> impl FnMut(&Animation, &mut VarModify<T>) + Send
756where
757    T: VarValue + Transitionable,
758{
759    let transition = Transition::new(start_value, end_value);
760    let mut prev_step = init_step;
761    move |a, vm| {
762        let t = a.elapsed(duration);
763        let mut step = easing(t);
764        if a.restart_count() % 2 != 0 {
765            step = step.flip()
766        }
767        if t.is_end() {
768            a.restart();
769        }
770
771        if prev_step != step {
772            vm.set(sampler(&transition, step));
773            prev_step = step;
774        }
775    }
776}
777
778pub(super) fn var_set_ease_keyed_with<T>(
779    transition: TransitionKeyed<T>,
780    duration: Duration,
781    easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
782    init_step: EasingStep,
783    sampler: impl Fn(&TransitionKeyed<T>, EasingStep) -> T + Send + 'static,
784) -> impl FnMut(&Animation, &mut VarModify<T>) + Send
785where
786    T: VarValue + Transitionable,
787{
788    let mut prev_step = init_step;
789    move |a, value| {
790        let step = easing(a.elapsed_stop(duration));
791
792        if prev_step != step {
793            value.set(sampler(&transition, step));
794            prev_step = step;
795        }
796    }
797}
798
799pub(super) fn var_step<T>(new_value: T, delay: Duration) -> impl FnMut(&Animation, &mut VarModify<T>)
800where
801    T: VarValue,
802{
803    let mut new_value = Some(new_value);
804    move |a, vm| {
805        if !a.animations_enabled() || a.elapsed_dur() >= delay {
806            a.stop();
807            if let Some(nv) = new_value.take() {
808                vm.set(nv);
809            }
810        } else {
811            a.sleep(delay);
812        }
813    }
814}
815
816pub(super) fn var_step_oci<T>(values: [T; 2], delay: Duration, mut set: bool) -> impl FnMut(&Animation, &mut VarModify<T>)
817where
818    T: VarValue,
819{
820    let mut first = false;
821    move |a, vm| {
822        if !a.animations_enabled() || mem::take(&mut set) {
823            vm.set(values[0].clone());
824        } else if a.elapsed_dur() >= delay {
825            if first {
826                vm.set(values[0].clone());
827            } else {
828                vm.set(values[1].clone());
829            }
830            first = !first;
831        }
832        a.sleep(delay);
833    }
834}
835
836pub(super) fn var_steps<T: VarValue>(
837    steps: Vec<(Factor, T)>,
838    duration: Duration,
839    easing: impl Fn(EasingTime) -> EasingStep + 'static,
840) -> impl FnMut(&Animation, &mut VarModify<T>) {
841    let mut prev_step = 999.fct();
842    move |a, vm| {
843        let step = easing(a.elapsed_stop(duration));
844        if step != prev_step {
845            prev_step = step;
846            if let Some(val) = steps.iter().find(|(f, _)| *f >= step).map(|(_, step)| step.clone()) {
847                vm.set(val);
848            }
849        }
850    }
851}
852
853/// Represents the editable final value of a [`Var::chase`] animation.
854pub struct ChaseAnimation<T: VarValue + animation::Transitionable> {
855    target: T,
856    var: BoxedVar<T>,
857    handle: animation::AnimationHandle,
858}
859impl<T> fmt::Debug for ChaseAnimation<T>
860where
861    T: VarValue + animation::Transitionable,
862{
863    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
864        f.debug_struct("ChaseAnimation")
865            .field("target", &self.target)
866            .finish_non_exhaustive()
867    }
868}
869impl<T> ChaseAnimation<T>
870where
871    T: VarValue + animation::Transitionable,
872{
873    /// Current animation target.
874    pub fn target(&self) -> &T {
875        &self.target
876    }
877
878    /// Modify the chase target, replaces the animation with a new one from the current value to the modified target.
879    pub fn modify(&mut self, modify: impl FnOnce(&mut T), duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
880        if self.handle.is_stopped() {
881            // re-sync target
882            self.target = self.var.get();
883        }
884        modify(&mut self.target);
885        self.handle = self.var.ease(self.target.clone(), duration, easing);
886    }
887
888    /// Replace the chase target, replaces the animation with a new one from the current value to the modified target.
889    pub fn set(&mut self, value: impl Into<T>, duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
890        self.target = value.into();
891        self.handle = self.var.ease(self.target.clone(), duration, easing);
892    }
893}
894
895pub(super) fn var_chase<T>(
896    var: BoxedVar<T>,
897    first_target: T,
898    duration: Duration,
899    easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
900) -> ChaseAnimation<T>
901where
902    T: VarValue + animation::Transitionable,
903{
904    ChaseAnimation {
905        handle: var.ease(first_target.clone(), duration, easing),
906        target: first_target,
907        var,
908    }
909}
910
911/// Represents the current *modify* operation when it is applying.
912#[derive(Clone)]
913pub struct ModifyInfo {
914    handle: Option<WeakAnimationHandle>,
915    pub(crate) importance: usize,
916}
917impl ModifyInfo {
918    /// Initial value, is always of lowest importance.
919    pub fn never() -> Self {
920        ModifyInfo {
921            handle: None,
922            importance: 0,
923        }
924    }
925
926    /// Indicates the *override* importance of the operation, when two animations target
927    /// a variable only the newer one must apply, and all running animations are *overridden* by
928    /// a later modify/set operation.
929    ///
930    /// Variables ignore modify requests from lower importance closures.
931    pub fn importance(&self) -> usize {
932        self.importance
933    }
934
935    /// Indicates if the *modify* request was made from inside an animation, if `true` the [`importance`]
936    /// is for that animation, even if the modify request is from the current frame.
937    ///
938    /// You can clone this info to track this animation, when it stops or is dropped this returns `false`. Note
939    /// that sleeping animations still count as animating.
940    ///
941    /// [`importance`]: Self::importance
942    pub fn is_animating(&self) -> bool {
943        self.handle.as_ref().map(|h| h.upgrade().is_some()).unwrap_or(false)
944    }
945
946    /// Returns `true` if `self` and `other` have the same animation or are both not animating.
947    pub fn animation_eq(&self, other: &Self) -> bool {
948        self.handle == other.handle
949    }
950
951    /// Register a `handler` to be called once when the current animation stops.
952    ///
953    /// [`importance`]: Self::importance
954    pub fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
955        if let Some(h) = &self.handle {
956            if let Some(h) = h.upgrade() {
957                return h.hook_animation_stop(handler);
958            }
959        }
960        Err(handler)
961    }
962}
963impl fmt::Debug for ModifyInfo {
964    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965        f.debug_struct("ModifyInfo")
966            .field("is_animating()", &self.is_animating())
967            .field("importance()", &self.importance)
968            .finish()
969    }
970}
971
972/// Animations controller.
973///
974/// See [`VARS.with_animation_controller`] for more details.
975///
976/// [`VARS.with_animation_controller`]: VARS::with_animation_controller
977pub trait AnimationController: Send + Sync + Any {
978    /// Called for each `animation` that starts in the controller context.
979    ///
980    /// Note that this handler itself is not called inside the controller context.
981    fn on_start(&self, animation: &Animation) {
982        let _ = animation;
983    }
984
985    /// Called for each `animation` that ends in the controller context.
986    ///
987    /// Note that this handler itself is not called inside the controller context.
988    fn on_stop(&self, animation: &Animation) {
989        let _ = animation;
990    }
991}
992
993impl AnimationController for () {}
994
995/// An [`AnimationController`] that forces animations to run even if animations are not enabled.
996pub struct ForceAnimationController;
997impl AnimationController for ForceAnimationController {
998    fn on_start(&self, animation: &Animation) {
999        animation.force_enable();
1000    }
1001}
1002
1003context_local! {
1004    pub(crate) static VARS_ANIMATION_CTRL_CTX: Box<dyn AnimationController> = {
1005        let r: Box<dyn AnimationController> = Box::new(());
1006        r
1007    };
1008}
1009
1010/// View on an app loop timer.
1011pub trait AnimationTimer {
1012    /// Returns `true` if the `deadline` has elapsed, `false` if the `deadline` was
1013    /// registered for future waking.
1014    fn elapsed(&mut self, deadline: Deadline) -> bool;
1015
1016    /// Register the future `deadline` for waking.
1017    fn register(&mut self, deadline: Deadline);
1018
1019    /// Frame timestamp.
1020    fn now(&self) -> DInstant;
1021}