1use bitflags::bitflags;
8use crate::rendering::composition::animations::AnimationInstanceFactory;
9use crate::rendering::composition::animations::ICompositionAnimation;
10use crate::rendering::composition::expressions::ExpressionVariant;
11use crate::rendering::composition::server::CompositionProperty;
12use crate::rendering::composition::server::CompositionPropertyOf;
13use crate::rendering::composition::server::ServerPropertyValue;
14use crate::rendering::composition::server::props_of;
15use crate::rendering::composition::transport::BatchStreamReader;
16use crate::rendering::composition::transport::BatchStreamWriter;
17use crate::rendering::composition::transport::BatchValue;
18use crate::rendering::composition::transport::BatchValueReader;
19use std::any::Any;
20use std::any::TypeId;
21use std::cell::Cell;
22use std::sync::OnceLock;
23use std::time::Duration;
24use super::CompositionClipHooks;
25use super::CompositionClipProps;
26use super::ServerCompositionClipHooks;
27use super::ServerCompositionClipProps;
28
29bitflags! {
30 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
32 pub struct InsetClipChangedFields: u8 {
33 const LEFT_INSET = 1;
34 const LEFT_INSET_ANIMATED = 2;
35 const TOP_INSET = 4;
36 const TOP_INSET_ANIMATED = 8;
37 const RIGHT_INSET = 16;
38 const RIGHT_INSET_ANIMATED = 32;
39 const BOTTOM_INSET = 64;
40 const BOTTOM_INSET_ANIMATED = 128;
41 }
42}
43
44impl BatchValue for InsetClipChangedFields {
45 #[inline]
46 fn write_to(&self, out: &mut Vec<u8>) {
47 self.bits().write_to(out);
48 }
49
50 #[inline]
51 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
52 InsetClipChangedFields::from_bits_retain(u8::read_from(input))
53 }
54}
55
56pub trait InsetClipHooks: CompositionClipHooks {
59 fn initialize_defaults_extra(&self) {}
60
61 fn validate_left_inset_change(&self, _old_value: f32, _new_value: f32) {}
62 fn on_left_inset_changed(&self) {}
63 fn on_left_inset_changing(&self) {}
64
65 fn validate_top_inset_change(&self, _old_value: f32, _new_value: f32) {}
66 fn on_top_inset_changed(&self) {}
67 fn on_top_inset_changing(&self) {}
68
69 fn validate_right_inset_change(&self, _old_value: f32, _new_value: f32) {}
70 fn on_right_inset_changed(&self) {}
71 fn on_right_inset_changing(&self) {}
72
73 fn validate_bottom_inset_change(&self, _old_value: f32, _new_value: f32) {}
74 fn on_bottom_inset_changed(&self) {}
75 fn on_bottom_inset_changing(&self) {}
76}
77
78pub struct InsetClipProps {
80 base: CompositionClipProps,
81 changed_fields_of_inset_clip: Cell<InsetClipChangedFields>,
82 left_inset: Cell<f32>,
83 top_inset: Cell<f32>,
84 right_inset: Cell<f32>,
85 bottom_inset: Cell<f32>,
86}
87
88impl Default for InsetClipProps {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl InsetClipProps {
95 pub fn new() -> Self {
99 Self {
100 base: CompositionClipProps::new(),
101 changed_fields_of_inset_clip: Cell::new(InsetClipChangedFields::empty()),
102 left_inset: Cell::new(Default::default()),
103 top_inset: Cell::new(Default::default()),
104 right_inset: Cell::new(Default::default()),
105 bottom_inset: Cell::new(Default::default()),
106 }
107 }
108
109 pub fn base(&self) -> &CompositionClipProps {
111 &self.base
112 }
113
114 pub fn initialize_defaults(&self, host: &dyn InsetClipHooks) {
117 self.base.initialize_defaults(host);
118 self.initialize_own_defaults(host);
119 }
120
121 pub fn initialize_own_defaults(&self, host: &dyn InsetClipHooks) {
123 InsetClipHooks::initialize_defaults_extra(host);
124 }
125
126 pub fn left_inset(&self) -> f32 {
128 self.left_inset.get()
129 }
130
131 pub fn set_left_inset(&self, host: &dyn InsetClipHooks, value: f32) {
133 let mut changed = false;
134 let old_value = self.left_inset.get();
135 if old_value.differs(&value) {
136 InsetClipHooks::validate_left_inset_change(host, old_value, value);
137 InsetClipHooks::on_left_inset_changing(host);
138 changed = true;
139 self.left_inset.set(value);
141 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::LEFT_INSET);
143 host.register_for_serialization();
144 host.pending_animations().remove(ServerInsetClipProps::id_of_left_inset_property().base());
146 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() & !InsetClipChangedFields::LEFT_INSET_ANIMATED);
147 if let Some(animation) = host.implicit_animation("LeftInset") {
149 if let Some(a) = animation.as_composition_animation() {
151 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::LEFT_INSET_ANIMATED);
152 host.pending_animations().set(
153 ServerInsetClipProps::id_of_left_inset_property().base(),
154 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
155 );
156 }
157 host.start_animation_group(&animation, "LeftInset", ExpressionVariant::create(value));
159 }
160 }
161 self.left_inset.set(value);
162 if changed {
163 InsetClipHooks::on_left_inset_changed(host);
164 }
165 }
166
167 pub fn top_inset(&self) -> f32 {
169 self.top_inset.get()
170 }
171
172 pub fn set_top_inset(&self, host: &dyn InsetClipHooks, value: f32) {
174 let mut changed = false;
175 let old_value = self.top_inset.get();
176 if old_value.differs(&value) {
177 InsetClipHooks::validate_top_inset_change(host, old_value, value);
178 InsetClipHooks::on_top_inset_changing(host);
179 changed = true;
180 self.top_inset.set(value);
182 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::TOP_INSET);
184 host.register_for_serialization();
185 host.pending_animations().remove(ServerInsetClipProps::id_of_top_inset_property().base());
187 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() & !InsetClipChangedFields::TOP_INSET_ANIMATED);
188 if let Some(animation) = host.implicit_animation("TopInset") {
190 if let Some(a) = animation.as_composition_animation() {
192 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::TOP_INSET_ANIMATED);
193 host.pending_animations().set(
194 ServerInsetClipProps::id_of_top_inset_property().base(),
195 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
196 );
197 }
198 host.start_animation_group(&animation, "TopInset", ExpressionVariant::create(value));
200 }
201 }
202 self.top_inset.set(value);
203 if changed {
204 InsetClipHooks::on_top_inset_changed(host);
205 }
206 }
207
208 pub fn right_inset(&self) -> f32 {
210 self.right_inset.get()
211 }
212
213 pub fn set_right_inset(&self, host: &dyn InsetClipHooks, value: f32) {
215 let mut changed = false;
216 let old_value = self.right_inset.get();
217 if old_value.differs(&value) {
218 InsetClipHooks::validate_right_inset_change(host, old_value, value);
219 InsetClipHooks::on_right_inset_changing(host);
220 changed = true;
221 self.right_inset.set(value);
223 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::RIGHT_INSET);
225 host.register_for_serialization();
226 host.pending_animations().remove(ServerInsetClipProps::id_of_right_inset_property().base());
228 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() & !InsetClipChangedFields::RIGHT_INSET_ANIMATED);
229 if let Some(animation) = host.implicit_animation("RightInset") {
231 if let Some(a) = animation.as_composition_animation() {
233 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::RIGHT_INSET_ANIMATED);
234 host.pending_animations().set(
235 ServerInsetClipProps::id_of_right_inset_property().base(),
236 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
237 );
238 }
239 host.start_animation_group(&animation, "RightInset", ExpressionVariant::create(value));
241 }
242 }
243 self.right_inset.set(value);
244 if changed {
245 InsetClipHooks::on_right_inset_changed(host);
246 }
247 }
248
249 pub fn bottom_inset(&self) -> f32 {
251 self.bottom_inset.get()
252 }
253
254 pub fn set_bottom_inset(&self, host: &dyn InsetClipHooks, value: f32) {
256 let mut changed = false;
257 let old_value = self.bottom_inset.get();
258 if old_value.differs(&value) {
259 InsetClipHooks::validate_bottom_inset_change(host, old_value, value);
260 InsetClipHooks::on_bottom_inset_changing(host);
261 changed = true;
262 self.bottom_inset.set(value);
264 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::BOTTOM_INSET);
266 host.register_for_serialization();
267 host.pending_animations().remove(ServerInsetClipProps::id_of_bottom_inset_property().base());
269 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() & !InsetClipChangedFields::BOTTOM_INSET_ANIMATED);
270 if let Some(animation) = host.implicit_animation("BottomInset") {
272 if let Some(a) = animation.as_composition_animation() {
274 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::BOTTOM_INSET_ANIMATED);
275 host.pending_animations().set(
276 ServerInsetClipProps::id_of_bottom_inset_property().base(),
277 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
278 );
279 }
280 host.start_animation_group(&animation, "BottomInset", ExpressionVariant::create(value));
282 }
283 }
284 self.bottom_inset.set(value);
285 if changed {
286 InsetClipHooks::on_bottom_inset_changed(host);
287 }
288 }
289
290 pub fn start_animation(
294 &self,
295 host: &dyn InsetClipHooks,
296 property_name: &str,
297 animation: &dyn ICompositionAnimation,
298 final_value: Option<ExpressionVariant>,
299 ) -> bool {
300 if property_name == "LeftInset" {
301 let server = animation.create_instance(host.server(), final_value);
302 host.pending_animations().set(ServerInsetClipProps::id_of_left_inset_property().base(), server);
303 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::LEFT_INSET_ANIMATED);
304 host.register_for_serialization();
305 return true;
306 }
307 if property_name == "TopInset" {
308 let server = animation.create_instance(host.server(), final_value);
309 host.pending_animations().set(ServerInsetClipProps::id_of_top_inset_property().base(), server);
310 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::TOP_INSET_ANIMATED);
311 host.register_for_serialization();
312 return true;
313 }
314 if property_name == "RightInset" {
315 let server = animation.create_instance(host.server(), final_value);
316 host.pending_animations().set(ServerInsetClipProps::id_of_right_inset_property().base(), server);
317 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::RIGHT_INSET_ANIMATED);
318 host.register_for_serialization();
319 return true;
320 }
321 if property_name == "BottomInset" {
322 let server = animation.create_instance(host.server(), final_value);
323 host.pending_animations().set(ServerInsetClipProps::id_of_bottom_inset_property().base(), server);
324 self.changed_fields_of_inset_clip.set(self.changed_fields_of_inset_clip.get() | InsetClipChangedFields::BOTTOM_INSET_ANIMATED);
325 host.register_for_serialization();
326 return true;
327 }
328 self.base.start_animation(host, property_name, animation, final_value)
329 }
330
331 pub fn serialize_changes_core(&self, host: &dyn InsetClipHooks, writer: &mut BatchStreamWriter<'_>) {
334 self.base.serialize_changes_core(host, writer);
335 let changed = self.changed_fields_of_inset_clip.get();
336 writer.write(changed);
337 if changed.contains(InsetClipChangedFields::LEFT_INSET_ANIMATED) {
338 writer.write_value(host.pending_animations().get_and_remove(ServerInsetClipProps::id_of_left_inset_property().base()));
339 } else if changed.contains(InsetClipChangedFields::LEFT_INSET) {
340 writer.write(self.left_inset.get());
341 }
342 if changed.contains(InsetClipChangedFields::TOP_INSET_ANIMATED) {
343 writer.write_value(host.pending_animations().get_and_remove(ServerInsetClipProps::id_of_top_inset_property().base()));
344 } else if changed.contains(InsetClipChangedFields::TOP_INSET) {
345 writer.write(self.top_inset.get());
346 }
347 if changed.contains(InsetClipChangedFields::RIGHT_INSET_ANIMATED) {
348 writer.write_value(host.pending_animations().get_and_remove(ServerInsetClipProps::id_of_right_inset_property().base()));
349 } else if changed.contains(InsetClipChangedFields::RIGHT_INSET) {
350 writer.write(self.right_inset.get());
351 }
352 if changed.contains(InsetClipChangedFields::BOTTOM_INSET_ANIMATED) {
353 writer.write_value(host.pending_animations().get_and_remove(ServerInsetClipProps::id_of_bottom_inset_property().base()));
354 } else if changed.contains(InsetClipChangedFields::BOTTOM_INSET) {
355 writer.write(self.bottom_inset.get());
356 }
357 self.changed_fields_of_inset_clip.set(InsetClipChangedFields::empty());
358 }
359}
360
361pub trait ServerInsetClipHooks: ServerCompositionClipHooks {
364 fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
365 fn on_fields_deserialized(&self, _changed: InsetClipChangedFields) {}
366}
367
368pub struct ServerInsetClipProps {
370 base: ServerCompositionClipProps,
371 left_inset: Cell<f32>,
372 top_inset: Cell<f32>,
373 right_inset: Cell<f32>,
374 bottom_inset: Cell<f32>,
375}
376
377impl Default for ServerInsetClipProps {
378 fn default() -> Self {
379 Self::new()
380 }
381}
382
383impl ServerInsetClipProps {
384 pub fn new() -> Self {
387 Self {
388 base: ServerCompositionClipProps::new(),
389 left_inset: Cell::new(Default::default()),
390 top_inset: Cell::new(Default::default()),
391 right_inset: Cell::new(Default::default()),
392 bottom_inset: Cell::new(Default::default()),
393 }
394 }
395
396 pub fn base(&self) -> &ServerCompositionClipProps {
398 &self.base
399 }
400
401 pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
404 if type_id == TypeId::of::<Self>() {
405 return Some(self);
406 }
407 self.base.find_props(type_id)
408 }
409
410 pub fn id_of_left_inset_property() -> &'static CompositionPropertyOf<f32> {
412 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
413 PROPERTY.get_or_init(|| {
414 CompositionProperty::register::<ServerInsetClipProps, f32>(
415 "LeftInset",
416 "ServerInsetClip",
417 |obj| props_of::<ServerInsetClipProps>(obj).left_inset.get(),
418 |obj, v| props_of::<ServerInsetClipProps>(obj).left_inset.set(v),
419 Some(|obj| ExpressionVariant::create(props_of::<ServerInsetClipProps>(obj).left_inset.get())),
420 Some(|obj, v: ExpressionVariant| props_of::<ServerInsetClipProps>(obj).left_inset.set(v.cast_or_default::<f32>())),
421 )
422 })
423 }
424
425 pub fn left_inset(&self) -> f32 {
427 self.left_inset.get()
428 }
429
430 pub fn set_left_inset(&self, host: &dyn ServerInsetClipHooks, value: f32) {
431 self.left_inset.set(value);
432 host.remove_animation_for_property(Self::id_of_left_inset_property().base());
433 }
434
435 pub fn id_of_top_inset_property() -> &'static CompositionPropertyOf<f32> {
437 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
438 PROPERTY.get_or_init(|| {
439 CompositionProperty::register::<ServerInsetClipProps, f32>(
440 "TopInset",
441 "ServerInsetClip",
442 |obj| props_of::<ServerInsetClipProps>(obj).top_inset.get(),
443 |obj, v| props_of::<ServerInsetClipProps>(obj).top_inset.set(v),
444 Some(|obj| ExpressionVariant::create(props_of::<ServerInsetClipProps>(obj).top_inset.get())),
445 Some(|obj, v: ExpressionVariant| props_of::<ServerInsetClipProps>(obj).top_inset.set(v.cast_or_default::<f32>())),
446 )
447 })
448 }
449
450 pub fn top_inset(&self) -> f32 {
452 self.top_inset.get()
453 }
454
455 pub fn set_top_inset(&self, host: &dyn ServerInsetClipHooks, value: f32) {
456 self.top_inset.set(value);
457 host.remove_animation_for_property(Self::id_of_top_inset_property().base());
458 }
459
460 pub fn id_of_right_inset_property() -> &'static CompositionPropertyOf<f32> {
462 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
463 PROPERTY.get_or_init(|| {
464 CompositionProperty::register::<ServerInsetClipProps, f32>(
465 "RightInset",
466 "ServerInsetClip",
467 |obj| props_of::<ServerInsetClipProps>(obj).right_inset.get(),
468 |obj, v| props_of::<ServerInsetClipProps>(obj).right_inset.set(v),
469 Some(|obj| ExpressionVariant::create(props_of::<ServerInsetClipProps>(obj).right_inset.get())),
470 Some(|obj, v: ExpressionVariant| props_of::<ServerInsetClipProps>(obj).right_inset.set(v.cast_or_default::<f32>())),
471 )
472 })
473 }
474
475 pub fn right_inset(&self) -> f32 {
477 self.right_inset.get()
478 }
479
480 pub fn set_right_inset(&self, host: &dyn ServerInsetClipHooks, value: f32) {
481 self.right_inset.set(value);
482 host.remove_animation_for_property(Self::id_of_right_inset_property().base());
483 }
484
485 pub fn id_of_bottom_inset_property() -> &'static CompositionPropertyOf<f32> {
487 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
488 PROPERTY.get_or_init(|| {
489 CompositionProperty::register::<ServerInsetClipProps, f32>(
490 "BottomInset",
491 "ServerInsetClip",
492 |obj| props_of::<ServerInsetClipProps>(obj).bottom_inset.get(),
493 |obj, v| props_of::<ServerInsetClipProps>(obj).bottom_inset.set(v),
494 Some(|obj| ExpressionVariant::create(props_of::<ServerInsetClipProps>(obj).bottom_inset.get())),
495 Some(|obj, v: ExpressionVariant| props_of::<ServerInsetClipProps>(obj).bottom_inset.set(v.cast_or_default::<f32>())),
496 )
497 })
498 }
499
500 pub fn bottom_inset(&self) -> f32 {
502 self.bottom_inset.get()
503 }
504
505 pub fn set_bottom_inset(&self, host: &dyn ServerInsetClipHooks, value: f32) {
506 self.bottom_inset.set(value);
507 host.remove_animation_for_property(Self::id_of_bottom_inset_property().base());
508 }
509
510 pub fn deserialize_changes_core(
513 &self,
514 host: &dyn ServerInsetClipHooks,
515 reader: &mut BatchStreamReader<'_>,
516 committed_at: Duration,
517 ) {
518 self.base.deserialize_changes_core(host, reader, committed_at);
519 ServerInsetClipHooks::deserialize_changes_extra(host, reader);
520 let changed = reader.read::<InsetClipChangedFields>();
521 if changed.contains(InsetClipChangedFields::LEFT_INSET_ANIMATED) {
522 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
523 panic!("a batch starts an animation of `LeftInset` without an animation instance");
524 };
525 host.set_animated_value(
526 Self::id_of_left_inset_property().base(),
527 ExpressionVariant::create(self.left_inset.get()),
528 committed_at,
529 animation,
530 );
531 } else if changed.contains(InsetClipChangedFields::LEFT_INSET) {
532 let value = reader.read::<f32>();
533 self.set_left_inset(host, value);
534 }
535 if changed.contains(InsetClipChangedFields::TOP_INSET_ANIMATED) {
536 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
537 panic!("a batch starts an animation of `TopInset` without an animation instance");
538 };
539 host.set_animated_value(
540 Self::id_of_top_inset_property().base(),
541 ExpressionVariant::create(self.top_inset.get()),
542 committed_at,
543 animation,
544 );
545 } else if changed.contains(InsetClipChangedFields::TOP_INSET) {
546 let value = reader.read::<f32>();
547 self.set_top_inset(host, value);
548 }
549 if changed.contains(InsetClipChangedFields::RIGHT_INSET_ANIMATED) {
550 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
551 panic!("a batch starts an animation of `RightInset` without an animation instance");
552 };
553 host.set_animated_value(
554 Self::id_of_right_inset_property().base(),
555 ExpressionVariant::create(self.right_inset.get()),
556 committed_at,
557 animation,
558 );
559 } else if changed.contains(InsetClipChangedFields::RIGHT_INSET) {
560 let value = reader.read::<f32>();
561 self.set_right_inset(host, value);
562 }
563 if changed.contains(InsetClipChangedFields::BOTTOM_INSET_ANIMATED) {
564 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
565 panic!("a batch starts an animation of `BottomInset` without an animation instance");
566 };
567 host.set_animated_value(
568 Self::id_of_bottom_inset_property().base(),
569 ExpressionVariant::create(self.bottom_inset.get()),
570 committed_at,
571 animation,
572 );
573 } else if changed.contains(InsetClipChangedFields::BOTTOM_INSET) {
574 let value = reader.read::<f32>();
575 self.set_bottom_inset(host, value);
576 }
577 ServerInsetClipHooks::on_fields_deserialized(host, changed);
578 }
579
580 pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
583 if name == "LeftInset" {
584 return Some(Self::id_of_left_inset_property().base());
585 }
586 if name == "TopInset" {
587 return Some(Self::id_of_top_inset_property().base());
588 }
589 if name == "RightInset" {
590 return Some(Self::id_of_right_inset_property().base());
591 }
592 if name == "BottomInset" {
593 return Some(Self::id_of_bottom_inset_property().base());
594 }
595 ServerCompositionClipProps::get_composition_property(name)
596 }
597}