Skip to main content

ferroui_controls/primitives/
visual_layer_manager.rs

1use super::{AdornerLayer, LightDismissOverlayLayer, OverlayLayer, PopupOverlayLayer, TextSelectorLayer};
2use crate::{Control, ControlImpl, Decorator, Panel};
3use ferroui_base::controls::ResourcesChangedEventArgs;
4use ferroui_base::input::InputElementImpl;
5use ferroui_base::interactivity::InteractiveImpl;
6use ferroui_base::layout::{LayoutableImpl, LayoutableImplExt};
7use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
8use ferroui_base::{
9    ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, ObjectType, Rect, Ref, Size, StyledElement,
10    StyledElementImpl, StyledElementImplExt, VisualImpl, WeakRef,
11};
12use std::cell::{Cell, RefCell};
13
14const ADORNER_Z_INDEX: i32 = i32::MAX - 100;
15const OVERLAY_Z_INDEX: i32 = i32::MAX - 98;
16const LIGHT_DISMISS_OVERLAY_Z_INDEX: i32 = i32::MAX - 97;
17const TEXT_SELECTOR_LAYER_Z_INDEX: i32 = i32::MAX - 96;
18const POPUP_OVERLAY_Z_INDEX: i32 = i32::MAX - 95;
19
20/// A control that manages multiple layers such as adorners, overlays, text
21/// selectors, and popups.
22#[repr(C)]
23pub struct VisualLayerManager {
24    base: Decorator,
25    logical_root: RefCell<Option<WeakRef<StyledElement>>>,
26    layers: RefCell<Vec<Ref<Control>>>,
27    overlay_layer: RefCell<Option<Ref<OverlayLayer>>>,
28    enable_adorner_layer: Cell<bool>,
29    enable_overlay_layer: Cell<bool>,
30    enable_popup_overlay_layer: Cell<bool>,
31    enable_text_selector_layer: Cell<bool>,
32}
33
34ferro_class!(VisualLayerManager: Decorator);
35ferroui_base::ferro_class_info!(VisualLayerManager { new: VisualLayerManager::new });
36ferro_impl_classes!(VisualLayerManager: FerroObjectImpl, VisualImpl, InteractiveImpl, InputElementImpl, ControlImpl);
37
38impl StyledElementImpl for VisualLayerManager {
39    fn notify_child_resources_changed(this: &Self, e: ResourcesChangedEventArgs) {
40        for layer in this.layers_snapshot() {
41            layer.notify_resources_changed(e, true);
42        }
43
44        Self::parent_notify_child_resources_changed(this, e);
45    }
46
47    fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
48        Self::parent_on_attached_to_logical_tree(this, e);
49        *this.logical_root.borrow_mut() = Some(e.root().downgrade());
50
51        for layer in this.layers_snapshot() {
52            layer.notify_attached_to_logical_tree(e);
53        }
54    }
55
56    fn on_detached_from_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
57        *this.logical_root.borrow_mut() = None;
58        Self::parent_on_detached_from_logical_tree(this, e);
59
60        for layer in this.layers_snapshot() {
61            layer.notify_detached_from_logical_tree(e);
62        }
63    }
64}
65
66impl LayoutableImpl for VisualLayerManager {
67    fn measure_override(this: &Self, available_size: Size) -> Size {
68        for layer in this.layers_snapshot() {
69            layer.measure(available_size);
70        }
71
72        Self::parent_measure_override(this, available_size)
73    }
74
75    fn arrange_override(this: &Self, final_size: Size) -> Size {
76        for layer in this.layers_snapshot() {
77            layer.arrange(Rect::from_size(final_size));
78        }
79
80        Self::parent_arrange_override(this, final_size)
81    }
82}
83
84impl VisualLayerManager {
85    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
86    pub fn construct() -> Self {
87        Self {
88            base: Decorator::construct(),
89            logical_root: RefCell::new(None),
90            layers: RefCell::new(Vec::new()),
91            overlay_layer: RefCell::new(None),
92            enable_adorner_layer: Cell::new(true),
93            enable_overlay_layer: Cell::new(false),
94            enable_popup_overlay_layer: Cell::new(false),
95            enable_text_selector_layer: Cell::new(false),
96        }
97    }
98
99    pub fn new() -> Ref<Self> {
100        instantiate(Self::construct())
101    }
102
103    /// Whether an [`AdornerLayer`] is created for this manager. When
104    /// enabled, the adorner layer is added to the visual tree, providing a
105    /// dedicated layer for rendering adorners.
106    pub fn enable_adorner_layer(&self) -> bool {
107        self.enable_adorner_layer.get()
108    }
109
110    pub fn set_enable_adorner_layer(&self, value: bool) {
111        self.enable_adorner_layer.set(value)
112    }
113
114    /// Whether an [`OverlayLayer`] is created for this manager. When
115    /// enabled, the overlay layer is added to the visual tree, providing a
116    /// dedicated layer for rendering overlay visuals.
117    pub fn enable_overlay_layer(&self) -> bool {
118        self.enable_overlay_layer.get()
119    }
120
121    pub fn set_enable_overlay_layer(&self, value: bool) {
122        self.enable_overlay_layer.set(value)
123    }
124
125    /// Whether a [`PopupOverlayLayer`] is created for this manager
126    /// (internal upstream: set by the top-levels that host their popups in
127    /// an overlay).
128    pub fn enable_popup_overlay_layer(&self) -> bool {
129        self.enable_popup_overlay_layer.get()
130    }
131
132    pub fn set_enable_popup_overlay_layer(&self, value: bool) {
133        self.enable_popup_overlay_layer.set(value)
134    }
135
136    /// Whether a [`TextSelectorLayer`] is created for this manager. When
137    /// enabled, the layer is added to the visual tree, providing a
138    /// dedicated layer for rendering text selection handles.
139    pub fn enable_text_selector_layer(&self) -> bool {
140        self.enable_text_selector_layer.get()
141    }
142
143    pub fn set_enable_text_selector_layer(&self, value: bool) {
144        self.enable_text_selector_layer.set(value)
145    }
146
147    /// The adorner layer, created on first use; `None` when the layer is
148    /// not enabled (internal upstream).
149    pub fn adorner_layer(&self) -> Option<Ref<AdornerLayer>> {
150        if !self.enable_adorner_layer() {
151            return None;
152        }
153
154        let mut rv = self.find_layer::<AdornerLayer>();
155        if rv.is_none() {
156            let layer = AdornerLayer::new();
157            self.add_layer(layer.clone().upcast(), ADORNER_Z_INDEX);
158            rv = Some(layer);
159        }
160        rv
161    }
162
163    /// The layer that hosts popups shown inside the top-level, created on
164    /// first use; `None` when the layer is not enabled (internal upstream).
165    pub fn popup_overlay_layer(&self) -> Option<Ref<PopupOverlayLayer>> {
166        if !self.enable_popup_overlay_layer() {
167            return None;
168        }
169
170        let mut rv = self.find_layer::<PopupOverlayLayer>();
171        if rv.is_none() {
172            let layer = PopupOverlayLayer::new();
173            self.add_layer(layer.clone().upcast(), POPUP_OVERLAY_Z_INDEX);
174            rv = Some(layer);
175        }
176        rv
177    }
178
179    /// The overlay layer, created on first use together with its dedicated
180    /// adorner layer; `None` when the layer is not enabled (internal
181    /// upstream).
182    pub fn overlay_layer(&self) -> Option<Ref<OverlayLayer>> {
183        if !self.enable_overlay_layer() {
184            return None;
185        }
186
187        let existing = self.overlay_layer.borrow().clone();
188        if existing.is_some() {
189            return existing;
190        }
191
192        let overlay_layer = OverlayLayer::new();
193        *self.overlay_layer.borrow_mut() = Some(overlay_layer.clone());
194        let adorner = AdornerLayer::new();
195        overlay_layer.set_adorner_layer(Some(adorner.clone()));
196
197        let panel = Panel::new();
198        panel.children().add(overlay_layer.clone());
199        panel.children().add(adorner);
200
201        self.add_layer(panel.upcast(), OVERLAY_Z_INDEX);
202
203        Some(overlay_layer)
204    }
205
206    /// The layer of the text selection handles, created on first use;
207    /// `None` when the layer is not enabled (internal upstream).
208    pub fn text_selector_layer(&self) -> Option<Ref<TextSelectorLayer>> {
209        if !self.enable_text_selector_layer() {
210            return None;
211        }
212
213        let mut rv = self.find_layer::<TextSelectorLayer>();
214        if rv.is_none() {
215            let layer = TextSelectorLayer::new();
216            self.add_layer(layer.clone().upcast(), TEXT_SELECTOR_LAYER_Z_INDEX);
217            rv = Some(layer);
218        }
219        rv
220    }
221
222    /// The layer that dismisses popups when the user clicks outside them,
223    /// created on first use (internal upstream).
224    pub fn light_dismiss_overlay_layer(&self) -> Ref<LightDismissOverlayLayer> {
225        match self.find_layer::<LightDismissOverlayLayer>() {
226            Some(rv) => rv,
227            None => {
228                let rv = LightDismissOverlayLayer::new();
229                self.add_layer(rv.clone().upcast(), LIGHT_DISMISS_OVERLAY_Z_INDEX);
230                rv
231            }
232        }
233    }
234
235    fn layers_snapshot(&self) -> Vec<Ref<Control>> {
236        self.layers.borrow().clone()
237    }
238
239    fn find_layer<T: ObjectType>(&self) -> Option<Ref<T>> {
240        self.layers.borrow().iter().find_map(|layer| layer.cast::<T>())
241    }
242
243    fn add_layer(&self, layer: Ref<Control>, z_index: i32) {
244        self.layers.borrow_mut().push(layer.clone());
245        layer.set_parent(self.to_ref());
246        layer.set_z_index(z_index);
247        self.visual_children().add(layer.clone().upcast());
248        if self.is_attached_to_logical_tree() {
249            let logical_root = self
250                .logical_root
251                .borrow()
252                .as_ref()
253                .and_then(WeakRef::upgrade)
254                .expect("an attached layer manager knows its logical root");
255            layer.notify_attached_to_logical_tree(&LogicalTreeAttachmentEventArgs::new(
256                logical_root,
257                layer.clone().upcast(),
258                Some(self.to_ref().upcast()),
259            ));
260        }
261        self.invalidate_arrange();
262    }
263}