Skip to main content

ferroui_base/rendering/composition/animations/
composition_animation.rs

1use super::{PropertySetSnapshot, PropertySetSnapshotSource};
2use crate::media::Color;
3use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
4use crate::rendering::composition::expressions::{
5    Expression, ExpressionEvaluationContext, ExpressionParseException, ExpressionParser, ExpressionVariant,
6    IExpressionForeignFunctionInterface, IExpressionObject, IExpressionParameterCollection,
7};
8use crate::rendering::composition::{AsCompositionObject, CompositionObject, CompositionPropertySet, Compositor};
9use std::cell::RefCell;
10use std::collections::BTreeMap;
11use std::rc::Rc;
12
13/// This is the base class for ExpressionAnimation and KeyFrameAnimation.
14///
15/// Use the `start_animation` method of a composition object to start the
16/// animation. Value parameters (as opposed to reference parameters which
17/// are set using [`set_reference_parameter`](Self::set_reference_parameter))
18/// are copied and "embedded" into an expression at the time
19/// `start_animation` is called. Changing the value of the variable after
20/// `start_animation` is called will not affect the value of the
21/// ExpressionAnimation. See the remarks of
22/// [`ExpressionAnimation`](super::ExpressionAnimation) for additional
23/// information.
24///
25/// A class derives from it by embedding it (`base`) and dereferencing to
26/// it; the class implements [`ICompositionAnimation`](super::ICompositionAnimation),
27/// whose `Rc<dyn ..>` is the handle of an animation of any class.
28pub struct CompositionAnimation {
29    object: CompositionObject,
30    property_set: Rc<CompositionPropertySet>,
31    target: RefCell<Option<String>>,
32    initial_value_expressions: InitialValueExpressionCollection,
33}
34
35impl CompositionAnimation {
36    pub(crate) fn new(compositor: &Rc<Compositor>) -> Self {
37        Self {
38            object: CompositionObject::new(compositor, None),
39            property_set: CompositionPropertySet::new_parameters(compositor),
40            target: RefCell::new(None),
41            initial_value_expressions: InitialValueExpressionCollection::default(),
42        }
43    }
44
45    /// The embedded `CompositionObject`.
46    pub fn object(&self) -> &CompositionObject {
47        &self.object
48    }
49
50    /// The associated compositor.
51    pub fn compositor(&self) -> &Rc<Compositor> {
52        self.object.compositor()
53    }
54
55    /// Clears all of the parameters of the animation.
56    pub fn clear_all_parameters(&self) {
57        self.property_set.clear_all()
58    }
59
60    /// Clears a parameter from the animation.
61    pub fn clear_parameter(&self, key: &str) {
62        self.property_set.clear(key)
63    }
64
65    fn set_variant(&self, key: &str, value: ExpressionVariant) {
66        self.property_set.set(key, value)
67    }
68
69    /// Sets a boolean value parameter for use with an expression animation
70    /// or an expression key frame.
71    pub fn set_boolean_parameter(&self, key: &str, value: bool) {
72        self.set_variant(key, value.into())
73    }
74
75    pub fn set_color_parameter(&self, key: &str, value: Color) {
76        self.set_variant(key, value.into())
77    }
78
79    /// Sets an object as a reference parameter of an expression: the
80    /// expression reads its properties (`key.Offset`) every time it is
81    /// evaluated, as with [`set_reference_parameter`](Self::set_reference_parameter).
82    //
83    // Deviation (winui-composition.md): WinUI takes an `IAnimationObject`,
84    // which lets an object that is not a composition object (a XAML element)
85    // stand for one through `PopulatePropertyInfo`. Here the parameter is a
86    // composition object, the same as `set_reference_parameter`; an element
87    // facade passes the composition object behind it (its visual).
88    pub fn set_expression_reference_parameter(&self, key: &str, animation_object: Rc<dyn AsCompositionObject>) {
89        self.set_reference_parameter(key, animation_object)
90    }
91
92    pub fn set_matrix3x2_parameter(&self, key: &str, value: Matrix3x2) {
93        self.set_variant(key, value.into())
94    }
95
96    pub fn set_matrix4x4_parameter(&self, key: &str, value: Matrix4x4) {
97        self.set_variant(key, value.into())
98    }
99
100    pub fn set_quaternion_parameter(&self, key: &str, value: Quaternion) {
101        self.set_variant(key, value.into())
102    }
103
104    pub fn set_reference_parameter(&self, key: &str, composition_object: Rc<dyn AsCompositionObject>) {
105        self.property_set.set_object(key, composition_object)
106    }
107
108    pub fn set_scalar_parameter(&self, key: &str, value: f32) {
109        self.set_variant(key, value.into())
110    }
111
112    pub fn set_vector2_parameter(&self, key: &str, value: Vector2) {
113        self.set_variant(key, value.into())
114    }
115
116    pub fn set_vector3_parameter(&self, key: &str, value: Vector3) {
117        self.set_variant(key, value.into())
118    }
119
120    pub fn set_vector4_parameter(&self, key: &str, value: Vector4) {
121        self.set_variant(key, value.into())
122    }
123
124    /// The name of the property the animation targets.
125    pub fn target(&self) -> Option<String> {
126        self.target.borrow().clone()
127    }
128
129    pub fn set_target(&self, value: Option<String>) {
130        *self.target.borrow_mut() = value;
131    }
132
133    /// The initial values of the animation: for a property of the target
134    /// object, the expression that gives the property its value when the
135    /// animation starts.
136    pub fn initial_value_expressions(&self) -> &InitialValueExpressionCollection {
137        &self.initial_value_expressions
138    }
139
140    pub(crate) fn create_snapshot(&self) -> Rc<PropertySetSnapshot> {
141        Rc::new(self.property_set.snapshot())
142    }
143
144    /// The parameters of the animation in the form that is sent to the
145    /// server.
146    pub(crate) fn create_snapshot_source(&self) -> PropertySetSnapshotSource {
147        self.property_set.snapshot_source()
148    }
149}
150
151/// The initial values of a [`CompositionAnimation`]
152/// (`CompositionAnimation.InitialValueExpressions`): a map from the name of a
153/// property of the target object (`"Offset"`, `"Opacity"`) to an expression
154/// string. When the animation starts, each expression is evaluated against the
155/// target object and the parameters of the animation, and the property is set
156/// to the result before the animation runs; `this.StartingValue` and
157/// `this.CurrentValue` in such an expression are the value the property has
158/// at that moment.
159//
160// Deviation (winui-composition.md): in WinUI the collection is a
161// `CompositionObject` (an `IMap<String, String>`); here it is a plain
162// collection owned by the animation, with the members of the map.
163#[derive(Default)]
164pub struct InitialValueExpressionCollection {
165    expressions: RefCell<BTreeMap<String, String>>,
166}
167
168impl InitialValueExpressionCollection {
169    /// Sets the initial value expression of a property. Returns `true` when it
170    /// replaced an expression the property already had.
171    pub fn insert(&self, property_name: &str, expression: &str) -> bool {
172        self.expressions
173            .borrow_mut()
174            .insert(property_name.to_owned(), expression.to_owned())
175            .is_some()
176    }
177
178    /// The initial value expression of a property, if it has one.
179    pub fn lookup(&self, property_name: &str) -> Option<String> {
180        self.expressions.borrow().get(property_name).cloned()
181    }
182
183    /// Whether the property has an initial value expression.
184    pub fn has_key(&self, property_name: &str) -> bool {
185        self.expressions.borrow().contains_key(property_name)
186    }
187
188    /// Removes the initial value expression of a property. Returns `true` when
189    /// the property had one.
190    pub fn remove(&self, property_name: &str) -> bool {
191        self.expressions.borrow_mut().remove(property_name).is_some()
192    }
193
194    /// Removes every initial value expression.
195    pub fn clear(&self) {
196        self.expressions.borrow_mut().clear()
197    }
198
199    /// The number of initial value expressions.
200    pub fn size(&self) -> usize {
201        self.expressions.borrow().len()
202    }
203
204    /// The `(property name, expression)` pairs, ordered by property name.
205    pub fn get_view(&self) -> Vec<(String, String)> {
206        self.expressions
207            .borrow()
208            .iter()
209            .map(|(k, v)| (k.clone(), v.clone()))
210            .collect()
211    }
212
213    /// The expressions parsed, ordered by property name, in the form the
214    /// starting animation takes them to the server (`Send`). Fails with the
215    /// error of the first expression that cannot be parsed.
216    pub fn parse_all(&self) -> Result<Vec<(String, Expression)>, ExpressionParseException> {
217        self.expressions
218            .borrow()
219            .iter()
220            .map(|(name, source)| ExpressionParser::parse(source).map(|parsed| (name.clone(), parsed)))
221            .collect()
222    }
223
224    /// Evaluates parsed initial value expressions for a target object: for each
225    /// `(property name, expression)` pair (from [`parse_all`](Self::parse_all)),
226    /// the value the property starts with. `this.StartingValue`,
227    /// `this.CurrentValue` and `this.FinalValue` are the value the target has
228    /// for that property, `this.Target` is the target and the parameters are
229    /// those of the animation. A result that is `Invalid` (an expression that
230    /// does not evaluate) is left out, so the property keeps its value.
231    ///
232    /// This is the step the start of an animation runs on the server before
233    /// the animation instance is initialized; writing the values to the
234    /// properties is left to the caller, which knows the property ids.
235    pub fn evaluate_parsed(
236        expressions: &[(String, Expression)],
237        target: &dyn IExpressionObject,
238        parameters: Option<&dyn IExpressionParameterCollection>,
239        foreign_function_interface: &dyn IExpressionForeignFunctionInterface,
240    ) -> Vec<(String, ExpressionVariant)> {
241        let mut values = Vec::with_capacity(expressions.len());
242        for (property_name, expression) in expressions {
243            let current = target.get_property(property_name);
244            let context = ExpressionEvaluationContext {
245                starting_value: current,
246                current_value: current,
247                final_value: current,
248                target: Some(target),
249                parameters,
250                foreign_function_interface: Some(foreign_function_interface),
251            };
252            let value = expression.evaluate(&context);
253            if value != ExpressionVariant::Invalid {
254                values.push((property_name.clone(), value));
255            }
256        }
257        values
258    }
259}