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#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12zng_wgt::enable_widget_macros!();
13
14use parking_lot::Mutex;
15use zng_app::widget::border::CORNER_RADIUS_VAR;
16use zng_app::widget::info::WIDGET_TREE_CHANGED_EVENT;
17use zng_ext_input::mouse::MOUSE;
18use zng_ext_input::touch::TOUCH;
19use zng_ext_window::WINDOW_Ext as _;
20use zng_var::{ContextInitHandle, animation};
21use zng_view_api::window::FrameId;
22use zng_wgt::prelude::*;
23
24use std::sync::Arc;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::{fmt, mem, ops};
27
28pub mod popup;
29
30struct LayersCtx {
31 items: EditableUiVecRef,
32}
33
34command! {
35 pub static LAYERS_INSERT_CMD;
50
51 pub static LAYERS_REMOVE_CMD;
61}
62
63pub struct LAYERS;
88impl LAYERS {
89 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 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 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 s.entry(*WINDOW_PRE_INIT_LAYERS_ID).or_default().push(Mutex::new(widget));
145 });
146 }
147 }
148
149 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 pub fn insert_anchored(
176 &self,
177 layer: impl IntoVar<LayerIndex>,
178 anchor: impl IntoVar<WidgetId>,
179 mode: impl IntoVar<AnchorMode>,
180
181 widget: impl IntoUiNode,
182 ) {
183 let layer = layer.into_var().current_context();
184 let anchor = anchor.into_var().current_context();
185 let mode = mode.into_var().current_context();
186
187 self.insert_anchored_impl(layer, anchor, mode, widget.into_node())
188 }
189 fn insert_anchored_impl(&self, layer: Var<LayerIndex>, anchor: Var<WidgetId>, mode: Var<AnchorMode>, widget: UiNode) {
190 let mut _info_changed_handle = None;
191 let mut mouse_pos_handle = None;
192
193 let mut cursor_once_pending = false;
194 let mut anchor_info = None;
195 let mut offset = (PxPoint::zero(), PxPoint::zero());
196 let mut cursor_bounds = None;
197 let mut interactivity = false;
198
199 let transform_key = FrameValueKey::new_unique();
200 let mut corner_radius_ctx_handle = None;
201
202 let widget = with_anchor_id(widget, anchor.clone());
203
204 fn get_anchor_info(anchor: WidgetId) -> (WidgetBoundsInfo, WidgetBorderInfo) {
205 let tree = WINDOW.info();
206 let w = tree.get(anchor).unwrap_or_else(|| tree.root());
207 (w.bounds_info(), w.border_info())
208 }
209
210 let widget = match_widget(widget, move |widget, op| match op {
211 UiNodeOp::Init => {
212 widget.init();
213
214 if let Some(mut wgt) = widget.node().as_widget() {
215 wgt.with_context(WidgetUpdateMode::Bubble, || {
216 WIDGET.sub_var(&anchor).sub_var(&mode);
217
218 anchor_info = Some(get_anchor_info(anchor.get()));
219
220 interactivity = mode.with(|m| m.interactivity);
221 let win_id = WINDOW.id();
222 _info_changed_handle = Some(
223 WIDGET_TREE_CHANGED_EVENT.subscribe_when(UpdateOp::Update, WIDGET.id(), move |args| {
224 !args.is_update && args.tree.window_id() == win_id
225 }),
226 );
227
228 if mode.with(|m| matches!(&m.transform, AnchorTransform::Cursor { .. })) {
229 mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
230 } else if mode.with(|m| matches!(&m.transform, AnchorTransform::CursorOnce { .. })) {
231 cursor_once_pending = true;
232 }
233 })
234 } else {
235 widget.deinit();
236 *widget.node() = UiNode::nil();
237 }
239 }
240 UiNodeOp::Deinit => {
241 widget.deinit();
242
243 anchor_info = None;
244 _info_changed_handle = None;
245 mouse_pos_handle = None;
246 corner_radius_ctx_handle = None;
247 cursor_once_pending = false;
248 }
249 UiNodeOp::Info { info } => {
250 if interactivity && let Some(mut wgt) = widget.node().as_widget() {
251 let wgt = wgt.id();
252 let anchor = anchor.get();
253 let querying = AtomicBool::new(false);
254 info.push_interactivity_filter(move |args| {
255 if args.info.id() == wgt {
256 if querying.swap(true, Ordering::Relaxed) {
257 return Interactivity::ENABLED; }
259 let _q = RunOnDrop::new(|| querying.store(false, Ordering::Relaxed));
260 args.info
261 .tree()
262 .get(anchor)
263 .map(|a| a.interactivity())
264 .unwrap_or(Interactivity::BLOCKED)
265 } else {
266 Interactivity::ENABLED
267 }
268 });
269 }
270 }
271 UiNodeOp::Update { .. } => {
272 if let Some(mut wgt) = widget.node().as_widget() {
273 wgt.with_context(WidgetUpdateMode::Bubble, || {
274 if let Some(anchor) = anchor.get_new() {
275 anchor_info = Some(get_anchor_info(anchor));
276 if mode.with(|m| m.interactivity) {
277 WIDGET.update_info();
278 }
279 WIDGET.layout().render();
280 }
281 if let Some(mode) = mode.get_new() {
282 if mode.interactivity != interactivity {
283 interactivity = mode.interactivity;
284 WIDGET.update_info();
285 }
286 if matches!(&mode.transform, AnchorTransform::Cursor { .. }) {
287 if mouse_pos_handle.is_none() {
288 mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
289 }
290 cursor_once_pending = false;
291 } else {
292 cursor_once_pending = matches!(&mode.transform, AnchorTransform::CursorOnce { .. });
293 mouse_pos_handle = None;
294 }
295 WIDGET.layout().render();
296 } else if mouse_pos_handle.is_some() && MOUSE.position().is_new() {
297 WIDGET.layout();
298 }
299 });
300
301 WIDGET_TREE_CHANGED_EVENT.each_update(true, |args| {
302 if !args.is_update && args.tree.window_id() == WINDOW.id() {
303 anchor_info = Some(get_anchor_info(anchor.get()));
304 }
305 });
306 }
307 }
308 UiNodeOp::Measure { wm, desired_size } => {
309 widget.delegated();
310
311 if let Some((bounds, border)) = &anchor_info {
312 let mode = mode.get();
313
314 if !mode.visibility || bounds.inner_size() != PxSize::zero() {
315 let mut constraints = match mode.min_size {
316 AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
317 AnchorSize::Window => LAYOUT.constraints(),
318 AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
319 AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
320 AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
321 };
322 if mode.max_size != mode.min_size {
323 constraints = match mode.max_size {
324 AnchorSize::Unbounded => constraints.with_unbounded(),
325 AnchorSize::Window => {
326 let w = LAYOUT.constraints();
327 constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
328 }
329 AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
330 AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
331 AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
332 };
333 }
334
335 *desired_size = LAYOUT.with_constraints(constraints, || widget.measure(wm));
336 }
337 }
338 }
339 UiNodeOp::Layout { wl, final_size } => {
340 widget.delegated();
341
342 if let Some((bounds, border)) = &anchor_info {
343 let mode = mode.get();
344
345 if !mode.visibility || bounds.inner_size() != PxSize::zero() {
346 let mut constraints = match mode.min_size {
349 AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
350 AnchorSize::Window => LAYOUT.constraints(),
351 AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
352 AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
353 AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
354 };
355 if mode.max_size != mode.min_size {
356 constraints = match mode.max_size {
357 AnchorSize::Unbounded => constraints.with_unbounded(),
358 AnchorSize::Window => {
359 let w = LAYOUT.constraints();
360 constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
361 }
362 AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
363 AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
364 AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
365 };
366 }
367
368 let layer_size = LAYOUT.with_constraints(constraints, || {
369 if mode.corner_radius {
370 let mut cr = border.corner_radius();
371 if let AnchorSize::InnerBorder = mode.max_size {
372 cr = cr.deflate(border.offsets());
373 }
374 CORNER_RADIUS_VAR.with_context_var(
375 corner_radius_ctx_handle.get_or_insert_with(ContextInitHandle::new).clone(),
376 cr,
377 || BORDER.with_corner_radius(|| widget.layout(wl)),
378 )
379 } else {
380 widget.layout(wl)
381 }
382 });
383
384 if let Some((p, include_touch, bounded, update)) = match &mode.transform {
385 AnchorTransform::Cursor {
386 offset,
387 include_touch,
388 bounds,
389 } => Some((offset, include_touch, bounds, true)),
390 AnchorTransform::CursorOnce {
391 offset,
392 include_touch,
393 bounds,
394 } => Some((offset, include_touch, bounds, mem::take(&mut cursor_once_pending))),
395 _ => None,
396 } {
397 const NO_POS_X: Px = Px::MIN;
399 if update {
400 let pos = if *include_touch {
401 let oldest_touch = TOUCH.positions().with(|p| p.iter().min_by_key(|p| p.start_time).cloned());
402 match (oldest_touch, MOUSE.position().get()) {
403 (Some(t), Some(m)) => {
404 let window_id = WINDOW.id();
405 if t.window_id == window_id && m.window_id == window_id {
406 Some(if t.update_time > m.timestamp { t.position } else { m.position })
407 } else {
408 None
409 }
410 }
411 (Some(t), None) => {
412 if t.window_id == WINDOW.id() {
413 Some(t.position)
414 } else {
415 None
416 }
417 }
418 (None, Some(m)) => {
419 if m.window_id == WINDOW.id() {
420 Some(m.position)
421 } else {
422 None
423 }
424 }
425 _ => None,
426 }
427 } else if let Some(p) = MOUSE.position().get() {
428 if p.window_id == WINDOW.id() { Some(p.position) } else { None }
429 } else {
430 None
431 };
432
433 if let Some(pos) = pos {
434 let fct = LAYOUT.scale_factor();
435 let pos = pos.to_px(fct);
436
437 let (cursor_size, cursor_spot) = {
438 let vars = WINDOW.vars();
439 #[cfg(feature = "image")]
440 if let Some((img, spot)) = vars.actual_cursor_img().get() {
441 (img.size(), spot)
442 } else {
443 vars.cursor().with(|s| s.icon()).map(|i| i.size_and_spot(fct)).unwrap_or_default()
444 }
445
446 #[cfg(not(feature = "image"))]
447 {
448 vars.cursor().with(|s| s.icon()).map(|i| i.size_and_spot(fct)).unwrap_or_default()
449 }
450 };
451 let cursor_rect = PxRect::new((pos - cursor_spot).to_point(), cursor_size);
452 let place = cursor_rect.origin
453 + LAYOUT
454 .with_constraints(PxConstraints2d::new_exact_size(cursor_rect.size), || p.place.layout())
455 .to_vector();
456 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
457
458 if let Some(sides) = bounded {
459 let sides = LAYOUT
460 .with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || sides.layout());
461 cursor_bounds = Some(PxRect::new(
463 -PxPoint::new(sides.left, sides.top),
464 bounds.inner_size() + PxSize::new(sides.horizontal(), sides.vertical()),
465 ));
466 } else {
467 cursor_bounds = None;
468 }
469
470 let o = (place, origin);
471 if offset != o {
472 offset = o;
473 WIDGET.render_update();
474 }
475
476 *final_size = layer_size;
477 return;
478 } else {
479 offset.0.x = NO_POS_X;
481 }
482 } else {
483 if offset.0.x != NO_POS_X {
485 *final_size = layer_size;
487 return;
488 }
489 }
490 } else {
491 let o = match &mode.transform {
493 AnchorTransform::InnerOffset(p) => {
494 let place =
495 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || p.place.layout());
496 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
497 (place, origin)
498 }
499 AnchorTransform::InnerBorderOffset(p) => {
500 let place = LAYOUT
501 .with_constraints(PxConstraints2d::new_exact_size(border.inner_size(bounds)), || p.place.layout());
502 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
503 (place, origin)
504 }
505 AnchorTransform::OuterOffset(p) => {
506 let place =
507 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.outer_size()), || p.place.layout());
508 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
509 (place, origin)
510 }
511 _ => (PxPoint::zero(), PxPoint::zero()),
512 };
513 if offset != o {
514 offset = o;
515 WIDGET.render_update();
516 }
517
518 *final_size = layer_size;
519 }
520 return;
521 }
522 }
523
524 if let Some(mut wgt) = widget.node().as_widget() {
525 wgt.with_context(WidgetUpdateMode::Bubble, || {
526 wl.collapse();
527 });
528 }
529 }
530 UiNodeOp::Render { frame } => {
531 widget.delegated();
532
533 if let Some((bounds_info, border_info)) = &anchor_info {
534 let mode = mode.get();
535 if !mode.visibility || bounds_info.rendered().is_some() {
536 let mut push_reference_frame = |mut transform: PxTransform, is_translate_only: bool| {
537 if mode.viewport_bound {
538 transform = adjust_viewport_bound(transform, widget.node());
539 }
540 frame.push_reference_frame(
541 transform_key.into(),
542 transform_key.bind(transform, true),
543 is_translate_only,
544 false,
545 |frame| widget.render(frame),
546 );
547 };
548
549 match mode.transform {
550 AnchorTransform::InnerOffset(_) => {
551 let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
552 let offset = place_in_window - offset.1;
553
554 push_reference_frame(PxTransform::from(offset), true);
555 }
556 AnchorTransform::InnerBorderOffset(_) => {
557 let place_in_window = border_info
558 .inner_transform(bounds_info)
559 .transform_point(offset.0)
560 .unwrap_or_default();
561 let offset = place_in_window - offset.1;
562
563 push_reference_frame(PxTransform::from(offset), true);
564 }
565 AnchorTransform::OuterOffset(_) => {
566 let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
567 let offset = place_in_window - offset.1;
568
569 push_reference_frame(PxTransform::from(offset), true);
570 }
571 AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
572 let (mut place, origin) = offset;
573
574 if let Some(b) = cursor_bounds {
575 let transform = bounds_info.inner_transform();
577 if let Some(inverse) = transform.inverse()
578 && let Some(p) = inverse.transform_point(place)
579 {
580 let bound_p = PxPoint::new(p.x.clamp(b.min_x(), b.max_x()), p.y.clamp(b.min_y(), b.max_y()));
581 if p != bound_p
582 && let Some(p) = transform.transform_point(bound_p)
583 {
584 place = p;
585 }
586 }
587 }
588 let offset = place - origin;
589
590 push_reference_frame(PxTransform::from(offset), true);
591 }
592 AnchorTransform::InnerTransform => {
593 push_reference_frame(bounds_info.inner_transform(), false);
594 }
595 AnchorTransform::InnerBorderTransform => {
596 push_reference_frame(border_info.inner_transform(bounds_info), false);
597 }
598 AnchorTransform::OuterTransform => {
599 push_reference_frame(bounds_info.outer_transform(), false);
600 }
601 _ => widget.render(frame),
602 }
603 } else {
604 frame.hide(|frame| widget.render(frame));
606
607 if frame.frame_id() == FrameId::first() && anchor.get() == WIDGET.id() {
608 WIDGET.render();
611 }
612 }
613 } else {
614 widget.render(frame);
615 }
616 }
617 UiNodeOp::RenderUpdate { update } => {
618 if let Some((bounds_info, border_info)) = &anchor_info {
619 let mode = mode.get();
620 if !mode.visibility || bounds_info.rendered().is_some() {
621 let mut with_transform = |mut transform: PxTransform| {
622 if mode.viewport_bound {
623 transform = adjust_viewport_bound(transform, widget.node());
624 }
625 update.with_transform(transform_key.update(transform, true), false, |update| widget.render_update(update));
626 };
627
628 match mode.transform {
629 AnchorTransform::InnerOffset(_) => {
630 let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
631 let offset = place_in_window - offset.1;
632 with_transform(PxTransform::from(offset));
633 }
634 AnchorTransform::InnerBorderOffset(_) => {
635 let place_in_window = border_info
636 .inner_transform(bounds_info)
637 .transform_point(offset.0)
638 .unwrap_or_default();
639 let offset = place_in_window - offset.1;
640 with_transform(PxTransform::from(offset));
641 }
642 AnchorTransform::OuterOffset(_) => {
643 let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
644 let offset = place_in_window - offset.1;
645 with_transform(PxTransform::from(offset));
646 }
647 AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
648 let offset = offset.0 - offset.1;
649 with_transform(PxTransform::from(offset));
650 }
651 AnchorTransform::InnerTransform => {
652 with_transform(bounds_info.inner_transform());
653 }
654 AnchorTransform::InnerBorderTransform => {
655 with_transform(border_info.inner_transform(bounds_info));
656 }
657 AnchorTransform::OuterTransform => {
658 with_transform(bounds_info.outer_transform());
659 }
660 _ => widget.render_update(update),
661 }
662 }
663 }
664 }
665 _ => {}
666 });
667 self.insert_impl(layer, widget);
668 }
669
670 pub fn insert_anchored_node(
682 &self,
683 layer: impl IntoVar<LayerIndex>,
684 anchor: impl IntoVar<WidgetId>,
685 mode: impl IntoVar<AnchorMode>,
686
687 maybe_widget: impl IntoUiNode,
688 ) -> ResponseVar<WidgetId> {
689 let (widget, rsp) = maybe_widget.into_node().init_widget();
690 self.insert_anchored(layer, anchor, mode, widget);
691 rsp
692 }
693
694 pub fn remove(&self, id: impl Into<WidgetId>) {
702 WINDOW.with_state(|s| {
703 s.req(*WINDOW_LAYERS_ID).items.remove(id);
704 });
705 }
706
707 pub fn remove_node(&self, id: ResponseVar<WidgetId>) {
712 if let Some(id) = id.rsp() {
713 self.remove(id);
714 } else {
715 let items = WINDOW.with_state(|s| s.req(*WINDOW_LAYERS_ID).items.clone());
716 id.hook(move |a| {
717 match a.value() {
718 zng_var::Response::Waiting => true,
719 zng_var::Response::Done(id) => {
720 items.remove(*id);
722 false
723 }
724 }
725 })
726 .perm();
727 }
728 }
729
730 pub fn anchor_id(&self) -> Var<Option<WidgetId>> {
732 ANCHOR_ID_VAR.read_only()
733 }
734
735 fn cleanup(&self) {
736 WINDOW.with_state(|s| {
737 s.req(*WINDOW_LAYERS_ID).items.retain(|n| n.as_widget().is_some());
738 });
739 }
740}
741
742fn adjust_viewport_bound(transform: PxTransform, widget: &mut UiNode) -> PxTransform {
743 let window_bounds = WINDOW.vars().actual_size_px().get();
744 let wgt_bounds = PxBox::from(
745 widget
746 .as_widget()
747 .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.bounds().outer_size()))
748 .unwrap_or_else(PxSize::zero),
749 );
750 let wgt_bounds = transform.outer_transformed(wgt_bounds).unwrap_or_default();
751
752 let x_underflow = -wgt_bounds.min.x.min(Px(0));
753 let x_overflow = (wgt_bounds.max.x - window_bounds.width).max(Px(0));
754 let y_underflow = -wgt_bounds.min.y.min(Px(0));
755 let y_overflow = (wgt_bounds.max.y - window_bounds.height).max(Px(0));
756
757 let x = x_underflow - x_overflow;
758 let y = y_underflow - y_overflow;
759
760 let correction = PxVector::new(x, y);
761
762 transform.then_translate(correction.cast())
763}
764
765fn with_anchor_id(child: impl IntoUiNode, anchor: Var<WidgetId>) -> UiNode {
766 let mut ctx = Some(Arc::new(anchor.map(|id| Some(*id)).into()));
767 let mut id = None;
768 match_widget(child, move |c, op| {
769 let mut is_deinit = false;
770 match &op {
771 UiNodeOp::Init => {
772 id = Some(ContextInitHandle::new());
773 }
774 UiNodeOp::Deinit => {
775 is_deinit = true;
776 }
777 _ => {}
778 }
779 ANCHOR_ID_VAR.with_context(id.clone().expect("node not inited"), &mut ctx, || c.op(op));
780
781 if is_deinit {
782 id = None;
783 }
784 })
785}
786
787context_var! {
788 static ANCHOR_ID_VAR: Option<WidgetId> = None;
789}
790
791static_id! {
792 static ref WINDOW_PRE_INIT_LAYERS_ID: StateId<Vec<Mutex<UiNode>>>;
793 static ref WINDOW_LAYERS_ID: StateId<LayersCtx>;
794 static ref LAYER_INDEX_ID: StateId<LayerIndex>;
795}
796
797#[derive(Default, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
801pub struct LayerIndex(pub u32);
802impl LayerIndex {
803 pub const TOP_MOST: LayerIndex = LayerIndex(u32::MAX);
810
811 pub const ADORNER: LayerIndex = LayerIndex(Self::TOP_MOST.0 - u16::MAX as u32);
818
819 pub const DEFAULT: LayerIndex = LayerIndex(0);
823
824 pub fn saturating_add(self, other: impl Into<LayerIndex>) -> Self {
828 Self(self.0.saturating_add(other.into().0))
829 }
830
831 pub fn saturating_sub(self, other: impl Into<LayerIndex>) -> Self {
835 Self(self.0.saturating_sub(other.into().0))
836 }
837
838 pub fn name(self) -> Option<&'static str> {
840 if self == Self::DEFAULT {
841 Some("DEFAULT")
842 } else if self == Self::TOP_MOST {
843 Some("TOP_MOST")
844 } else if self == Self::ADORNER {
845 Some("ADORNER")
846 } else {
847 None
848 }
849 }
850}
851impl fmt::Debug for LayerIndex {
852 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
853 if let Some(name) = self.name() {
854 if f.alternate() {
855 write!(f, "LayerIndex::")?;
856 }
857 write!(f, "{name}")
858 } else {
859 write!(f, "LayerIndex({})", self.0)
860 }
861 }
862}
863impl std::str::FromStr for LayerIndex {
864 type Err = <u32 as std::str::FromStr>::Err;
865
866 fn from_str(s: &str) -> Result<Self, Self::Err> {
867 match s {
868 "DEFAULT" => Ok(Self::DEFAULT),
869 "TOP_MOST" => Ok(Self::TOP_MOST),
870 "ADORNER" => Ok(Self::ADORNER),
871 n => Ok(Self(n.parse()?)),
872 }
873 }
874}
875impl_from_and_into_var! {
876 fn from(index: u32) -> LayerIndex {
877 LayerIndex(index)
878 }
879}
880impl<T: Into<Self>> ops::Add<T> for LayerIndex {
882 type Output = Self;
883
884 fn add(self, rhs: T) -> Self::Output {
885 self.saturating_add(rhs)
886 }
887}
888impl<T: Into<Self>> ops::Sub<T> for LayerIndex {
890 type Output = Self;
891
892 fn sub(self, rhs: T) -> Self::Output {
893 self.saturating_sub(rhs)
894 }
895}
896impl<T: Into<Self>> ops::AddAssign<T> for LayerIndex {
898 fn add_assign(&mut self, rhs: T) {
899 *self = *self + rhs;
900 }
901}
902impl<T: Into<Self>> ops::SubAssign<T> for LayerIndex {
904 fn sub_assign(&mut self, rhs: T) {
905 *self = *self - rhs;
906 }
907}
908impl ops::Mul<Factor> for LayerIndex {
909 type Output = Self;
910
911 fn mul(self, rhs: Factor) -> Self::Output {
912 LayerIndex(self.0 * rhs)
913 }
914}
915impl ops::MulAssign<Factor> for LayerIndex {
916 fn mul_assign(&mut self, rhs: Factor) {
917 self.0 *= rhs;
918 }
919}
920impl ops::Div<Factor> for LayerIndex {
921 type Output = Self;
922
923 fn div(self, rhs: Factor) -> Self::Output {
924 LayerIndex(self.0 / rhs)
925 }
926}
927impl ops::DivAssign<Factor> for LayerIndex {
928 fn div_assign(&mut self, rhs: Factor) {
929 self.0 /= rhs;
930 }
931}
932#[derive(serde::Serialize, serde::Deserialize)]
933#[serde(untagged)]
934enum LayerIndexSerde<'s> {
935 Named(&'s str),
936 Unnamed(u32),
937}
938impl serde::Serialize for LayerIndex {
939 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
940 where
941 S: serde::Serializer,
942 {
943 if serializer.is_human_readable()
944 && let Some(name) = self.name()
945 {
946 return LayerIndexSerde::Named(name).serialize(serializer);
947 }
948 LayerIndexSerde::Unnamed(self.0).serialize(serializer)
949 }
950}
951impl<'de> serde::Deserialize<'de> for LayerIndex {
952 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
953 where
954 D: serde::Deserializer<'de>,
955 {
956 use serde::de::Error;
957
958 match LayerIndexSerde::deserialize(deserializer)? {
959 LayerIndexSerde::Named(name) => match name {
960 "DEFAULT" => Ok(Self::DEFAULT),
961 "TOP_MOST" => Ok(Self::TOP_MOST),
962 "ADORNER" => Ok(Self::ADORNER),
963 unknown => Err(D::Error::unknown_variant(unknown, &["DEFAULT", "TOP_MOST", "ADORNER"])),
964 },
965 LayerIndexSerde::Unnamed(i) => Ok(Self(i)),
966 }
967 }
968}
969
970#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
975pub struct AnchorOffset {
976 pub place: Point,
978 pub origin: Point,
980}
981impl AnchorOffset {
982 pub fn new(point: Point) -> Self {
984 Self {
985 place: point.clone(),
986 origin: point,
987 }
988 }
989
990 pub fn in_top() -> Self {
992 Self::new(Point::top())
993 }
994
995 pub fn in_bottom() -> Self {
997 Self::new(Point::bottom())
998 }
999
1000 pub fn in_left() -> Self {
1002 Self::new(Point::left())
1003 }
1004
1005 pub fn in_right() -> Self {
1007 Self::new(Point::right())
1008 }
1009
1010 pub fn in_top_left() -> Self {
1012 Self::new(Point::top_left())
1013 }
1014
1015 pub fn in_top_right() -> Self {
1017 Self::new(Point::top_right())
1018 }
1019
1020 pub fn in_bottom_left() -> Self {
1022 Self::new(Point::bottom_left())
1023 }
1024
1025 pub fn in_bottom_right() -> Self {
1027 Self::new(Point::bottom_right())
1028 }
1029
1030 pub fn center() -> Self {
1032 Self::new(Point::center())
1033 }
1034
1035 pub fn out_top() -> Self {
1037 Self {
1038 place: Point::top(),
1039 origin: Point::bottom(),
1040 }
1041 }
1042
1043 pub fn out_bottom() -> Self {
1045 Self {
1046 place: Point::bottom(),
1047 origin: Point::top(),
1048 }
1049 }
1050
1051 pub fn out_left() -> Self {
1053 Self {
1054 place: Point::left(),
1055 origin: Point::right(),
1056 }
1057 }
1058
1059 pub fn out_right() -> Self {
1061 Self {
1062 place: Point::right(),
1063 origin: Point::left(),
1064 }
1065 }
1066
1067 pub fn out_top_left() -> Self {
1069 Self {
1070 place: Point::top_left(),
1071 origin: Point::bottom_right(),
1072 }
1073 }
1074
1075 pub fn out_top_right() -> Self {
1077 Self {
1078 place: Point::top_right(),
1079 origin: Point::bottom_left(),
1080 }
1081 }
1082
1083 pub fn out_bottom_left() -> Self {
1085 Self {
1086 place: Point::bottom_left(),
1087 origin: Point::top_right(),
1088 }
1089 }
1090
1091 pub fn out_bottom_right() -> Self {
1093 Self {
1094 place: Point::bottom_right(),
1095 origin: Point::top_left(),
1096 }
1097 }
1098
1099 pub fn out_top_in_left() -> Self {
1101 Self {
1102 place: Point::top_left(),
1103 origin: Point::bottom_left(),
1104 }
1105 }
1106
1107 pub fn out_top_in_right() -> Self {
1109 Self {
1110 place: Point::top_right(),
1111 origin: Point::bottom_right(),
1112 }
1113 }
1114
1115 pub fn out_bottom_in_left() -> Self {
1117 Self {
1118 place: Point::bottom_left(),
1119 origin: Point::top_left(),
1120 }
1121 }
1122
1123 pub fn out_bottom_in_right() -> Self {
1125 Self {
1126 place: Point::bottom_right(),
1127 origin: Point::top_right(),
1128 }
1129 }
1130
1131 pub fn out_left_in_top() -> Self {
1133 Self {
1134 place: Point::top_left(),
1135 origin: Point::top_right(),
1136 }
1137 }
1138
1139 pub fn out_left_in_bottom() -> Self {
1141 Self {
1142 place: Point::bottom_left(),
1143 origin: Point::bottom_right(),
1144 }
1145 }
1146
1147 pub fn out_right_in_top() -> Self {
1149 Self {
1150 place: Point::top_right(),
1151 origin: Point::top_left(),
1152 }
1153 }
1154
1155 pub fn out_right_in_bottom() -> Self {
1157 Self {
1158 place: Point::bottom_right(),
1159 origin: Point::bottom_left(),
1160 }
1161 }
1162}
1163impl_from_and_into_var! {
1164 fn from<P: Into<Point>, O: Into<Point>>(place_origin: (P, O)) -> AnchorOffset {
1166 AnchorOffset {
1167 place: place_origin.0.into(),
1168 origin: place_origin.1.into(),
1169 }
1170 }
1171}
1172impl animation::Transitionable for AnchorOffset {
1173 fn lerp(self, to: &Self, step: animation::easing::EasingStep) -> Self {
1174 Self {
1175 place: self.place.lerp(&to.place, step),
1176 origin: self.origin.lerp(&to.place, step),
1177 }
1178 }
1179}
1180
1181#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1183pub enum AnchorTransform {
1184 None,
1186 InnerOffset(AnchorOffset),
1189 InnerBorderOffset(AnchorOffset),
1192
1193 OuterOffset(AnchorOffset),
1196
1197 InnerTransform,
1199
1200 InnerBorderTransform,
1202
1203 OuterTransform,
1205
1206 CursorOnce {
1208 offset: AnchorOffset,
1210 include_touch: bool,
1216
1217 bounds: Option<SideOffsets>,
1222 },
1223 Cursor {
1227 offset: AnchorOffset,
1230
1231 include_touch: bool,
1237
1238 bounds: Option<SideOffsets>,
1243 },
1244}
1245impl_from_and_into_var! {
1246 fn from(inner_offset: AnchorOffset) -> AnchorTransform {
1248 AnchorTransform::InnerOffset(inner_offset)
1249 }
1250 fn from<P: Into<Point>, O: Into<Point>>(inner_offset: (P, O)) -> AnchorTransform {
1252 AnchorOffset::from(inner_offset).into()
1253 }
1254}
1255
1256#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1258pub enum AnchorSize {
1259 Unbounded,
1265 Window,
1268 OuterSize,
1270 InnerSize,
1272 InnerBorder,
1274}
1275
1276#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1278pub struct AnchorMode {
1279 pub transform: AnchorTransform,
1281
1282 pub min_size: AnchorSize,
1284 pub max_size: AnchorSize,
1286
1287 pub viewport_bound: bool,
1292
1293 pub visibility: bool,
1298 pub interactivity: bool,
1302
1303 pub corner_radius: bool,
1309}
1310impl AnchorMode {
1311 pub fn window() -> Self {
1314 AnchorMode {
1315 transform: AnchorTransform::None,
1316 min_size: AnchorSize::Window,
1317 max_size: AnchorSize::Window,
1318 viewport_bound: false,
1319 visibility: false,
1320 interactivity: false,
1321 corner_radius: false,
1322 }
1323 }
1324
1325 pub fn foreground() -> Self {
1327 AnchorMode {
1328 transform: AnchorTransform::InnerTransform,
1329 min_size: AnchorSize::InnerSize,
1330 max_size: AnchorSize::InnerSize,
1331 visibility: true,
1332 viewport_bound: false,
1333 interactivity: false,
1334 corner_radius: true,
1335 }
1336 }
1337
1338 pub fn popup(place: AnchorOffset) -> Self {
1340 AnchorMode {
1341 transform: place.into(),
1342 min_size: AnchorSize::InnerSize,
1343 max_size: AnchorSize::Window,
1344 visibility: true,
1345 viewport_bound: true,
1346 interactivity: true,
1347 corner_radius: false,
1348 }
1349 }
1350
1351 pub fn tooltip() -> Self {
1353 AnchorMode {
1354 transform: AnchorTransform::CursorOnce {
1355 offset: AnchorOffset::out_bottom_in_left(),
1356 include_touch: true,
1357 bounds: None,
1358 },
1359 min_size: AnchorSize::Unbounded,
1360 max_size: AnchorSize::Window,
1361 viewport_bound: true,
1362 corner_radius: false,
1363 visibility: true,
1364 interactivity: false,
1365 }
1366 }
1367
1368 pub fn tooltip_shortcut() -> Self {
1370 AnchorMode {
1371 transform: AnchorTransform::InnerOffset({
1372 let mut p = AnchorOffset::out_top();
1373 p.origin.y += 4;
1374 p
1375 }),
1376 min_size: AnchorSize::Unbounded,
1377 max_size: AnchorSize::Window,
1378 viewport_bound: true,
1379 corner_radius: false,
1380 visibility: true,
1381 interactivity: false,
1382 }
1383 }
1384
1385 pub fn context_menu() -> Self {
1387 AnchorMode {
1388 transform: AnchorTransform::CursorOnce {
1389 offset: AnchorOffset::in_top_left(),
1390 include_touch: true,
1391 bounds: None,
1392 },
1393 min_size: AnchorSize::Unbounded,
1394 max_size: AnchorSize::Window,
1395 viewport_bound: true,
1396 corner_radius: false,
1397 visibility: true,
1398 interactivity: false,
1399 }
1400 }
1401
1402 pub fn context_menu_shortcut() -> Self {
1404 AnchorMode {
1405 transform: AnchorTransform::InnerOffset(AnchorOffset::in_top()),
1406 min_size: AnchorSize::Unbounded,
1407 max_size: AnchorSize::Window,
1408 viewport_bound: true,
1409 corner_radius: false,
1410 visibility: true,
1411 interactivity: false,
1412 }
1413 }
1414
1415 pub fn with_transform(mut self, transform: impl Into<AnchorTransform>) -> Self {
1417 self.transform = transform.into();
1418 self
1419 }
1420
1421 pub fn with_min_size(mut self, size: impl Into<AnchorSize>) -> Self {
1423 self.min_size = size.into();
1424 self
1425 }
1426
1427 pub fn with_max_size(mut self, size: impl Into<AnchorSize>) -> Self {
1429 self.max_size = size.into();
1430 self
1431 }
1432
1433 pub fn with_size(mut self, size: impl Into<AnchorSize>) -> Self {
1435 let size = size.into();
1436 self.min_size = size;
1437 self.max_size = size;
1438 self
1439 }
1440
1441 pub fn with_visibility(mut self, visibility: bool) -> Self {
1443 self.visibility = visibility;
1444 self
1445 }
1446
1447 pub fn with_interactivity(mut self, interactivity: bool) -> Self {
1449 self.interactivity = interactivity;
1450 self
1451 }
1452
1453 pub fn with_corner_radius(mut self, corner_radius: bool) -> Self {
1455 self.corner_radius = corner_radius;
1456 self
1457 }
1458
1459 pub fn with_viewport_bound(mut self, viewport_bound: bool) -> Self {
1461 self.viewport_bound = viewport_bound;
1462 self
1463 }
1464}
1465impl Default for AnchorMode {
1466 fn default() -> Self {
1468 AnchorMode {
1469 transform: AnchorTransform::InnerOffset(AnchorOffset::in_top_left()),
1470 min_size: AnchorSize::Unbounded,
1471 max_size: AnchorSize::Unbounded,
1472 viewport_bound: false,
1473 visibility: true,
1474 interactivity: false,
1475 corner_radius: true,
1476 }
1477 }
1478}
1479impl_from_and_into_var! {
1480 fn from(transform: AnchorTransform) -> AnchorMode {
1482 AnchorMode {
1483 transform,
1484 ..AnchorMode::default()
1485 }
1486 }
1487 fn from(inner_offset: AnchorOffset) -> AnchorMode {
1489 AnchorTransform::from(inner_offset).into()
1490 }
1491
1492 fn from<T: Into<AnchorTransform>, S: Into<AnchorSize>>((transform, size): (T, S)) -> AnchorMode {
1494 let size = size.into();
1495 AnchorMode {
1496 transform: transform.into(),
1497 min_size: size,
1498 max_size: size,
1499 ..AnchorMode::default()
1500 }
1501 }
1502}
1503
1504pub fn layers_node(child: impl IntoUiNode) -> UiNode {
1508 let layers = EditableUiVec::new();
1509 let layered = layers.reference();
1510
1511 fn sort(a: &mut UiNode, b: &mut UiNode) -> std::cmp::Ordering {
1512 let a = a
1513 .as_widget()
1514 .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID)))
1515 .unwrap_or(LayerIndex::DEFAULT);
1516 let b = b
1517 .as_widget()
1518 .map(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID)))
1519 .unwrap_or(LayerIndex::DEFAULT);
1520
1521 a.cmp(&b)
1522 }
1523 let sorting_layers = SortingList::new(layers, sort);
1524 let children = ChainList(ui_vec![child, sorting_layers]);
1525
1526 let mut _insert_handle = CommandHandle::dummy();
1527 let mut _remove_handle = CommandHandle::dummy();
1528
1529 match_node(children, move |c, op| match op {
1530 UiNodeOp::Init => {
1531 WINDOW.with_state_mut(|mut s| {
1532 s.set(*WINDOW_LAYERS_ID, LayersCtx { items: layered.clone() });
1533
1534 if let Some(widgets) = s.get_mut(*WINDOW_PRE_INIT_LAYERS_ID) {
1535 for wgt in widgets.drain(..) {
1536 layered.push(wgt.into_inner());
1537 }
1538 }
1539 });
1540 _insert_handle = LAYERS_INSERT_CMD.scoped(WINDOW.id()).subscribe(true);
1541 _remove_handle = LAYERS_REMOVE_CMD.scoped(WINDOW.id()).subscribe(true);
1542 }
1543 UiNodeOp::Deinit => {
1544 _insert_handle = CommandHandle::dummy();
1545 _remove_handle = CommandHandle::dummy();
1546 }
1547 UiNodeOp::Update { updates } => {
1548 let mut changed = false;
1549 c.update_list(updates, &mut changed);
1550 if changed {
1551 WIDGET.layout().render();
1552 }
1553
1554 let win_id = WINDOW.id();
1555 LAYERS_INSERT_CMD.scoped(win_id).each_update(true, false, |args| {
1556 if let Some((layer, widget)) = args.param::<(LayerIndex, WidgetFn<()>)>() {
1557 LAYERS.insert(*layer, widget(()));
1558 args.propagation.stop();
1559 } else if let Some((layer, anchor, mode, widget)) = args.param::<(LayerIndex, WidgetId, AnchorMode, WidgetFn<()>)>() {
1560 LAYERS.insert_anchored(*layer, *anchor, mode.clone(), widget(()));
1561 args.propagation.stop();
1562 } else {
1563 tracing::debug!("ignoring LAYERS_INSERT_CMD, unknown param type");
1564 }
1565 });
1566 LAYERS_REMOVE_CMD.scoped(win_id).each_update(true, false, |args| {
1567 if let Some(id) = args.param::<WidgetId>() {
1568 LAYERS.remove(*id);
1569 args.propagation.stop();
1570 } else {
1571 tracing::debug!("ignoring LAYERS_REMOVE_CMD, unknown param type");
1572 }
1573 });
1574 }
1575 UiNodeOp::Measure { wm, desired_size } => {
1576 c.delegated();
1577 *desired_size = c.node_impl::<ChainList>().0[0].measure(wm);
1578 }
1579 UiNodeOp::Layout { wl, final_size } => {
1580 c.delegated();
1581 let list = c.node_impl::<ChainList>();
1582 *final_size = list.0[0].layout(wl);
1583 let _ = list.0[1].layout(wl);
1584 }
1585 UiNodeOp::Render { frame } => {
1586 c.delegated();
1587 let list = c.node_impl::<ChainList>();
1588 list.0[0].render(frame); list.0[1].render(frame);
1590 }
1591 UiNodeOp::RenderUpdate { update } => {
1592 c.delegated();
1593 let list = c.node_impl::<ChainList>();
1594 list.0[0].render_update(update);
1595 list.0[1].render_update(update);
1596 }
1597 _ => {}
1598 })
1599}
1600
1601#[property(FILL, default(WidgetFn::nil()))]
1612pub fn adorner_fn(child: impl IntoUiNode, adorner_fn: impl IntoVar<WidgetFn<()>>) -> UiNode {
1613 let adorner_fn = adorner_fn.into_var();
1614 let mut adorner_id = None;
1615
1616 match_node(child, move |_, op| match op {
1617 UiNodeOp::Init => {
1618 WIDGET.sub_var(&adorner_fn);
1619 let f = adorner_fn.get();
1620 if !f.is_nil() {
1621 let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1622 let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1623 adorner_id = Some(id);
1624 }
1625 }
1626 UiNodeOp::Deinit => {
1627 if let Some(id) = adorner_id.take() {
1628 LAYERS.remove_node(id);
1629 }
1630 }
1631 UiNodeOp::Update { .. } => {
1632 if let Some(f) = adorner_fn.get_new() {
1633 if let Some(id) = adorner_id.take() {
1634 LAYERS.remove_node(id);
1635 }
1636
1637 if !f.is_nil() {
1638 let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1639 let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1640 adorner_id = Some(id);
1641 }
1642 }
1643 }
1644 _ => {}
1645 })
1646}
1647
1648#[property(FILL, default(UiNode::nil()))]
1655pub fn adorner(child: impl IntoUiNode, adorner: impl IntoUiNode) -> UiNode {
1656 adorner_fn(child, WidgetFn::singleton(adorner))
1657}
1658
1659#[cfg(test)]
1660mod tests {
1661 use super::*;
1662
1663 #[test]
1664 pub fn layer_index_ops() {
1665 let idx = LayerIndex::DEFAULT;
1666
1667 let p1 = idx + 1;
1668 let m1 = idx - 1;
1669
1670 let mut idx = idx;
1671
1672 idx += 1;
1673 assert_eq!(idx, p1);
1674
1675 idx -= 2;
1676 assert_eq!(idx, m1);
1677 }
1678}