Skip to main content

ferroui_base/rendering/composition/
composition_property_set.rs

1use super::animations::{PropertySetSnapshot, PropertySetSnapshotSource, PropertySetSnapshotSourceValue};
2use super::expressions::{ExpressionVariant, ExpressionVariantValue};
3use super::{AsCompositionObject, CompositionObject, Compositor};
4use crate::media::Color;
5use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::rc::Rc;
9
10/// Property sets are composition objects that allow storage of key values pairs
11/// that can be shared across the application and are not tied to the lifetime of another composition object.
12/// Property sets are most commonly used with animations, where they maintain key-value pairs
13/// that are referenced to drive portions of composition animations. Property sets
14/// provide the ability to insert key-value pairs or retrieve a value for a given key.
15/// A property set does not support a delete function – ensure you use property sets
16/// to store values that will be shared across the application.
17pub struct CompositionPropertySet {
18    object: CompositionObject,
19    variants: RefCell<HashMap<String, ExpressionVariant>>,
20    objects: RefCell<HashMap<String, Rc<dyn AsCompositionObject>>>,
21}
22
23/// The result of reading a value of a [`CompositionPropertySet`].
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
25pub enum CompositionGetValueStatus {
26    Succeeded,
27    TypeMismatch,
28    NotFound,
29}
30
31impl CompositionPropertySet {
32    pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<CompositionPropertySet> {
33        Rc::new(CompositionPropertySet {
34            object: CompositionObject::new(compositor, None),
35            variants: RefCell::new(HashMap::new()),
36            objects: RefCell::new(HashMap::new()),
37        })
38    }
39
40    pub fn compositor(&self) -> &Rc<Compositor> {
41        self.object.compositor()
42    }
43
44    pub(crate) fn set(&self, key: &str, value: ExpressionVariant) {
45        self.objects.borrow_mut().remove(key);
46        self.variants.borrow_mut().insert(key.to_owned(), value);
47    }
48
49    // For INTERNAL USE by CompositionAnimation ONLY, we DON'T support expression
50    // paths like SomeParam.SomePropertyObject.SomeValue
51    pub(crate) fn set_object(&self, key: &str, obj: Rc<dyn AsCompositionObject>) {
52        self.objects.borrow_mut().insert(key.to_owned(), obj);
53        self.variants.borrow_mut().remove(key);
54    }
55
56    pub fn insert_color(&self, property_name: &str, value: Color) {
57        self.set(property_name, value.into())
58    }
59
60    pub fn insert_matrix3x2(&self, property_name: &str, value: Matrix3x2) {
61        self.set(property_name, value.into())
62    }
63
64    pub fn insert_matrix4x4(&self, property_name: &str, value: Matrix4x4) {
65        self.set(property_name, value.into())
66    }
67
68    pub fn insert_quaternion(&self, property_name: &str, value: Quaternion) {
69        self.set(property_name, value.into())
70    }
71
72    pub fn insert_scalar(&self, property_name: &str, value: f32) {
73        self.set(property_name, value.into())
74    }
75
76    pub fn insert_vector2(&self, property_name: &str, value: Vector2) {
77        self.set(property_name, value.into())
78    }
79
80    pub fn insert_vector3(&self, property_name: &str, value: Vector3) {
81        self.set(property_name, value.into())
82    }
83
84    pub fn insert_vector4(&self, property_name: &str, value: Vector4) {
85        self.set(property_name, value.into())
86    }
87
88    fn try_get_variant<T: ExpressionVariantValue>(&self, key: &str) -> (CompositionGetValueStatus, T) {
89        let Some(v) = self.variants.borrow().get(key).copied() else {
90            let status = if self.objects.borrow().contains_key(key) {
91                CompositionGetValueStatus::TypeMismatch
92            } else {
93                CompositionGetValueStatus::NotFound
94            };
95            return (status, T::default());
96        };
97
98        match v.try_cast::<T>() {
99            Some(value) => (CompositionGetValueStatus::Succeeded, value),
100            None => (CompositionGetValueStatus::TypeMismatch, T::default()),
101        }
102    }
103
104    pub fn try_get_color(&self, property_name: &str) -> (CompositionGetValueStatus, Color) {
105        self.try_get_variant(property_name)
106    }
107
108    pub fn try_get_matrix3x2(&self, property_name: &str) -> (CompositionGetValueStatus, Matrix3x2) {
109        self.try_get_variant(property_name)
110    }
111
112    pub fn try_get_matrix4x4(&self, property_name: &str) -> (CompositionGetValueStatus, Matrix4x4) {
113        self.try_get_variant(property_name)
114    }
115
116    pub fn try_get_quaternion(&self, property_name: &str) -> (CompositionGetValueStatus, Quaternion) {
117        self.try_get_variant(property_name)
118    }
119
120    pub fn try_get_scalar(&self, property_name: &str) -> (CompositionGetValueStatus, f32) {
121        self.try_get_variant(property_name)
122    }
123
124    pub fn try_get_vector2(&self, property_name: &str) -> (CompositionGetValueStatus, Vector2) {
125        self.try_get_variant(property_name)
126    }
127
128    pub fn try_get_vector3(&self, property_name: &str) -> (CompositionGetValueStatus, Vector3) {
129        self.try_get_variant(property_name)
130    }
131
132    pub fn try_get_vector4(&self, property_name: &str) -> (CompositionGetValueStatus, Vector4) {
133        self.try_get_variant(property_name)
134    }
135
136    pub fn insert_boolean(&self, property_name: &str, value: bool) {
137        self.set(property_name, value.into())
138    }
139
140    pub fn try_get_boolean(&self, property_name: &str) -> (CompositionGetValueStatus, bool) {
141        self.try_get_variant(property_name)
142    }
143
144    pub(crate) fn clear_all(&self) {
145        self.objects.borrow_mut().clear();
146        self.variants.borrow_mut().clear();
147    }
148
149    pub(crate) fn clear(&self, key: &str) {
150        self.objects.borrow_mut().remove(key);
151        self.variants.borrow_mut().remove(key);
152    }
153
154    pub(crate) fn snapshot(&self) -> PropertySetSnapshot {
155        self.snapshot_source().build()
156    }
157
158    /// The snapshot in the form that is sent to the server, which builds
159    /// the snapshot from it.
160    pub(crate) fn snapshot_source(&self) -> PropertySetSnapshotSource {
161        self.snapshot_core(1)
162    }
163
164    fn snapshot_core(&self, allowed_nesting_level: i32) -> PropertySetSnapshotSource {
165        let objects: Vec<(String, Rc<dyn AsCompositionObject>)> =
166            self.objects.borrow().iter().map(|(k, v)| (k.clone(), v.clone())).collect();
167        let variants = self.variants.borrow();
168        let mut dic = HashMap::with_capacity(objects.len() + variants.len());
169        for (key, value) in objects {
170            let type_name = value.composition_type_name();
171            let server = value.as_composition_object().server();
172            if let Some(ps) = value.as_property_set() {
173                if allowed_nesting_level <= 0 {
174                    panic!("PropertySet depth limit reached");
175                }
176                dic.insert(
177                    key,
178                    PropertySetSnapshotSourceValue::PropertySet(ps.snapshot_core(allowed_nesting_level - 1)),
179                );
180            } else if let Some(server) = server {
181                dic.insert(key, PropertySetSnapshotSourceValue::Server(server));
182            } else {
183                panic!("Object of type {type_name} is not allowed");
184            }
185        }
186
187        for (key, value) in variants.iter() {
188            dic.insert(key.clone(), PropertySetSnapshotSourceValue::Variant(*value));
189        }
190
191        PropertySetSnapshotSource::new(dic)
192    }
193}
194
195impl AsCompositionObject for CompositionPropertySet {
196    fn as_composition_object(&self) -> &CompositionObject {
197        &self.object
198    }
199
200    fn composition_type_name(&self) -> &'static str {
201        "CompositionPropertySet"
202    }
203
204    fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
205        Some(self)
206    }
207}