ferroui_base/rendering/composition/
composition_property_set.rs1use super::animations::{ICompositionAnimation, PropertySetSnapshot, PropertySetSnapshotSource, PropertySetSnapshotSourceValue};
2use super::expressions::{ExpressionVariant, ExpressionVariantValue};
3use super::server::{CompositionProperty, PropertySetChanges, ServerCompositionPropertySet, ServerObjectId};
4use super::transport::BatchStreamWriter;
5use super::{AsCompositionObject, CompositionObject, Compositor, ICompositionObjectAnimations, ICompositorSerializable};
6use crate::media::Color;
7use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::rc::{Rc, Weak};
11
12pub struct CompositionPropertySet {
27 this: Weak<CompositionPropertySet>,
28 object: CompositionObject,
29 variants: RefCell<HashMap<String, ExpressionVariant>>,
30 objects: RefCell<HashMap<String, Rc<dyn AsCompositionObject>>>,
31 pending: RefCell<PropertySetChanges>,
33 pending_animation_names: RefCell<Vec<String>>,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
40pub enum CompositionGetValueStatus {
41 Succeeded,
42 TypeMismatch,
43 NotFound,
44}
45
46impl CompositionPropertySet {
47 pub fn new(compositor: &Rc<Compositor>) -> Rc<CompositionPropertySet> {
49 let server = compositor.create_server_object(|compositor, _| ServerCompositionPropertySet::new(compositor));
50 Self::create(compositor, Some(server))
51 }
52
53 pub(crate) fn new_parameters(compositor: &Rc<Compositor>) -> Rc<CompositionPropertySet> {
56 Self::create(compositor, None)
57 }
58
59 fn create(compositor: &Rc<Compositor>, server: Option<ServerObjectId>) -> Rc<CompositionPropertySet> {
60 Rc::new_cyclic(|this| CompositionPropertySet {
61 this: this.clone(),
62 object: CompositionObject::new(compositor, server),
63 variants: RefCell::new(HashMap::new()),
64 objects: RefCell::new(HashMap::new()),
65 pending: RefCell::new(PropertySetChanges::default()),
66 pending_animation_names: RefCell::new(Vec::new()),
67 })
68 }
69
70 pub fn is_live(&self) -> bool {
72 self.object.server().is_some()
73 }
74
75 fn register_for_serialization(&self) {
76 let this = self.this.clone();
77 self.object.register_for_serialization(move || {
78 this.upgrade().map(|this| this as Rc<dyn ICompositorSerializable>)
79 });
80 }
81
82 pub fn compositor(&self) -> &Rc<Compositor> {
83 self.object.compositor()
84 }
85
86 pub(crate) fn set(&self, key: &str, value: ExpressionVariant) {
87 self.objects.borrow_mut().remove(key);
88 self.variants.borrow_mut().insert(key.to_owned(), value);
89 if self.is_live() && !self.object.is_disposed() {
90 {
91 let mut pending = self.pending.borrow_mut();
92 self.object.pending_animations().remove(CompositionProperty::dynamic(key));
95 pending.values.retain(|(name, _)| name != key);
96 pending.values.push((key.to_owned(), value));
97 }
98 self.register_for_serialization();
99 }
100 }
101
102 pub(crate) fn set_object(&self, key: &str, obj: Rc<dyn AsCompositionObject>) {
105 self.objects.borrow_mut().insert(key.to_owned(), obj);
106 self.variants.borrow_mut().remove(key);
107 }
108
109 pub fn insert_color(&self, property_name: &str, value: Color) {
110 self.set(property_name, value.into())
111 }
112
113 pub fn insert_matrix3x2(&self, property_name: &str, value: Matrix3x2) {
114 self.set(property_name, value.into())
115 }
116
117 pub fn insert_matrix4x4(&self, property_name: &str, value: Matrix4x4) {
118 self.set(property_name, value.into())
119 }
120
121 pub fn insert_quaternion(&self, property_name: &str, value: Quaternion) {
122 self.set(property_name, value.into())
123 }
124
125 pub fn insert_scalar(&self, property_name: &str, value: f32) {
126 self.set(property_name, value.into())
127 }
128
129 pub fn insert_vector2(&self, property_name: &str, value: Vector2) {
130 self.set(property_name, value.into())
131 }
132
133 pub fn insert_vector3(&self, property_name: &str, value: Vector3) {
134 self.set(property_name, value.into())
135 }
136
137 pub fn insert_vector4(&self, property_name: &str, value: Vector4) {
138 self.set(property_name, value.into())
139 }
140
141 fn try_get_variant<T: ExpressionVariantValue>(&self, key: &str) -> (CompositionGetValueStatus, T) {
142 let Some(v) = self.variants.borrow().get(key).copied() else {
143 let status = if self.objects.borrow().contains_key(key) {
144 CompositionGetValueStatus::TypeMismatch
145 } else {
146 CompositionGetValueStatus::NotFound
147 };
148 return (status, T::default());
149 };
150
151 match v.try_cast::<T>() {
152 Some(value) => (CompositionGetValueStatus::Succeeded, value),
153 None => (CompositionGetValueStatus::TypeMismatch, T::default()),
154 }
155 }
156
157 pub fn try_get_color(&self, property_name: &str) -> (CompositionGetValueStatus, Color) {
158 self.try_get_variant(property_name)
159 }
160
161 pub fn try_get_matrix3x2(&self, property_name: &str) -> (CompositionGetValueStatus, Matrix3x2) {
162 self.try_get_variant(property_name)
163 }
164
165 pub fn try_get_matrix4x4(&self, property_name: &str) -> (CompositionGetValueStatus, Matrix4x4) {
166 self.try_get_variant(property_name)
167 }
168
169 pub fn try_get_quaternion(&self, property_name: &str) -> (CompositionGetValueStatus, Quaternion) {
170 self.try_get_variant(property_name)
171 }
172
173 pub fn try_get_scalar(&self, property_name: &str) -> (CompositionGetValueStatus, f32) {
174 self.try_get_variant(property_name)
175 }
176
177 pub fn try_get_vector2(&self, property_name: &str) -> (CompositionGetValueStatus, Vector2) {
178 self.try_get_variant(property_name)
179 }
180
181 pub fn try_get_vector3(&self, property_name: &str) -> (CompositionGetValueStatus, Vector3) {
182 self.try_get_variant(property_name)
183 }
184
185 pub fn try_get_vector4(&self, property_name: &str) -> (CompositionGetValueStatus, Vector4) {
186 self.try_get_variant(property_name)
187 }
188
189 pub fn insert_boolean(&self, property_name: &str, value: bool) {
190 self.set(property_name, value.into())
191 }
192
193 pub fn try_get_boolean(&self, property_name: &str) -> (CompositionGetValueStatus, bool) {
194 self.try_get_variant(property_name)
195 }
196
197 pub(crate) fn clear_all(&self) {
198 self.objects.borrow_mut().clear();
199 self.variants.borrow_mut().clear();
200 }
201
202 pub(crate) fn clear(&self, key: &str) {
203 self.objects.borrow_mut().remove(key);
204 self.variants.borrow_mut().remove(key);
205 }
206
207 pub(crate) fn snapshot(&self) -> PropertySetSnapshot {
210 self.snapshot_core(1, true).build()
211 }
212
213 pub(crate) fn snapshot_source(&self) -> PropertySetSnapshotSource {
216 self.snapshot_core(1, false)
217 }
218
219 fn snapshot_core(&self, allowed_nesting_level: i32, copy_live_sets: bool) -> PropertySetSnapshotSource {
220 let objects: Vec<(String, Rc<dyn AsCompositionObject>)> =
221 self.objects.borrow().iter().map(|(k, v)| (k.clone(), v.clone())).collect();
222 let variants = self.variants.borrow();
223 let mut dic = HashMap::with_capacity(objects.len() + variants.len());
224 for (key, value) in objects {
225 let type_name = value.composition_type_name();
226 let server = value.as_composition_object().server();
227 if let Some(ps) = value.clone().as_property_set().filter(|ps| copy_live_sets || !ps.is_live()) {
228 if allowed_nesting_level <= 0 {
229 panic!("PropertySet depth limit reached");
230 }
231 dic.insert(
232 key,
233 PropertySetSnapshotSourceValue::PropertySet(ps.snapshot_core(allowed_nesting_level - 1, copy_live_sets)),
234 );
235 } else if let Some(server) = server {
236 dic.insert(key, PropertySetSnapshotSourceValue::Server(server));
237 } else {
238 panic!("Object of type {type_name} is not allowed");
239 }
240 }
241
242 for (key, value) in variants.iter() {
243 dic.insert(key.clone(), PropertySetSnapshotSourceValue::Variant(*value));
244 }
245
246 PropertySetSnapshotSource::new(dic)
247 }
248}
249
250impl AsCompositionObject for CompositionPropertySet {
251 fn as_composition_object(&self) -> &CompositionObject {
252 &self.object
253 }
254
255 fn composition_type_name(&self) -> &'static str {
256 "CompositionPropertySet"
257 }
258
259 fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
260 Some(self)
261 }
262}
263
264impl ICompositorSerializable for CompositionPropertySet {
265 fn try_get_server(&self, c: &Compositor) -> Option<ServerObjectId> {
266 self.object.try_get_server(c)
267 }
268
269 fn serialize_changes(&self, c: &Compositor, writer: &mut BatchStreamWriter<'_>) {
270 self.object.begin_serialize_changes(c);
271 let mut changes = std::mem::take(&mut *self.pending.borrow_mut());
272 for name in std::mem::take(&mut *self.pending_animation_names.borrow_mut()) {
273 if let Some(factory) = self.object.pending_animations().get_and_remove(CompositionProperty::dynamic(&name)) {
274 changes.animations.push((name, factory));
275 }
276 }
277 writer.write_value(Some(changes));
278 }
279}
280
281impl ICompositionObjectAnimations for CompositionPropertySet {
282 fn try_start_animation(
285 &self,
286 property_name: &str,
287 animation: &dyn ICompositionAnimation,
288 final_value: Option<ExpressionVariant>,
289 ) -> bool {
290 let Some(server) = self.object.server() else { return false };
291 if !self.variants.borrow().contains_key(property_name) {
292 return false;
293 }
294 let factory = animation.create_instance(server, final_value);
295 self.object.pending_animations().set(CompositionProperty::dynamic(property_name), factory);
296 {
297 let mut names = self.pending_animation_names.borrow_mut();
298 if !names.iter().any(|name| name == property_name) {
299 names.push(property_name.to_owned());
300 }
301 }
302 self.register_for_serialization();
303 true
304 }
305
306 fn get_composition_property(&self, property_name: &str) -> Option<&'static CompositionProperty> {
307 self.variants.borrow().contains_key(property_name).then(|| CompositionProperty::dynamic(property_name))
308 }
309
310 fn composition_object(&self) -> &CompositionObject {
311 &self.object
312 }
313}