1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12zng_wgt::enable_widget_macros!();
13
14use parking_lot::Mutex;
15use zng_app::widget::border::CORNER_RADIUS_VAR;
16use zng_app::widget::info::WIDGET_INFO_CHANGED_EVENT;
17use zng_ext_input::mouse::MOUSE;
18use zng_ext_input::touch::TOUCH;
19use zng_ext_window::WINDOW_Ext as _;
20use zng_var::{ContextInitHandle, ReadOnlyContextVar, animation};
21use zng_view_api::window::FrameId;
22use zng_wgt::prelude::*;
23
24use std::sync::Arc;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::{fmt, mem, ops};
27
28pub mod popup;
29
30struct LayersCtx {
31 items: EditableUiNodeListRef,
32}
33
34command! {
35 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 UiNode) {
100 let layer = layer.into_var().actual_var();
101 self.insert_impl(layer.boxed(), widget.boxed());
102 }
103 fn insert_impl(&self, layer: BoxedVar<LayerIndex>, widget: BoxedUiNode) {
104 let widget = match_widget(widget, move |widget, op| match op {
105 UiNodeOp::Init => {
106 widget.init();
107
108 if !widget.is_widget() {
109 *widget.child() = NilUiNode.boxed();
110 LAYERS.cleanup();
111 }
112
113 widget.with_context(WidgetUpdateMode::Bubble, || {
115 WIDGET.set_state(*LAYER_INDEX_ID, layer.get());
116 WIDGET.sub_var(&layer);
117 });
118 }
119 UiNodeOp::Update { .. } => {
120 if let Some(index) = layer.get_new() {
121 widget.with_context(WidgetUpdateMode::Bubble, || {
122 WIDGET.set_state(*LAYER_INDEX_ID, index);
123 SORTING_LIST.invalidate_sort();
124 });
125 }
126 }
127 _ => {}
128 })
129 .boxed();
130
131 let r = WINDOW.with_state(|s| match s.get(*WINDOW_LAYERS_ID) {
132 Some(open) => {
133 open.items.push(widget);
135 Ok(())
136 }
137 None => Err(widget),
138 });
139 if let Err(widget) = r {
140 WINDOW.with_state_mut(|mut s| {
141 s.entry(*WINDOW_PRE_INIT_LAYERS_ID).or_default().push(Mutex::new(widget));
143 });
144 }
145 }
146
147 pub fn insert_node(&self, layer: impl IntoVar<LayerIndex>, maybe_widget: impl UiNode) -> ResponseVar<WidgetId> {
157 let (widget, rsp) = maybe_widget.init_widget();
158 self.insert(layer, widget);
159 rsp
160 }
161
162 pub fn insert_anchored(
174 &self,
175 layer: impl IntoVar<LayerIndex>,
176 anchor: impl IntoVar<WidgetId>,
177 mode: impl IntoVar<AnchorMode>,
178
179 widget: impl UiNode,
180 ) {
181 let layer = layer.into_var().actual_var();
182 let anchor = anchor.into_var().actual_var();
183 let mode = mode.into_var().actual_var();
184
185 self.insert_anchored_impl(layer.boxed(), anchor.boxed(), mode.boxed(), widget.boxed())
186 }
187 fn insert_anchored_impl(
188 &self,
189 layer: BoxedVar<LayerIndex>,
190 anchor: BoxedVar<WidgetId>,
191 mode: BoxedVar<AnchorMode>,
192 widget: BoxedUiNode,
193 ) {
194 let mut _info_changed_handle = None;
195 let mut mouse_pos_handle = None;
196
197 let mut cursor_once_pending = false;
198 let mut anchor_info = None;
199 let mut offset = (PxPoint::zero(), PxPoint::zero());
200 let mut cursor_bounds = None;
201 let mut interactivity = false;
202
203 let transform_key = FrameValueKey::new_unique();
204 let mut corner_radius_ctx_handle = None;
205
206 let widget = with_anchor_id(widget, anchor.clone().boxed());
207
208 fn get_anchor_info(anchor: WidgetId) -> (WidgetBoundsInfo, WidgetBorderInfo) {
209 let tree = WINDOW.info();
210 let w = tree.get(anchor).unwrap_or_else(|| tree.root());
211 (w.bounds_info(), w.border_info())
212 }
213
214 let widget = match_widget(widget.boxed(), move |widget, op| match op {
215 UiNodeOp::Init => {
216 widget.init();
217
218 if !widget.is_widget() {
219 widget.deinit();
220 *widget.child() = NilUiNode.boxed();
221 }
223
224 widget.with_context(WidgetUpdateMode::Bubble, || {
225 WIDGET.sub_var(&anchor).sub_var(&mode);
226
227 anchor_info = Some(get_anchor_info(anchor.get()));
228
229 interactivity = mode.with(|m| m.interactivity);
230 _info_changed_handle = Some(WIDGET_INFO_CHANGED_EVENT.subscribe(WIDGET.id()));
231
232 if mode.with(|m| matches!(&m.transform, AnchorTransform::Cursor { .. })) {
233 mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
234 } else if mode.with(|m| matches!(&m.transform, AnchorTransform::CursorOnce { .. })) {
235 cursor_once_pending = true;
236 }
237 });
238 }
239 UiNodeOp::Deinit => {
240 widget.deinit();
241
242 anchor_info = None;
243 _info_changed_handle = None;
244 mouse_pos_handle = None;
245 corner_radius_ctx_handle = None;
246 cursor_once_pending = false;
247 }
248 UiNodeOp::Info { info } => {
249 if interactivity {
250 if let Some(widget) = widget.with_context(WidgetUpdateMode::Ignore, || WIDGET.id()) {
251 let anchor = anchor.get();
252 let querying = AtomicBool::new(false);
253 info.push_interactivity_filter(move |args| {
254 if args.info.id() == widget {
255 if querying.swap(true, Ordering::Relaxed) {
256 return Interactivity::ENABLED; }
258 let _q = RunOnDrop::new(|| querying.store(false, Ordering::Relaxed));
259 args.info
260 .tree()
261 .get(anchor)
262 .map(|a| a.interactivity())
263 .unwrap_or(Interactivity::BLOCKED)
264 } else {
265 Interactivity::ENABLED
266 }
267 });
268 }
269 }
270 }
271 UiNodeOp::Event { update } => {
272 if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
273 if args.window_id == WINDOW.id() {
274 anchor_info = Some(get_anchor_info(anchor.get()));
275 }
276 }
277 }
278 UiNodeOp::Update { .. } => {
279 widget.with_context(WidgetUpdateMode::Bubble, || {
280 if let Some(anchor) = anchor.get_new() {
281 anchor_info = Some(get_anchor_info(anchor));
282 if mode.with(|m| m.interactivity) {
283 WIDGET.update_info();
284 }
285 WIDGET.layout().render();
286 }
287 if let Some(mode) = mode.get_new() {
288 if mode.interactivity != interactivity {
289 interactivity = mode.interactivity;
290 WIDGET.update_info();
291 }
292 if matches!(&mode.transform, AnchorTransform::Cursor { .. }) {
293 if mouse_pos_handle.is_none() {
294 mouse_pos_handle = Some(MOUSE.position().subscribe(UpdateOp::Update, WIDGET.id()));
295 }
296 cursor_once_pending = false;
297 } else {
298 cursor_once_pending = matches!(&mode.transform, AnchorTransform::CursorOnce { .. });
299 mouse_pos_handle = None;
300 }
301 WIDGET.layout().render();
302 } else if mouse_pos_handle.is_some() && MOUSE.position().is_new() {
303 WIDGET.layout();
304 }
305 });
306 }
307 UiNodeOp::Measure { wm, desired_size } => {
308 widget.delegated();
309
310 if let Some((bounds, border)) = &anchor_info {
311 let mode = mode.get();
312
313 if !mode.visibility || bounds.inner_size() != PxSize::zero() {
314 let mut constraints = match mode.min_size {
315 AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
316 AnchorSize::Window => LAYOUT.constraints(),
317 AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
318 AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
319 AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
320 };
321 if mode.max_size != mode.min_size {
322 constraints = match mode.max_size {
323 AnchorSize::Unbounded => constraints.with_unbounded(),
324 AnchorSize::Window => {
325 let w = LAYOUT.constraints();
326 constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
327 }
328 AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
329 AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
330 AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
331 };
332 }
333
334 *desired_size = LAYOUT.with_constraints(constraints, || widget.measure(wm));
335 }
336 }
337 }
338 UiNodeOp::Layout { wl, final_size } => {
339 widget.delegated();
340
341 if let Some((bounds, border)) = &anchor_info {
342 let mode = mode.get();
343
344 if !mode.visibility || bounds.inner_size() != PxSize::zero() {
345 let mut constraints = match mode.min_size {
348 AnchorSize::Unbounded => PxConstraints2d::new_unbounded(),
349 AnchorSize::Window => LAYOUT.constraints(),
350 AnchorSize::InnerSize => PxConstraints2d::new_exact_size(bounds.inner_size()).with_fill(false, false),
351 AnchorSize::InnerBorder => PxConstraints2d::new_exact_size(border.inner_size(bounds)).with_fill(false, false),
352 AnchorSize::OuterSize => PxConstraints2d::new_exact_size(bounds.outer_size()).with_fill(false, false),
353 };
354 if mode.max_size != mode.min_size {
355 constraints = match mode.max_size {
356 AnchorSize::Unbounded => constraints.with_unbounded(),
357 AnchorSize::Window => {
358 let w = LAYOUT.constraints();
359 constraints.with_new_max(w.x.max().unwrap_or(Px::MAX), w.y.max().unwrap_or(Px::MAX))
360 }
361 AnchorSize::InnerSize => constraints.with_new_max_size(bounds.inner_size()),
362 AnchorSize::InnerBorder => constraints.with_new_max_size(border.inner_size(bounds)),
363 AnchorSize::OuterSize => constraints.with_new_max_size(bounds.outer_size()),
364 };
365 }
366
367 let layer_size = LAYOUT.with_constraints(constraints, || {
368 if mode.corner_radius {
369 let mut cr = border.corner_radius();
370 if let AnchorSize::InnerBorder = mode.max_size {
371 cr = cr.deflate(border.offsets());
372 }
373 CORNER_RADIUS_VAR.with_context_var(
374 corner_radius_ctx_handle.get_or_insert_with(ContextInitHandle::new).clone(),
375 cr,
376 || BORDER.with_corner_radius(|| widget.layout(wl)),
377 )
378 } else {
379 widget.layout(wl)
380 }
381 });
382
383 if let Some((p, include_touch, bounded, update)) = match &mode.transform {
384 AnchorTransform::Cursor {
385 offset,
386 include_touch,
387 bounds,
388 } => Some((offset, include_touch, bounds, true)),
389 AnchorTransform::CursorOnce {
390 offset,
391 include_touch,
392 bounds,
393 } => Some((offset, include_touch, bounds, mem::take(&mut cursor_once_pending))),
394 _ => None,
395 } {
396 const NO_POS_X: Px = Px::MIN;
398 if update {
399 let pos = if *include_touch {
400 let oldest_touch = TOUCH.positions().with(|p| p.iter().min_by_key(|p| p.start_time).cloned());
401 match (oldest_touch, MOUSE.position().get()) {
402 (Some(t), Some(m)) => {
403 let window_id = WINDOW.id();
404 if t.window_id == window_id && m.window_id == window_id {
405 Some(if t.update_time > m.timestamp { t.position } else { m.position })
406 } else {
407 None
408 }
409 }
410 (Some(t), None) => {
411 if t.window_id == WINDOW.id() {
412 Some(t.position)
413 } else {
414 None
415 }
416 }
417 (None, Some(m)) => {
418 if m.window_id == WINDOW.id() {
419 Some(m.position)
420 } else {
421 None
422 }
423 }
424 _ => None,
425 }
426 } else if let Some(p) = MOUSE.position().get() {
427 if p.window_id == WINDOW.id() { Some(p.position) } else { None }
428 } else {
429 None
430 };
431
432 if let Some(pos) = pos {
433 let fct = LAYOUT.scale_factor();
434 let pos = pos.to_px(fct);
435
436 let (cursor_size, cursor_spot) = {
437 let vars = WINDOW.vars();
438 if let Some((img, spot)) = vars.actual_cursor_img().get() {
439 (img.size(), spot)
440 } else {
441 vars.cursor().with(|s| s.icon()).map(|i| i.size_and_spot(fct)).unwrap_or_default()
442 }
443 };
444 let cursor_rect = PxRect::new((pos - cursor_spot).to_point(), cursor_size);
445 let place = cursor_rect.origin
446 + LAYOUT
447 .with_constraints(PxConstraints2d::new_exact_size(cursor_rect.size), || p.place.layout())
448 .to_vector();
449 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
450
451 if let Some(sides) = bounded {
452 let sides = LAYOUT
453 .with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || sides.layout());
454 cursor_bounds = Some(PxRect::new(
456 -PxPoint::new(sides.left, sides.top),
457 bounds.inner_size() + PxSize::new(sides.horizontal(), sides.vertical()),
458 ));
459 } else {
460 cursor_bounds = None;
461 }
462
463 let o = (place, origin);
464 if offset != o {
465 offset = o;
466 WIDGET.render_update();
467 }
468
469 *final_size = layer_size;
470 return;
471 } else {
472 offset.0.x = NO_POS_X;
474 }
475 } else {
476 if offset.0.x != NO_POS_X {
478 *final_size = layer_size;
480 return;
481 }
482 }
483 } else {
484 let o = match &mode.transform {
486 AnchorTransform::InnerOffset(p) => {
487 let place =
488 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.inner_size()), || p.place.layout());
489 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
490 (place, origin)
491 }
492 AnchorTransform::InnerBorderOffset(p) => {
493 let place = LAYOUT
494 .with_constraints(PxConstraints2d::new_exact_size(border.inner_size(bounds)), || p.place.layout());
495 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
496 (place, origin)
497 }
498 AnchorTransform::OuterOffset(p) => {
499 let place =
500 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.outer_size()), || p.place.layout());
501 let origin = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(layer_size), || p.origin.layout());
502 (place, origin)
503 }
504 _ => (PxPoint::zero(), PxPoint::zero()),
505 };
506 if offset != o {
507 offset = o;
508 WIDGET.render_update();
509 }
510
511 *final_size = layer_size;
512 }
513 return;
514 }
515 }
516
517 widget.with_context(WidgetUpdateMode::Bubble, || {
518 wl.collapse();
519 });
520 }
521 UiNodeOp::Render { frame } => {
522 widget.delegated();
523
524 if let Some((bounds_info, border_info)) = &anchor_info {
525 let mode = mode.get();
526 if !mode.visibility || bounds_info.rendered().is_some() {
527 let mut push_reference_frame = |mut transform: PxTransform, is_translate_only: bool| {
528 if mode.viewport_bound {
529 transform = adjust_viewport_bound(transform, widget);
530 }
531 frame.push_reference_frame(
532 transform_key.into(),
533 transform_key.bind(transform, true),
534 is_translate_only,
535 false,
536 |frame| widget.render(frame),
537 );
538 };
539
540 match mode.transform {
541 AnchorTransform::InnerOffset(_) => {
542 let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
543 let offset = place_in_window - offset.1;
544
545 push_reference_frame(PxTransform::from(offset), true);
546 }
547 AnchorTransform::InnerBorderOffset(_) => {
548 let place_in_window = border_info
549 .inner_transform(bounds_info)
550 .transform_point(offset.0)
551 .unwrap_or_default();
552 let offset = place_in_window - offset.1;
553
554 push_reference_frame(PxTransform::from(offset), true);
555 }
556 AnchorTransform::OuterOffset(_) => {
557 let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
558 let offset = place_in_window - offset.1;
559
560 push_reference_frame(PxTransform::from(offset), true);
561 }
562 AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
563 let (mut place, origin) = offset;
564
565 if let Some(b) = cursor_bounds {
566 let transform = bounds_info.inner_transform();
568 if let Some(inverse) = transform.inverse() {
569 if let Some(p) = inverse.transform_point(place) {
570 let bound_p = PxPoint::new(p.x.clamp(b.min_x(), b.max_x()), p.y.clamp(b.min_y(), b.max_y()));
571 if p != bound_p {
572 if let Some(p) = transform.transform_point(bound_p) {
573 place = p;
574 }
575 }
576 }
577 }
578 }
579 let offset = place - origin;
580
581 push_reference_frame(PxTransform::from(offset), true);
582 }
583 AnchorTransform::InnerTransform => {
584 push_reference_frame(bounds_info.inner_transform(), false);
585 }
586 AnchorTransform::InnerBorderTransform => {
587 push_reference_frame(border_info.inner_transform(bounds_info), false);
588 }
589 AnchorTransform::OuterTransform => {
590 push_reference_frame(bounds_info.outer_transform(), false);
591 }
592 _ => widget.render(frame),
593 }
594 } else {
595 frame.hide(|frame| widget.render(frame));
597
598 if frame.frame_id() == FrameId::first() && anchor.get() == WIDGET.id() {
599 WIDGET.render();
602 }
603 }
604 } else {
605 widget.render(frame);
606 }
607 }
608 UiNodeOp::RenderUpdate { update } => {
609 if let Some((bounds_info, border_info)) = &anchor_info {
610 let mode = mode.get();
611 if !mode.visibility || bounds_info.rendered().is_some() {
612 let mut with_transform = |mut transform: PxTransform| {
613 if mode.viewport_bound {
614 transform = adjust_viewport_bound(transform, widget);
615 }
616 update.with_transform(transform_key.update(transform, true), false, |update| widget.render_update(update));
617 };
618
619 match mode.transform {
620 AnchorTransform::InnerOffset(_) => {
621 let place_in_window = bounds_info.inner_transform().transform_point(offset.0).unwrap_or_default();
622 let offset = place_in_window - offset.1;
623 with_transform(PxTransform::from(offset));
624 }
625 AnchorTransform::InnerBorderOffset(_) => {
626 let place_in_window = border_info
627 .inner_transform(bounds_info)
628 .transform_point(offset.0)
629 .unwrap_or_default();
630 let offset = place_in_window - offset.1;
631 with_transform(PxTransform::from(offset));
632 }
633 AnchorTransform::OuterOffset(_) => {
634 let place_in_window = bounds_info.outer_transform().transform_point(offset.0).unwrap_or_default();
635 let offset = place_in_window - offset.1;
636 with_transform(PxTransform::from(offset));
637 }
638 AnchorTransform::Cursor { .. } | AnchorTransform::CursorOnce { .. } => {
639 let offset = offset.0 - offset.1;
640 with_transform(PxTransform::from(offset));
641 }
642 AnchorTransform::InnerTransform => {
643 with_transform(bounds_info.inner_transform());
644 }
645 AnchorTransform::InnerBorderTransform => {
646 with_transform(border_info.inner_transform(bounds_info));
647 }
648 AnchorTransform::OuterTransform => {
649 with_transform(bounds_info.outer_transform());
650 }
651 _ => widget.render_update(update),
652 }
653 }
654 }
655 }
656 _ => {}
657 });
658 self.insert_impl(layer, widget.boxed());
659 }
660
661 pub fn insert_anchored_node(
673 &self,
674 layer: impl IntoVar<LayerIndex>,
675 anchor: impl IntoVar<WidgetId>,
676 mode: impl IntoVar<AnchorMode>,
677
678 maybe_widget: impl UiNode,
679 ) -> ResponseVar<WidgetId> {
680 let (widget, rsp) = maybe_widget.init_widget();
681 self.insert_anchored(layer, anchor, mode, widget);
682 rsp
683 }
684
685 pub fn remove(&self, id: impl Into<WidgetId>) {
693 WINDOW.with_state(|s| {
694 s.req(*WINDOW_LAYERS_ID).items.remove(id);
695 });
696 }
697
698 pub fn remove_node(&self, id: ResponseVar<WidgetId>) {
703 if let Some(id) = id.rsp() {
704 self.remove(id);
705 } else {
706 let items = WINDOW.with_state(|s| s.req(*WINDOW_LAYERS_ID).items.clone());
707 id.hook(move |a| {
708 match a.value() {
709 zng_var::types::Response::Waiting => true,
710 zng_var::types::Response::Done(id) => {
711 items.remove(*id);
713 false
714 }
715 }
716 })
717 .perm();
718 }
719 }
720
721 pub fn anchor_id(&self) -> ReadOnlyContextVar<Option<WidgetId>> {
723 ANCHOR_ID_VAR.read_only()
724 }
725
726 fn cleanup(&self) {
727 WINDOW.with_state(|s| {
728 s.req(*WINDOW_LAYERS_ID).items.retain(|n| n.is_widget());
729 });
730 }
731}
732
733fn adjust_viewport_bound(transform: PxTransform, widget: &mut impl UiNode) -> PxTransform {
734 let window_bounds = WINDOW.vars().actual_size_px().get();
735 let wgt_bounds = PxBox::from(
736 widget
737 .with_context(WidgetUpdateMode::Ignore, || WIDGET.bounds().outer_size())
738 .unwrap_or_else(PxSize::zero),
739 );
740 let wgt_bounds = transform.outer_transformed(wgt_bounds).unwrap_or_default();
741
742 let x_underflow = -wgt_bounds.min.x.min(Px(0));
743 let x_overflow = (wgt_bounds.max.x - window_bounds.width).max(Px(0));
744 let y_underflow = -wgt_bounds.min.y.min(Px(0));
745 let y_overflow = (wgt_bounds.max.y - window_bounds.height).max(Px(0));
746
747 let x = x_underflow - x_overflow;
748 let y = y_underflow - y_overflow;
749
750 let correction = PxVector::new(x, y);
751
752 transform.then_translate(correction.cast())
753}
754
755fn with_anchor_id(child: impl UiNode, anchor: BoxedVar<WidgetId>) -> impl UiNode {
756 let mut ctx = Some(Arc::new(anchor.map(|id| Some(*id))));
757 let mut id = None;
758 match_widget(child, move |c, op| {
759 let mut is_deinit = false;
760 match &op {
761 UiNodeOp::Init => {
762 id = Some(ContextInitHandle::new());
763 }
764 UiNodeOp::Deinit => {
765 is_deinit = true;
766 }
767 _ => {}
768 }
769 ANCHOR_ID_VAR.with_context(id.clone().expect("node not inited"), &mut ctx, || c.op(op));
770
771 if is_deinit {
772 id = None;
773 }
774 })
775}
776
777context_var! {
778 static ANCHOR_ID_VAR: Option<WidgetId> = None;
779}
780
781static_id! {
782 static ref WINDOW_PRE_INIT_LAYERS_ID: StateId<Vec<Mutex<BoxedUiNode>>>;
783 static ref WINDOW_LAYERS_ID: StateId<LayersCtx>;
784 static ref LAYER_INDEX_ID: StateId<LayerIndex>;
785}
786
787#[derive(Default, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
791pub struct LayerIndex(pub u32);
792impl LayerIndex {
793 pub const TOP_MOST: LayerIndex = LayerIndex(u32::MAX);
800
801 pub const ADORNER: LayerIndex = LayerIndex(Self::TOP_MOST.0 - u16::MAX as u32);
808
809 pub const DEFAULT: LayerIndex = LayerIndex(0);
813
814 pub fn saturating_add(self, other: impl Into<LayerIndex>) -> Self {
818 Self(self.0.saturating_add(other.into().0))
819 }
820
821 pub fn saturating_sub(self, other: impl Into<LayerIndex>) -> Self {
825 Self(self.0.saturating_sub(other.into().0))
826 }
827
828 pub fn name(self) -> Option<&'static str> {
830 if self == Self::DEFAULT {
831 Some("DEFAULT")
832 } else if self == Self::TOP_MOST {
833 Some("TOP_MOST")
834 } else if self == Self::ADORNER {
835 Some("ADORNER")
836 } else {
837 None
838 }
839 }
840}
841impl fmt::Debug for LayerIndex {
842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
843 if let Some(name) = self.name() {
844 if f.alternate() {
845 write!(f, "LayerIndex::")?;
846 }
847 write!(f, "{}", name)
848 } else {
849 write!(f, "LayerIndex({})", self.0)
850 }
851 }
852}
853impl std::str::FromStr for LayerIndex {
854 type Err = <u32 as std::str::FromStr>::Err;
855
856 fn from_str(s: &str) -> Result<Self, Self::Err> {
857 match s {
858 "DEFAULT" => Ok(Self::DEFAULT),
859 "TOP_MOST" => Ok(Self::TOP_MOST),
860 "ADORNER" => Ok(Self::ADORNER),
861 n => Ok(Self(n.parse()?)),
862 }
863 }
864}
865impl_from_and_into_var! {
866 fn from(index: u32) -> LayerIndex {
867 LayerIndex(index)
868 }
869}
870impl<T: Into<Self>> ops::Add<T> for LayerIndex {
872 type Output = Self;
873
874 fn add(self, rhs: T) -> Self::Output {
875 self.saturating_add(rhs)
876 }
877}
878impl<T: Into<Self>> ops::Sub<T> for LayerIndex {
880 type Output = Self;
881
882 fn sub(self, rhs: T) -> Self::Output {
883 self.saturating_sub(rhs)
884 }
885}
886impl<T: Into<Self>> ops::AddAssign<T> for LayerIndex {
888 fn add_assign(&mut self, rhs: T) {
889 *self = *self + rhs;
890 }
891}
892impl<T: Into<Self>> ops::SubAssign<T> for LayerIndex {
894 fn sub_assign(&mut self, rhs: T) {
895 *self = *self - rhs;
896 }
897}
898impl ops::Mul<Factor> for LayerIndex {
899 type Output = Self;
900
901 fn mul(self, rhs: Factor) -> Self::Output {
902 LayerIndex(self.0 * rhs)
903 }
904}
905impl ops::MulAssign<Factor> for LayerIndex {
906 fn mul_assign(&mut self, rhs: Factor) {
907 self.0 *= rhs;
908 }
909}
910impl ops::Div<Factor> for LayerIndex {
911 type Output = Self;
912
913 fn div(self, rhs: Factor) -> Self::Output {
914 LayerIndex(self.0 / rhs)
915 }
916}
917impl ops::DivAssign<Factor> for LayerIndex {
918 fn div_assign(&mut self, rhs: Factor) {
919 self.0 /= rhs;
920 }
921}
922#[derive(serde::Serialize, serde::Deserialize)]
923#[serde(untagged)]
924enum LayerIndexSerde<'s> {
925 Named(&'s str),
926 Unnamed(u32),
927}
928impl serde::Serialize for LayerIndex {
929 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
930 where
931 S: serde::Serializer,
932 {
933 if serializer.is_human_readable() {
934 if let Some(name) = self.name() {
935 return LayerIndexSerde::Named(name).serialize(serializer);
936 }
937 }
938 LayerIndexSerde::Unnamed(self.0).serialize(serializer)
939 }
940}
941impl<'de> serde::Deserialize<'de> for LayerIndex {
942 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
943 where
944 D: serde::Deserializer<'de>,
945 {
946 use serde::de::Error;
947
948 match LayerIndexSerde::deserialize(deserializer)? {
949 LayerIndexSerde::Named(name) => match name {
950 "DEFAULT" => Ok(Self::DEFAULT),
951 "TOP_MOST" => Ok(Self::TOP_MOST),
952 "ADORNER" => Ok(Self::ADORNER),
953 unknown => Err(D::Error::unknown_variant(unknown, &["DEFAULT", "TOP_MOST", "ADORNER"])),
954 },
955 LayerIndexSerde::Unnamed(i) => Ok(Self(i)),
956 }
957 }
958}
959
960#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
965pub struct AnchorOffset {
966 pub place: Point,
968 pub origin: Point,
970}
971impl AnchorOffset {
972 pub fn new(point: Point) -> Self {
974 Self {
975 place: point.clone(),
976 origin: point,
977 }
978 }
979
980 pub fn in_top() -> Self {
982 Self::new(Point::top())
983 }
984
985 pub fn in_bottom() -> Self {
987 Self::new(Point::bottom())
988 }
989
990 pub fn in_left() -> Self {
992 Self::new(Point::left())
993 }
994
995 pub fn in_right() -> Self {
997 Self::new(Point::right())
998 }
999
1000 pub fn in_top_left() -> Self {
1002 Self::new(Point::top_left())
1003 }
1004
1005 pub fn in_top_right() -> Self {
1007 Self::new(Point::top_right())
1008 }
1009
1010 pub fn in_bottom_left() -> Self {
1012 Self::new(Point::bottom_left())
1013 }
1014
1015 pub fn in_bottom_right() -> Self {
1017 Self::new(Point::bottom_right())
1018 }
1019
1020 pub fn center() -> Self {
1022 Self::new(Point::center())
1023 }
1024
1025 pub fn out_top() -> Self {
1027 Self {
1028 place: Point::top(),
1029 origin: Point::bottom(),
1030 }
1031 }
1032
1033 pub fn out_bottom() -> Self {
1035 Self {
1036 place: Point::bottom(),
1037 origin: Point::top(),
1038 }
1039 }
1040
1041 pub fn out_left() -> Self {
1043 Self {
1044 place: Point::left(),
1045 origin: Point::right(),
1046 }
1047 }
1048
1049 pub fn out_right() -> Self {
1051 Self {
1052 place: Point::right(),
1053 origin: Point::left(),
1054 }
1055 }
1056
1057 pub fn out_top_left() -> Self {
1059 Self {
1060 place: Point::top_left(),
1061 origin: Point::bottom_right(),
1062 }
1063 }
1064
1065 pub fn out_top_right() -> Self {
1067 Self {
1068 place: Point::top_right(),
1069 origin: Point::bottom_left(),
1070 }
1071 }
1072
1073 pub fn out_bottom_left() -> Self {
1075 Self {
1076 place: Point::bottom_left(),
1077 origin: Point::top_right(),
1078 }
1079 }
1080
1081 pub fn out_bottom_right() -> Self {
1083 Self {
1084 place: Point::bottom_right(),
1085 origin: Point::top_left(),
1086 }
1087 }
1088
1089 pub fn out_top_in_left() -> Self {
1091 Self {
1092 place: Point::top_left(),
1093 origin: Point::bottom_left(),
1094 }
1095 }
1096
1097 pub fn out_top_in_right() -> Self {
1099 Self {
1100 place: Point::top_right(),
1101 origin: Point::bottom_right(),
1102 }
1103 }
1104
1105 pub fn out_bottom_in_left() -> Self {
1107 Self {
1108 place: Point::bottom_left(),
1109 origin: Point::top_left(),
1110 }
1111 }
1112
1113 pub fn out_bottom_in_right() -> Self {
1115 Self {
1116 place: Point::bottom_right(),
1117 origin: Point::top_right(),
1118 }
1119 }
1120
1121 pub fn out_left_in_top() -> Self {
1123 Self {
1124 place: Point::top_left(),
1125 origin: Point::top_right(),
1126 }
1127 }
1128
1129 pub fn out_left_in_bottom() -> Self {
1131 Self {
1132 place: Point::bottom_left(),
1133 origin: Point::bottom_right(),
1134 }
1135 }
1136
1137 pub fn out_right_in_top() -> Self {
1139 Self {
1140 place: Point::top_right(),
1141 origin: Point::top_left(),
1142 }
1143 }
1144
1145 pub fn out_right_in_bottom() -> Self {
1147 Self {
1148 place: Point::bottom_right(),
1149 origin: Point::bottom_left(),
1150 }
1151 }
1152}
1153impl_from_and_into_var! {
1154 fn from<P: Into<Point>, O: Into<Point>>(place_origin: (P, O)) -> AnchorOffset {
1156 AnchorOffset {
1157 place: place_origin.0.into(),
1158 origin: place_origin.1.into(),
1159 }
1160 }
1161}
1162impl animation::Transitionable for AnchorOffset {
1163 fn lerp(self, to: &Self, step: animation::easing::EasingStep) -> Self {
1164 Self {
1165 place: self.place.lerp(&to.place, step),
1166 origin: self.origin.lerp(&to.place, step),
1167 }
1168 }
1169}
1170
1171#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1173pub enum AnchorTransform {
1174 None,
1176 InnerOffset(AnchorOffset),
1179 InnerBorderOffset(AnchorOffset),
1182
1183 OuterOffset(AnchorOffset),
1186
1187 InnerTransform,
1189
1190 InnerBorderTransform,
1192
1193 OuterTransform,
1195
1196 CursorOnce {
1198 offset: AnchorOffset,
1200 include_touch: bool,
1206
1207 bounds: Option<SideOffsets>,
1212 },
1213 Cursor {
1217 offset: AnchorOffset,
1220
1221 include_touch: bool,
1227
1228 bounds: Option<SideOffsets>,
1233 },
1234}
1235impl_from_and_into_var! {
1236 fn from(inner_offset: AnchorOffset) -> AnchorTransform {
1238 AnchorTransform::InnerOffset(inner_offset)
1239 }
1240 fn from<P: Into<Point>, O: Into<Point>>(inner_offset: (P, O)) -> AnchorTransform {
1242 AnchorOffset::from(inner_offset).into()
1243 }
1244}
1245
1246#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1248pub enum AnchorSize {
1249 Unbounded,
1255 Window,
1258 OuterSize,
1260 InnerSize,
1262 InnerBorder,
1264}
1265
1266#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1268pub struct AnchorMode {
1269 pub transform: AnchorTransform,
1271
1272 pub min_size: AnchorSize,
1274 pub max_size: AnchorSize,
1276
1277 pub viewport_bound: bool,
1282
1283 pub visibility: bool,
1288 pub interactivity: bool,
1292
1293 pub corner_radius: bool,
1299}
1300impl AnchorMode {
1301 pub fn window() -> Self {
1304 AnchorMode {
1305 transform: AnchorTransform::None,
1306 min_size: AnchorSize::Window,
1307 max_size: AnchorSize::Window,
1308 viewport_bound: false,
1309 visibility: false,
1310 interactivity: false,
1311 corner_radius: false,
1312 }
1313 }
1314
1315 pub fn foreground() -> Self {
1317 AnchorMode {
1318 transform: AnchorTransform::InnerTransform,
1319 min_size: AnchorSize::InnerSize,
1320 max_size: AnchorSize::InnerSize,
1321 visibility: true,
1322 viewport_bound: false,
1323 interactivity: false,
1324 corner_radius: true,
1325 }
1326 }
1327
1328 pub fn popup(place: AnchorOffset) -> Self {
1330 AnchorMode {
1331 transform: place.into(),
1332 min_size: AnchorSize::InnerSize,
1333 max_size: AnchorSize::Window,
1334 visibility: true,
1335 viewport_bound: true,
1336 interactivity: true,
1337 corner_radius: false,
1338 }
1339 }
1340
1341 pub fn tooltip() -> Self {
1343 AnchorMode {
1344 transform: AnchorTransform::CursorOnce {
1345 offset: AnchorOffset::out_bottom_in_left(),
1346 include_touch: true,
1347 bounds: None,
1348 },
1349 min_size: AnchorSize::Unbounded,
1350 max_size: AnchorSize::Window,
1351 viewport_bound: true,
1352 corner_radius: false,
1353 visibility: true,
1354 interactivity: false,
1355 }
1356 }
1357
1358 pub fn tooltip_shortcut() -> Self {
1360 AnchorMode {
1361 transform: AnchorTransform::InnerOffset({
1362 let mut p = AnchorOffset::out_top();
1363 p.origin.y += 4;
1364 p
1365 }),
1366 min_size: AnchorSize::Unbounded,
1367 max_size: AnchorSize::Window,
1368 viewport_bound: true,
1369 corner_radius: false,
1370 visibility: true,
1371 interactivity: false,
1372 }
1373 }
1374
1375 pub fn context_menu() -> Self {
1377 AnchorMode {
1378 transform: AnchorTransform::CursorOnce {
1379 offset: AnchorOffset::in_top_left(),
1380 include_touch: true,
1381 bounds: None,
1382 },
1383 min_size: AnchorSize::Unbounded,
1384 max_size: AnchorSize::Window,
1385 viewport_bound: true,
1386 corner_radius: false,
1387 visibility: true,
1388 interactivity: false,
1389 }
1390 }
1391
1392 pub fn context_menu_shortcut() -> Self {
1394 AnchorMode {
1395 transform: AnchorTransform::InnerOffset(AnchorOffset::in_top()),
1396 min_size: AnchorSize::Unbounded,
1397 max_size: AnchorSize::Window,
1398 viewport_bound: true,
1399 corner_radius: false,
1400 visibility: true,
1401 interactivity: false,
1402 }
1403 }
1404
1405 pub fn with_transform(mut self, transform: impl Into<AnchorTransform>) -> Self {
1407 self.transform = transform.into();
1408 self
1409 }
1410
1411 pub fn with_min_size(mut self, size: impl Into<AnchorSize>) -> Self {
1413 self.min_size = size.into();
1414 self
1415 }
1416
1417 pub fn with_max_size(mut self, size: impl Into<AnchorSize>) -> Self {
1419 self.max_size = size.into();
1420 self
1421 }
1422
1423 pub fn with_size(mut self, size: impl Into<AnchorSize>) -> Self {
1425 let size = size.into();
1426 self.min_size = size;
1427 self.max_size = size;
1428 self
1429 }
1430
1431 pub fn with_visibility(mut self, visibility: bool) -> Self {
1433 self.visibility = visibility;
1434 self
1435 }
1436
1437 pub fn with_interactivity(mut self, interactivity: bool) -> Self {
1439 self.interactivity = interactivity;
1440 self
1441 }
1442
1443 pub fn with_corner_radius(mut self, corner_radius: bool) -> Self {
1445 self.corner_radius = corner_radius;
1446 self
1447 }
1448
1449 pub fn with_viewport_bound(mut self, viewport_bound: bool) -> Self {
1451 self.viewport_bound = viewport_bound;
1452 self
1453 }
1454}
1455impl Default for AnchorMode {
1456 fn default() -> Self {
1458 AnchorMode {
1459 transform: AnchorTransform::InnerOffset(AnchorOffset::in_top_left()),
1460 min_size: AnchorSize::Unbounded,
1461 max_size: AnchorSize::Unbounded,
1462 viewport_bound: false,
1463 visibility: true,
1464 interactivity: false,
1465 corner_radius: true,
1466 }
1467 }
1468}
1469impl_from_and_into_var! {
1470 fn from(transform: AnchorTransform) -> AnchorMode {
1472 AnchorMode {
1473 transform,
1474 ..AnchorMode::default()
1475 }
1476 }
1477 fn from(inner_offset: AnchorOffset) -> AnchorMode {
1479 AnchorTransform::from(inner_offset).into()
1480 }
1481
1482 fn from<T: Into<AnchorTransform>, S: Into<AnchorSize>>((transform, size): (T, S)) -> AnchorMode {
1484 let size = size.into();
1485 AnchorMode {
1486 transform: transform.into(),
1487 min_size: size,
1488 max_size: size,
1489 ..AnchorMode::default()
1490 }
1491 }
1492}
1493
1494pub fn layers_node(child: impl UiNode) -> impl UiNode {
1498 let layers = EditableUiNodeList::new();
1499 let layered = layers.reference();
1500
1501 fn sort(a: &mut BoxedUiNode, b: &mut BoxedUiNode) -> std::cmp::Ordering {
1502 let a = a
1503 .with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID))
1504 .unwrap_or(LayerIndex::DEFAULT);
1505 let b = b
1506 .with_context(WidgetUpdateMode::Ignore, || WIDGET.req_state(*LAYER_INDEX_ID))
1507 .unwrap_or(LayerIndex::DEFAULT);
1508
1509 a.cmp(&b)
1510 }
1511 let sorting_layers = SortingList::new(layers, sort);
1512 let children = ui_vec![child].chain(sorting_layers);
1513
1514 let mut _insert_handle = CommandHandle::dummy();
1515 let mut _remove_handle = CommandHandle::dummy();
1516
1517 match_node_list(children, move |c, op| match op {
1518 UiNodeOp::Init => {
1519 WINDOW.with_state_mut(|mut s| {
1520 s.set(*WINDOW_LAYERS_ID, LayersCtx { items: layered.clone() });
1521
1522 if let Some(widgets) = s.get_mut(*WINDOW_PRE_INIT_LAYERS_ID) {
1523 for wgt in widgets.drain(..) {
1524 layered.push(wgt.into_inner());
1525 }
1526 }
1527 });
1528 _insert_handle = LAYERS_INSERT_CMD.scoped(WINDOW.id()).subscribe(true);
1529 _remove_handle = LAYERS_REMOVE_CMD.scoped(WINDOW.id()).subscribe(true);
1530 }
1531 UiNodeOp::Deinit => {
1532 _insert_handle = CommandHandle::dummy();
1533 _remove_handle = CommandHandle::dummy();
1534 }
1535 UiNodeOp::Event { update } => {
1536 c.event_all(update);
1537 if let Some(args) = LAYERS_INSERT_CMD.scoped(WINDOW.id()).on_unhandled(update) {
1538 if let Some((layer, widget)) = args.param::<(LayerIndex, WidgetFn<()>)>() {
1539 LAYERS.insert(*layer, widget(()));
1540 args.propagation().stop();
1541 } else if let Some((layer, anchor, mode, widget)) = args.param::<(LayerIndex, WidgetId, AnchorMode, WidgetFn<()>)>() {
1542 LAYERS.insert_anchored(*layer, *anchor, mode.clone(), widget(()));
1543 args.propagation().stop();
1544 } else {
1545 tracing::debug!("ignoring LAYERS_INSERT_CMD, unknown param type");
1546 }
1547 } else if let Some(args) = LAYERS_REMOVE_CMD.scoped(WINDOW.id()).on_unhandled(update) {
1548 if let Some(id) = args.param::<WidgetId>() {
1549 LAYERS.remove(*id);
1550 } else {
1551 tracing::debug!("ignoring LAYERS_REMOVE_CMD, unknown param type");
1552 }
1553 }
1554 }
1555 UiNodeOp::Update { updates } => {
1556 let mut changed = false;
1557 c.update_all(updates, &mut changed);
1558
1559 if changed {
1560 WIDGET.layout().render();
1561 }
1562 }
1563 UiNodeOp::Measure { wm, desired_size } => {
1564 *desired_size = c.with_node(0, |n| n.measure(wm));
1565 }
1566 UiNodeOp::Layout { wl, final_size } => {
1567 *final_size = c.with_node(0, |n| n.layout(wl));
1568 let _ = c.children().1.layout_each(wl, |_, l, wl| l.layout(wl), |_, _| PxSize::zero());
1569 }
1570 UiNodeOp::Render { frame } => {
1571 c.with_node(0, |n| n.render(frame));
1572 c.children().1.render_all(frame);
1573 }
1574 UiNodeOp::RenderUpdate { update } => {
1575 c.with_node(0, |n| n.render_update(update));
1576 c.children().1.render_update_all(update);
1577 }
1578 _ => {}
1579 })
1580}
1581
1582#[property(FILL, default(WidgetFn::nil()))]
1593pub fn adorner_fn(child: impl UiNode, adorner_fn: impl IntoVar<WidgetFn<()>>) -> impl UiNode {
1594 let adorner_fn = adorner_fn.into_var();
1595 let mut adorner_id = None;
1596
1597 match_node(child, move |_, op| match op {
1598 UiNodeOp::Init => {
1599 WIDGET.sub_var(&adorner_fn);
1600 let f = adorner_fn.get();
1601 if !f.is_nil() {
1602 let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1603 let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1604 adorner_id = Some(id);
1605 }
1606 }
1607 UiNodeOp::Deinit => {
1608 if let Some(id) = adorner_id.take() {
1609 LAYERS.remove_node(id);
1610 }
1611 }
1612 UiNodeOp::Update { .. } => {
1613 if let Some(f) = adorner_fn.get_new() {
1614 if let Some(id) = adorner_id.take() {
1615 LAYERS.remove_node(id);
1616 }
1617
1618 if !f.is_nil() {
1619 let widget = with_context_blend(LocalContext::capture_filtered(CaptureFilter::All), false, f(()));
1620 let id = LAYERS.insert_anchored_node(LayerIndex::ADORNER, WIDGET.id(), AnchorMode::foreground(), widget);
1621 adorner_id = Some(id);
1622 }
1623 }
1624 }
1625 _ => {}
1626 })
1627}
1628
1629#[property(FILL, default(NilUiNode))]
1636pub fn adorner(child: impl UiNode, adorner: impl UiNode) -> impl UiNode {
1637 adorner_fn(child, WidgetFn::singleton(adorner))
1638}
1639
1640#[cfg(test)]
1641mod tests {
1642 use super::*;
1643
1644 #[test]
1645 pub fn layer_index_ops() {
1646 let idx = LayerIndex::DEFAULT;
1647
1648 let p1 = idx + 1;
1649 let m1 = idx - 1;
1650
1651 let mut idx = idx;
1652
1653 idx += 1;
1654 assert_eq!(idx, p1);
1655
1656 idx -= 2;
1657 assert_eq!(idx, m1);
1658 }
1659}