Skip to main content

ferroui_base/rendering/composition/animations/
expression_animation.rs

1use super::{
2    CompositionAnimation, ExpressionAnimationInstance, ICompositionAnimation,
3    ICompositionAnimationBase,
4};
5use crate::rendering::composition::expressions::{Expression, ExpressionParser, ExpressionVariant};
6use crate::rendering::composition::server::ServerObjectId;
7use crate::rendering::composition::{AsCompositionObject, CompositionObject, Compositor};
8use std::cell::RefCell;
9use std::ops::Deref;
10use std::rc::Rc;
11
12/// A Composition Animation that uses a mathematical equation to calculate the value for an animating property every frame.
13///
14/// The core of ExpressionAnimations allows a developer to define a mathematical equation that can be used to calculate the value
15/// of a targeted animating property each frame.
16/// This contrasts [`KeyFrameAnimation`](super::KeyFrameAnimation)s, which use an interpolator to define how the animating
17/// property changes over time. The mathematical equation can be defined using references to properties
18/// of Composition objects, mathematical functions and operators and Input.
19/// Use the `start_animation` method of a composition object to start the animation.
20pub struct ExpressionAnimation {
21    base: CompositionAnimation,
22    expression: RefCell<Option<String>>,
23    parsed_expression: RefCell<Option<std::sync::Arc<Expression>>>,
24}
25
26impl Deref for ExpressionAnimation {
27    type Target = CompositionAnimation;
28
29    fn deref(&self) -> &CompositionAnimation {
30        &self.base
31    }
32}
33
34impl ExpressionAnimation {
35    pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<ExpressionAnimation> {
36        Rc::new(ExpressionAnimation {
37            base: CompositionAnimation::new(compositor),
38            expression: RefCell::new(None),
39            parsed_expression: RefCell::new(None),
40        })
41    }
42
43    /// The mathematical equation specifying how the animated value is calculated each frame.
44    /// The Expression is the core of an [`ExpressionAnimation`] and represents the equation
45    /// the system will use to calculate the value of the animation property each frame.
46    /// The equation is set on this property in the form of a string.
47    /// Although expressions can be defined by simple mathematical equations such as "2+2",
48    /// the real power lies in creating mathematical relationships where the input values can change frame over frame.
49    pub fn expression(&self) -> Option<String> {
50        self.expression.borrow().clone()
51    }
52
53    pub fn set_expression(&self, value: Option<String>) {
54        *self.expression.borrow_mut() = value;
55        *self.parsed_expression.borrow_mut() = None;
56    }
57
58    /// The parsed expression. Panics with the parse error when the
59    /// expression is not valid (upstream throws the parse exception).
60    fn parsed_expression(&self) -> std::sync::Arc<Expression> {
61        if let Some(parsed) = self.parsed_expression.borrow().clone() {
62            return parsed;
63        }
64        let source = self.expression.borrow().clone().unwrap_or_default();
65        let parsed = match ExpressionParser::parse(&source) {
66            Ok(parsed) => std::sync::Arc::new(parsed),
67            Err(error) => panic!("{error}"),
68        };
69        *self.parsed_expression.borrow_mut() = Some(parsed.clone());
70        parsed
71    }
72}
73
74impl ICompositionAnimationBase for ExpressionAnimation {
75    fn as_composition_animation(&self) -> Option<&dyn ICompositionAnimation> {
76        Some(self)
77    }
78}
79
80impl ICompositionAnimation for ExpressionAnimation {
81    fn target(&self) -> Option<String> {
82        self.base.target()
83    }
84
85    fn create_instance(
86        &self,
87        target_object: ServerObjectId,
88        final_value: Option<ExpressionVariant>,
89    ) -> super::AnimationInstanceFactory {
90        let expression = self.parsed_expression();
91        let parameters = self.create_snapshot_source();
92        super::AnimationInstanceFactory::new(move || {
93            ExpressionAnimationInstance::new(expression, target_object, final_value, Rc::new(parameters.build()))
94        })
95    }
96}
97
98impl AsCompositionObject for ExpressionAnimation {
99    fn as_composition_object(&self) -> &CompositionObject {
100        self.base.object()
101    }
102
103    fn composition_type_name(&self) -> &'static str {
104        "ExpressionAnimation"
105    }
106}