1use std::{borrow::Cow, fmt, mem, ops, sync::Arc, time::Duration};
4
5pub mod access;
6
7mod tree;
8use parking_lot::{MappedMutexGuard, Mutex, MutexGuard, RwLock};
9use tree::Tree;
10
11mod path;
12pub use path::*;
13
14mod builder;
15pub use builder::*;
16
17pub mod iter;
18pub use iter::TreeFilter;
19
20mod hit;
21pub(crate) use hit::{HitTestClips, ParallelSegmentOffsets};
22use zng_clone_move::clmv;
23use zng_layout::{
24 context::{LayoutMask, LayoutMetricsSnapshot},
25 unit::{
26 DistanceKey, Factor, FactorUnits, Orientation2D, Px, PxBox, PxCornerRadius, PxPoint, PxRect, PxSideOffsets, PxSize, PxTransform,
27 PxVector, euclid,
28 },
29};
30use zng_state_map::{OwnedStateMap, StateMapRef};
31use zng_txt::{Txt, formatx};
32use zng_unique_id::IdMap;
33use zng_var::impl_from_and_into_var;
34use zng_view_api::{ViewProcessGen, display_list::FrameValueUpdate, window::FrameId};
35
36use crate::{DInstant, render::TransformStyle, window::WindowId};
37
38pub use self::hit::RelativeHitZ;
39use self::{access::AccessEnabled, hit::ParallelSegmentId, iter::TreeIterator};
40
41use super::{WidgetId, node::ZIndex};
42
43#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
47#[non_exhaustive]
48pub struct WidgetInfoTreeStats {
49 pub generation: u32,
51
52 pub build_time: Duration,
56
57 pub reused_widgets: u32,
59
60 pub last_frame: FrameId,
64
65 pub bounds_updated_frame: FrameId,
67
68 pub bounds_updated: u32,
70
71 pub vis_updated_frame: FrameId,
73}
74impl WidgetInfoTreeStats {
75 fn new(build_start: DInstant, reused_widgets: u32, generation: u32) -> Self {
76 Self {
77 generation,
78 build_time: build_start.elapsed(),
79 reused_widgets,
80 last_frame: FrameId::INVALID,
81 bounds_updated_frame: FrameId::INVALID,
82 bounds_updated: 0,
83 vis_updated_frame: FrameId::INVALID,
84 }
85 }
86
87 fn update(&mut self, frame: FrameId, update: WidgetInfoTreeStatsUpdate) {
88 self.last_frame = frame;
89
90 if update.bounds_updated > 0 {
91 self.bounds_updated = update.bounds_updated;
92 self.bounds_updated_frame = frame;
93 } else if self.bounds_updated_frame == FrameId::INVALID {
94 self.bounds_updated_frame = frame;
95 }
96
97 if update.vis_updated > 0 || self.vis_updated_frame == FrameId::INVALID {
99 self.vis_updated_frame = frame;
100 }
101 }
102}
103#[derive(Default)]
104struct WidgetInfoTreeStatsUpdate {
105 bounds_updated: u32,
106 vis_updated: u32,
107}
108impl WidgetInfoTreeStatsUpdate {
109 fn take(&mut self) -> Self {
110 mem::take(self)
111 }
112}
113
114#[derive(Clone, Debug, Default)]
116pub struct WeakWidgetInfoTree(std::sync::Weak<WidgetInfoTreeInner>);
117impl WeakWidgetInfoTree {
118 pub const fn new() -> Self {
120 Self(std::sync::Weak::new())
121 }
122
123 pub fn upgrade(&self) -> Option<WidgetInfoTree> {
125 self.0.upgrade().map(WidgetInfoTree)
126 }
127}
128impl PartialEq for WeakWidgetInfoTree {
129 fn eq(&self, other: &Self) -> bool {
130 self.0.ptr_eq(&other.0)
131 }
132}
133impl Eq for WeakWidgetInfoTree {}
134
135#[derive(Clone)]
141pub struct WidgetInfoTree(Arc<WidgetInfoTreeInner>);
142struct WidgetInfoTreeInner {
143 window_id: WindowId,
144 access_enabled: AccessEnabled,
145 tree: Tree<WidgetInfoData>,
146 lookup: IdMap<WidgetId, tree::NodeId>,
147 interactivity_filters: InteractivityFilters,
148 build_meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
149 frame: RwLock<WidgetInfoTreeFrame>,
150}
151struct WidgetInfoTreeFrame {
153 stats: WidgetInfoTreeStats,
154 stats_update: WidgetInfoTreeStatsUpdate,
155 out_of_bounds_update: Vec<(tree::NodeId, bool)>,
156 scale_factor: Factor,
157 view_process_gen: ViewProcessGen,
158
159 out_of_bounds: Arc<Vec<tree::NodeId>>,
160 spatial_bounds: PxBox,
161
162 widget_count_offsets: ParallelSegmentOffsets,
163
164 transform_changed_subs: IdMap<WidgetId, PxTransform>,
165 visibility_changed_subs: IdMap<WidgetId, Visibility>,
166}
167impl PartialEq for WidgetInfoTree {
168 fn eq(&self, other: &Self) -> bool {
169 Arc::ptr_eq(&self.0, &other.0)
170 }
171}
172impl Eq for WidgetInfoTree {}
173impl WidgetInfoTree {
174 pub fn wgt(window_id: WindowId, root_id: WidgetId) -> Self {
176 WidgetInfoBuilder::new(
177 Arc::default(),
178 window_id,
179 AccessEnabled::empty(),
180 root_id,
181 WidgetBoundsInfo::new(),
182 WidgetBorderInfo::new(),
183 1.fct(),
184 )
185 .finalize(None, false)
186 }
187
188 pub fn stats(&self) -> WidgetInfoTreeStats {
190 self.0.frame.read().stats.clone()
191 }
192
193 pub fn scale_factor(&self) -> Factor {
195 self.0.frame.read().scale_factor
196 }
197
198 pub fn view_process_gen(&self) -> ViewProcessGen {
204 self.0.frame.read().view_process_gen
205 }
206
207 pub fn build_meta(&self) -> StateMapRef<'_, WidgetInfoMeta> {
211 self.0.build_meta.borrow()
212 }
213
214 pub fn root(&self) -> WidgetInfo {
216 WidgetInfo::new(self.clone(), self.0.tree.root().id())
217 }
218
219 pub fn all_widgets(&self) -> iter::TreeIter {
221 self.root().self_and_descendants()
222 }
223
224 pub fn window_id(&self) -> WindowId {
226 self.0.window_id
227 }
228
229 pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
231 self.0.lookup.get(&widget_id.into()).map(|i| WidgetInfo::new(self.clone(), *i))
232 }
233
234 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
236 self.0.lookup.contains_key(&widget_id.into())
237 }
238
239 pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetInfo> {
241 self.get(path.widget_id())
242 .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
243 }
244
245 pub fn is_rendered(&self) -> bool {
248 self.0.frame.read().stats.last_frame != FrameId::INVALID
249 }
250
251 pub fn out_of_bounds(&self) -> impl std::iter::ExactSizeIterator<Item = WidgetInfo> + 'static + use<> {
253 let out = self.0.frame.read().out_of_bounds.clone();
254 let me = self.clone();
255 (0..out.len()).map(move |i| WidgetInfo::new(me.clone(), out[i]))
256 }
257
258 pub fn spatial_bounds(&self) -> PxRect {
260 self.0.frame.read().spatial_bounds.to_rect()
261 }
262
263 #[expect(clippy::len_without_is_empty)]
267 pub fn len(&self) -> usize {
268 self.0.lookup.len()
269 }
270
271 fn bounds_changed(&self) {
272 self.0.frame.write().stats_update.bounds_updated += 1;
273 }
274
275 fn in_bounds_changed(&self, widget_id: WidgetId, in_bounds: bool) {
276 let id = *self.0.lookup.get(&widget_id).unwrap();
277 self.0.frame.write().out_of_bounds_update.push((id, in_bounds));
278 }
279
280 fn visibility_changed(&self) {
281 self.0.frame.write().stats_update.vis_updated += 1;
282 }
283
284 pub(crate) fn after_render(
285 &self,
286 frame_id: FrameId,
287 scale_factor: Factor,
288 view_process_gen: Option<ViewProcessGen>,
289 widget_count_offsets: Option<ParallelSegmentOffsets>,
290 notify: bool,
291 ) {
292 let mut frame = self.0.frame.write();
293 let stats_update = frame.stats_update.take();
294
295 let mut any_update = stats_update.bounds_updated > 0 || stats_update.vis_updated > 0;
296
297 frame.stats.update(frame_id, stats_update);
298
299 if !frame.out_of_bounds_update.is_empty() {
300 any_update = true;
301
302 let mut out_of_bounds = Arc::try_unwrap(mem::take(&mut frame.out_of_bounds)).unwrap_or_else(|rc| (*rc).clone());
306
307 for (id, remove) in frame.out_of_bounds_update.drain(..) {
308 if remove {
309 if let Some(i) = out_of_bounds.iter().position(|i| *i == id) {
310 out_of_bounds.swap_remove(i);
311 }
312 } else {
313 out_of_bounds.push(id);
314 }
315 }
316 frame.out_of_bounds = Arc::new(out_of_bounds);
317 }
318
319 let mut spatial_bounds = self.root().outer_bounds().to_box2d();
320 for out in frame.out_of_bounds.iter() {
321 let b = WidgetInfo::new(self.clone(), *out).inner_bounds().to_box2d();
322 spatial_bounds = spatial_bounds.union(&b);
323 }
324 any_update = any_update || frame.spatial_bounds != spatial_bounds;
325 frame.spatial_bounds = spatial_bounds;
326
327 any_update = any_update || frame.scale_factor != scale_factor;
328 frame.scale_factor = scale_factor;
329
330 if let Some(vp_gen) = view_process_gen {
331 any_update = any_update || frame.view_process_gen != vp_gen;
332
333 frame.view_process_gen = vp_gen;
334 }
335 if let Some(w) = widget_count_offsets {
336 any_update = any_update || frame.widget_count_offsets != w;
337
338 frame.widget_count_offsets = w;
339 }
340
341 if notify && any_update {
342 WIDGET_TREE_CHANGED_EVENT.notify(WidgetTreeChangedArgs::now(self.downgrade(), self.clone(), true));
343 }
344 }
345
346 pub(crate) fn after_render_update(&self, frame_id: FrameId, notify: bool) {
347 let scale_factor = self.0.frame.read().scale_factor;
348 self.after_render(frame_id, scale_factor, None, None, notify);
349 }
350
351 pub fn downgrade(&self) -> WeakWidgetInfoTree {
353 WeakWidgetInfoTree(Arc::downgrade(&self.0))
354 }
355}
356impl fmt::Debug for WidgetInfoTree {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 f.debug_struct("WidgetInfoTree")
359 .field("len()", &self.len())
360 .field("spatial_bounds()", &self.spatial_bounds())
361 .field("stats()", &self.stats())
362 .finish_non_exhaustive()
363 }
364}
365
366#[derive(Debug, Default)]
367struct WidgetBoundsData {
368 inner_offset: PxVector,
369 child_offset: PxVector,
370 parent_child_offset: PxVector,
371
372 inline: Option<WidgetInlineInfo>,
373 measure_inline: Option<WidgetInlineMeasure>,
374
375 measure_outer_size: PxSize,
376 outer_size: PxSize,
377 inner_size: PxSize,
378 baseline: Px,
379 inner_offset_baseline: bool,
380
381 transform_style: TransformStyle,
382 perspective: f32,
383 perspective_origin: Option<PxPoint>,
384
385 measure_metrics: Option<LayoutMetricsSnapshot>,
386 measure_metrics_used: LayoutMask,
387 metrics: Option<LayoutMetricsSnapshot>,
388 metrics_used: LayoutMask,
389
390 outer_transform: PxTransform,
391 inner_transform: PxTransform,
392 rendered: Option<WidgetRenderInfo>,
393
394 outer_bounds: PxRect,
395 inner_bounds: PxRect,
396
397 hit_clips: HitTestClips,
398 hit_index: hit::HitChildIndex,
399
400 is_in_bounds: Option<bool>,
401 is_partially_culled: bool,
402 cannot_auto_hide: bool,
403 is_collapsed: bool,
404}
405
406#[derive(Debug, Clone, Copy)]
408pub(crate) struct WidgetRenderInfo {
409 pub visible: bool,
411
412 pub parent_perspective: Option<(f32, PxPoint)>,
413
414 pub seg_id: ParallelSegmentId,
416 pub back: usize,
417 pub front: usize,
418}
419
420#[derive(Default, Clone, Debug)]
426pub struct WidgetBoundsInfo(Arc<Mutex<WidgetBoundsData>>);
427impl PartialEq for WidgetBoundsInfo {
428 fn eq(&self, other: &Self) -> bool {
429 Arc::ptr_eq(&self.0, &other.0)
430 }
431}
432impl Eq for WidgetBoundsInfo {}
433impl WidgetBoundsInfo {
434 pub fn new() -> Self {
436 Self::default()
437 }
438
439 pub fn new_size(outer: PxSize, inner: PxSize) -> Self {
441 let me = Self::new();
442 me.set_outer_size(outer);
443 me.set_inner_size(inner);
444 me
445 }
446
447 pub fn measure_outer_size(&self) -> PxSize {
451 self.0.lock().measure_outer_size
452 }
453
454 pub fn outer_size(&self) -> PxSize {
456 self.0.lock().outer_size
457 }
458
459 pub fn inner_offset(&self) -> PxVector {
466 let mut r = self.0.lock().inner_offset;
467 if self.inner_offset_baseline() {
468 r.y += self.baseline();
469 }
470 r
471 }
472
473 pub fn inner_offset_baseline(&self) -> bool {
478 self.0.lock().inner_offset_baseline
479 }
480
481 pub fn child_offset(&self) -> PxVector {
487 self.0.lock().child_offset
488 }
489
490 pub fn inner_size(&self) -> PxSize {
492 self.0.lock().inner_size
493 }
494
495 pub fn baseline(&self) -> Px {
504 self.0.lock().baseline
505 }
506
507 pub fn final_baseline(&self) -> Px {
515 let s = self.0.lock();
516 if s.inner_offset_baseline { Px(0) } else { s.baseline }
517 }
518
519 pub fn outer_transform(&self) -> PxTransform {
521 self.0.lock().outer_transform
522 }
523
524 pub fn parent_child_offset(&self) -> PxVector {
528 self.0.lock().parent_child_offset
529 }
530
531 pub fn inner_transform(&self) -> PxTransform {
533 self.0.lock().inner_transform
534 }
535
536 pub fn measure_inline(&self) -> Option<WidgetInlineMeasure> {
545 self.0.lock().measure_inline.clone()
546 }
547
548 pub fn inline(&self) -> Option<MappedMutexGuard<'_, WidgetInlineInfo>> {
552 let me = self.0.lock();
553 if me.inline.is_some() {
554 Some(MutexGuard::map(me, |m| m.inline.as_mut().unwrap()))
555 } else {
556 None
557 }
558 }
559
560 pub fn rendered(&self) -> Option<bool> {
562 self.0.lock().rendered.map(|i| i.visible)
563 }
564
565 pub(crate) fn render_info(&self) -> Option<WidgetRenderInfo> {
566 self.0.lock().rendered
567 }
568
569 pub fn is_in_bounds(&self) -> bool {
573 self.0.lock().is_in_bounds.unwrap_or(false)
574 }
575
576 pub fn can_auto_hide(&self) -> bool {
585 !self.0.lock().cannot_auto_hide
586 }
587
588 fn set_can_auto_hide(&self, enabled: bool) {
589 self.0.lock().cannot_auto_hide = !enabled;
590 }
591
592 pub(crate) fn is_actually_out_of_bounds(&self) -> bool {
593 self.0.lock().is_in_bounds.map(|is| !is).unwrap_or(false)
594 }
595
596 pub(crate) fn set_rendered(&self, rendered: Option<WidgetRenderInfo>, info: &WidgetInfoTree) {
597 let mut m = self.0.lock();
598 if m.rendered.map(|i| i.visible) != rendered.map(|i| i.visible) {
599 info.visibility_changed();
600 }
601 m.rendered = rendered;
602 }
603
604 pub(crate) fn set_outer_transform(&self, transform: PxTransform, info: &WidgetInfoTree) {
605 let bounds = transform
606 .outer_transformed(PxBox::from_size(self.outer_size()))
607 .unwrap_or_default()
608 .to_rect();
609
610 let mut m = self.0.lock();
611
612 if m.outer_bounds.size.is_empty() != bounds.size.is_empty() {
613 info.visibility_changed();
614 }
615
616 m.outer_bounds = bounds;
617 m.outer_transform = transform;
618 }
619
620 pub(crate) fn set_parent_child_offset(&self, offset: PxVector) {
621 self.0.lock().parent_child_offset = offset;
622 }
623
624 pub(crate) fn set_inner_transform(
625 &self,
626 transform: PxTransform,
627 info: &WidgetInfoTree,
628 widget_id: WidgetId,
629 parent_inner: Option<PxRect>,
630 ) {
631 let bounds = transform
632 .outer_transformed(PxBox::from_size(self.inner_size()))
633 .unwrap_or_default()
634 .to_rect();
635
636 let mut m = self.0.lock();
637
638 if m.inner_bounds != bounds {
639 m.inner_bounds = bounds;
640 info.bounds_changed();
641 }
642 let in_bounds = parent_inner.map(|r| r.contains_rect(&bounds)).unwrap_or(true);
643 if let Some(prev) = m.is_in_bounds {
644 if prev != in_bounds {
645 m.is_in_bounds = Some(in_bounds);
646 info.in_bounds_changed(widget_id, in_bounds);
647 }
648 } else {
649 m.is_in_bounds = Some(in_bounds);
650 if !in_bounds {
651 info.in_bounds_changed(widget_id, in_bounds);
652 }
653 }
654
655 m.inner_transform = transform;
656 }
657
658 pub fn outer_bounds(&self) -> PxRect {
660 self.0.lock().outer_bounds
661 }
662
663 pub fn inner_bounds(&self) -> PxRect {
665 self.0.lock().inner_bounds
666 }
667
668 pub fn inner_rects(&self) -> Vec<PxRect> {
672 let m = self.0.lock();
673 if let Some(i) = &m.inline {
674 let offset = m.inner_bounds.origin.to_vector();
675 let mut rows = i.rows.clone();
676 for r in &mut rows {
677 r.origin += offset;
678 }
679 rows
680 } else {
681 vec![m.inner_bounds]
682 }
683 }
684
685 pub fn visit_inner_rects<B>(&self, mut visitor: impl FnMut(PxRect, usize, usize) -> ops::ControlFlow<B>) -> Option<B> {
692 let m = self.0.lock();
693 let inner_bounds = m.inner_bounds;
694 let inline_range = m.inline.as_ref().map(|i| 0..i.rows.len());
695 drop(m);
696
697 if let Some(inline_range) = inline_range {
698 let offset = inner_bounds.origin.to_vector();
699 let len = inline_range.len();
700
701 for i in inline_range {
702 let mut r = match self.0.lock().inline.as_ref().and_then(|inl| inl.rows.get(i).copied()) {
703 Some(r) => r,
704 None => break, };
706 r.origin += offset;
707 match visitor(r, i, len) {
708 ops::ControlFlow::Continue(()) => continue,
709 ops::ControlFlow::Break(r) => return Some(r),
710 }
711 }
712 None
713 } else {
714 match visitor(inner_bounds, 0, 0) {
715 ops::ControlFlow::Continue(()) => None,
716 ops::ControlFlow::Break(r) => Some(r),
717 }
718 }
719 }
720
721 pub fn is_collapsed(&self) -> bool {
723 self.0.lock().is_collapsed
724 }
725
726 pub fn transform_style(&self) -> TransformStyle {
728 self.0.lock().transform_style
729 }
730
731 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
733 let p = self.0.lock();
734 if p.perspective.is_finite() {
735 let s = p.inner_size;
736 let o = p.perspective_origin.unwrap_or_else(|| PxPoint::new(s.width / 2.0, s.height / 2.0));
737 Some((p.perspective, o))
738 } else {
739 None
740 }
741 }
742
743 pub fn metrics(&self) -> Option<LayoutMetricsSnapshot> {
752 self.0.lock().metrics.clone()
753 }
754
755 pub fn metrics_used(&self) -> LayoutMask {
759 self.0.lock().metrics_used
760 }
761
762 pub fn hit_test_z(&self, window_point: PxPoint) -> RelativeHitZ {
764 let m = self.0.lock();
765 if m.hit_clips.is_hit_testable() {
766 m.hit_clips.hit_test_z(&m.inner_transform, window_point)
767 } else {
768 RelativeHitZ::NoHit
769 }
770 }
771
772 fn hit_test_index(&self) -> hit::HitChildIndex {
774 self.0.lock().hit_index
775 }
776
777 pub fn hit_test_clip_child(&self, child: &WidgetInfo, window_point: PxPoint) -> bool {
779 let m = self.0.lock();
780 if m.hit_clips.is_hit_testable() {
781 m.hit_clips
782 .clip_child(child.bounds_info().hit_test_index(), &m.inner_transform, window_point)
783 } else {
784 false
785 }
786 }
787
788 pub(crate) fn update_hit_test_transform(&self, value: FrameValueUpdate<PxTransform>) {
789 self.0.lock().hit_clips.update_transform(value);
790 }
791
792 pub(crate) fn measure_metrics(&self) -> Option<LayoutMetricsSnapshot> {
793 self.0.lock().measure_metrics.clone()
794 }
795 pub(crate) fn measure_metrics_used(&self) -> LayoutMask {
796 self.0.lock().measure_metrics_used
797 }
798
799 fn set_outer_size(&self, size: PxSize) {
800 let mut s = self.0.lock();
801 if !size.is_empty() {
802 s.is_collapsed = false;
803 }
804 s.outer_size = size;
805 }
806
807 fn set_is_collapsed(&self, collapsed: bool) {
808 self.0.lock().is_collapsed = collapsed;
809 }
810
811 fn take_inline(&self) -> Option<WidgetInlineInfo> {
812 self.0.lock().inline.take()
813 }
814
815 fn set_inline(&self, inline: Option<WidgetInlineInfo>) {
816 self.0.lock().inline = inline;
817 }
818
819 pub(super) fn set_measure_inline(&self, inline: Option<WidgetInlineMeasure>) {
820 self.0.lock().measure_inline = inline;
821 }
822
823 pub(crate) fn set_measure_outer_size(&self, size: PxSize) {
824 self.0.lock().measure_outer_size = size;
825 }
826
827 fn set_inner_offset(&self, offset: PxVector) {
828 self.0.lock().inner_offset = offset;
829 }
830
831 fn set_child_offset(&self, offset: PxVector) {
832 self.0.lock().child_offset = offset;
833 }
834
835 fn set_inner_size(&self, size: PxSize) {
836 self.0.lock().inner_size = size;
837 }
838
839 fn set_baseline(&self, baseline: Px) {
840 self.0.lock().baseline = baseline;
841 }
842
843 fn set_inner_offset_baseline(&self, enabled: bool) {
844 self.0.lock().inner_offset_baseline = enabled;
845 }
846
847 fn set_transform_style(&self, style: TransformStyle) {
848 self.0.lock().transform_style = style;
849 }
850
851 fn raw_perspective(&self) -> f32 {
852 self.0.lock().perspective
853 }
854
855 fn raw_perspective_origin(&self) -> Option<PxPoint> {
856 self.0.lock().perspective_origin
857 }
858
859 fn set_perspective(&self, d: f32) {
860 self.0.lock().perspective = d;
861 }
862
863 fn set_perspective_origin(&self, o: Option<PxPoint>) {
864 self.0.lock().perspective_origin = o;
865 }
866
867 fn set_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
868 self.0.lock().metrics = metrics;
869 self.0.lock().metrics_used = used;
870 }
871
872 pub(crate) fn set_measure_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
873 self.0.lock().measure_metrics = metrics;
874 self.0.lock().measure_metrics_used = used;
875 }
876
877 pub(crate) fn set_hit_clips(&self, clips: HitTestClips) {
878 self.0.lock().hit_clips = clips;
879 }
880
881 pub(crate) fn set_hit_index(&self, index: hit::HitChildIndex) {
882 self.0.lock().hit_index = index;
883 }
884
885 pub(crate) fn is_partially_culled(&self) -> bool {
886 self.0.lock().is_partially_culled
887 }
888
889 pub(crate) fn set_is_partially_culled(&self, is: bool) {
890 self.0.lock().is_partially_culled = is;
891 }
892}
893
894#[derive(Default, Debug)]
895struct WidgetBorderData {
896 offsets: PxSideOffsets,
897 corner_radius: PxCornerRadius,
898}
899
900#[derive(Default, Clone, Debug)]
902pub struct WidgetBorderInfo(Arc<Mutex<WidgetBorderData>>);
903impl WidgetBorderInfo {
904 pub fn new() -> Self {
906 Self::default()
907 }
908
909 #[cfg(test)]
911 pub fn new_test(offsets: PxSideOffsets, corner_radius: PxCornerRadius) -> Self {
912 let r = Self::default();
913 r.set_offsets(offsets);
914 r.set_corner_radius(corner_radius);
915 r
916 }
917
918 pub fn offsets(&self) -> PxSideOffsets {
920 self.0.lock().offsets
921 }
922
923 pub fn corner_radius(&self) -> PxCornerRadius {
925 self.0.lock().corner_radius
926 }
927
928 pub fn inner_corner_radius(&self) -> PxCornerRadius {
933 self.corner_radius().deflate(self.offsets())
934 }
935
936 pub fn inner_offset(&self, bounds: &WidgetBoundsInfo) -> PxVector {
940 let o = self.offsets();
941 let o = PxVector::new(o.left, o.top);
942 bounds.inner_offset() + o
943 }
944
945 pub fn inner_size(&self, bounds: &WidgetBoundsInfo) -> PxSize {
949 let o = self.offsets();
950 bounds.inner_size() - PxSize::new(o.horizontal(), o.vertical())
951 }
952
953 pub fn inner_transform(&self, bounds: &WidgetBoundsInfo) -> PxTransform {
957 let o = self.offsets();
958 let o = PxVector::new(o.left, o.top);
959 bounds.inner_transform().pre_translate(o.cast())
960 }
961
962 pub(super) fn set_offsets(&self, widths: PxSideOffsets) {
963 self.0.lock().offsets = widths;
964 }
965
966 pub(super) fn set_corner_radius(&self, radius: PxCornerRadius) {
967 self.0.lock().corner_radius = radius;
968 }
969}
970
971struct WidgetInfoData {
972 id: WidgetId,
973 bounds_info: WidgetBoundsInfo,
974 border_info: WidgetBorderInfo,
975 meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
976 interactivity_filters: InteractivityFilters,
977 local_interactivity: Interactivity,
978 is_reused: bool,
979 cache: Mutex<WidgetInfoCache>,
980}
981impl Clone for WidgetInfoData {
982 fn clone(&self) -> Self {
983 Self {
984 id: self.id,
985 bounds_info: self.bounds_info.clone(),
986 border_info: self.border_info.clone(),
987 meta: self.meta.clone(),
988 interactivity_filters: self.interactivity_filters.clone(),
989 local_interactivity: self.local_interactivity,
990 is_reused: self.is_reused,
991 cache: Mutex::new(match self.cache.try_lock() {
992 Some(c) => c.clone(),
993 None => WidgetInfoCache { interactivity: None },
994 }),
995 }
996 }
997}
998impl fmt::Debug for WidgetInfoData {
999 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1000 f.debug_struct("WidgetInfoData").field("id", &self.id).finish_non_exhaustive()
1001 }
1002}
1003#[derive(Clone)]
1004struct WidgetInfoCache {
1005 interactivity: Option<Interactivity>,
1006}
1007
1008#[derive(Clone)]
1010pub struct WidgetInfo {
1011 tree: WidgetInfoTree,
1012 node_id: tree::NodeId,
1013}
1014impl PartialEq for WidgetInfo {
1015 fn eq(&self, other: &Self) -> bool {
1016 self.node_id == other.node_id && self.tree == other.tree
1017 }
1018}
1019impl Eq for WidgetInfo {}
1020impl std::hash::Hash for WidgetInfo {
1021 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1022 std::hash::Hash::hash(&self.node_id, state)
1023 }
1024}
1025impl std::fmt::Debug for WidgetInfo {
1026 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1027 f.debug_struct("WidgetInfo")
1028 .field("trace_path()", &self.trace_path())
1029 .field("inner_bounds()", &self.inner_bounds())
1030 .finish_non_exhaustive()
1031 }
1032}
1033
1034impl WidgetInfo {
1035 fn new(tree: WidgetInfoTree, node_id: tree::NodeId) -> Self {
1036 Self { tree, node_id }
1037 }
1038
1039 fn node(&self) -> tree::NodeRef<'_, WidgetInfoData> {
1040 self.tree.0.tree.index(self.node_id)
1041 }
1042
1043 fn info(&self) -> &WidgetInfoData {
1044 self.node().value()
1045 }
1046
1047 pub fn id(&self) -> WidgetId {
1049 self.info().id
1050 }
1051
1052 pub fn path(&self) -> WidgetPath {
1054 let mut path: Vec<_> = self.ancestors().map(|a| a.id()).collect();
1055 path.reverse();
1056 path.push(self.id());
1057 path.shrink_to_fit();
1058
1059 WidgetPath::new(self.tree.0.window_id, path.into())
1060 }
1061
1062 pub fn trace_path(&self) -> Txt {
1066 let mut ws: Vec<_> = self.self_and_ancestors().collect();
1067 ws.reverse();
1068
1069 use std::fmt::*;
1070
1071 let mut s = String::new();
1072
1073 let _ = write!(&mut s, "{:?}/", self.tree.window_id());
1074 for w in ws {
1075 #[cfg(feature = "inspector")]
1076 {
1077 use crate::widget::inspector::*;
1078 if let Some(info) = w.inspector_info() {
1079 let mod_path = info.builder.widget_type().path;
1080 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1081 ident
1082 } else {
1083 mod_path
1084 };
1085
1086 let id = w.id();
1087 let name = id.name();
1088 if !name.is_empty() {
1089 let _ = write!(&mut s, "/{mod_ident}!({name:?})");
1090 } else {
1091 let _ = write!(&mut s, "/{mod_ident}!({})", id.sequential());
1092 }
1093 } else {
1094 let _ = write!(&mut s, "/{}", w.id());
1095 }
1096 }
1097
1098 #[cfg(not(feature = "inspector"))]
1099 {
1100 let _ = write!(&mut s, "/{}", w.id());
1101 }
1102 }
1103
1104 s.into()
1105 }
1106
1107 pub fn trace_id(&self) -> Txt {
1111 #[cfg(feature = "inspector")]
1112 {
1113 use crate::widget::inspector::*;
1114 if let Some(info) = self.inspector_info() {
1115 let mod_path = info.builder.widget_type().path;
1116 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1117 ident
1118 } else {
1119 mod_path
1120 };
1121
1122 let id = self.id();
1123 let name = id.name();
1124 if !name.is_empty() {
1125 return formatx!("{mod_ident}!({name:?})");
1126 } else {
1127 return formatx!("{mod_ident}!({})", id.sequential());
1128 }
1129 }
1130 }
1131 formatx!("{}", self.id())
1132 }
1133
1134 pub fn interaction_path(&self) -> InteractionPath {
1138 let mut path = vec![];
1139
1140 let mut blocked = None;
1141 let mut disabled = None;
1142
1143 for w in self.self_and_ancestors() {
1144 let interactivity = w.interactivity();
1145 if interactivity.contains(Interactivity::BLOCKED) {
1146 blocked = Some(path.len());
1147 }
1148 if interactivity.contains(Interactivity::DISABLED) {
1149 disabled = Some(path.len());
1150 }
1151
1152 path.push(w.id());
1153 }
1154 path.reverse();
1155 path.shrink_to_fit();
1156
1157 let len = path.len();
1158
1159 let path = WidgetPath::new(self.tree.0.window_id, path.into());
1160 InteractionPath::new_internal(
1161 path,
1162 blocked.map(|i| len - i - 1).unwrap_or(len),
1163 disabled.map(|i| len - i - 1).unwrap_or(len),
1164 )
1165 }
1166
1167 pub fn new_path(&self, old_path: &WidgetPath) -> Option<WidgetPath> {
1177 assert_eq!(old_path.widget_id(), self.id());
1178 if self
1179 .ancestors()
1180 .zip(old_path.ancestors().iter().rev())
1181 .any(|(ancestor, id)| ancestor.id() != *id)
1182 {
1183 Some(self.path())
1184 } else {
1185 None
1186 }
1187 }
1188
1189 pub fn new_interaction_path(&self, old_path: &InteractionPath) -> Option<InteractionPath> {
1199 assert_eq!(old_path.widget_id(), self.id());
1200
1201 if self.interactivity() != old_path.interactivity()
1202 || self
1203 .ancestors()
1204 .zip(old_path.zip().rev().skip(1))
1205 .any(|(anc, (id, int))| anc.id() != id || anc.interactivity() != int)
1206 {
1207 Some(self.interaction_path())
1208 } else {
1209 None
1210 }
1211 }
1212
1213 pub fn z_index(&self) -> Option<(ZIndex, ZIndex)> {
1222 self.info().bounds_info.render_info().map(|i| {
1223 let offset = self.tree.0.frame.read().widget_count_offsets.offset(i.seg_id);
1224 (ZIndex::from((i.back + offset) as u32), ZIndex::from((i.front + offset) as u32))
1225 })
1226 }
1227
1228 pub fn visibility(&self) -> Visibility {
1237 match self.info().bounds_info.rendered() {
1238 Some(vis) => {
1239 if vis {
1240 Visibility::Visible
1241 } else {
1242 Visibility::Hidden
1243 }
1244 }
1245 None => {
1246 if self.tree.is_rendered() {
1247 Visibility::Collapsed
1248 } else {
1249 Visibility::Visible
1250 }
1251 }
1252 }
1253 }
1254
1255 pub fn interactivity(&self) -> Interactivity {
1260 let cached = self.info().cache.lock().interactivity;
1261 if let Some(cache) = cached {
1262 cache
1263 } else {
1264 let mut cache = self.info().cache.lock();
1265 let mut interactivity = self.info().local_interactivity;
1266
1267 if interactivity != Interactivity::BLOCKED_DISABLED {
1268 interactivity |= self.parent().map(|n| n.interactivity()).unwrap_or(Interactivity::ENABLED);
1269 if interactivity != Interactivity::BLOCKED_DISABLED {
1270 let args = InteractivityFilterArgs { info: self.clone() };
1271 for filter in &self.tree.0.interactivity_filters {
1272 interactivity |= filter(&args);
1273 if interactivity == Interactivity::BLOCKED_DISABLED {
1274 break;
1275 }
1276 }
1277 }
1278 }
1279
1280 cache.interactivity = Some(interactivity);
1281 interactivity
1282 }
1283 }
1284
1285 pub fn bounds_info(&self) -> WidgetBoundsInfo {
1289 self.info().bounds_info.clone()
1290 }
1291
1292 pub fn border_info(&self) -> WidgetBorderInfo {
1296 self.info().border_info.clone()
1297 }
1298
1299 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
1303 self.parent()?.bounds_info().perspective()
1304 }
1305
1306 pub fn transform_style(&self) -> TransformStyle {
1310 if let TransformStyle::Flat = self.bounds_info().transform_style() {
1311 if let Some(p) = self.parent() {
1312 p.bounds_info().transform_style()
1313 } else {
1314 TransformStyle::Flat
1315 }
1316 } else {
1317 TransformStyle::Preserve3D
1318 }
1319 }
1320
1321 pub fn outer_size(&self) -> PxSize {
1325 self.info().bounds_info.outer_size()
1326 }
1327
1328 pub fn inner_size(&self) -> PxSize {
1332 self.info().bounds_info.inner_size()
1333 }
1334
1335 pub fn inner_border_size(&self) -> PxSize {
1339 let info = self.info();
1340 info.border_info.inner_size(&info.bounds_info)
1341 }
1342
1343 pub fn baseline(&self) -> Px {
1345 self.info().bounds_info.baseline()
1346 }
1347
1348 pub fn outer_transform(&self) -> PxTransform {
1352 self.info().bounds_info.outer_transform()
1353 }
1354
1355 pub fn inner_transform(&self) -> PxTransform {
1359 self.info().bounds_info.inner_transform()
1360 }
1361
1362 pub fn outer_bounds(&self) -> PxRect {
1366 let info = self.info();
1367 info.bounds_info.outer_bounds()
1368 }
1369
1370 pub fn inner_bounds(&self) -> PxRect {
1374 let info = self.info();
1375 info.bounds_info.inner_bounds()
1376 }
1377
1378 pub fn spatial_bounds(&self) -> PxBox {
1380 self.out_of_bounds()
1381 .fold(self.inner_bounds().to_box2d(), |acc, w| acc.union(&w.inner_bounds().to_box2d()))
1382 }
1383
1384 pub fn center(&self) -> PxPoint {
1386 self.inner_bounds().center()
1387 }
1388
1389 pub fn meta(&self) -> StateMapRef<'_, WidgetInfoMeta> {
1391 self.info().meta.borrow()
1392 }
1393
1394 pub fn tree(&self) -> &WidgetInfoTree {
1396 &self.tree
1397 }
1398
1399 pub fn is_reused(&self) -> bool {
1401 self.info().is_reused
1402 }
1403
1404 pub fn root(&self) -> Self {
1406 self.tree.root()
1407 }
1408
1409 pub fn parent(&self) -> Option<Self> {
1413 self.node().parent().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1414 }
1415
1416 pub fn prev_sibling(&self) -> Option<Self> {
1418 self.node().prev_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1419 }
1420
1421 pub fn next_sibling(&self) -> Option<Self> {
1423 self.node().next_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1424 }
1425
1426 pub fn first_child(&self) -> Option<Self> {
1428 self.node().first_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1429 }
1430
1431 pub fn last_child(&self) -> Option<Self> {
1433 self.node().last_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1434 }
1435
1436 pub fn has_siblings(&self) -> bool {
1438 self.node().has_siblings()
1439 }
1440
1441 pub fn has_children(&self) -> bool {
1443 self.node().has_children()
1444 }
1445
1446 pub fn siblings(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1448 self.prev_siblings().chain(self.next_siblings())
1449 }
1450
1451 pub fn children(&self) -> iter::Children {
1453 let mut r = self.self_and_children();
1454 r.next();
1455 r.next_back();
1456 r
1457 }
1458
1459 pub fn children_count(&self) -> usize {
1463 self.node().children_count()
1464 }
1465
1466 pub fn self_and_children(&self) -> iter::Children {
1468 iter::Children::new(self.clone())
1469 }
1470
1471 pub fn descendants(&self) -> iter::TreeIter {
1473 let mut d = self.self_and_descendants();
1474 d.next();
1475 d
1476 }
1477
1478 pub fn descendants_len(&self) -> usize {
1482 self.node().descendants_range().len()
1483 }
1484
1485 pub fn self_and_descendants(&self) -> iter::TreeIter {
1487 iter::TreeIter::self_and_descendants(self.clone())
1488 }
1489
1490 pub fn ancestors(&self) -> iter::Ancestors {
1492 let mut r = self.self_and_ancestors();
1493 r.next();
1494 r
1495 }
1496
1497 pub fn descendants_range(&self) -> WidgetDescendantsRange {
1499 WidgetDescendantsRange {
1500 tree: Some(self.tree.clone()),
1501 range: self.node().descendants_range(),
1502 }
1503 }
1504
1505 pub fn cmp_sibling_in(&self, sibling: &WidgetInfo, ancestor: &WidgetInfo) -> Option<std::cmp::Ordering> {
1509 let range = ancestor.node().descendants_range();
1510 let index = self.node_id.get();
1511 let sibling_index = sibling.node_id.get();
1512 if range.contains(&index) && self.tree == sibling.tree && self.tree == ancestor.tree {
1513 return Some(index.cmp(&sibling_index));
1514 }
1515 None
1516 }
1517
1518 pub fn is_ancestor(&self, maybe_descendant: &WidgetInfo) -> bool {
1520 self.descendants_range().contains(maybe_descendant)
1521 }
1522
1523 pub fn is_descendant(&self, maybe_ancestor: &WidgetInfo) -> bool {
1525 maybe_ancestor.descendants_range().contains(self)
1526 }
1527
1528 pub fn self_and_ancestors(&self) -> iter::Ancestors {
1530 iter::Ancestors::new(self.clone())
1531 }
1532
1533 pub fn prev_siblings(&self) -> iter::PrevSiblings {
1535 let mut r = self.self_and_prev_siblings();
1536 r.next();
1537 r
1538 }
1539
1540 pub fn self_and_prev_siblings(&self) -> iter::PrevSiblings {
1542 iter::PrevSiblings::new(self.clone())
1543 }
1544
1545 pub fn next_siblings(&self) -> iter::NextSiblings {
1547 let mut r = self.self_and_next_siblings();
1548 r.next();
1549 r
1550 }
1551
1552 pub fn self_and_next_siblings(&self) -> iter::NextSiblings {
1554 iter::NextSiblings::new(self.clone())
1555 }
1556
1557 pub fn prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1561 iter::TreeIter::prev_siblings_in(self.clone(), ancestor.clone())
1562 }
1563
1564 pub fn self_and_prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1568 iter::TreeIter::self_and_prev_siblings_in(self.clone(), ancestor.clone())
1569 }
1570
1571 pub fn next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1575 iter::TreeIter::next_siblings_in(self.clone(), ancestor.clone())
1576 }
1577
1578 pub fn self_and_next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1582 iter::TreeIter::self_and_next_siblings_in(self.clone(), ancestor.clone())
1583 }
1584
1585 pub fn orientation_from(&self, origin: PxPoint) -> Option<Orientation2D> {
1591 let o = self.center();
1592 [
1593 Orientation2D::Above,
1594 Orientation2D::Right,
1595 Orientation2D::Below,
1596 Orientation2D::Left,
1597 ]
1598 .iter()
1599 .find(|&&d| d.point_is(origin, o))
1600 .copied()
1601 }
1602
1603 pub fn distance_key(&self, origin: PxPoint) -> DistanceKey {
1605 DistanceKey::from_points(origin, self.center())
1606 }
1607
1608 pub fn rect_distance_key(&self, origin: PxPoint) -> DistanceKey {
1612 self.rect_distance_key_filtered(origin, |_, _, _| true)
1613 }
1614
1615 pub fn rect_distance_key_filtered(&self, origin: PxPoint, mut filter: impl FnMut(PxRect, usize, usize) -> bool) -> DistanceKey {
1622 let mut d = DistanceKey::NONE_MAX;
1623 self.info().bounds_info.visit_inner_rects::<()>(|r, i, len| {
1624 if !filter(r, i, len) {
1625 return ops::ControlFlow::Continue(());
1626 }
1627 let dd = DistanceKey::from_rect_to_point(r, origin);
1628 d = d.min(dd);
1629 if d == DistanceKey::MIN {
1630 ops::ControlFlow::Break(())
1631 } else {
1632 ops::ControlFlow::Continue(())
1633 }
1634 });
1635 d
1636 }
1637
1638 pub fn depth(&self) -> usize {
1640 self.ancestors().count()
1641 }
1642
1643 pub fn shared_ancestor(&self, other: &Self) -> Option<WidgetInfo> {
1647 if self.tree == other.tree {
1648 let a = self.path();
1649 let b = other.path();
1650 let shared = a.shared_ancestor(&b).unwrap();
1651 self.tree.get(shared.widget_id())
1652 } else {
1653 None
1654 }
1655 }
1656
1657 fn hit_test_z(&self, point: PxPoint) -> Option<ZIndex> {
1663 let bounds = &self.info().bounds_info;
1664 if bounds.inner_bounds().contains(point) {
1665 let z = match bounds.hit_test_z(point) {
1666 RelativeHitZ::NoHit => None,
1667 RelativeHitZ::Back => bounds.render_info().map(|i| (i.seg_id, i.back)),
1668 RelativeHitZ::Over(w) => self
1669 .tree
1670 .get(w)
1671 .and_then(|w| w.info().bounds_info.render_info())
1672 .map(|i| (i.seg_id, i.front)),
1673 RelativeHitZ::Front => bounds.render_info().map(|i| (i.seg_id, i.front)),
1674 };
1675
1676 match z {
1677 Some((seg_id, z)) => {
1678 let mut parent = self.parent();
1679 let mut child = self.clone();
1680
1681 while let Some(p) = parent {
1682 if p.info().bounds_info.hit_test_clip_child(&child, point) {
1683 return None;
1684 }
1685
1686 parent = p.parent();
1687 child = p;
1688 }
1689
1690 Some(ZIndex::from(
1691 (z + self.tree.0.frame.read().widget_count_offsets.offset(seg_id)) as u32,
1692 ))
1693 }
1694 None => None,
1695 }
1696 } else {
1697 None
1698 }
1699 }
1700
1701 pub fn is_in_bounds(&self) -> bool {
1703 self.info().bounds_info.is_in_bounds()
1704 }
1705
1706 pub fn out_of_bounds(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1708 let range = self.descendants_range();
1709 self.tree.out_of_bounds().filter(move |w| range.contains(w))
1710 }
1711
1712 pub fn spatial_iter<F>(&self, filter: F) -> impl Iterator<Item = WidgetInfo> + use<F>
1718 where
1719 F: Fn(&WidgetInfo) -> bool + Clone,
1720 {
1721 let self_id = self.id();
1722 self.self_and_descendants()
1723 .tree_filter(clmv!(filter, |w| {
1724 if (w.is_in_bounds() || w.id() == self_id) && filter(w) {
1725 TreeFilter::Include
1726 } else {
1727 TreeFilter::SkipAll
1728 }
1729 }))
1730 .chain(self.out_of_bounds().flat_map(clmv!(filter, |w| {
1731 let out_of_bound_root_id = w.id();
1732 w.self_and_descendants().tree_filter(clmv!(filter, |w| {
1733 if (w.is_in_bounds() || w.id() == out_of_bound_root_id) && filter(w) {
1734 TreeFilter::Include
1735 } else {
1736 TreeFilter::SkipAll
1737 }
1738 }))
1739 })))
1740 }
1741
1742 pub fn inner_contains(&self, point: PxPoint) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1744 self.spatial_iter(move |w| w.inner_bounds().contains(point))
1745 }
1746
1747 pub fn inner_intersects(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1749 let rect = rect.to_box2d();
1750 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&rect))
1751 }
1752
1753 pub fn inner_contains_rect(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1755 let rect = rect.to_box2d();
1756 self.spatial_iter(move |w| w.inner_bounds().to_box2d().contains_box(&rect))
1757 }
1758
1759 pub fn inner_contained(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1761 let rect = rect.to_box2d();
1762 self.spatial_iter(move |w| rect.contains_box(&w.inner_bounds().to_box2d()))
1763 }
1764
1765 pub fn center_contained(&self, area: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1767 let area = area.to_box2d();
1768 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1769 .filter(move |w| area.contains(w.center()))
1770 }
1771
1772 pub fn center_in_distance(&self, origin: PxPoint, max_radius: Px) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1774 let area = PxRect::new(origin, PxSize::splat(max_radius))
1775 .inflate(max_radius, max_radius)
1776 .to_box2d();
1777
1778 let distance_key = DistanceKey::from_distance(max_radius);
1779
1780 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1781 .filter(move |w| w.distance_key(origin) <= distance_key)
1782 }
1783
1784 pub fn hit_test(&self, point: PxPoint) -> HitTestInfo {
1786 let _span = tracing::trace_span!("hit_test").entered();
1787
1788 let mut hits: Vec<_> = self
1789 .inner_contains(point)
1790 .filter_map(|w| {
1791 w.hit_test_z(point).map(|z| HitInfo {
1792 widget_id: w.id(),
1793 z_index: z,
1794 })
1795 })
1796 .collect();
1797
1798 hits.sort_by(|a, b| b.z_index.cmp(&a.z_index));
1799
1800 HitTestInfo {
1801 window_id: self.tree.0.window_id,
1802 frame_id: self.tree.0.frame.read().stats.last_frame,
1803 point,
1804 hits,
1805 }
1806 }
1807
1808 pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetInfo> {
1814 self.nearest_filtered(origin, max_radius, |_| true)
1815 }
1816
1817 pub fn nearest_filtered(&self, origin: PxPoint, max_radius: Px, filter: impl FnMut(&WidgetInfo) -> bool) -> Option<WidgetInfo> {
1819 self.nearest_bounded_filtered(origin, max_radius, self.tree.spatial_bounds(), filter)
1820 }
1821
1822 pub fn nearest_bounded_filtered(
1825 &self,
1826 origin: PxPoint,
1827 max_radius: Px,
1828 bounds: PxRect,
1829 mut filter: impl FnMut(&WidgetInfo) -> bool,
1830 ) -> Option<WidgetInfo> {
1831 let max_quad = self.tree.spatial_bounds().intersection(&bounds)?;
1834
1835 let mut source_quad = PxRect::new(origin - PxVector::splat(Px(64)), PxSize::splat(Px(128)));
1836 let mut search_quad = source_quad.intersection(&max_quad)?;
1837
1838 let max_diameter = max_radius * Px(2);
1839
1840 let mut dist = if max_radius != Px::MAX {
1841 DistanceKey::from_distance(max_radius + Px(1))
1842 } else {
1843 DistanceKey::NONE_MAX
1844 };
1845
1846 let mut nearest = None;
1847 loop {
1848 for w in self.center_contained(search_quad) {
1849 let w_dist = w.distance_key(origin);
1850 if w_dist < dist && filter(&w) {
1851 dist = w_dist;
1852 nearest = Some(w);
1853 }
1854 }
1855
1856 let source_width = source_quad.width();
1857 if nearest.is_some() || source_width >= max_diameter {
1858 break;
1859 } else {
1860 source_quad = source_quad.inflate(source_width, source_width);
1861 let new_search = match source_quad.intersection(&max_quad) {
1862 Some(b) if b != search_quad => b,
1863 _ => break, };
1865 search_quad = new_search;
1866 }
1867 }
1868
1869 if nearest.is_some() {
1870 let distance = PxVector::splat(Px(2) * dist.distance().unwrap_or(Px(0)));
1872
1873 let quad = euclid::Box2D::new(origin - distance, origin + distance).intersection_unchecked(&max_quad.to_box2d());
1874
1875 for w in self.center_contained(quad.to_rect()) {
1876 let w_dist = w.distance_key(origin);
1877 if w_dist < dist && filter(&w) {
1878 dist = w_dist;
1879 nearest = Some(w);
1880 }
1881 }
1882 }
1883
1884 nearest
1885 }
1886
1887 pub fn nearest_rect(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetInfo> {
1892 self.nearest_rect_filtered(origin, max_radius, |_, _, _, _| true)
1893 }
1894
1895 pub fn nearest_rect_filtered(
1901 &self,
1902 origin: PxPoint,
1903 max_radius: Px,
1904 filter: impl FnMut(&WidgetInfo, PxRect, usize, usize) -> bool,
1905 ) -> Option<WidgetInfo> {
1906 self.nearest_rect_bounded_filtered(origin, max_radius, self.tree.spatial_bounds(), filter)
1907 }
1908
1909 pub fn nearest_rect_bounded_filtered(
1915 &self,
1916 origin: PxPoint,
1917 max_radius: Px,
1918 bounds: PxRect,
1919 mut filter: impl FnMut(&WidgetInfo, PxRect, usize, usize) -> bool,
1920 ) -> Option<WidgetInfo> {
1921 let max_quad = self.tree.spatial_bounds().intersection(&bounds)?;
1924
1925 let mut source_quad = PxRect::new(origin - PxVector::splat(Px(64)), PxSize::splat(Px(128)));
1926 let mut search_quad = source_quad.intersection(&max_quad)?;
1927
1928 let max_diameter = max_radius * Px(2);
1929
1930 let mut dist = if max_radius != Px::MAX {
1931 DistanceKey::from_distance(max_radius + Px(1))
1932 } else {
1933 DistanceKey::NONE_MAX
1934 };
1935
1936 let mut nearest = None;
1937 loop {
1938 for w in self.inner_intersects(search_quad) {
1939 let w_dist = w.rect_distance_key_filtered(origin, |rect, i, len| filter(&w, rect, i, len));
1940 if w_dist < dist {
1941 dist = w_dist;
1942 nearest = Some(w);
1943 } else if w_dist == DistanceKey::MIN {
1944 let w_center_dist = w.distance_key(origin);
1945 let center_dist = nearest.as_ref().unwrap().distance_key(origin);
1946 if w_center_dist < center_dist {
1947 nearest = Some(w);
1948 }
1949 }
1950 }
1951
1952 let source_width = source_quad.width();
1953 if nearest.is_some() || source_width >= max_diameter {
1954 break;
1955 } else {
1956 source_quad = source_quad.inflate(source_width, source_width);
1957 let new_search = match source_quad.intersection(&max_quad) {
1958 Some(b) if b != search_quad => b,
1959 _ => break, };
1961 search_quad = new_search;
1962 }
1963 }
1964
1965 nearest
1966 }
1967
1968 pub fn oriented(
1979 &self,
1980 origin: PxPoint,
1981 max_distance: Px,
1982 orientation: Orientation2D,
1983 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1984 let distance_bounded = max_distance != Px::MAX;
1985 let distance_key = if distance_bounded {
1986 DistanceKey::from_distance(max_distance)
1987 } else {
1988 DistanceKey::NONE_MAX
1989 };
1990 let me = self.clone();
1991 orientation
1992 .search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d())
1993 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
1994 .filter_map(move |(sq, w)| {
1995 let center = w.center();
1996 if sq.contains(center)
1997 && orientation.point_is(origin, center)
1998 && (!distance_bounded || DistanceKey::from_points(origin, center) <= distance_key)
1999 {
2000 Some(w)
2001 } else {
2002 None
2003 }
2004 })
2005 }
2006
2007 pub fn oriented_box(
2020 &self,
2021 origin: PxBox,
2022 max_distance: Px,
2023 orientation: Orientation2D,
2024 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
2025 let distance_bounded = max_distance != Px::MAX;
2026 let distance_key = if distance_bounded {
2027 DistanceKey::from_distance(max_distance)
2028 } else {
2029 DistanceKey::NONE_MAX
2030 };
2031 let me = self.clone();
2032 let origin_center = origin.center();
2033 orientation
2034 .search_bounds(origin_center, max_distance, self.tree.spatial_bounds().to_box2d())
2035 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
2036 .filter_map(move |(sq, w)| {
2037 let bounds = w.inner_bounds().to_box2d();
2038 if sq.intersects(&bounds)
2039 && orientation.box_is(origin, bounds)
2040 && (!distance_bounded || DistanceKey::from_points(origin_center, bounds.center()) <= distance_key)
2041 {
2042 Some(w)
2043 } else {
2044 None
2045 }
2046 })
2047 }
2048
2049 pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
2055 self.nearest_oriented_filtered(origin, max_distance, orientation, |_| true)
2056 }
2057
2058 pub fn nearest_oriented_filtered(
2065 &self,
2066 origin: PxPoint,
2067 max_distance: Px,
2068 orientation: Orientation2D,
2069 filter: impl FnMut(&WidgetInfo) -> bool,
2070 ) -> Option<WidgetInfo> {
2071 self.nearest_oriented_filtered_impl(origin, max_distance, orientation, filter, |w| {
2072 orientation.point_is(origin, w.center())
2073 })
2074 }
2075
2076 pub fn nearest_box_oriented(&self, origin: PxBox, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
2082 self.nearest_box_oriented_filtered(origin, max_distance, orientation, |_| true)
2083 }
2084
2085 pub fn nearest_box_oriented_filtered(
2092 &self,
2093 origin: PxBox,
2094 max_distance: Px,
2095 orientation: Orientation2D,
2096 filter: impl FnMut(&WidgetInfo) -> bool,
2097 ) -> Option<WidgetInfo> {
2098 self.nearest_oriented_filtered_impl(origin.center(), max_distance, orientation, filter, |w| {
2099 orientation.box_is(origin, w.inner_bounds().to_box2d())
2100 })
2101 }
2102
2103 fn nearest_oriented_filtered_impl(
2104 &self,
2105 origin: PxPoint,
2106 max_distance: Px,
2107 orientation: Orientation2D,
2108 mut filter: impl FnMut(&WidgetInfo) -> bool,
2109 intersect: impl Fn(&WidgetInfo) -> bool,
2110 ) -> Option<WidgetInfo> {
2111 let mut dist = DistanceKey::from_distance(max_distance + Px(1));
2112 let mut nearest = None;
2113 let mut last_quad = euclid::Box2D::zero();
2114
2115 for search_quad in orientation.search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d()) {
2116 for w in self.center_contained(search_quad.to_rect()) {
2117 if intersect(&w) {
2118 let w_dist = w.distance_key(origin);
2119 if w_dist < dist && filter(&w) {
2120 dist = w_dist;
2121 nearest = Some(w);
2122 }
2123 }
2124 }
2125
2126 if nearest.is_some() {
2127 last_quad = search_quad;
2128 break;
2129 }
2130 }
2131
2132 if nearest.is_some() {
2133 match orientation {
2136 Orientation2D::Above => {
2137 let extra = last_quad.height() / Px(2);
2138 last_quad.max.y = last_quad.min.y;
2139 last_quad.min.y -= extra;
2140 }
2141 Orientation2D::Right => {
2142 let extra = last_quad.width() / Px(2);
2143 last_quad.min.x = last_quad.max.x;
2144 last_quad.max.x += extra;
2145 }
2146 Orientation2D::Below => {
2147 let extra = last_quad.height() / Px(2);
2148 last_quad.min.y = last_quad.max.y;
2149 last_quad.max.y += extra;
2150 }
2151 Orientation2D::Left => {
2152 let extra = last_quad.width() / Px(2);
2153 last_quad.max.x = last_quad.min.x;
2154 last_quad.min.x -= extra;
2155 }
2156 }
2157
2158 for w in self.center_contained(last_quad.to_rect()) {
2159 let w_dist = w.distance_key(origin);
2160 if w_dist < dist && filter(&w) {
2161 dist = w_dist;
2162 nearest = Some(w);
2163 }
2164 }
2165 }
2166
2167 nearest
2168 }
2169
2170 pub fn fmt_debug(&self, fmt: &dyn Fn(&Self, &mut fmt::DebugStruct) -> bool) -> impl fmt::Debug {
2179 struct FmtDebug<'a> {
2180 wgt: WidgetInfo,
2181 fmt: &'a dyn Fn(&WidgetInfo, &mut fmt::DebugStruct) -> bool,
2182 }
2183 impl<'a> fmt::Debug for FmtDebug<'a> {
2184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2185 let mut s = f.debug_struct(&self.wgt.trace_id());
2186 let enter = (self.fmt)(&self.wgt, &mut s);
2187 if enter {
2188 s.field(
2189 "children",
2190 &FmtDebugChildren {
2191 wgt: self.wgt.clone(),
2192 fmt: self.fmt,
2193 },
2194 );
2195 s.finish()
2196 } else if self.wgt.descendants_len() > 0 {
2197 s.finish_non_exhaustive()
2198 } else {
2199 s.finish()
2200 }
2201 }
2202 }
2203 struct FmtDebugChildren<'a> {
2204 wgt: WidgetInfo,
2205 fmt: &'a dyn Fn(&WidgetInfo, &mut fmt::DebugStruct) -> bool,
2206 }
2207 impl<'a> fmt::Debug for FmtDebugChildren<'a> {
2208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2209 let mut c = f.debug_list();
2210 for child in self.wgt.children() {
2211 c.entry(&FmtDebug { wgt: child, fmt: self.fmt });
2212 }
2213 c.finish()
2214 }
2215 }
2216 FmtDebug { wgt: self.clone(), fmt }
2217 }
2218}
2219
2220#[derive(Debug)]
2224#[non_exhaustive]
2225pub struct InteractivityFilterArgs {
2226 pub info: WidgetInfo,
2228}
2229impl InteractivityFilterArgs {
2230 pub fn new(info: WidgetInfo) -> Self {
2232 Self { info }
2233 }
2234}
2235
2236type InteractivityFilters = Vec<Arc<dyn Fn(&InteractivityFilterArgs) -> Interactivity + Send + Sync>>;
2237
2238bitflags::bitflags! {
2239 #[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2241 #[serde(transparent)]
2242 pub struct Interactivity: u8 {
2243 const ENABLED = 0b00;
2247
2248 const DISABLED = 0b01;
2252
2253 const BLOCKED = 0b10;
2258
2259 const BLOCKED_DISABLED = Self::DISABLED.bits() | Self::BLOCKED.bits();
2261 }
2262}
2263impl Interactivity {
2264 pub fn is_enabled(self) -> bool {
2266 self == Self::ENABLED
2267 }
2268
2269 pub fn is_vis_enabled(self) -> bool {
2271 !self.contains(Self::DISABLED)
2272 }
2273
2274 pub fn is_disabled(self) -> bool {
2276 self == Self::DISABLED
2277 }
2278
2279 pub fn is_vis_disabled(self) -> bool {
2281 self.contains(Self::DISABLED)
2282 }
2283
2284 pub fn is_blocked(self) -> bool {
2286 self.contains(Self::BLOCKED)
2287 }
2288}
2289impl Default for Interactivity {
2290 fn default() -> Self {
2292 Interactivity::ENABLED
2293 }
2294}
2295impl_from_and_into_var! {
2296 fn from(enabled: bool) -> Interactivity {
2299 if enabled { Interactivity::ENABLED } else { Interactivity::DISABLED }
2300 }
2301}
2302impl fmt::Debug for Interactivity {
2303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2304 if self.is_enabled() {
2305 return write!(f, "ENABLED");
2306 }
2307 if *self == Self::BLOCKED_DISABLED {
2308 return write!(f, "BLOCKED_DISABLED");
2309 }
2310 if *self == Self::DISABLED {
2311 return write!(f, "DISABLED");
2312 }
2313 if *self == Self::BLOCKED {
2314 return write!(f, "BLOCKED");
2315 }
2316 write!(f, "Interactivity({:x})", self.bits())
2317 }
2318}
2319
2320#[derive(Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2330pub enum Visibility {
2331 Visible,
2335 Hidden,
2339 Collapsed,
2343}
2344impl Visibility {
2345 pub fn is_visible(self) -> bool {
2347 matches!(self, Self::Visible)
2348 }
2349
2350 pub fn is_hidden(self) -> bool {
2352 matches!(self, Self::Hidden)
2353 }
2354
2355 pub fn is_collapsed(self) -> bool {
2357 matches!(self, Self::Collapsed)
2358 }
2359}
2360impl fmt::Debug for Visibility {
2361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2362 if f.alternate() {
2363 write!(f, "Visibility::")?;
2364 }
2365 match self {
2366 Visibility::Visible => write!(f, "Visible"),
2367 Visibility::Hidden => write!(f, "Hidden"),
2368 Visibility::Collapsed => write!(f, "Collapsed"),
2369 }
2370 }
2371}
2372impl Default for Visibility {
2373 fn default() -> Self {
2375 Visibility::Visible
2376 }
2377}
2378impl ops::BitOr for Visibility {
2379 type Output = Self;
2380
2381 fn bitor(self, rhs: Self) -> Self::Output {
2383 use Visibility::*;
2384 match (self, rhs) {
2385 (Collapsed, _) | (_, Collapsed) => Collapsed,
2386 (Hidden, _) | (_, Hidden) => Hidden,
2387 _ => Visible,
2388 }
2389 }
2390}
2391impl ops::BitOrAssign for Visibility {
2392 fn bitor_assign(&mut self, rhs: Self) {
2393 *self = *self | rhs;
2394 }
2395}
2396impl_from_and_into_var! {
2397 fn from(visible: bool) -> Visibility {
2400 if visible { Visibility::Visible } else { Visibility::Collapsed }
2401 }
2402}
2403
2404#[derive(Clone, PartialEq, Eq, Default)]
2406pub struct WidgetDescendantsRange {
2407 tree: Option<WidgetInfoTree>,
2408 range: std::ops::Range<usize>,
2409}
2410impl WidgetDescendantsRange {
2411 pub fn contains(&self, wgt: &WidgetInfo) -> bool {
2413 self.range.contains(&wgt.node_id.get()) && self.tree.as_ref() == Some(&wgt.tree)
2414 }
2415}
2416
2417#[derive(Clone, Debug, PartialEq)]
2419#[non_exhaustive]
2420pub struct HitInfo {
2421 pub widget_id: WidgetId,
2423
2424 pub z_index: ZIndex,
2426}
2427
2428#[derive(Clone, Debug, PartialEq)]
2430pub struct HitTestInfo {
2431 window_id: WindowId,
2432 frame_id: FrameId,
2433 point: PxPoint,
2434 hits: Vec<HitInfo>,
2435}
2436impl HitTestInfo {
2437 pub fn no_hits(window_id: WindowId) -> Self {
2439 HitTestInfo {
2440 window_id,
2441 frame_id: FrameId::INVALID,
2442 point: PxPoint::new(Px(-1), Px(-1)),
2443 hits: vec![],
2444 }
2445 }
2446
2447 pub fn window_id(&self) -> WindowId {
2449 self.window_id
2450 }
2451
2452 pub fn frame_id(&self) -> FrameId {
2454 self.frame_id
2455 }
2456
2457 pub fn point(&self) -> PxPoint {
2459 self.point
2460 }
2461
2462 pub fn hits(&self) -> &[HitInfo] {
2464 &self.hits
2465 }
2466
2467 pub fn target(&self) -> Option<&HitInfo> {
2469 self.hits.first()
2470 }
2471
2472 pub fn find(&self, widget_id: WidgetId) -> Option<&HitInfo> {
2474 self.hits.iter().find(|h| h.widget_id == widget_id)
2475 }
2476
2477 pub fn contains(&self, widget_id: WidgetId) -> bool {
2479 self.hits.iter().any(|h| h.widget_id == widget_id)
2480 }
2481
2482 pub fn intersection(&self, other: &HitTestInfo) -> HitTestInfo {
2484 let mut hits: Vec<_> = self.hits.iter().filter(|h| other.contains(h.widget_id)).cloned().collect();
2485 hits.shrink_to_fit();
2486
2487 HitTestInfo {
2488 window_id: self.window_id,
2489 frame_id: self.frame_id,
2490 point: self.point,
2491 hits,
2492 }
2493 }
2494}