zng_wgt_layer/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Window layers and popup.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12zng_wgt::enable_widget_macros!();
13
14use parking_lot::Mutex;
15use zng_app::widget::border::CORNER_RADIUS_VAR;
16use zng_app::widget::info::WIDGET_TREE_CHANGED_EVENT;
17use zng_ext_input::mouse::MOUSE;
18use zng_ext_input::touch::TOUCH;
19use zng_ext_window::WINDOW_Ext as _;
20use zng_var::{ContextInitHandle, animation};
21use zng_view_api::window::FrameId;
22use zng_wgt::prelude::*;
23
24use std::sync::Arc;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::{fmt, mem, ops};
27
28pub mod popup;
29
30struct LayersCtx {
31    items: EditableUiVecRef,
32}
33
34command! {
35    /// Insert a layer widget on the scoped window.
36    ///
37    /// # Params
38    ///
39    /// The parameter must be a tuple with the `LAYERS` service inputs:
40    ///
41    /// * `(LayerIndex, WidgetFn<()>)` - Calls the widget function in the window context, then calls [`LAYERS.insert`].
42    /// * `(LayerIndex, WidgetId, AnchorMode, WidgetFn<()>)` - Calls the widget function in the window context,
43    ///    then calls [`LAYERS.insert_anchored`].
44    ///
45    /// If the parameter type does not match any of the above a debug trace is logged.
46    ///
47    /// [`LAYERS.insert`]: LAYERS::insert
48    /// [`LAYERS.insert_anchored`]: LAYERS::insert_anchored
49    pub static LAYERS_INSERT_CMD;
50
51    /// Remove a layer widget on the scoped window.
52    ///
53    /// # Params
54    ///
55    /// * `WidgetId` - Calls [`LAYERS.remove`].
56    ///
57    /// If the parameter type does not match any of the above a debug trace is logged.
58    ///
59    /// [`LAYERS.remove`]: LAYERS::remove
60    pub static LAYERS_REMOVE_CMD;
61}
62
63/// Windows layers.
64///
65/// The window layers is a z-order stacking panel that fills the window content area, widgets can be inserted
66/// with a *z-index* that is the [`LayerIndex`]. The inserted widgets parent is the window root widget and
67/// it is affected by the context properties set on the window only.
68///
69/// # Layout & Render
70///
71/// Layered widgets are measured and arranged using the same constraints as the window root widget, the desired
72/// size is discarded, only the root widget desired size can affect the window size.
73///
74/// Layered widgets are all layout and rendered after the window content, this means that the [`WidgetBoundsInfo`]
75/// of normal widgets are always up-to-date when the layered widget is arranged and rendered, so if you
76/// implement custom layouts that align the layered widget with a normal widget using the info values it will always be in sync with
77/// a single layout pass, see [`insert_anchored`] for more details.
78///
79/// Note that this single pass behavior only works automatically in the [`AnchorMode`], to implement custom
80/// sizing and positioning based on the anchor you must wrap the layered widget with a custom widget node, this
81/// is because the default widget implementation skips layout and render when it was not requested for the widget
82/// or descendants. See the [`insert_anchored`] source code for an example.
83///
84///
85/// [`insert_anchored`]: Self::insert_anchored
86/// [`WidgetBoundsInfo`]: zng_wgt::prelude::WidgetBoundsInfo
87pub struct LAYERS;
88impl LAYERS {
89    /// Insert the `widget` in the layer identified by a [`LayerIndex`].
90    ///
91    /// If the `layer` variable updates the widget is moved to the new layer, if multiple widgets
92    /// are inserted in the same layer the later inserts are on top of the previous.
93    ///
94    /// If the `widget` node is not a full widget after init it is immediately deinited and removed. Only full
95    /// widgets are allowed, use this method when you know the node is a widget and know the widget ID so it can
96    /// be removed later. Use [`insert_node`] to insert nodes that may not always be widgets.
97    ///
98    /// [`insert_node`]: Self::insert_node
99    pub fn insert(&self, layer: impl IntoVar<LayerIndex>, widget: impl IntoUiNode) {
100        let layer = layer.into_var().current_context();
101        self.insert_impl(layer, widget.into_node());
102    }
103    fn insert_impl(&self, layer: Var<LayerIndex>, widget: UiNode) {
104        let widget = match_widget(widget, move |widget, op| match op {
105            UiNodeOp::Init => {
106                widget.init();
107
108                // widget may only become a full widget after init (ArcNode)
109
110                if let Some(mut wgt) = widget.node().as_widget() {
111                    wgt.with_context(WidgetUpdateMode::Bubble, || {
112                        WIDGET.set_state(*LAYER_INDEX_ID, layer.get());
113                        WIDGET.sub_var(&layer);
114                    });
115                } else {
116                    *widget.node() = UiNode::nil();
117                    LAYERS.cleanup();
118                }
119            }
120            UiNodeOp::Update { .. } => {
121                if let Some(mut wgt) = widget.node().as_widget()
122                    && let Some(index) = layer.get_new()
123                {
124                    wgt.with_context(WidgetUpdateMode::Bubble, || {
125                        WIDGET.set_state(*LAYER_INDEX_ID, index);
126                        SORTING_LIST.invalidate_sort();
127                    });
128                }
129            }
130            _ => {}
131        });
132
133        let r = WINDOW.with_state(|s| match s.get(*WINDOW_LAYERS_ID) {
134            Some(open) => {
135                // window already open
136                open.items.push(widget);
137                Ok(())
138            }
139            None => Err(widget),
140        });
141        if let Err(widget) = r {
142            WINDOW.with_state_mut(|mut s| {
143                // window not open yet, `widget` will be inited with the window
144                s.entry(*WINDOW_PRE_INIT_LAYERS_ID).or_default().push(Mutex::new(widget));
145            });
146        }
147    }
148
149    /// Like [`insert`], but does not fail if `maybe_widget` is not a full widget.
150    ///
151    /// If the `maybe_widget` is not a full widget after the first init, it is upgraded to a full widget. The
152    /// widget ID (existing or upgraded) is set on a response var that can be used to remove the node.
153    ///
154    /// This is the equivalent of calling [`insert`] with the node wrapped in [`UiNode::init_widget`].
155    ///
156    /// [`insert`]: Self::insert
157    /// [`UiNode::init_widget`]: zng_wgt::prelude::UiNode::init_widget
158    pub fn insert_node(&self, layer: impl IntoVar<LayerIndex>, maybe_widget: impl IntoUiNode) -> ResponseVar<WidgetId> {
159        let (widget, rsp) = maybe_widget.into_node().init_widget();
160        self.insert(layer, widget);
161        rsp
162    }
163
164    /// Insert the `widget` in the layer and *anchor* it to the offset/transform of another widget.
165    ///
166    /// The `anchor` is the ID of another widget, the inserted `widget` will be offset/transform so that it aligns
167    /// with the `anchor` widget top-left. The `mode` is a value of [`AnchorMode`] that defines if the `widget` will
168    /// receive the full transform or just the offset.
169    ///
170    /// If the `anchor` widget is not found the `widget` is anchored to the window root. If the `widget`
171    /// is not a full widget after init it is immediately deinited and removed. If you don't
172    /// know the widget ID use [`insert_anchored_node`] instead to receive the ID so the layer can be removed.
173    ///
174    /// [`insert_anchored_node`]: Self::insert_anchored_node
175    pub fn insert_anchored(
176        &self,
177        layer: impl IntoVar<LayerIndex>,
178        anchor: impl IntoVar<WidgetId>,
179        mode: impl IntoVar<AnchorMode>,
180
181        widget: impl IntoUiNode,
182    ) {
183        let layer = layer.into_var().current_context();
184        let anchor = anchor.into_var().current_context();
185        let mode = mode.into_var().current_context();
186
187        self.insert_anchored_impl(layer, anchor, mode, widget.into_node())
188    }
189    fn insert_anchored_impl(&self, layer: Var<LayerIndex>, anchor: Var<WidgetId>, mode: Var<AnchorMode>, widget: UiNode) {
190        let mut _info_changed_handle = None;
191        let mut mouse_pos_handle = None;
192
193        let mut cursor_once_pending = false;
194        let mut anchor_info = None;
195        let mut offset = (PxPoint::zero(), PxPoint::zero());
196        let mut cursor_bounds = None;
197        let mut interactivity = false;
198
199        let transform_key = FrameValueKey::new_unique();
200        let mut corner_radius_ctx_handle = None;
201
202        let widget = with_anchor_id(widget, anchor.clone());
203
204        fn get_anchor_info(anchor: WidgetId) -> (WidgetBoundsInfo, WidgetBorderInfo) {
205            let tree = WINDOW.info();
206            let w = tree.get(anchor).unwrap_or_else(|| tree.root());
207            (w.bounds_info(), w.border_info())
208        }
209
210        let widget = match_widget(widget, move |widget, op| match op {
211            UiNodeOp::Init => {
212                widget.init();
213
214                if let Some(mut wgt) = widget.node().as_widget() {
215                    wgt.with_context(WidgetUpdateMode::Bubble, || {
216                        WIDGET.sub_var(&anchor).sub_var(&mode);
217
218                        anchor_info = Some(get_anchor_info(anchor.get()));
219
220                        interactivity = mode.with(|m| m.interactivity);
221                        let win_id = WINDOW.id();
222                        _info_changed_handle = Some(
223                            WIDGET_TREE_CHANGED_EVENT.subscribe_when(UpdateOp::Update, WIDGET.id(), move |args| {
224                                !args.is_update && args.tree.window_id() == win_id
225                            }),
226                        );
227
228                        if mode.with(|m| matches!(&m.transform, AnchorTransform::Cursor { .. })) {
229                            mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
230                        } else if mode.with(|m| matches!(&m.transform, AnchorTransform::CursorOnce { .. })) {
231                            cursor_once_pending = true;
232                        }
233                    })
234                } else {
235                    widget.deinit();
236                    *widget.node() = UiNode::nil();
237                    // cleanup requested by the `insert` node.
238                }
239            }
240            UiNodeOp::Deinit => {
241                widget.deinit();
242
243                anchor_info = None;
244                _info_changed_handle = None;
245                mouse_pos_handle = None;
246                corner_radius_ctx_handle = None;
247                cursor_once_pending = false;
248            }
249            UiNodeOp::Info { info } => {
250                if interactivity && let Some(mut wgt) = widget.node().as_widget() {
251                    let wgt = wgt.id();
252                    let anchor = anchor.get();
253                    let querying = AtomicBool::new(false);
254                    info.push_interactivity_filter(move |args| {
255                        if args.info.id() == wgt {
256                            if querying.swap(true, Ordering::Relaxed) {
257                                return Interactivity::ENABLED; // avoid recursion.
258                            }
259                            let _q = RunOnDrop::new(|| querying.store(false, Ordering::Relaxed));
260                            args.info
261                                .tree()
262                                .get(anchor)
263                                .map(|a| a.interactivity())
264                                .unwrap_or(Interactivity::BLOCKED)
265                        } else {
266                            Interactivity::ENABLED
267                        }
268                    });
269                }
270            }
271            UiNodeOp::Update { .. } => {
272                if let Some(mut wgt) = widget.node().as_widget() {
273                    wgt.with_context(WidgetUpdateMode::Bubble, || {
274                        if let Some(anchor) = anchor.get_new() {
275                            anchor_info = Some(get_anchor_info(anchor));
276                            if mode.with(|m| m.interactivity) {
277                                WIDGET.update_info();
278                            }
279                            WIDGET.layout().render();
280                        }
281                        if let Some(mode) = mode.get_new() {
282                            if mode.interactivity != interactivity {
283                                interactivity = mode.interactivity;
284                                WIDGET.update_info();
285                            }
286                            if matches!(&mode.transform, AnchorTransform::Cursor { .. }) {
287                                if mouse_pos_handle.is_none() {
288                                    mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
289                                }
290                                cursor_once_pending = false;
291                            } else {
292                                cursor_once_pending = matches!(&mode.transform, AnchorTransform::CursorOnce { .. });
293                                mouse_pos_handle = None;
294                            }
295                            WIDGET.layout().render();
296                        } else if mouse_pos_handle.is_some() && MOUSE.position().is_new() {
297                            WIDGET.layout();
298                        }
299                    });
300
301                    WIDGET_TREE_CHANGED_EVENT.each_update(true, |args| {
302                        if !args.is_update && args.tree.window_id() == WINDOW.id() {
303                            anchor_info = Some(get_anchor_info(anchor.get()));
304                        }
305                    });
306                }
307            }
308            UiNodeOp::Measure { wm, desired_size } => {
309                widget.delegated();
310
311                if let Some((bounds, border)) = &anchor_info {
312                    let mode = mode.get();
313
314                    if !mode.visibility || bounds.inner_size() != PxSize::zero() {
315                        let mut constraints = match mode.min_size {
316                            AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
317                            AnchorSize::Window => LAYOUT.constraints(),
318                            AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
319                            AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
320                            AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
321                        };
322                        if mode.max_size != mode.min_size {
323                            constraints = match mode.max_size {
324                                AnchorSize::Unbounded => constraints.with_unbounded(),
325                                AnchorSize::Window => {
326                                    let w = LAYOUT.constraints();
327                                    constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
328                                }
329                                AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
330                                AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
331                                AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
332                            };
333                        }
334
335                        *desired_size = LAYOUT.with_constraints(constraints, || widget.measure(wm));
336                    }
337                }
338            }
339            UiNodeOp::Layout { wl, final_size } => {
340                widget.delegated();
341
342                if let Some((bounds, border)) = &anchor_info {
343                    let mode = mode.get();
344
345                    if !mode.visibility || bounds.inner_size() != PxSize::zero() {
346                        // if we don't link visibility or anchor is not collapsed.
347
348                        let mut constraints = match mode.min_size {
349                            AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
350                            AnchorSize::Window => LAYOUT.constraints(),
351                            AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
352                            AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
353                            AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
354                        };
355                        if mode.max_size != mode.min_size {
356                            constraints = match mode.max_size {
357                                AnchorSize::Unbounded => constraints.with_unbounded(),
358                                AnchorSize::Window => {
359                                    let w = LAYOUT.constraints();
360                                    constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
361                                }
362                                AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
363                                AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
364                                AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
365                            };
366                        }
367
368                        let layer_size = LAYOUT.with_constraints(constraints, || {
369                            if mode.corner_radius {
370                                let mut cr = border.corner_radius();
371                                if let AnchorSize::InnerBorder = mode.max_size {
372                                    cr = cr.deflate(border.offsets());
373                                }
374                                CORNER_RADIUS_VAR.with_context_var(
375                                    corner_radius_ctx_handle.get_or_insert_with(ContextInitHandle::new).clone(),
376                                    cr,
377                                    || BORDER.with_corner_radius(|| widget.layout(wl)),
378                                )
379                            } else {
380                                widget.layout(wl)
381                            }
382                        });
383
384                        if let Some((p, include_touch, bounded, update)) = match &mode.transform {
385                            AnchorTransform::Cursor {
386                                offset,
387                                include_touch,
388                                bounds,
389                            } => Some((offset, include_touch, bounds, true)),
390                            AnchorTransform::CursorOnce {
391                                offset,
392                                include_touch,
393                                bounds,
394                            } => Some((offset, include_touch, bounds, mem::take(&mut cursor_once_pending))),
395                            _ => None,
396                        } {
397                            // cursor transform mode, only visible if cursor over window
398                            const NO_POS_X: Px = Px::MIN;
399                            if update {
400                                let pos = if *include_touch {
401                                    let oldest_touch = TOUCH.positions().with(|p| p.iter().min_by_key(|p| p.start_time).cloned());
402                                    match (oldest_touch, MOUSE.position().get()) {
403                                        (Some(t), Some(m)) => {
404                                            let window_id = WINDOW.id();
405                                            if t.window_id == window_id && m.window_id == window_id {
406                                                Some(if t.update_time > m.timestamp { t.position } else { m.position })
407                                            } else {
408                                                None
409                                            }
410                                        }
411                                        (Some(t), None) => {
412                                            if t.window_id == WINDOW.id() {
413                                                Some(t.position)
414                                            } else {
415                                                None
416                                            }
417                                        }
418                                        (None, Some(m)) => {
419                                            if m.window_id == WINDOW.id() {
420                                                Some(m.position)
421                                            } else {
422                                                None
423                                            }
424                                        }
425                                        _ => None,
426                                    }
427                                } else if let Some(p) = MOUSE.position().get() {
428                                    if p.window_id == WINDOW.id() { Some(p.position) } else { None }
429                                } else {
430                                    None
431                                };
432
433                                if let Some(pos) = pos {
434                                    let fct = LAYOUT.scale_factor();
435                                    let pos = pos.to_px(fct);
436
437                                    let (cursor_size, cursor_spot) = {
438                                        let vars = WINDOW.vars();
439                                        #[cfg(feature = "image")]
440                                        if let Some((img, spot)) = vars.actual_cursor_img().get() {
441                                            (img.size(), spot)
442                                        } else {
443                                            vars.cursor().with(|s| s.icon()).map(|i| i.size_and_spot(fct)).unwrap_or_default()
444                                        }
445
446                                        #[cfg(not(feature = "image"))]
447                                        {
448                                            vars.cursor().with(|s| s.icon()).map(|i| i.size_and_spot(fct)).unwrap_or_default()
449                                        }
450                                    };
451                                    let cursor_rect = PxRect::new((pos - cursor_spot).to_point(), cursor_size);
452                                    let place = cursor_rect.origin
453                                        + LAYOUT
454                                            .with_constraints(PxConstraints2d::new_exact_size(cursor_rect.size), || p.place.layout())
455                                            .to_vector();
456                                    let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
457
458                                    if let Some(sides) = bounded {
459                                        let sides = LAYOUT
460                                            .with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || sides.layout());
461                                        // render will transform this to anchor and apply to place point
462                                        cursor_bounds = Some(PxRect::new(
463                                            -PxPoint::new(sides.left, sides.top),
464                                            bounds.inner_size() + PxSize::new(sides.horizontal(), sides.vertical()),
465                                        ));
466                                    } else {
467                                        cursor_bounds = None;
468                                    }
469
470                                    let o = (place, origin);
471                                    if offset != o {
472                                        offset = o;
473                                        WIDGET.render_update();
474                                    }
475
476                                    *final_size = layer_size;
477                                    return;
478                                } else {
479                                    // collapsed signal (permanent if `CursorOnce`)
480                                    offset.0.x = NO_POS_X;
481                                }
482                            } else {
483                                // offset already set
484                                if offset.0.x != NO_POS_X {
485                                    // and it is not collapsed `CursorOnce`
486                                    *final_size = layer_size;
487                                    return;
488                                }
489                            }
490                        } else {
491                            // other transform modes, will be visible
492                            let o = match &mode.transform {
493                                AnchorTransform::InnerOffset(p) => {
494                                    let place =
495                                        LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || p.place.layout());
496                                    let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
497                                    (place, origin)
498                                }
499                                AnchorTransform::InnerBorderOffset(p) => {
500                                    let place = LAYOUT
501                                        .with_constraints(PxConstraints2d::new_exact_size(border.inner_size(bounds)), || p.place.layout());
502                                    let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
503                                    (place, origin)
504                                }
505                                AnchorTransform::OuterOffset(p) => {
506                                    let place =
507                                        LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.outer_size()), || p.place.layout());
508                                    let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
509                                    (place, origin)
510                                }
511                                _ => (PxPoint::zero(), PxPoint::zero()),
512                            };
513                            if offset != o {
514                                offset = o;
515                                WIDGET.render_update();
516                            }
517
518                            *final_size = layer_size;
519                        }
520                        return;
521                    }
522                }
523
524                if let Some(mut wgt) = widget.node().as_widget() {
525                    wgt.with_context(WidgetUpdateMode::Bubble, || {
526                        wl.collapse();
527                    });
528                }
529            }
530            UiNodeOp::Render { frame } => {
531                widget.delegated();
532
533                if let Some((bounds_info, border_info)) = &anchor_info {
534                    let mode = mode.get();
535                    if !mode.visibility || bounds_info.rendered().is_some() {
536                        let mut push_reference_frame = |mut transform: PxTransform, is_translate_only: bool| {
537                            if mode.viewport_bound {
538                                transform = adjust_viewport_bound(transform, widget.node());
539                            }
540                            frame.push_reference_frame(
541                                transform_key.into(),
542                                transform_key.bind(transform, true),
543                                is_translate_only,
544                                false,
545                                |frame| widget.render(frame),
546                            );
547                        };
548
549                        match mode.transform {
550                            AnchorTransform::InnerOffset(_) => {
551                                let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
552                                let offset = place_in_window - offset.1;
553
554                                push_reference_frame(PxTransform::from(offset), true);
555                            }
556                            AnchorTransform::InnerBorderOffset(_) => {
557                                let place_in_window = border_info
558                                    .inner_transform(bounds_info)
559                                    .transform_point(offset.0)
560                                    .unwrap_or_default();
561                                let offset = place_in_window - offset.1;
562
563                                push_reference_frame(PxTransform::from(offset), true);
564                            }
565                            AnchorTransform::OuterOffset(_) => {
566                                let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
567                                let offset = place_in_window - offset.1;
568
569                                push_reference_frame(PxTransform::from(offset), true);
570                            }
571                            AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
572                                let (mut place, origin) = offset;
573
574                                if let Some(b) = cursor_bounds {
575                                    // transform `place` to bounds space, clamp to bounds, transform back to window space.
576                                    let transform = bounds_info.inner_transform();
577                                    if let Some(inverse) = transform.inverse()
578                                        && let Some(p) = inverse.transform_point(place)
579                                    {
580                                        let bound_p = PxPoint::new(p.x.clamp(b.min_x(), b.max_x()), p.y.clamp(b.min_y(), b.max_y()));
581                                        if p != bound_p
582                                            && let Some(p) = transform.transform_point(bound_p)
583                                        {
584                                            place = p;
585                                        }
586                                    }
587                                }
588                                let offset = place - origin;
589
590                                push_reference_frame(PxTransform::from(offset), true);
591                            }
592                            AnchorTransform::InnerTransform => {
593                                push_reference_frame(bounds_info.inner_transform(), false);
594                            }
595                            AnchorTransform::InnerBorderTransform => {
596                                push_reference_frame(border_info.inner_transform(bounds_info), false);
597                            }
598                            AnchorTransform::OuterTransform => {
599                                push_reference_frame(bounds_info.outer_transform(), false);
600                            }
601                            _ => widget.render(frame),
602                        }
603                    } else {
604                        // anchor not visible, call render to properly hide or collapse (if collapsed during layout)
605                        frame.hide(|frame| widget.render(frame));
606
607                        if frame.frame_id() == FrameId::first() && anchor.get() == WIDGET.id() {
608                            // anchor is the root widget, the only widget that is not done rendering before the layers
609                            // if only the first frame is rendered the layer can remain hidden, so ensure a second frame renders.
610                            WIDGET.render();
611                        }
612                    }
613                } else {
614                    widget.render(frame);
615                }
616            }
617            UiNodeOp::RenderUpdate { update } => {
618                if let Some((bounds_info, border_info)) = &anchor_info {
619                    let mode = mode.get();
620                    if !mode.visibility || bounds_info.rendered().is_some() {
621                        let mut with_transform = |mut transform: PxTransform| {
622                            if mode.viewport_bound {
623                                transform = adjust_viewport_bound(transform, widget.node());
624                            }
625                            update.with_transform(transform_key.update(transform, true), false, |update| widget.render_update(update));
626                        };
627
628                        match mode.transform {
629                            AnchorTransform::InnerOffset(_) => {
630                                let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
631                                let offset = place_in_window - offset.1;
632                                with_transform(PxTransform::from(offset));
633                            }
634                            AnchorTransform::InnerBorderOffset(_) => {
635                                let place_in_window = border_info
636                                    .inner_transform(bounds_info)
637                                    .transform_point(offset.0)
638                                    .unwrap_or_default();
639                                let offset = place_in_window - offset.1;
640                                with_transform(PxTransform::from(offset));
641                            }
642                            AnchorTransform::OuterOffset(_) => {
643                                let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
644                                let offset = place_in_window - offset.1;
645                                with_transform(PxTransform::from(offset));
646                            }
647                            AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
648                                let offset = offset.0 - offset.1;
649                                with_transform(PxTransform::from(offset));
650                            }
651                            AnchorTransform::InnerTransform => {
652                                with_transform(bounds_info.inner_transform());
653                            }
654                            AnchorTransform::InnerBorderTransform => {
655                                with_transform(border_info.inner_transform(bounds_info));
656                            }
657                            AnchorTransform::OuterTransform => {
658                                with_transform(bounds_info.outer_transform());
659                            }
660                            _ => widget.render_update(update),
661                        }
662                    }
663                }
664            }
665            _ => {}
666        });
667        self.insert_impl(layer, widget);
668    }
669
670    /// Like [`insert_anchored`], but does not fail if `maybe_widget` is not a full widget.
671    ///
672    /// If the `maybe_widget` is not a full widget after the first init, it is upgraded to a full widget. The
673    /// widget ID is set on a response var that can be used to remove the node.
674    ///
675    /// This is the equivalent of calling [`insert_anchored`] with the node wrapped in [`UiNode::init_widget`].
676    ///
677    /// [`insert`]: Self::insert
678    ///
679    /// [`insert_anchored`]: Self::insert_anchored
680    /// [`UiNode::init_widget`]: zng_wgt::prelude::UiNode::init_widget
681    pub fn insert_anchored_node(
682        &self,
683        layer: impl IntoVar<LayerIndex>,
684        anchor: impl IntoVar<WidgetId>,
685        mode: impl IntoVar<AnchorMode>,
686
687        maybe_widget: impl IntoUiNode,
688    ) -> ResponseVar<WidgetId> {
689        let (widget, rsp) = maybe_widget.into_node().init_widget();
690        self.insert_anchored(layer, anchor, mode, widget);
691        rsp
692    }
693
694    /// Remove the widget in the next update.
695    ///
696    /// The `id` must the widget id of a previous inserted widget, nothing happens if the widget is not found.
697    ///
698    /// See also [`remove_node`] for removing nodes inserted by `_node` variants.
699    ///
700    /// [`remove_node`]: Self::remove_node
701    pub fn remove(&self, id: impl Into<WidgetId>) {
702        WINDOW.with_state(|s| {
703            s.req(*WINDOW_LAYERS_ID).items.remove(id);
704        });
705    }
706
707    /// Remove the widget in the next update.
708    ///
709    /// If the `id` has not responded yet it will be removed as soon as it initializes. This can happen if
710    /// the remove request is made before an update cycle allows time for the inserted widget first init.
711    pub fn remove_node(&self, id: ResponseVar<WidgetId>) {
712        if let Some(id) = id.rsp() {
713            self.remove(id);
714        } else {
715            let items = WINDOW.with_state(|s| s.req(*WINDOW_LAYERS_ID).items.clone());
716            id.hook(move |a| {
717                match a.value() {
718                    zng_var::Response::Waiting => true,
719                    zng_var::Response::Done(id) => {
720                        // remove item and hook
721                        items.remove(*id);
722                        false
723                    }
724                }
725            })
726            .perm();
727        }
728    }
729
730    /// Gets a read-only var that tracks the anchor widget in a layered widget context.
731    pub fn anchor_id(&self) -> Var<Option<WidgetId>> {
732        ANCHOR_ID_VAR.read_only()
733    }
734
735    fn cleanup(&self) {
736        WINDOW.with_state(|s| {
737            s.req(*WINDOW_LAYERS_ID).items.retain(|n| n.as_widget().is_some());
738        });
739    }
740}
741
742fn adjust_viewport_bound(transform: PxTransform, widget: &mut UiNode) -> PxTransform {
743    let window_bounds = WINDOW.vars().actual_size_px().get();
744    let wgt_bounds = PxBox::from(
745        widget
746            .as_widget()
747            .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.bounds().outer_size()))
748            .unwrap_or_else(PxSize::zero),
749    );
750    let wgt_bounds = transform.outer_transformed(wgt_bounds).unwrap_or_default();
751
752    let x_underflow = -wgt_bounds.min.x.min(Px(0));
753    let x_overflow = (wgt_bounds.max.x - window_bounds.width).max(Px(0));
754    let y_underflow = -wgt_bounds.min.y.min(Px(0));
755    let y_overflow = (wgt_bounds.max.y - window_bounds.height).max(Px(0));
756
757    let x = x_underflow - x_overflow;
758    let y = y_underflow - y_overflow;
759
760    let correction = PxVector::new(x, y);
761
762    transform.then_translate(correction.cast())
763}
764
765fn with_anchor_id(child: impl IntoUiNode, anchor: Var<WidgetId>) -> UiNode {
766    let mut ctx = Some(Arc::new(anchor.map(|id| Some(*id)).into()));
767    let mut id = None;
768    match_widget(child, move |c, op| {
769        let mut is_deinit = false;
770        match &op {
771            UiNodeOp::Init => {
772                id = Some(ContextInitHandle::new());
773            }
774            UiNodeOp::Deinit => {
775                is_deinit = true;
776            }
777            _ => {}
778        }
779        ANCHOR_ID_VAR.with_context(id.clone().expect("node not inited"), &mut ctx, || c.op(op));
780
781        if is_deinit {
782            id = None;
783        }
784    })
785}
786
787context_var! {
788    static ANCHOR_ID_VAR: Option<WidgetId> = None;
789}
790
791static_id! {
792    static ref WINDOW_PRE_INIT_LAYERS_ID: StateId<Vec<Mutex<UiNode>>>;
793    static ref WINDOW_LAYERS_ID: StateId<LayersCtx>;
794    static ref LAYER_INDEX_ID: StateId<LayerIndex>;
795}
796
797/// Represents a layer in a window.
798///
799/// See [`LAYERS`] for more information.
800#[derive(Default, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
801pub struct LayerIndex(pub u32);
802impl LayerIndex {
803    /// The top-most layer.
804    ///
805    /// Only widgets that are pretending to be a child window should use this layer, including menus,
806    /// drop-downs, pop-ups and tooltips.
807    ///
808    /// This is the [`u32::MAX`] value.
809    pub const TOP_MOST: LayerIndex = LayerIndex(u32::MAX);
810
811    /// The layer for *adorner* display items.
812    ///
813    /// Adorner widgets are related to another widget but not as a visual part of it, examples of adorners
814    /// are resize handles in a widget visual editor, or an interactive help/guide feature.
815    ///
816    /// This is the `TOP_MOST - u16::MAX` value.
817    pub const ADORNER: LayerIndex = LayerIndex(Self::TOP_MOST.0 - u16::MAX as u32);
818
819    /// The default layer, just above the normal window content.
820    ///
821    /// This is the `0` value.
822    pub const DEFAULT: LayerIndex = LayerIndex(0);
823
824    /// Compute `self + other` saturating at the [`TOP_MOST`] bound instead of overflowing.
825    ///
826    /// [`TOP_MOST`]: Self::TOP_MOST
827    pub fn saturating_add(self, other: impl Into<LayerIndex>) -> Self {
828        Self(self.0.saturating_add(other.into().0))
829    }
830
831    /// Compute `self - other` saturating at the [`DEFAULT`] bound instead of overflowing.
832    ///
833    /// [`DEFAULT`]: Self::DEFAULT
834    pub fn saturating_sub(self, other: impl Into<LayerIndex>) -> Self {
835        Self(self.0.saturating_sub(other.into().0))
836    }
837
838    /// Gets the const name of this value.
839    pub fn name(self) -> Option<&'static str> {
840        if self == Self::DEFAULT {
841            Some("DEFAULT")
842        } else if self == Self::TOP_MOST {
843            Some("TOP_MOST")
844        } else if self == Self::ADORNER {
845            Some("ADORNER")
846        } else {
847            None
848        }
849    }
850}
851impl fmt::Debug for LayerIndex {
852    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
853        if let Some(name) = self.name() {
854            if f.alternate() {
855                write!(f, "LayerIndex::")?;
856            }
857            write!(f, "{name}")
858        } else {
859            write!(f, "LayerIndex({})", self.0)
860        }
861    }
862}
863impl std::str::FromStr for LayerIndex {
864    type Err = <u32 as std::str::FromStr>::Err;
865
866    fn from_str(s: &str) -> Result<Self, Self::Err> {
867        match s {
868            "DEFAULT" => Ok(Self::DEFAULT),
869            "TOP_MOST" => Ok(Self::TOP_MOST),
870            "ADORNER" => Ok(Self::ADORNER),
871            n => Ok(Self(n.parse()?)),
872        }
873    }
874}
875impl_from_and_into_var! {
876    fn from(index: u32) -> LayerIndex {
877        LayerIndex(index)
878    }
879}
880/// Calls [`LayerIndex::saturating_add`].
881impl<T: Into<Self>> ops::Add<T> for LayerIndex {
882    type Output = Self;
883
884    fn add(self, rhs: T) -> Self::Output {
885        self.saturating_add(rhs)
886    }
887}
888/// Calls [`LayerIndex::saturating_sub`].
889impl<T: Into<Self>> ops::Sub<T> for LayerIndex {
890    type Output = Self;
891
892    fn sub(self, rhs: T) -> Self::Output {
893        self.saturating_sub(rhs)
894    }
895}
896/// Calls [`LayerIndex::saturating_add`].
897impl<T: Into<Self>> ops::AddAssign<T> for LayerIndex {
898    fn add_assign(&mut self, rhs: T) {
899        *self = *self + rhs;
900    }
901}
902/// Calls [`LayerIndex::saturating_sub`].
903impl<T: Into<Self>> ops::SubAssign<T> for LayerIndex {
904    fn sub_assign(&mut self, rhs: T) {
905        *self = *self - rhs;
906    }
907}
908impl ops::Mul<Factor> for LayerIndex {
909    type Output = Self;
910
911    fn mul(self, rhs: Factor) -> Self::Output {
912        LayerIndex(self.0 * rhs)
913    }
914}
915impl ops::MulAssign<Factor> for LayerIndex {
916    fn mul_assign(&mut self, rhs: Factor) {
917        self.0 *= rhs;
918    }
919}
920impl ops::Div<Factor> for LayerIndex {
921    type Output = Self;
922
923    fn div(self, rhs: Factor) -> Self::Output {
924        LayerIndex(self.0 / rhs)
925    }
926}
927impl ops::DivAssign<Factor> for LayerIndex {
928    fn div_assign(&mut self, rhs: Factor) {
929        self.0 /= rhs;
930    }
931}
932#[derive(serde::Serialize, serde::Deserialize)]
933#[serde(untagged)]
934enum LayerIndexSerde<'s> {
935    Named(&'s str),
936    Unnamed(u32),
937}
938impl serde::Serialize for LayerIndex {
939    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
940    where
941        S: serde::Serializer,
942    {
943        if serializer.is_human_readable()
944            && let Some(name) = self.name()
945        {
946            return LayerIndexSerde::Named(name).serialize(serializer);
947        }
948        LayerIndexSerde::Unnamed(self.0).serialize(serializer)
949    }
950}
951impl<'de> serde::Deserialize<'de> for LayerIndex {
952    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
953    where
954        D: serde::Deserializer<'de>,
955    {
956        use serde::de::Error;
957
958        match LayerIndexSerde::deserialize(deserializer)? {
959            LayerIndexSerde::Named(name) => match name {
960                "DEFAULT" => Ok(Self::DEFAULT),
961                "TOP_MOST" => Ok(Self::TOP_MOST),
962                "ADORNER" => Ok(Self::ADORNER),
963                unknown => Err(D::Error::unknown_variant(unknown, &["DEFAULT", "TOP_MOST", "ADORNER"])),
964            },
965            LayerIndexSerde::Unnamed(i) => Ok(Self(i)),
966        }
967    }
968}
969
970/// Represents two points that position a layer widget with its anchor widget.
971///
972/// The `place` point is layout in the anchor widget bounds, the `origin` point is layout in the layer widget bounds,
973/// the layer widget is offset so that the `origin` point aligns with the `place` point.
974#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
975pub struct AnchorOffset {
976    /// Point in the anchor widget.
977    pub place: Point,
978    /// Point in the layer widget.
979    pub origin: Point,
980}
981impl AnchorOffset {
982    /// New place and origin points from same `point`.
983    pub fn new(point: Point) -> Self {
984        Self {
985            place: point.clone(),
986            origin: point,
987        }
988    }
989
990    /// Layer widget is horizontally centered on the anchor widget and the top edge aligns.
991    pub fn in_top() -> Self {
992        Self::new(Point::top())
993    }
994
995    /// Layer widget is horizontally centered on the anchor widget and the bottom edge aligns.
996    pub fn in_bottom() -> Self {
997        Self::new(Point::bottom())
998    }
999
1000    /// Layer widget is vertically centered on the anchor widget and the left edge aligns.
1001    pub fn in_left() -> Self {
1002        Self::new(Point::left())
1003    }
1004
1005    /// Layer widget is vertically centered on the anchor widget and the right edge aligns.
1006    pub fn in_right() -> Self {
1007        Self::new(Point::right())
1008    }
1009
1010    /// Layer widget top-left corner aligns with the anchor widget top-left corner.
1011    pub fn in_top_left() -> Self {
1012        Self::new(Point::top_left())
1013    }
1014
1015    /// Layer widget top-right corner aligns with the anchor widget top-right corner.
1016    pub fn in_top_right() -> Self {
1017        Self::new(Point::top_right())
1018    }
1019
1020    /// Layer widget bottom-left corner aligns with the anchor widget bottom-left corner.
1021    pub fn in_bottom_left() -> Self {
1022        Self::new(Point::bottom_left())
1023    }
1024
1025    /// Layer widget bottom-right corner aligns with the anchor widget bottom-right corner.
1026    pub fn in_bottom_right() -> Self {
1027        Self::new(Point::bottom_right())
1028    }
1029
1030    /// Layer widget is centered on the anchor widget.
1031    pub fn center() -> Self {
1032        Self::new(Point::center())
1033    }
1034
1035    /// Layer widget is horizontally centered on the anchor widget and its bottom edge aligns with the anchors top edge.
1036    pub fn out_top() -> Self {
1037        Self {
1038            place: Point::top(),
1039            origin: Point::bottom(),
1040        }
1041    }
1042
1043    /// Layer widget is horizontally centered on the anchor widget and its top edge aligns with the anchors bottom edge.
1044    pub fn out_bottom() -> Self {
1045        Self {
1046            place: Point::bottom(),
1047            origin: Point::top(),
1048        }
1049    }
1050
1051    /// Layer widget is vertically centered on the anchor widget and its right edge aligns with the anchors left edge.
1052    pub fn out_left() -> Self {
1053        Self {
1054            place: Point::left(),
1055            origin: Point::right(),
1056        }
1057    }
1058
1059    /// Layer widget is vertically centered on the anchor widget and its left edge aligns with the anchors right edge.
1060    pub fn out_right() -> Self {
1061        Self {
1062            place: Point::right(),
1063            origin: Point::left(),
1064        }
1065    }
1066
1067    /// Layer widget bottom-right corner aligns with anchor widget top-left corner.
1068    pub fn out_top_left() -> Self {
1069        Self {
1070            place: Point::top_left(),
1071            origin: Point::bottom_right(),
1072        }
1073    }
1074
1075    /// Layer widget bottom-left corner aligns with anchor widget top-right corner.
1076    pub fn out_top_right() -> Self {
1077        Self {
1078            place: Point::top_right(),
1079            origin: Point::bottom_left(),
1080        }
1081    }
1082
1083    /// Layer widget top-right corner aligns with anchor widget bottom-left corner.
1084    pub fn out_bottom_left() -> Self {
1085        Self {
1086            place: Point::bottom_left(),
1087            origin: Point::top_right(),
1088        }
1089    }
1090
1091    /// Layer widget bottom-right corner aligns with anchor widget top-left corner.
1092    pub fn out_bottom_right() -> Self {
1093        Self {
1094            place: Point::bottom_right(),
1095            origin: Point::top_left(),
1096        }
1097    }
1098
1099    /// Layer widget bottom-left corner aligns with anchor widget top-left corner.
1100    pub fn out_top_in_left() -> Self {
1101        Self {
1102            place: Point::top_left(),
1103            origin: Point::bottom_left(),
1104        }
1105    }
1106
1107    /// Layer widget bottom-right corner aligns with anchor widget top-right corner.
1108    pub fn out_top_in_right() -> Self {
1109        Self {
1110            place: Point::top_right(),
1111            origin: Point::bottom_right(),
1112        }
1113    }
1114
1115    /// Layer widget top-left corner aligns with anchor widget bottom-left corner.
1116    pub fn out_bottom_in_left() -> Self {
1117        Self {
1118            place: Point::bottom_left(),
1119            origin: Point::top_left(),
1120        }
1121    }
1122
1123    /// Layer widget top-right corner aligns with anchor widget bottom-right corner.
1124    pub fn out_bottom_in_right() -> Self {
1125        Self {
1126            place: Point::bottom_right(),
1127            origin: Point::top_right(),
1128        }
1129    }
1130
1131    /// Layer widget top-right corner aligns with anchor widget top-left corner.
1132    pub fn out_left_in_top() -> Self {
1133        Self {
1134            place: Point::top_left(),
1135            origin: Point::top_right(),
1136        }
1137    }
1138
1139    /// Layer widget bottom-right corner aligns with anchor widget bottom-left corner.
1140    pub fn out_left_in_bottom() -> Self {
1141        Self {
1142            place: Point::bottom_left(),
1143            origin: Point::bottom_right(),
1144        }
1145    }
1146
1147    /// Layer widget top-left corner aligns with anchor widget top-right corner.
1148    pub fn out_right_in_top() -> Self {
1149        Self {
1150            place: Point::top_right(),
1151            origin: Point::top_left(),
1152        }
1153    }
1154
1155    /// Layer widget bottom-left corner aligns with anchor widget bottom-right corner.
1156    pub fn out_right_in_bottom() -> Self {
1157        Self {
1158            place: Point::bottom_right(),
1159            origin: Point::bottom_left(),
1160        }
1161    }
1162}
1163impl_from_and_into_var! {
1164    /// `(place, origin)`.
1165    fn from<P: Into<Point>, O: Into<Point>>(place_origin: (P, O)) -> AnchorOffset {
1166        AnchorOffset {
1167            place: place_origin.0.into(),
1168            origin: place_origin.1.into(),
1169        }
1170    }
1171}
1172impl animation::Transitionable for AnchorOffset {
1173    fn lerp(self, to: &Self, step: animation::easing::EasingStep) -> Self {
1174        Self {
1175            place: self.place.lerp(&to.place, step),
1176            origin: self.origin.lerp(&to.place, step),
1177        }
1178    }
1179}
1180
1181/// Options for [`AnchorMode::transform`].
1182#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1183pub enum AnchorTransform {
1184    /// Widget does not copy any position from the anchor widget.
1185    None,
1186    /// The layer widget is translated so that a point in the layer widget outer-bounds aligns with a point
1187    /// in the anchor widget inner-bounds.
1188    InnerOffset(AnchorOffset),
1189    /// The layer widget is translated so that a point in the layer widget outer-bounds aligns with a point
1190    /// in the anchor widget fill area (inside the border offset).
1191    InnerBorderOffset(AnchorOffset),
1192
1193    /// The layer widget is translated so that a point in the layer widget outer-bounds aligns with a point
1194    /// in the anchor widget outer-bounds.
1195    OuterOffset(AnchorOffset),
1196
1197    /// The full inner transform of the anchor object is applied to the widget.
1198    InnerTransform,
1199
1200    /// The full inner transform of the anchor object is applied to the widget plus the border widths offset.
1201    InnerBorderTransform,
1202
1203    /// The full outer transform of the anchor object is applied to the widget.
1204    OuterTransform,
1205
1206    /// The layer widget is translated on the first layout to be at the cursor position.
1207    CursorOnce {
1208        /// The anchor offset place point is resolved in the cursor icon size (approximate).
1209        offset: AnchorOffset,
1210        /// If the latest touch position counts as a cursor.
1211        ///
1212        /// If `true` the latest position between mouse move and touch start or move is used, if `false`
1213        /// only the latest mouse position is used. Only active touch points count, that is touch start or
1214        /// move events only.
1215        include_touch: bool,
1216
1217        /// If set defines the offset from the anchor widget inner bounds that is the allowed
1218        /// area for the layer widget origin.
1219        ///
1220        /// Negative offsets are inside the inner bounds, positive outside.
1221        bounds: Option<SideOffsets>,
1222    },
1223    /// The layer widget is translated to follow the cursor position.
1224    ///
1225    /// The anchor offset place point is resolved in the cursor icon size (approximate).
1226    Cursor {
1227        /// The anchor offset place point is resolved in the cursor icon size (approximate), or in touch point pixel
1228        /// for touch positions.
1229        offset: AnchorOffset,
1230
1231        /// If the latest touch position counts as a cursor.
1232        ///
1233        /// If `true` the latest position between mouse move and touch start or move is used, if `false`
1234        /// only the latest mouse position is used. Only active touch points count, that is touch start or
1235        /// move events only. In case multiple touches are active only the first one counts.
1236        include_touch: bool,
1237
1238        /// If set defines the offset from the anchor widget inner bounds that is the allowed
1239        /// area for the layer widget origin.
1240        ///
1241        /// Negative offsets are inside the inner bounds, positive outside.
1242        bounds: Option<SideOffsets>,
1243    },
1244}
1245impl_from_and_into_var! {
1246    /// `InnerOffset`.
1247    fn from(inner_offset: AnchorOffset) -> AnchorTransform {
1248        AnchorTransform::InnerOffset(inner_offset)
1249    }
1250    /// `InnerOffset`.
1251    fn from<P: Into<Point>, O: Into<Point>>(inner_offset: (P, O)) -> AnchorTransform {
1252        AnchorOffset::from(inner_offset).into()
1253    }
1254}
1255
1256/// Options for [`AnchorMode`] size constraints.
1257#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1258pub enum AnchorSize {
1259    /// Widget does not copy any size from the anchor widget, the available size is infinite, the
1260    /// final size is the desired size.
1261    ///
1262    /// Note that layered widgets do not affect the window size and a widget that overflows the content
1263    /// boundaries is clipped.
1264    Unbounded,
1265    /// Widget does not copy any size from the anchor widget, the available size and final size
1266    /// are the window's root size.
1267    Window,
1268    /// The available size and final size is the anchor widget's outer size.
1269    OuterSize,
1270    /// The available size and final size is the anchor widget's inner size.
1271    InnerSize,
1272    /// The available size and final size is the anchor widget's inner size offset by the border widths.
1273    InnerBorder,
1274}
1275
1276/// Defines what properties the layered widget takes from the anchor widget.
1277#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1278pub struct AnchorMode {
1279    /// What transforms are copied from the anchor widget and applied as a *parent* transform of the widget.
1280    pub transform: AnchorTransform,
1281
1282    /// What size is copied from the anchor widget and used as the available and final min size of the widget.
1283    pub min_size: AnchorSize,
1284    /// What size is copied from the anchor widget and used as the available and final max size of the widget.
1285    pub max_size: AnchorSize,
1286
1287    /// After the `transform` and `size` are resolved the transform is adjusted so that the layered widget is
1288    /// fully visible in the window.
1289    ///
1290    /// Has no effect if [`AnchorTransform::None`].
1291    pub viewport_bound: bool,
1292
1293    /// If the widget is only layout if the anchor widget is not [`Collapsed`] and is only rendered
1294    /// if the anchor widget is rendered.
1295    ///
1296    /// [`Collapsed`]: Visibility::Collapsed
1297    pub visibility: bool,
1298    /// The widget [`interactivity`] is set to the same as the anchor widget.
1299    ///
1300    /// [`interactivity`]: zng_app::widget::info::WidgetInfo::interactivity
1301    pub interactivity: bool,
1302
1303    /// The widget's corner radius is set for the layer.
1304    ///
1305    /// If `size` is [`InnerBorder`] the corner radius are deflated to fit the *inner* curve of the borders.
1306    ///
1307    /// [`InnerBorder`]: AnchorSize::InnerBorder
1308    pub corner_radius: bool,
1309}
1310impl AnchorMode {
1311    /// Mode where widget behaves like an unanchored widget, except that it is still only
1312    /// layout an rendered if the anchor widget exists in the same window.
1313    pub fn window() -> Self {
1314        AnchorMode {
1315            transform: AnchorTransform::None,
1316            min_size: AnchorSize::Window,
1317            max_size: AnchorSize::Window,
1318            viewport_bound: false,
1319            visibility: false,
1320            interactivity: false,
1321            corner_radius: false,
1322        }
1323    }
1324
1325    /// Mode where the widget behaves like a `foreground` to the target widget.
1326    pub fn foreground() -> Self {
1327        AnchorMode {
1328            transform: AnchorTransform::InnerTransform,
1329            min_size: AnchorSize::InnerSize,
1330            max_size: AnchorSize::InnerSize,
1331            visibility: true,
1332            viewport_bound: false,
1333            interactivity: false,
1334            corner_radius: true,
1335        }
1336    }
1337
1338    /// Mode where widget behaves like a flyout menu for the anchor.
1339    pub fn popup(place: AnchorOffset) -> Self {
1340        AnchorMode {
1341            transform: place.into(),
1342            min_size: AnchorSize::InnerSize,
1343            max_size: AnchorSize::Window,
1344            visibility: true,
1345            viewport_bound: true,
1346            interactivity: true,
1347            corner_radius: false,
1348        }
1349    }
1350
1351    /// Mode where the widget behaves like a tooltip anchored to the cursor.
1352    pub fn tooltip() -> Self {
1353        AnchorMode {
1354            transform: AnchorTransform::CursorOnce {
1355                offset: AnchorOffset::out_bottom_in_left(),
1356                include_touch: true,
1357                bounds: None,
1358            },
1359            min_size: AnchorSize::Unbounded,
1360            max_size: AnchorSize::Window,
1361            viewport_bound: true,
1362            corner_radius: false,
1363            visibility: true,
1364            interactivity: false,
1365        }
1366    }
1367
1368    /// Mode where the widget behaves like a tooltip anchored to the widget.
1369    pub fn tooltip_shortcut() -> Self {
1370        AnchorMode {
1371            transform: AnchorTransform::InnerOffset({
1372                let mut p = AnchorOffset::out_top();
1373                p.origin.y += 4;
1374                p
1375            }),
1376            min_size: AnchorSize::Unbounded,
1377            max_size: AnchorSize::Window,
1378            viewport_bound: true,
1379            corner_radius: false,
1380            visibility: true,
1381            interactivity: false,
1382        }
1383    }
1384
1385    /// Mode where the widget behaves like a context-menu anchored to the cursor.
1386    pub fn context_menu() -> Self {
1387        AnchorMode {
1388            transform: AnchorTransform::CursorOnce {
1389                offset: AnchorOffset::in_top_left(),
1390                include_touch: true,
1391                bounds: None,
1392            },
1393            min_size: AnchorSize::Unbounded,
1394            max_size: AnchorSize::Window,
1395            viewport_bound: true,
1396            corner_radius: false,
1397            visibility: true,
1398            interactivity: false,
1399        }
1400    }
1401
1402    /// Mode where the widget behaves like a context-menu anchored to widget.
1403    pub fn context_menu_shortcut() -> Self {
1404        AnchorMode {
1405            transform: AnchorTransform::InnerOffset(AnchorOffset::in_top()),
1406            min_size: AnchorSize::Unbounded,
1407            max_size: AnchorSize::Window,
1408            viewport_bound: true,
1409            corner_radius: false,
1410            visibility: true,
1411            interactivity: false,
1412        }
1413    }
1414
1415    /// Returns the mode with `transform` set.
1416    pub fn with_transform(mut self, transform: impl Into<AnchorTransform>) -> Self {
1417        self.transform = transform.into();
1418        self
1419    }
1420
1421    /// Returns the mode with `min_size` set.
1422    pub fn with_min_size(mut self, size: impl Into<AnchorSize>) -> Self {
1423        self.min_size = size.into();
1424        self
1425    }
1426
1427    /// Returns the mode with `max_size` set.
1428    pub fn with_max_size(mut self, size: impl Into<AnchorSize>) -> Self {
1429        self.max_size = size.into();
1430        self
1431    }
1432
1433    /// Returns the mode with `min_size` and `max_size` set.
1434    pub fn with_size(mut self, size: impl Into<AnchorSize>) -> Self {
1435        let size = size.into();
1436        self.min_size = size;
1437        self.max_size = size;
1438        self
1439    }
1440
1441    /// Returns the mode with `visibility` set.
1442    pub fn with_visibility(mut self, visibility: bool) -> Self {
1443        self.visibility = visibility;
1444        self
1445    }
1446
1447    /// Returns the mode with `interactivity` set.
1448    pub fn with_interactivity(mut self, interactivity: bool) -> Self {
1449        self.interactivity = interactivity;
1450        self
1451    }
1452
1453    /// Returns the mode with `corner_radius` set.
1454    pub fn with_corner_radius(mut self, corner_radius: bool) -> Self {
1455        self.corner_radius = corner_radius;
1456        self
1457    }
1458
1459    /// Returns the mode with `viewport_bound` set.
1460    pub fn with_viewport_bound(mut self, viewport_bound: bool) -> Self {
1461        self.viewport_bound = viewport_bound;
1462        self
1463    }
1464}
1465impl Default for AnchorMode {
1466    /// Transform `InnerOffset` top-left, size infinite, copy visibility and corner-radius.
1467    fn default() -> Self {
1468        AnchorMode {
1469            transform: AnchorTransform::InnerOffset(AnchorOffset::in_top_left()),
1470            min_size: AnchorSize::Unbounded,
1471            max_size: AnchorSize::Unbounded,
1472            viewport_bound: false,
1473            visibility: true,
1474            interactivity: false,
1475            corner_radius: true,
1476        }
1477    }
1478}
1479impl_from_and_into_var! {
1480    /// Custom transform, all else default.
1481    fn from(transform: AnchorTransform) -> AnchorMode {
1482        AnchorMode {
1483            transform,
1484            ..AnchorMode::default()
1485        }
1486    }
1487    /// Transform `InnerOffset`, all else default.
1488    fn from(inner_offset: AnchorOffset) -> AnchorMode {
1489        AnchorTransform::from(inner_offset).into()
1490    }
1491
1492    /// Custom transform and size, all else default.
1493    fn from<T: Into<AnchorTransform>, S: Into<AnchorSize>>((transform, size): (T, S)) -> AnchorMode {
1494        let size = size.into();
1495        AnchorMode {
1496            transform: transform.into(),
1497            min_size: size,
1498            max_size: size,
1499            ..AnchorMode::default()
1500        }
1501    }
1502}
1503
1504/// Node that implements the layers, must be inserted in the [`NestGroup::EVENT`] group by the window implementer.
1505///
1506/// [`NestGroup::EVENT`]: zng_app::widget::builder::NestGroup::EVENT
1507pub fn layers_node(child: impl IntoUiNode) -> UiNode {
1508    let layers = EditableUiVec::new();
1509    let layered = layers.reference();
1510
1511    fn sort(a: &mut UiNode, b: &mut UiNode) -> std::cmp::Ordering {
1512        let a = a
1513            .as_widget()
1514            .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID)))
1515            .unwrap_or(LayerIndex::DEFAULT);
1516        let b = b
1517            .as_widget()
1518            .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID)))
1519            .unwrap_or(LayerIndex::DEFAULT);
1520
1521        a.cmp(&b)
1522    }
1523    let sorting_layers = SortingList::new(layers, sort);
1524    let children = ChainList(ui_vec![child, sorting_layers]);
1525
1526    let mut _insert_handle = CommandHandle::dummy();
1527    let mut _remove_handle = CommandHandle::dummy();
1528
1529    match_node(children, move |c, op| match op {
1530        UiNodeOp::Init => {
1531            WINDOW.with_state_mut(|mut s| {
1532                s.set(*WINDOW_LAYERS_ID, LayersCtx { items: layered.clone() });
1533
1534                if let Some(widgets) = s.get_mut(*WINDOW_PRE_INIT_LAYERS_ID) {
1535                    for wgt in widgets.drain(..) {
1536                        layered.push(wgt.into_inner());
1537                    }
1538                }
1539            });
1540            _insert_handle = LAYERS_INSERT_CMD.scoped(WINDOW.id()).subscribe(true);
1541            _remove_handle = LAYERS_REMOVE_CMD.scoped(WINDOW.id()).subscribe(true);
1542        }
1543        UiNodeOp::Deinit => {
1544            _insert_handle = CommandHandle::dummy();
1545            _remove_handle = CommandHandle::dummy();
1546        }
1547        UiNodeOp::Update { updates } => {
1548            let mut changed = false;
1549            c.update_list(updates, &mut changed);
1550            if changed {
1551                WIDGET.layout().render();
1552            }
1553
1554            let win_id = WINDOW.id();
1555            LAYERS_INSERT_CMD.scoped(win_id).each_update(true, false, |args| {
1556                if let Some((layer, widget)) = args.param::<(LayerIndex, WidgetFn<()>)>() {
1557                    LAYERS.insert(*layer, widget(()));
1558                    args.propagation.stop();
1559                } else if let Some((layer, anchor, mode, widget)) = args.param::<(LayerIndex, WidgetId, AnchorMode, WidgetFn<()>)>() {
1560                    LAYERS.insert_anchored(*layer, *anchor, mode.clone(), widget(()));
1561                    args.propagation.stop();
1562                } else {
1563                    tracing::debug!("ignoring LAYERS_INSERT_CMD, unknown param type");
1564                }
1565            });
1566            LAYERS_REMOVE_CMD.scoped(win_id).each_update(true, false, |args| {
1567                if let Some(id) = args.param::<WidgetId>() {
1568                    LAYERS.remove(*id);
1569                    args.propagation.stop();
1570                } else {
1571                    tracing::debug!("ignoring LAYERS_REMOVE_CMD, unknown param type");
1572                }
1573            });
1574        }
1575        UiNodeOp::Measure { wm, desired_size } => {
1576            c.delegated();
1577            *desired_size = c.node_impl::<ChainList>().0[0].measure(wm);
1578        }
1579        UiNodeOp::Layout { wl, final_size } => {
1580            c.delegated();
1581            let list = c.node_impl::<ChainList>();
1582            *final_size = list.0[0].layout(wl);
1583            let _ = list.0[1].layout(wl);
1584        }
1585        UiNodeOp::Render { frame } => {
1586            c.delegated();
1587            let list = c.node_impl::<ChainList>();
1588            list.0[0].render(frame); // render main UI first
1589            list.0[1].render(frame);
1590        }
1591        UiNodeOp::RenderUpdate { update } => {
1592            c.delegated();
1593            let list = c.node_impl::<ChainList>();
1594            list.0[0].render_update(update);
1595            list.0[1].render_update(update);
1596        }
1597        _ => {}
1598    })
1599}
1600
1601/// Custom layered foreground generated using a [`WidgetFn<()>`].
1602///
1603/// If the `adorner_fn` is not nil, the generated node is [layered] anchored to the widget inner bounds,
1604/// displaying like a `foreground` that is not clipped by the widget and overlays all other widgets
1605/// and layers not placed above [`LayerIndex::ADORNER`].
1606///
1607/// The full context is captured for adorner widget so you can use context variables inside without issue.
1608///
1609/// [layered]: LAYERS
1610/// [`WidgetFn<()>`]: WidgetFn
1611#[property(FILL, default(WidgetFn::nil()))]
1612pub fn adorner_fn(child: impl IntoUiNode, adorner_fn: impl IntoVar<WidgetFn<()>>) -> UiNode {
1613    let adorner_fn = adorner_fn.into_var();
1614    let mut adorner_id = None;
1615
1616    match_node(child, move |_, op| match op {
1617        UiNodeOp::Init => {
1618            WIDGET.sub_var(&adorner_fn);
1619            let f = adorner_fn.get();
1620            if !f.is_nil() {
1621                let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1622                let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1623                adorner_id = Some(id);
1624            }
1625        }
1626        UiNodeOp::Deinit => {
1627            if let Some(id) = adorner_id.take() {
1628                LAYERS.remove_node(id);
1629            }
1630        }
1631        UiNodeOp::Update { .. } => {
1632            if let Some(f) = adorner_fn.get_new() {
1633                if let Some(id) = adorner_id.take() {
1634                    LAYERS.remove_node(id);
1635                }
1636
1637                if !f.is_nil() {
1638                    let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1639                    let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1640                    adorner_id = Some(id);
1641                }
1642            }
1643        }
1644        _ => {}
1645    })
1646}
1647
1648/// Custom layered foreground.
1649///
1650/// This is the equivalent of setting [`adorner_fn`] to a [`WidgetFn::singleton`].
1651///
1652/// [`adorner_fn`]: fn@adorner_fn
1653/// [`WidgetFn::singleton`]: zng_wgt::prelude::WidgetFn::singleton
1654#[property(FILL, default(UiNode::nil()))]
1655pub fn adorner(child: impl IntoUiNode, adorner: impl IntoUiNode) -> UiNode {
1656    adorner_fn(child, WidgetFn::singleton(adorner))
1657}
1658
1659#[cfg(test)]
1660mod tests {
1661    use super::*;
1662
1663    #[test]
1664    pub fn layer_index_ops() {
1665        let idx = LayerIndex::DEFAULT;
1666
1667        let p1 = idx + 1;
1668        let m1 = idx - 1;
1669
1670        let mut idx = idx;
1671
1672        idx += 1;
1673        assert_eq!(idx, p1);
1674
1675        idx -= 2;
1676        assert_eq!(idx, m1);
1677    }
1678}