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_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 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 _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 }
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; }
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 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 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 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 offset.0.x = NO_POS_X;
477 }
478 } else {
479 if offset.0.x != NO_POS_X {
481 *final_size = layer_size;
483 return;
484 }
485 }
486 } else {
487 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 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 frame.hide(|frame| widget.render(frame));
602
603 if frame.frame_id() == FrameId::first() && anchor.get() == WIDGET.id() {
604 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 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 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 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 items.remove(*id);
718 false
719 }
720 }
721 })
722 .perm();
723 }
724 }
725
726 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#[derive(Default, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
797pub struct LayerIndex(pub u32);
798impl LayerIndex {
799 pub const TOP_MOST: LayerIndex = LayerIndex(u32::MAX);
806
807 pub const ADORNER: LayerIndex = LayerIndex(Self::TOP_MOST.0 - u16::MAX as u32);
814
815 pub const DEFAULT: LayerIndex = LayerIndex(0);
819
820 pub fn saturating_add(self, other: impl Into<LayerIndex>) -> Self {
824 Self(self.0.saturating_add(other.into().0))
825 }
826
827 pub fn saturating_sub(self, other: impl Into<LayerIndex>) -> Self {
831 Self(self.0.saturating_sub(other.into().0))
832 }
833
834 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}
876impl<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}
884impl<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}
892impl<T: Into<Self>> ops::AddAssign<T> for LayerIndex {
894 fn add_assign(&mut self, rhs: T) {
895 *self = *self + rhs;
896 }
897}
898impl<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#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
971pub struct AnchorOffset {
972 pub place: Point,
974 pub origin: Point,
976}
977impl AnchorOffset {
978 pub fn new(point: Point) -> Self {
980 Self {
981 place: point.clone(),
982 origin: point,
983 }
984 }
985
986 pub fn in_top() -> Self {
988 Self::new(Point::top())
989 }
990
991 pub fn in_bottom() -> Self {
993 Self::new(Point::bottom())
994 }
995
996 pub fn in_left() -> Self {
998 Self::new(Point::left())
999 }
1000
1001 pub fn in_right() -> Self {
1003 Self::new(Point::right())
1004 }
1005
1006 pub fn in_top_left() -> Self {
1008 Self::new(Point::top_left())
1009 }
1010
1011 pub fn in_top_right() -> Self {
1013 Self::new(Point::top_right())
1014 }
1015
1016 pub fn in_bottom_left() -> Self {
1018 Self::new(Point::bottom_left())
1019 }
1020
1021 pub fn in_bottom_right() -> Self {
1023 Self::new(Point::bottom_right())
1024 }
1025
1026 pub fn center() -> Self {
1028 Self::new(Point::center())
1029 }
1030
1031 pub fn out_top() -> Self {
1033 Self {
1034 place: Point::top(),
1035 origin: Point::bottom(),
1036 }
1037 }
1038
1039 pub fn out_bottom() -> Self {
1041 Self {
1042 place: Point::bottom(),
1043 origin: Point::top(),
1044 }
1045 }
1046
1047 pub fn out_left() -> Self {
1049 Self {
1050 place: Point::left(),
1051 origin: Point::right(),
1052 }
1053 }
1054
1055 pub fn out_right() -> Self {
1057 Self {
1058 place: Point::right(),
1059 origin: Point::left(),
1060 }
1061 }
1062
1063 pub fn out_top_left() -> Self {
1065 Self {
1066 place: Point::top_left(),
1067 origin: Point::bottom_right(),
1068 }
1069 }
1070
1071 pub fn out_top_right() -> Self {
1073 Self {
1074 place: Point::top_right(),
1075 origin: Point::bottom_left(),
1076 }
1077 }
1078
1079 pub fn out_bottom_left() -> Self {
1081 Self {
1082 place: Point::bottom_left(),
1083 origin: Point::top_right(),
1084 }
1085 }
1086
1087 pub fn out_bottom_right() -> Self {
1089 Self {
1090 place: Point::bottom_right(),
1091 origin: Point::top_left(),
1092 }
1093 }
1094
1095 pub fn out_top_in_left() -> Self {
1097 Self {
1098 place: Point::top_left(),
1099 origin: Point::bottom_left(),
1100 }
1101 }
1102
1103 pub fn out_top_in_right() -> Self {
1105 Self {
1106 place: Point::top_right(),
1107 origin: Point::bottom_right(),
1108 }
1109 }
1110
1111 pub fn out_bottom_in_left() -> Self {
1113 Self {
1114 place: Point::bottom_left(),
1115 origin: Point::top_left(),
1116 }
1117 }
1118
1119 pub fn out_bottom_in_right() -> Self {
1121 Self {
1122 place: Point::bottom_right(),
1123 origin: Point::top_right(),
1124 }
1125 }
1126
1127 pub fn out_left_in_top() -> Self {
1129 Self {
1130 place: Point::top_left(),
1131 origin: Point::top_right(),
1132 }
1133 }
1134
1135 pub fn out_left_in_bottom() -> Self {
1137 Self {
1138 place: Point::bottom_left(),
1139 origin: Point::bottom_right(),
1140 }
1141 }
1142
1143 pub fn out_right_in_top() -> Self {
1145 Self {
1146 place: Point::top_right(),
1147 origin: Point::top_left(),
1148 }
1149 }
1150
1151 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 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#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1179pub enum AnchorTransform {
1180 None,
1182 InnerOffset(AnchorOffset),
1185 InnerBorderOffset(AnchorOffset),
1188
1189 OuterOffset(AnchorOffset),
1192
1193 InnerTransform,
1195
1196 InnerBorderTransform,
1198
1199 OuterTransform,
1201
1202 CursorOnce {
1204 offset: AnchorOffset,
1206 include_touch: bool,
1212
1213 bounds: Option<SideOffsets>,
1218 },
1219 Cursor {
1223 offset: AnchorOffset,
1226
1227 include_touch: bool,
1233
1234 bounds: Option<SideOffsets>,
1239 },
1240}
1241impl_from_and_into_var! {
1242 fn from(inner_offset: AnchorOffset) -> AnchorTransform {
1244 AnchorTransform::InnerOffset(inner_offset)
1245 }
1246 fn from<P: Into<Point>, O: Into<Point>>(inner_offset: (P, O)) -> AnchorTransform {
1248 AnchorOffset::from(inner_offset).into()
1249 }
1250}
1251
1252#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1254pub enum AnchorSize {
1255 Unbounded,
1261 Window,
1264 OuterSize,
1266 InnerSize,
1268 InnerBorder,
1270}
1271
1272#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1274pub struct AnchorMode {
1275 pub transform: AnchorTransform,
1277
1278 pub min_size: AnchorSize,
1280 pub max_size: AnchorSize,
1282
1283 pub viewport_bound: bool,
1288
1289 pub visibility: bool,
1294 pub interactivity: bool,
1298
1299 pub corner_radius: bool,
1305}
1306impl AnchorMode {
1307 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 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 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 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 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 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 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 pub fn with_transform(mut self, transform: impl Into<AnchorTransform>) -> Self {
1413 self.transform = transform.into();
1414 self
1415 }
1416
1417 pub fn with_min_size(mut self, size: impl Into<AnchorSize>) -> Self {
1419 self.min_size = size.into();
1420 self
1421 }
1422
1423 pub fn with_max_size(mut self, size: impl Into<AnchorSize>) -> Self {
1425 self.max_size = size.into();
1426 self
1427 }
1428
1429 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 pub fn with_visibility(mut self, visibility: bool) -> Self {
1439 self.visibility = visibility;
1440 self
1441 }
1442
1443 pub fn with_interactivity(mut self, interactivity: bool) -> Self {
1445 self.interactivity = interactivity;
1446 self
1447 }
1448
1449 pub fn with_corner_radius(mut self, corner_radius: bool) -> Self {
1451 self.corner_radius = corner_radius;
1452 self
1453 }
1454
1455 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 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 fn from(transform: AnchorTransform) -> AnchorMode {
1478 AnchorMode {
1479 transform,
1480 ..AnchorMode::default()
1481 }
1482 }
1483 fn from(inner_offset: AnchorOffset) -> AnchorMode {
1485 AnchorTransform::from(inner_offset).into()
1486 }
1487
1488 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
1500pub 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); 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#[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#[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}