Skip to main content

ferroui_base/rendering/composition/generated/
composition_surface_brush.rs

1// @generated by scripts/generate_composition.py — do not edit
2
3//! `CompositionSurfaceBrush`.
4//!
5//! Defined by the FerroUI overlay (scripts/composition-schema-winui.xml).
6
7use bitflags::bitflags;
8use crate::Matrix;
9use crate::Vector;
10use crate::rendering::composition::CompositionBitmapInterpolationMode;
11use crate::rendering::composition::CompositionStretch;
12use crate::rendering::composition::ICompositionObject;
13use crate::rendering::composition::animations::AnimationInstanceFactory;
14use crate::rendering::composition::animations::ICompositionAnimation;
15use crate::rendering::composition::expressions::ExpressionVariant;
16use crate::rendering::composition::server::AsServerRenderResource;
17use crate::rendering::composition::server::CompositionProperty;
18use crate::rendering::composition::server::CompositionPropertyOf;
19use crate::rendering::composition::server::IServerObject;
20use crate::rendering::composition::server::ServerPropertyValue;
21use crate::rendering::composition::server::ServerValueChange;
22use crate::rendering::composition::server::props_of;
23use crate::rendering::composition::server::read_server_object;
24use crate::rendering::composition::transport::BatchStreamReader;
25use crate::rendering::composition::transport::BatchStreamWriter;
26use crate::rendering::composition::transport::BatchValue;
27use crate::rendering::composition::transport::BatchValueReader;
28use std::any::Any;
29use std::any::TypeId;
30use std::cell::Cell;
31use std::cell::RefCell;
32use std::rc::Rc;
33use std::sync::OnceLock;
34use std::time::Duration;
35use super::CompositionBrushHooks;
36use super::CompositionBrushProps;
37use super::ServerCompositionBrushHooks;
38use super::ServerCompositionBrushProps;
39
40bitflags! {
41    /// The fields of `CompositionSurfaceBrush` that changed since its last serialization.
42    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
43    pub struct CompositionSurfaceBrushChangedFields: u32 {
44        const SURFACE = 1;
45        const STRETCH = 2;
46        const HORIZONTAL_ALIGNMENT_RATIO = 4;
47        const HORIZONTAL_ALIGNMENT_RATIO_ANIMATED = 8;
48        const VERTICAL_ALIGNMENT_RATIO = 16;
49        const VERTICAL_ALIGNMENT_RATIO_ANIMATED = 32;
50        const BITMAP_INTERPOLATION_MODE = 64;
51        const ANCHOR_POINT = 128;
52        const ANCHOR_POINT_ANIMATED = 256;
53        const CENTER_POINT = 512;
54        const CENTER_POINT_ANIMATED = 1024;
55        const OFFSET = 2048;
56        const OFFSET_ANIMATED = 4096;
57        const ROTATION_ANGLE = 8192;
58        const ROTATION_ANGLE_ANIMATED = 16384;
59        const SCALE = 32768;
60        const SCALE_ANIMATED = 65536;
61        const TRANSFORM_MATRIX = 131072;
62        const TRANSFORM_MATRIX_ANIMATED = 262144;
63        const SNAP_TO_PIXELS = 524288;
64    }
65}
66
67impl BatchValue for CompositionSurfaceBrushChangedFields {
68    #[inline]
69    fn write_to(&self, out: &mut Vec<u8>) {
70        self.bits().write_to(out);
71    }
72
73    #[inline]
74    fn read_from(input: &mut BatchValueReader<'_>) -> Self {
75        CompositionSurfaceBrushChangedFields::from_bits_retain(u32::read_from(input))
76    }
77}
78
79/// The partial methods of `CompositionSurfaceBrush`: implemented by the class that embeds
80/// [`CompositionSurfaceBrushProps`]. Every hook defaults to doing nothing.
81pub trait CompositionSurfaceBrushHooks: CompositionBrushHooks {
82    fn initialize_defaults_extra(&self) {}
83
84    fn validate_surface_change(&self, _old_value: &Option<Rc<dyn ICompositionObject>>, _new_value: &Option<Rc<dyn ICompositionObject>>) {}
85    fn on_surface_changed(&self) {}
86    fn on_surface_changing(&self) {}
87
88    fn validate_stretch_change(&self, _old_value: CompositionStretch, _new_value: CompositionStretch) {}
89    fn on_stretch_changed(&self) {}
90    fn on_stretch_changing(&self) {}
91
92    fn validate_horizontal_alignment_ratio_change(&self, _old_value: f32, _new_value: f32) {}
93    fn on_horizontal_alignment_ratio_changed(&self) {}
94    fn on_horizontal_alignment_ratio_changing(&self) {}
95
96    fn validate_vertical_alignment_ratio_change(&self, _old_value: f32, _new_value: f32) {}
97    fn on_vertical_alignment_ratio_changed(&self) {}
98    fn on_vertical_alignment_ratio_changing(&self) {}
99
100    fn validate_bitmap_interpolation_mode_change(&self, _old_value: CompositionBitmapInterpolationMode, _new_value: CompositionBitmapInterpolationMode) {}
101    fn on_bitmap_interpolation_mode_changed(&self) {}
102    fn on_bitmap_interpolation_mode_changing(&self) {}
103
104    fn validate_anchor_point_change(&self, _old_value: Vector, _new_value: Vector) {}
105    fn on_anchor_point_changed(&self) {}
106    fn on_anchor_point_changing(&self) {}
107
108    fn validate_center_point_change(&self, _old_value: Vector, _new_value: Vector) {}
109    fn on_center_point_changed(&self) {}
110    fn on_center_point_changing(&self) {}
111
112    fn validate_offset_change(&self, _old_value: Vector, _new_value: Vector) {}
113    fn on_offset_changed(&self) {}
114    fn on_offset_changing(&self) {}
115
116    fn validate_rotation_angle_change(&self, _old_value: f32, _new_value: f32) {}
117    fn on_rotation_angle_changed(&self) {}
118    fn on_rotation_angle_changing(&self) {}
119
120    fn validate_scale_change(&self, _old_value: Vector, _new_value: Vector) {}
121    fn on_scale_changed(&self) {}
122    fn on_scale_changing(&self) {}
123
124    fn validate_transform_matrix_change(&self, _old_value: Matrix, _new_value: Matrix) {}
125    fn on_transform_matrix_changed(&self) {}
126    fn on_transform_matrix_changing(&self) {}
127
128    fn validate_snap_to_pixels_change(&self, _old_value: bool, _new_value: bool) {}
129    fn on_snap_to_pixels_changed(&self) {}
130    fn on_snap_to_pixels_changing(&self) {}
131}
132
133/// The UI-thread properties of `CompositionSurfaceBrush`.
134pub struct CompositionSurfaceBrushProps {
135    base: CompositionBrushProps,
136    changed_fields_of_composition_surface_brush: Cell<CompositionSurfaceBrushChangedFields>,
137    surface: RefCell<Option<Rc<dyn ICompositionObject>>>,
138    stretch: Cell<CompositionStretch>,
139    horizontal_alignment_ratio: Cell<f32>,
140    vertical_alignment_ratio: Cell<f32>,
141    bitmap_interpolation_mode: Cell<CompositionBitmapInterpolationMode>,
142    anchor_point: Cell<Vector>,
143    center_point: Cell<Vector>,
144    offset: Cell<Vector>,
145    rotation_angle: Cell<f32>,
146    scale: Cell<Vector>,
147    transform_matrix: Cell<Matrix>,
148    snap_to_pixels: Cell<bool>,
149}
150
151impl Default for CompositionSurfaceBrushProps {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157impl CompositionSurfaceBrushProps {
158    /// Creates the properties with every field at the default of its type.
159    /// The defaults of the schema are applied by
160    /// [`initialize_defaults`](Self::initialize_defaults).
161    pub fn new() -> Self {
162        Self {
163            base: CompositionBrushProps::new(),
164            changed_fields_of_composition_surface_brush: Cell::new(CompositionSurfaceBrushChangedFields::empty()),
165            surface: RefCell::new(None),
166            stretch: Cell::new(Default::default()),
167            horizontal_alignment_ratio: Cell::new(Default::default()),
168            vertical_alignment_ratio: Cell::new(Default::default()),
169            bitmap_interpolation_mode: Cell::new(Default::default()),
170            anchor_point: Cell::new(Default::default()),
171            center_point: Cell::new(Default::default()),
172            offset: Cell::new(Default::default()),
173            rotation_angle: Cell::new(Default::default()),
174            scale: Cell::new(Default::default()),
175            transform_matrix: Cell::new(Default::default()),
176            snap_to_pixels: Cell::new(Default::default()),
177        }
178    }
179
180    /// The properties of the base class.
181    pub fn base(&self) -> &CompositionBrushProps {
182        &self.base
183    }
184
185    /// The `InitializeDefaults` calls of the constructor chain: the base
186    /// classes first, then this class.
187    pub fn initialize_defaults(&self, host: &dyn CompositionSurfaceBrushHooks) {
188        self.base.initialize_defaults(host);
189        self.initialize_own_defaults(host);
190    }
191
192    /// `InitializeDefaults` of this class alone.
193    pub fn initialize_own_defaults(&self, host: &dyn CompositionSurfaceBrushHooks) {
194        CompositionSurfaceBrushHooks::initialize_defaults_extra(host);
195        self.set_stretch(host, CompositionStretch::Fill);
196        self.set_horizontal_alignment_ratio(host, 0.5);
197        self.set_vertical_alignment_ratio(host, 0.5);
198        self.set_bitmap_interpolation_mode(host, CompositionBitmapInterpolationMode::Linear);
199        self.set_scale(host, Vector::new(1.0, 1.0));
200        self.set_transform_matrix(host, Matrix::IDENTITY);
201    }
202
203    /// `Surface` (FerroUI overlay).
204    pub fn surface(&self) -> Option<Rc<dyn ICompositionObject>> {
205        self.surface.borrow().clone()
206    }
207
208    /// Sets `Surface` (FerroUI overlay).
209    pub fn set_surface(&self, host: &dyn CompositionSurfaceBrushHooks, value: Option<Rc<dyn ICompositionObject>>) {
210        let mut changed = false;
211        let old_value = self.surface.borrow().clone();
212        if old_value.differs(&value) {
213            CompositionSurfaceBrushHooks::validate_surface_change(host, &old_value, &value);
214            CompositionSurfaceBrushHooks::on_surface_changing(host);
215            changed = true;
216            // Update the backing value
217            *self.surface.borrow_mut() = value.clone();
218            // Register object for serialization in the next batch
219            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::SURFACE);
220            host.register_for_serialization();
221        }
222        *self.surface.borrow_mut() = value;
223        if changed {
224            CompositionSurfaceBrushHooks::on_surface_changed(host);
225        }
226    }
227
228    /// `Stretch` (default `CompositionStretch::Fill`, FerroUI overlay).
229    pub fn stretch(&self) -> CompositionStretch {
230        self.stretch.get()
231    }
232
233    /// Sets `Stretch` (default `CompositionStretch::Fill`, FerroUI overlay).
234    pub fn set_stretch(&self, host: &dyn CompositionSurfaceBrushHooks, value: CompositionStretch) {
235        let mut changed = false;
236        let old_value = self.stretch.get();
237        if old_value.differs(&value) {
238            CompositionSurfaceBrushHooks::validate_stretch_change(host, old_value, value);
239            CompositionSurfaceBrushHooks::on_stretch_changing(host);
240            changed = true;
241            // Update the backing value
242            self.stretch.set(value);
243            // Register object for serialization in the next batch
244            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::STRETCH);
245            host.register_for_serialization();
246        }
247        self.stretch.set(value);
248        if changed {
249            CompositionSurfaceBrushHooks::on_stretch_changed(host);
250        }
251    }
252
253    /// `HorizontalAlignmentRatio` (animated, default `0.5`, FerroUI overlay).
254    pub fn horizontal_alignment_ratio(&self) -> f32 {
255        self.horizontal_alignment_ratio.get()
256    }
257
258    /// Sets `HorizontalAlignmentRatio` (animated, default `0.5`, FerroUI overlay).
259    pub fn set_horizontal_alignment_ratio(&self, host: &dyn CompositionSurfaceBrushHooks, value: f32) {
260        let mut changed = false;
261        let old_value = self.horizontal_alignment_ratio.get();
262        if old_value.differs(&value) {
263            CompositionSurfaceBrushHooks::validate_horizontal_alignment_ratio_change(host, old_value, value);
264            CompositionSurfaceBrushHooks::on_horizontal_alignment_ratio_changing(host);
265            changed = true;
266            // Update the backing value
267            self.horizontal_alignment_ratio.set(value);
268            // Register object for serialization in the next batch
269            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO);
270            host.register_for_serialization();
271            // Reset previous animation if any
272            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_horizontal_alignment_ratio_property().base());
273            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO_ANIMATED);
274            // Check for implicit animations
275            if let Some(animation) = host.implicit_animation("HorizontalAlignmentRatio") {
276                // Animation affects only current property
277                if let Some(a) = animation.as_composition_animation() {
278                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO_ANIMATED);
279                    host.pending_animations().set(
280                        ServerCompositionSurfaceBrushProps::id_of_horizontal_alignment_ratio_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, "HorizontalAlignmentRatio", ExpressionVariant::create(value));
286            }
287        }
288        self.horizontal_alignment_ratio.set(value);
289        if changed {
290            CompositionSurfaceBrushHooks::on_horizontal_alignment_ratio_changed(host);
291        }
292    }
293
294    /// `VerticalAlignmentRatio` (animated, default `0.5`, FerroUI overlay).
295    pub fn vertical_alignment_ratio(&self) -> f32 {
296        self.vertical_alignment_ratio.get()
297    }
298
299    /// Sets `VerticalAlignmentRatio` (animated, default `0.5`, FerroUI overlay).
300    pub fn set_vertical_alignment_ratio(&self, host: &dyn CompositionSurfaceBrushHooks, value: f32) {
301        let mut changed = false;
302        let old_value = self.vertical_alignment_ratio.get();
303        if old_value.differs(&value) {
304            CompositionSurfaceBrushHooks::validate_vertical_alignment_ratio_change(host, old_value, value);
305            CompositionSurfaceBrushHooks::on_vertical_alignment_ratio_changing(host);
306            changed = true;
307            // Update the backing value
308            self.vertical_alignment_ratio.set(value);
309            // Register object for serialization in the next batch
310            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO);
311            host.register_for_serialization();
312            // Reset previous animation if any
313            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_vertical_alignment_ratio_property().base());
314            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO_ANIMATED);
315            // Check for implicit animations
316            if let Some(animation) = host.implicit_animation("VerticalAlignmentRatio") {
317                // Animation affects only current property
318                if let Some(a) = animation.as_composition_animation() {
319                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO_ANIMATED);
320                    host.pending_animations().set(
321                        ServerCompositionSurfaceBrushProps::id_of_vertical_alignment_ratio_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, "VerticalAlignmentRatio", ExpressionVariant::create(value));
327            }
328        }
329        self.vertical_alignment_ratio.set(value);
330        if changed {
331            CompositionSurfaceBrushHooks::on_vertical_alignment_ratio_changed(host);
332        }
333    }
334
335    /// `BitmapInterpolationMode` (default `CompositionBitmapInterpolationMode::Linear`, FerroUI overlay).
336    pub fn bitmap_interpolation_mode(&self) -> CompositionBitmapInterpolationMode {
337        self.bitmap_interpolation_mode.get()
338    }
339
340    /// Sets `BitmapInterpolationMode` (default `CompositionBitmapInterpolationMode::Linear`, FerroUI overlay).
341    pub fn set_bitmap_interpolation_mode(&self, host: &dyn CompositionSurfaceBrushHooks, value: CompositionBitmapInterpolationMode) {
342        let mut changed = false;
343        let old_value = self.bitmap_interpolation_mode.get();
344        if old_value.differs(&value) {
345            CompositionSurfaceBrushHooks::validate_bitmap_interpolation_mode_change(host, old_value, value);
346            CompositionSurfaceBrushHooks::on_bitmap_interpolation_mode_changing(host);
347            changed = true;
348            // Update the backing value
349            self.bitmap_interpolation_mode.set(value);
350            // Register object for serialization in the next batch
351            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::BITMAP_INTERPOLATION_MODE);
352            host.register_for_serialization();
353        }
354        self.bitmap_interpolation_mode.set(value);
355        if changed {
356            CompositionSurfaceBrushHooks::on_bitmap_interpolation_mode_changed(host);
357        }
358    }
359
360    /// `AnchorPoint` (animated, FerroUI overlay).
361    pub fn anchor_point(&self) -> Vector {
362        self.anchor_point.get()
363    }
364
365    /// Sets `AnchorPoint` (animated, FerroUI overlay).
366    pub fn set_anchor_point(&self, host: &dyn CompositionSurfaceBrushHooks, value: Vector) {
367        let mut changed = false;
368        let old_value = self.anchor_point.get();
369        if old_value.differs(&value) {
370            CompositionSurfaceBrushHooks::validate_anchor_point_change(host, old_value, value);
371            CompositionSurfaceBrushHooks::on_anchor_point_changing(host);
372            changed = true;
373            // Update the backing value
374            self.anchor_point.set(value);
375            // Register object for serialization in the next batch
376            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ANCHOR_POINT);
377            host.register_for_serialization();
378            // Reset previous animation if any
379            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_anchor_point_property().base());
380            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::ANCHOR_POINT_ANIMATED);
381            // Check for implicit animations
382            if let Some(animation) = host.implicit_animation("AnchorPoint") {
383                // Animation affects only current property
384                if let Some(a) = animation.as_composition_animation() {
385                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ANCHOR_POINT_ANIMATED);
386                    host.pending_animations().set(
387                        ServerCompositionSurfaceBrushProps::id_of_anchor_point_property().base(),
388                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
389                    );
390                }
391                // Animation is triggered by the current field, but does not necessary affects it
392                host.start_animation_group(&animation, "AnchorPoint", ExpressionVariant::create(value));
393            }
394        }
395        self.anchor_point.set(value);
396        if changed {
397            CompositionSurfaceBrushHooks::on_anchor_point_changed(host);
398        }
399    }
400
401    /// `CenterPoint` (animated, FerroUI overlay).
402    pub fn center_point(&self) -> Vector {
403        self.center_point.get()
404    }
405
406    /// Sets `CenterPoint` (animated, FerroUI overlay).
407    pub fn set_center_point(&self, host: &dyn CompositionSurfaceBrushHooks, value: Vector) {
408        let mut changed = false;
409        let old_value = self.center_point.get();
410        if old_value.differs(&value) {
411            CompositionSurfaceBrushHooks::validate_center_point_change(host, old_value, value);
412            CompositionSurfaceBrushHooks::on_center_point_changing(host);
413            changed = true;
414            // Update the backing value
415            self.center_point.set(value);
416            // Register object for serialization in the next batch
417            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::CENTER_POINT);
418            host.register_for_serialization();
419            // Reset previous animation if any
420            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_center_point_property().base());
421            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::CENTER_POINT_ANIMATED);
422            // Check for implicit animations
423            if let Some(animation) = host.implicit_animation("CenterPoint") {
424                // Animation affects only current property
425                if let Some(a) = animation.as_composition_animation() {
426                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::CENTER_POINT_ANIMATED);
427                    host.pending_animations().set(
428                        ServerCompositionSurfaceBrushProps::id_of_center_point_property().base(),
429                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
430                    );
431                }
432                // Animation is triggered by the current field, but does not necessary affects it
433                host.start_animation_group(&animation, "CenterPoint", ExpressionVariant::create(value));
434            }
435        }
436        self.center_point.set(value);
437        if changed {
438            CompositionSurfaceBrushHooks::on_center_point_changed(host);
439        }
440    }
441
442    /// `Offset` (animated, FerroUI overlay).
443    pub fn offset(&self) -> Vector {
444        self.offset.get()
445    }
446
447    /// Sets `Offset` (animated, FerroUI overlay).
448    pub fn set_offset(&self, host: &dyn CompositionSurfaceBrushHooks, value: Vector) {
449        let mut changed = false;
450        let old_value = self.offset.get();
451        if old_value.differs(&value) {
452            CompositionSurfaceBrushHooks::validate_offset_change(host, old_value, value);
453            CompositionSurfaceBrushHooks::on_offset_changing(host);
454            changed = true;
455            // Update the backing value
456            self.offset.set(value);
457            // Register object for serialization in the next batch
458            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::OFFSET);
459            host.register_for_serialization();
460            // Reset previous animation if any
461            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_offset_property().base());
462            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::OFFSET_ANIMATED);
463            // Check for implicit animations
464            if let Some(animation) = host.implicit_animation("Offset") {
465                // Animation affects only current property
466                if let Some(a) = animation.as_composition_animation() {
467                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::OFFSET_ANIMATED);
468                    host.pending_animations().set(
469                        ServerCompositionSurfaceBrushProps::id_of_offset_property().base(),
470                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
471                    );
472                }
473                // Animation is triggered by the current field, but does not necessary affects it
474                host.start_animation_group(&animation, "Offset", ExpressionVariant::create(value));
475            }
476        }
477        self.offset.set(value);
478        if changed {
479            CompositionSurfaceBrushHooks::on_offset_changed(host);
480        }
481    }
482
483    /// `RotationAngle` (animated, FerroUI overlay).
484    pub fn rotation_angle(&self) -> f32 {
485        self.rotation_angle.get()
486    }
487
488    /// Sets `RotationAngle` (animated, FerroUI overlay).
489    pub fn set_rotation_angle(&self, host: &dyn CompositionSurfaceBrushHooks, value: f32) {
490        let mut changed = false;
491        let old_value = self.rotation_angle.get();
492        if old_value.differs(&value) {
493            CompositionSurfaceBrushHooks::validate_rotation_angle_change(host, old_value, value);
494            CompositionSurfaceBrushHooks::on_rotation_angle_changing(host);
495            changed = true;
496            // Update the backing value
497            self.rotation_angle.set(value);
498            // Register object for serialization in the next batch
499            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ROTATION_ANGLE);
500            host.register_for_serialization();
501            // Reset previous animation if any
502            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_rotation_angle_property().base());
503            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::ROTATION_ANGLE_ANIMATED);
504            // Check for implicit animations
505            if let Some(animation) = host.implicit_animation("RotationAngle") {
506                // Animation affects only current property
507                if let Some(a) = animation.as_composition_animation() {
508                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ROTATION_ANGLE_ANIMATED);
509                    host.pending_animations().set(
510                        ServerCompositionSurfaceBrushProps::id_of_rotation_angle_property().base(),
511                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
512                    );
513                }
514                // Animation is triggered by the current field, but does not necessary affects it
515                host.start_animation_group(&animation, "RotationAngle", ExpressionVariant::create(value));
516            }
517        }
518        self.rotation_angle.set(value);
519        if changed {
520            CompositionSurfaceBrushHooks::on_rotation_angle_changed(host);
521        }
522    }
523
524    /// `Scale` (animated, default `Vector::new(1.0, 1.0)`, FerroUI overlay).
525    pub fn scale(&self) -> Vector {
526        self.scale.get()
527    }
528
529    /// Sets `Scale` (animated, default `Vector::new(1.0, 1.0)`, FerroUI overlay).
530    pub fn set_scale(&self, host: &dyn CompositionSurfaceBrushHooks, value: Vector) {
531        let mut changed = false;
532        let old_value = self.scale.get();
533        if old_value.differs(&value) {
534            CompositionSurfaceBrushHooks::validate_scale_change(host, old_value, value);
535            CompositionSurfaceBrushHooks::on_scale_changing(host);
536            changed = true;
537            // Update the backing value
538            self.scale.set(value);
539            // Register object for serialization in the next batch
540            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::SCALE);
541            host.register_for_serialization();
542            // Reset previous animation if any
543            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_scale_property().base());
544            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::SCALE_ANIMATED);
545            // Check for implicit animations
546            if let Some(animation) = host.implicit_animation("Scale") {
547                // Animation affects only current property
548                if let Some(a) = animation.as_composition_animation() {
549                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::SCALE_ANIMATED);
550                    host.pending_animations().set(
551                        ServerCompositionSurfaceBrushProps::id_of_scale_property().base(),
552                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
553                    );
554                }
555                // Animation is triggered by the current field, but does not necessary affects it
556                host.start_animation_group(&animation, "Scale", ExpressionVariant::create(value));
557            }
558        }
559        self.scale.set(value);
560        if changed {
561            CompositionSurfaceBrushHooks::on_scale_changed(host);
562        }
563    }
564
565    /// `TransformMatrix` (animated, default `Matrix::IDENTITY`, FerroUI overlay).
566    pub fn transform_matrix(&self) -> Matrix {
567        self.transform_matrix.get()
568    }
569
570    /// Sets `TransformMatrix` (animated, default `Matrix::IDENTITY`, FerroUI overlay).
571    pub fn set_transform_matrix(&self, host: &dyn CompositionSurfaceBrushHooks, value: Matrix) {
572        let mut changed = false;
573        let old_value = self.transform_matrix.get();
574        if old_value.differs(&value) {
575            CompositionSurfaceBrushHooks::validate_transform_matrix_change(host, old_value, value);
576            CompositionSurfaceBrushHooks::on_transform_matrix_changing(host);
577            changed = true;
578            // Update the backing value
579            self.transform_matrix.set(value);
580            // Register object for serialization in the next batch
581            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX);
582            host.register_for_serialization();
583            // Reset previous animation if any
584            host.pending_animations().remove(ServerCompositionSurfaceBrushProps::id_of_transform_matrix_property().base());
585            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() & !CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX_ANIMATED);
586            // Check for implicit animations
587            if let Some(animation) = host.implicit_animation("TransformMatrix") {
588                // Animation affects only current property
589                if let Some(a) = animation.as_composition_animation() {
590                    self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX_ANIMATED);
591                    host.pending_animations().set(
592                        ServerCompositionSurfaceBrushProps::id_of_transform_matrix_property().base(),
593                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
594                    );
595                }
596                // Animation is triggered by the current field, but does not necessary affects it
597                host.start_animation_group(&animation, "TransformMatrix", ExpressionVariant::create(value));
598            }
599        }
600        self.transform_matrix.set(value);
601        if changed {
602            CompositionSurfaceBrushHooks::on_transform_matrix_changed(host);
603        }
604    }
605
606    /// `SnapToPixels` (FerroUI overlay).
607    pub fn snap_to_pixels(&self) -> bool {
608        self.snap_to_pixels.get()
609    }
610
611    /// Sets `SnapToPixels` (FerroUI overlay).
612    pub fn set_snap_to_pixels(&self, host: &dyn CompositionSurfaceBrushHooks, value: bool) {
613        let mut changed = false;
614        let old_value = self.snap_to_pixels.get();
615        if old_value.differs(&value) {
616            CompositionSurfaceBrushHooks::validate_snap_to_pixels_change(host, old_value, value);
617            CompositionSurfaceBrushHooks::on_snap_to_pixels_changing(host);
618            changed = true;
619            // Update the backing value
620            self.snap_to_pixels.set(value);
621            // Register object for serialization in the next batch
622            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::SNAP_TO_PIXELS);
623            host.register_for_serialization();
624        }
625        self.snap_to_pixels.set(value);
626        if changed {
627            CompositionSurfaceBrushHooks::on_snap_to_pixels_changed(host);
628        }
629    }
630
631    /// Connects an animation with the named property. Returns `false` when
632    /// neither this class nor its generated bases have an animated property
633    /// of that name: the caller continues with its hand-written base.
634    pub fn start_animation(
635        &self,
636        host: &dyn CompositionSurfaceBrushHooks,
637        property_name: &str,
638        animation: &dyn ICompositionAnimation,
639        final_value: Option<ExpressionVariant>,
640    ) -> bool {
641        if property_name == "HorizontalAlignmentRatio" {
642            let server = animation.create_instance(host.server(), final_value);
643            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_horizontal_alignment_ratio_property().base(), server);
644            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO_ANIMATED);
645            host.register_for_serialization();
646            return true;
647        }
648        if property_name == "VerticalAlignmentRatio" {
649            let server = animation.create_instance(host.server(), final_value);
650            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_vertical_alignment_ratio_property().base(), server);
651            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO_ANIMATED);
652            host.register_for_serialization();
653            return true;
654        }
655        if property_name == "AnchorPoint" {
656            let server = animation.create_instance(host.server(), final_value);
657            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_anchor_point_property().base(), server);
658            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ANCHOR_POINT_ANIMATED);
659            host.register_for_serialization();
660            return true;
661        }
662        if property_name == "CenterPoint" {
663            let server = animation.create_instance(host.server(), final_value);
664            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_center_point_property().base(), server);
665            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::CENTER_POINT_ANIMATED);
666            host.register_for_serialization();
667            return true;
668        }
669        if property_name == "Offset" {
670            let server = animation.create_instance(host.server(), final_value);
671            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_offset_property().base(), server);
672            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::OFFSET_ANIMATED);
673            host.register_for_serialization();
674            return true;
675        }
676        if property_name == "RotationAngle" {
677            let server = animation.create_instance(host.server(), final_value);
678            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_rotation_angle_property().base(), server);
679            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::ROTATION_ANGLE_ANIMATED);
680            host.register_for_serialization();
681            return true;
682        }
683        if property_name == "Scale" {
684            let server = animation.create_instance(host.server(), final_value);
685            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_scale_property().base(), server);
686            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::SCALE_ANIMATED);
687            host.register_for_serialization();
688            return true;
689        }
690        if property_name == "TransformMatrix" {
691            let server = animation.create_instance(host.server(), final_value);
692            host.pending_animations().set(ServerCompositionSurfaceBrushProps::id_of_transform_matrix_property().base(), server);
693            self.changed_fields_of_composition_surface_brush.set(self.changed_fields_of_composition_surface_brush.get() | CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX_ANIMATED);
694            host.register_for_serialization();
695            return true;
696        }
697        self.base.start_animation(host, property_name, animation, final_value)
698    }
699
700    /// Writes the changes of the object: the base class first, then the
701    /// changed-fields mask and the changed members of this class.
702    pub fn serialize_changes_core(&self, host: &dyn CompositionSurfaceBrushHooks, writer: &mut BatchStreamWriter<'_>) {
703        self.base.serialize_changes_core(host, writer);
704        let changed = self.changed_fields_of_composition_surface_brush.get();
705        writer.write(changed);
706        if changed.contains(CompositionSurfaceBrushChangedFields::SURFACE) {
707            writer.write_server_object(self.surface.borrow().clone().map(|o| o.server()));
708        }
709        if changed.contains(CompositionSurfaceBrushChangedFields::STRETCH) {
710            writer.write(self.stretch.get());
711        }
712        if changed.contains(CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO_ANIMATED) {
713            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_horizontal_alignment_ratio_property().base()));
714        } else if changed.contains(CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO) {
715            writer.write(self.horizontal_alignment_ratio.get());
716        }
717        if changed.contains(CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO_ANIMATED) {
718            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_vertical_alignment_ratio_property().base()));
719        } else if changed.contains(CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO) {
720            writer.write(self.vertical_alignment_ratio.get());
721        }
722        if changed.contains(CompositionSurfaceBrushChangedFields::BITMAP_INTERPOLATION_MODE) {
723            writer.write(self.bitmap_interpolation_mode.get());
724        }
725        if changed.contains(CompositionSurfaceBrushChangedFields::ANCHOR_POINT_ANIMATED) {
726            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_anchor_point_property().base()));
727        } else if changed.contains(CompositionSurfaceBrushChangedFields::ANCHOR_POINT) {
728            writer.write(self.anchor_point.get());
729        }
730        if changed.contains(CompositionSurfaceBrushChangedFields::CENTER_POINT_ANIMATED) {
731            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_center_point_property().base()));
732        } else if changed.contains(CompositionSurfaceBrushChangedFields::CENTER_POINT) {
733            writer.write(self.center_point.get());
734        }
735        if changed.contains(CompositionSurfaceBrushChangedFields::OFFSET_ANIMATED) {
736            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_offset_property().base()));
737        } else if changed.contains(CompositionSurfaceBrushChangedFields::OFFSET) {
738            writer.write(self.offset.get());
739        }
740        if changed.contains(CompositionSurfaceBrushChangedFields::ROTATION_ANGLE_ANIMATED) {
741            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_rotation_angle_property().base()));
742        } else if changed.contains(CompositionSurfaceBrushChangedFields::ROTATION_ANGLE) {
743            writer.write(self.rotation_angle.get());
744        }
745        if changed.contains(CompositionSurfaceBrushChangedFields::SCALE_ANIMATED) {
746            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_scale_property().base()));
747        } else if changed.contains(CompositionSurfaceBrushChangedFields::SCALE) {
748            writer.write(self.scale.get());
749        }
750        if changed.contains(CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX_ANIMATED) {
751            writer.write_value(host.pending_animations().get_and_remove(ServerCompositionSurfaceBrushProps::id_of_transform_matrix_property().base()));
752        } else if changed.contains(CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX) {
753            writer.write(self.transform_matrix.get());
754        }
755        if changed.contains(CompositionSurfaceBrushChangedFields::SNAP_TO_PIXELS) {
756            writer.write(self.snap_to_pixels.get());
757        }
758        self.changed_fields_of_composition_surface_brush.set(CompositionSurfaceBrushChangedFields::empty());
759    }
760}
761
762/// The partial methods of `ServerCompositionSurfaceBrush`: implemented by the class that embeds
763/// [`ServerCompositionSurfaceBrushProps`]. Every hook defaults to doing nothing.
764pub trait ServerCompositionSurfaceBrushHooks: ServerCompositionBrushHooks {
765    fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
766    fn on_fields_deserialized(&self, _changed: CompositionSurfaceBrushChangedFields) {}
767
768    fn on_surface_changed(&self) {}
769    fn on_surface_changing(&self) {}
770
771    fn on_stretch_changed(&self) {}
772    fn on_stretch_changing(&self) {}
773
774    fn on_bitmap_interpolation_mode_changed(&self) {}
775    fn on_bitmap_interpolation_mode_changing(&self) {}
776
777    fn on_snap_to_pixels_changed(&self) {}
778    fn on_snap_to_pixels_changing(&self) {}
779}
780
781/// The server-side properties of `ServerCompositionSurfaceBrush`.
782pub struct ServerCompositionSurfaceBrushProps {
783    base: ServerCompositionBrushProps,
784    surface: RefCell<Option<Rc<dyn IServerObject>>>,
785    stretch: Cell<CompositionStretch>,
786    horizontal_alignment_ratio: Cell<f32>,
787    vertical_alignment_ratio: Cell<f32>,
788    bitmap_interpolation_mode: Cell<CompositionBitmapInterpolationMode>,
789    anchor_point: Cell<Vector>,
790    center_point: Cell<Vector>,
791    offset: Cell<Vector>,
792    rotation_angle: Cell<f32>,
793    scale: Cell<Vector>,
794    transform_matrix: Cell<Matrix>,
795    snap_to_pixels: Cell<bool>,
796}
797
798impl Default for ServerCompositionSurfaceBrushProps {
799    fn default() -> Self {
800        Self::new()
801    }
802}
803
804impl ServerCompositionSurfaceBrushProps {
805    /// Creates the properties with every field at the default of its type; the
806    /// values of the UI thread arrive with the first batch.
807    pub fn new() -> Self {
808        Self {
809            base: ServerCompositionBrushProps::new(),
810            surface: RefCell::new(None),
811            stretch: Cell::new(Default::default()),
812            horizontal_alignment_ratio: Cell::new(Default::default()),
813            vertical_alignment_ratio: Cell::new(Default::default()),
814            bitmap_interpolation_mode: Cell::new(Default::default()),
815            anchor_point: Cell::new(Default::default()),
816            center_point: Cell::new(Default::default()),
817            offset: Cell::new(Default::default()),
818            rotation_angle: Cell::new(Default::default()),
819            scale: Cell::new(Default::default()),
820            transform_matrix: Cell::new(Default::default()),
821            snap_to_pixels: Cell::new(Default::default()),
822        }
823    }
824
825    /// The properties of the base class.
826    pub fn base(&self) -> &ServerCompositionBrushProps {
827        &self.base
828    }
829
830    /// This block or the block of a base class, by type: what
831    /// `IServerObject::get_props` of the embedding class returns.
832    pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
833        if type_id == TypeId::of::<Self>() {
834            return Some(self);
835        }
836        self.base.find_props(type_id)
837    }
838
839    /// The composition property of `Surface`.
840    pub fn id_of_surface_property() -> &'static CompositionPropertyOf<Option<Rc<dyn IServerObject>>> {
841        static PROPERTY: OnceLock<CompositionPropertyOf<Option<Rc<dyn IServerObject>>>> = OnceLock::new();
842        PROPERTY.get_or_init(|| {
843            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Option<Rc<dyn IServerObject>>>(
844                "Surface",
845                "ServerCompositionSurfaceBrush",
846                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).surface.borrow().clone(),
847                |obj, v| *props_of::<ServerCompositionSurfaceBrushProps>(obj).surface.borrow_mut() = v,
848                None,
849                None,
850            )
851        })
852    }
853
854    /// `Surface`.
855    pub fn surface(&self) -> Option<Rc<dyn IServerObject>> {
856        self.surface.borrow().clone()
857    }
858
859    pub fn set_surface(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Option<Rc<dyn IServerObject>>) {
860        let old_value = self.surface.borrow().clone();
861        let mut changed = false;
862        if old_value.differs(&value) {
863            ServerCompositionSurfaceBrushHooks::on_surface_changing(host);
864            changed = true;
865        }
866        host.set_value(
867            Self::id_of_surface_property().base(),
868            ServerValueChange {
869                equal: old_value.default_equals(&value),
870                old_resource: old_value.as_server_render_resource(),
871                new_resource: value.as_server_render_resource(),
872                assign: &mut || *self.surface.borrow_mut() = value.clone(),
873            },
874        );
875        if changed {
876            ServerCompositionSurfaceBrushHooks::on_surface_changed(host);
877        }
878    }
879
880    /// The composition property of `Stretch`.
881    pub fn id_of_stretch_property() -> &'static CompositionPropertyOf<CompositionStretch> {
882        static PROPERTY: OnceLock<CompositionPropertyOf<CompositionStretch>> = OnceLock::new();
883        PROPERTY.get_or_init(|| {
884            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, CompositionStretch>(
885                "Stretch",
886                "ServerCompositionSurfaceBrush",
887                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).stretch.get(),
888                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).stretch.set(v),
889                None,
890                None,
891            )
892        })
893    }
894
895    /// `Stretch`.
896    pub fn stretch(&self) -> CompositionStretch {
897        self.stretch.get()
898    }
899
900    pub fn set_stretch(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: CompositionStretch) {
901        let old_value = self.stretch.get();
902        let mut changed = false;
903        if old_value.differs(&value) {
904            ServerCompositionSurfaceBrushHooks::on_stretch_changing(host);
905            changed = true;
906        }
907        host.set_value(
908            Self::id_of_stretch_property().base(),
909            ServerValueChange {
910                equal: old_value.default_equals(&value),
911                old_resource: None,
912                new_resource: None,
913                assign: &mut || self.stretch.set(value),
914            },
915        );
916        if changed {
917            ServerCompositionSurfaceBrushHooks::on_stretch_changed(host);
918        }
919    }
920
921    /// The composition property of `HorizontalAlignmentRatio`.
922    pub fn id_of_horizontal_alignment_ratio_property() -> &'static CompositionPropertyOf<f32> {
923        static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
924        PROPERTY.get_or_init(|| {
925            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, f32>(
926                "HorizontalAlignmentRatio",
927                "ServerCompositionSurfaceBrush",
928                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).horizontal_alignment_ratio.get(),
929                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).horizontal_alignment_ratio.set(v),
930                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).horizontal_alignment_ratio.get())),
931                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).horizontal_alignment_ratio.set(v.cast_or_default::<f32>())),
932            )
933        })
934    }
935
936    /// `HorizontalAlignmentRatio` (animated).
937    pub fn horizontal_alignment_ratio(&self) -> f32 {
938        self.horizontal_alignment_ratio.get()
939    }
940
941    pub fn set_horizontal_alignment_ratio(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: f32) {
942        self.horizontal_alignment_ratio.set(value);
943        host.remove_animation_for_property(Self::id_of_horizontal_alignment_ratio_property().base());
944    }
945
946    /// The composition property of `VerticalAlignmentRatio`.
947    pub fn id_of_vertical_alignment_ratio_property() -> &'static CompositionPropertyOf<f32> {
948        static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
949        PROPERTY.get_or_init(|| {
950            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, f32>(
951                "VerticalAlignmentRatio",
952                "ServerCompositionSurfaceBrush",
953                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).vertical_alignment_ratio.get(),
954                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).vertical_alignment_ratio.set(v),
955                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).vertical_alignment_ratio.get())),
956                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).vertical_alignment_ratio.set(v.cast_or_default::<f32>())),
957            )
958        })
959    }
960
961    /// `VerticalAlignmentRatio` (animated).
962    pub fn vertical_alignment_ratio(&self) -> f32 {
963        self.vertical_alignment_ratio.get()
964    }
965
966    pub fn set_vertical_alignment_ratio(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: f32) {
967        self.vertical_alignment_ratio.set(value);
968        host.remove_animation_for_property(Self::id_of_vertical_alignment_ratio_property().base());
969    }
970
971    /// The composition property of `BitmapInterpolationMode`.
972    pub fn id_of_bitmap_interpolation_mode_property() -> &'static CompositionPropertyOf<CompositionBitmapInterpolationMode> {
973        static PROPERTY: OnceLock<CompositionPropertyOf<CompositionBitmapInterpolationMode>> = OnceLock::new();
974        PROPERTY.get_or_init(|| {
975            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, CompositionBitmapInterpolationMode>(
976                "BitmapInterpolationMode",
977                "ServerCompositionSurfaceBrush",
978                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).bitmap_interpolation_mode.get(),
979                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).bitmap_interpolation_mode.set(v),
980                None,
981                None,
982            )
983        })
984    }
985
986    /// `BitmapInterpolationMode`.
987    pub fn bitmap_interpolation_mode(&self) -> CompositionBitmapInterpolationMode {
988        self.bitmap_interpolation_mode.get()
989    }
990
991    pub fn set_bitmap_interpolation_mode(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: CompositionBitmapInterpolationMode) {
992        let old_value = self.bitmap_interpolation_mode.get();
993        let mut changed = false;
994        if old_value.differs(&value) {
995            ServerCompositionSurfaceBrushHooks::on_bitmap_interpolation_mode_changing(host);
996            changed = true;
997        }
998        host.set_value(
999            Self::id_of_bitmap_interpolation_mode_property().base(),
1000            ServerValueChange {
1001                equal: old_value.default_equals(&value),
1002                old_resource: None,
1003                new_resource: None,
1004                assign: &mut || self.bitmap_interpolation_mode.set(value),
1005            },
1006        );
1007        if changed {
1008            ServerCompositionSurfaceBrushHooks::on_bitmap_interpolation_mode_changed(host);
1009        }
1010    }
1011
1012    /// The composition property of `AnchorPoint`.
1013    pub fn id_of_anchor_point_property() -> &'static CompositionPropertyOf<Vector> {
1014        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
1015        PROPERTY.get_or_init(|| {
1016            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Vector>(
1017                "AnchorPoint",
1018                "ServerCompositionSurfaceBrush",
1019                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).anchor_point.get(),
1020                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).anchor_point.set(v),
1021                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).anchor_point.get())),
1022                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).anchor_point.set(v.cast_or_default::<Vector>())),
1023            )
1024        })
1025    }
1026
1027    /// `AnchorPoint` (animated).
1028    pub fn anchor_point(&self) -> Vector {
1029        self.anchor_point.get()
1030    }
1031
1032    pub fn set_anchor_point(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Vector) {
1033        self.anchor_point.set(value);
1034        host.remove_animation_for_property(Self::id_of_anchor_point_property().base());
1035    }
1036
1037    /// The composition property of `CenterPoint`.
1038    pub fn id_of_center_point_property() -> &'static CompositionPropertyOf<Vector> {
1039        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
1040        PROPERTY.get_or_init(|| {
1041            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Vector>(
1042                "CenterPoint",
1043                "ServerCompositionSurfaceBrush",
1044                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).center_point.get(),
1045                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).center_point.set(v),
1046                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).center_point.get())),
1047                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).center_point.set(v.cast_or_default::<Vector>())),
1048            )
1049        })
1050    }
1051
1052    /// `CenterPoint` (animated).
1053    pub fn center_point(&self) -> Vector {
1054        self.center_point.get()
1055    }
1056
1057    pub fn set_center_point(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Vector) {
1058        self.center_point.set(value);
1059        host.remove_animation_for_property(Self::id_of_center_point_property().base());
1060    }
1061
1062    /// The composition property of `Offset`.
1063    pub fn id_of_offset_property() -> &'static CompositionPropertyOf<Vector> {
1064        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
1065        PROPERTY.get_or_init(|| {
1066            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Vector>(
1067                "Offset",
1068                "ServerCompositionSurfaceBrush",
1069                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).offset.get(),
1070                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).offset.set(v),
1071                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).offset.get())),
1072                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).offset.set(v.cast_or_default::<Vector>())),
1073            )
1074        })
1075    }
1076
1077    /// `Offset` (animated).
1078    pub fn offset(&self) -> Vector {
1079        self.offset.get()
1080    }
1081
1082    pub fn set_offset(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Vector) {
1083        self.offset.set(value);
1084        host.remove_animation_for_property(Self::id_of_offset_property().base());
1085    }
1086
1087    /// The composition property of `RotationAngle`.
1088    pub fn id_of_rotation_angle_property() -> &'static CompositionPropertyOf<f32> {
1089        static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1090        PROPERTY.get_or_init(|| {
1091            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, f32>(
1092                "RotationAngle",
1093                "ServerCompositionSurfaceBrush",
1094                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).rotation_angle.get(),
1095                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).rotation_angle.set(v),
1096                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).rotation_angle.get())),
1097                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).rotation_angle.set(v.cast_or_default::<f32>())),
1098            )
1099        })
1100    }
1101
1102    /// `RotationAngle` (animated).
1103    pub fn rotation_angle(&self) -> f32 {
1104        self.rotation_angle.get()
1105    }
1106
1107    pub fn set_rotation_angle(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: f32) {
1108        self.rotation_angle.set(value);
1109        host.remove_animation_for_property(Self::id_of_rotation_angle_property().base());
1110    }
1111
1112    /// The composition property of `Scale`.
1113    pub fn id_of_scale_property() -> &'static CompositionPropertyOf<Vector> {
1114        static PROPERTY: OnceLock<CompositionPropertyOf<Vector>> = OnceLock::new();
1115        PROPERTY.get_or_init(|| {
1116            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Vector>(
1117                "Scale",
1118                "ServerCompositionSurfaceBrush",
1119                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).scale.get(),
1120                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).scale.set(v),
1121                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).scale.get())),
1122                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).scale.set(v.cast_or_default::<Vector>())),
1123            )
1124        })
1125    }
1126
1127    /// `Scale` (animated).
1128    pub fn scale(&self) -> Vector {
1129        self.scale.get()
1130    }
1131
1132    pub fn set_scale(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Vector) {
1133        self.scale.set(value);
1134        host.remove_animation_for_property(Self::id_of_scale_property().base());
1135    }
1136
1137    /// The composition property of `TransformMatrix`.
1138    pub fn id_of_transform_matrix_property() -> &'static CompositionPropertyOf<Matrix> {
1139        static PROPERTY: OnceLock<CompositionPropertyOf<Matrix>> = OnceLock::new();
1140        PROPERTY.get_or_init(|| {
1141            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, Matrix>(
1142                "TransformMatrix",
1143                "ServerCompositionSurfaceBrush",
1144                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).transform_matrix.get(),
1145                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).transform_matrix.set(v),
1146                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).transform_matrix.get())),
1147                Some(|obj, v: ExpressionVariant| props_of::<ServerCompositionSurfaceBrushProps>(obj).transform_matrix.set(v.cast_or_default::<Matrix>())),
1148            )
1149        })
1150    }
1151
1152    /// `TransformMatrix` (animated).
1153    pub fn transform_matrix(&self) -> Matrix {
1154        self.transform_matrix.get()
1155    }
1156
1157    pub fn set_transform_matrix(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: Matrix) {
1158        self.transform_matrix.set(value);
1159        host.remove_animation_for_property(Self::id_of_transform_matrix_property().base());
1160    }
1161
1162    /// The composition property of `SnapToPixels`.
1163    pub fn id_of_snap_to_pixels_property() -> &'static CompositionPropertyOf<bool> {
1164        static PROPERTY: OnceLock<CompositionPropertyOf<bool>> = OnceLock::new();
1165        PROPERTY.get_or_init(|| {
1166            CompositionProperty::register::<ServerCompositionSurfaceBrushProps, bool>(
1167                "SnapToPixels",
1168                "ServerCompositionSurfaceBrush",
1169                |obj| props_of::<ServerCompositionSurfaceBrushProps>(obj).snap_to_pixels.get(),
1170                |obj, v| props_of::<ServerCompositionSurfaceBrushProps>(obj).snap_to_pixels.set(v),
1171                Some(|obj| ExpressionVariant::create(props_of::<ServerCompositionSurfaceBrushProps>(obj).snap_to_pixels.get())),
1172                None,
1173            )
1174        })
1175    }
1176
1177    /// `SnapToPixels`.
1178    pub fn snap_to_pixels(&self) -> bool {
1179        self.snap_to_pixels.get()
1180    }
1181
1182    pub fn set_snap_to_pixels(&self, host: &dyn ServerCompositionSurfaceBrushHooks, value: bool) {
1183        let old_value = self.snap_to_pixels.get();
1184        let mut changed = false;
1185        if old_value.differs(&value) {
1186            ServerCompositionSurfaceBrushHooks::on_snap_to_pixels_changing(host);
1187            changed = true;
1188        }
1189        host.set_value(
1190            Self::id_of_snap_to_pixels_property().base(),
1191            ServerValueChange {
1192                equal: old_value.default_equals(&value),
1193                old_resource: None,
1194                new_resource: None,
1195                assign: &mut || self.snap_to_pixels.set(value),
1196            },
1197        );
1198        if changed {
1199            ServerCompositionSurfaceBrushHooks::on_snap_to_pixels_changed(host);
1200        }
1201    }
1202
1203    /// Reads the changes of the object: the base class first, then the
1204    /// changed-fields mask and the changed members of this class.
1205    pub fn deserialize_changes_core(
1206        &self,
1207        host: &dyn ServerCompositionSurfaceBrushHooks,
1208        reader: &mut BatchStreamReader<'_>,
1209        committed_at: Duration,
1210    ) {
1211        let compositor = host.server_compositor();
1212        self.base.deserialize_changes_core(host, reader, committed_at);
1213        ServerCompositionSurfaceBrushHooks::deserialize_changes_extra(host, reader);
1214        let changed = reader.read::<CompositionSurfaceBrushChangedFields>();
1215        if changed.contains(CompositionSurfaceBrushChangedFields::SURFACE) {
1216            let value = read_server_object(reader, compositor.as_ref());
1217            self.set_surface(host, value);
1218        }
1219        if changed.contains(CompositionSurfaceBrushChangedFields::STRETCH) {
1220            let value = reader.read::<CompositionStretch>();
1221            self.set_stretch(host, value);
1222        }
1223        if changed.contains(CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO_ANIMATED) {
1224            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1225                panic!("a batch starts an animation of `HorizontalAlignmentRatio` without an animation instance");
1226            };
1227            host.set_animated_value(
1228                Self::id_of_horizontal_alignment_ratio_property().base(),
1229                ExpressionVariant::create(self.horizontal_alignment_ratio.get()),
1230                committed_at,
1231                animation,
1232            );
1233        } else if changed.contains(CompositionSurfaceBrushChangedFields::HORIZONTAL_ALIGNMENT_RATIO) {
1234            let value = reader.read::<f32>();
1235            self.set_horizontal_alignment_ratio(host, value);
1236        }
1237        if changed.contains(CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO_ANIMATED) {
1238            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1239                panic!("a batch starts an animation of `VerticalAlignmentRatio` without an animation instance");
1240            };
1241            host.set_animated_value(
1242                Self::id_of_vertical_alignment_ratio_property().base(),
1243                ExpressionVariant::create(self.vertical_alignment_ratio.get()),
1244                committed_at,
1245                animation,
1246            );
1247        } else if changed.contains(CompositionSurfaceBrushChangedFields::VERTICAL_ALIGNMENT_RATIO) {
1248            let value = reader.read::<f32>();
1249            self.set_vertical_alignment_ratio(host, value);
1250        }
1251        if changed.contains(CompositionSurfaceBrushChangedFields::BITMAP_INTERPOLATION_MODE) {
1252            let value = reader.read::<CompositionBitmapInterpolationMode>();
1253            self.set_bitmap_interpolation_mode(host, value);
1254        }
1255        if changed.contains(CompositionSurfaceBrushChangedFields::ANCHOR_POINT_ANIMATED) {
1256            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1257                panic!("a batch starts an animation of `AnchorPoint` without an animation instance");
1258            };
1259            host.set_animated_value(
1260                Self::id_of_anchor_point_property().base(),
1261                ExpressionVariant::create(self.anchor_point.get()),
1262                committed_at,
1263                animation,
1264            );
1265        } else if changed.contains(CompositionSurfaceBrushChangedFields::ANCHOR_POINT) {
1266            let value = reader.read::<Vector>();
1267            self.set_anchor_point(host, value);
1268        }
1269        if changed.contains(CompositionSurfaceBrushChangedFields::CENTER_POINT_ANIMATED) {
1270            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1271                panic!("a batch starts an animation of `CenterPoint` without an animation instance");
1272            };
1273            host.set_animated_value(
1274                Self::id_of_center_point_property().base(),
1275                ExpressionVariant::create(self.center_point.get()),
1276                committed_at,
1277                animation,
1278            );
1279        } else if changed.contains(CompositionSurfaceBrushChangedFields::CENTER_POINT) {
1280            let value = reader.read::<Vector>();
1281            self.set_center_point(host, value);
1282        }
1283        if changed.contains(CompositionSurfaceBrushChangedFields::OFFSET_ANIMATED) {
1284            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1285                panic!("a batch starts an animation of `Offset` without an animation instance");
1286            };
1287            host.set_animated_value(
1288                Self::id_of_offset_property().base(),
1289                ExpressionVariant::create(self.offset.get()),
1290                committed_at,
1291                animation,
1292            );
1293        } else if changed.contains(CompositionSurfaceBrushChangedFields::OFFSET) {
1294            let value = reader.read::<Vector>();
1295            self.set_offset(host, value);
1296        }
1297        if changed.contains(CompositionSurfaceBrushChangedFields::ROTATION_ANGLE_ANIMATED) {
1298            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1299                panic!("a batch starts an animation of `RotationAngle` without an animation instance");
1300            };
1301            host.set_animated_value(
1302                Self::id_of_rotation_angle_property().base(),
1303                ExpressionVariant::create(self.rotation_angle.get()),
1304                committed_at,
1305                animation,
1306            );
1307        } else if changed.contains(CompositionSurfaceBrushChangedFields::ROTATION_ANGLE) {
1308            let value = reader.read::<f32>();
1309            self.set_rotation_angle(host, value);
1310        }
1311        if changed.contains(CompositionSurfaceBrushChangedFields::SCALE_ANIMATED) {
1312            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1313                panic!("a batch starts an animation of `Scale` without an animation instance");
1314            };
1315            host.set_animated_value(
1316                Self::id_of_scale_property().base(),
1317                ExpressionVariant::create(self.scale.get()),
1318                committed_at,
1319                animation,
1320            );
1321        } else if changed.contains(CompositionSurfaceBrushChangedFields::SCALE) {
1322            let value = reader.read::<Vector>();
1323            self.set_scale(host, value);
1324        }
1325        if changed.contains(CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX_ANIMATED) {
1326            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1327                panic!("a batch starts an animation of `TransformMatrix` without an animation instance");
1328            };
1329            host.set_animated_value(
1330                Self::id_of_transform_matrix_property().base(),
1331                ExpressionVariant::create(self.transform_matrix.get()),
1332                committed_at,
1333                animation,
1334            );
1335        } else if changed.contains(CompositionSurfaceBrushChangedFields::TRANSFORM_MATRIX) {
1336            let value = reader.read::<Matrix>();
1337            self.set_transform_matrix(host, value);
1338        }
1339        if changed.contains(CompositionSurfaceBrushChangedFields::SNAP_TO_PIXELS) {
1340            let value = reader.read::<bool>();
1341            self.set_snap_to_pixels(host, value);
1342        }
1343        ServerCompositionSurfaceBrushHooks::on_fields_deserialized(host, changed);
1344    }
1345
1346    /// The composition property with the given name, among the properties
1347    /// of this class and of its generated bases that have a variant form.
1348    pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
1349        if name == "HorizontalAlignmentRatio" {
1350            return Some(Self::id_of_horizontal_alignment_ratio_property().base());
1351        }
1352        if name == "VerticalAlignmentRatio" {
1353            return Some(Self::id_of_vertical_alignment_ratio_property().base());
1354        }
1355        if name == "AnchorPoint" {
1356            return Some(Self::id_of_anchor_point_property().base());
1357        }
1358        if name == "CenterPoint" {
1359            return Some(Self::id_of_center_point_property().base());
1360        }
1361        if name == "Offset" {
1362            return Some(Self::id_of_offset_property().base());
1363        }
1364        if name == "RotationAngle" {
1365            return Some(Self::id_of_rotation_angle_property().base());
1366        }
1367        if name == "Scale" {
1368            return Some(Self::id_of_scale_property().base());
1369        }
1370        if name == "TransformMatrix" {
1371            return Some(Self::id_of_transform_matrix_property().base());
1372        }
1373        if name == "SnapToPixels" {
1374            return Some(Self::id_of_snap_to_pixels_property().base());
1375        }
1376        ServerCompositionBrushProps::get_composition_property(name)
1377    }
1378}