1use std::{any::Any, fmt, sync::Arc, time::Duration};
4
5use parking_lot::Mutex;
6use smallbox::SmallBox;
7use zng_app_context::context_local;
8use zng_handle::{Handle, HandleOwner, WeakHandle};
9use zng_time::{DInstant, Deadline};
10use zng_unit::Factor;
11
12use crate::{
13 Var, VarHandle, VarHandlerOwner, VarValue,
14 animation::easing::{EasingStep, EasingTime},
15};
16
17pub mod easing;
18pub use zng_var_proc_macros::Transitionable;
19
20pub trait AnimationTimer {
22 fn elapsed(&mut self, deadline: Deadline) -> bool;
25
26 fn register(&mut self, deadline: Deadline);
28
29 fn now(&self) -> DInstant;
31}
32
33pub trait AnimationController: Send + Sync + Any {
39 fn on_start(&self, animation: &Animation) {
43 let _ = animation;
44 }
45
46 fn on_stop(&self, animation: &Animation) {
50 let _ = animation;
51 }
52}
53
54impl AnimationController for () {}
55
56pub struct ForceAnimationController;
58impl AnimationController for ForceAnimationController {
59 fn on_start(&self, animation: &Animation) {
60 animation.force_enable();
61 }
62}
63
64context_local! {
65 pub(crate) static VARS_ANIMATION_CTRL_CTX: Box<dyn AnimationController> = {
66 let r: Box<dyn AnimationController> = Box::new(());
67 r
68 };
69}
70
71#[derive(Clone)]
77pub struct Animation(Arc<Mutex<AnimationData>>);
78struct AnimationData {
79 start_time: DInstant,
80 restarted_count: usize,
81 stop: bool,
82 sleep: Option<Deadline>,
83 restart_next: bool,
84 animations_enabled: bool,
85 force_enabled: bool,
86 now: DInstant,
87 time_scale: Factor,
88}
89
90impl Animation {
91 pub(super) fn new(animations_enabled: bool, now: DInstant, time_scale: Factor) -> Self {
92 Animation(Arc::new(Mutex::new(AnimationData {
93 start_time: now,
94 restarted_count: 0,
95 stop: false,
96 now,
97 sleep: None,
98 restart_next: false,
99 animations_enabled,
100 force_enabled: false,
101 time_scale,
102 })))
103 }
104
105 pub fn start_time(&self) -> DInstant {
107 self.0.lock().start_time
108 }
109
110 pub fn now(&self) -> DInstant {
117 self.0.lock().now
118 }
119
120 pub fn time_scale(&self) -> Factor {
122 self.0.lock().time_scale
123 }
124
125 pub(crate) fn reset_state(&self, enabled: bool, now: DInstant, time_scale: Factor) {
126 let mut m = self.0.lock();
127 if !m.force_enabled {
128 m.animations_enabled = enabled;
129 }
130 m.now = now;
131 m.time_scale = time_scale;
132 m.sleep = None;
133
134 if std::mem::take(&mut m.restart_next) {
135 m.start_time = now;
136 m.restarted_count += 1;
137 }
138 }
139
140 pub(crate) fn reset_sleep(&self) {
141 self.0.lock().sleep = None;
142 }
143
144 pub fn sleep(&self, duration: Duration) {
151 self.sleep_impl(duration, false);
152 }
153
154 pub fn sleep_restart(&self, duration: Duration) {
159 self.sleep_impl(duration, true);
161 }
162 fn sleep_impl(&self, duration: Duration, restart: bool) {
163 let mut me = self.0.lock();
164 me.sleep = Some(Deadline(me.now + duration));
165 me.restart_next = restart;
166 }
167
168 pub(crate) fn sleep_deadline(&self) -> Option<Deadline> {
169 self.0.lock().sleep
170 }
171
172 pub fn animations_enabled(&self) -> bool {
176 self.0.lock().animations_enabled
177 }
178
179 pub fn force_enable(&self) {
185 let mut me = self.0.lock();
186 me.force_enabled = true;
187 me.animations_enabled = true;
188 }
189
190 pub fn elapsed_dur(&self) -> Duration {
195 let me = self.0.lock();
196 me.now - me.start_time
197 }
198
199 pub fn elapsed(&self, duration: Duration) -> EasingTime {
205 let me = self.0.lock();
206 if me.animations_enabled {
207 EasingTime::elapsed(duration, me.now - me.start_time, me.time_scale)
208 } else {
209 EasingTime::end()
210 }
211 }
212
213 pub fn elapsed_stop(&self, duration: Duration) -> EasingTime {
217 let t = self.elapsed(duration);
218 if t.is_end() {
219 self.stop()
220 }
221 t
222 }
223
224 pub fn elapsed_restart(&self, duration: Duration) -> EasingTime {
228 let t = self.elapsed(duration);
229 if t.is_end() {
230 self.restart()
231 }
232 t
233 }
234
235 pub fn elapsed_restart_stop(&self, duration: Duration, max_restarts: usize) -> EasingTime {
240 let t = self.elapsed(duration);
241 if t.is_end() {
242 if self.count() < max_restarts {
243 self.restart();
244 } else {
245 self.stop();
246 }
247 }
248 t
249 }
250
251 pub fn stop(&self) {
253 self.0.lock().stop = true;
254 }
255
256 pub fn stop_requested(&self) -> bool {
258 self.0.lock().stop
259 }
260
261 pub fn restart(&self) {
263 let mut me = self.0.lock();
264 me.start_time = me.now;
265 me.restarted_count += 1;
266 }
267
268 #[doc(hidden)]
269 #[deprecated = "use `count`"]
270 pub fn restart_count(&self) -> usize {
271 self.0.lock().restarted_count
272 }
273
274 pub fn count(&self) -> usize {
276 self.0.lock().restarted_count
277 }
278
279 pub fn set_start_time(&self, instant: DInstant) {
283 self.0.lock().start_time = instant;
284 }
285
286 pub fn set_elapsed(&self, elapsed: EasingTime, duration: Duration) {
291 let now = self.0.lock().now;
292 self.set_start_time(now.checked_sub(duration * elapsed.fct()).unwrap());
293 }
294
295 pub fn set_count(&self, count: usize) {
297 self.0.lock().restarted_count = count;
298 }
299}
300
301#[derive(Clone)]
303pub struct ModifyInfo {
304 pub(crate) handle: Option<WeakAnimationHandle>,
305 pub(crate) importance: usize,
306}
307impl ModifyInfo {
308 pub fn never() -> Self {
310 ModifyInfo {
311 handle: None,
312 importance: 0,
313 }
314 }
315
316 pub fn importance(&self) -> usize {
322 self.importance
323 }
324
325 pub fn is_animating(&self) -> bool {
333 self.handle.as_ref().map(|h| h.upgrade().is_some()).unwrap_or(false)
334 }
335
336 pub fn animation_eq(&self, other: &Self) -> bool {
338 self.handle == other.handle
339 }
340
341 pub fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle {
345 if let Some(h) = &self.handle
346 && let Some(h) = h.upgrade()
347 {
348 return h.hook_animation_stop(handler);
349 }
350 VarHandle::dummy()
351 }
352}
353impl fmt::Debug for ModifyInfo {
354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355 f.debug_struct("ModifyInfo")
356 .field("is_animating()", &self.is_animating())
357 .field("importance()", &self.importance)
358 .finish()
359 }
360}
361
362pub(crate) type AnimationStopFn = SmallBox<dyn FnMut() + Send + 'static, smallbox::space::S4>;
363
364#[derive(Default)]
365pub(super) struct AnimationHandleData {
366 on_drop: Mutex<Vec<(AnimationStopFn, VarHandlerOwner)>>,
367}
368impl Drop for AnimationHandleData {
369 fn drop(&mut self) {
370 for (mut f, h) in self.on_drop.get_mut().drain(..) {
371 if h.is_alive() {
372 f()
373 }
374 }
375 }
376}
377#[derive(Clone, PartialEq, Eq, Hash, Debug)]
384#[repr(transparent)]
385#[must_use = "the animation stops if the handle is dropped"]
386pub struct AnimationHandle(Handle<AnimationHandleData>);
387impl Default for AnimationHandle {
388 fn default() -> Self {
390 Self::dummy()
391 }
392}
393impl AnimationHandle {
394 pub(super) fn new() -> (HandleOwner<AnimationHandleData>, Self) {
395 let (owner, handle) = Handle::new(AnimationHandleData::default());
396 (owner, AnimationHandle(handle))
397 }
398
399 pub fn dummy() -> Self {
403 AnimationHandle(Handle::dummy(AnimationHandleData::default()))
404 }
405
406 pub fn perm(self) {
410 self.0.perm();
411 }
412
413 pub fn is_permanent(&self) -> bool {
417 self.0.is_permanent()
418 }
419
420 pub fn stop(self) {
422 self.0.force_drop();
423 }
424
425 pub fn is_stopped(&self) -> bool {
429 self.0.is_dropped()
430 }
431
432 pub fn downgrade(&self) -> WeakAnimationHandle {
434 WeakAnimationHandle(self.0.downgrade())
435 }
436
437 pub fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle {
443 if !self.is_stopped() {
444 let (owner, handle) = VarHandle::new();
445 self.0.data().on_drop.lock().push((handler, owner));
446 handle
447 } else {
448 VarHandle::dummy()
449 }
450 }
451}
452
453#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
455pub struct WeakAnimationHandle(pub(super) WeakHandle<AnimationHandleData>);
456impl WeakAnimationHandle {
457 pub fn new() -> Self {
459 Self(WeakHandle::new())
460 }
461
462 pub fn upgrade(&self) -> Option<AnimationHandle> {
464 self.0.upgrade().map(AnimationHandle)
465 }
466}
467
468pub trait Transitionable: VarValue {
475 fn lerp(self, to: &Self, step: EasingStep) -> Self;
477}
478
479#[non_exhaustive]
481pub struct Transition<T> {
482 pub from: T,
484 pub to: T,
487}
488impl<T> Transition<T>
489where
490 T: Transitionable,
491{
492 pub fn new(from: T, to: T) -> Self {
494 Self { from, to }
495 }
496
497 pub fn sample(&self, step: EasingStep) -> T {
499 self.from.clone().lerp(&self.to, step)
500 }
501}
502
503#[derive(Clone, Debug)]
505pub struct TransitionKeyed<T> {
506 keys: Vec<(Factor, T)>,
507}
508impl<T> TransitionKeyed<T>
509where
510 T: Transitionable,
511{
512 pub fn new(mut keys: Vec<(Factor, T)>) -> Option<Self> {
516 if keys.is_empty() {
517 return None;
518 }
519
520 for i in 1..keys.len() {
522 if keys[i].0 < keys[i - 1].0 {
523 keys[i].0 = keys[i - 1].0;
524 }
525 }
526
527 Some(TransitionKeyed { keys })
528 }
529
530 pub fn keys(&self) -> &[(Factor, T)] {
532 &self.keys
533 }
534
535 pub fn sample(&self, step: EasingStep) -> T {
537 if let Some(i) = self.keys.iter().position(|(f, _)| *f > step) {
538 if i == 0 {
539 self.keys[0].1.clone()
541 } else {
542 let (from_step, from_value) = self.keys[i - 1].clone();
543 if from_step == step {
544 from_value
546 } else {
547 let (_, to_value) = &self.keys[i];
550 let step = step - from_step;
551
552 from_value.lerp(to_value, step)
553 }
554 }
555 } else {
556 self.keys[self.keys.len() - 1].1.clone()
558 }
559 }
560}
561
562pub struct ChaseAnimation<T: VarValue + Transitionable> {
564 pub(super) target: T,
565 pub(super) var: Var<T>,
566 pub(super) handle: AnimationHandle,
567}
568impl<T> fmt::Debug for ChaseAnimation<T>
569where
570 T: VarValue + Transitionable,
571{
572 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573 f.debug_struct("ChaseAnimation")
574 .field("target", &self.target)
575 .finish_non_exhaustive()
576 }
577}
578impl<T> ChaseAnimation<T>
579where
580 T: VarValue + Transitionable,
581{
582 pub fn target(&self) -> &T {
584 &self.target
585 }
586
587 pub fn modify(&mut self, modify: impl FnOnce(&mut T), duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
589 if self.handle.is_stopped() {
590 self.target = self.var.get();
592 }
593 modify(&mut self.target);
594 self.handle = self.var.ease(self.target.clone(), duration, easing);
595 }
596
597 pub fn set(&mut self, value: impl Into<T>, duration: Duration, easing: impl Fn(EasingTime) -> EasingStep + Send + 'static) {
599 self.target = value.into();
600 self.handle = self.var.ease(self.target.clone(), duration, easing);
601 }
602}
603
604pub fn slerp_sampler<T: Transitionable>(t: &Transition<T>, step: EasingStep) -> T {
615 slerp_enabled(true, || t.sample(step))
616}
617
618pub fn is_slerp_enabled() -> bool {
622 SLERP_ENABLED.get_clone()
623}
624
625pub fn slerp_enabled<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
629 SLERP_ENABLED.with_context(&mut Some(Arc::new(enabled)), f)
630}
631
632context_local! {
633 static SLERP_ENABLED: bool = false;
634}
635
636#[expect(non_camel_case_types)]
638pub struct TRANSITIONABLE_APP;