Skip to main content

ferroui_base/rendering/composition/animations/
property_set_snapshot.rs

1use crate::rendering::composition::expressions::{
2    ExpressionObjectKey, ExpressionVariant, IExpressionObject, IExpressionParameterCollection,
3};
4use crate::rendering::composition::server::{IAnimatedServerObject, ServerCompositor, ServerObjectId};
5use std::cell::OnceCell;
6use std::collections::HashMap;
7use std::rc::{Rc, Weak};
8
9/// A snapshot of properties used by an animation
10///
11/// The snapshot is taken on the UI thread, where a reference parameter is
12/// known by the id of its server object; the ids are resolved on the server
13/// by [`resolve`](Self::resolve), before the animation instance that holds
14/// the snapshot is initialized.
15pub struct PropertySetSnapshot {
16    dic: HashMap<String, PropertySetSnapshotValue>,
17}
18
19/// The object a value of a snapshot refers to.
20pub enum PropertySetSnapshotObject {
21    /// A nested property set.
22    PropertySet(Rc<PropertySetSnapshot>),
23    /// A composition object, by the id of its server object; the server
24    /// object once resolved.
25    Server(ServerObjectId, OnceCell<SnapshotServerObject>),
26}
27
28/// The server object a reference parameter refers to, held weakly.
29///
30/// Upstream the snapshot holds the server object, and the garbage collector
31/// reclaims two animations that refer to each other's targets. Here the
32/// animations of an object hold its instances, and an instance holds its
33/// parameters, so a strong reference would form a cycle between two such
34/// objects; the snapshot holds the object weakly, as the instance holds its
35/// target. The object outlives its disposal while its composition object is
36/// alive, so the reference goes dead only once nothing refers to the object
37/// any more; a dead reference is read as a parameter without an object.
38pub struct SnapshotServerObject(Weak<dyn IAnimatedServerObject>);
39
40impl SnapshotServerObject {
41    /// The server object, while it is alive.
42    pub fn upgrade(&self) -> Option<Rc<dyn IAnimatedServerObject>> {
43        self.0.upgrade()
44    }
45
46    fn is_alive(&self) -> bool {
47        self.0.strong_count() > 0
48    }
49}
50
51impl IExpressionObject for SnapshotServerObject {
52    fn get_property(&self, name: &str) -> ExpressionVariant {
53        match self.0.upgrade() {
54            Some(object) => object.server_object().get_property(name),
55            None => ExpressionVariant::default(),
56        }
57    }
58
59    fn key(&self) -> ExpressionObjectKey {
60        ExpressionObjectKey(Weak::as_ptr(&self.0) as *const () as usize)
61    }
62}
63
64/// One value of a [`PropertySetSnapshot`]: a variant or an object.
65#[derive(Default)]
66pub struct PropertySetSnapshotValue {
67    pub variant: ExpressionVariant,
68    pub object: Option<PropertySetSnapshotObject>,
69}
70
71impl PropertySetSnapshotValue {
72    /// A value that refers to an object.
73    pub fn from_object(o: PropertySetSnapshotObject) -> Self {
74        Self { object: Some(o), variant: ExpressionVariant::default() }
75    }
76}
77
78impl From<ExpressionVariant> for PropertySetSnapshotValue {
79    fn from(v: ExpressionVariant) -> Self {
80        Self { variant: v, object: None }
81    }
82}
83
84impl PropertySetSnapshot {
85    pub fn new(dic: HashMap<String, PropertySetSnapshotValue>) -> Self {
86        Self { dic }
87    }
88
89    pub fn get_parameter(&self, name: &str) -> ExpressionVariant {
90        self.dic.get(name).map(|v| v.variant).unwrap_or_default()
91    }
92
93    pub fn get_object_parameter(&self, name: &str) -> Option<&dyn IExpressionObject> {
94        match self.dic.get(name)?.object.as_ref()? {
95            PropertySetSnapshotObject::PropertySet(snapshot) => Some(&**snapshot as &dyn IExpressionObject),
96            PropertySetSnapshotObject::Server(_, resolved) => {
97                resolved.get().filter(|o| o.is_alive()).map(|o| o as &dyn IExpressionObject)
98            }
99        }
100    }
101
102    /// The parameter as a server object, when it refers to one (the
103    /// `is ServerObject` test of upstream on `GetObjectParameter`).
104    pub fn get_server_object_parameter(&self, name: &str) -> Option<Rc<dyn IAnimatedServerObject>> {
105        match self.dic.get(name)?.object.as_ref()? {
106            PropertySetSnapshotObject::PropertySet(_) => None,
107            PropertySetSnapshotObject::Server(_, resolved) => resolved.get().and_then(SnapshotServerObject::upgrade),
108        }
109    }
110
111    pub fn get_property(&self, name: &str) -> ExpressionVariant {
112        self.get_parameter(name)
113    }
114
115    /// Resolves the server objects the snapshot refers to on `compositor`,
116    /// nested property sets included.
117    ///
118    /// Upstream the snapshot holds the server objects themselves. An id
119    /// names its server object until the composition object that holds it is
120    /// dropped, disposed or not, and the snapshot is resolved by the batch
121    /// that carries it, which precedes the release of any object it refers
122    /// to; an id that names no animatable object leaves the parameter
123    /// without an object, as a null reference does upstream.
124    pub fn resolve(&self, compositor: &ServerCompositor) {
125        for value in self.dic.values() {
126            match &value.object {
127                Some(PropertySetSnapshotObject::PropertySet(snapshot)) => snapshot.resolve(compositor),
128                Some(PropertySetSnapshotObject::Server(id, resolved)) => {
129                    if resolved.get().is_some() {
130                        continue;
131                    }
132                    if let Some(object) = compositor.get_animated_object(*id) {
133                        let _ = resolved.set(SnapshotServerObject(Rc::downgrade(&object)));
134                    }
135                }
136                None => {}
137            }
138        }
139    }
140}
141
142impl IExpressionParameterCollection for PropertySetSnapshot {
143    fn get_parameter(&self, name: &str) -> ExpressionVariant {
144        PropertySetSnapshot::get_parameter(self, name)
145    }
146
147    fn get_object_parameter(&self, name: &str) -> Option<&dyn IExpressionObject> {
148        PropertySetSnapshot::get_object_parameter(self, name)
149    }
150}
151
152impl IExpressionObject for PropertySetSnapshot {
153    fn get_property(&self, name: &str) -> ExpressionVariant {
154        PropertySetSnapshot::get_property(self, name)
155    }
156}
157
158/// A snapshot of a property set as it is sent to the server: values and the
159/// ids of server objects. The snapshot itself is built from it on the
160/// server, where it resolves the ids and keeps handles to the objects.
161#[derive(Default)]
162pub struct PropertySetSnapshotSource {
163    dic: HashMap<String, PropertySetSnapshotSourceValue>,
164}
165
166/// A value of a [`PropertySetSnapshotSource`].
167pub enum PropertySetSnapshotSourceValue {
168    Variant(ExpressionVariant),
169    /// A nested property set.
170    PropertySet(PropertySetSnapshotSource),
171    /// A composition object, by the id of its server object.
172    Server(ServerObjectId),
173}
174
175impl PropertySetSnapshotSource {
176    pub fn new(dic: HashMap<String, PropertySetSnapshotSourceValue>) -> Self {
177        Self { dic }
178    }
179
180    /// Builds the snapshot.
181    pub fn build(self) -> PropertySetSnapshot {
182        let dic = self
183            .dic
184            .into_iter()
185            .map(|(key, value)| {
186                let value = match value {
187                    PropertySetSnapshotSourceValue::Variant(variant) => variant.into(),
188                    PropertySetSnapshotSourceValue::PropertySet(source) => PropertySetSnapshotValue::from_object(
189                        PropertySetSnapshotObject::PropertySet(Rc::new(source.build())),
190                    ),
191                    PropertySetSnapshotSourceValue::Server(server) => PropertySetSnapshotValue::from_object(
192                        PropertySetSnapshotObject::Server(server, OnceCell::new()),
193                    ),
194                };
195                (key, value)
196            })
197            .collect();
198        PropertySetSnapshot::new(dic)
199    }
200}