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::{IdEntry, 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)]
47pub struct WidgetInfoTreeStats {
48 pub generation: u32,
50
51 pub build_time: Duration,
55
56 pub reused_widgets: u32,
58
59 pub last_frame: FrameId,
63
64 pub bounds_updated_frame: FrameId,
66
67 pub bounds_updated: u32,
69
70 pub vis_updated_frame: FrameId,
72}
73impl WidgetInfoTreeStats {
74 fn new(build_start: DInstant, reused_widgets: u32, generation: u32) -> Self {
75 Self {
76 generation,
77 build_time: build_start.elapsed(),
78 reused_widgets,
79 last_frame: FrameId::INVALID,
80 bounds_updated_frame: FrameId::INVALID,
81 bounds_updated: 0,
82 vis_updated_frame: FrameId::INVALID,
83 }
84 }
85
86 fn update(&mut self, frame: FrameId, update: WidgetInfoTreeStatsUpdate) {
87 self.last_frame = frame;
88
89 if update.bounds_updated > 0 {
90 self.bounds_updated = update.bounds_updated;
91 self.bounds_updated_frame = frame;
92 } else if self.bounds_updated_frame == FrameId::INVALID {
93 self.bounds_updated_frame = frame;
94 }
95
96 if update.vis_updated > 0 || self.vis_updated_frame == FrameId::INVALID {
98 self.vis_updated_frame = frame;
99 }
100 }
101}
102#[derive(Default)]
103struct WidgetInfoTreeStatsUpdate {
104 bounds_updated: u32,
105 vis_updated: u32,
106}
107impl WidgetInfoTreeStatsUpdate {
108 fn take(&mut self) -> Self {
109 mem::take(self)
110 }
111}
112
113#[derive(Clone)]
119pub struct WidgetInfoTree(Arc<WidgetInfoTreeInner>);
120struct WidgetInfoTreeInner {
121 window_id: WindowId,
122 access_enabled: AccessEnabled,
123 tree: Tree<WidgetInfoData>,
124 lookup: IdMap<WidgetId, tree::NodeId>,
125 interactivity_filters: InteractivityFilters,
126 build_meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
127 frame: RwLock<WidgetInfoTreeFrame>,
128}
129struct WidgetInfoTreeFrame {
131 stats: WidgetInfoTreeStats,
132 stats_update: WidgetInfoTreeStatsUpdate,
133 out_of_bounds_update: Vec<(tree::NodeId, bool)>,
134 scale_factor: Factor,
135 view_process_gen: ViewProcessGen,
136
137 out_of_bounds: Arc<Vec<tree::NodeId>>,
138 spatial_bounds: PxBox,
139
140 widget_count_offsets: ParallelSegmentOffsets,
141
142 transform_changed_subs: IdMap<WidgetId, PxTransform>,
143 visibility_changed_subs: IdMap<WidgetId, Visibility>,
144}
145impl PartialEq for WidgetInfoTree {
146 fn eq(&self, other: &Self) -> bool {
147 Arc::ptr_eq(&self.0, &other.0)
148 }
149}
150impl Eq for WidgetInfoTree {}
151impl WidgetInfoTree {
152 pub fn wgt(window_id: WindowId, root_id: WidgetId) -> Self {
154 WidgetInfoBuilder::new(
155 Arc::default(),
156 window_id,
157 AccessEnabled::empty(),
158 root_id,
159 WidgetBoundsInfo::new(),
160 WidgetBorderInfo::new(),
161 1.fct(),
162 )
163 .finalize(None, false)
164 }
165
166 pub fn stats(&self) -> WidgetInfoTreeStats {
168 self.0.frame.read().stats.clone()
169 }
170
171 pub fn scale_factor(&self) -> Factor {
173 self.0.frame.read().scale_factor
174 }
175
176 pub fn view_process_gen(&self) -> ViewProcessGen {
182 self.0.frame.read().view_process_gen
183 }
184
185 pub fn build_meta(&self) -> StateMapRef<WidgetInfoMeta> {
189 self.0.build_meta.borrow()
190 }
191
192 pub fn root(&self) -> WidgetInfo {
194 WidgetInfo::new(self.clone(), self.0.tree.root().id())
195 }
196
197 pub fn all_widgets(&self) -> iter::TreeIter {
199 self.root().self_and_descendants()
200 }
201
202 pub fn window_id(&self) -> WindowId {
204 self.0.window_id
205 }
206
207 pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
209 self.0.lookup.get(&widget_id.into()).map(|i| WidgetInfo::new(self.clone(), *i))
210 }
211
212 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
214 self.0.lookup.contains_key(&widget_id.into())
215 }
216
217 pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetInfo> {
219 self.get(path.widget_id())
220 .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
221 }
222
223 pub fn is_rendered(&self) -> bool {
226 self.0.frame.read().stats.last_frame != FrameId::INVALID
227 }
228
229 pub fn out_of_bounds(&self) -> impl std::iter::ExactSizeIterator<Item = WidgetInfo> + 'static + use<> {
231 let out = self.0.frame.read().out_of_bounds.clone();
232 let me = self.clone();
233 (0..out.len()).map(move |i| WidgetInfo::new(me.clone(), out[i]))
234 }
235
236 pub fn spatial_bounds(&self) -> PxRect {
238 self.0.frame.read().spatial_bounds.to_rect()
239 }
240
241 #[expect(clippy::len_without_is_empty)]
245 pub fn len(&self) -> usize {
246 self.0.lookup.len()
247 }
248
249 fn bounds_changed(&self) {
250 self.0.frame.write().stats_update.bounds_updated += 1;
251 }
252
253 fn in_bounds_changed(&self, widget_id: WidgetId, in_bounds: bool) {
254 let id = *self.0.lookup.get(&widget_id).unwrap();
255 self.0.frame.write().out_of_bounds_update.push((id, in_bounds));
256 }
257
258 fn visibility_changed(&self) {
259 self.0.frame.write().stats_update.vis_updated += 1;
260 }
261
262 pub(crate) fn after_render(
263 &self,
264 frame_id: FrameId,
265 scale_factor: Factor,
266 view_process_gen: Option<ViewProcessGen>,
267 widget_count_offsets: Option<ParallelSegmentOffsets>,
268 ) {
269 let mut frame = self.0.frame.write();
270 let stats_update = frame.stats_update.take();
271 frame.stats.update(frame_id, stats_update);
272
273 if !frame.out_of_bounds_update.is_empty() {
274 let mut out_of_bounds = Arc::try_unwrap(mem::take(&mut frame.out_of_bounds)).unwrap_or_else(|rc| (*rc).clone());
278
279 for (id, remove) in frame.out_of_bounds_update.drain(..) {
280 if remove {
281 if let Some(i) = out_of_bounds.iter().position(|i| *i == id) {
282 out_of_bounds.swap_remove(i);
283 }
284 } else {
285 out_of_bounds.push(id);
286 }
287 }
288 frame.out_of_bounds = Arc::new(out_of_bounds);
289 }
290
291 let mut spatial_bounds = self.root().outer_bounds().to_box2d();
292 for out in frame.out_of_bounds.iter() {
293 let b = WidgetInfo::new(self.clone(), *out).inner_bounds().to_box2d();
294 spatial_bounds = spatial_bounds.union(&b);
295 }
296 frame.spatial_bounds = spatial_bounds;
297
298 frame.scale_factor = scale_factor;
299 if let Some(vp_gen) = view_process_gen {
300 frame.view_process_gen = vp_gen;
301 }
302 if let Some(w) = widget_count_offsets {
303 frame.widget_count_offsets = w;
304 }
305
306 let mut changes = IdMap::new();
307 TRANSFORM_CHANGED_EVENT.visit_subscribers::<()>(|wid| {
308 if let Some(wgt) = self.get(wid) {
309 let transform = wgt.inner_transform();
310 match frame.transform_changed_subs.entry(wid) {
311 IdEntry::Occupied(mut e) => {
312 let prev = e.insert(transform);
313 if prev != transform {
314 changes.insert(wid, prev);
315 }
316 }
317 IdEntry::Vacant(e) => {
318 e.insert(transform);
319 }
320 }
321 }
322 ops::ControlFlow::Continue(())
323 });
324 if !changes.is_empty() {
325 if (frame.transform_changed_subs.len() - changes.len()) > 500 {
326 frame
327 .transform_changed_subs
328 .retain(|k, _| TRANSFORM_CHANGED_EVENT.is_subscriber(*k));
329 }
330
331 TRANSFORM_CHANGED_EVENT.notify(TransformChangedArgs::now(self.clone(), changes));
332 }
333 drop(frame); let mut changes = IdMap::new();
336 VISIBILITY_CHANGED_EVENT.visit_subscribers::<()>(|wid| {
337 if let Some(wgt) = self.get(wid) {
338 let visibility = wgt.visibility();
339 let mut frame = self.0.frame.write();
340 match frame.visibility_changed_subs.entry(wid) {
341 IdEntry::Occupied(mut e) => {
342 let prev = e.insert(visibility);
343 if prev != visibility {
344 changes.insert(wid, prev);
345 }
346 }
347 IdEntry::Vacant(e) => {
348 e.insert(visibility);
349 }
350 }
351 }
352 ops::ControlFlow::Continue(())
353 });
354 if !changes.is_empty() {
355 if (self.0.frame.read().visibility_changed_subs.len() - changes.len()) > 500 {
356 self.0
357 .frame
358 .write()
359 .visibility_changed_subs
360 .retain(|k, _| VISIBILITY_CHANGED_EVENT.is_subscriber(*k));
361 }
362
363 VISIBILITY_CHANGED_EVENT.notify(VisibilityChangedArgs::now(self.clone(), changes));
364 }
365 }
366
367 pub(crate) fn after_render_update(&self, frame_id: FrameId) {
368 let scale_factor = self.0.frame.read().scale_factor;
369 self.after_render(frame_id, scale_factor, None, None);
370 }
371}
372impl fmt::Debug for WidgetInfoTree {
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 let nl = if f.alternate() { "\n " } else { " " };
375
376 write!(
377 f,
378 "WidgetInfoTree(Rc<{{{nl}window_id: {},{nl}widget_count: {},{nl}...}}>)",
379 self.0.window_id,
380 self.0.lookup.len(),
381 nl = nl
382 )
383 }
384}
385
386#[derive(Debug, Default)]
387struct WidgetBoundsData {
388 inner_offset: PxVector,
389 child_offset: PxVector,
390 parent_child_offset: PxVector,
391
392 inline: Option<WidgetInlineInfo>,
393 measure_inline: Option<WidgetInlineMeasure>,
394
395 measure_outer_size: PxSize,
396 outer_size: PxSize,
397 inner_size: PxSize,
398 baseline: Px,
399 inner_offset_baseline: bool,
400
401 transform_style: TransformStyle,
402 perspective: f32,
403 perspective_origin: Option<PxPoint>,
404
405 measure_metrics: Option<LayoutMetricsSnapshot>,
406 measure_metrics_used: LayoutMask,
407 metrics: Option<LayoutMetricsSnapshot>,
408 metrics_used: LayoutMask,
409
410 outer_transform: PxTransform,
411 inner_transform: PxTransform,
412 rendered: Option<WidgetRenderInfo>,
413
414 outer_bounds: PxRect,
415 inner_bounds: PxRect,
416
417 hit_clips: HitTestClips,
418 hit_index: hit::HitChildIndex,
419
420 is_in_bounds: Option<bool>,
421 is_partially_culled: bool,
422 cannot_auto_hide: bool,
423 is_collapsed: bool,
424}
425
426#[derive(Debug, Clone, Copy)]
428pub(crate) struct WidgetRenderInfo {
429 pub visible: bool,
431
432 pub parent_perspective: Option<(f32, PxPoint)>,
433
434 pub seg_id: ParallelSegmentId,
436 pub back: usize,
437 pub front: usize,
438}
439
440#[derive(Default, Clone, Debug)]
446pub struct WidgetBoundsInfo(Arc<Mutex<WidgetBoundsData>>);
447impl PartialEq for WidgetBoundsInfo {
448 fn eq(&self, other: &Self) -> bool {
449 Arc::ptr_eq(&self.0, &other.0)
450 }
451}
452impl Eq for WidgetBoundsInfo {}
453impl WidgetBoundsInfo {
454 pub fn new() -> Self {
456 Self::default()
457 }
458
459 pub fn new_size(outer: PxSize, inner: PxSize) -> Self {
461 let me = Self::new();
462 me.set_outer_size(outer);
463 me.set_inner_size(inner);
464 me
465 }
466
467 pub fn measure_outer_size(&self) -> PxSize {
471 self.0.lock().measure_outer_size
472 }
473
474 pub fn outer_size(&self) -> PxSize {
476 self.0.lock().outer_size
477 }
478
479 pub fn inner_offset(&self) -> PxVector {
486 let mut r = self.0.lock().inner_offset;
487 if self.inner_offset_baseline() {
488 r.y += self.baseline();
489 }
490 r
491 }
492
493 pub fn inner_offset_baseline(&self) -> bool {
498 self.0.lock().inner_offset_baseline
499 }
500
501 pub fn child_offset(&self) -> PxVector {
507 self.0.lock().child_offset
508 }
509
510 pub fn inner_size(&self) -> PxSize {
512 self.0.lock().inner_size
513 }
514
515 pub fn baseline(&self) -> Px {
524 self.0.lock().baseline
525 }
526
527 pub fn final_baseline(&self) -> Px {
535 let s = self.0.lock();
536 if s.inner_offset_baseline { Px(0) } else { s.baseline }
537 }
538
539 pub fn outer_transform(&self) -> PxTransform {
541 self.0.lock().outer_transform
542 }
543
544 pub fn parent_child_offset(&self) -> PxVector {
548 self.0.lock().parent_child_offset
549 }
550
551 pub fn inner_transform(&self) -> PxTransform {
553 self.0.lock().inner_transform
554 }
555
556 pub fn measure_inline(&self) -> Option<WidgetInlineMeasure> {
565 self.0.lock().measure_inline.clone()
566 }
567
568 pub fn inline(&self) -> Option<MappedMutexGuard<WidgetInlineInfo>> {
572 let me = self.0.lock();
573 if me.inline.is_some() {
574 Some(MutexGuard::map(me, |m| m.inline.as_mut().unwrap()))
575 } else {
576 None
577 }
578 }
579
580 pub fn rendered(&self) -> Option<bool> {
582 self.0.lock().rendered.map(|i| i.visible)
583 }
584
585 pub(crate) fn render_info(&self) -> Option<WidgetRenderInfo> {
586 self.0.lock().rendered
587 }
588
589 pub fn is_in_bounds(&self) -> bool {
593 self.0.lock().is_in_bounds.unwrap_or(false)
594 }
595
596 pub fn can_auto_hide(&self) -> bool {
605 !self.0.lock().cannot_auto_hide
606 }
607
608 fn set_can_auto_hide(&self, enabled: bool) {
609 self.0.lock().cannot_auto_hide = !enabled;
610 }
611
612 pub(crate) fn is_actually_out_of_bounds(&self) -> bool {
613 self.0.lock().is_in_bounds.map(|is| !is).unwrap_or(false)
614 }
615
616 pub(crate) fn set_rendered(&self, rendered: Option<WidgetRenderInfo>, info: &WidgetInfoTree) {
617 let mut m = self.0.lock();
618 if m.rendered.map(|i| i.visible) != rendered.map(|i| i.visible) {
619 info.visibility_changed();
620 }
621 m.rendered = rendered;
622 }
623
624 pub(crate) fn set_outer_transform(&self, transform: PxTransform, info: &WidgetInfoTree) {
625 let bounds = transform
626 .outer_transformed(PxBox::from_size(self.outer_size()))
627 .unwrap_or_default()
628 .to_rect();
629
630 let mut m = self.0.lock();
631
632 if m.outer_bounds.size.is_empty() != bounds.size.is_empty() {
633 info.visibility_changed();
634 }
635
636 m.outer_bounds = bounds;
637 m.outer_transform = transform;
638 }
639
640 pub(crate) fn set_parent_child_offset(&self, offset: PxVector) {
641 self.0.lock().parent_child_offset = offset;
642 }
643
644 pub(crate) fn set_inner_transform(
645 &self,
646 transform: PxTransform,
647 info: &WidgetInfoTree,
648 widget_id: WidgetId,
649 parent_inner: Option<PxRect>,
650 ) {
651 let bounds = transform
652 .outer_transformed(PxBox::from_size(self.inner_size()))
653 .unwrap_or_default()
654 .to_rect();
655
656 let mut m = self.0.lock();
657
658 if m.inner_bounds != bounds {
659 m.inner_bounds = bounds;
660 info.bounds_changed();
661 }
662 let in_bounds = parent_inner.map(|r| r.contains_rect(&bounds)).unwrap_or(true);
663 if let Some(prev) = m.is_in_bounds {
664 if prev != in_bounds {
665 m.is_in_bounds = Some(in_bounds);
666 info.in_bounds_changed(widget_id, in_bounds);
667 }
668 } else {
669 m.is_in_bounds = Some(in_bounds);
670 if !in_bounds {
671 info.in_bounds_changed(widget_id, in_bounds);
672 }
673 }
674
675 m.inner_transform = transform;
676 }
677
678 pub fn outer_bounds(&self) -> PxRect {
680 self.0.lock().outer_bounds
681 }
682
683 pub fn inner_bounds(&self) -> PxRect {
685 self.0.lock().inner_bounds
686 }
687
688 pub fn is_collapsed(&self) -> bool {
690 self.0.lock().is_collapsed
691 }
692
693 pub fn transform_style(&self) -> TransformStyle {
695 self.0.lock().transform_style
696 }
697
698 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
700 let p = self.0.lock();
701 if p.perspective.is_finite() {
702 let s = p.inner_size;
703 let o = p.perspective_origin.unwrap_or_else(|| PxPoint::new(s.width / 2.0, s.height / 2.0));
704 Some((p.perspective, o))
705 } else {
706 None
707 }
708 }
709
710 pub fn metrics(&self) -> Option<LayoutMetricsSnapshot> {
719 self.0.lock().metrics.clone()
720 }
721
722 pub fn metrics_used(&self) -> LayoutMask {
726 self.0.lock().metrics_used
727 }
728
729 pub fn hit_test_z(&self, window_point: PxPoint) -> RelativeHitZ {
731 let m = self.0.lock();
732 if m.hit_clips.is_hit_testable() {
733 m.hit_clips.hit_test_z(&m.inner_transform, window_point)
734 } else {
735 RelativeHitZ::NoHit
736 }
737 }
738
739 fn hit_test_index(&self) -> hit::HitChildIndex {
741 self.0.lock().hit_index
742 }
743
744 pub fn hit_test_clip_child(&self, child: &WidgetInfo, window_point: PxPoint) -> bool {
746 let m = self.0.lock();
747 if m.hit_clips.is_hit_testable() {
748 m.hit_clips
749 .clip_child(child.bounds_info().hit_test_index(), &m.inner_transform, window_point)
750 } else {
751 false
752 }
753 }
754
755 pub(crate) fn update_hit_test_transform(&self, value: FrameValueUpdate<PxTransform>) {
756 self.0.lock().hit_clips.update_transform(value);
757 }
758
759 pub(crate) fn measure_metrics(&self) -> Option<LayoutMetricsSnapshot> {
760 self.0.lock().measure_metrics.clone()
761 }
762 pub(crate) fn measure_metrics_used(&self) -> LayoutMask {
763 self.0.lock().measure_metrics_used
764 }
765
766 fn set_outer_size(&self, size: PxSize) {
767 let mut s = self.0.lock();
768 if !size.is_empty() {
769 s.is_collapsed = false;
770 }
771 s.outer_size = size;
772 }
773
774 fn set_is_collapsed(&self, collapsed: bool) {
775 self.0.lock().is_collapsed = collapsed;
776 }
777
778 fn take_inline(&self) -> Option<WidgetInlineInfo> {
779 self.0.lock().inline.take()
780 }
781
782 fn set_inline(&self, inline: Option<WidgetInlineInfo>) {
783 self.0.lock().inline = inline;
784 }
785
786 pub(super) fn set_measure_inline(&self, inline: Option<WidgetInlineMeasure>) {
787 self.0.lock().measure_inline = inline;
788 }
789
790 pub(crate) fn set_measure_outer_size(&self, size: PxSize) {
791 self.0.lock().measure_outer_size = size;
792 }
793
794 fn set_inner_offset(&self, offset: PxVector) {
795 self.0.lock().inner_offset = offset;
796 }
797
798 fn set_child_offset(&self, offset: PxVector) {
799 self.0.lock().child_offset = offset;
800 }
801
802 fn set_inner_size(&self, size: PxSize) {
803 self.0.lock().inner_size = size;
804 }
805
806 fn set_baseline(&self, baseline: Px) {
807 self.0.lock().baseline = baseline;
808 }
809
810 fn set_inner_offset_baseline(&self, enabled: bool) {
811 self.0.lock().inner_offset_baseline = enabled;
812 }
813
814 fn set_transform_style(&self, style: TransformStyle) {
815 self.0.lock().transform_style = style;
816 }
817
818 fn raw_perspective(&self) -> f32 {
819 self.0.lock().perspective
820 }
821
822 fn raw_perspective_origin(&self) -> Option<PxPoint> {
823 self.0.lock().perspective_origin
824 }
825
826 fn set_perspective(&self, d: f32) {
827 self.0.lock().perspective = d;
828 }
829
830 fn set_perspective_origin(&self, o: Option<PxPoint>) {
831 self.0.lock().perspective_origin = o;
832 }
833
834 fn set_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
835 self.0.lock().metrics = metrics;
836 self.0.lock().metrics_used = used;
837 }
838
839 pub(crate) fn set_measure_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
840 self.0.lock().measure_metrics = metrics;
841 self.0.lock().measure_metrics_used = used;
842 }
843
844 pub(crate) fn set_hit_clips(&self, clips: HitTestClips) {
845 self.0.lock().hit_clips = clips;
846 }
847
848 pub(crate) fn set_hit_index(&self, index: hit::HitChildIndex) {
849 self.0.lock().hit_index = index;
850 }
851
852 pub(crate) fn is_partially_culled(&self) -> bool {
853 self.0.lock().is_partially_culled
854 }
855
856 pub(crate) fn set_is_partially_culled(&self, is: bool) {
857 self.0.lock().is_partially_culled = is;
858 }
859}
860
861#[derive(Default, Debug)]
862struct WidgetBorderData {
863 offsets: PxSideOffsets,
864 corner_radius: PxCornerRadius,
865}
866
867#[derive(Default, Clone, Debug)]
869pub struct WidgetBorderInfo(Arc<Mutex<WidgetBorderData>>);
870impl WidgetBorderInfo {
871 pub fn new() -> Self {
873 Self::default()
874 }
875
876 #[cfg(test)]
878 pub fn new_test(offsets: PxSideOffsets, corner_radius: PxCornerRadius) -> Self {
879 let r = Self::default();
880 r.set_offsets(offsets);
881 r.set_corner_radius(corner_radius);
882 r
883 }
884
885 pub fn offsets(&self) -> PxSideOffsets {
887 self.0.lock().offsets
888 }
889
890 pub fn corner_radius(&self) -> PxCornerRadius {
892 self.0.lock().corner_radius
893 }
894
895 pub fn inner_corner_radius(&self) -> PxCornerRadius {
900 self.corner_radius().deflate(self.offsets())
901 }
902
903 pub fn inner_offset(&self, bounds: &WidgetBoundsInfo) -> PxVector {
907 let o = self.offsets();
908 let o = PxVector::new(o.left, o.top);
909 bounds.inner_offset() + o
910 }
911
912 pub fn inner_size(&self, bounds: &WidgetBoundsInfo) -> PxSize {
916 let o = self.offsets();
917 bounds.inner_size() - PxSize::new(o.horizontal(), o.vertical())
918 }
919
920 pub fn inner_transform(&self, bounds: &WidgetBoundsInfo) -> PxTransform {
924 let o = self.offsets();
925 let o = PxVector::new(o.left, o.top);
926 bounds.inner_transform().pre_translate(o.cast())
927 }
928
929 pub(super) fn set_offsets(&self, widths: PxSideOffsets) {
930 self.0.lock().offsets = widths;
931 }
932
933 pub(super) fn set_corner_radius(&self, radius: PxCornerRadius) {
934 self.0.lock().corner_radius = radius;
935 }
936}
937
938struct WidgetInfoData {
939 id: WidgetId,
940 bounds_info: WidgetBoundsInfo,
941 border_info: WidgetBorderInfo,
942 meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
943 interactivity_filters: InteractivityFilters,
944 local_interactivity: Interactivity,
945 is_reused: bool,
946 cache: Mutex<WidgetInfoCache>,
947}
948impl Clone for WidgetInfoData {
949 fn clone(&self) -> Self {
950 Self {
951 id: self.id,
952 bounds_info: self.bounds_info.clone(),
953 border_info: self.border_info.clone(),
954 meta: self.meta.clone(),
955 interactivity_filters: self.interactivity_filters.clone(),
956 local_interactivity: self.local_interactivity,
957 is_reused: self.is_reused,
958 cache: Mutex::new(match self.cache.try_lock() {
959 Some(c) => c.clone(),
960 None => WidgetInfoCache { interactivity: None },
961 }),
962 }
963 }
964}
965impl fmt::Debug for WidgetInfoData {
966 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
967 f.debug_struct("WidgetInfoData").field("id", &self.id).finish_non_exhaustive()
968 }
969}
970#[derive(Clone)]
971struct WidgetInfoCache {
972 interactivity: Option<Interactivity>,
973}
974
975#[derive(Clone)]
977pub struct WidgetInfo {
978 tree: WidgetInfoTree,
979 node_id: tree::NodeId,
980}
981impl PartialEq for WidgetInfo {
982 fn eq(&self, other: &Self) -> bool {
983 self.node_id == other.node_id && self.tree == other.tree
984 }
985}
986impl Eq for WidgetInfo {}
987impl std::hash::Hash for WidgetInfo {
988 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
989 std::hash::Hash::hash(&self.node_id, state)
990 }
991}
992impl std::fmt::Debug for WidgetInfo {
993 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994 f.debug_struct("WidgetInfo")
995 .field("[path]", &self.path().to_string())
996 .field("[meta]", &self.meta())
997 .finish_non_exhaustive()
998 }
999}
1000
1001impl WidgetInfo {
1002 fn new(tree: WidgetInfoTree, node_id: tree::NodeId) -> Self {
1003 Self { tree, node_id }
1004 }
1005
1006 fn node(&self) -> tree::NodeRef<WidgetInfoData> {
1007 self.tree.0.tree.index(self.node_id)
1008 }
1009
1010 fn info(&self) -> &WidgetInfoData {
1011 self.node().value()
1012 }
1013
1014 pub fn id(&self) -> WidgetId {
1016 self.info().id
1017 }
1018
1019 pub fn path(&self) -> WidgetPath {
1021 let mut path: Vec<_> = self.ancestors().map(|a| a.id()).collect();
1022 path.reverse();
1023 path.push(self.id());
1024 path.shrink_to_fit();
1025
1026 WidgetPath::new(self.tree.0.window_id, path.into())
1027 }
1028
1029 pub fn trace_path(&self) -> Txt {
1033 let mut ws: Vec<_> = self.self_and_ancestors().collect();
1034 ws.reverse();
1035
1036 use std::fmt::*;
1037
1038 let mut s = String::new();
1039
1040 let _ = write!(&mut s, "{:?}/", self.tree.window_id());
1041 for w in ws {
1042 #[cfg(feature = "inspector")]
1043 {
1044 use crate::widget::inspector::*;
1045 if let Some(info) = w.inspector_info() {
1046 let mod_path = info.builder.widget_type().path;
1047 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1048 ident
1049 } else {
1050 mod_path
1051 };
1052
1053 let id = w.id();
1054 let name = id.name();
1055 if !name.is_empty() {
1056 let _ = write!(&mut s, "/{mod_ident}!({name:?})");
1057 } else {
1058 let _ = write!(&mut s, "/{mod_ident}!({})", id.sequential());
1059 }
1060 } else {
1061 let _ = write!(&mut s, "/{}", w.id());
1062 }
1063 }
1064
1065 #[cfg(not(feature = "inspector"))]
1066 {
1067 let _ = write!(&mut s, "/{}", w.id());
1068 }
1069 }
1070
1071 s.into()
1072 }
1073
1074 pub fn trace_id(&self) -> Txt {
1078 #[cfg(feature = "inspector")]
1079 {
1080 use crate::widget::inspector::*;
1081 if let Some(info) = self.inspector_info() {
1082 let mod_path = info.builder.widget_type().path;
1083 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1084 ident
1085 } else {
1086 mod_path
1087 };
1088
1089 let id = self.id();
1090 let name = id.name();
1091 if !name.is_empty() {
1092 return formatx!("{mod_ident}!({name:?})");
1093 } else {
1094 return formatx!("{mod_ident}!({})", id.sequential());
1095 }
1096 }
1097 }
1098 formatx!("{}", self.id())
1099 }
1100
1101 pub fn interaction_path(&self) -> InteractionPath {
1105 let mut path = vec![];
1106
1107 let mut blocked = None;
1108 let mut disabled = None;
1109
1110 for w in self.self_and_ancestors() {
1111 let interactivity = w.interactivity();
1112 if interactivity.contains(Interactivity::BLOCKED) {
1113 blocked = Some(path.len());
1114 }
1115 if interactivity.contains(Interactivity::DISABLED) {
1116 disabled = Some(path.len());
1117 }
1118
1119 path.push(w.id());
1120 }
1121 path.reverse();
1122 path.shrink_to_fit();
1123
1124 let len = path.len();
1125
1126 let path = WidgetPath::new(self.tree.0.window_id, path.into());
1127 InteractionPath::new_internal(
1128 path,
1129 blocked.map(|i| len - i - 1).unwrap_or(len),
1130 disabled.map(|i| len - i - 1).unwrap_or(len),
1131 )
1132 }
1133
1134 pub fn new_path(&self, old_path: &WidgetPath) -> Option<WidgetPath> {
1144 assert_eq!(old_path.widget_id(), self.id());
1145 if self
1146 .ancestors()
1147 .zip(old_path.ancestors().iter().rev())
1148 .any(|(ancestor, id)| ancestor.id() != *id)
1149 {
1150 Some(self.path())
1151 } else {
1152 None
1153 }
1154 }
1155
1156 pub fn new_interaction_path(&self, old_path: &InteractionPath) -> Option<InteractionPath> {
1166 assert_eq!(old_path.widget_id(), self.id());
1167
1168 if self.interactivity() != old_path.interactivity()
1169 || self
1170 .ancestors()
1171 .zip(old_path.zip().rev().skip(1))
1172 .any(|(anc, (id, int))| anc.id() != id || anc.interactivity() != int)
1173 {
1174 Some(self.interaction_path())
1175 } else {
1176 None
1177 }
1178 }
1179
1180 pub fn z_index(&self) -> Option<(ZIndex, ZIndex)> {
1189 self.info().bounds_info.render_info().map(|i| {
1190 let offset = self.tree.0.frame.read().widget_count_offsets.offset(i.seg_id);
1191 (ZIndex((i.back + offset) as u32), ZIndex((i.front + offset) as u32))
1192 })
1193 }
1194
1195 pub fn visibility(&self) -> Visibility {
1204 match self.info().bounds_info.rendered() {
1205 Some(vis) => {
1206 if vis {
1207 Visibility::Visible
1208 } else {
1209 Visibility::Hidden
1210 }
1211 }
1212 None => {
1213 if self.tree.is_rendered() {
1214 Visibility::Collapsed
1215 } else {
1216 Visibility::Visible
1217 }
1218 }
1219 }
1220 }
1221
1222 pub fn interactivity(&self) -> Interactivity {
1227 let cached = self.info().cache.lock().interactivity;
1228 if let Some(cache) = cached {
1229 cache
1230 } else {
1231 let mut cache = self.info().cache.lock();
1232 let mut interactivity = self.info().local_interactivity;
1233
1234 if interactivity != Interactivity::BLOCKED_DISABLED {
1235 interactivity |= self.parent().map(|n| n.interactivity()).unwrap_or(Interactivity::ENABLED);
1236 if interactivity != Interactivity::BLOCKED_DISABLED {
1237 let args = InteractivityFilterArgs { info: self.clone() };
1238 for filter in &self.tree.0.interactivity_filters {
1239 interactivity |= filter(&args);
1240 if interactivity == Interactivity::BLOCKED_DISABLED {
1241 break;
1242 }
1243 }
1244 }
1245 }
1246
1247 cache.interactivity = Some(interactivity);
1248 interactivity
1249 }
1250 }
1251
1252 pub fn bounds_info(&self) -> WidgetBoundsInfo {
1256 self.info().bounds_info.clone()
1257 }
1258
1259 pub fn border_info(&self) -> WidgetBorderInfo {
1263 self.info().border_info.clone()
1264 }
1265
1266 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
1270 self.parent()?.bounds_info().perspective()
1271 }
1272
1273 pub fn transform_style(&self) -> TransformStyle {
1277 if let TransformStyle::Flat = self.bounds_info().transform_style() {
1278 if let Some(p) = self.parent() {
1279 p.bounds_info().transform_style()
1280 } else {
1281 TransformStyle::Flat
1282 }
1283 } else {
1284 TransformStyle::Preserve3D
1285 }
1286 }
1287
1288 pub fn outer_size(&self) -> PxSize {
1292 self.info().bounds_info.outer_size()
1293 }
1294
1295 pub fn inner_size(&self) -> PxSize {
1299 self.info().bounds_info.inner_size()
1300 }
1301
1302 pub fn inner_border_size(&self) -> PxSize {
1306 let info = self.info();
1307 info.border_info.inner_size(&info.bounds_info)
1308 }
1309
1310 pub fn baseline(&self) -> Px {
1312 self.info().bounds_info.baseline()
1313 }
1314
1315 pub fn outer_transform(&self) -> PxTransform {
1319 self.info().bounds_info.outer_transform()
1320 }
1321
1322 pub fn inner_transform(&self) -> PxTransform {
1326 self.info().bounds_info.inner_transform()
1327 }
1328
1329 pub fn outer_bounds(&self) -> PxRect {
1333 let info = self.info();
1334 info.bounds_info.outer_bounds()
1335 }
1336
1337 pub fn inner_bounds(&self) -> PxRect {
1341 let info = self.info();
1342 info.bounds_info.inner_bounds()
1343 }
1344
1345 pub fn spatial_bounds(&self) -> PxBox {
1347 self.out_of_bounds()
1348 .fold(self.inner_bounds().to_box2d(), |acc, w| acc.union(&w.inner_bounds().to_box2d()))
1349 }
1350
1351 pub fn center(&self) -> PxPoint {
1353 self.inner_bounds().center()
1354 }
1355
1356 pub fn meta(&self) -> StateMapRef<WidgetInfoMeta> {
1358 self.info().meta.borrow()
1359 }
1360
1361 pub fn tree(&self) -> &WidgetInfoTree {
1363 &self.tree
1364 }
1365
1366 pub fn is_reused(&self) -> bool {
1368 self.info().is_reused
1369 }
1370
1371 pub fn root(&self) -> Self {
1373 self.tree.root()
1374 }
1375
1376 pub fn parent(&self) -> Option<Self> {
1380 self.node().parent().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1381 }
1382
1383 pub fn prev_sibling(&self) -> Option<Self> {
1385 self.node().prev_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1386 }
1387
1388 pub fn next_sibling(&self) -> Option<Self> {
1390 self.node().next_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1391 }
1392
1393 pub fn first_child(&self) -> Option<Self> {
1395 self.node().first_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1396 }
1397
1398 pub fn last_child(&self) -> Option<Self> {
1400 self.node().last_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1401 }
1402
1403 pub fn has_siblings(&self) -> bool {
1405 self.node().has_siblings()
1406 }
1407
1408 pub fn has_children(&self) -> bool {
1410 self.node().has_children()
1411 }
1412
1413 pub fn siblings(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1415 self.prev_siblings().chain(self.next_siblings())
1416 }
1417
1418 pub fn children(&self) -> iter::Children {
1420 let mut r = self.self_and_children();
1421 r.next();
1422 r.next_back();
1423 r
1424 }
1425
1426 pub fn children_count(&self) -> usize {
1430 self.node().children_count()
1431 }
1432
1433 pub fn self_and_children(&self) -> iter::Children {
1435 iter::Children::new(self.clone())
1436 }
1437
1438 pub fn descendants(&self) -> iter::TreeIter {
1440 let mut d = self.self_and_descendants();
1441 d.next();
1442 d
1443 }
1444
1445 pub fn descendants_len(&self) -> usize {
1449 self.node().descendants_range().len()
1450 }
1451
1452 pub fn self_and_descendants(&self) -> iter::TreeIter {
1454 iter::TreeIter::self_and_descendants(self.clone())
1455 }
1456
1457 pub fn ancestors(&self) -> iter::Ancestors {
1459 let mut r = self.self_and_ancestors();
1460 r.next();
1461 r
1462 }
1463
1464 pub fn descendants_range(&self) -> WidgetDescendantsRange {
1466 WidgetDescendantsRange {
1467 tree: Some(self.tree.clone()),
1468 range: self.node().descendants_range(),
1469 }
1470 }
1471
1472 pub fn is_ancestor(&self, maybe_descendant: &WidgetInfo) -> bool {
1474 self.descendants_range().contains(maybe_descendant)
1475 }
1476
1477 pub fn is_descendant(&self, maybe_ancestor: &WidgetInfo) -> bool {
1479 maybe_ancestor.descendants_range().contains(self)
1480 }
1481
1482 pub fn self_and_ancestors(&self) -> iter::Ancestors {
1484 iter::Ancestors::new(self.clone())
1485 }
1486
1487 pub fn prev_siblings(&self) -> iter::PrevSiblings {
1489 let mut r = self.self_and_prev_siblings();
1490 r.next();
1491 r
1492 }
1493
1494 pub fn self_and_prev_siblings(&self) -> iter::PrevSiblings {
1496 iter::PrevSiblings::new(self.clone())
1497 }
1498
1499 pub fn next_siblings(&self) -> iter::NextSiblings {
1501 let mut r = self.self_and_next_siblings();
1502 r.next();
1503 r
1504 }
1505
1506 pub fn self_and_next_siblings(&self) -> iter::NextSiblings {
1508 iter::NextSiblings::new(self.clone())
1509 }
1510
1511 pub fn prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1515 iter::TreeIter::prev_siblings_in(self.clone(), ancestor.clone())
1516 }
1517
1518 pub fn self_and_prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1522 iter::TreeIter::self_and_prev_siblings_in(self.clone(), ancestor.clone())
1523 }
1524
1525 pub fn next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1529 iter::TreeIter::next_siblings_in(self.clone(), ancestor.clone())
1530 }
1531
1532 pub fn self_and_next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1536 iter::TreeIter::self_and_next_siblings_in(self.clone(), ancestor.clone())
1537 }
1538
1539 pub fn orientation_from(&self, origin: PxPoint) -> Option<Orientation2D> {
1545 let o = self.center();
1546 [
1547 Orientation2D::Above,
1548 Orientation2D::Right,
1549 Orientation2D::Below,
1550 Orientation2D::Left,
1551 ]
1552 .iter()
1553 .find(|&&d| d.point_is(origin, o))
1554 .copied()
1555 }
1556
1557 pub fn distance_key(&self, origin: PxPoint) -> DistanceKey {
1559 DistanceKey::from_points(origin, self.center())
1560 }
1561
1562 pub fn depth(&self) -> usize {
1564 self.ancestors().count()
1565 }
1566
1567 pub fn shared_ancestor(&self, other: &Self) -> Option<WidgetInfo> {
1571 if self.tree == other.tree {
1572 let a = self.path();
1573 let b = other.path();
1574 let shared = a.shared_ancestor(&b).unwrap();
1575 self.tree.get(shared.widget_id())
1576 } else {
1577 None
1578 }
1579 }
1580
1581 fn hit_test_z(&self, point: PxPoint) -> Option<ZIndex> {
1587 let bounds = &self.info().bounds_info;
1588 if bounds.inner_bounds().contains(point) {
1589 let z = match bounds.hit_test_z(point) {
1590 RelativeHitZ::NoHit => None,
1591 RelativeHitZ::Back => bounds.render_info().map(|i| (i.seg_id, i.back)),
1592 RelativeHitZ::Over(w) => self
1593 .tree
1594 .get(w)
1595 .and_then(|w| w.info().bounds_info.render_info())
1596 .map(|i| (i.seg_id, i.front)),
1597 RelativeHitZ::Front => bounds.render_info().map(|i| (i.seg_id, i.front)),
1598 };
1599
1600 match z {
1601 Some((seg_id, z)) => {
1602 let mut parent = self.parent();
1603 let mut child = self.clone();
1604
1605 while let Some(p) = parent {
1606 if p.info().bounds_info.hit_test_clip_child(&child, point) {
1607 return None;
1608 }
1609
1610 parent = p.parent();
1611 child = p;
1612 }
1613
1614 Some(ZIndex((z + self.tree.0.frame.read().widget_count_offsets.offset(seg_id)) as u32))
1615 }
1616 None => None,
1617 }
1618 } else {
1619 None
1620 }
1621 }
1622
1623 pub fn is_in_bounds(&self) -> bool {
1625 self.info().bounds_info.is_in_bounds()
1626 }
1627
1628 pub fn out_of_bounds(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1630 let range = self.descendants_range();
1631 self.tree.out_of_bounds().filter(move |w| range.contains(w))
1632 }
1633
1634 pub fn spatial_iter<F>(&self, filter: F) -> impl Iterator<Item = WidgetInfo> + use<F>
1640 where
1641 F: Fn(&WidgetInfo) -> bool + Clone,
1642 {
1643 let self_id = self.id();
1644 self.self_and_descendants()
1645 .tree_filter(clmv!(filter, |w| {
1646 if (w.is_in_bounds() || w.id() == self_id) && filter(w) {
1647 TreeFilter::Include
1648 } else {
1649 TreeFilter::SkipAll
1650 }
1651 }))
1652 .chain(self.out_of_bounds().flat_map(clmv!(filter, |w| {
1653 let out_of_bound_root_id = w.id();
1654 w.self_and_descendants().tree_filter(clmv!(filter, |w| {
1655 if (w.is_in_bounds() || w.id() == out_of_bound_root_id) && filter(w) {
1656 TreeFilter::Include
1657 } else {
1658 TreeFilter::SkipAll
1659 }
1660 }))
1661 })))
1662 }
1663
1664 pub fn inner_contains(&self, point: PxPoint) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1666 self.spatial_iter(move |w| w.inner_bounds().contains(point))
1667 }
1668
1669 pub fn inner_intersects(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1671 let rect = rect.to_box2d();
1672 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&rect))
1673 }
1674
1675 pub fn inner_contains_rect(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1677 let rect = rect.to_box2d();
1678 self.spatial_iter(move |w| w.inner_bounds().to_box2d().contains_box(&rect))
1679 }
1680
1681 pub fn inner_contained(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1683 let rect = rect.to_box2d();
1684 self.spatial_iter(move |w| rect.contains_box(&w.inner_bounds().to_box2d()))
1685 }
1686
1687 pub fn center_contained(&self, area: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1689 let area = area.to_box2d();
1690 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1691 .filter(move |w| area.contains(w.center()))
1692 }
1693
1694 pub fn center_in_distance(&self, origin: PxPoint, max_radius: Px) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1696 let area = PxRect::new(origin, PxSize::splat(max_radius))
1697 .inflate(max_radius, max_radius)
1698 .to_box2d();
1699
1700 let distance_key = DistanceKey::from_distance(max_radius);
1701
1702 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1703 .filter(move |w| w.distance_key(origin) <= distance_key)
1704 }
1705
1706 pub fn hit_test(&self, point: PxPoint) -> HitTestInfo {
1708 let _span = tracing::trace_span!("hit_test").entered();
1709
1710 let mut hits: Vec<_> = self
1711 .inner_contains(point)
1712 .filter_map(|w| {
1713 w.hit_test_z(point).map(|z| HitInfo {
1714 widget_id: w.id(),
1715 z_index: z,
1716 })
1717 })
1718 .collect();
1719
1720 hits.sort_by(|a, b| b.z_index.cmp(&a.z_index));
1721
1722 HitTestInfo {
1723 window_id: self.tree.0.window_id,
1724 frame_id: self.tree.0.frame.read().stats.last_frame,
1725 point,
1726 hits,
1727 }
1728 }
1729
1730 pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetInfo> {
1736 self.nearest_filtered(origin, max_radius, |_| true)
1737 }
1738
1739 pub fn nearest_filtered(&self, origin: PxPoint, max_radius: Px, filter: impl FnMut(&WidgetInfo) -> bool) -> Option<WidgetInfo> {
1741 self.nearest_bounded_filtered(origin, max_radius, self.tree.spatial_bounds(), filter)
1742 }
1743
1744 pub fn nearest_bounded_filtered(
1747 &self,
1748 origin: PxPoint,
1749 max_radius: Px,
1750 bounds: PxRect,
1751 mut filter: impl FnMut(&WidgetInfo) -> bool,
1752 ) -> Option<WidgetInfo> {
1753 let max_quad = self.tree.spatial_bounds().intersection(&bounds)?;
1756
1757 let mut source_quad = PxRect::new(origin - PxVector::splat(Px(64)), PxSize::splat(Px(128)));
1758 let mut search_quad = source_quad.intersection(&max_quad)?;
1759
1760 let max_diameter = max_radius * Px(2);
1761
1762 let mut dist = if max_radius != Px::MAX {
1763 DistanceKey::from_distance(max_radius + Px(1))
1764 } else {
1765 DistanceKey::NONE_MAX
1766 };
1767
1768 let mut nearest = None;
1769 loop {
1770 for w in self.center_contained(search_quad) {
1771 let w_dist = w.distance_key(origin);
1772 if w_dist < dist && filter(&w) {
1773 dist = w_dist;
1774 nearest = Some(w);
1775 }
1776 }
1777
1778 let source_width = source_quad.width();
1779 if nearest.is_some() || source_width >= max_diameter {
1780 break;
1781 } else {
1782 source_quad = source_quad.inflate(source_width, source_width);
1783 let new_search = match source_quad.intersection(&max_quad) {
1784 Some(b) if b != search_quad => b,
1785 _ => break, };
1787 search_quad = new_search;
1788 }
1789 }
1790
1791 if nearest.is_some() {
1792 let distance = PxVector::splat(Px(2) * dist.distance().unwrap_or(Px(0)));
1794
1795 let quad = euclid::Box2D::new(origin - distance, origin + distance).intersection_unchecked(&max_quad.to_box2d());
1796
1797 for w in self.center_contained(quad.to_rect()) {
1798 let w_dist = w.distance_key(origin);
1799 if w_dist < dist && filter(&w) {
1800 dist = w_dist;
1801 nearest = Some(w);
1802 }
1803 }
1804 }
1805
1806 nearest
1807 }
1808
1809 pub fn oriented(
1820 &self,
1821 origin: PxPoint,
1822 max_distance: Px,
1823 orientation: Orientation2D,
1824 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1825 let distance_bounded = max_distance != Px::MAX;
1826 let distance_key = if distance_bounded {
1827 DistanceKey::from_distance(max_distance)
1828 } else {
1829 DistanceKey::NONE_MAX
1830 };
1831 let me = self.clone();
1832 orientation
1833 .search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d())
1834 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
1835 .filter_map(move |(sq, w)| {
1836 let center = w.center();
1837 if sq.contains(center)
1838 && orientation.point_is(origin, center)
1839 && (!distance_bounded || DistanceKey::from_points(origin, center) <= distance_key)
1840 {
1841 Some(w)
1842 } else {
1843 None
1844 }
1845 })
1846 }
1847
1848 pub fn oriented_box(
1861 &self,
1862 origin: PxBox,
1863 max_distance: Px,
1864 orientation: Orientation2D,
1865 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1866 let distance_bounded = max_distance != Px::MAX;
1867 let distance_key = if distance_bounded {
1868 DistanceKey::from_distance(max_distance)
1869 } else {
1870 DistanceKey::NONE_MAX
1871 };
1872 let me = self.clone();
1873 let origin_center = origin.center();
1874 orientation
1875 .search_bounds(origin_center, max_distance, self.tree.spatial_bounds().to_box2d())
1876 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
1877 .filter_map(move |(sq, w)| {
1878 let bounds = w.inner_bounds().to_box2d();
1879 if sq.intersects(&bounds)
1880 && orientation.box_is(origin, bounds)
1881 && (!distance_bounded || DistanceKey::from_points(origin_center, bounds.center()) <= distance_key)
1882 {
1883 Some(w)
1884 } else {
1885 None
1886 }
1887 })
1888 }
1889
1890 pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
1896 self.nearest_oriented_filtered(origin, max_distance, orientation, |_| true)
1897 }
1898
1899 pub fn nearest_oriented_filtered(
1906 &self,
1907 origin: PxPoint,
1908 max_distance: Px,
1909 orientation: Orientation2D,
1910 filter: impl FnMut(&WidgetInfo) -> bool,
1911 ) -> Option<WidgetInfo> {
1912 self.nearest_oriented_filtered_impl(origin, max_distance, orientation, filter, |w| {
1913 orientation.point_is(origin, w.center())
1914 })
1915 }
1916
1917 pub fn nearest_box_oriented(&self, origin: PxBox, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
1923 self.nearest_box_oriented_filtered(origin, max_distance, orientation, |_| true)
1924 }
1925
1926 pub fn nearest_box_oriented_filtered(
1933 &self,
1934 origin: PxBox,
1935 max_distance: Px,
1936 orientation: Orientation2D,
1937 filter: impl FnMut(&WidgetInfo) -> bool,
1938 ) -> Option<WidgetInfo> {
1939 self.nearest_oriented_filtered_impl(origin.center(), max_distance, orientation, filter, |w| {
1940 orientation.box_is(origin, w.inner_bounds().to_box2d())
1941 })
1942 }
1943
1944 fn nearest_oriented_filtered_impl(
1945 &self,
1946 origin: PxPoint,
1947 max_distance: Px,
1948 orientation: Orientation2D,
1949 mut filter: impl FnMut(&WidgetInfo) -> bool,
1950 intersect: impl Fn(&WidgetInfo) -> bool,
1951 ) -> Option<WidgetInfo> {
1952 let mut dist = DistanceKey::from_distance(max_distance + Px(1));
1953 let mut nearest = None;
1954 let mut last_quad = euclid::Box2D::zero();
1955
1956 for search_quad in orientation.search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d()) {
1957 for w in self.center_contained(search_quad.to_rect()) {
1958 if intersect(&w) {
1959 let w_dist = w.distance_key(origin);
1960 if w_dist < dist && filter(&w) {
1961 dist = w_dist;
1962 nearest = Some(w);
1963 }
1964 }
1965 }
1966
1967 if nearest.is_some() {
1968 last_quad = search_quad;
1969 break;
1970 }
1971 }
1972
1973 if nearest.is_some() {
1974 match orientation {
1977 Orientation2D::Above => {
1978 let extra = last_quad.height() / Px(2);
1979 last_quad.max.y = last_quad.min.y;
1980 last_quad.min.y -= extra;
1981 }
1982 Orientation2D::Right => {
1983 let extra = last_quad.width() / Px(2);
1984 last_quad.min.x = last_quad.max.x;
1985 last_quad.max.x += extra;
1986 }
1987 Orientation2D::Below => {
1988 let extra = last_quad.height() / Px(2);
1989 last_quad.min.y = last_quad.max.y;
1990 last_quad.max.y += extra;
1991 }
1992 Orientation2D::Left => {
1993 let extra = last_quad.width() / Px(2);
1994 last_quad.max.x = last_quad.min.x;
1995 last_quad.min.x -= extra;
1996 }
1997 }
1998
1999 for w in self.center_contained(last_quad.to_rect()) {
2000 let w_dist = w.distance_key(origin);
2001 if w_dist < dist && filter(&w) {
2002 dist = w_dist;
2003 nearest = Some(w);
2004 }
2005 }
2006 }
2007
2008 nearest
2009 }
2010}
2011
2012#[derive(Debug)]
2016pub struct InteractivityFilterArgs {
2017 pub info: WidgetInfo,
2019}
2020impl InteractivityFilterArgs {
2021 pub fn new(info: WidgetInfo) -> Self {
2023 Self { info }
2024 }
2025}
2026
2027type InteractivityFilters = Vec<Arc<dyn Fn(&InteractivityFilterArgs) -> Interactivity + Send + Sync>>;
2028
2029bitflags::bitflags! {
2030 #[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2032 #[serde(transparent)]
2033 pub struct Interactivity: u8 {
2034 const ENABLED = 0b00;
2038
2039 const DISABLED = 0b01;
2043
2044 const BLOCKED = 0b10;
2049
2050 const BLOCKED_DISABLED = Self::DISABLED.bits() | Self::BLOCKED.bits();
2052 }
2053}
2054impl Interactivity {
2055 pub fn is_enabled(self) -> bool {
2057 self == Self::ENABLED
2058 }
2059
2060 pub fn is_vis_enabled(self) -> bool {
2062 !self.contains(Self::DISABLED)
2063 }
2064
2065 pub fn is_disabled(self) -> bool {
2067 self == Self::DISABLED
2068 }
2069
2070 pub fn is_vis_disabled(self) -> bool {
2072 self.contains(Self::DISABLED)
2073 }
2074
2075 pub fn is_blocked(self) -> bool {
2077 self.contains(Self::BLOCKED)
2078 }
2079}
2080impl Default for Interactivity {
2081 fn default() -> Self {
2083 Interactivity::ENABLED
2084 }
2085}
2086impl_from_and_into_var! {
2087 fn from(enabled: bool) -> Interactivity {
2090 if enabled {
2091 Interactivity::ENABLED
2092 } else {
2093 Interactivity::DISABLED
2094 }
2095 }
2096}
2097impl fmt::Debug for Interactivity {
2098 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2099 if self.is_enabled() {
2100 return write!(f, "ENABLED");
2101 }
2102 if *self == Self::BLOCKED_DISABLED {
2103 return write!(f, "BLOCKED_DISABLED");
2104 }
2105 if *self == Self::DISABLED {
2106 return write!(f, "DISABLED");
2107 }
2108 if *self == Self::BLOCKED {
2109 return write!(f, "BLOCKED");
2110 }
2111 write!(f, "Interactivity({:x})", self.bits())
2112 }
2113}
2114
2115#[derive(Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2125pub enum Visibility {
2126 Visible,
2130 Hidden,
2134 Collapsed,
2138}
2139impl Visibility {
2140 pub fn is_visible(self) -> bool {
2142 matches!(self, Self::Visible)
2143 }
2144
2145 pub fn is_hidden(self) -> bool {
2147 matches!(self, Self::Hidden)
2148 }
2149
2150 pub fn is_collapsed(self) -> bool {
2152 matches!(self, Self::Collapsed)
2153 }
2154}
2155impl fmt::Debug for Visibility {
2156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2157 if f.alternate() {
2158 write!(f, "Visibility::")?;
2159 }
2160 match self {
2161 Visibility::Visible => write!(f, "Visible"),
2162 Visibility::Hidden => write!(f, "Hidden"),
2163 Visibility::Collapsed => write!(f, "Collapsed"),
2164 }
2165 }
2166}
2167impl Default for Visibility {
2168 fn default() -> Self {
2170 Visibility::Visible
2171 }
2172}
2173impl ops::BitOr for Visibility {
2174 type Output = Self;
2175
2176 fn bitor(self, rhs: Self) -> Self::Output {
2178 use Visibility::*;
2179 match (self, rhs) {
2180 (Collapsed, _) | (_, Collapsed) => Collapsed,
2181 (Hidden, _) | (_, Hidden) => Hidden,
2182 _ => Visible,
2183 }
2184 }
2185}
2186impl ops::BitOrAssign for Visibility {
2187 fn bitor_assign(&mut self, rhs: Self) {
2188 *self = *self | rhs;
2189 }
2190}
2191impl_from_and_into_var! {
2192 fn from(visible: bool) -> Visibility {
2195 if visible {
2196 Visibility::Visible
2197 } else {
2198 Visibility::Collapsed
2199 }
2200 }
2201}
2202
2203#[derive(Clone, PartialEq, Eq, Default)]
2205pub struct WidgetDescendantsRange {
2206 tree: Option<WidgetInfoTree>,
2207 range: std::ops::Range<usize>,
2208}
2209impl WidgetDescendantsRange {
2210 pub fn contains(&self, wgt: &WidgetInfo) -> bool {
2212 self.range.contains(&wgt.node_id.get()) && self.tree.as_ref() == Some(&wgt.tree)
2213 }
2214}
2215
2216#[derive(Clone, Debug)]
2218pub struct HitInfo {
2219 pub widget_id: WidgetId,
2221
2222 pub z_index: ZIndex,
2224}
2225
2226#[derive(Clone, Debug)]
2228pub struct HitTestInfo {
2229 window_id: WindowId,
2230 frame_id: FrameId,
2231 point: PxPoint,
2232 hits: Vec<HitInfo>,
2233}
2234impl HitTestInfo {
2235 pub fn no_hits(window_id: WindowId) -> Self {
2237 HitTestInfo {
2238 window_id,
2239 frame_id: FrameId::INVALID,
2240 point: PxPoint::new(Px(-1), Px(-1)),
2241 hits: vec![],
2242 }
2243 }
2244
2245 pub fn window_id(&self) -> WindowId {
2247 self.window_id
2248 }
2249
2250 pub fn frame_id(&self) -> FrameId {
2252 self.frame_id
2253 }
2254
2255 pub fn point(&self) -> PxPoint {
2257 self.point
2258 }
2259
2260 pub fn hits(&self) -> &[HitInfo] {
2262 &self.hits
2263 }
2264
2265 pub fn target(&self) -> Option<&HitInfo> {
2267 self.hits.first()
2268 }
2269
2270 pub fn find(&self, widget_id: WidgetId) -> Option<&HitInfo> {
2272 self.hits.iter().find(|h| h.widget_id == widget_id)
2273 }
2274
2275 pub fn contains(&self, widget_id: WidgetId) -> bool {
2277 self.hits.iter().any(|h| h.widget_id == widget_id)
2278 }
2279
2280 pub fn intersection(&self, other: &HitTestInfo) -> HitTestInfo {
2282 let mut hits: Vec<_> = self.hits.iter().filter(|h| other.contains(h.widget_id)).cloned().collect();
2283 hits.shrink_to_fit();
2284
2285 HitTestInfo {
2286 window_id: self.window_id,
2287 frame_id: self.frame_id,
2288 point: self.point,
2289 hits,
2290 }
2291 }
2292}