Skip to main content

ferroui_controls/primitives/
adorner_layer.rs

1use super::{AdornerHelper, OverlayLayer, VisualLayerManager};
2use crate::templates::ITemplateOf;
3use crate::{Canvas, CanvasImpl, Control, ControlImpl, PanelImpl, TopLevel};
4use ferroui_base::collections::{NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
5use ferroui_base::data::core::ValueTypes;
6use ferroui_base::input::text_input::TransformTrackingHelper;
7use ferroui_base::ElementRef;
8use ferroui_base::input::InputElementImpl;
9use ferroui_base::interactivity::InteractiveImpl;
10use ferroui_base::layout::LayoutableImpl;
11use ferroui_base::media::{ITransform, MatrixTransform};
12use ferroui_base::reactive::IDisposable;
13use ferroui_base::{
14    ferro_class, ferro_impl_classes, ferro_property, instantiate, AttachedProperty, FerroObjectImpl,
15    FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Matrix, Nullable, Point, Rect, Ref,
16    RelativePoint, RelativeUnit, Size, StyledElement, StyledElementImpl, StyledProperty, Visual, VisualImpl, WeakRef,
17};
18use std::cell::RefCell;
19use std::rc::Rc;
20
21/// Represents a surface for showing adorners.
22/// Adorners are always on top of the adorned element and are positioned to
23/// stay relative to the adorned element.
24#[repr(C)]
25pub struct AdornerLayer {
26    base: Canvas,
27    tracking_helper: Rc<TransformTrackingHelper>,
28}
29
30ferro_class!(AdornerLayer: Canvas);
31ferro_impl_classes!(AdornerLayer: StyledElementImpl, VisualImpl, InteractiveImpl, InputElementImpl, ControlImpl, PanelImpl, CanvasImpl);
32
33impl FerroObjectImpl for AdornerLayer {
34    fn constructed(this: &Self) {
35        Self::parent_constructed(this);
36
37        let weak = this.to_ref().downgrade();
38        this.children().add_collection_changed(Rc::new(
39            move |e: &NotifyCollectionChangedEventArgs<'_, Ref<Control>>| {
40                if let Some(this) = weak.upgrade() {
41                    this.children_collection_changed(e);
42                }
43            },
44        ));
45
46        this.tracking_helper.set_visual(Some(&this.to_ref().upcast()));
47        let weak = this.to_ref().downgrade();
48        this.tracking_helper.matrix_changed(move || {
49            if let Some(this) = weak.upgrade() {
50                this.invalidate_measure();
51            }
52        });
53    }
54}
55
56impl LayoutableImpl for AdornerLayer {
57    fn measure_override(this: &Self, available_size: Size) -> Size {
58        for child in this.children().snapshot().iter() {
59            let info = child.get_value(Self::adorned_element_info_property());
60
61            match info.and_then(|info| info.0.adorned_element()) {
62                Some(adorned_element) => child.measure(adorned_element.bounds().size()),
63                None => child.measure(available_size),
64            }
65        }
66
67        Size::default()
68    }
69
70    fn arrange_override(this: &Self, final_size: Size) -> Size {
71        for child in this.children().snapshot().iter() {
72            let info = child.get_value(Self::adorned_element_info_property());
73            let is_clip_enabled = child.get_value(Self::is_clip_enabled_property());
74
75            let adorned = info.and_then(|info| info.0.adorned_element());
76
77            if let Some(adorned) = adorned {
78                child.arrange(Rect::from_size(adorned.bounds().size()));
79                let mut transform = adorned.transform_to_visual(this);
80                // If somebody decides that having Margin on an adorner is a good idea,
81                // we need to compensate for element being positioned at non-(0,0) coords.
82                let position = child.bounds().position();
83                if let Some(value) = transform {
84                    if position != Point::default() {
85                        transform = Some(
86                            Matrix::create_translation(position.x, position.y)
87                                * value
88                                * Matrix::create_translation(-position.x, -position.y),
89                        );
90                    }
91                }
92                let render_transform: Rc<dyn ITransform> =
93                    MatrixTransform::with_matrix(transform.unwrap_or(ZERO_MATRIX)).into();
94                child.set_render_transform(Some(render_transform));
95                child.set_render_transform_origin(RelativePoint::new(0.0, 0.0, RelativeUnit::Absolute));
96                this.update_clip(child, &adorned, is_clip_enabled);
97            } else {
98                this.arrange_child(child, final_size);
99            }
100        }
101
102        final_size
103    }
104}
105
106/// The value the reference uses when the adorned element has no transform
107/// to the layer: the all-zero matrix, which collapses the adorner.
108const ZERO_MATRIX: Matrix = Matrix::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
109
110ferroui_base::ferro_properties! { impl AdornerLayer {
111    ferro_property!(
112        /// Allows for getting and setting of the adorned element.
113        pub fn adorned_element_property() -> AttachedProperty<Option<ElementRef<Visual>>> {
114            ValueTypes::register_element_ref::<Visual>();
115            let property = FerroProperty::register_attached::<AdornerLayer, Visual, _>("AdornedElement", None);
116            property.changed().subscribe(Self::adorned_element_changed);
117            property
118        }
119    );
120
121    ferro_property!(
122        /// Allows for controlling clipping of the adorner.
123        pub fn is_clip_enabled_property() -> AttachedProperty<bool> {
124            let property = FerroProperty::register_attached::<AdornerLayer, Visual, _>("IsClipEnabled", true);
125            property.changed().subscribe(Self::adorner_is_clip_enabled_changed);
126            property
127        }
128    );
129
130    ferro_property!(
131        /// Allows for getting and setting of the adorner for control.
132        pub fn adorner_property() -> AttachedProperty<Option<Ref<Control>>> {
133            let property = FerroProperty::register_attached::<AdornerLayer, Visual, _>("Adorner", None);
134            property.changed().subscribe(Self::adorner_changed);
135            property
136        }
137    );
138
139    ferro_property!(
140        /// Defines the `DefaultFocusAdorner` property.
141        pub fn default_focus_adorner_property() -> StyledProperty<Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>> {
142            FerroProperty::register::<AdornerLayer, _>("DefaultFocusAdorner", None)
143        }
144    );
145
146    ferro_property!(
147        fn adorned_element_info_property() -> AttachedProperty<Option<AdornedElementInfoHandle>> {
148            FerroProperty::register_attached::<AdornerLayer, Visual, _>("AdornedElementInfo", None)
149        }
150    );
151
152    ferro_property!(
153        fn saved_adorner_layer_property() -> AttachedProperty<Option<SavedAdornerLayer>> {
154            FerroProperty::register_attached::<Visual, Visual, _>("SavedAdornerLayer", None)
155        }
156    );
157
158    ferro_property!(
159        /// The subscriptions to the attachment events of a visual that has
160        /// an adorner (the static event handlers of the reference).
161        fn adorner_attachment_subscriptions_property() -> AttachedProperty<Option<AttachmentSubscriptions>> {
162            FerroProperty::register_attached::<AdornerLayer, Visual, _>("AdornerAttachmentSubscriptions", None)
163        }
164    );
165} }
166
167impl AdornerLayer {
168    /// Creates the layer (internal upstream: the layers are created by
169    /// their [`VisualLayerManager`]).
170    pub(crate) fn new() -> Ref<Self> {
171        instantiate(Self { base: Canvas::construct(), tracking_helper: TransformTrackingHelper::new(false) })
172    }
173
174    pub fn get_adorned_element(adorner: &Visual) -> Option<Ref<Visual>> {
175        ElementRef::resolve(&adorner.get_value(Self::adorned_element_property()))
176    }
177
178    pub fn set_adorned_element(adorner: &Visual, adorned: impl Into<Nullable<Visual>>) {
179        adorner.set_value(Self::adorned_element_property(), ElementRef::from_nullable(adorned.into().0))
180    }
181
182    /// Gets the adorner layer of a visual: the dedicated layer of the
183    /// overlay layer the visual is in, otherwise the layer of the nearest
184    /// layer manager, otherwise the first one found in its top-level.
185    pub fn get_adorner_layer(visual: &Visual) -> Option<Ref<AdornerLayer>> {
186        // Check if the visual is inside an OverlayLayer with a dedicated AdornerLayer
187        for ancestor in visual.get_self_and_visual_ancestors() {
188            if let Some(adorner_layer) = Self::get_direct_adorner_layer(&ancestor) {
189                return Some(adorner_layer);
190            }
191        }
192
193        if let Some(top_level) = TopLevel::get_top_level(Some(visual)) {
194            for descendant in top_level.get_visual_descendants() {
195                if let Some(adorner_layer) = Self::get_direct_adorner_layer(&descendant) {
196                    return Some(adorner_layer);
197                }
198            }
199        }
200
201        None
202    }
203
204    fn get_direct_adorner_layer(visual: &Ref<Visual>) -> Option<Ref<AdornerLayer>> {
205        if let Some(adorner_layer) = visual.cast::<OverlayLayer>().and_then(|overlay| overlay.adorner_layer()) {
206            return Some(adorner_layer);
207        }
208        if let Some(vlm) = visual.cast::<VisualLayerManager>() {
209            return vlm.adorner_layer();
210        }
211        None
212    }
213
214    pub fn get_is_clip_enabled(adorner: &Visual) -> bool {
215        adorner.get_value(Self::is_clip_enabled_property())
216    }
217
218    pub fn set_is_clip_enabled(adorner: &Visual, is_clip_enabled: bool) {
219        adorner.set_value(Self::is_clip_enabled_property(), is_clip_enabled)
220    }
221
222    pub fn get_adorner(visual: &Visual) -> Option<Ref<Control>> {
223        visual.get_value(Self::adorner_property())
224    }
225
226    pub fn set_adorner(visual: &Visual, adorner: impl Into<Nullable<Control>>) {
227        visual.set_value(Self::adorner_property(), adorner.into().0)
228    }
229
230    /// The default control's focus adorner.
231    pub fn default_focus_adorner(&self) -> Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>> {
232        self.get_value(Self::default_focus_adorner_property())
233    }
234
235    pub fn set_default_focus_adorner(&self, value: Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>) {
236        self.set_value(Self::default_focus_adorner_property(), value)
237    }
238
239    fn adorner_changed(e: &FerroPropertyChangedEventArgs<'_>) {
240        let Some(visual) = e.sender().downcast_ref::<Visual>() else { return };
241        let (old_adorner, new_adorner) = e.get_old_and_new_value::<Option<Ref<Control>>>();
242
243        if old_adorner == new_adorner {
244            return;
245        }
246
247        if let Some(old_adorner) = old_adorner {
248            if let Some(subscriptions) = visual.get_value(Self::adorner_attachment_subscriptions_property()) {
249                for subscription in subscriptions.0.iter() {
250                    subscription.dispose();
251                }
252            }
253            visual.clear_value(Self::adorner_attachment_subscriptions_property());
254            Self::detach(visual, &old_adorner);
255        }
256
257        if let Some(new_adorner) = new_adorner {
258            let weak = visual.to_ref().downgrade();
259            let attached = visual.attached_to_visual_tree(move |_| {
260                if let Some(visual) = weak.upgrade() {
261                    Self::visual_on_attached_to_visual_tree(&visual);
262                }
263            });
264            let weak = visual.to_ref().downgrade();
265            let detached = visual.detached_from_visual_tree(move |_| {
266                if let Some(visual) = weak.upgrade() {
267                    Self::visual_on_detached_from_visual_tree(&visual);
268                }
269            });
270            visual.set_value(
271                Self::adorner_attachment_subscriptions_property(),
272                Some(AttachmentSubscriptions(Rc::new([attached, detached]))),
273            );
274            Self::attach(visual, &new_adorner);
275        }
276    }
277
278    fn visual_on_attached_to_visual_tree(visual: &Visual) {
279        if let Some(adorner) = Self::get_adorner(visual) {
280            Self::attach(visual, &adorner);
281        }
282    }
283
284    fn visual_on_detached_from_visual_tree(visual: &Visual) {
285        if let Some(adorner) = Self::get_adorner(visual) {
286            Self::detach(visual, &adorner);
287        }
288    }
289
290    fn attach(visual: &Visual, adorner: &Ref<Control>) {
291        let layer = AdornerLayer::get_adorner_layer(visual);
292        Self::add_visual_adorner(visual, adorner, layer.as_ref());
293        visual.set_value(
294            Self::saved_adorner_layer_property(),
295            layer.map(|layer| SavedAdornerLayer(layer.downgrade())),
296        );
297    }
298
299    fn detach(visual: &Visual, adorner: &Ref<Control>) {
300        let layer = visual.get_value(Self::saved_adorner_layer_property()).and_then(|layer| layer.0.upgrade());
301        Self::remove_visual_adorner(adorner, layer.as_ref());
302        visual.clear_value(Self::saved_adorner_layer_property());
303    }
304
305    fn add_visual_adorner(visual: &Visual, adorner: &Ref<Control>, layer: Option<&Ref<AdornerLayer>>) {
306        let Some(layer) = layer else { return };
307        if layer.children().contains(adorner) {
308            return;
309        }
310
311        Self::set_adorned_element(adorner, visual.to_ref());
312
313        adorner.set_parent(visual.to_ref().upcast::<StyledElement>());
314        layer.children().add(adorner.clone());
315    }
316
317    fn remove_visual_adorner(adorner: &Ref<Control>, layer: Option<&Ref<AdornerLayer>>) {
318        let Some(layer) = layer else { return };
319        if !layer.children().contains(adorner) {
320            return;
321        }
322
323        layer.children().remove(adorner.clone());
324        adorner.set_parent(None);
325    }
326
327    fn adorned_element_changed(e: &FerroPropertyChangedEventArgs<'_>) {
328        let Some(adorner) = e.sender().downcast_ref::<Visual>() else { return };
329        let adorned = Self::get_adorned_element(adorner);
330        if let Some(layer) = adorner.get_visual_parent_of_type::<AdornerLayer>() {
331            layer.update_adorned_element(adorner, adorned);
332        }
333    }
334
335    fn adorner_is_clip_enabled_changed(e: &FerroPropertyChangedEventArgs<'_>) {
336        let Some(adorner) = e.sender().downcast_ref::<Visual>() else { return };
337        if let Some(info) = adorner.get_value(Self::adorned_element_info_property()) {
338            info.0.update_subscription();
339            if let Some(layer) = info.0.layer() {
340                layer.invalidate_measure();
341            }
342        }
343    }
344
345    fn update_clip(&self, control: &Control, adorned: &Visual, is_enabled: bool) {
346        if !is_enabled {
347            control.set_clip(None);
348            return;
349        }
350
351        control.set_clip(AdornerHelper::calculate_adorner_clip(adorned));
352    }
353
354    fn children_collection_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Ref<Control>>) {
355        if e.action == NotifyCollectionChangedAction::Add {
356            for i in e.new_items.iter() {
357                self.update_adorned_element(i, Self::get_adorned_element(i));
358            }
359        } else {
360            // An adorner that left the layer stops watching its adorned
361            // element: the subscription references the element and its
362            // ancestors, which would keep an adorned visual and the adorner
363            // it owns alive through each other. It is renewed when the
364            // adorner is added to a layer again.
365            for i in e.old_items.iter() {
366                if let Some(info) = i.get_value(Self::adorned_element_info_property()) {
367                    if let Some(subscription) = info.0.subscription.borrow_mut().take() {
368                        subscription.dispose();
369                    }
370                }
371            }
372        }
373
374        self.invalidate_arrange();
375    }
376
377    fn update_adorned_element(&self, adorner: &Visual, adorned: Option<Ref<Visual>>) {
378        if let Some(composition_visual) = adorner.composition_visual() {
379            let adorned_visual = adorned.as_ref().and_then(|adorned| adorned.composition_visual());
380            composition_visual.set_adorned_visual(adorned_visual.map(|visual| (*visual).clone()));
381            composition_visual.set_adorner_is_clipped(Self::get_is_clip_enabled(adorner));
382        }
383
384        let mut info = adorner.get_value(Self::adorned_element_info_property());
385
386        if let Some(existing) = &info {
387            if let Some(subscription) = existing.0.subscription.borrow().clone() {
388                subscription.dispose();
389            }
390
391            if adorned.is_none() {
392                adorner.clear_value(Self::adorned_element_info_property());
393            }
394        }
395
396        if let Some(adorned) = adorned {
397            let info = match info.take() {
398                Some(info) => info,
399                None => {
400                    let info = AdornedElementInfoHandle(Rc::new(AdornedElementInfo {
401                        adorner: adorner.to_ref().downgrade(),
402                        layer: RefCell::new(None),
403                        subscription: RefCell::new(None),
404                        adorned_element: RefCell::new(None),
405                    }));
406                    adorner.set_value(Self::adorned_element_info_property(), Some(info.clone()));
407                    info
408                }
409            };
410
411            *info.0.layer.borrow_mut() = Some(self.to_ref().downgrade());
412            *info.0.adorned_element.borrow_mut() = Some(adorned.downgrade());
413            info.0.update_subscription();
414        }
415    }
416}
417
418/// What the layer knows about an adorner: its adorned element and the
419/// subscription to the changes that move it. The back references are weak.
420struct AdornedElementInfo {
421    adorner: WeakRef<Visual>,
422    layer: RefCell<Option<WeakRef<AdornerLayer>>>,
423    subscription: RefCell<Option<Rc<dyn IDisposable>>>,
424    adorned_element: RefCell<Option<WeakRef<Visual>>>,
425}
426
427impl AdornedElementInfo {
428    fn layer(&self) -> Option<Ref<AdornerLayer>> {
429        self.layer.borrow().as_ref().and_then(WeakRef::upgrade)
430    }
431
432    fn adorned_element(&self) -> Option<Ref<Visual>> {
433        self.adorned_element.borrow().as_ref().and_then(WeakRef::upgrade)
434    }
435
436    fn update_subscription(self: &Rc<Self>) {
437        let subscription = self.subscription.borrow_mut().take();
438        if let Some(subscription) = subscription {
439            subscription.dispose();
440        }
441
442        if let Some(adorned_element) = self.adorned_element() {
443            let include_clip =
444                self.adorner.upgrade().map(|adorner| AdornerLayer::get_is_clip_enabled(&adorner)).unwrap_or(true);
445            let weak = Rc::downgrade(self);
446            let subscription =
447                AdornerHelper::subscribe_to_ancestor_property_changes(&adorned_element, include_clip, move || {
448                    if let Some(layer) = weak.upgrade().and_then(|info| info.layer()) {
449                        layer.invalidate_measure();
450                    }
451                });
452            *self.subscription.borrow_mut() = Some(subscription);
453        }
454    }
455}
456
457#[derive(Clone)]
458struct AdornedElementInfoHandle(Rc<AdornedElementInfo>);
459
460impl PartialEq for AdornedElementInfoHandle {
461    fn eq(&self, other: &Self) -> bool {
462        Rc::ptr_eq(&self.0, &other.0)
463    }
464}
465
466#[derive(Clone)]
467struct SavedAdornerLayer(WeakRef<AdornerLayer>);
468
469impl PartialEq for SavedAdornerLayer {
470    fn eq(&self, other: &Self) -> bool {
471        self.0.upgrade() == other.0.upgrade()
472    }
473}
474
475#[derive(Clone)]
476struct AttachmentSubscriptions(Rc<[Rc<dyn IDisposable>; 2]>);
477
478impl PartialEq for AttachmentSubscriptions {
479    fn eq(&self, other: &Self) -> bool {
480        Rc::ptr_eq(&self.0, &other.0)
481    }
482}