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