zng_app/widget/
easing.rs
1use std::{any::Any, sync::Arc, time::Duration};
2
3use super::builder::*;
4use zng_layout::unit::*;
5use zng_var::{
6 BoxedVar, Var, VarValue,
7 animation::{
8 Transitionable,
9 easing::{EasingStep, EasingTime},
10 },
11 types::{ArcWhenVar, ContextualizedVar},
12};
13
14pub use zng_app_proc_macros::easing;
15
16type EasingFn = Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>;
17
18#[doc(hidden)]
19#[expect(non_camel_case_types)]
20pub trait easing_property: Send + Sync + Clone + Copy {
21 fn easing_property_unset(self);
22 fn easing_property(self, duration: Duration, easing: EasingFn) -> Vec<Box<dyn AnyPropertyBuildAction>>;
23 fn easing_when_data(self, duration: Duration, easing: EasingFn) -> WhenBuildAction;
24}
25
26#[doc(hidden)]
27#[expect(non_camel_case_types)]
28#[diagnostic::on_unimplemented(note = "property type must be `Transitionable` to support `#[easing]`")]
29pub trait easing_property_input_Transitionable: Any + Send {
30 fn easing(self, duration: Duration, easing: EasingFn, when_conditions_data: &[Option<Arc<dyn Any + Send + Sync>>]) -> Self;
31}
32impl<T: VarValue + Transitionable> easing_property_input_Transitionable for BoxedVar<T> {
33 fn easing(self, duration: Duration, easing: EasingFn, when_conditions_data: &[Option<Arc<dyn Any + Send + Sync>>]) -> Self {
34 if let Some(when) = (*self).as_unboxed_any().downcast_ref::<ContextualizedVar<T>>() {
35 let conditions: Vec<_> = when_conditions_data
36 .iter()
37 .map(|d| d.as_ref().and_then(|d| d.downcast_ref::<(Duration, EasingFn)>().cloned()))
38 .collect();
39
40 if conditions.iter().any(|c| c.is_some()) {
41 let when = when.clone();
42 return ContextualizedVar::new(move || {
43 when.borrow_init()
44 .as_any()
45 .downcast_ref::<ArcWhenVar<T>>()
46 .expect("expected `ArcWhenVar`")
47 .easing_when(conditions.clone(), (duration, easing.clone()))
48 })
49 .boxed();
50 }
51 } else if let Some(when) = (*self).as_unboxed_any().downcast_ref::<ArcWhenVar<T>>() {
52 let conditions: Vec<_> = when_conditions_data
53 .iter()
54 .map(|d| d.as_ref().and_then(|d| d.downcast_ref::<(Duration, EasingFn)>().cloned()))
55 .collect();
56
57 if conditions.iter().any(|c| c.is_some()) {
58 return when.easing_when(conditions.clone(), (duration, easing.clone())).boxed();
59 }
60 }
61 Var::easing(&self, duration, move |t| easing(t)).boxed()
62 }
63}
64
65macro_rules! impl_easing_property_inputs {
66 ($T0:ident, $($T:ident,)*) => {
67 impl_easing_property_inputs! {
68 $($T,)*
69 }
70
71 impl<
72 $T0: easing_property_input_Transitionable,
73 $($T: easing_property_input_Transitionable),*
74 > easing_property for PropertyInputTypes<($T0, $($T,)*)> {
75 fn easing_property_unset(self) { }
76 fn easing_property(self, duration: Duration, easing: EasingFn) -> Vec<Box<dyn AnyPropertyBuildAction>> {
77 if duration == Duration::ZERO {
78 vec![]
79 } else {
80 vec![
81 Box::new(PropertyBuildAction::<$T0>::new($crate::handler::clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, duration, easing.clone(), &a.when_conditions_data)))),
82 $(Box::new(PropertyBuildAction::<$T>::new($crate::handler::clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, duration, easing.clone(), &a.when_conditions_data)))),)*
83 ]
84 }
85 }
86 fn easing_when_data(self, duration: Duration, easing: EasingFn) -> WhenBuildAction {
87 if duration == Duration::ZERO {
88 WhenBuildAction::new_no_default((duration, easing))
89 } else {
90 WhenBuildAction::new(
91 (duration, easing),
92 || {
93 let easing = Arc::new($crate::var::animation::easing::linear) as EasingFn;
94 vec![
95 Box::new(PropertyBuildAction::<$T0>::new($crate::handler::clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, 0.ms(), easing.clone(), &a.when_conditions_data)))),
96 $(Box::new(PropertyBuildAction::<$T>::new($crate::handler::clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, 0.ms(), easing.clone(), &a.when_conditions_data)))),)*
97 ]
98 }
99 )
100 }
101 }
102 }
103 };
104 () => { };
105}
106impl_easing_property_inputs! {
107 I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15,
108}