1use super::*;
5use crate::{
6 animations::physics_simulation::{self, Simulation},
7 items::{AnimationDirection, PropertyAnimation},
8 lengths::LogicalLength,
9};
10use euclid::Length;
11#[cfg(not(feature = "std"))]
12use num_traits::Float;
13
14enum AnimationState {
15 Delaying,
17 Animating {
19 current_iteration: u64,
20 },
21 Done {
22 iteration_count: u64,
23 },
24}
25
26pub(super) struct PropertyPhysicsAnimationData<S> {
27 simulation: S,
28 state: AnimationState,
29}
30
31impl<S> PropertyPhysicsAnimationData<S>
32where
33 S: physics_simulation::Simulation,
34{
35 pub fn new(simulation: S) -> PropertyPhysicsAnimationData<S> {
36 PropertyPhysicsAnimationData { simulation, state: AnimationState::Delaying }
37 }
38
39 pub fn update_value(&mut self, target: &mut crate::Coord) -> bool {
41 match self.state {
42 AnimationState::Delaying => {
43 self.state = AnimationState::Animating { current_iteration: 0 };
45 self.update_value(target)
46 }
47 AnimationState::Animating { current_iteration: _ } => {
48 let mut value: f32 = *target as f32;
50 let finished = self.simulation.step(&mut value, crate::animations::current_tick());
51 *target = value as crate::Coord;
52 if finished {
53 self.state = AnimationState::Done { iteration_count: 0 };
54 true
55 } else {
56 false
57 }
58 }
59 AnimationState::Done { iteration_count: _ } => true,
60 }
61 }
62}
63
64pub(super) struct PropertyValueAnimationData<T> {
65 from_value: T,
66 to_value: T,
67 details: PropertyAnimation,
68 start_time: crate::animations::Instant,
69 state: AnimationState,
70}
71
72impl<T: InterpolatedPropertyValue + Clone> PropertyValueAnimationData<T> {
73 pub fn new(from_value: T, to_value: T, details: PropertyAnimation) -> Self {
74 let start_time = crate::animations::current_tick();
75
76 Self { from_value, to_value, details, start_time, state: AnimationState::Delaying }
77 }
78
79 pub fn compute_interpolated_value(&mut self) -> (T, bool) {
81 if !self.details.enabled {
83 return (self.to_value.clone(), true);
84 }
85
86 let new_tick = crate::animations::current_tick();
87 let mut time_progress = new_tick.duration_since(self.start_time).as_millis() as u64;
88 let reversed = |iteration: u64| -> bool {
89 #[allow(clippy::manual_is_multiple_of)] match self.details.direction {
91 AnimationDirection::Normal => false,
92 AnimationDirection::Reverse => true,
93 AnimationDirection::Alternate => iteration % 2 == 1,
94 AnimationDirection::AlternateReverse => iteration % 2 == 0,
95 }
96 };
97
98 match self.state {
99 AnimationState::Delaying => {
100 if self.details.delay <= 0 {
101 self.state = AnimationState::Animating { current_iteration: 0 };
102 return self.compute_interpolated_value();
103 }
104
105 let delay = self.details.delay as u64;
106
107 if time_progress < delay {
108 if reversed(0) {
109 (self.to_value.clone(), false)
110 } else {
111 (self.from_value.clone(), false)
112 }
113 } else {
114 self.start_time =
115 new_tick - core::time::Duration::from_millis(time_progress - delay);
116
117 self.state = AnimationState::Animating { current_iteration: 0 };
119 self.compute_interpolated_value()
120 }
121 }
122 AnimationState::Animating { mut current_iteration } => {
123 if self.details.duration <= 0 || self.details.iteration_count == 0. {
124 self.state = AnimationState::Done { iteration_count: 0 };
125 return self.compute_interpolated_value();
126 }
127
128 let duration = self.details.duration as u64;
129 if time_progress >= duration {
130 current_iteration += time_progress / duration;
132 time_progress %= duration;
133 self.start_time = new_tick - core::time::Duration::from_millis(time_progress);
134 }
135
136 if (self.details.iteration_count < 0.)
137 || (((current_iteration * duration) + time_progress) as f64)
138 < ((self.details.iteration_count as f64) * (duration as f64))
139 {
140 self.state = AnimationState::Animating { current_iteration };
141
142 let progress = {
143 let progress =
144 (time_progress as f32 / self.details.duration as f32).clamp(0., 1.);
145 if reversed(current_iteration) { 1. - progress } else { progress }
146 };
147 let t = crate::animations::easing_curve(&self.details.easing, progress);
148 let val = self.from_value.interpolate(&self.to_value, t);
149
150 (val, false)
151 } else {
152 self.state =
153 AnimationState::Done { iteration_count: current_iteration.max(1) - 1 };
154 self.compute_interpolated_value()
155 }
156 }
157 AnimationState::Done { iteration_count } => {
158 if reversed(iteration_count) {
159 (self.from_value.clone(), true)
160 } else {
161 (self.to_value.clone(), true)
162 }
163 }
164 }
165 }
166
167 fn reset(&mut self) {
168 self.state = AnimationState::Delaying;
169 self.start_time = crate::animations::current_tick();
170 }
171}
172
173#[derive(Clone, Copy, Eq, PartialEq, Debug)]
174pub(super) enum AnimatedBindingState {
175 Animating,
176 NotAnimating,
177 ShouldStart,
178}
179
180#[pin_project::pin_project]
181pub(super) struct AnimatedBindingCallable<T, A> {
182 #[pin]
183 pub(super) original_binding: PropertyHandle,
184 pub(super) state: Cell<AnimatedBindingState>,
185 pub(super) animation_data: RefCell<PropertyValueAnimationData<T>>,
186 pub(super) compute_animation_details: A,
187}
188
189pub(super) type AnimationDetail = (PropertyAnimation, Option<crate::animations::Instant>);
190
191unsafe impl<T: InterpolatedPropertyValue + Clone, A: Fn() -> AnimationDetail> BindingCallable<T>
192 for AnimatedBindingCallable<T, A>
193{
194 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
195 let original_binding = self.project_ref().original_binding;
196 original_binding.register_as_dependency_to_current_binding(
197 #[cfg(slint_debug_property)]
198 "<AnimatedBindingCallable>",
199 );
200 match self.state.get() {
201 AnimatedBindingState::Animating => {
202 let (val, finished) = self.animation_data.borrow_mut().compute_interpolated_value();
203 *value = val;
204 if finished {
205 self.state.set(AnimatedBindingState::NotAnimating)
206 } else {
207 crate::animations::CURRENT_ANIMATION_DRIVER
208 .with(|driver| driver.set_has_active_animations());
209 }
210 }
211 AnimatedBindingState::NotAnimating => {
212 unsafe { self.original_binding.update(value as *mut T) };
214 }
215 AnimatedBindingState::ShouldStart => {
216 self.state.set(AnimatedBindingState::Animating);
217 let mut animation_data = self.animation_data.borrow_mut();
218 animation_data.from_value = value.clone();
220 let (details, start_time) = (self.compute_animation_details)();
221 if let Some(start_time) = start_time {
222 animation_data.start_time = start_time;
223 }
224 animation_data.details = details;
225
226 unsafe { self.original_binding.update((&mut animation_data.to_value) as *mut T) };
228 let (val, finished) = animation_data.compute_interpolated_value();
229 *value = val;
230 if finished {
231 self.state.set(AnimatedBindingState::NotAnimating)
232 } else {
233 crate::animations::CURRENT_ANIMATION_DRIVER
234 .with(|driver| driver.set_has_active_animations());
235 }
236 }
237 };
238 BindingResult::KeepBinding
239 }
240 fn mark_dirty(self: Pin<&Self>) {
241 if self.state.get() == AnimatedBindingState::ShouldStart {
242 return;
243 }
244 let original_dirty = self.original_binding.access(|b| b.unwrap().dirty.get());
245 if original_dirty {
246 self.state.set(AnimatedBindingState::ShouldStart);
247 self.animation_data.borrow_mut().reset();
248 }
249 }
250}
251
252pub trait InterpolatedPropertyValue: PartialEq + Default + 'static {
256 #[must_use]
260 fn interpolate(&self, target_value: &Self, t: f32) -> Self;
261}
262
263impl InterpolatedPropertyValue for f32 {
264 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
265 self + t * (target_value - self)
266 }
267}
268
269impl InterpolatedPropertyValue for i32 {
270 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
271 self + (t * (target_value - self) as f32).round() as i32
272 }
273}
274
275impl InterpolatedPropertyValue for i64 {
276 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
277 self + (t * (target_value - self) as f32).round() as Self
278 }
279}
280
281impl InterpolatedPropertyValue for u8 {
282 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
283 ((*self as f32) + (t * ((*target_value as f32) - (*self as f32)))).round().clamp(0., 255.)
284 as u8
285 }
286}
287
288impl InterpolatedPropertyValue for LogicalLength {
289 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
290 LogicalLength::new(self.get().interpolate(&target_value.get(), t))
291 }
292}
293
294impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
295 pub fn remove_binding(self: Pin<&Self>) {
300 self.set_animated_value(self.get(), PropertyAnimation::default());
305 }
306
307 pub fn set_animated_value(self: Pin<&Self>, value: T, animation_data: PropertyAnimation) {
314 let d = RefCell::new(properties_animations::PropertyValueAnimationData::new(
315 self.get(),
316 value,
317 animation_data,
318 ));
319 unsafe {
321 self.handle.set_binding(
322 move |val: &mut T| {
323 let (value, finished) = d.borrow_mut().compute_interpolated_value();
324 *val = value;
325 if finished {
326 BindingResult::RemoveBinding
327 } else {
328 crate::animations::CURRENT_ANIMATION_DRIVER
329 .with(|driver| driver.set_has_active_animations());
330 BindingResult::KeepBinding
331 }
332 },
333 #[cfg(slint_debug_property)]
334 self.debug_name.borrow().as_str(),
335 );
336 }
337 self.handle.mark_dirty(
338 #[cfg(slint_debug_property)]
339 self.debug_name.borrow().as_str(),
340 );
341 }
342
343 pub fn set_animated_binding(
346 &self,
347 binding: impl Binding<T> + 'static,
348 compute_animation_details: impl Fn() -> (PropertyAnimation, Option<crate::animations::Instant>)
349 + 'static,
350 ) {
351 let binding_callable = properties_animations::AnimatedBindingCallable::<T, _> {
352 original_binding: PropertyHandle {
353 handle: Cell::new(
354 (alloc_binding_holder(move |val: &mut T| {
355 *val = binding.evaluate(val);
356 BindingResult::KeepBinding
357 }) as *mut ())
358 .map_addr(|a| a | 0b10),
359 ),
360 },
361 state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
362 animation_data: RefCell::new(properties_animations::PropertyValueAnimationData::new(
363 T::default(),
364 T::default(),
365 PropertyAnimation::default(),
366 )),
367 compute_animation_details,
368 };
369
370 unsafe {
372 self.handle.set_binding(
373 binding_callable,
374 #[cfg(slint_debug_property)]
375 self.debug_name.borrow().as_str(),
376 )
377 };
378 self.handle.mark_dirty(
379 #[cfg(slint_debug_property)]
380 self.debug_name.borrow().as_str(),
381 );
382 }
383}
384
385unsafe impl<Unit, S: Simulation> BindingCallable<Length<crate::Coord, Unit>>
386 for RefCell<PropertyPhysicsAnimationData<S>>
387{
388 fn evaluate(self: Pin<&Self>, value: &mut Length<crate::Coord, Unit>) -> BindingResult {
389 let finished = self.borrow_mut().update_value(&mut value.0);
390 if finished {
391 BindingResult::RemoveBinding
392 } else {
393 crate::animations::CURRENT_ANIMATION_DRIVER
394 .with(|driver| driver.set_has_active_animations());
395 BindingResult::KeepBinding
396 }
397 }
398
399 fn intercept_set(self: Pin<&Self>, _value: &Length<crate::Coord, Unit>) -> bool {
401 true
402 }
403}
404
405impl<Unit> Property<Length<crate::Coord, Unit>> {
406 pub fn set_physic_animation_value<
408 S: physics_simulation::Simulation + 'static,
409 AD: physics_simulation::Parameter<Output = S>,
410 >(
411 &self,
412 limit_value: Pin<Box<Property<f32>>>,
413 simulation_data: AD,
414 ) {
415 unsafe {
417 self.handle.set_binding::<Length<crate::Coord, Unit>, core::cell::RefCell<PropertyPhysicsAnimationData<S>>>(RefCell::new(PropertyPhysicsAnimationData::new(
418 simulation_data.simulation(self.get_internal().0 as f32, limit_value),
419 )),
420 #[cfg(slint_debug_property)]
421 self.debug_name.borrow().as_str()
422 );
423 }
424 self.handle.mark_dirty(
425 #[cfg(slint_debug_property)]
426 self.debug_name.borrow().as_str(),
427 );
428 }
429}
430
431#[cfg(test)]
432mod animation_tests {
433 use super::*;
434 use pin_weak::rc::PinWeak;
435 use std::rc::Rc;
436
437 #[derive(Default)]
438 struct Component {
439 width: Property<i32>,
440 width_times_two: Property<i32>,
441 feed_property: Property<i32>, }
443
444 impl Component {
445 fn new_test_component() -> Pin<Rc<Self>> {
446 let compo = Rc::pin(Component::default());
447 let w = PinWeak::downgrade(compo.clone());
448 compo.width_times_two.set_binding(move || {
449 let compo = w.upgrade().unwrap();
450 get_prop_value(&compo.width) * 2
451 });
452
453 compo
454 }
455 }
456
457 const DURATION: std::time::Duration = std::time::Duration::from_millis(10000);
458 const DELAY: std::time::Duration = std::time::Duration::from_millis(800);
459
460 fn get_prop_value<T: Clone>(prop: &Property<T>) -> T {
462 unsafe { Pin::new_unchecked(prop).get() }
463 }
464
465 fn set_animated_value<T: Clone + InterpolatedPropertyValue + 'static>(
467 prop: &Property<T>,
468 value: T,
469 animation_data: PropertyAnimation,
470 ) {
471 unsafe { Pin::new_unchecked(prop) }.set_animated_value(value, animation_data);
472 }
473
474 #[test]
475 fn properties_test_animation_negative_delay_triggered_by_set() {
476 let compo = Component::new_test_component();
477
478 let animation_details = PropertyAnimation {
479 delay: -25,
480 duration: DURATION.as_millis() as _,
481 iteration_count: 1.,
482 ..PropertyAnimation::default()
483 };
484
485 compo.width.set(100);
486 assert_eq!(get_prop_value(&compo.width), 100);
487 assert_eq!(get_prop_value(&compo.width_times_two), 200);
488
489 let start_time = crate::animations::current_tick();
490
491 set_animated_value(&compo.width, 200, animation_details);
492 assert_eq!(get_prop_value(&compo.width), 100);
493 assert_eq!(get_prop_value(&compo.width_times_two), 200);
494
495 crate::animations::CURRENT_ANIMATION_DRIVER
496 .with(|driver| driver.update_animations(start_time + DURATION / 2));
497 assert_eq!(get_prop_value(&compo.width), 150);
498 assert_eq!(get_prop_value(&compo.width_times_two), 300);
499
500 crate::animations::CURRENT_ANIMATION_DRIVER
501 .with(|driver| driver.update_animations(start_time + DURATION));
502 assert_eq!(get_prop_value(&compo.width), 200);
503 assert_eq!(get_prop_value(&compo.width_times_two), 400);
504
505 crate::animations::CURRENT_ANIMATION_DRIVER
507 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
508 assert_eq!(get_prop_value(&compo.width), 200);
509 assert_eq!(get_prop_value(&compo.width_times_two), 400);
510
511 compo.width.handle.access(|binding| assert!(binding.is_none()));
513 }
514
515 #[test]
516 fn properties_test_animation_triggered_by_set() {
517 let compo = Component::new_test_component();
518
519 let animation_details = PropertyAnimation {
520 duration: DURATION.as_millis() as _,
521 iteration_count: 1.,
522 ..PropertyAnimation::default()
523 };
524
525 compo.width.set(100);
526 assert_eq!(get_prop_value(&compo.width), 100);
527 assert_eq!(get_prop_value(&compo.width_times_two), 200);
528
529 let start_time = crate::animations::current_tick();
530
531 set_animated_value(&compo.width, 200, animation_details);
532 assert_eq!(get_prop_value(&compo.width), 100);
533 assert_eq!(get_prop_value(&compo.width_times_two), 200);
534
535 crate::animations::CURRENT_ANIMATION_DRIVER
536 .with(|driver| driver.update_animations(start_time + DURATION / 2));
537 assert_eq!(get_prop_value(&compo.width), 150);
538 assert_eq!(get_prop_value(&compo.width_times_two), 300);
539
540 crate::animations::CURRENT_ANIMATION_DRIVER
541 .with(|driver| driver.update_animations(start_time + DURATION));
542 assert_eq!(get_prop_value(&compo.width), 200);
543 assert_eq!(get_prop_value(&compo.width_times_two), 400);
544
545 crate::animations::CURRENT_ANIMATION_DRIVER
547 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
548 assert_eq!(get_prop_value(&compo.width), 200);
549 assert_eq!(get_prop_value(&compo.width_times_two), 400);
550
551 compo.width.handle.access(|binding| assert!(binding.is_none()));
553 }
554
555 #[test]
556 fn properties_test_delayed_animation_triggered_by_set() {
557 let compo = Component::new_test_component();
558
559 let animation_details = PropertyAnimation {
560 delay: DELAY.as_millis() as _,
561 iteration_count: 1.,
562 duration: DURATION.as_millis() as _,
563 ..PropertyAnimation::default()
564 };
565
566 compo.width.set(100);
567 assert_eq!(get_prop_value(&compo.width), 100);
568 assert_eq!(get_prop_value(&compo.width_times_two), 200);
569
570 let start_time = crate::animations::current_tick();
571
572 set_animated_value(&compo.width, 200, animation_details);
573 assert_eq!(get_prop_value(&compo.width), 100);
574 assert_eq!(get_prop_value(&compo.width_times_two), 200);
575
576 crate::animations::CURRENT_ANIMATION_DRIVER
578 .with(|driver| driver.update_animations(start_time + DELAY / 2));
579 assert_eq!(get_prop_value(&compo.width), 100);
580 assert_eq!(get_prop_value(&compo.width_times_two), 200);
581
582 crate::animations::CURRENT_ANIMATION_DRIVER
584 .with(|driver| driver.update_animations(start_time + DELAY));
585 assert_eq!(get_prop_value(&compo.width), 100);
586 assert_eq!(get_prop_value(&compo.width_times_two), 200);
587
588 crate::animations::CURRENT_ANIMATION_DRIVER
589 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
590 assert_eq!(get_prop_value(&compo.width), 150);
591 assert_eq!(get_prop_value(&compo.width_times_two), 300);
592
593 crate::animations::CURRENT_ANIMATION_DRIVER
594 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
595 assert_eq!(get_prop_value(&compo.width), 200);
596 assert_eq!(get_prop_value(&compo.width_times_two), 400);
597
598 crate::animations::CURRENT_ANIMATION_DRIVER
600 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
601 assert_eq!(get_prop_value(&compo.width), 200);
602 assert_eq!(get_prop_value(&compo.width_times_two), 400);
603
604 compo.width.handle.access(|binding| assert!(binding.is_none()));
606 }
607
608 #[test]
609 fn properties_test_delayed_animation_fractal_iteration_triggered_by_set() {
610 let compo = Component::new_test_component();
611
612 let animation_details = PropertyAnimation {
613 delay: DELAY.as_millis() as _,
614 iteration_count: 1.5,
615 duration: DURATION.as_millis() as _,
616 ..PropertyAnimation::default()
617 };
618
619 compo.width.set(100);
620 assert_eq!(get_prop_value(&compo.width), 100);
621 assert_eq!(get_prop_value(&compo.width_times_two), 200);
622
623 let start_time = crate::animations::current_tick();
624
625 set_animated_value(&compo.width, 200, animation_details);
626 assert_eq!(get_prop_value(&compo.width), 100);
627 assert_eq!(get_prop_value(&compo.width_times_two), 200);
628
629 crate::animations::CURRENT_ANIMATION_DRIVER
631 .with(|driver| driver.update_animations(start_time + DELAY / 2));
632 assert_eq!(get_prop_value(&compo.width), 100);
633 assert_eq!(get_prop_value(&compo.width_times_two), 200);
634
635 crate::animations::CURRENT_ANIMATION_DRIVER
637 .with(|driver| driver.update_animations(start_time + DELAY));
638 assert_eq!(get_prop_value(&compo.width), 100);
639 assert_eq!(get_prop_value(&compo.width_times_two), 200);
640
641 crate::animations::CURRENT_ANIMATION_DRIVER
642 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
643 assert_eq!(get_prop_value(&compo.width), 150);
644 assert_eq!(get_prop_value(&compo.width_times_two), 300);
645
646 crate::animations::CURRENT_ANIMATION_DRIVER
647 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
648 assert_eq!(get_prop_value(&compo.width), 100);
649 assert_eq!(get_prop_value(&compo.width_times_two), 200);
650
651 crate::animations::CURRENT_ANIMATION_DRIVER
653 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 4));
654 assert_eq!(get_prop_value(&compo.width), 125);
655 assert_eq!(get_prop_value(&compo.width_times_two), 250);
656
657 crate::animations::CURRENT_ANIMATION_DRIVER
659 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
660 assert_eq!(get_prop_value(&compo.width), 200);
661 assert_eq!(get_prop_value(&compo.width_times_two), 400);
662
663 compo.width.handle.access(|binding| assert!(binding.is_none()));
665 }
666 #[test]
667 fn properties_test_delayed_animation_null_duration_triggered_by_set() {
668 let compo = Component::new_test_component();
669
670 let animation_details = PropertyAnimation {
671 delay: DELAY.as_millis() as _,
672 iteration_count: 1.0,
673 duration: 0,
674 ..PropertyAnimation::default()
675 };
676
677 compo.width.set(100);
678 assert_eq!(get_prop_value(&compo.width), 100);
679 assert_eq!(get_prop_value(&compo.width_times_two), 200);
680
681 let start_time = crate::animations::current_tick();
682
683 set_animated_value(&compo.width, 200, animation_details);
684 assert_eq!(get_prop_value(&compo.width), 100);
685 assert_eq!(get_prop_value(&compo.width_times_two), 200);
686
687 crate::animations::CURRENT_ANIMATION_DRIVER
689 .with(|driver| driver.update_animations(start_time + DELAY / 2));
690 assert_eq!(get_prop_value(&compo.width), 100);
691 assert_eq!(get_prop_value(&compo.width_times_two), 200);
692
693 crate::animations::CURRENT_ANIMATION_DRIVER
695 .with(|driver| driver.update_animations(start_time + DELAY));
696 assert_eq!(get_prop_value(&compo.width), 200);
697 assert_eq!(get_prop_value(&compo.width_times_two), 400);
698
699 crate::animations::CURRENT_ANIMATION_DRIVER
701 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
702 assert_eq!(get_prop_value(&compo.width), 200);
703 assert_eq!(get_prop_value(&compo.width_times_two), 400);
704
705 compo.width.handle.access(|binding| assert!(binding.is_none()));
707 }
708
709 #[test]
710 fn properties_test_delayed_animation_negative_duration_triggered_by_set() {
711 let compo = Component::new_test_component();
712
713 let animation_details = PropertyAnimation {
714 delay: DELAY.as_millis() as _,
715 iteration_count: 1.0,
716 duration: -25,
717 ..PropertyAnimation::default()
718 };
719
720 compo.width.set(100);
721 assert_eq!(get_prop_value(&compo.width), 100);
722 assert_eq!(get_prop_value(&compo.width_times_two), 200);
723
724 let start_time = crate::animations::current_tick();
725
726 set_animated_value(&compo.width, 200, animation_details);
727 assert_eq!(get_prop_value(&compo.width), 100);
728 assert_eq!(get_prop_value(&compo.width_times_two), 200);
729
730 crate::animations::CURRENT_ANIMATION_DRIVER
732 .with(|driver| driver.update_animations(start_time + DELAY / 2));
733 assert_eq!(get_prop_value(&compo.width), 100);
734 assert_eq!(get_prop_value(&compo.width_times_two), 200);
735
736 crate::animations::CURRENT_ANIMATION_DRIVER
738 .with(|driver| driver.update_animations(start_time + DELAY));
739 assert_eq!(get_prop_value(&compo.width), 200);
740 assert_eq!(get_prop_value(&compo.width_times_two), 400);
741
742 crate::animations::CURRENT_ANIMATION_DRIVER
744 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
745 assert_eq!(get_prop_value(&compo.width), 200);
746 assert_eq!(get_prop_value(&compo.width_times_two), 400);
747
748 compo.width.handle.access(|binding| assert!(binding.is_none()));
750 }
751
752 #[test]
753 fn properties_test_delayed_animation_no_iteration_triggered_by_set() {
754 let compo = Component::new_test_component();
755
756 let animation_details = PropertyAnimation {
757 delay: DELAY.as_millis() as _,
758 iteration_count: 0.0,
759 duration: DURATION.as_millis() as _,
760 ..PropertyAnimation::default()
761 };
762
763 compo.width.set(100);
764 assert_eq!(get_prop_value(&compo.width), 100);
765 assert_eq!(get_prop_value(&compo.width_times_two), 200);
766
767 let start_time = crate::animations::current_tick();
768
769 set_animated_value(&compo.width, 200, animation_details);
770 assert_eq!(get_prop_value(&compo.width), 100);
771 assert_eq!(get_prop_value(&compo.width_times_two), 200);
772
773 crate::animations::CURRENT_ANIMATION_DRIVER
775 .with(|driver| driver.update_animations(start_time + DELAY / 2));
776 assert_eq!(get_prop_value(&compo.width), 100);
777 assert_eq!(get_prop_value(&compo.width_times_two), 200);
778
779 crate::animations::CURRENT_ANIMATION_DRIVER
781 .with(|driver| driver.update_animations(start_time + DELAY));
782 assert_eq!(get_prop_value(&compo.width), 200);
783 assert_eq!(get_prop_value(&compo.width_times_two), 400);
784
785 crate::animations::CURRENT_ANIMATION_DRIVER
787 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
788 assert_eq!(get_prop_value(&compo.width), 200);
789 assert_eq!(get_prop_value(&compo.width_times_two), 400);
790
791 compo.width.handle.access(|binding| assert!(binding.is_none()));
793 }
794
795 #[test]
796 fn properties_test_delayed_animation_negative_iteration_triggered_by_set() {
797 let compo = Component::new_test_component();
798
799 let animation_details = PropertyAnimation {
800 delay: DELAY.as_millis() as _,
801 iteration_count: -42., duration: DURATION.as_millis() as _,
803 ..PropertyAnimation::default()
804 };
805
806 compo.width.set(100);
807 assert_eq!(get_prop_value(&compo.width), 100);
808 assert_eq!(get_prop_value(&compo.width_times_two), 200);
809
810 let start_time = crate::animations::current_tick();
811
812 set_animated_value(&compo.width, 200, animation_details);
813 assert_eq!(get_prop_value(&compo.width), 100);
814 assert_eq!(get_prop_value(&compo.width_times_two), 200);
815
816 crate::animations::CURRENT_ANIMATION_DRIVER
818 .with(|driver| driver.update_animations(start_time + DELAY / 2));
819 assert_eq!(get_prop_value(&compo.width), 100);
820 assert_eq!(get_prop_value(&compo.width_times_two), 200);
821
822 crate::animations::CURRENT_ANIMATION_DRIVER
824 .with(|driver| driver.update_animations(start_time + DELAY));
825 assert_eq!(get_prop_value(&compo.width), 100);
826 assert_eq!(get_prop_value(&compo.width_times_two), 200);
827
828 crate::animations::CURRENT_ANIMATION_DRIVER
829 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
830 assert_eq!(get_prop_value(&compo.width), 150);
831 assert_eq!(get_prop_value(&compo.width_times_two), 300);
832
833 crate::animations::CURRENT_ANIMATION_DRIVER
834 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
835 assert_eq!(get_prop_value(&compo.width), 100);
836 assert_eq!(get_prop_value(&compo.width_times_two), 200);
837
838 crate::animations::CURRENT_ANIMATION_DRIVER
840 .with(|driver| driver.update_animations(start_time + DELAY + 500 * DURATION));
841 assert_eq!(get_prop_value(&compo.width), 100);
842 assert_eq!(get_prop_value(&compo.width_times_two), 200);
843
844 crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
845 driver.update_animations(start_time + DELAY + 50000 * DURATION + DURATION / 2)
846 });
847 assert_eq!(get_prop_value(&compo.width), 150);
848 assert_eq!(get_prop_value(&compo.width_times_two), 300);
849
850 compo.width.handle.access(|binding| assert!(binding.is_some()));
852 }
853
854 #[test]
855 fn properties_test_animation_direction_triggered_by_set() {
856 let compo = Component::new_test_component();
857
858 let animation_details = PropertyAnimation {
859 delay: -25,
860 duration: DURATION.as_millis() as _,
861 direction: AnimationDirection::AlternateReverse,
862 iteration_count: 1.,
863 ..PropertyAnimation::default()
864 };
865
866 compo.width.set(100);
867 assert_eq!(get_prop_value(&compo.width), 100);
868 assert_eq!(get_prop_value(&compo.width_times_two), 200);
869
870 let start_time = crate::animations::current_tick();
871
872 set_animated_value(&compo.width, 200, animation_details);
873 assert_eq!(get_prop_value(&compo.width), 200);
874 assert_eq!(get_prop_value(&compo.width_times_two), 400);
875
876 crate::animations::CURRENT_ANIMATION_DRIVER
877 .with(|driver| driver.update_animations(start_time + DURATION / 2));
878 assert_eq!(get_prop_value(&compo.width), 150);
879 assert_eq!(get_prop_value(&compo.width_times_two), 300);
880
881 crate::animations::CURRENT_ANIMATION_DRIVER
882 .with(|driver| driver.update_animations(start_time + DURATION));
883 assert_eq!(get_prop_value(&compo.width), 100);
884 assert_eq!(get_prop_value(&compo.width_times_two), 200);
885
886 crate::animations::CURRENT_ANIMATION_DRIVER
888 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
889 assert_eq!(get_prop_value(&compo.width), 100);
890 assert_eq!(get_prop_value(&compo.width_times_two), 200);
891
892 compo.width.handle.access(|binding| assert!(binding.is_none()));
894 }
895
896 #[test]
897 fn properties_test_animation_triggered_by_binding() {
898 let compo = Component::new_test_component();
899
900 let start_time = crate::animations::current_tick();
901
902 let animation_details = PropertyAnimation {
903 duration: DURATION.as_millis() as _,
904 iteration_count: 1.,
905 ..PropertyAnimation::default()
906 };
907
908 let w = PinWeak::downgrade(compo.clone());
909 compo.width.set_animated_binding(
910 move || {
911 let compo = w.upgrade().unwrap();
912 get_prop_value(&compo.feed_property)
913 },
914 move || (animation_details.clone(), None),
915 );
916
917 compo.feed_property.set(100);
918 assert_eq!(get_prop_value(&compo.width), 100);
919 assert_eq!(get_prop_value(&compo.width_times_two), 200);
920
921 compo.feed_property.set(200);
922 assert_eq!(get_prop_value(&compo.width), 100);
923 assert_eq!(get_prop_value(&compo.width_times_two), 200);
924
925 crate::animations::CURRENT_ANIMATION_DRIVER
926 .with(|driver| driver.update_animations(start_time + DURATION / 2));
927 assert_eq!(get_prop_value(&compo.width), 150);
928 assert_eq!(get_prop_value(&compo.width_times_two), 300);
929
930 crate::animations::CURRENT_ANIMATION_DRIVER
931 .with(|driver| driver.update_animations(start_time + DURATION));
932 assert_eq!(get_prop_value(&compo.width), 200);
933 assert_eq!(get_prop_value(&compo.width_times_two), 400);
934 }
935
936 #[test]
937 fn properties_test_delayed_animation_triggered_by_binding() {
938 let compo = Component::new_test_component();
939
940 let start_time = crate::animations::current_tick();
941
942 let animation_details = PropertyAnimation {
943 delay: DELAY.as_millis() as _,
944 duration: DURATION.as_millis() as _,
945 iteration_count: 1.0,
946 ..PropertyAnimation::default()
947 };
948
949 let w = PinWeak::downgrade(compo.clone());
950 compo.width.set_animated_binding(
951 move || {
952 let compo = w.upgrade().unwrap();
953 get_prop_value(&compo.feed_property)
954 },
955 move || (animation_details.clone(), None),
956 );
957
958 compo.feed_property.set(100);
959 assert_eq!(get_prop_value(&compo.width), 100);
960 assert_eq!(get_prop_value(&compo.width_times_two), 200);
961
962 compo.feed_property.set(200);
963 assert_eq!(get_prop_value(&compo.width), 100);
964 assert_eq!(get_prop_value(&compo.width_times_two), 200);
965
966 crate::animations::CURRENT_ANIMATION_DRIVER
968 .with(|driver| driver.update_animations(start_time + DELAY / 2));
969 assert_eq!(get_prop_value(&compo.width), 100);
970 assert_eq!(get_prop_value(&compo.width_times_two), 200);
971
972 crate::animations::CURRENT_ANIMATION_DRIVER
974 .with(|driver| driver.update_animations(start_time + DELAY));
975 assert_eq!(get_prop_value(&compo.width), 100);
976 assert_eq!(get_prop_value(&compo.width_times_two), 200);
977
978 crate::animations::CURRENT_ANIMATION_DRIVER
979 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
980 assert_eq!(get_prop_value(&compo.width), 150);
981 assert_eq!(get_prop_value(&compo.width_times_two), 300);
982
983 crate::animations::CURRENT_ANIMATION_DRIVER
984 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
985 assert_eq!(get_prop_value(&compo.width), 200);
986 assert_eq!(get_prop_value(&compo.width_times_two), 400);
987
988 crate::animations::CURRENT_ANIMATION_DRIVER
990 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
991 assert_eq!(get_prop_value(&compo.width), 200);
992 assert_eq!(get_prop_value(&compo.width_times_two), 400);
993 }
994
995 #[test]
996 fn test_loop() {
997 let compo = Component::new_test_component();
998
999 let animation_details = PropertyAnimation {
1000 duration: DURATION.as_millis() as _,
1001 iteration_count: 2.,
1002 ..PropertyAnimation::default()
1003 };
1004
1005 compo.width.set(100);
1006
1007 let start_time = crate::animations::current_tick();
1008
1009 set_animated_value(&compo.width, 200, animation_details);
1010 assert_eq!(get_prop_value(&compo.width), 100);
1011
1012 crate::animations::CURRENT_ANIMATION_DRIVER
1013 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1014 assert_eq!(get_prop_value(&compo.width), 150);
1015
1016 crate::animations::CURRENT_ANIMATION_DRIVER
1017 .with(|driver| driver.update_animations(start_time + DURATION));
1018 assert_eq!(get_prop_value(&compo.width), 100);
1019
1020 crate::animations::CURRENT_ANIMATION_DRIVER
1021 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1022 assert_eq!(get_prop_value(&compo.width), 150);
1023
1024 crate::animations::CURRENT_ANIMATION_DRIVER
1025 .with(|driver| driver.update_animations(start_time + DURATION * 2));
1026 assert_eq!(get_prop_value(&compo.width), 200);
1027
1028 compo.width.handle.access(|binding| assert!(binding.is_none()));
1030 }
1031
1032 #[test]
1033 fn test_loop_via_binding() {
1034 let compo = Component::new_test_component();
1037
1038 let start_time = crate::animations::current_tick();
1039
1040 let animation_details = PropertyAnimation {
1041 duration: DURATION.as_millis() as _,
1042 iteration_count: 2.,
1043 ..PropertyAnimation::default()
1044 };
1045
1046 let w = PinWeak::downgrade(compo.clone());
1047 compo.width.set_animated_binding(
1048 move || {
1049 let compo = w.upgrade().unwrap();
1050 get_prop_value(&compo.feed_property)
1051 },
1052 move || (animation_details.clone(), None),
1053 );
1054
1055 compo.feed_property.set(100);
1056 assert_eq!(get_prop_value(&compo.width), 100);
1057
1058 compo.feed_property.set(200);
1059 assert_eq!(get_prop_value(&compo.width), 100);
1060
1061 crate::animations::CURRENT_ANIMATION_DRIVER
1062 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1063
1064 assert_eq!(get_prop_value(&compo.width), 150);
1065
1066 crate::animations::CURRENT_ANIMATION_DRIVER
1067 .with(|driver| driver.update_animations(start_time + DURATION));
1068
1069 assert_eq!(get_prop_value(&compo.width), 100);
1070
1071 crate::animations::CURRENT_ANIMATION_DRIVER
1072 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1073
1074 assert_eq!(get_prop_value(&compo.width), 150);
1075
1076 crate::animations::CURRENT_ANIMATION_DRIVER
1077 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
1078
1079 assert_eq!(get_prop_value(&compo.width), 200);
1080
1081 crate::animations::CURRENT_ANIMATION_DRIVER
1083 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
1084
1085 assert_eq!(get_prop_value(&compo.width), 200);
1086
1087 let start_time = crate::animations::current_tick();
1090
1091 compo.feed_property.set(300);
1092 assert_eq!(get_prop_value(&compo.width), 200);
1093
1094 crate::animations::CURRENT_ANIMATION_DRIVER
1095 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1096
1097 assert_eq!(get_prop_value(&compo.width), 250);
1098
1099 crate::animations::CURRENT_ANIMATION_DRIVER
1100 .with(|driver| driver.update_animations(start_time + DURATION));
1101
1102 assert_eq!(get_prop_value(&compo.width), 200);
1103
1104 crate::animations::CURRENT_ANIMATION_DRIVER
1105 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1106
1107 assert_eq!(get_prop_value(&compo.width), 250);
1108
1109 crate::animations::CURRENT_ANIMATION_DRIVER
1110 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
1111
1112 assert_eq!(get_prop_value(&compo.width), 300);
1113
1114 crate::animations::CURRENT_ANIMATION_DRIVER
1115 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
1116
1117 assert_eq!(get_prop_value(&compo.width), 300);
1118 }
1119}