Skip to main content

ferroui_base/rendering/composition/generated/
composition_shape.rs

1// @generated by scripts/generate_composition.py — do not edit
2
3//! `CompositionShape`.
4//!
5//! Defined by the FerroUI overlay (scripts/composition-schema-winui.xml).
6//!
7//! Schema attributes: abstract.
8
9use bitflags::bitflags;
10use crate::Matrix;
11use crate::Vector;
12use crate::rendering::composition::ICompositionObjectHost;
13use crate::rendering::composition::animations::AnimationInstanceFactory;
14use crate::rendering::composition::animations::ICompositionAnimation;
15use crate::rendering::composition::expressions::ExpressionVariant;
16use crate::rendering::composition::server::CompositionProperty;
17use crate::rendering::composition::server::CompositionPropertyOf;
18use crate::rendering::composition::server::IServerAnimatedPropertyHost;
19use crate::rendering::composition::server::ServerPropertyValue;
20use crate::rendering::composition::server::props_of;
21use crate::rendering::composition::transport::BatchStreamReader;
22use crate::rendering::composition::transport::BatchStreamWriter;
23use crate::rendering::composition::transport::BatchValue;
24use crate::rendering::composition::transport::BatchValueReader;
25use std::any::Any;
26use std::any::TypeId;
27use std::cell::Cell;
28use std::sync::OnceLock;
29use std::time::Duration;
30
31bitflags! {
32    /// The fields of `CompositionShape` that changed since its last serialization.
33    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
34    pub struct CompositionShapeChangedFields: u16 {
35        const CENTER_POINT = 1;
36        const CENTER_POINT_ANIMATED = 2;
37        const OFFSET = 4;
38        const OFFSET_ANIMATED = 8;
39        const ROTATION_ANGLE = 16;
40        const ROTATION_ANGLE_ANIMATED = 32;
41        const SCALE = 64;
42        const SCALE_ANIMATED = 128;
43        const TRANSFORM_MATRIX = 256;
44        const TRANSFORM_MATRIX_ANIMATED = 512;
45    }
46}
47
48impl BatchValue for CompositionShapeChangedFields {
49    #[inline]
50    fn write_to(&self, out: &mut Vec<u8>) {
51        self.bits().write_to(out);
52    }
53
54    #[inline]
55    fn read_from(input: &mut BatchValueReader<'_>) -> Self {
56        CompositionShapeChangedFields::from_bits_retain(u16::read_from(input))
57    }
58}
59
60/// The partial methods of `CompositionShape`: implemented by the class that embeds
61/// [`CompositionShapeProps`]. Every hook defaults to doing nothing.
62pub trait CompositionShapeHooks: ICompositionObjectHost {
63    fn initialize_defaults_extra(&self) {}
64
65    fn validate_center_point_change(&self, _old_value: Vector, _new_value: Vector) {}
66    fn on_center_point_changed(&self) {}
67    fn on_center_point_changing(&self) {}
68
69    fn validate_offset_change(&self, _old_value: Vector, _new_value: Vector) {}
70    fn on_offset_changed(&self) {}
71    fn on_offset_changing(&self) {}
72
73    fn validate_rotation_angle_change(&self, _old_value: f32, _new_value: f32) {}
74    fn on_rotation_angle_changed(&self) {}
75    fn on_rotation_angle_changing(&self) {}
76
77    fn validate_scale_change(&self, _old_value: Vector, _new_value: Vector) {}
78    fn on_scale_changed(&self) {}
79    fn on_scale_changing(&self) {}
80
81    fn validate_transform_matrix_change(&self, _old_value: Matrix, _new_value: Matrix) {}
82    fn on_transform_matrix_changed(&self) {}
83    fn on_transform_matrix_changing(&self) {}
84}
85
86/// The UI-thread properties of `CompositionShape`.
87pub struct CompositionShapeProps {
88    changed_fields_of_composition_shape: Cell<CompositionShapeChangedFields>,
89    center_point: Cell<Vector>,
90    offset: Cell<Vector>,
91    rotation_angle: Cell<f32>,
92    scale: Cell<Vector>,
93    transform_matrix: Cell<Matrix>,
94}
95
96impl Default for CompositionShapeProps {
97    fn default() -> Self {
98        Self::new()
99    }
100}
101
102impl CompositionShapeProps {
103    /// Creates the properties with every field at the default of its type.
104    /// The defaults of the schema are applied by
105    /// [`initialize_defaults`](Self::initialize_defaults).
106    pub fn new() -> Self {
107        Self {
108            changed_fields_of_composition_shape: Cell::new(CompositionShapeChangedFields::empty()),
109            center_point: Cell::new(Default::default()),
110            offset: Cell::new(Default::default()),
111            rotation_angle: Cell::new(Default::default()),
112            scale: Cell::new(Default::default()),
113            transform_matrix: Cell::new(Default::default()),
114        }
115    }
116
117    /// The `InitializeDefaults` calls of the constructor chain: the base
118    /// classes first, then this class.
119    pub fn initialize_defaults(&self, host: &dyn CompositionShapeHooks) {
120        self.initialize_own_defaults(host);
121    }
122
123    /// `InitializeDefaults` of this class alone.
124    pub fn initialize_own_defaults(&self, host: &dyn CompositionShapeHooks) {
125        CompositionShapeHooks::initialize_defaults_extra(host);
126        self.set_scale(host, Vector::new(1.0, 1.0));
127        self.set_transform_matrix(host, Matrix::IDENTITY);
128    }
129
130    /// `CenterPoint` (animated, FerroUI overlay).
131    pub fn center_point(&self) -> Vector {
132        self.center_point.get()
133    }
134
135    /// Sets `CenterPoint` (animated, FerroUI overlay).
136    pub fn set_center_point(&self, host: &dyn CompositionShapeHooks, value: Vector) {
137        let mut changed = false;
138        let old_value = self.center_point.get();
139        if old_value.differs(&value) {
140            CompositionShapeHooks::validate_center_point_change(host, old_value, value);
141            CompositionShapeHooks::on_center_point_changing(host);
142            changed = true;
143            // Update the backing value
144            self.center_point.set(value);
145            // Register object for serialization in the next batch
146            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::CENTER_POINT);
147            host.register_for_serialization();
148            // Reset previous animation if any
149            host.pending_animations().remove(ServerCompositionShapeProps::id_of_center_point_property().base());
150            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() & !CompositionShapeChangedFields::CENTER_POINT_ANIMATED);
151            // Check for implicit animations
152            if let Some(animation) = host.implicit_animation("CenterPoint") {
153                // Animation affects only current property
154                if let Some(a) = animation.as_composition_animation() {
155                    self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::CENTER_POINT_ANIMATED);
156                    host.pending_animations().set(
157                        ServerCompositionShapeProps::id_of_center_point_property().base(),
158                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
159                    );
160                }
161                // Animation is triggered by the current field, but does not necessary affects it
162                host.start_animation_group(&animation, "CenterPoint", ExpressionVariant::create(value));
163            }
164        }
165        self.center_point.set(value);
166        if changed {
167            CompositionShapeHooks::on_center_point_changed(host);
168        }
169    }
170
171    /// `Offset` (animated, FerroUI overlay).
172    pub fn offset(&self) -> Vector {
173        self.offset.get()
174    }
175
176    /// Sets `Offset` (animated, FerroUI overlay).
177    pub fn set_offset(&self, host: &dyn CompositionShapeHooks, value: Vector) {
178        let mut changed = false;
179        let old_value = self.offset.get();
180        if old_value.differs(&value) {
181            CompositionShapeHooks::validate_offset_change(host, old_value, value);
182            CompositionShapeHooks::on_offset_changing(host);
183            changed = true;
184            // Update the backing value
185            self.offset.set(value);
186            // Register object for serialization in the next batch
187            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::OFFSET);
188            host.register_for_serialization();
189            // Reset previous animation if any
190            host.pending_animations().remove(ServerCompositionShapeProps::id_of_offset_property().base());
191            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() & !CompositionShapeChangedFields::OFFSET_ANIMATED);
192            // Check for implicit animations
193            if let Some(animation) = host.implicit_animation("Offset") {
194                // Animation affects only current property
195                if let Some(a) = animation.as_composition_animation() {
196                    self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::OFFSET_ANIMATED);
197                    host.pending_animations().set(
198                        ServerCompositionShapeProps::id_of_offset_property().base(),
199                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
200                    );
201                }
202                // Animation is triggered by the current field, but does not necessary affects it
203                host.start_animation_group(&animation, "Offset", ExpressionVariant::create(value));
204            }
205        }
206        self.offset.set(value);
207        if changed {
208            CompositionShapeHooks::on_offset_changed(host);
209        }
210    }
211
212    /// `RotationAngle` (animated, FerroUI overlay).
213    pub fn rotation_angle(&self) -> f32 {
214        self.rotation_angle.get()
215    }
216
217    /// Sets `RotationAngle` (animated, FerroUI overlay).
218    pub fn set_rotation_angle(&self, host: &dyn CompositionShapeHooks, value: f32) {
219        let mut changed = false;
220        let old_value = self.rotation_angle.get();
221        if old_value.differs(&value) {
222            CompositionShapeHooks::validate_rotation_angle_change(host, old_value, value);
223            CompositionShapeHooks::on_rotation_angle_changing(host);
224            changed = true;
225            // Update the backing value
226            self.rotation_angle.set(value);
227            // Register object for serialization in the next batch
228            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::ROTATION_ANGLE);
229            host.register_for_serialization();
230            // Reset previous animation if any
231            host.pending_animations().remove(ServerCompositionShapeProps::id_of_rotation_angle_property().base());
232            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() & !CompositionShapeChangedFields::ROTATION_ANGLE_ANIMATED);
233            // Check for implicit animations
234            if let Some(animation) = host.implicit_animation("RotationAngle") {
235                // Animation affects only current property
236                if let Some(a) = animation.as_composition_animation() {
237                    self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::ROTATION_ANGLE_ANIMATED);
238                    host.pending_animations().set(
239                        ServerCompositionShapeProps::id_of_rotation_angle_property().base(),
240                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
241                    );
242                }
243                // Animation is triggered by the current field, but does not necessary affects it
244                host.start_animation_group(&animation, "RotationAngle", ExpressionVariant::create(value));
245            }
246        }
247        self.rotation_angle.set(value);
248        if changed {
249            CompositionShapeHooks::on_rotation_angle_changed(host);
250        }
251    }
252
253    /// `Scale` (animated, default `Vector::new(1.0, 1.0)`, FerroUI overlay).
254    pub fn scale(&self) -> Vector {
255        self.scale.get()
256    }
257
258    /// Sets `Scale` (animated, default `Vector::new(1.0, 1.0)`, FerroUI overlay).
259    pub fn set_scale(&self, host: &dyn CompositionShapeHooks, value: Vector) {
260        let mut changed = false;
261        let old_value = self.scale.get();
262        if old_value.differs(&value) {
263            CompositionShapeHooks::validate_scale_change(host, old_value, value);
264            CompositionShapeHooks::on_scale_changing(host);
265            changed = true;
266            // Update the backing value
267            self.scale.set(value);
268            // Register object for serialization in the next batch
269            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::SCALE);
270            host.register_for_serialization();
271            // Reset previous animation if any
272            host.pending_animations().remove(ServerCompositionShapeProps::id_of_scale_property().base());
273            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() & !CompositionShapeChangedFields::SCALE_ANIMATED);
274            // Check for implicit animations
275            if let Some(animation) = host.implicit_animation("Scale") {
276                // Animation affects only current property
277                if let Some(a) = animation.as_composition_animation() {
278                    self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::SCALE_ANIMATED);
279                    host.pending_animations().set(
280                        ServerCompositionShapeProps::id_of_scale_property().base(),
281                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
282                    );
283                }
284                // Animation is triggered by the current field, but does not necessary affects it
285                host.start_animation_group(&animation, "Scale", ExpressionVariant::create(value));
286            }
287        }
288        self.scale.set(value);
289        if changed {
290            CompositionShapeHooks::on_scale_changed(host);
291        }
292    }
293
294    /// `TransformMatrix` (animated, default `Matrix::IDENTITY`, FerroUI overlay).
295    pub fn transform_matrix(&self) -> Matrix {
296        self.transform_matrix.get()
297    }
298
299    /// Sets `TransformMatrix` (animated, default `Matrix::IDENTITY`, FerroUI overlay).
300    pub fn set_transform_matrix(&self, host: &dyn CompositionShapeHooks, value: Matrix) {
301        let mut changed = false;
302        let old_value = self.transform_matrix.get();
303        if old_value.differs(&value) {
304            CompositionShapeHooks::validate_transform_matrix_change(host, old_value, value);
305            CompositionShapeHooks::on_transform_matrix_changing(host);
306            changed = true;
307            // Update the backing value
308            self.transform_matrix.set(value);
309            // Register object for serialization in the next batch
310            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::TRANSFORM_MATRIX);
311            host.register_for_serialization();
312            // Reset previous animation if any
313            host.pending_animations().remove(ServerCompositionShapeProps::id_of_transform_matrix_property().base());
314            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() & !CompositionShapeChangedFields::TRANSFORM_MATRIX_ANIMATED);
315            // Check for implicit animations
316            if let Some(animation) = host.implicit_animation("TransformMatrix") {
317                // Animation affects only current property
318                if let Some(a) = animation.as_composition_animation() {
319                    self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::TRANSFORM_MATRIX_ANIMATED);
320                    host.pending_animations().set(
321                        ServerCompositionShapeProps::id_of_transform_matrix_property().base(),
322                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
323                    );
324                }
325                // Animation is triggered by the current field, but does not necessary affects it
326                host.start_animation_group(&animation, "TransformMatrix", ExpressionVariant::create(value));
327            }
328        }
329        self.transform_matrix.set(value);
330        if changed {
331            CompositionShapeHooks::on_transform_matrix_changed(host);
332        }
333    }
334
335    /// Connects an animation with the named property. Returns `false` when
336    /// neither this class nor its generated bases have an animated property
337    /// of that name: the caller continues with its hand-written base.
338    pub fn start_animation(
339        &self,
340        host: &dyn CompositionShapeHooks,
341        property_name: &str,
342        animation: &dyn ICompositionAnimation,
343        final_value: Option<ExpressionVariant>,
344    ) -> bool {
345        if property_name == "CenterPoint" {
346            let server = animation.create_instance(host.server(), final_value);
347            host.pending_animations().set(ServerCompositionShapeProps::id_of_center_point_property().base(), server);
348            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::CENTER_POINT_ANIMATED);
349            host.register_for_serialization();
350            return true;
351        }
352        if property_name == "Offset" {
353            let server = animation.create_instance(host.server(), final_value);
354            host.pending_animations().set(ServerCompositionShapeProps::id_of_offset_property().base(), server);
355            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::OFFSET_ANIMATED);
356            host.register_for_serialization();
357            return true;
358        }
359        if property_name == "RotationAngle" {
360            let server = animation.create_instance(host.server(), final_value);
361            host.pending_animations().set(ServerCompositionShapeProps::id_of_rotation_angle_property().base(), server);
362            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::ROTATION_ANGLE_ANIMATED);
363            host.register_for_serialization();
364            return true;
365        }
366        if property_name == "Scale" {
367            let server = animation.create_instance(host.server(), final_value);
368            host.pending_animations().set(ServerCompositionShapeProps::id_of_scale_property().base(), server);
369            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::SCALE_ANIMATED);
370            host.register_for_serialization();
371            return true;
372        }
373        if property_name == "TransformMatrix" {
374            let server = animation.create_instance(host.server(), final_value);
375            host.pending_animations().set(ServerCompositionShapeProps::id_of_transform_matrix_property().base(), server);
376            self.changed_fields_of_composition_shape.set(self.changed_fields_of_composition_shape.get() | CompositionShapeChangedFields::TRANSFORM_MATRIX_ANIMATED);
377            host.register_for_serialization();
378            return true;
379        }
380        false
381    }
382
383    /// Writes the changes of the object: the base class first, then the
384    /// changed-fields mask and the changed members of this class.
385    pub fn serialize_changes_core(&self, host: &dyn CompositionShapeHooks, writer: &mut BatchStreamWriter<'_>) {
386        let changed = self.changed_fields_of_composition_shape.get();
387        writer.write(changed);
388        if changed.contains(CompositionShapeChangedFields::CENTER_POINT_ANIMATED) {
389            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionShapeProps::id_of_center_point_property().base()));
390        } else if changed.contains(CompositionShapeChangedFields::CENTER_POINT) {
391            writer.write(self.center_point.get());
392        }
393        if changed.contains(CompositionShapeChangedFields::OFFSET_ANIMATED) {
394            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionShapeProps::id_of_offset_property().base()));
395        } else if changed.contains(CompositionShapeChangedFields::OFFSET) {
396            writer.write(self.offset.get());
397        }
398        if changed.contains(CompositionShapeChangedFields::ROTATION_ANGLE_ANIMATED) {
399            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionShapeProps::id_of_rotation_angle_property().base()));
400        } else if changed.contains(CompositionShapeChangedFields::ROTATION_ANGLE) {
401            writer.write(self.rotation_angle.get());
402        }
403        if changed.contains(CompositionShapeChangedFields::SCALE_ANIMATED) {
404            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionShapeProps::id_of_scale_property().base()));
405        } else if changed.contains(CompositionShapeChangedFields::SCALE) {
406            writer.write(self.scale.get());
407        }
408        if changed.contains(CompositionShapeChangedFields::TRANSFORM_MATRIX_ANIMATED) {
409            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionShapeProps::id_of_transform_matrix_property().base()));
410        } else if changed.contains(CompositionShapeChangedFields::TRANSFORM_MATRIX) {
411            writer.write(self.transform_matrix.get());
412        }
413        self.changed_fields_of_composition_shape.set(CompositionShapeChangedFields::empty());
414    }
415}
416
417/// The partial methods of `ServerCompositionShape`: implemented by the class that embeds
418/// [`ServerCompositionShapeProps`]. Every hook defaults to doing nothing.
419pub trait ServerCompositionShapeHooks: IServerAnimatedPropertyHost {
420    fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
421    fn on_fields_deserialized(&self, _changed: CompositionShapeChangedFields) {}
422}
423
424/// The server-side properties of `ServerCompositionShape`.
425pub struct ServerCompositionShapeProps {
426    center_point: Cell<Vector>,
427    offset: Cell<Vector>,
428    rotation_angle: Cell<f32>,
429    scale: Cell<Vector>,
430    transform_matrix: Cell<Matrix>,
431}
432
433impl Default for ServerCompositionShapeProps {
434    fn default() -> Self {
435        Self::new()
436    }
437}
438
439impl ServerCompositionShapeProps {
440    /// Creates the properties with every field at the default of its type; the
441    /// values of the UI thread arrive with the first batch.
442    pub fn new() -> Self {
443        Self {
444            center_point: Cell::new(Default::default()),
445            offset: Cell::new(Default::default()),
446            rotation_angle: Cell::new(Default::default()),
447            scale: Cell::new(Default::default()),
448            transform_matrix: Cell::new(Default::default()),
449        }
450    }
451
452    /// This block or the block of a base class, by type: what
453    /// `IServerObject::get_props` of the embedding class returns.
454    pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
455        if type_id == TypeId::of::<Self>() {
456            return Some(self);
457        }
458        None
459    }
460
461    /// The composition property of `CenterPoint`.
462    pub fn id_of_center_point_property() -> &'static CompositionPropertyOf<Vector> {
463        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
464        PROPERTY.get_or_init(|| {
465            CompositionProperty::register::<ServerCompositionShapeProps, Vector>(
466                "CenterPoint",
467                "ServerCompositionShape",
468                |obj| props_of::<ServerCompositionShapeProps>(obj).center_point.get(),
469                |obj, v| props_of::<ServerCompositionShapeProps>(obj).center_point.set(v),
470                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionShapeProps>(obj).center_point.get())),
471                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionShapeProps>(obj).center_point.set(v.cast_or_default::<Vector>())),
472            )
473        })
474    }
475
476    /// `CenterPoint` (animated).
477    pub fn center_point(&self) -> Vector {
478        self.center_point.get()
479    }
480
481    pub fn set_center_point(&self, host: &dyn ServerCompositionShapeHooks, value: Vector) {
482        self.center_point.set(value);
483        host.remove_animation_for_property(Self::id_of_center_point_property().base());
484    }
485
486    /// The composition property of `Offset`.
487    pub fn id_of_offset_property() -> &'static CompositionPropertyOf<Vector> {
488        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
489        PROPERTY.get_or_init(|| {
490            CompositionProperty::register::<ServerCompositionShapeProps, Vector>(
491                "Offset",
492                "ServerCompositionShape",
493                |obj| props_of::<ServerCompositionShapeProps>(obj).offset.get(),
494                |obj, v| props_of::<ServerCompositionShapeProps>(obj).offset.set(v),
495                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionShapeProps>(obj).offset.get())),
496                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionShapeProps>(obj).offset.set(v.cast_or_default::<Vector>())),
497            )
498        })
499    }
500
501    /// `Offset` (animated).
502    pub fn offset(&self) -> Vector {
503        self.offset.get()
504    }
505
506    pub fn set_offset(&self, host: &dyn ServerCompositionShapeHooks, value: Vector) {
507        self.offset.set(value);
508        host.remove_animation_for_property(Self::id_of_offset_property().base());
509    }
510
511    /// The composition property of `RotationAngle`.
512    pub fn id_of_rotation_angle_property() -> &'static CompositionPropertyOf<f32> {
513        static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
514        PROPERTY.get_or_init(|| {
515            CompositionProperty::register::<ServerCompositionShapeProps, f32>(
516                "RotationAngle",
517                "ServerCompositionShape",
518                |obj| props_of::<ServerCompositionShapeProps>(obj).rotation_angle.get(),
519                |obj, v| props_of::<ServerCompositionShapeProps>(obj).rotation_angle.set(v),
520                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionShapeProps>(obj).rotation_angle.get())),
521                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionShapeProps>(obj).rotation_angle.set(v.cast_or_default::<f32>())),
522            )
523        })
524    }
525
526    /// `RotationAngle` (animated).
527    pub fn rotation_angle(&self) -> f32 {
528        self.rotation_angle.get()
529    }
530
531    pub fn set_rotation_angle(&self, host: &dyn ServerCompositionShapeHooks, value: f32) {
532        self.rotation_angle.set(value);
533        host.remove_animation_for_property(Self::id_of_rotation_angle_property().base());
534    }
535
536    /// The composition property of `Scale`.
537    pub fn id_of_scale_property() -> &'static CompositionPropertyOf<Vector> {
538        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
539        PROPERTY.get_or_init(|| {
540            CompositionProperty::register::<ServerCompositionShapeProps, Vector>(
541                "Scale",
542                "ServerCompositionShape",
543                |obj| props_of::<ServerCompositionShapeProps>(obj).scale.get(),
544                |obj, v| props_of::<ServerCompositionShapeProps>(obj).scale.set(v),
545                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionShapeProps>(obj).scale.get())),
546                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionShapeProps>(obj).scale.set(v.cast_or_default::<Vector>())),
547            )
548        })
549    }
550
551    /// `Scale` (animated).
552    pub fn scale(&self) -> Vector {
553        self.scale.get()
554    }
555
556    pub fn set_scale(&self, host: &dyn ServerCompositionShapeHooks, value: Vector) {
557        self.scale.set(value);
558        host.remove_animation_for_property(Self::id_of_scale_property().base());
559    }
560
561    /// The composition property of `TransformMatrix`.
562    pub fn id_of_transform_matrix_property() -> &'static CompositionPropertyOf<Matrix> {
563        static PROPERTY: OnceLock<CompositionPropertyOf<Matrix>> = OnceLock::new();
564        PROPERTY.get_or_init(|| {
565            CompositionProperty::register::<ServerCompositionShapeProps, Matrix>(
566                "TransformMatrix",
567                "ServerCompositionShape",
568                |obj| props_of::<ServerCompositionShapeProps>(obj).transform_matrix.get(),
569                |obj, v| props_of::<ServerCompositionShapeProps>(obj).transform_matrix.set(v),
570                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionShapeProps>(obj).transform_matrix.get())),
571                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionShapeProps>(obj).transform_matrix.set(v.cast_or_default::<Matrix>())),
572            )
573        })
574    }
575
576    /// `TransformMatrix` (animated).
577    pub fn transform_matrix(&self) -> Matrix {
578        self.transform_matrix.get()
579    }
580
581    pub fn set_transform_matrix(&self, host: &dyn ServerCompositionShapeHooks, value: Matrix) {
582        self.transform_matrix.set(value);
583        host.remove_animation_for_property(Self::id_of_transform_matrix_property().base());
584    }
585
586    /// Reads the changes of the object: the base class first, then the
587    /// changed-fields mask and the changed members of this class.
588    pub fn deserialize_changes_core(
589        &self,
590        host: &dyn ServerCompositionShapeHooks,
591        reader: &mut BatchStreamReader<'_>,
592        committed_at: Duration,
593    ) {
594        ServerCompositionShapeHooks::deserialize_changes_extra(host, reader);
595        let changed = reader.read::<CompositionShapeChangedFields>();
596        if changed.contains(CompositionShapeChangedFields::CENTER_POINT_ANIMATED) {
597            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
598                panic!("a batch starts an animation of `CenterPoint` without an animation instance");
599            };
600            host.set_animated_value(
601                Self::id_of_center_point_property().base(),
602                ExpressionVariant::create(self.center_point.get()),
603                committed_at,
604                animation,
605            );
606        } else if changed.contains(CompositionShapeChangedFields::CENTER_POINT) {
607            let value = reader.read::<Vector>();
608            self.set_center_point(host, value);
609        }
610        if changed.contains(CompositionShapeChangedFields::OFFSET_ANIMATED) {
611            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
612                panic!("a batch starts an animation of `Offset` without an animation instance");
613            };
614            host.set_animated_value(
615                Self::id_of_offset_property().base(),
616                ExpressionVariant::create(self.offset.get()),
617                committed_at,
618                animation,
619            );
620        } else if changed.contains(CompositionShapeChangedFields::OFFSET) {
621            let value = reader.read::<Vector>();
622            self.set_offset(host, value);
623        }
624        if changed.contains(CompositionShapeChangedFields::ROTATION_ANGLE_ANIMATED) {
625            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
626                panic!("a batch starts an animation of `RotationAngle` without an animation instance");
627            };
628            host.set_animated_value(
629                Self::id_of_rotation_angle_property().base(),
630                ExpressionVariant::create(self.rotation_angle.get()),
631                committed_at,
632                animation,
633            );
634        } else if changed.contains(CompositionShapeChangedFields::ROTATION_ANGLE) {
635            let value = reader.read::<f32>();
636            self.set_rotation_angle(host, value);
637        }
638        if changed.contains(CompositionShapeChangedFields::SCALE_ANIMATED) {
639            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
640                panic!("a batch starts an animation of `Scale` without an animation instance");
641            };
642            host.set_animated_value(
643                Self::id_of_scale_property().base(),
644                ExpressionVariant::create(self.scale.get()),
645                committed_at,
646                animation,
647            );
648        } else if changed.contains(CompositionShapeChangedFields::SCALE) {
649            let value = reader.read::<Vector>();
650            self.set_scale(host, value);
651        }
652        if changed.contains(CompositionShapeChangedFields::TRANSFORM_MATRIX_ANIMATED) {
653            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
654                panic!("a batch starts an animation of `TransformMatrix` without an animation instance");
655            };
656            host.set_animated_value(
657                Self::id_of_transform_matrix_property().base(),
658                ExpressionVariant::create(self.transform_matrix.get()),
659                committed_at,
660                animation,
661            );
662        } else if changed.contains(CompositionShapeChangedFields::TRANSFORM_MATRIX) {
663            let value = reader.read::<Matrix>();
664            self.set_transform_matrix(host, value);
665        }
666        ServerCompositionShapeHooks::on_fields_deserialized(host, changed);
667    }
668
669    /// The composition property with the given name, among the properties
670    /// of this class and of its generated bases that have a variant form.
671    pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
672        if name == "CenterPoint" {
673            return Some(Self::id_of_center_point_property().base());
674        }
675        if name == "Offset" {
676            return Some(Self::id_of_offset_property().base());
677        }
678        if name == "RotationAngle" {
679            return Some(Self::id_of_rotation_angle_property().base());
680        }
681        if name == "Scale" {
682            return Some(Self::id_of_scale_property().base());
683        }
684        if name == "TransformMatrix" {
685            return Some(Self::id_of_transform_matrix_property().base());
686        }
687        None
688    }
689}