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