1use crate::collections::{FerroList, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs, ResetBehavior};
2use crate::media::{
3 can_value_affect_render, subscribe_invalidated, CacheMode, DrawingContext, FlowDirection, Geometry, IBrush, IEffect,
4 ITransform, RenderOptions, TextOptions, Transform,
5};
6use crate::rendering::composition::{CompositionDrawListVisual, CompositionVisual, Compositor};
7use crate::reactive::{Disposable, IDisposable};
8use crate::rendering::IPresentationSource;
9use crate::utilities::HandlerList;
10use crate::{
11 ferro_class, ferro_property, instantiate, AttachedProperty, DirectProperty, FerroObjectImpl,
12 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, ObjectType, Rect, Ref,
13 RelativePoint, StyledElement,
14 StyledElementImpl, StyledElementImplExt, StyledProperty, StyledPropertyOptions, Upcast,
15 VisualTreeAttachmentEventArgs, WeakRef,
16};
17use std::cell::{Cell, OnceCell, RefCell};
18use std::rc::Rc;
19
20#[repr(C)]
28pub struct Visual {
29 base: StyledElement,
30 bounds: Cell<Rect>,
31 presentation_source: RefCell<Option<Rc<dyn IPresentationSource>>>,
32 visual_parent: RefCell<Option<WeakRef<Visual>>>,
33 has_mirror_transform: Cell<bool>,
34 has_render_transform: Cell<bool>,
37 has_manipulation_transform: Cell<bool>,
40 is_effectively_visible: Cell<bool>,
41 has_non_uniform_z_index_children: Cell<bool>,
42 visual_children: OnceCell<FerroList<Ref<Visual>>>,
43 attached_to_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
44 detached_from_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
45 is_effectively_visible_changed: HandlerList<dyn Fn()>,
46 render_options: Cell<RenderOptions>,
47 text_options: Cell<TextOptions>,
48 composition_visual: RefCell<Option<CompositionDrawListVisual>>,
49 child_composition_visual: RefCell<Option<Rc<CompositionVisual>>>,
50 composition_extras: RefCell<Option<Box<CompositionExtras>>>,
54 render_transform_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
57 affects_render_subscriptions: RefCell<Vec<(&'static FerroProperty, Rc<dyn IDisposable>)>>,
58}
59
60#[derive(Default)]
63struct CompositionExtras {
64 translation: Option<crate::Vector3D>,
67 hide_requests: u32,
70 render_suppression_requests: u32,
73 show_requests: u32,
76 layer_visual: Option<Rc<CompositionVisual>>,
79 unloading_visuals: Vec<Rc<CompositionVisual>>,
82 facades: composition_facades::CompositionFacades,
86 lights: Option<FerroList<Ref<crate::media::XamlLight>>>,
89 lights_subscriptions: Vec<Rc<dyn IDisposable>>,
90 light_target_ids: Vec<String>,
93 light_target_subscription: Option<Rc<dyn IDisposable>>,
94 manipulation_transform: Option<crate::Matrix>,
96 theme_shadow: Option<ThemeShadowState>,
99}
100
101#[derive(Default)]
103struct ThemeShadowState {
104 popup: Option<WeakRef<Visual>>,
106 delegates_to_child: bool,
108 visual: Option<Rc<crate::rendering::composition::ThemeShadowVisual>>,
111}
112
113mod composition_facades;
114mod theme_shadow;
115#[cfg(test)]
116mod composition_facades_tests;
117
118pub use composition_facades::{ScalarTransition, Vector3Transition, Vector3TransitionComponents};
119
120ferro_class! {
121 Visual: StyledElement, virtuals VisualImpl: StyledElementImpl {
122 fn render(this, context: &mut DrawingContext);
124 fn on_attached_to_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
127 fn on_detached_from_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
130 fn on_attached_to_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
132 fn on_detached_from_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
134 fn on_visual_parent_changed(this, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>);
136 fn bypass_flow_direction_policies(this) -> bool;
139 fn invalidate_mirror_transform(this);
142 fn clip_to_bounds_radius(this) -> Option<crate::CornerRadius>;
146 fn custom_hit_test(this, point: crate::Point) -> Option<bool>;
151 fn custom_hit_test_geometry(this, geometry: &Ref<crate::media::Geometry>) -> Option<crate::media::IntersectionResult>;
154 fn ensure_initialized_for_visual_brush(this);
158 fn create_composition_visual(this, compositor: &Rc<Compositor>) -> CompositionDrawListVisual;
160 fn detach_from_compositor(this);
162 fn synchronize_composition_child_visuals(this);
165 fn synchronize_composition_properties(this);
167 }
168}
169
170crate::ferro_thread_local! {
171 static ROOTED_VISUAL_CHILDREN_COUNT: Cell<i32> = const { Cell::new(0) };
172}
173crate::ferro_class_info!(Visual { new: Visual::new });
174
175crate::ferro_thread_local! {
176 static GEOMETRY_VERSION: Cell<u64> = const { Cell::new(0) };
181}
182
183#[inline]
185pub(crate) fn geometry_version() -> u64 {
186 GEOMETRY_VERSION.with(Cell::get)
187}
188
189#[inline]
190fn bump_geometry_version() {
191 GEOMETRY_VERSION.with(|version| version.set(version.get().wrapping_add(1)));
192}
193
194crate::ferro_overrides! { impl FerroObjectImpl for Visual {
195 fn constructed(this: &Self) {
196 Self::parent_constructed(this);
197
198 this.disable_transitions();
200 }
201
202 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
203 Self::parent_on_property_changed(this, change);
204
205 if change.property() == Self::is_visible_property().as_property() {
206 let parent_state = this.visual_parent().is_none_or(|p| p.is_effectively_visible());
207 this.update_is_effectively_visible(parent_state);
208 } else if change.property() == Self::shadow_property().as_property() {
209 let old = change.get_old_value::<Option<Ref<crate::media::Shadow>>>().flatten();
210 let new = change.get_new_value::<Option<Ref<crate::media::Shadow>>>();
211 this.on_shadow_changed(old.as_ref(), new.as_ref());
212 } else if change.property() == crate::styling::ThemeVariant::actual_theme_variant_property().as_property() {
213 if this.may_cast_theme_shadow() {
215 this.invalidate_visual();
216 }
217 } else if change.property() == Self::flow_direction_property().as_property() {
218 this.invalidate_mirror_transform();
219 if let Some(children) = this.visual_children.get() {
220 for child in children.snapshot().iter() {
221 child.invalidate_mirror_transform();
222 }
223 }
224 }
225 }
226} }
227
228impl StyledElementImpl for Visual {
229 fn logical_children_collection_changed(this: &Self, e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>) {
230 Self::parent_logical_children_collection_changed(this, e);
231 if let Some(source) = this.presentation_source() {
232 source.renderer().recalculate_children(this);
233 }
234 }
235
236 fn on_templated_parent_control_theme_changed(this: &Self) {
237 Self::parent_on_templated_parent_control_theme_changed(this);
238 let templated_parent = this.templated_parent();
239 if let Some(children) = this.visual_children.get() {
240 for child in children.snapshot().iter() {
241 if child.templated_parent() == templated_parent {
242 child.on_templated_parent_control_theme_changed();
243 }
244 }
245 }
246 }
247}
248
249impl VisualImpl for Visual {
250 fn render(_this: &Self, _context: &mut DrawingContext) {}
251
252 fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
253 *this.presentation_source.borrow_mut() = Some(e.presentation_source().clone());
254 ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() + 1));
255
256 if let Some(render_transform) = this.render_transform() {
257 if let Some(mutable_transform) = render_transform.as_mutable_transform() {
258 let weak = this.to_ref().downgrade();
259 let subscription = mutable_transform.changed(Rc::new(move || {
260 if let Some(visual) = weak.upgrade() {
261 visual.render_transform_value_changed();
262 }
263 }));
264 this.set_render_transform_subscription(Some(subscription));
265 }
266 }
267
268 this.enable_transitions();
269 let renderer = e.presentation_source().renderer();
270 if let Some(compositor) = renderer.compositor() {
271 this.attach_to_compositor(&compositor);
272 }
273
274 this.invalidate_mirror_transform();
275 let parent = this.visual_parent();
276 this.update_is_effectively_visible(parent.as_ref().is_none_or(|p| p.is_effectively_visible()));
277 this.on_attached_to_visual_tree(e);
278 if !this.attached_to_visual_tree.is_empty() {
279 for (_, handler) in this.attached_to_visual_tree.snapshot().iter() {
280 handler(e);
281 }
282 }
283 this.invalidate_visual();
284
285 if let Some(parent) = &parent {
286 renderer.recalculate_children(parent);
287 if this.z_index() != 0 {
288 parent.has_non_uniform_z_index_children.set(true);
289 }
290 }
291
292 if let Some(children) = this.visual_children.get() {
293 let this_ref = this.to_ref();
294 for child in children.snapshot().iter() {
295 let attached_here = child
299 .presentation_source()
300 .is_some_and(|s| Rc::ptr_eq(&s, e.presentation_source()));
301 if !attached_here && child.visual_parent().is_some_and(|p| p == this_ref) {
302 child.on_attached_to_visual_tree_core(e);
303 }
304 }
305 }
306 }
307
308 fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
309 ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() - 1));
310
311 this.set_render_transform_subscription(None);
312
313 this.disable_transitions();
314 this.update_is_effectively_visible(true);
315 this.on_detached_from_visual_tree(e);
316 this.detach_from_compositor();
317 if !this.detached_from_visual_tree.is_empty() {
318 for (_, handler) in this.detached_from_visual_tree.snapshot().iter() {
319 handler(e);
320 }
321 }
322 if let Some(source) = this.presentation_source() {
323 source.renderer().add_dirty(this);
324 }
325 *this.presentation_source.borrow_mut() = None;
326
327 if let Some(children) = this.visual_children.get() {
328 for child in children.snapshot().iter() {
329 if child.presentation_source().is_some() {
332 child.on_detached_from_visual_tree_core(e);
333 }
334 }
335 }
336 }
337
338 fn on_attached_to_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
339
340 fn on_detached_from_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
341
342 fn on_visual_parent_changed(this: &Self, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>) {
343 this.raise_direct_property_changed(
344 Self::visual_parent_property(),
345 &old_parent.cloned(),
346 &new_parent.cloned(),
347 );
348 }
349
350 fn bypass_flow_direction_policies(_this: &Self) -> bool {
351 false
352 }
353
354 fn clip_to_bounds_radius(_this: &Self) -> Option<crate::CornerRadius> {
355 None
356 }
357
358 fn custom_hit_test(_this: &Self, _point: crate::Point) -> Option<bool> {
359 None
360 }
361
362 fn custom_hit_test_geometry(
363 _this: &Self,
364 _geometry: &Ref<crate::media::Geometry>,
365 ) -> Option<crate::media::IntersectionResult> {
366 None
367 }
368
369 fn ensure_initialized_for_visual_brush(_this: &Self) {}
370
371 fn create_composition_visual(this: &Self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
372 CompositionDrawListVisual::new(compositor, this)
373 }
374
375 fn detach_from_compositor(this: &Self) {
376 this.release_theme_shadow_visual();
378 let composition_visual = this.composition_visual.borrow_mut().take();
379 if let Some(composition_visual) = composition_visual {
380 if let Some(child) = this.child_composition_visual() {
381 composition_visual.children().remove(&child);
382 }
383 if let Some(layer) = this.composition_layer_visual() {
384 composition_visual.children().remove(&layer);
385 }
386 composition_visual.set_draw_list(None);
387 composition_visual.set_opacity_mask(None);
388 composition_visual.children().clear();
393 }
394 }
395
396 fn synchronize_composition_child_visuals(this: &Self) {
397 let Some(composition_visual) = this.composition_visual() else { return };
398 let composition_children = composition_visual.children();
399 let visual_children: Rc<Vec<Ref<Visual>>> =
400 this.visual_children_snapshot().unwrap_or_else(|| Rc::new(Vec::new()));
401
402 let mut sorted_children: Option<Vec<(Ref<Visual>, usize)>> = None;
403 if this.has_non_uniform_z_index_children() && visual_children.len() > 1 {
404 let mut sorted: Vec<(Ref<Visual>, usize)> =
405 visual_children.iter().cloned().enumerate().map(|(index, visual)| (visual, index)).collect();
406 sorted.sort_by(|lhs, rhs| lhs.0.z_index().cmp(&rhs.0.z_index()).then(lhs.1.cmp(&rhs.1)));
408 sorted_children = Some(sorted);
409 }
410
411 let mut child_visual = this.child_composition_visual();
412
413 if child_visual.as_ref().is_some_and(|child| !Rc::ptr_eq(child.compositor(), composition_visual.compositor())) {
415 child_visual = None;
416 }
417
418 let mut layer_visual = this.composition_layer_visual();
419 if layer_visual.as_ref().is_some_and(|layer| !Rc::ptr_eq(layer.compositor(), composition_visual.compositor())) {
420 layer_visual = None;
421 }
422
423 let unloading_visuals: Vec<Rc<CompositionVisual>> = this
424 .read_composition_extras(|extras| extras.unloading_visuals.clone())
425 .unwrap_or_default()
426 .into_iter()
427 .filter(|unloading| Rc::ptr_eq(unloading.compositor(), composition_visual.compositor()))
428 .collect();
429
430 let child_count = visual_children.len();
431 let child_at = |c: usize| -> &Ref<Visual> {
432 match &sorted_children {
433 Some(sorted) => &sorted[c].0,
434 None => &visual_children[c],
435 }
436 };
437
438 let shadows: Option<Vec<Option<Rc<CompositionVisual>>>> =
443 if visual_children.iter().any(|child| child.may_cast_theme_shadow()) {
444 Some((0..child_count).map(|c| child_at(c).ensure_theme_shadow_composition_visual()).collect())
445 } else {
446 None
447 };
448 let shadow_at = |c: usize| -> Option<&Rc<CompositionVisual>> {
449 shadows.as_ref().and_then(|shadows| shadows[c].as_ref())
450 };
451 let shadow_count = shadows.as_ref().map_or(0, |shadows| shadows.iter().flatten().count());
452
453 let mut expected_count = child_count + shadow_count + unloading_visuals.len();
454 if child_visual.is_some() {
455 expected_count += 1;
456 }
457 if layer_visual.is_some() {
458 expected_count += 1;
459 }
460
461 let same = |a: &Rc<CompositionVisual>, b: Option<CompositionDrawListVisual>| {
462 b.is_some_and(|b| Rc::ptr_eq(a, &b))
463 };
464
465 if composition_children.count() == expected_count {
466 let mut mismatch = false;
467 let mut index = 0;
468 for c in 0..child_count {
469 if let Some(shadow) = shadow_at(c) {
470 if !Rc::ptr_eq(&composition_children.get(index), shadow) {
471 mismatch = true;
472 break;
473 }
474 index += 1;
475 }
476 if !same(&composition_children.get(index), child_at(c).composition_visual()) {
477 mismatch = true;
478 break;
479 }
480 index += 1;
481 }
482
483 if !mismatch {
484 for unloading in &unloading_visuals {
485 if !Rc::ptr_eq(&composition_children.get(index), unloading) {
486 mismatch = true;
487 }
488 index += 1;
489 }
490 if let Some(child_visual) = &child_visual {
491 if !Rc::ptr_eq(&composition_children.get(index), child_visual) {
492 mismatch = true;
493 }
494 index += 1;
495 }
496 if let Some(layer_visual) = &layer_visual {
497 if !Rc::ptr_eq(&composition_children.get(index), layer_visual) {
498 mismatch = true;
499 }
500 }
501 }
502
503 if !mismatch {
504 return;
505 }
506 }
507
508 composition_children.clear();
509 for c in 0..child_count {
510 if let Some(shadow) = shadow_at(c) {
511 composition_children.add(shadow.clone());
512 }
513 if let Some(composition_child) = child_at(c).composition_visual() {
514 composition_children.add((*composition_child).clone());
515 }
516 }
517
518 for unloading in unloading_visuals {
519 composition_children.add(unloading);
520 }
521 if let Some(child_visual) = child_visual {
522 composition_children.add(child_visual);
523 }
524 if let Some(layer_visual) = layer_visual {
525 composition_children.add(layer_visual);
526 }
527 }
528
529 fn synchronize_composition_properties(this: &Self) {
530 let Some(comp) = this.composition_visual() else { return };
531 let bounds = this.bounds();
532
533 this.apply_composition_transitions(&comp, false);
537
538 comp.set_offset(crate::Vector3D::new(bounds.x, bounds.y, 0.0));
540 comp.set_size(crate::Vector::new(bounds.width, bounds.height));
541 comp.set_visible(
542 (this.is_visible() || this.is_shown_by_composition())
543 && !this.is_hidden_by_composition()
544 && !this.is_render_suppressed(),
545 );
546 comp.set_opacity(this.opacity() as f32);
547 comp.set_clip_to_bounds(this.clip_to_bounds());
548 comp.set_clip(this.clip().and_then(|clip| clip.platform_impl()));
549
550 let opacity_mask = this.opacity_mask();
551 let same_mask = match (comp.opacity_mask(), &opacity_mask) {
552 (Some(a), Some(b)) => *a == **b,
553 (None, None) => true,
554 _ => false,
555 };
556 if !same_mask {
557 comp.set_opacity_mask(opacity_mask);
558 }
559
560 let cache_mode = this.cache_mode().map(|cache_mode| cache_mode.get_for_compositor(comp.compositor()));
561 let same_cache_mode = match (comp.cache_mode(), &cache_mode) {
562 (Some(a), Some(b)) => Rc::ptr_eq(&a, b),
563 (None, None) => true,
564 _ => false,
565 };
566 if !same_cache_mode {
567 comp.set_cache_mode(cache_mode);
568 }
569
570 let effect = this.effect();
571 if !crate::media::EffectExtensions::effect_equals(comp.effect().as_deref(), effect.as_deref()) {
572 comp.set_effect(effect.as_ref().map(crate::media::EffectExtensions::to_immutable));
573 }
574
575 comp.set_render_options(this.render_options());
576 comp.set_text_options(this.text_options());
577
578 let mut render_transform = crate::Matrix::IDENTITY;
579
580 if this.has_mirror_transform() {
581 render_transform = crate::Matrix::new(-1.0, 0.0, 0.0, 1.0, bounds.width, 0.0);
582 }
583
584 if let Some(transform) = this.render_transform() {
585 let origin = this.render_transform_origin().to_pixels(crate::Size::new(bounds.width, bounds.height));
586 let offset = crate::Matrix::create_translation(origin.x, origin.y);
587 render_transform = render_transform * ((-offset) * transform.value() * offset);
588 }
589
590 if let Some(facade) = this.composition_facade_transform_matrix() {
593 render_transform = render_transform * facade;
594 }
595
596 if let Some(manipulation) = this.manipulation_transform() {
598 render_transform = render_transform * manipulation;
599 }
600
601 comp.set_transform_matrix(render_transform);
602
603 if let Some(translation) = this.composition_translation() {
604 comp.set_translation(translation);
605 }
606
607 this.synchronize_composition_facades(&comp);
608 this.apply_composition_transitions(&comp, true);
609
610 this.update_theme_shadow_visual();
612 }
613
614 fn invalidate_mirror_transform(this: &Self) {
615 let flow_direction = this.flow_direction();
616 let mut parent_flow_direction = FlowDirection::LeftToRight;
617 let bypass = this.bypass_flow_direction_policies();
618 let mut parent_bypass = false;
619
620 if let Some(parent) = this.visual_parent() {
621 parent_flow_direction = parent.flow_direction();
622 parent_bypass = parent.bypass_flow_direction_policies();
623 }
624
625 let this_should_be_mirrored = flow_direction == FlowDirection::RightToLeft && !bypass;
626 let parent_should_be_mirrored = parent_flow_direction == FlowDirection::RightToLeft && !parent_bypass;
627
628 this.set_has_mirror_transform(this_should_be_mirrored != parent_should_be_mirrored);
629 }
630}
631
632impl Drop for Visual {
641 fn drop(&mut self) {
642 for (_, subscription) in self.affects_render_subscriptions.get_mut().drain(..) {
643 subscription.dispose();
644 }
645 }
646}
647
648crate::ferro_properties! { impl Visual {
649 ferro_property!(
650 pub fn bounds_property() -> DirectProperty<Visual, Rect> {
652 FerroProperty::register_direct::<Visual, _>("Bounds", |o| o.bounds(), None, Rect::default())
653 }
654 );
655
656 ferro_property!(
657 pub fn clip_to_bounds_property() -> StyledProperty<bool> {
659 FerroProperty::register::<Visual, _>("ClipToBounds", false)
660 }
661 );
662
663 ferro_property!(
664 pub fn clip_property() -> StyledProperty<Option<Ref<Geometry>>> {
666 FerroProperty::register::<Visual, _>("Clip", None)
667 }
668 );
669
670 ferro_property!(
671 pub fn shadow_property() -> StyledProperty<Option<Ref<crate::media::Shadow>>> {
674 FerroProperty::register::<Visual, _>("Shadow", None)
675 }
676 );
677
678 ferro_property!(
679 pub fn translation_property() -> DirectProperty<Visual, crate::Vector3D> {
683 FerroProperty::register_direct::<Visual, _>(
684 "Translation",
685 |o| o.translation(),
686 Some(|o, value| o.set_translation(value)),
687 crate::Vector3D::default(),
688 )
689 }
690 );
691
692 ferro_property!(
693 pub fn cache_mode_property() -> StyledProperty<Option<Ref<CacheMode>>> {
695 FerroProperty::register::<Visual, _>("CacheMode", None)
696 }
697 );
698
699 ferro_property!(
700 pub fn effect_property() -> StyledProperty<Option<Rc<dyn IEffect>>> {
702 FerroProperty::register::<Visual, _>("Effect", None)
703 }
704 );
705
706 ferro_property!(
707 pub fn opacity_mask_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
709 FerroProperty::register::<Visual, _>("OpacityMask", None)
710 }
711 );
712
713 ferro_property!(
714 pub fn render_transform_property() -> StyledProperty<Option<Rc<dyn ITransform>>> {
716 FerroProperty::register::<Visual, _>("RenderTransform", None)
717 }
718 );
719
720 ferro_property!(
721 pub fn render_transform_origin_property() -> StyledProperty<RelativePoint> {
723 FerroProperty::register::<Visual, _>("RenderTransformOrigin", RelativePoint::CENTER)
724 }
725 );
726
727 ferro_property!(
728 pub fn is_visible_property() -> StyledProperty<bool> {
730 FerroProperty::register::<Visual, _>("IsVisible", true)
731 }
732 );
733
734 ferro_property!(
735 pub fn opacity_property() -> StyledProperty<f64> {
737 FerroProperty::register::<Visual, _>("Opacity", 1.0)
738 }
739 );
740
741 ferro_property!(
742 pub fn has_mirror_transform_property() -> DirectProperty<Visual, bool> {
744 FerroProperty::register_direct::<Visual, _>("HasMirrorTransform", |o| o.has_mirror_transform(), None, false)
745 }
746 );
747
748 ferro_property!(
749 pub fn flow_direction_property() -> AttachedProperty<FlowDirection> {
751 FerroProperty::register_attached_with::<Visual, Visual, _>(
752 "FlowDirection",
753 StyledPropertyOptions::new(FlowDirection::LeftToRight).inherits(true),
754 )
755 }
756 );
757
758 ferro_property!(
759 pub fn visual_parent_property() -> DirectProperty<Visual, Option<Ref<Visual>>> {
761 FerroProperty::register_direct::<Visual, _>("VisualParent", |o| o.visual_parent(), None, None)
762 }
763 );
764
765 ferro_property!(
766 pub fn z_index_property() -> StyledProperty<i32> {
768 FerroProperty::register::<Visual, _>("ZIndex", 0)
769 }
770 );
771} }
772
773impl Visual {
774 fn static_constructor() {
775 Self::affects_render::<Visual>(&[
776 Self::bounds_property().as_property(),
777 Self::clip_property().as_property(),
778 Self::clip_to_bounds_property().as_property(),
779 Self::is_visible_property().as_property(),
780 Self::opacity_property().as_property(),
781 Self::opacity_mask_property().as_property(),
782 Self::effect_property().as_property(),
783 Self::has_mirror_transform_property().as_property(),
784 ]);
785 Self::render_transform_property().changed().subscribe(Self::render_transform_changed);
786 Self::clip_to_bounds_property().changed().subscribe(|_| bump_geometry_version());
787 Self::render_transform_origin_property().changed().subscribe(|_| bump_geometry_version());
788 Self::z_index_property().changed().subscribe(|e| {
789 let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
790 let parent = sender.visual_parent();
791 if let Some(parent) = &parent {
792 if sender.z_index() != 0 {
793 parent.has_non_uniform_z_index_children.set(true);
794 }
795 }
796 sender.invalidate_visual();
797 if let Some(parent) = &parent {
798 if let Some(source) = parent.presentation_source() {
799 source.renderer().recalculate_children(parent);
800 }
801 }
802 });
803 }
804
805 pub fn construct() -> Self {
807 Self {
808 base: StyledElement::construct(),
809 bounds: Cell::new(Rect::default()),
810 presentation_source: RefCell::new(None),
811 visual_parent: RefCell::new(None),
812 has_mirror_transform: Cell::new(false),
813 has_render_transform: Cell::new(false),
814 has_manipulation_transform: Cell::new(false),
815 is_effectively_visible: Cell::new(true),
816 has_non_uniform_z_index_children: Cell::new(false),
817 visual_children: OnceCell::new(),
818 attached_to_visual_tree: HandlerList::new(),
819 detached_from_visual_tree: HandlerList::new(),
820 is_effectively_visible_changed: HandlerList::new(),
821 render_options: Cell::new(RenderOptions::default()),
822 text_options: Cell::new(TextOptions::default()),
823 composition_visual: RefCell::new(None),
824 child_composition_visual: RefCell::new(None),
825 composition_extras: RefCell::new(None),
826 render_transform_subscription: RefCell::new(None),
827 affects_render_subscriptions: RefCell::new(Vec::new()),
828 }
829 }
830
831 pub fn new() -> Ref<Self> {
832 instantiate(Self::construct())
833 }
834
835 pub fn attached_to_visual_tree(
839 &self,
840 handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
841 ) -> Rc<dyn IDisposable> {
842 let token = self.attached_to_visual_tree.add(Rc::new(handler));
843 let weak = self.to_ref().downgrade();
844 Disposable::create(move || {
845 if let Some(this) = weak.upgrade() {
846 this.attached_to_visual_tree.remove(token);
847 }
848 })
849 }
850
851 pub fn detached_from_visual_tree(
853 &self,
854 handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
855 ) -> Rc<dyn IDisposable> {
856 let token = self.detached_from_visual_tree.add(Rc::new(handler));
857 let weak = self.to_ref().downgrade();
858 Disposable::create(move || {
859 if let Some(this) = weak.upgrade() {
860 this.detached_from_visual_tree.remove(token);
861 }
862 })
863 }
864
865 pub fn is_effectively_visible_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
867 let token = self.is_effectively_visible_changed.add(Rc::new(handler));
868 let weak = self.to_ref().downgrade();
869 Disposable::create(move || {
870 if let Some(this) = weak.upgrade() {
871 this.is_effectively_visible_changed.remove(token);
872 }
873 })
874 }
875
876 #[inline]
880 pub fn bounds(&self) -> Rect {
881 self.bounds.get()
882 }
883
884 pub fn set_bounds(&self, value: Rect) {
886 if self.bounds.get() != value {
887 bump_geometry_version();
888 }
889 self.set_and_raise_cell(Self::bounds_property(), &self.bounds, value);
890 }
891
892 pub fn clip_to_bounds(&self) -> bool {
894 self.get_value(Self::clip_to_bounds_property())
895 }
896
897 pub fn set_clip_to_bounds(&self, value: bool) {
898 self.set_value(Self::clip_to_bounds_property(), value)
899 }
900
901 pub fn clip(&self) -> Option<Ref<Geometry>> {
903 self.get_value(Self::clip_property())
904 }
905
906 pub fn set_clip(&self, value: impl Into<Nullable<Geometry>>) {
907 self.set_value(Self::clip_property(), value.into().0)
908 }
909
910 pub fn cache_mode(&self) -> Option<Ref<CacheMode>> {
912 self.get_value(Self::cache_mode_property())
913 }
914
915 pub fn set_cache_mode(&self, value: impl Into<Nullable<CacheMode>>) {
916 self.set_value(Self::cache_mode_property(), value.into().0)
917 }
918
919 pub fn effect(&self) -> Option<Rc<dyn IEffect>> {
921 self.get_value(Self::effect_property())
922 }
923
924 pub fn set_effect(&self, value: Option<Rc<dyn IEffect>>) {
925 self.set_value(Self::effect_property(), value)
926 }
927
928 pub fn opacity_mask(&self) -> Option<Rc<dyn IBrush>> {
930 self.get_value(Self::opacity_mask_property())
931 }
932
933 pub fn set_opacity_mask(&self, value: Option<Rc<dyn IBrush>>) {
934 self.set_value(Self::opacity_mask_property(), value)
935 }
936
937 pub fn render_transform(&self) -> Option<Rc<dyn ITransform>> {
939 self.get_value(Self::render_transform_property())
940 }
941
942 pub fn set_render_transform(&self, value: Option<Rc<dyn ITransform>>) {
943 self.set_value(Self::render_transform_property(), value)
944 }
945
946 pub fn render_transform_origin(&self) -> RelativePoint {
948 self.get_value(Self::render_transform_origin_property())
949 }
950
951 pub fn set_render_transform_origin(&self, value: RelativePoint) {
952 self.set_value(Self::render_transform_origin_property(), value)
953 }
954
955 #[inline]
957 pub fn is_effectively_visible(&self) -> bool {
958 self.is_effectively_visible.get()
959 }
960
961 pub fn is_visible(&self) -> bool {
963 self.get_value(Self::is_visible_property())
964 }
965
966 pub fn set_is_visible(&self, value: bool) {
967 self.set_value(Self::is_visible_property(), value)
968 }
969
970 pub fn opacity(&self) -> f64 {
972 self.get_value(Self::opacity_property())
973 }
974
975 pub fn set_opacity(&self, value: f64) {
976 self.set_value(Self::opacity_property(), value)
977 }
978
979 #[inline]
982 pub(crate) fn has_render_transform(&self) -> bool {
983 self.has_render_transform.get()
984 }
985
986 pub fn manipulation_transform(&self) -> Option<crate::Matrix> {
993 if !self.has_manipulation_transform.get() {
994 return None;
995 }
996 self.read_composition_extras(|extras| extras.manipulation_transform).flatten()
997 }
998
999 pub fn set_manipulation_transform(&self, value: Option<crate::Matrix>) {
1002 let value = value.filter(|matrix| !matrix.is_identity());
1003 if self.manipulation_transform() == value {
1004 return;
1005 }
1006 self.has_manipulation_transform.set(value.is_some());
1007 self.with_composition_extras(|extras| extras.manipulation_transform = value);
1008 bump_geometry_version();
1009 self.invalidate_visual();
1010 }
1011
1012 #[inline]
1015 pub fn has_mirror_transform(&self) -> bool {
1016 self.has_mirror_transform.get()
1017 }
1018
1019 pub fn set_has_mirror_transform(&self, value: bool) {
1021 self.set_and_raise_cell(Self::has_mirror_transform_property(), &self.has_mirror_transform, value);
1022 }
1023
1024 pub fn flow_direction(&self) -> FlowDirection {
1026 self.get_value(Self::flow_direction_property())
1027 }
1028
1029 pub fn set_flow_direction(&self, value: FlowDirection) {
1030 self.set_value(Self::flow_direction_property(), value)
1031 }
1032
1033 pub fn get_flow_direction(visual: &Visual) -> FlowDirection {
1035 visual.get_value(Self::flow_direction_property())
1036 }
1037
1038 pub fn set_flow_direction_on(visual: &Visual, value: FlowDirection) {
1040 visual.set_value(Self::flow_direction_property(), value)
1041 }
1042
1043 pub fn z_index(&self) -> i32 {
1049 self.get_value(Self::z_index_property())
1050 }
1051
1052 pub fn set_z_index(&self, value: i32) {
1053 self.set_value(Self::z_index_property(), value)
1054 }
1055
1056 pub fn visual_children(&self) -> &FerroList<Ref<Visual>> {
1058 self.visual_children.get_or_init(|| {
1059 let list = FerroList::new();
1060 list.set_reset_behavior(ResetBehavior::Remove);
1061 let weak = self.to_ref().downgrade();
1062 let validator_owner = weak.clone();
1063 list.set_validate(Some(Rc::new(move |item: &Ref<Visual>| {
1064 if let Some(parent) = item.visual_parent() {
1065 let owner = validator_owner.upgrade();
1066 panic!(
1067 "The control {:?} already has a visual parent {:?} while trying to add it as a child of {:?}.",
1068 item, parent, owner
1069 );
1070 }
1071 })));
1072 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>| {
1073 if let Some(this) = weak.upgrade() {
1074 this.visual_children_changed(e);
1075 }
1076 }));
1077 list
1078 })
1079 }
1080
1081 #[inline]
1083 pub fn visual_children_count(&self) -> usize {
1084 self.visual_children.get().map_or(0, FerroList::count)
1085 }
1086
1087 #[inline]
1089 pub fn visual_children_snapshot(&self) -> Option<Rc<Vec<Ref<Visual>>>> {
1090 self.visual_children.get().map(FerroList::snapshot)
1091 }
1092
1093 #[inline]
1095 pub fn presentation_source(&self) -> Option<Rc<dyn IPresentationSource>> {
1096 self.presentation_source.borrow().clone()
1097 }
1098
1099 pub fn visual_root(&self) -> Option<Ref<Visual>> {
1101 self.presentation_source().and_then(|s| s.root_visual())
1102 }
1103
1104 #[inline]
1107 pub(crate) fn render_options(&self) -> RenderOptions {
1108 self.render_options.get()
1109 }
1110
1111 pub(crate) fn set_render_options(&self, value: RenderOptions) {
1112 self.render_options.set(value);
1113 self.invalidate_visual();
1114 }
1115
1116 #[inline]
1119 pub(crate) fn text_options(&self) -> TextOptions {
1120 self.text_options.get()
1121 }
1122
1123 pub(crate) fn set_text_options(&self, value: TextOptions) {
1124 self.text_options.set(value);
1125 self.invalidate_visual();
1126 }
1127
1128 pub fn composition_visual(&self) -> Option<CompositionDrawListVisual> {
1131 self.composition_visual.borrow().clone()
1132 }
1133
1134 pub fn child_composition_visual(&self) -> Option<Rc<CompositionVisual>> {
1137 self.child_composition_visual.borrow().clone()
1138 }
1139
1140 pub fn set_child_composition_visual(&self, value: Option<Rc<CompositionVisual>>) {
1141 *self.child_composition_visual.borrow_mut() = value;
1142 }
1143
1144 fn with_composition_extras<R>(&self, f: impl FnOnce(&mut CompositionExtras) -> R) -> R {
1145 let mut extras = self.composition_extras.borrow_mut();
1146 f(extras.get_or_insert_with(Default::default))
1147 }
1148
1149 fn read_composition_extras<R>(&self, f: impl FnOnce(&CompositionExtras) -> R) -> Option<R> {
1150 self.composition_extras.borrow().as_deref().map(f)
1151 }
1152
1153 pub fn lights(&self) -> FerroList<Ref<crate::media::XamlLight>> {
1158 if let Some(lights) = self.read_composition_extras(|extras| extras.lights.clone()).flatten() {
1159 return lights;
1160 }
1161 let lights: FerroList<Ref<crate::media::XamlLight>> = FerroList::new();
1162 let subscriptions = crate::media::xaml_light::track_lights(self, &lights);
1163 self.with_composition_extras(|extras| {
1164 extras.lights = Some(lights.clone());
1165 extras.lights_subscriptions = subscriptions;
1166 });
1167 lights
1168 }
1169
1170 pub fn has_lights(&self) -> bool {
1172 self.read_composition_extras(|extras| extras.lights.as_ref().is_some_and(|lights| lights.count() > 0))
1173 .unwrap_or(false)
1174 }
1175
1176 pub fn light_target_ids(&self) -> Vec<String> {
1178 self.read_composition_extras(|extras| extras.light_target_ids.clone()).unwrap_or_default()
1179 }
1180
1181 pub(crate) fn add_light_target_id(&self, id: &str) -> bool {
1184 let added = self.with_composition_extras(|extras| {
1185 if extras.light_target_ids.iter().any(|existing| existing == id) {
1186 return false;
1187 }
1188 extras.light_target_ids.push(id.to_owned());
1189 true
1190 });
1191 if added && self.read_composition_extras(|extras| extras.light_target_subscription.is_none()).unwrap_or(true) {
1192 let subscription = crate::media::xaml_light::track_light_target(self);
1193 self.with_composition_extras(|extras| extras.light_target_subscription = Some(subscription));
1194 }
1195 added
1196 }
1197
1198 pub(crate) fn remove_light_target_id(&self, id: &str) -> bool {
1201 self.read_composition_extras(|extras| extras.light_target_ids.iter().any(|existing| existing == id))
1202 .unwrap_or(false)
1203 && self.with_composition_extras(|extras| {
1204 extras.light_target_ids.retain(|existing| existing != id);
1205 true
1206 })
1207 }
1208
1209 pub fn translation(&self) -> crate::Vector3D {
1217 self.composition_translation().unwrap_or_default()
1218 }
1219
1220 pub fn set_translation(&self, value: crate::Vector3D) {
1221 let old = self.translation();
1222 self.with_composition_extras(|extras| extras.translation = Some(value));
1223 self.invalidate_visual();
1224 if old != value {
1225 self.raise_direct_property_changed(Self::translation_property(), &old, &value);
1226 }
1227 }
1228
1229 fn composition_translation(&self) -> Option<crate::Vector3D> {
1230 self.read_composition_extras(|extras| extras.translation).flatten()
1231 }
1232
1233 pub fn is_hidden_by_composition(&self) -> bool {
1236 self.read_composition_extras(|extras| extras.hide_requests > 0).unwrap_or(false)
1237 }
1238
1239 pub fn add_composition_hide_request(&self) {
1245 self.with_composition_extras(|extras| extras.hide_requests += 1);
1246 self.invalidate_visual();
1247 }
1248
1249 pub fn remove_composition_hide_request(&self) {
1251 self.with_composition_extras(|extras| extras.hide_requests = extras.hide_requests.saturating_sub(1));
1252 self.invalidate_visual();
1253 }
1254
1255 pub fn is_render_suppressed(&self) -> bool {
1260 self.read_composition_extras(|extras| extras.render_suppression_requests > 0)
1261 .unwrap_or(false)
1262 }
1263
1264 pub fn add_render_suppression_request(&self) {
1268 let changed = self.with_composition_extras(|extras| {
1269 let previous = extras.render_suppression_requests;
1270 extras.render_suppression_requests = previous
1271 .checked_add(1)
1272 .expect("Render suppression request count exhausted");
1273 previous == 0
1274 });
1275 if changed {
1276 self.invalidate_visual();
1277 }
1278 }
1279
1280 pub fn remove_render_suppression_request(&self) {
1283 if !self.is_render_suppressed() {
1284 return;
1285 }
1286 let changed = self.with_composition_extras(|extras| {
1287 extras.render_suppression_requests -= 1;
1288 extras.render_suppression_requests == 0
1289 });
1290 if changed {
1291 self.invalidate_visual();
1292 }
1293 }
1294
1295 pub fn is_shown_by_composition(&self) -> bool {
1299 self.read_composition_extras(|extras| extras.show_requests > 0).unwrap_or(false)
1300 }
1301
1302 pub fn add_composition_show_request(&self) {
1307 self.with_composition_extras(|extras| extras.show_requests += 1);
1308 self.invalidate_visual();
1309 }
1310
1311 pub fn remove_composition_show_request(&self) {
1313 self.with_composition_extras(|extras| extras.show_requests = extras.show_requests.saturating_sub(1));
1314 self.invalidate_visual();
1315 }
1316
1317 pub fn composition_layer_visual(&self) -> Option<Rc<CompositionVisual>> {
1321 self.read_composition_extras(|extras| extras.layer_visual.clone()).flatten()
1322 }
1323
1324 pub(crate) fn add_composition_unloading_visual(&self, visual: Rc<CompositionVisual>) {
1328 self.with_composition_extras(|extras| extras.unloading_visuals.push(visual));
1329 if let Some(source) = self.presentation_source() {
1330 source.renderer().recalculate_children(self);
1331 }
1332 }
1333
1334 pub(crate) fn remove_composition_unloading_visual(&self, visual: &Rc<CompositionVisual>) {
1336 self.with_composition_extras(|extras| extras.unloading_visuals.retain(|v| !Rc::ptr_eq(v, visual)));
1337 if let Some(own) = self.composition_visual() {
1338 own.children().remove(visual);
1339 }
1340 if let Some(source) = self.presentation_source() {
1341 source.renderer().recalculate_children(self);
1342 }
1343 }
1344
1345 pub fn set_composition_layer_visual(&self, value: Option<Rc<CompositionVisual>>) {
1347 let old = self.with_composition_extras(|extras| std::mem::replace(&mut extras.layer_visual, value.clone()));
1348 if let (Some(old), Some(own)) = (&old, self.composition_visual()) {
1349 if !value.as_ref().is_some_and(|new| Rc::ptr_eq(new, old)) {
1350 own.children().remove(old);
1351 }
1352 }
1353 if let Some(source) = self.presentation_source() {
1354 source.renderer().recalculate_children(self);
1355 }
1356 }
1357
1358 pub fn attach_to_compositor(&self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
1361 let current = self.composition_visual();
1362 match current {
1363 Some(current) if Rc::ptr_eq(current.compositor(), compositor) => current,
1364 _ => {
1365 let created = self.create_composition_visual(compositor);
1366 *self.composition_visual.borrow_mut() = Some(created.clone());
1367 self.composition_visual_created(&created);
1368 created
1369 }
1370 }
1371 }
1372
1373 pub fn has_non_uniform_z_index_children(&self) -> bool {
1375 self.has_non_uniform_z_index_children.get()
1376 }
1377
1378 #[inline]
1380 pub fn is_attached_to_visual_tree(&self) -> bool {
1381 self.presentation_source.borrow().is_some()
1382 }
1383
1384 #[inline]
1386 pub fn visual_parent(&self) -> Option<Ref<Visual>> {
1387 self.visual_parent.borrow().as_ref().and_then(WeakRef::upgrade)
1388 }
1389
1390 pub(crate) fn rooted_visual_children_count() -> i32 {
1393 ROOTED_VISUAL_CHILDREN_COUNT.with(Cell::get)
1394 }
1395
1396 pub fn invalidate_visual(&self) {
1400 if let Some(source) = self.presentation_source() {
1401 source.renderer().add_dirty(self);
1402 }
1403 }
1404
1405 pub fn affects_render<T: ObjectType + Upcast<Visual>>(properties: &[&'static FerroProperty]) {
1412 for property in properties {
1413 if can_value_affect_render(property.property_type()) {
1414 property.changed().subscribe(|e| {
1415 if let Some(sender) = e.sender().downcast_ref::<T>() {
1416 let visual: &Visual = sender.upcast();
1417 visual.update_affects_render_subscription(e);
1418 visual.invalidate_visual();
1419 }
1420 });
1421 } else {
1422 property.changed().subscribe(|e| {
1423 if let Some(sender) = e.sender().downcast_ref::<T>() {
1424 let visual: &Visual = sender.upcast();
1425 visual.invalidate_visual();
1426 }
1427 });
1428 }
1429 }
1430 }
1431
1432 fn update_affects_render_subscription(&self, e: &FerroPropertyChangedEventArgs<'_>) {
1435 let property = e.property();
1436 let old = {
1437 let mut subscriptions = self.affects_render_subscriptions.borrow_mut();
1438 let position = subscriptions.iter().position(|(p, _)| *p == property);
1439 position.map(|position| subscriptions.swap_remove(position))
1440 };
1441 if let Some((_, subscription)) = old {
1442 subscription.dispose();
1443 }
1444
1445 let weak = self.to_ref().downgrade();
1446 let subscription = subscribe_invalidated(
1447 e.new_value(),
1448 Rc::new(move || {
1449 if let Some(target) = weak.upgrade() {
1450 target.invalidate_visual();
1451 }
1452 }),
1453 );
1454 if let Some(subscription) = subscription {
1455 self.affects_render_subscriptions.borrow_mut().push((property, subscription));
1456 }
1457 }
1458
1459 fn render_transform_changed(e: &FerroPropertyChangedEventArgs<'_>) {
1463 let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
1464 let (_, new_value) = e.get_old_and_new_value::<Option<Rc<dyn ITransform>>>();
1465 sender.has_render_transform.set(new_value.is_some());
1466 bump_geometry_version();
1467
1468 if sender.visual_root().is_some() {
1469
1470 let subscription = new_value.as_ref().and_then(|new_value| {
1472 let new_transform = new_value.as_any().downcast_ref::<Transform>()?;
1473 let weak = sender.to_ref().downgrade();
1474 Some(new_transform.changed(move || {
1475 if let Some(visual) = weak.upgrade() {
1476 visual.render_transform_value_changed();
1477 }
1478 }))
1479 });
1480 sender.set_render_transform_subscription(subscription);
1481
1482 sender.invalidate_visual();
1483 }
1484 }
1485
1486 fn render_transform_value_changed(&self) {
1488 bump_geometry_version();
1489 self.invalidate_visual();
1490 }
1491
1492 fn set_render_transform_subscription(&self, value: Option<Rc<dyn IDisposable>>) {
1493 let previous = self.render_transform_subscription.replace(value);
1494 if let Some(previous) = previous {
1495 previous.dispose();
1496 }
1497 }
1498
1499 pub fn set_presentation_source_for_root_visual(&self, presentation_source: Option<Rc<dyn IPresentationSource>>) {
1501 let current = self.presentation_source();
1502 let same = match (¤t, &presentation_source) {
1503 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
1504 (None, None) => true,
1505 _ => false,
1506 };
1507 if same {
1508 return;
1509 }
1510
1511 if let Some(current) = current {
1512 assert!(
1513 presentation_source.is_none(),
1514 "Visual is already attached to a presentation source. Only one presentation source can be \
1515 attached to a visual tree."
1516 );
1517 self.on_detached_from_visual_tree_core(&VisualTreeAttachmentEventArgs::new(None, current));
1518 }
1519
1520 *self.presentation_source.borrow_mut() = presentation_source.clone();
1521
1522 if let Some(source) = presentation_source {
1523 let e = VisualTreeAttachmentEventArgs::new(None, source);
1524 self.on_attached_to_visual_tree_core(&e);
1525 }
1526 }
1527
1528 fn update_is_effectively_visible(&self, parent_state: bool) {
1529 let is_effectively_visible = parent_state && self.is_visible();
1530 if self.is_effectively_visible.get() == is_effectively_visible {
1531 return;
1532 }
1533 self.is_effectively_visible.set(is_effectively_visible);
1534 if !self.is_effectively_visible_changed.is_empty() {
1535 for (_, handler) in self.is_effectively_visible_changed.snapshot().iter() {
1536 handler();
1537 }
1538 }
1539 if let Some(children) = self.visual_children.get() {
1541 for child in children.snapshot().iter() {
1542 child.update_is_effectively_visible(is_effectively_visible);
1543 }
1544 }
1545 }
1546
1547 fn set_visual_parent(&self, value: Option<&Ref<Visual>>) {
1548 let old = self.visual_parent();
1549 if old.as_ref() == value {
1550 return;
1551 }
1552 *self.visual_parent.borrow_mut() = value.map(Ref::downgrade);
1553 bump_geometry_version();
1554
1555 if let (Some(source), Some(old)) = (self.presentation_source(), &old) {
1556 let e = VisualTreeAttachmentEventArgs::new(Some(old.clone()), source);
1557 self.on_detached_from_visual_tree_core(&e);
1558 }
1559
1560 if let Some(parent) = value {
1561 if let Some(source) = parent.presentation_source() {
1562 let e = VisualTreeAttachmentEventArgs::new(Some(parent.clone()), source);
1563 self.on_attached_to_visual_tree_core(&e);
1564 }
1565 }
1566
1567 self.on_visual_parent_changed(old.as_ref(), value);
1568 }
1569
1570 fn visual_children_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>) {
1571 match e.action {
1572 NotifyCollectionChangedAction::Add => {
1573 let this = self.to_ref();
1574 for visual in e.new_items {
1575 visual.set_visual_parent(Some(&this));
1576 }
1577 }
1578 NotifyCollectionChangedAction::Remove => {
1579 for visual in e.old_items {
1580 visual.set_visual_parent(None);
1581 }
1582 }
1583 NotifyCollectionChangedAction::Replace => {
1584 for visual in e.old_items {
1585 visual.set_visual_parent(None);
1586 }
1587 let this = self.to_ref();
1588 for visual in e.new_items {
1589 visual.set_visual_parent(Some(&this));
1590 }
1591 }
1592 NotifyCollectionChangedAction::Move | NotifyCollectionChangedAction::Reset => {}
1593 }
1594 }
1595}