Skip to main content

ferroui_base/rendering/composition/generated/
composition_sprite_shape.rs

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