zng_wgt_layer/
lib.rs

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