Skip to main content

ferroui_base/
visual.rs

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/// Base class for controls that provides rendering and related visual
21/// properties.
22///
23/// The visual class represents elements that have a visual on-screen
24/// representation and stores all the information needed for a renderer to
25/// render the control. To traverse the visual tree, use the methods in the
26/// `visual_tree` module.
27#[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    // Deviation (DEVIATIONS.md, Visual tree): whether `RenderTransform` has a value, kept by its
35    // change handler, so that the transforms of the visual tree skip the property read.
36    has_render_transform: Cell<bool>,
37    is_effectively_visible: Cell<bool>,
38    has_non_uniform_z_index_children: Cell<bool>,
39    visual_children: OnceCell<FerroList<Ref<Visual>>>,
40    attached_to_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
41    detached_from_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
42    is_effectively_visible_changed: HandlerList<dyn Fn()>,
43    render_options: Cell<RenderOptions>,
44    text_options: Cell<TextOptions>,
45    composition_visual: RefCell<Option<CompositionDrawListVisual>>,
46    child_composition_visual: RefCell<Option<Rc<CompositionVisual>>>,
47    /// The subscription to the `changed` notification of the render
48    /// transform, held while the visual is attached to a visual tree.
49    render_transform_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
50    affects_render_subscriptions: RefCell<Vec<(&'static FerroProperty, Rc<dyn IDisposable>)>>,
51}
52
53ferro_class! {
54    Visual: StyledElement, virtuals VisualImpl: StyledElementImpl {
55        /// Renders the visual to a drawing context.
56        fn render(this, context: &mut DrawingContext);
57        /// Calls the `on_attached_to_visual_tree` method for this control and
58        /// all of its visual descendants.
59        fn on_attached_to_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
60        /// Calls the `on_detached_from_visual_tree` method for this control
61        /// and all of its visual descendants.
62        fn on_detached_from_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
63        /// Called when the control is added to a rooted visual tree.
64        fn on_attached_to_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
65        /// Called when the control is removed from a rooted visual tree.
66        fn on_detached_from_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
67        /// Called when the control's visual parent changes.
68        fn on_visual_parent_changed(this, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>);
69        /// Whether the control ignores the flow direction policies, i.e. is
70        /// never mirrored.
71        fn bypass_flow_direction_policies(this) -> bool;
72        /// Computes the `has_mirror_transform` value according to the flow
73        /// direction of this visual and its parent.
74        fn invalidate_mirror_transform(this);
75        /// The corner radius the visual clips its content with when
76        /// `clip_to_bounds` is set; `None` for a plain rectangular clip.
77        /// (Upstream: the `IVisualWithRoundRectClip` interface.)
78        fn clip_to_bounds_radius(this) -> Option<crate::CornerRadius>;
79        /// The custom hit test of the visual at a point in its own
80        /// coordinates; `None` when the visual has none and is hit by what
81        /// it occupies or draws. (Upstream: the `ICustomHitTest` interface;
82        /// a class implementing it overrides this and the geometry form.)
83        fn custom_hit_test(this, point: crate::Point) -> Option<bool>;
84        /// The custom hit test of the visual against a geometry in its own
85        /// coordinates; `None` when the visual has none.
86        fn custom_hit_test_geometry(this, geometry: &Ref<crate::media::Geometry>) -> Option<crate::media::IntersectionResult>;
87        /// Ensures that the visual is ready to use as the visual in a
88        /// visual brush. (Upstream: the `IVisualBrushInitialize` interface;
89        /// a class implementing it overrides this.)
90        fn ensure_initialized_for_visual_brush(this);
91        /// Creates the composition visual of the visual on `compositor`.
92        fn create_composition_visual(this, compositor: &Rc<Compositor>) -> CompositionDrawListVisual;
93        /// Releases the composition visual of the visual.
94        fn detach_from_compositor(this);
95        /// Makes the children of the composition visual match the visual
96        /// children (in Z order) and the child composition visual.
97        fn synchronize_composition_child_visuals(this);
98        /// Copies the properties of the visual to its composition visual.
99        fn synchronize_composition_properties(this);
100    }
101}
102
103crate::ferro_thread_local! {
104    static ROOTED_VISUAL_CHILDREN_COUNT: Cell<i32> = const { Cell::new(0) };
105}
106crate::ferro_class_info!(Visual { new: Visual::new });
107
108crate::ferro_thread_local! {
109    // Not upstream: a version of what the effective viewports of the visuals are computed from (the
110    // bounds, the visual parents, `ClipToBounds`, `RenderTransform` and `RenderTransformOrigin` of
111    // the visuals of the thread), which the layout manager reads to reuse the viewports of shared
112    // ancestors within one pass (DEVIATIONS.md, Layout).
113    static GEOMETRY_VERSION: Cell<u64> = const { Cell::new(0) };
114}
115
116/// The version of the geometry of the visuals of the thread (see `GEOMETRY_VERSION`).
117#[inline]
118pub(crate) fn geometry_version() -> u64 {
119    GEOMETRY_VERSION.with(Cell::get)
120}
121
122#[inline]
123fn bump_geometry_version() {
124    GEOMETRY_VERSION.with(|version| version.set(version.get().wrapping_add(1)));
125}
126
127crate::ferro_overrides! { impl FerroObjectImpl for Visual {
128    fn constructed(this: &Self) {
129        Self::parent_constructed(this);
130
131        // Disable transitions until we're added to the visual tree.
132        this.disable_transitions();
133    }
134
135    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
136        Self::parent_on_property_changed(this, change);
137
138        if change.property() == Self::is_visible_property().as_property() {
139            let parent_state = this.visual_parent().is_none_or(|p| p.is_effectively_visible());
140            this.update_is_effectively_visible(parent_state);
141        } else if change.property() == Self::flow_direction_property().as_property() {
142            this.invalidate_mirror_transform();
143            if let Some(children) = this.visual_children.get() {
144                for child in children.snapshot().iter() {
145                    child.invalidate_mirror_transform();
146                }
147            }
148        }
149    }
150} }
151
152impl StyledElementImpl for Visual {
153    fn logical_children_collection_changed(this: &Self, e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>) {
154        Self::parent_logical_children_collection_changed(this, e);
155        if let Some(source) = this.presentation_source() {
156            source.renderer().recalculate_children(this);
157        }
158    }
159
160    fn on_templated_parent_control_theme_changed(this: &Self) {
161        Self::parent_on_templated_parent_control_theme_changed(this);
162        let templated_parent = this.templated_parent();
163        if let Some(children) = this.visual_children.get() {
164            for child in children.snapshot().iter() {
165                if child.templated_parent() == templated_parent {
166                    child.on_templated_parent_control_theme_changed();
167                }
168            }
169        }
170    }
171}
172
173impl VisualImpl for Visual {
174    fn render(_this: &Self, _context: &mut DrawingContext) {}
175
176    fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
177        *this.presentation_source.borrow_mut() = Some(e.presentation_source().clone());
178        ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() + 1));
179
180        if let Some(render_transform) = this.render_transform() {
181            if let Some(mutable_transform) = render_transform.as_mutable_transform() {
182                let weak = this.to_ref().downgrade();
183                let subscription = mutable_transform.changed(Rc::new(move || {
184                    if let Some(visual) = weak.upgrade() {
185                        visual.render_transform_value_changed();
186                    }
187                }));
188                this.set_render_transform_subscription(Some(subscription));
189            }
190        }
191
192        this.enable_transitions();
193        let renderer = e.presentation_source().renderer();
194        if let Some(compositor) = renderer.compositor() {
195            this.attach_to_compositor(&compositor);
196        }
197
198        this.invalidate_mirror_transform();
199        let parent = this.visual_parent();
200        this.update_is_effectively_visible(parent.as_ref().is_none_or(|p| p.is_effectively_visible()));
201        this.on_attached_to_visual_tree(e);
202        if !this.attached_to_visual_tree.is_empty() {
203            for (_, handler) in this.attached_to_visual_tree.snapshot().iter() {
204                handler(e);
205            }
206        }
207        this.invalidate_visual();
208
209        if let Some(parent) = &parent {
210            renderer.recalculate_children(parent);
211            if this.z_index() != 0 {
212                parent.has_non_uniform_z_index_children.set(true);
213            }
214        }
215
216        if let Some(children) = this.visual_children.get() {
217            let this_ref = this.to_ref();
218            for child in children.snapshot().iter() {
219                // An event handler may have modified the children: skip a
220                // child which has already been attached, or which has been
221                // removed from this visual (this is a snapshot).
222                let attached_here = child
223                    .presentation_source()
224                    .is_some_and(|s| Rc::ptr_eq(&s, e.presentation_source()));
225                if !attached_here && child.visual_parent().is_some_and(|p| p == this_ref) {
226                    child.on_attached_to_visual_tree_core(e);
227                }
228            }
229        }
230    }
231
232    fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
233        ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() - 1));
234
235        this.set_render_transform_subscription(None);
236
237        this.disable_transitions();
238        this.update_is_effectively_visible(true);
239        this.on_detached_from_visual_tree(e);
240        this.detach_from_compositor();
241        if !this.detached_from_visual_tree.is_empty() {
242            for (_, handler) in this.detached_from_visual_tree.snapshot().iter() {
243                handler(e);
244            }
245        }
246        if let Some(source) = this.presentation_source() {
247            source.renderer().add_dirty(this);
248        }
249        *this.presentation_source.borrow_mut() = None;
250
251        if let Some(children) = this.visual_children.get() {
252            for child in children.snapshot().iter() {
253                // A child removed within an event handler has already been
254                // detached by the removal itself.
255                if child.presentation_source().is_some() {
256                    child.on_detached_from_visual_tree_core(e);
257                }
258            }
259        }
260    }
261
262    fn on_attached_to_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
263
264    fn on_detached_from_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
265
266    fn on_visual_parent_changed(this: &Self, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>) {
267        this.raise_direct_property_changed(
268            Self::visual_parent_property(),
269            &old_parent.cloned(),
270            &new_parent.cloned(),
271        );
272    }
273
274    fn bypass_flow_direction_policies(_this: &Self) -> bool {
275        false
276    }
277
278    fn clip_to_bounds_radius(_this: &Self) -> Option<crate::CornerRadius> {
279        None
280    }
281
282    fn custom_hit_test(_this: &Self, _point: crate::Point) -> Option<bool> {
283        None
284    }
285
286    fn custom_hit_test_geometry(
287        _this: &Self,
288        _geometry: &Ref<crate::media::Geometry>,
289    ) -> Option<crate::media::IntersectionResult> {
290        None
291    }
292
293    fn ensure_initialized_for_visual_brush(_this: &Self) {}
294
295    fn create_composition_visual(this: &Self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
296        CompositionDrawListVisual::new(compositor, this)
297    }
298
299    fn detach_from_compositor(this: &Self) {
300        let composition_visual = this.composition_visual.borrow_mut().take();
301        if let Some(composition_visual) = composition_visual {
302            if let Some(child) = this.child_composition_visual() {
303                composition_visual.children().remove(&child);
304            }
305            composition_visual.set_draw_list(None);
306            composition_visual.set_opacity_mask(None);
307            // The composition visual and its children refer to each other;
308            // nothing collects such a cycle here, so the visual lets go of
309            // its children. They are re-added when their visuals are
310            // synchronized again.
311            composition_visual.children().clear();
312        }
313    }
314
315    fn synchronize_composition_child_visuals(this: &Self) {
316        let Some(composition_visual) = this.composition_visual() else { return };
317        let composition_children = composition_visual.children();
318        let visual_children: Rc<Vec<Ref<Visual>>> =
319            this.visual_children_snapshot().unwrap_or_else(|| Rc::new(Vec::new()));
320
321        let mut sorted_children: Option<Vec<(Ref<Visual>, usize)>> = None;
322        if this.has_non_uniform_z_index_children() && visual_children.len() > 1 {
323            let mut sorted: Vec<(Ref<Visual>, usize)> =
324                visual_children.iter().cloned().enumerate().map(|(index, visual)| (visual, index)).collect();
325            // The indices keep the order of elements with the same Z index.
326            sorted.sort_by(|lhs, rhs| lhs.0.z_index().cmp(&rhs.0.z_index()).then(lhs.1.cmp(&rhs.1)));
327            sorted_children = Some(sorted);
328        }
329
330        let mut child_visual = this.child_composition_visual();
331
332        // Check if the current visual somehow got migrated to another compositor
333        if child_visual.as_ref().is_some_and(|child| !Rc::ptr_eq(child.compositor(), composition_visual.compositor())) {
334            child_visual = None;
335        }
336
337        let mut expected_count = visual_children.len();
338        if child_visual.is_some() {
339            expected_count += 1;
340        }
341
342        let same = |a: &Rc<CompositionVisual>, b: Option<CompositionDrawListVisual>| {
343            b.is_some_and(|b| Rc::ptr_eq(a, &b))
344        };
345
346        if composition_children.count() == expected_count {
347            let mut mismatch = false;
348            for c in 0..visual_children.len() {
349                let expected = match &sorted_children {
350                    Some(sorted) => sorted[c].0.composition_visual(),
351                    None => visual_children[c].composition_visual(),
352                };
353                if !same(&composition_children.get(c), expected) {
354                    mismatch = true;
355                    break;
356                }
357            }
358
359            if let Some(child_visual) = &child_visual {
360                if !Rc::ptr_eq(&composition_children.get(composition_children.count() - 1), child_visual) {
361                    mismatch = true;
362                }
363            }
364
365            if !mismatch {
366                return;
367            }
368        }
369
370        composition_children.clear();
371        match &sorted_children {
372            Some(sorted) => {
373                for (child, _) in sorted {
374                    if let Some(composition_child) = child.composition_visual() {
375                        composition_children.add((*composition_child).clone());
376                    }
377                }
378            }
379            None => {
380                for child in visual_children.iter() {
381                    if let Some(composition_child) = child.composition_visual() {
382                        composition_children.add((*composition_child).clone());
383                    }
384                }
385            }
386        }
387
388        if let Some(child_visual) = child_visual {
389            composition_children.add(child_visual);
390        }
391    }
392
393    fn synchronize_composition_properties(this: &Self) {
394        let Some(comp) = this.composition_visual() else { return };
395        let bounds = this.bounds();
396
397        // TODO: Introduce a dirty mask like WPF has, so we don't overwrite properties every time
398        comp.set_offset(crate::Vector3D::new(bounds.x, bounds.y, 0.0));
399        comp.set_size(crate::Vector::new(bounds.width, bounds.height));
400        comp.set_visible(this.is_visible());
401        comp.set_opacity(this.opacity() as f32);
402        comp.set_clip_to_bounds(this.clip_to_bounds());
403        comp.set_clip(this.clip().and_then(|clip| clip.platform_impl()));
404
405        let opacity_mask = this.opacity_mask();
406        let same_mask = match (comp.opacity_mask(), &opacity_mask) {
407            (Some(a), Some(b)) => *a == **b,
408            (None, None) => true,
409            _ => false,
410        };
411        if !same_mask {
412            comp.set_opacity_mask(opacity_mask);
413        }
414
415        let cache_mode = this.cache_mode().map(|cache_mode| cache_mode.get_for_compositor(comp.compositor()));
416        let same_cache_mode = match (comp.cache_mode(), &cache_mode) {
417            (Some(a), Some(b)) => Rc::ptr_eq(&a, b),
418            (None, None) => true,
419            _ => false,
420        };
421        if !same_cache_mode {
422            comp.set_cache_mode(cache_mode);
423        }
424
425        let effect = this.effect();
426        if !crate::media::EffectExtensions::effect_equals(comp.effect().as_deref(), effect.as_deref()) {
427            comp.set_effect(effect.as_ref().map(crate::media::EffectExtensions::to_immutable));
428        }
429
430        comp.set_render_options(this.render_options());
431        comp.set_text_options(this.text_options());
432
433        let mut render_transform = crate::Matrix::IDENTITY;
434
435        if this.has_mirror_transform() {
436            render_transform = crate::Matrix::new(-1.0, 0.0, 0.0, 1.0, bounds.width, 0.0);
437        }
438
439        if let Some(transform) = this.render_transform() {
440            let origin = this.render_transform_origin().to_pixels(crate::Size::new(bounds.width, bounds.height));
441            let offset = crate::Matrix::create_translation(origin.x, origin.y);
442            render_transform = render_transform * ((-offset) * transform.value() * offset);
443        }
444
445        comp.set_transform_matrix(render_transform);
446    }
447
448    fn invalidate_mirror_transform(this: &Self) {
449        let flow_direction = this.flow_direction();
450        let mut parent_flow_direction = FlowDirection::LeftToRight;
451        let bypass = this.bypass_flow_direction_policies();
452        let mut parent_bypass = false;
453
454        if let Some(parent) = this.visual_parent() {
455            parent_flow_direction = parent.flow_direction();
456            parent_bypass = parent.bypass_flow_direction_policies();
457        }
458
459        let this_should_be_mirrored = flow_direction == FlowDirection::RightToLeft && !bypass;
460        let parent_should_be_mirrored = parent_flow_direction == FlowDirection::RightToLeft && !parent_bypass;
461
462        this.set_has_mirror_transform(this_should_be_mirrored != parent_should_be_mirrored);
463    }
464}
465
466// Deviation (DEVIATIONS.md, Visual tree): the subscriptions to the values of
467// the render-affecting properties are weak events in the managed original (`InvalidatedWeakEvent`): the value does not
468// keep the visual, and the entries of visuals that were collected are removed
469// from its list when the list is compacted. Here the handler of a visual holds
470// it weakly too, and the visual takes its handlers out of the lists when it is
471// dropped: a value many visuals share (the geometry of an icon that is a
472// resource) would otherwise keep one entry, and the memory of the visual its
473// weak reference holds, for every visual that ever drew it.
474impl Drop for Visual {
475    fn drop(&mut self) {
476        for (_, subscription) in self.affects_render_subscriptions.get_mut().drain(..) {
477            subscription.dispose();
478        }
479    }
480}
481
482crate::ferro_properties! { impl Visual {
483    ferro_property!(
484        /// Defines the `Bounds` property.
485        pub fn bounds_property() -> DirectProperty<Visual, Rect> {
486            FerroProperty::register_direct::<Visual, _>("Bounds", |o| o.bounds(), None, Rect::default())
487        }
488    );
489
490    ferro_property!(
491        /// Defines the `ClipToBounds` property.
492        pub fn clip_to_bounds_property() -> StyledProperty<bool> {
493            FerroProperty::register::<Visual, _>("ClipToBounds", false)
494        }
495    );
496
497    ferro_property!(
498        /// Defines the `Clip` property.
499        pub fn clip_property() -> StyledProperty<Option<Ref<Geometry>>> {
500            FerroProperty::register::<Visual, _>("Clip", None)
501        }
502    );
503
504    ferro_property!(
505        /// Defines the `CacheMode` property.
506        pub fn cache_mode_property() -> StyledProperty<Option<Ref<CacheMode>>> {
507            FerroProperty::register::<Visual, _>("CacheMode", None)
508        }
509    );
510
511    ferro_property!(
512        /// Defines the `Effect` property.
513        pub fn effect_property() -> StyledProperty<Option<Rc<dyn IEffect>>> {
514            FerroProperty::register::<Visual, _>("Effect", None)
515        }
516    );
517
518    ferro_property!(
519        /// Defines the `OpacityMask` property.
520        pub fn opacity_mask_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
521            FerroProperty::register::<Visual, _>("OpacityMask", None)
522        }
523    );
524
525    ferro_property!(
526        /// Defines the `RenderTransform` property.
527        pub fn render_transform_property() -> StyledProperty<Option<Rc<dyn ITransform>>> {
528            FerroProperty::register::<Visual, _>("RenderTransform", None)
529        }
530    );
531
532    ferro_property!(
533        /// Defines the `RenderTransformOrigin` property.
534        pub fn render_transform_origin_property() -> StyledProperty<RelativePoint> {
535            FerroProperty::register::<Visual, _>("RenderTransformOrigin", RelativePoint::CENTER)
536        }
537    );
538
539    ferro_property!(
540        /// Defines the `IsVisible` property.
541        pub fn is_visible_property() -> StyledProperty<bool> {
542            FerroProperty::register::<Visual, _>("IsVisible", true)
543        }
544    );
545
546    ferro_property!(
547        /// Defines the `Opacity` property.
548        pub fn opacity_property() -> StyledProperty<f64> {
549            FerroProperty::register::<Visual, _>("Opacity", 1.0)
550        }
551    );
552
553    ferro_property!(
554        /// Defines the `HasMirrorTransform` property.
555        pub fn has_mirror_transform_property() -> DirectProperty<Visual, bool> {
556            FerroProperty::register_direct::<Visual, _>("HasMirrorTransform", |o| o.has_mirror_transform(), None, false)
557        }
558    );
559
560    ferro_property!(
561        /// Defines the `FlowDirection` attached property.
562        pub fn flow_direction_property() -> AttachedProperty<FlowDirection> {
563            FerroProperty::register_attached_with::<Visual, Visual, _>(
564                "FlowDirection",
565                StyledPropertyOptions::new(FlowDirection::LeftToRight).inherits(true),
566            )
567        }
568    );
569
570    ferro_property!(
571        /// Defines the `VisualParent` property.
572        pub fn visual_parent_property() -> DirectProperty<Visual, Option<Ref<Visual>>> {
573            FerroProperty::register_direct::<Visual, _>("VisualParent", |o| o.visual_parent(), None, None)
574        }
575    );
576
577    ferro_property!(
578        /// Defines the `ZIndex` property.
579        pub fn z_index_property() -> StyledProperty<i32> {
580            FerroProperty::register::<Visual, _>("ZIndex", 0)
581        }
582    );
583} }
584
585impl Visual {
586    fn static_constructor() {
587        Self::affects_render::<Visual>(&[
588            Self::bounds_property().as_property(),
589            Self::clip_property().as_property(),
590            Self::clip_to_bounds_property().as_property(),
591            Self::is_visible_property().as_property(),
592            Self::opacity_property().as_property(),
593            Self::opacity_mask_property().as_property(),
594            Self::effect_property().as_property(),
595            Self::has_mirror_transform_property().as_property(),
596        ]);
597        Self::render_transform_property().changed().subscribe(Self::render_transform_changed);
598        Self::clip_to_bounds_property().changed().subscribe(|_| bump_geometry_version());
599        Self::render_transform_origin_property().changed().subscribe(|_| bump_geometry_version());
600        Self::z_index_property().changed().subscribe(|e| {
601            let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
602            let parent = sender.visual_parent();
603            if let Some(parent) = &parent {
604                if sender.z_index() != 0 {
605                    parent.has_non_uniform_z_index_children.set(true);
606                }
607            }
608            sender.invalidate_visual();
609            if let Some(parent) = &parent {
610                if let Some(source) = parent.presentation_source() {
611                    source.renderer().recalculate_children(parent);
612                }
613            }
614        });
615    }
616
617    /// Creates the class data; see [`crate::FerroObject::construct`].
618    pub fn construct() -> Self {
619        Self {
620            base: StyledElement::construct(),
621            bounds: Cell::new(Rect::default()),
622            presentation_source: RefCell::new(None),
623            visual_parent: RefCell::new(None),
624            has_mirror_transform: Cell::new(false),
625            has_render_transform: Cell::new(false),
626            is_effectively_visible: Cell::new(true),
627            has_non_uniform_z_index_children: Cell::new(false),
628            visual_children: OnceCell::new(),
629            attached_to_visual_tree: HandlerList::new(),
630            detached_from_visual_tree: HandlerList::new(),
631            is_effectively_visible_changed: HandlerList::new(),
632            render_options: Cell::new(RenderOptions::default()),
633            text_options: Cell::new(TextOptions::default()),
634            composition_visual: RefCell::new(None),
635            child_composition_visual: RefCell::new(None),
636            render_transform_subscription: RefCell::new(None),
637            affects_render_subscriptions: RefCell::new(Vec::new()),
638        }
639    }
640
641    pub fn new() -> Ref<Self> {
642        instantiate(Self::construct())
643    }
644
645    // --- events -----------------------------------------------------------
646
647    /// Raised when the control is attached to a rooted visual tree.
648    pub fn attached_to_visual_tree(
649        &self,
650        handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
651    ) -> Rc<dyn IDisposable> {
652        let token = self.attached_to_visual_tree.add(Rc::new(handler));
653        let weak = self.to_ref().downgrade();
654        Disposable::create(move || {
655            if let Some(this) = weak.upgrade() {
656                this.attached_to_visual_tree.remove(token);
657            }
658        })
659    }
660
661    /// Raised when the control is detached from a rooted visual tree.
662    pub fn detached_from_visual_tree(
663        &self,
664        handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
665    ) -> Rc<dyn IDisposable> {
666        let token = self.detached_from_visual_tree.add(Rc::new(handler));
667        let weak = self.to_ref().downgrade();
668        Disposable::create(move || {
669            if let Some(this) = weak.upgrade() {
670                this.detached_from_visual_tree.remove(token);
671            }
672        })
673    }
674
675    /// Raised when `is_effectively_visible` changes.
676    pub fn is_effectively_visible_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
677        let token = self.is_effectively_visible_changed.add(Rc::new(handler));
678        let weak = self.to_ref().downgrade();
679        Disposable::create(move || {
680            if let Some(this) = weak.upgrade() {
681                this.is_effectively_visible_changed.remove(token);
682            }
683        })
684    }
685
686    // --- properties -------------------------------------------------------
687
688    /// The bounds of the control relative to its parent.
689    #[inline]
690    pub fn bounds(&self) -> Rect {
691        self.bounds.get()
692    }
693
694    /// Sets the bounds of the control. Called by the layout system.
695    pub fn set_bounds(&self, value: Rect) {
696        if self.bounds.get() != value {
697            bump_geometry_version();
698        }
699        self.set_and_raise_cell(Self::bounds_property(), &self.bounds, value);
700    }
701
702    /// Whether the control should be clipped to its bounds.
703    pub fn clip_to_bounds(&self) -> bool {
704        self.get_value(Self::clip_to_bounds_property())
705    }
706
707    pub fn set_clip_to_bounds(&self, value: bool) {
708        self.set_value(Self::clip_to_bounds_property(), value)
709    }
710
711    /// The geometry clip for this visual.
712    pub fn clip(&self) -> Option<Ref<Geometry>> {
713        self.get_value(Self::clip_property())
714    }
715
716    pub fn set_clip(&self, value: impl Into<Nullable<Geometry>>) {
717        self.set_value(Self::clip_property(), value.into().0)
718    }
719
720    /// The cache mode of the visual.
721    pub fn cache_mode(&self) -> Option<Ref<CacheMode>> {
722        self.get_value(Self::cache_mode_property())
723    }
724
725    pub fn set_cache_mode(&self, value: impl Into<Nullable<CacheMode>>) {
726        self.set_value(Self::cache_mode_property(), value.into().0)
727    }
728
729    /// The effect of the control.
730    pub fn effect(&self) -> Option<Rc<dyn IEffect>> {
731        self.get_value(Self::effect_property())
732    }
733
734    pub fn set_effect(&self, value: Option<Rc<dyn IEffect>>) {
735        self.set_value(Self::effect_property(), value)
736    }
737
738    /// The opacity mask of the control.
739    pub fn opacity_mask(&self) -> Option<Rc<dyn IBrush>> {
740        self.get_value(Self::opacity_mask_property())
741    }
742
743    pub fn set_opacity_mask(&self, value: Option<Rc<dyn IBrush>>) {
744        self.set_value(Self::opacity_mask_property(), value)
745    }
746
747    /// The render transform of the control.
748    pub fn render_transform(&self) -> Option<Rc<dyn ITransform>> {
749        self.get_value(Self::render_transform_property())
750    }
751
752    pub fn set_render_transform(&self, value: Option<Rc<dyn ITransform>>) {
753        self.set_value(Self::render_transform_property(), value)
754    }
755
756    /// The transform origin of the control.
757    pub fn render_transform_origin(&self) -> RelativePoint {
758        self.get_value(Self::render_transform_origin_property())
759    }
760
761    pub fn set_render_transform_origin(&self, value: RelativePoint) {
762        self.set_value(Self::render_transform_origin_property(), value)
763    }
764
765    /// Whether this control and all its parents are visible.
766    #[inline]
767    pub fn is_effectively_visible(&self) -> bool {
768        self.is_effectively_visible.get()
769    }
770
771    /// Whether this control is visible.
772    pub fn is_visible(&self) -> bool {
773        self.get_value(Self::is_visible_property())
774    }
775
776    pub fn set_is_visible(&self, value: bool) {
777        self.set_value(Self::is_visible_property(), value)
778    }
779
780    /// The opacity of the control.
781    pub fn opacity(&self) -> f64 {
782        self.get_value(Self::opacity_property())
783    }
784
785    pub fn set_opacity(&self, value: f64) {
786        self.set_value(Self::opacity_property(), value)
787    }
788
789    /// Whether `RenderTransform` has a value (not upstream: the transforms of the visual tree
790    /// skip the property read when it has none).
791    #[inline]
792    pub(crate) fn has_render_transform(&self) -> bool {
793        self.has_render_transform.get()
794    }
795
796    /// Whether the control is mirrored relative to its parent because of its
797    /// flow direction.
798    #[inline]
799    pub fn has_mirror_transform(&self) -> bool {
800        self.has_mirror_transform.get()
801    }
802
803    /// Sets the mirror transform flag.
804    pub fn set_has_mirror_transform(&self, value: bool) {
805        self.set_and_raise_cell(Self::has_mirror_transform_property(), &self.has_mirror_transform, value);
806    }
807
808    /// The text flow direction.
809    pub fn flow_direction(&self) -> FlowDirection {
810        self.get_value(Self::flow_direction_property())
811    }
812
813    pub fn set_flow_direction(&self, value: FlowDirection) {
814        self.set_value(Self::flow_direction_property(), value)
815    }
816
817    /// Gets the value of the attached `FlowDirection` property on a visual.
818    pub fn get_flow_direction(visual: &Visual) -> FlowDirection {
819        visual.get_value(Self::flow_direction_property())
820    }
821
822    /// Sets the value of the attached `FlowDirection` property on a visual.
823    pub fn set_flow_direction_on(visual: &Visual, value: FlowDirection) {
824        visual.set_value(Self::flow_direction_property(), value)
825    }
826
827    /// The Z index of the control.
828    ///
829    /// Controls with a higher Z index appear in front of controls with a
830    /// lower one. Among controls with the same value, order of appearance in
831    /// the visual children decides.
832    pub fn z_index(&self) -> i32 {
833        self.get_value(Self::z_index_property())
834    }
835
836    pub fn set_z_index(&self, value: i32) {
837        self.set_value(Self::z_index_property(), value)
838    }
839
840    /// The control's child visuals.
841    pub fn visual_children(&self) -> &FerroList<Ref<Visual>> {
842        self.visual_children.get_or_init(|| {
843            let list = FerroList::new();
844            list.set_reset_behavior(ResetBehavior::Remove);
845            let weak = self.to_ref().downgrade();
846            let validator_owner = weak.clone();
847            list.set_validate(Some(Rc::new(move |item: &Ref<Visual>| {
848                if let Some(parent) = item.visual_parent() {
849                    let owner = validator_owner.upgrade();
850                    panic!(
851                        "The control {:?} already has a visual parent {:?} while trying to add it as a child of {:?}.",
852                        item, parent, owner
853                    );
854                }
855            })));
856            list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>| {
857                if let Some(this) = weak.upgrade() {
858                    this.visual_children_changed(e);
859                }
860            }));
861            list
862        })
863    }
864
865    /// The number of visual children, without creating the collection.
866    #[inline]
867    pub fn visual_children_count(&self) -> usize {
868        self.visual_children.get().map_or(0, FerroList::count)
869    }
870
871    /// A snapshot of the visual children, without creating the collection.
872    #[inline]
873    pub fn visual_children_snapshot(&self) -> Option<Rc<Vec<Ref<Visual>>>> {
874        self.visual_children.get().map(FerroList::snapshot)
875    }
876
877    /// The presentation source the visual is attached to.
878    #[inline]
879    pub fn presentation_source(&self) -> Option<Rc<dyn IPresentationSource>> {
880        self.presentation_source.borrow().clone()
881    }
882
883    /// The root of the visual tree, if the control is attached to one.
884    pub fn visual_root(&self) -> Option<Ref<Visual>> {
885        self.presentation_source().and_then(|s| s.root_visual())
886    }
887
888    /// The render options of the visual; see the accessors on
889    /// [`RenderOptions`].
890    #[inline]
891    pub(crate) fn render_options(&self) -> RenderOptions {
892        self.render_options.get()
893    }
894
895    pub(crate) fn set_render_options(&self, value: RenderOptions) {
896        self.render_options.set(value);
897        self.invalidate_visual();
898    }
899
900    /// The text options of the visual; see the accessors on
901    /// [`TextOptions`].
902    #[inline]
903    pub(crate) fn text_options(&self) -> TextOptions {
904        self.text_options.get()
905    }
906
907    pub(crate) fn set_text_options(&self, value: TextOptions) {
908        self.text_options.set(value);
909        self.invalidate_visual();
910    }
911
912    /// The composition visual of the visual, while it is attached to a
913    /// visual tree rendered by a compositor.
914    pub fn composition_visual(&self) -> Option<CompositionDrawListVisual> {
915        self.composition_visual.borrow().clone()
916    }
917
918    /// The composition visual shown as the last child of the composition
919    /// visual of this visual (see `ElementComposition`).
920    pub fn child_composition_visual(&self) -> Option<Rc<CompositionVisual>> {
921        self.child_composition_visual.borrow().clone()
922    }
923
924    pub fn set_child_composition_visual(&self, value: Option<Rc<CompositionVisual>>) {
925        *self.child_composition_visual.borrow_mut() = value;
926    }
927
928    /// Makes sure the visual has a composition visual on `compositor` and
929    /// returns it.
930    pub fn attach_to_compositor(&self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
931        let current = self.composition_visual();
932        match current {
933            Some(current) if Rc::ptr_eq(current.compositor(), compositor) => current,
934            _ => {
935                let created = self.create_composition_visual(compositor);
936                *self.composition_visual.borrow_mut() = Some(created.clone());
937                created
938            }
939        }
940    }
941
942    /// Whether any child has a non-default Z index.
943    pub fn has_non_uniform_z_index_children(&self) -> bool {
944        self.has_non_uniform_z_index_children.get()
945    }
946
947    /// Whether this control is attached to a visual root.
948    #[inline]
949    pub fn is_attached_to_visual_tree(&self) -> bool {
950        self.presentation_source.borrow().is_some()
951    }
952
953    /// The control's parent visual.
954    #[inline]
955    pub fn visual_parent(&self) -> Option<Ref<Visual>> {
956        self.visual_parent.borrow().as_ref().and_then(WeakRef::upgrade)
957    }
958
959    /// The number of visuals attached to a rooted visual tree (on this
960    /// thread; visuals are UI-thread-affine).
961    pub(crate) fn rooted_visual_children_count() -> i32 {
962        ROOTED_VISUAL_CHILDREN_COUNT.with(Cell::get)
963    }
964
965    // --- methods ----------------------------------------------------------
966
967    /// Invalidates the visual and queues a repaint.
968    pub fn invalidate_visual(&self) {
969        if let Some(source) = self.presentation_source() {
970            source.renderer().add_dirty(self);
971        }
972    }
973
974    /// Indicates that a property change should cause `invalidate_visual` to
975    /// be called on objects of class `T`.
976    ///
977    /// When the value of such a property is a media object that can change
978    /// (a brush, pen, geometry, transform, effect or image), its
979    /// invalidation invalidates the visual as well.
980    pub fn affects_render<T: ObjectType + Upcast<Visual>>(properties: &[&'static FerroProperty]) {
981        for property in properties {
982            if can_value_affect_render(property.property_type()) {
983                property.changed().subscribe(|e| {
984                    if let Some(sender) = e.sender().downcast_ref::<T>() {
985                        let visual: &Visual = sender.upcast();
986                        visual.update_affects_render_subscription(e);
987                        visual.invalidate_visual();
988                    }
989                });
990            } else {
991                property.changed().subscribe(|e| {
992                    if let Some(sender) = e.sender().downcast_ref::<T>() {
993                        let visual: &Visual = sender.upcast();
994                        visual.invalidate_visual();
995                    }
996                });
997            }
998        }
999    }
1000
1001    /// Moves the invalidation subscription of a render-affecting property
1002    /// from its old value to its new value.
1003    fn update_affects_render_subscription(&self, e: &FerroPropertyChangedEventArgs<'_>) {
1004        let property = e.property();
1005        let old = {
1006            let mut subscriptions = self.affects_render_subscriptions.borrow_mut();
1007            let position = subscriptions.iter().position(|(p, _)| *p == property);
1008            position.map(|position| subscriptions.swap_remove(position))
1009        };
1010        if let Some((_, subscription)) = old {
1011            subscription.dispose();
1012        }
1013
1014        let weak = self.to_ref().downgrade();
1015        let subscription = subscribe_invalidated(
1016            e.new_value(),
1017            Rc::new(move || {
1018                if let Some(target) = weak.upgrade() {
1019                    target.invalidate_visual();
1020                }
1021            }),
1022        );
1023        if let Some(subscription) = subscription {
1024            self.affects_render_subscriptions.borrow_mut().push((property, subscription));
1025        }
1026    }
1027
1028    /// Called when a visual's render transform property changes: while the
1029    /// visual is attached, the subscription to the transform's `changed`
1030    /// notification moves from the old value to the new one.
1031    fn render_transform_changed(e: &FerroPropertyChangedEventArgs<'_>) {
1032        let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
1033        let (_, new_value) = e.get_old_and_new_value::<Option<Rc<dyn ITransform>>>();
1034        sender.has_render_transform.set(new_value.is_some());
1035        bump_geometry_version();
1036
1037        if sender.visual_root().is_some() {
1038
1039            // The old transform's subscription is the one held.
1040            let subscription = new_value.as_ref().and_then(|new_value| {
1041                let new_transform = new_value.as_any().downcast_ref::<Transform>()?;
1042                let weak = sender.to_ref().downgrade();
1043                Some(new_transform.changed(move || {
1044                    if let Some(visual) = weak.upgrade() {
1045                        visual.render_transform_value_changed();
1046                    }
1047                }))
1048            });
1049            sender.set_render_transform_subscription(subscription);
1050
1051            sender.invalidate_visual();
1052        }
1053    }
1054
1055    /// Called when the render transform's `changed` notification is raised.
1056    fn render_transform_value_changed(&self) {
1057        bump_geometry_version();
1058        self.invalidate_visual();
1059    }
1060
1061    fn set_render_transform_subscription(&self, value: Option<Rc<dyn IDisposable>>) {
1062        let previous = self.render_transform_subscription.replace(value);
1063        if let Some(previous) = previous {
1064            previous.dispose();
1065        }
1066    }
1067
1068    /// Attaches or detaches the root visual of a presentation source.
1069    pub fn set_presentation_source_for_root_visual(&self, presentation_source: Option<Rc<dyn IPresentationSource>>) {
1070        let current = self.presentation_source();
1071        let same = match (&current, &presentation_source) {
1072            (Some(a), Some(b)) => Rc::ptr_eq(a, b),
1073            (None, None) => true,
1074            _ => false,
1075        };
1076        if same {
1077            return;
1078        }
1079
1080        if let Some(current) = current {
1081            assert!(
1082                presentation_source.is_none(),
1083                "Visual is already attached to a presentation source. Only one presentation source can be \
1084                 attached to a visual tree."
1085            );
1086            self.on_detached_from_visual_tree_core(&VisualTreeAttachmentEventArgs::new(None, current));
1087        }
1088
1089        *self.presentation_source.borrow_mut() = presentation_source.clone();
1090
1091        if let Some(source) = presentation_source {
1092            let e = VisualTreeAttachmentEventArgs::new(None, source);
1093            self.on_attached_to_visual_tree_core(&e);
1094        }
1095    }
1096
1097    fn update_is_effectively_visible(&self, parent_state: bool) {
1098        let is_effectively_visible = parent_state && self.is_visible();
1099        if self.is_effectively_visible.get() == is_effectively_visible {
1100            return;
1101        }
1102        self.is_effectively_visible.set(is_effectively_visible);
1103        if !self.is_effectively_visible_changed.is_empty() {
1104            for (_, handler) in self.is_effectively_visible_changed.snapshot().iter() {
1105                handler();
1106            }
1107        }
1108        // PERF-SENSITIVE: this is called on the entire hierarchy.
1109        if let Some(children) = self.visual_children.get() {
1110            for child in children.snapshot().iter() {
1111                child.update_is_effectively_visible(is_effectively_visible);
1112            }
1113        }
1114    }
1115
1116    fn set_visual_parent(&self, value: Option<&Ref<Visual>>) {
1117        let old = self.visual_parent();
1118        if old.as_ref() == value {
1119            return;
1120        }
1121        *self.visual_parent.borrow_mut() = value.map(Ref::downgrade);
1122        bump_geometry_version();
1123
1124        if let (Some(source), Some(old)) = (self.presentation_source(), &old) {
1125            let e = VisualTreeAttachmentEventArgs::new(Some(old.clone()), source);
1126            self.on_detached_from_visual_tree_core(&e);
1127        }
1128
1129        if let Some(parent) = value {
1130            if let Some(source) = parent.presentation_source() {
1131                let e = VisualTreeAttachmentEventArgs::new(Some(parent.clone()), source);
1132                self.on_attached_to_visual_tree_core(&e);
1133            }
1134        }
1135
1136        self.on_visual_parent_changed(old.as_ref(), value);
1137    }
1138
1139    fn visual_children_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>) {
1140        match e.action {
1141            NotifyCollectionChangedAction::Add => {
1142                let this = self.to_ref();
1143                for visual in e.new_items {
1144                    visual.set_visual_parent(Some(&this));
1145                }
1146            }
1147            NotifyCollectionChangedAction::Remove => {
1148                for visual in e.old_items {
1149                    visual.set_visual_parent(None);
1150                }
1151            }
1152            NotifyCollectionChangedAction::Replace => {
1153                for visual in e.old_items {
1154                    visual.set_visual_parent(None);
1155                }
1156                let this = self.to_ref();
1157                for visual in e.new_items {
1158                    visual.set_visual_parent(Some(&this));
1159                }
1160            }
1161            NotifyCollectionChangedAction::Move | NotifyCollectionChangedAction::Reset => {}
1162        }
1163    }
1164}