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)]
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)]
120pub struct WidgetInfoTree(Arc<WidgetInfoTreeInner>);
121struct WidgetInfoTreeInner {
122 window_id: WindowId,
123 access_enabled: AccessEnabled,
124 tree: Tree<WidgetInfoData>,
125 lookup: IdMap<WidgetId, tree::NodeId>,
126 interactivity_filters: InteractivityFilters,
127 build_meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
128 frame: RwLock<WidgetInfoTreeFrame>,
129}
130struct WidgetInfoTreeFrame {
132 stats: WidgetInfoTreeStats,
133 stats_update: WidgetInfoTreeStatsUpdate,
134 out_of_bounds_update: Vec<(tree::NodeId, bool)>,
135 scale_factor: Factor,
136 view_process_gen: ViewProcessGen,
137
138 out_of_bounds: Arc<Vec<tree::NodeId>>,
139 spatial_bounds: PxBox,
140
141 widget_count_offsets: ParallelSegmentOffsets,
142
143 transform_changed_subs: IdMap<WidgetId, PxTransform>,
144 visibility_changed_subs: IdMap<WidgetId, Visibility>,
145}
146impl PartialEq for WidgetInfoTree {
147 fn eq(&self, other: &Self) -> bool {
148 Arc::ptr_eq(&self.0, &other.0)
149 }
150}
151impl Eq for WidgetInfoTree {}
152impl WidgetInfoTree {
153 pub fn wgt(window_id: WindowId, root_id: WidgetId) -> Self {
155 WidgetInfoBuilder::new(
156 Arc::default(),
157 window_id,
158 AccessEnabled::empty(),
159 root_id,
160 WidgetBoundsInfo::new(),
161 WidgetBorderInfo::new(),
162 1.fct(),
163 )
164 .finalize(None, false)
165 }
166
167 pub fn stats(&self) -> WidgetInfoTreeStats {
169 self.0.frame.read().stats.clone()
170 }
171
172 pub fn scale_factor(&self) -> Factor {
174 self.0.frame.read().scale_factor
175 }
176
177 pub fn view_process_gen(&self) -> ViewProcessGen {
183 self.0.frame.read().view_process_gen
184 }
185
186 pub fn build_meta(&self) -> StateMapRef<'_, WidgetInfoMeta> {
190 self.0.build_meta.borrow()
191 }
192
193 pub fn root(&self) -> WidgetInfo {
195 WidgetInfo::new(self.clone(), self.0.tree.root().id())
196 }
197
198 pub fn all_widgets(&self) -> iter::TreeIter {
200 self.root().self_and_descendants()
201 }
202
203 pub fn window_id(&self) -> WindowId {
205 self.0.window_id
206 }
207
208 pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
210 self.0.lookup.get(&widget_id.into()).map(|i| WidgetInfo::new(self.clone(), *i))
211 }
212
213 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
215 self.0.lookup.contains_key(&widget_id.into())
216 }
217
218 pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetInfo> {
220 self.get(path.widget_id())
221 .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
222 }
223
224 pub fn is_rendered(&self) -> bool {
227 self.0.frame.read().stats.last_frame != FrameId::INVALID
228 }
229
230 pub fn out_of_bounds(&self) -> impl std::iter::ExactSizeIterator<Item = WidgetInfo> + 'static + use<> {
232 let out = self.0.frame.read().out_of_bounds.clone();
233 let me = self.clone();
234 (0..out.len()).map(move |i| WidgetInfo::new(me.clone(), out[i]))
235 }
236
237 pub fn spatial_bounds(&self) -> PxRect {
239 self.0.frame.read().spatial_bounds.to_rect()
240 }
241
242 #[expect(clippy::len_without_is_empty)]
246 pub fn len(&self) -> usize {
247 self.0.lookup.len()
248 }
249
250 fn bounds_changed(&self) {
251 self.0.frame.write().stats_update.bounds_updated += 1;
252 }
253
254 fn in_bounds_changed(&self, widget_id: WidgetId, in_bounds: bool) {
255 let id = *self.0.lookup.get(&widget_id).unwrap();
256 self.0.frame.write().out_of_bounds_update.push((id, in_bounds));
257 }
258
259 fn visibility_changed(&self) {
260 self.0.frame.write().stats_update.vis_updated += 1;
261 }
262
263 pub(crate) fn after_render(
264 &self,
265 frame_id: FrameId,
266 scale_factor: Factor,
267 view_process_gen: Option<ViewProcessGen>,
268 widget_count_offsets: Option<ParallelSegmentOffsets>,
269 ) {
270 let mut frame = self.0.frame.write();
271 let stats_update = frame.stats_update.take();
272 frame.stats.update(frame_id, stats_update);
273
274 if !frame.out_of_bounds_update.is_empty() {
275 let mut out_of_bounds = Arc::try_unwrap(mem::take(&mut frame.out_of_bounds)).unwrap_or_else(|rc| (*rc).clone());
279
280 for (id, remove) in frame.out_of_bounds_update.drain(..) {
281 if remove {
282 if let Some(i) = out_of_bounds.iter().position(|i| *i == id) {
283 out_of_bounds.swap_remove(i);
284 }
285 } else {
286 out_of_bounds.push(id);
287 }
288 }
289 frame.out_of_bounds = Arc::new(out_of_bounds);
290 }
291
292 let mut spatial_bounds = self.root().outer_bounds().to_box2d();
293 for out in frame.out_of_bounds.iter() {
294 let b = WidgetInfo::new(self.clone(), *out).inner_bounds().to_box2d();
295 spatial_bounds = spatial_bounds.union(&b);
296 }
297 frame.spatial_bounds = spatial_bounds;
298
299 frame.scale_factor = scale_factor;
300 if let Some(vp_gen) = view_process_gen {
301 frame.view_process_gen = vp_gen;
302 }
303 if let Some(w) = widget_count_offsets {
304 frame.widget_count_offsets = w;
305 }
306
307 let mut changes = IdMap::new();
308 TRANSFORM_CHANGED_EVENT.visit_subscribers::<()>(|wid| {
309 if let Some(wgt) = self.get(wid) {
310 let transform = wgt.inner_transform();
311 match frame.transform_changed_subs.entry(wid) {
312 IdEntry::Occupied(mut e) => {
313 let prev = e.insert(transform);
314 if prev != transform {
315 changes.insert(wid, prev);
316 }
317 }
318 IdEntry::Vacant(e) => {
319 e.insert(transform);
320 }
321 }
322 }
323 ops::ControlFlow::Continue(())
324 });
325 if !changes.is_empty() {
326 if (frame.transform_changed_subs.len() - changes.len()) > 500 {
327 frame
328 .transform_changed_subs
329 .retain(|k, _| TRANSFORM_CHANGED_EVENT.is_subscriber(*k));
330 }
331
332 TRANSFORM_CHANGED_EVENT.notify(TransformChangedArgs::now(self.clone(), changes));
333 }
334 drop(frame); let mut changes = IdMap::new();
337 VISIBILITY_CHANGED_EVENT.visit_subscribers::<()>(|wid| {
338 if let Some(wgt) = self.get(wid) {
339 let visibility = wgt.visibility();
340 let mut frame = self.0.frame.write();
341 match frame.visibility_changed_subs.entry(wid) {
342 IdEntry::Occupied(mut e) => {
343 let prev = e.insert(visibility);
344 if prev != visibility {
345 changes.insert(wid, prev);
346 }
347 }
348 IdEntry::Vacant(e) => {
349 e.insert(visibility);
350 }
351 }
352 }
353 ops::ControlFlow::Continue(())
354 });
355 if !changes.is_empty() {
356 if (self.0.frame.read().visibility_changed_subs.len() - changes.len()) > 500 {
357 self.0
358 .frame
359 .write()
360 .visibility_changed_subs
361 .retain(|k, _| VISIBILITY_CHANGED_EVENT.is_subscriber(*k));
362 }
363
364 VISIBILITY_CHANGED_EVENT.notify(VisibilityChangedArgs::now(self.clone(), changes));
365 }
366 }
367
368 pub(crate) fn after_render_update(&self, frame_id: FrameId) {
369 let scale_factor = self.0.frame.read().scale_factor;
370 self.after_render(frame_id, scale_factor, None, None);
371 }
372}
373impl fmt::Debug for WidgetInfoTree {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 let nl = if f.alternate() { "\n " } else { " " };
376
377 write!(
378 f,
379 "WidgetInfoTree(Rc<{{{nl}window_id: {},{nl}widget_count: {},{nl}...}}>)",
380 self.0.window_id,
381 self.0.lookup.len(),
382 nl = nl
383 )
384 }
385}
386
387#[derive(Debug, Default)]
388struct WidgetBoundsData {
389 inner_offset: PxVector,
390 child_offset: PxVector,
391 parent_child_offset: PxVector,
392
393 inline: Option<WidgetInlineInfo>,
394 measure_inline: Option<WidgetInlineMeasure>,
395
396 measure_outer_size: PxSize,
397 outer_size: PxSize,
398 inner_size: PxSize,
399 baseline: Px,
400 inner_offset_baseline: bool,
401
402 transform_style: TransformStyle,
403 perspective: f32,
404 perspective_origin: Option<PxPoint>,
405
406 measure_metrics: Option<LayoutMetricsSnapshot>,
407 measure_metrics_used: LayoutMask,
408 metrics: Option<LayoutMetricsSnapshot>,
409 metrics_used: LayoutMask,
410
411 outer_transform: PxTransform,
412 inner_transform: PxTransform,
413 rendered: Option<WidgetRenderInfo>,
414
415 outer_bounds: PxRect,
416 inner_bounds: PxRect,
417
418 hit_clips: HitTestClips,
419 hit_index: hit::HitChildIndex,
420
421 is_in_bounds: Option<bool>,
422 is_partially_culled: bool,
423 cannot_auto_hide: bool,
424 is_collapsed: bool,
425}
426
427#[derive(Debug, Clone, Copy)]
429pub(crate) struct WidgetRenderInfo {
430 pub visible: bool,
432
433 pub parent_perspective: Option<(f32, PxPoint)>,
434
435 pub seg_id: ParallelSegmentId,
437 pub back: usize,
438 pub front: usize,
439}
440
441#[derive(Default, Clone, Debug)]
447pub struct WidgetBoundsInfo(Arc<Mutex<WidgetBoundsData>>);
448impl PartialEq for WidgetBoundsInfo {
449 fn eq(&self, other: &Self) -> bool {
450 Arc::ptr_eq(&self.0, &other.0)
451 }
452}
453impl Eq for WidgetBoundsInfo {}
454impl WidgetBoundsInfo {
455 pub fn new() -> Self {
457 Self::default()
458 }
459
460 pub fn new_size(outer: PxSize, inner: PxSize) -> Self {
462 let me = Self::new();
463 me.set_outer_size(outer);
464 me.set_inner_size(inner);
465 me
466 }
467
468 pub fn measure_outer_size(&self) -> PxSize {
472 self.0.lock().measure_outer_size
473 }
474
475 pub fn outer_size(&self) -> PxSize {
477 self.0.lock().outer_size
478 }
479
480 pub fn inner_offset(&self) -> PxVector {
487 let mut r = self.0.lock().inner_offset;
488 if self.inner_offset_baseline() {
489 r.y += self.baseline();
490 }
491 r
492 }
493
494 pub fn inner_offset_baseline(&self) -> bool {
499 self.0.lock().inner_offset_baseline
500 }
501
502 pub fn child_offset(&self) -> PxVector {
508 self.0.lock().child_offset
509 }
510
511 pub fn inner_size(&self) -> PxSize {
513 self.0.lock().inner_size
514 }
515
516 pub fn baseline(&self) -> Px {
525 self.0.lock().baseline
526 }
527
528 pub fn final_baseline(&self) -> Px {
536 let s = self.0.lock();
537 if s.inner_offset_baseline { Px(0) } else { s.baseline }
538 }
539
540 pub fn outer_transform(&self) -> PxTransform {
542 self.0.lock().outer_transform
543 }
544
545 pub fn parent_child_offset(&self) -> PxVector {
549 self.0.lock().parent_child_offset
550 }
551
552 pub fn inner_transform(&self) -> PxTransform {
554 self.0.lock().inner_transform
555 }
556
557 pub fn measure_inline(&self) -> Option<WidgetInlineMeasure> {
566 self.0.lock().measure_inline.clone()
567 }
568
569 pub fn inline(&self) -> Option<MappedMutexGuard<'_, WidgetInlineInfo>> {
573 let me = self.0.lock();
574 if me.inline.is_some() {
575 Some(MutexGuard::map(me, |m| m.inline.as_mut().unwrap()))
576 } else {
577 None
578 }
579 }
580
581 pub fn rendered(&self) -> Option<bool> {
583 self.0.lock().rendered.map(|i| i.visible)
584 }
585
586 pub(crate) fn render_info(&self) -> Option<WidgetRenderInfo> {
587 self.0.lock().rendered
588 }
589
590 pub fn is_in_bounds(&self) -> bool {
594 self.0.lock().is_in_bounds.unwrap_or(false)
595 }
596
597 pub fn can_auto_hide(&self) -> bool {
606 !self.0.lock().cannot_auto_hide
607 }
608
609 fn set_can_auto_hide(&self, enabled: bool) {
610 self.0.lock().cannot_auto_hide = !enabled;
611 }
612
613 pub(crate) fn is_actually_out_of_bounds(&self) -> bool {
614 self.0.lock().is_in_bounds.map(|is| !is).unwrap_or(false)
615 }
616
617 pub(crate) fn set_rendered(&self, rendered: Option<WidgetRenderInfo>, info: &WidgetInfoTree) {
618 let mut m = self.0.lock();
619 if m.rendered.map(|i| i.visible) != rendered.map(|i| i.visible) {
620 info.visibility_changed();
621 }
622 m.rendered = rendered;
623 }
624
625 pub(crate) fn set_outer_transform(&self, transform: PxTransform, info: &WidgetInfoTree) {
626 let bounds = transform
627 .outer_transformed(PxBox::from_size(self.outer_size()))
628 .unwrap_or_default()
629 .to_rect();
630
631 let mut m = self.0.lock();
632
633 if m.outer_bounds.size.is_empty() != bounds.size.is_empty() {
634 info.visibility_changed();
635 }
636
637 m.outer_bounds = bounds;
638 m.outer_transform = transform;
639 }
640
641 pub(crate) fn set_parent_child_offset(&self, offset: PxVector) {
642 self.0.lock().parent_child_offset = offset;
643 }
644
645 pub(crate) fn set_inner_transform(
646 &self,
647 transform: PxTransform,
648 info: &WidgetInfoTree,
649 widget_id: WidgetId,
650 parent_inner: Option<PxRect>,
651 ) {
652 let bounds = transform
653 .outer_transformed(PxBox::from_size(self.inner_size()))
654 .unwrap_or_default()
655 .to_rect();
656
657 let mut m = self.0.lock();
658
659 if m.inner_bounds != bounds {
660 m.inner_bounds = bounds;
661 info.bounds_changed();
662 }
663 let in_bounds = parent_inner.map(|r| r.contains_rect(&bounds)).unwrap_or(true);
664 if let Some(prev) = m.is_in_bounds {
665 if prev != in_bounds {
666 m.is_in_bounds = Some(in_bounds);
667 info.in_bounds_changed(widget_id, in_bounds);
668 }
669 } else {
670 m.is_in_bounds = Some(in_bounds);
671 if !in_bounds {
672 info.in_bounds_changed(widget_id, in_bounds);
673 }
674 }
675
676 m.inner_transform = transform;
677 }
678
679 pub fn outer_bounds(&self) -> PxRect {
681 self.0.lock().outer_bounds
682 }
683
684 pub fn inner_bounds(&self) -> PxRect {
686 self.0.lock().inner_bounds
687 }
688
689 pub fn inner_rects(&self) -> Vec<PxRect> {
693 let m = self.0.lock();
694 if let Some(i) = &m.inline {
695 let offset = m.inner_bounds.origin.to_vector();
696 let mut rows = i.rows.clone();
697 for r in &mut rows {
698 r.origin += offset;
699 }
700 rows
701 } else {
702 vec![m.inner_bounds]
703 }
704 }
705
706 pub fn visit_inner_rects<B>(&self, mut visitor: impl FnMut(PxRect, usize, usize) -> ops::ControlFlow<B>) -> Option<B> {
713 let m = self.0.lock();
714 let inner_bounds = m.inner_bounds;
715 let inline_range = m.inline.as_ref().map(|i| 0..i.rows.len());
716 drop(m);
717
718 if let Some(inline_range) = inline_range {
719 let offset = inner_bounds.origin.to_vector();
720 let len = inline_range.len();
721
722 for i in inline_range {
723 let mut r = match self.0.lock().inline.as_ref().and_then(|inl| inl.rows.get(i).copied()) {
724 Some(r) => r,
725 None => break, };
727 r.origin += offset;
728 match visitor(r, i, len) {
729 ops::ControlFlow::Continue(()) => continue,
730 ops::ControlFlow::Break(r) => return Some(r),
731 }
732 }
733 None
734 } else {
735 match visitor(inner_bounds, 0, 0) {
736 ops::ControlFlow::Continue(()) => None,
737 ops::ControlFlow::Break(r) => Some(r),
738 }
739 }
740 }
741
742 pub fn is_collapsed(&self) -> bool {
744 self.0.lock().is_collapsed
745 }
746
747 pub fn transform_style(&self) -> TransformStyle {
749 self.0.lock().transform_style
750 }
751
752 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
754 let p = self.0.lock();
755 if p.perspective.is_finite() {
756 let s = p.inner_size;
757 let o = p.perspective_origin.unwrap_or_else(|| PxPoint::new(s.width / 2.0, s.height / 2.0));
758 Some((p.perspective, o))
759 } else {
760 None
761 }
762 }
763
764 pub fn metrics(&self) -> Option<LayoutMetricsSnapshot> {
773 self.0.lock().metrics.clone()
774 }
775
776 pub fn metrics_used(&self) -> LayoutMask {
780 self.0.lock().metrics_used
781 }
782
783 pub fn hit_test_z(&self, window_point: PxPoint) -> RelativeHitZ {
785 let m = self.0.lock();
786 if m.hit_clips.is_hit_testable() {
787 m.hit_clips.hit_test_z(&m.inner_transform, window_point)
788 } else {
789 RelativeHitZ::NoHit
790 }
791 }
792
793 fn hit_test_index(&self) -> hit::HitChildIndex {
795 self.0.lock().hit_index
796 }
797
798 pub fn hit_test_clip_child(&self, child: &WidgetInfo, window_point: PxPoint) -> bool {
800 let m = self.0.lock();
801 if m.hit_clips.is_hit_testable() {
802 m.hit_clips
803 .clip_child(child.bounds_info().hit_test_index(), &m.inner_transform, window_point)
804 } else {
805 false
806 }
807 }
808
809 pub(crate) fn update_hit_test_transform(&self, value: FrameValueUpdate<PxTransform>) {
810 self.0.lock().hit_clips.update_transform(value);
811 }
812
813 pub(crate) fn measure_metrics(&self) -> Option<LayoutMetricsSnapshot> {
814 self.0.lock().measure_metrics.clone()
815 }
816 pub(crate) fn measure_metrics_used(&self) -> LayoutMask {
817 self.0.lock().measure_metrics_used
818 }
819
820 fn set_outer_size(&self, size: PxSize) {
821 let mut s = self.0.lock();
822 if !size.is_empty() {
823 s.is_collapsed = false;
824 }
825 s.outer_size = size;
826 }
827
828 fn set_is_collapsed(&self, collapsed: bool) {
829 self.0.lock().is_collapsed = collapsed;
830 }
831
832 fn take_inline(&self) -> Option<WidgetInlineInfo> {
833 self.0.lock().inline.take()
834 }
835
836 fn set_inline(&self, inline: Option<WidgetInlineInfo>) {
837 self.0.lock().inline = inline;
838 }
839
840 pub(super) fn set_measure_inline(&self, inline: Option<WidgetInlineMeasure>) {
841 self.0.lock().measure_inline = inline;
842 }
843
844 pub(crate) fn set_measure_outer_size(&self, size: PxSize) {
845 self.0.lock().measure_outer_size = size;
846 }
847
848 fn set_inner_offset(&self, offset: PxVector) {
849 self.0.lock().inner_offset = offset;
850 }
851
852 fn set_child_offset(&self, offset: PxVector) {
853 self.0.lock().child_offset = offset;
854 }
855
856 fn set_inner_size(&self, size: PxSize) {
857 self.0.lock().inner_size = size;
858 }
859
860 fn set_baseline(&self, baseline: Px) {
861 self.0.lock().baseline = baseline;
862 }
863
864 fn set_inner_offset_baseline(&self, enabled: bool) {
865 self.0.lock().inner_offset_baseline = enabled;
866 }
867
868 fn set_transform_style(&self, style: TransformStyle) {
869 self.0.lock().transform_style = style;
870 }
871
872 fn raw_perspective(&self) -> f32 {
873 self.0.lock().perspective
874 }
875
876 fn raw_perspective_origin(&self) -> Option<PxPoint> {
877 self.0.lock().perspective_origin
878 }
879
880 fn set_perspective(&self, d: f32) {
881 self.0.lock().perspective = d;
882 }
883
884 fn set_perspective_origin(&self, o: Option<PxPoint>) {
885 self.0.lock().perspective_origin = o;
886 }
887
888 fn set_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
889 self.0.lock().metrics = metrics;
890 self.0.lock().metrics_used = used;
891 }
892
893 pub(crate) fn set_measure_metrics(&self, metrics: Option<LayoutMetricsSnapshot>, used: LayoutMask) {
894 self.0.lock().measure_metrics = metrics;
895 self.0.lock().measure_metrics_used = used;
896 }
897
898 pub(crate) fn set_hit_clips(&self, clips: HitTestClips) {
899 self.0.lock().hit_clips = clips;
900 }
901
902 pub(crate) fn set_hit_index(&self, index: hit::HitChildIndex) {
903 self.0.lock().hit_index = index;
904 }
905
906 pub(crate) fn is_partially_culled(&self) -> bool {
907 self.0.lock().is_partially_culled
908 }
909
910 pub(crate) fn set_is_partially_culled(&self, is: bool) {
911 self.0.lock().is_partially_culled = is;
912 }
913}
914
915#[derive(Default, Debug)]
916struct WidgetBorderData {
917 offsets: PxSideOffsets,
918 corner_radius: PxCornerRadius,
919}
920
921#[derive(Default, Clone, Debug)]
923pub struct WidgetBorderInfo(Arc<Mutex<WidgetBorderData>>);
924impl WidgetBorderInfo {
925 pub fn new() -> Self {
927 Self::default()
928 }
929
930 #[cfg(test)]
932 pub fn new_test(offsets: PxSideOffsets, corner_radius: PxCornerRadius) -> Self {
933 let r = Self::default();
934 r.set_offsets(offsets);
935 r.set_corner_radius(corner_radius);
936 r
937 }
938
939 pub fn offsets(&self) -> PxSideOffsets {
941 self.0.lock().offsets
942 }
943
944 pub fn corner_radius(&self) -> PxCornerRadius {
946 self.0.lock().corner_radius
947 }
948
949 pub fn inner_corner_radius(&self) -> PxCornerRadius {
954 self.corner_radius().deflate(self.offsets())
955 }
956
957 pub fn inner_offset(&self, bounds: &WidgetBoundsInfo) -> PxVector {
961 let o = self.offsets();
962 let o = PxVector::new(o.left, o.top);
963 bounds.inner_offset() + o
964 }
965
966 pub fn inner_size(&self, bounds: &WidgetBoundsInfo) -> PxSize {
970 let o = self.offsets();
971 bounds.inner_size() - PxSize::new(o.horizontal(), o.vertical())
972 }
973
974 pub fn inner_transform(&self, bounds: &WidgetBoundsInfo) -> PxTransform {
978 let o = self.offsets();
979 let o = PxVector::new(o.left, o.top);
980 bounds.inner_transform().pre_translate(o.cast())
981 }
982
983 pub(super) fn set_offsets(&self, widths: PxSideOffsets) {
984 self.0.lock().offsets = widths;
985 }
986
987 pub(super) fn set_corner_radius(&self, radius: PxCornerRadius) {
988 self.0.lock().corner_radius = radius;
989 }
990}
991
992struct WidgetInfoData {
993 id: WidgetId,
994 bounds_info: WidgetBoundsInfo,
995 border_info: WidgetBorderInfo,
996 meta: Arc<OwnedStateMap<WidgetInfoMeta>>,
997 interactivity_filters: InteractivityFilters,
998 local_interactivity: Interactivity,
999 is_reused: bool,
1000 cache: Mutex<WidgetInfoCache>,
1001}
1002impl Clone for WidgetInfoData {
1003 fn clone(&self) -> Self {
1004 Self {
1005 id: self.id,
1006 bounds_info: self.bounds_info.clone(),
1007 border_info: self.border_info.clone(),
1008 meta: self.meta.clone(),
1009 interactivity_filters: self.interactivity_filters.clone(),
1010 local_interactivity: self.local_interactivity,
1011 is_reused: self.is_reused,
1012 cache: Mutex::new(match self.cache.try_lock() {
1013 Some(c) => c.clone(),
1014 None => WidgetInfoCache { interactivity: None },
1015 }),
1016 }
1017 }
1018}
1019impl fmt::Debug for WidgetInfoData {
1020 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1021 f.debug_struct("WidgetInfoData").field("id", &self.id).finish_non_exhaustive()
1022 }
1023}
1024#[derive(Clone)]
1025struct WidgetInfoCache {
1026 interactivity: Option<Interactivity>,
1027}
1028
1029#[derive(Clone)]
1031pub struct WidgetInfo {
1032 tree: WidgetInfoTree,
1033 node_id: tree::NodeId,
1034}
1035impl PartialEq for WidgetInfo {
1036 fn eq(&self, other: &Self) -> bool {
1037 self.node_id == other.node_id && self.tree == other.tree
1038 }
1039}
1040impl Eq for WidgetInfo {}
1041impl std::hash::Hash for WidgetInfo {
1042 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1043 std::hash::Hash::hash(&self.node_id, state)
1044 }
1045}
1046impl std::fmt::Debug for WidgetInfo {
1047 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1048 f.debug_struct("WidgetInfo")
1049 .field("[path]", &self.path().to_string())
1050 .field("[meta]", &self.meta())
1051 .finish_non_exhaustive()
1052 }
1053}
1054
1055impl WidgetInfo {
1056 fn new(tree: WidgetInfoTree, node_id: tree::NodeId) -> Self {
1057 Self { tree, node_id }
1058 }
1059
1060 fn node(&self) -> tree::NodeRef<'_, WidgetInfoData> {
1061 self.tree.0.tree.index(self.node_id)
1062 }
1063
1064 fn info(&self) -> &WidgetInfoData {
1065 self.node().value()
1066 }
1067
1068 pub fn id(&self) -> WidgetId {
1070 self.info().id
1071 }
1072
1073 pub fn path(&self) -> WidgetPath {
1075 let mut path: Vec<_> = self.ancestors().map(|a| a.id()).collect();
1076 path.reverse();
1077 path.push(self.id());
1078 path.shrink_to_fit();
1079
1080 WidgetPath::new(self.tree.0.window_id, path.into())
1081 }
1082
1083 pub fn trace_path(&self) -> Txt {
1087 let mut ws: Vec<_> = self.self_and_ancestors().collect();
1088 ws.reverse();
1089
1090 use std::fmt::*;
1091
1092 let mut s = String::new();
1093
1094 let _ = write!(&mut s, "{:?}/", self.tree.window_id());
1095 for w in ws {
1096 #[cfg(feature = "inspector")]
1097 {
1098 use crate::widget::inspector::*;
1099 if let Some(info) = w.inspector_info() {
1100 let mod_path = info.builder.widget_type().path;
1101 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1102 ident
1103 } else {
1104 mod_path
1105 };
1106
1107 let id = w.id();
1108 let name = id.name();
1109 if !name.is_empty() {
1110 let _ = write!(&mut s, "/{mod_ident}!({name:?})");
1111 } else {
1112 let _ = write!(&mut s, "/{mod_ident}!({})", id.sequential());
1113 }
1114 } else {
1115 let _ = write!(&mut s, "/{}", w.id());
1116 }
1117 }
1118
1119 #[cfg(not(feature = "inspector"))]
1120 {
1121 let _ = write!(&mut s, "/{}", w.id());
1122 }
1123 }
1124
1125 s.into()
1126 }
1127
1128 pub fn trace_id(&self) -> Txt {
1132 #[cfg(feature = "inspector")]
1133 {
1134 use crate::widget::inspector::*;
1135 if let Some(info) = self.inspector_info() {
1136 let mod_path = info.builder.widget_type().path;
1137 let mod_ident = if let Some((_, ident)) = mod_path.rsplit_once(':') {
1138 ident
1139 } else {
1140 mod_path
1141 };
1142
1143 let id = self.id();
1144 let name = id.name();
1145 if !name.is_empty() {
1146 return formatx!("{mod_ident}!({name:?})");
1147 } else {
1148 return formatx!("{mod_ident}!({})", id.sequential());
1149 }
1150 }
1151 }
1152 formatx!("{}", self.id())
1153 }
1154
1155 pub fn interaction_path(&self) -> InteractionPath {
1159 let mut path = vec![];
1160
1161 let mut blocked = None;
1162 let mut disabled = None;
1163
1164 for w in self.self_and_ancestors() {
1165 let interactivity = w.interactivity();
1166 if interactivity.contains(Interactivity::BLOCKED) {
1167 blocked = Some(path.len());
1168 }
1169 if interactivity.contains(Interactivity::DISABLED) {
1170 disabled = Some(path.len());
1171 }
1172
1173 path.push(w.id());
1174 }
1175 path.reverse();
1176 path.shrink_to_fit();
1177
1178 let len = path.len();
1179
1180 let path = WidgetPath::new(self.tree.0.window_id, path.into());
1181 InteractionPath::new_internal(
1182 path,
1183 blocked.map(|i| len - i - 1).unwrap_or(len),
1184 disabled.map(|i| len - i - 1).unwrap_or(len),
1185 )
1186 }
1187
1188 pub fn new_path(&self, old_path: &WidgetPath) -> Option<WidgetPath> {
1198 assert_eq!(old_path.widget_id(), self.id());
1199 if self
1200 .ancestors()
1201 .zip(old_path.ancestors().iter().rev())
1202 .any(|(ancestor, id)| ancestor.id() != *id)
1203 {
1204 Some(self.path())
1205 } else {
1206 None
1207 }
1208 }
1209
1210 pub fn new_interaction_path(&self, old_path: &InteractionPath) -> Option<InteractionPath> {
1220 assert_eq!(old_path.widget_id(), self.id());
1221
1222 if self.interactivity() != old_path.interactivity()
1223 || self
1224 .ancestors()
1225 .zip(old_path.zip().rev().skip(1))
1226 .any(|(anc, (id, int))| anc.id() != id || anc.interactivity() != int)
1227 {
1228 Some(self.interaction_path())
1229 } else {
1230 None
1231 }
1232 }
1233
1234 pub fn z_index(&self) -> Option<(ZIndex, ZIndex)> {
1243 self.info().bounds_info.render_info().map(|i| {
1244 let offset = self.tree.0.frame.read().widget_count_offsets.offset(i.seg_id);
1245 (ZIndex::from((i.back + offset) as u32), ZIndex::from((i.front + offset) as u32))
1246 })
1247 }
1248
1249 pub fn visibility(&self) -> Visibility {
1258 match self.info().bounds_info.rendered() {
1259 Some(vis) => {
1260 if vis {
1261 Visibility::Visible
1262 } else {
1263 Visibility::Hidden
1264 }
1265 }
1266 None => {
1267 if self.tree.is_rendered() {
1268 Visibility::Collapsed
1269 } else {
1270 Visibility::Visible
1271 }
1272 }
1273 }
1274 }
1275
1276 pub fn interactivity(&self) -> Interactivity {
1281 let cached = self.info().cache.lock().interactivity;
1282 if let Some(cache) = cached {
1283 cache
1284 } else {
1285 let mut cache = self.info().cache.lock();
1286 let mut interactivity = self.info().local_interactivity;
1287
1288 if interactivity != Interactivity::BLOCKED_DISABLED {
1289 interactivity |= self.parent().map(|n| n.interactivity()).unwrap_or(Interactivity::ENABLED);
1290 if interactivity != Interactivity::BLOCKED_DISABLED {
1291 let args = InteractivityFilterArgs { info: self.clone() };
1292 for filter in &self.tree.0.interactivity_filters {
1293 interactivity |= filter(&args);
1294 if interactivity == Interactivity::BLOCKED_DISABLED {
1295 break;
1296 }
1297 }
1298 }
1299 }
1300
1301 cache.interactivity = Some(interactivity);
1302 interactivity
1303 }
1304 }
1305
1306 pub fn bounds_info(&self) -> WidgetBoundsInfo {
1310 self.info().bounds_info.clone()
1311 }
1312
1313 pub fn border_info(&self) -> WidgetBorderInfo {
1317 self.info().border_info.clone()
1318 }
1319
1320 pub fn perspective(&self) -> Option<(f32, PxPoint)> {
1324 self.parent()?.bounds_info().perspective()
1325 }
1326
1327 pub fn transform_style(&self) -> TransformStyle {
1331 if let TransformStyle::Flat = self.bounds_info().transform_style() {
1332 if let Some(p) = self.parent() {
1333 p.bounds_info().transform_style()
1334 } else {
1335 TransformStyle::Flat
1336 }
1337 } else {
1338 TransformStyle::Preserve3D
1339 }
1340 }
1341
1342 pub fn outer_size(&self) -> PxSize {
1346 self.info().bounds_info.outer_size()
1347 }
1348
1349 pub fn inner_size(&self) -> PxSize {
1353 self.info().bounds_info.inner_size()
1354 }
1355
1356 pub fn inner_border_size(&self) -> PxSize {
1360 let info = self.info();
1361 info.border_info.inner_size(&info.bounds_info)
1362 }
1363
1364 pub fn baseline(&self) -> Px {
1366 self.info().bounds_info.baseline()
1367 }
1368
1369 pub fn outer_transform(&self) -> PxTransform {
1373 self.info().bounds_info.outer_transform()
1374 }
1375
1376 pub fn inner_transform(&self) -> PxTransform {
1380 self.info().bounds_info.inner_transform()
1381 }
1382
1383 pub fn outer_bounds(&self) -> PxRect {
1387 let info = self.info();
1388 info.bounds_info.outer_bounds()
1389 }
1390
1391 pub fn inner_bounds(&self) -> PxRect {
1395 let info = self.info();
1396 info.bounds_info.inner_bounds()
1397 }
1398
1399 pub fn spatial_bounds(&self) -> PxBox {
1401 self.out_of_bounds()
1402 .fold(self.inner_bounds().to_box2d(), |acc, w| acc.union(&w.inner_bounds().to_box2d()))
1403 }
1404
1405 pub fn center(&self) -> PxPoint {
1407 self.inner_bounds().center()
1408 }
1409
1410 pub fn meta(&self) -> StateMapRef<'_, WidgetInfoMeta> {
1412 self.info().meta.borrow()
1413 }
1414
1415 pub fn tree(&self) -> &WidgetInfoTree {
1417 &self.tree
1418 }
1419
1420 pub fn is_reused(&self) -> bool {
1422 self.info().is_reused
1423 }
1424
1425 pub fn root(&self) -> Self {
1427 self.tree.root()
1428 }
1429
1430 pub fn parent(&self) -> Option<Self> {
1434 self.node().parent().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1435 }
1436
1437 pub fn prev_sibling(&self) -> Option<Self> {
1439 self.node().prev_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1440 }
1441
1442 pub fn next_sibling(&self) -> Option<Self> {
1444 self.node().next_sibling().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1445 }
1446
1447 pub fn first_child(&self) -> Option<Self> {
1449 self.node().first_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1450 }
1451
1452 pub fn last_child(&self) -> Option<Self> {
1454 self.node().last_child().map(move |n| WidgetInfo::new(self.tree.clone(), n.id()))
1455 }
1456
1457 pub fn has_siblings(&self) -> bool {
1459 self.node().has_siblings()
1460 }
1461
1462 pub fn has_children(&self) -> bool {
1464 self.node().has_children()
1465 }
1466
1467 pub fn siblings(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1469 self.prev_siblings().chain(self.next_siblings())
1470 }
1471
1472 pub fn children(&self) -> iter::Children {
1474 let mut r = self.self_and_children();
1475 r.next();
1476 r.next_back();
1477 r
1478 }
1479
1480 pub fn children_count(&self) -> usize {
1484 self.node().children_count()
1485 }
1486
1487 pub fn self_and_children(&self) -> iter::Children {
1489 iter::Children::new(self.clone())
1490 }
1491
1492 pub fn descendants(&self) -> iter::TreeIter {
1494 let mut d = self.self_and_descendants();
1495 d.next();
1496 d
1497 }
1498
1499 pub fn descendants_len(&self) -> usize {
1503 self.node().descendants_range().len()
1504 }
1505
1506 pub fn self_and_descendants(&self) -> iter::TreeIter {
1508 iter::TreeIter::self_and_descendants(self.clone())
1509 }
1510
1511 pub fn ancestors(&self) -> iter::Ancestors {
1513 let mut r = self.self_and_ancestors();
1514 r.next();
1515 r
1516 }
1517
1518 pub fn descendants_range(&self) -> WidgetDescendantsRange {
1520 WidgetDescendantsRange {
1521 tree: Some(self.tree.clone()),
1522 range: self.node().descendants_range(),
1523 }
1524 }
1525
1526 pub fn cmp_sibling_in(&self, sibling: &WidgetInfo, ancestor: &WidgetInfo) -> Option<std::cmp::Ordering> {
1530 let range = ancestor.node().descendants_range();
1531 let index = self.node_id.get();
1532 let sibling_index = sibling.node_id.get();
1533 if range.contains(&index) && self.tree == sibling.tree && self.tree == ancestor.tree {
1534 return Some(index.cmp(&sibling_index));
1535 }
1536 None
1537 }
1538
1539 pub fn is_ancestor(&self, maybe_descendant: &WidgetInfo) -> bool {
1541 self.descendants_range().contains(maybe_descendant)
1542 }
1543
1544 pub fn is_descendant(&self, maybe_ancestor: &WidgetInfo) -> bool {
1546 maybe_ancestor.descendants_range().contains(self)
1547 }
1548
1549 pub fn self_and_ancestors(&self) -> iter::Ancestors {
1551 iter::Ancestors::new(self.clone())
1552 }
1553
1554 pub fn prev_siblings(&self) -> iter::PrevSiblings {
1556 let mut r = self.self_and_prev_siblings();
1557 r.next();
1558 r
1559 }
1560
1561 pub fn self_and_prev_siblings(&self) -> iter::PrevSiblings {
1563 iter::PrevSiblings::new(self.clone())
1564 }
1565
1566 pub fn next_siblings(&self) -> iter::NextSiblings {
1568 let mut r = self.self_and_next_siblings();
1569 r.next();
1570 r
1571 }
1572
1573 pub fn self_and_next_siblings(&self) -> iter::NextSiblings {
1575 iter::NextSiblings::new(self.clone())
1576 }
1577
1578 pub fn prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1582 iter::TreeIter::prev_siblings_in(self.clone(), ancestor.clone())
1583 }
1584
1585 pub fn self_and_prev_siblings_in(&self, ancestor: &WidgetInfo) -> iter::RevTreeIter {
1589 iter::TreeIter::self_and_prev_siblings_in(self.clone(), ancestor.clone())
1590 }
1591
1592 pub fn next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1596 iter::TreeIter::next_siblings_in(self.clone(), ancestor.clone())
1597 }
1598
1599 pub fn self_and_next_siblings_in(&self, ancestor: &WidgetInfo) -> iter::TreeIter {
1603 iter::TreeIter::self_and_next_siblings_in(self.clone(), ancestor.clone())
1604 }
1605
1606 pub fn orientation_from(&self, origin: PxPoint) -> Option<Orientation2D> {
1612 let o = self.center();
1613 [
1614 Orientation2D::Above,
1615 Orientation2D::Right,
1616 Orientation2D::Below,
1617 Orientation2D::Left,
1618 ]
1619 .iter()
1620 .find(|&&d| d.point_is(origin, o))
1621 .copied()
1622 }
1623
1624 pub fn distance_key(&self, origin: PxPoint) -> DistanceKey {
1626 DistanceKey::from_points(origin, self.center())
1627 }
1628
1629 pub fn rect_distance_key(&self, origin: PxPoint) -> DistanceKey {
1633 self.rect_distance_key_filtered(origin, |_, _, _| true)
1634 }
1635
1636 pub fn rect_distance_key_filtered(&self, origin: PxPoint, mut filter: impl FnMut(PxRect, usize, usize) -> bool) -> DistanceKey {
1643 let mut d = DistanceKey::NONE_MAX;
1644 self.info().bounds_info.visit_inner_rects::<()>(|r, i, len| {
1645 if !filter(r, i, len) {
1646 return ops::ControlFlow::Continue(());
1647 }
1648 let dd = DistanceKey::from_rect_to_point(r, origin);
1649 d = d.min(dd);
1650 if d == DistanceKey::MIN {
1651 ops::ControlFlow::Break(())
1652 } else {
1653 ops::ControlFlow::Continue(())
1654 }
1655 });
1656 d
1657 }
1658
1659 pub fn depth(&self) -> usize {
1661 self.ancestors().count()
1662 }
1663
1664 pub fn shared_ancestor(&self, other: &Self) -> Option<WidgetInfo> {
1668 if self.tree == other.tree {
1669 let a = self.path();
1670 let b = other.path();
1671 let shared = a.shared_ancestor(&b).unwrap();
1672 self.tree.get(shared.widget_id())
1673 } else {
1674 None
1675 }
1676 }
1677
1678 fn hit_test_z(&self, point: PxPoint) -> Option<ZIndex> {
1684 let bounds = &self.info().bounds_info;
1685 if bounds.inner_bounds().contains(point) {
1686 let z = match bounds.hit_test_z(point) {
1687 RelativeHitZ::NoHit => None,
1688 RelativeHitZ::Back => bounds.render_info().map(|i| (i.seg_id, i.back)),
1689 RelativeHitZ::Over(w) => self
1690 .tree
1691 .get(w)
1692 .and_then(|w| w.info().bounds_info.render_info())
1693 .map(|i| (i.seg_id, i.front)),
1694 RelativeHitZ::Front => bounds.render_info().map(|i| (i.seg_id, i.front)),
1695 };
1696
1697 match z {
1698 Some((seg_id, z)) => {
1699 let mut parent = self.parent();
1700 let mut child = self.clone();
1701
1702 while let Some(p) = parent {
1703 if p.info().bounds_info.hit_test_clip_child(&child, point) {
1704 return None;
1705 }
1706
1707 parent = p.parent();
1708 child = p;
1709 }
1710
1711 Some(ZIndex::from(
1712 (z + self.tree.0.frame.read().widget_count_offsets.offset(seg_id)) as u32,
1713 ))
1714 }
1715 None => None,
1716 }
1717 } else {
1718 None
1719 }
1720 }
1721
1722 pub fn is_in_bounds(&self) -> bool {
1724 self.info().bounds_info.is_in_bounds()
1725 }
1726
1727 pub fn out_of_bounds(&self) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1729 let range = self.descendants_range();
1730 self.tree.out_of_bounds().filter(move |w| range.contains(w))
1731 }
1732
1733 pub fn spatial_iter<F>(&self, filter: F) -> impl Iterator<Item = WidgetInfo> + use<F>
1739 where
1740 F: Fn(&WidgetInfo) -> bool + Clone,
1741 {
1742 let self_id = self.id();
1743 self.self_and_descendants()
1744 .tree_filter(clmv!(filter, |w| {
1745 if (w.is_in_bounds() || w.id() == self_id) && filter(w) {
1746 TreeFilter::Include
1747 } else {
1748 TreeFilter::SkipAll
1749 }
1750 }))
1751 .chain(self.out_of_bounds().flat_map(clmv!(filter, |w| {
1752 let out_of_bound_root_id = w.id();
1753 w.self_and_descendants().tree_filter(clmv!(filter, |w| {
1754 if (w.is_in_bounds() || w.id() == out_of_bound_root_id) && filter(w) {
1755 TreeFilter::Include
1756 } else {
1757 TreeFilter::SkipAll
1758 }
1759 }))
1760 })))
1761 }
1762
1763 pub fn inner_contains(&self, point: PxPoint) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1765 self.spatial_iter(move |w| w.inner_bounds().contains(point))
1766 }
1767
1768 pub fn inner_intersects(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1770 let rect = rect.to_box2d();
1771 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&rect))
1772 }
1773
1774 pub fn inner_contains_rect(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1776 let rect = rect.to_box2d();
1777 self.spatial_iter(move |w| w.inner_bounds().to_box2d().contains_box(&rect))
1778 }
1779
1780 pub fn inner_contained(&self, rect: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1782 let rect = rect.to_box2d();
1783 self.spatial_iter(move |w| rect.contains_box(&w.inner_bounds().to_box2d()))
1784 }
1785
1786 pub fn center_contained(&self, area: PxRect) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1788 let area = area.to_box2d();
1789 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1790 .filter(move |w| area.contains(w.center()))
1791 }
1792
1793 pub fn center_in_distance(&self, origin: PxPoint, max_radius: Px) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
1795 let area = PxRect::new(origin, PxSize::splat(max_radius))
1796 .inflate(max_radius, max_radius)
1797 .to_box2d();
1798
1799 let distance_key = DistanceKey::from_distance(max_radius);
1800
1801 self.spatial_iter(move |w| w.inner_bounds().to_box2d().intersects(&area))
1802 .filter(move |w| w.distance_key(origin) <= distance_key)
1803 }
1804
1805 pub fn hit_test(&self, point: PxPoint) -> HitTestInfo {
1807 let _span = tracing::trace_span!("hit_test").entered();
1808
1809 let mut hits: Vec<_> = self
1810 .inner_contains(point)
1811 .filter_map(|w| {
1812 w.hit_test_z(point).map(|z| HitInfo {
1813 widget_id: w.id(),
1814 z_index: z,
1815 })
1816 })
1817 .collect();
1818
1819 hits.sort_by(|a, b| b.z_index.cmp(&a.z_index));
1820
1821 HitTestInfo {
1822 window_id: self.tree.0.window_id,
1823 frame_id: self.tree.0.frame.read().stats.last_frame,
1824 point,
1825 hits,
1826 }
1827 }
1828
1829 pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetInfo> {
1835 self.nearest_filtered(origin, max_radius, |_| true)
1836 }
1837
1838 pub fn nearest_filtered(&self, origin: PxPoint, max_radius: Px, filter: impl FnMut(&WidgetInfo) -> bool) -> Option<WidgetInfo> {
1840 self.nearest_bounded_filtered(origin, max_radius, self.tree.spatial_bounds(), filter)
1841 }
1842
1843 pub fn nearest_bounded_filtered(
1846 &self,
1847 origin: PxPoint,
1848 max_radius: Px,
1849 bounds: PxRect,
1850 mut filter: impl FnMut(&WidgetInfo) -> bool,
1851 ) -> Option<WidgetInfo> {
1852 let max_quad = self.tree.spatial_bounds().intersection(&bounds)?;
1855
1856 let mut source_quad = PxRect::new(origin - PxVector::splat(Px(64)), PxSize::splat(Px(128)));
1857 let mut search_quad = source_quad.intersection(&max_quad)?;
1858
1859 let max_diameter = max_radius * Px(2);
1860
1861 let mut dist = if max_radius != Px::MAX {
1862 DistanceKey::from_distance(max_radius + Px(1))
1863 } else {
1864 DistanceKey::NONE_MAX
1865 };
1866
1867 let mut nearest = None;
1868 loop {
1869 for w in self.center_contained(search_quad) {
1870 let w_dist = w.distance_key(origin);
1871 if w_dist < dist && filter(&w) {
1872 dist = w_dist;
1873 nearest = Some(w);
1874 }
1875 }
1876
1877 let source_width = source_quad.width();
1878 if nearest.is_some() || source_width >= max_diameter {
1879 break;
1880 } else {
1881 source_quad = source_quad.inflate(source_width, source_width);
1882 let new_search = match source_quad.intersection(&max_quad) {
1883 Some(b) if b != search_quad => b,
1884 _ => break, };
1886 search_quad = new_search;
1887 }
1888 }
1889
1890 if nearest.is_some() {
1891 let distance = PxVector::splat(Px(2) * dist.distance().unwrap_or(Px(0)));
1893
1894 let quad = euclid::Box2D::new(origin - distance, origin + distance).intersection_unchecked(&max_quad.to_box2d());
1895
1896 for w in self.center_contained(quad.to_rect()) {
1897 let w_dist = w.distance_key(origin);
1898 if w_dist < dist && filter(&w) {
1899 dist = w_dist;
1900 nearest = Some(w);
1901 }
1902 }
1903 }
1904
1905 nearest
1906 }
1907
1908 pub fn nearest_rect(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetInfo> {
1913 self.nearest_rect_filtered(origin, max_radius, |_, _, _, _| true)
1914 }
1915
1916 pub fn nearest_rect_filtered(
1922 &self,
1923 origin: PxPoint,
1924 max_radius: Px,
1925 filter: impl FnMut(&WidgetInfo, PxRect, usize, usize) -> bool,
1926 ) -> Option<WidgetInfo> {
1927 self.nearest_rect_bounded_filtered(origin, max_radius, self.tree.spatial_bounds(), filter)
1928 }
1929
1930 pub fn nearest_rect_bounded_filtered(
1936 &self,
1937 origin: PxPoint,
1938 max_radius: Px,
1939 bounds: PxRect,
1940 mut filter: impl FnMut(&WidgetInfo, PxRect, usize, usize) -> bool,
1941 ) -> Option<WidgetInfo> {
1942 let max_quad = self.tree.spatial_bounds().intersection(&bounds)?;
1945
1946 let mut source_quad = PxRect::new(origin - PxVector::splat(Px(64)), PxSize::splat(Px(128)));
1947 let mut search_quad = source_quad.intersection(&max_quad)?;
1948
1949 let max_diameter = max_radius * Px(2);
1950
1951 let mut dist = if max_radius != Px::MAX {
1952 DistanceKey::from_distance(max_radius + Px(1))
1953 } else {
1954 DistanceKey::NONE_MAX
1955 };
1956
1957 let mut nearest = None;
1958 loop {
1959 for w in self.inner_intersects(search_quad) {
1960 let w_dist = w.rect_distance_key_filtered(origin, |rect, i, len| filter(&w, rect, i, len));
1961 if w_dist < dist {
1962 dist = w_dist;
1963 nearest = Some(w);
1964 } else if w_dist == DistanceKey::MIN {
1965 let w_center_dist = w.distance_key(origin);
1966 let center_dist = nearest.as_ref().unwrap().distance_key(origin);
1967 if w_center_dist < center_dist {
1968 nearest = Some(w);
1969 }
1970 }
1971 }
1972
1973 let source_width = source_quad.width();
1974 if nearest.is_some() || source_width >= max_diameter {
1975 break;
1976 } else {
1977 source_quad = source_quad.inflate(source_width, source_width);
1978 let new_search = match source_quad.intersection(&max_quad) {
1979 Some(b) if b != search_quad => b,
1980 _ => break, };
1982 search_quad = new_search;
1983 }
1984 }
1985
1986 nearest
1987 }
1988
1989 pub fn oriented(
2000 &self,
2001 origin: PxPoint,
2002 max_distance: Px,
2003 orientation: Orientation2D,
2004 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
2005 let distance_bounded = max_distance != Px::MAX;
2006 let distance_key = if distance_bounded {
2007 DistanceKey::from_distance(max_distance)
2008 } else {
2009 DistanceKey::NONE_MAX
2010 };
2011 let me = self.clone();
2012 orientation
2013 .search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d())
2014 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
2015 .filter_map(move |(sq, w)| {
2016 let center = w.center();
2017 if sq.contains(center)
2018 && orientation.point_is(origin, center)
2019 && (!distance_bounded || DistanceKey::from_points(origin, center) <= distance_key)
2020 {
2021 Some(w)
2022 } else {
2023 None
2024 }
2025 })
2026 }
2027
2028 pub fn oriented_box(
2041 &self,
2042 origin: PxBox,
2043 max_distance: Px,
2044 orientation: Orientation2D,
2045 ) -> impl Iterator<Item = WidgetInfo> + 'static + use<> {
2046 let distance_bounded = max_distance != Px::MAX;
2047 let distance_key = if distance_bounded {
2048 DistanceKey::from_distance(max_distance)
2049 } else {
2050 DistanceKey::NONE_MAX
2051 };
2052 let me = self.clone();
2053 let origin_center = origin.center();
2054 orientation
2055 .search_bounds(origin_center, max_distance, self.tree.spatial_bounds().to_box2d())
2056 .flat_map(move |sq| me.inner_intersects(sq.to_rect()).map(move |w| (sq, w)))
2057 .filter_map(move |(sq, w)| {
2058 let bounds = w.inner_bounds().to_box2d();
2059 if sq.intersects(&bounds)
2060 && orientation.box_is(origin, bounds)
2061 && (!distance_bounded || DistanceKey::from_points(origin_center, bounds.center()) <= distance_key)
2062 {
2063 Some(w)
2064 } else {
2065 None
2066 }
2067 })
2068 }
2069
2070 pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
2076 self.nearest_oriented_filtered(origin, max_distance, orientation, |_| true)
2077 }
2078
2079 pub fn nearest_oriented_filtered(
2086 &self,
2087 origin: PxPoint,
2088 max_distance: Px,
2089 orientation: Orientation2D,
2090 filter: impl FnMut(&WidgetInfo) -> bool,
2091 ) -> Option<WidgetInfo> {
2092 self.nearest_oriented_filtered_impl(origin, max_distance, orientation, filter, |w| {
2093 orientation.point_is(origin, w.center())
2094 })
2095 }
2096
2097 pub fn nearest_box_oriented(&self, origin: PxBox, max_distance: Px, orientation: Orientation2D) -> Option<WidgetInfo> {
2103 self.nearest_box_oriented_filtered(origin, max_distance, orientation, |_| true)
2104 }
2105
2106 pub fn nearest_box_oriented_filtered(
2113 &self,
2114 origin: PxBox,
2115 max_distance: Px,
2116 orientation: Orientation2D,
2117 filter: impl FnMut(&WidgetInfo) -> bool,
2118 ) -> Option<WidgetInfo> {
2119 self.nearest_oriented_filtered_impl(origin.center(), max_distance, orientation, filter, |w| {
2120 orientation.box_is(origin, w.inner_bounds().to_box2d())
2121 })
2122 }
2123
2124 fn nearest_oriented_filtered_impl(
2125 &self,
2126 origin: PxPoint,
2127 max_distance: Px,
2128 orientation: Orientation2D,
2129 mut filter: impl FnMut(&WidgetInfo) -> bool,
2130 intersect: impl Fn(&WidgetInfo) -> bool,
2131 ) -> Option<WidgetInfo> {
2132 let mut dist = DistanceKey::from_distance(max_distance + Px(1));
2133 let mut nearest = None;
2134 let mut last_quad = euclid::Box2D::zero();
2135
2136 for search_quad in orientation.search_bounds(origin, max_distance, self.tree.spatial_bounds().to_box2d()) {
2137 for w in self.center_contained(search_quad.to_rect()) {
2138 if intersect(&w) {
2139 let w_dist = w.distance_key(origin);
2140 if w_dist < dist && filter(&w) {
2141 dist = w_dist;
2142 nearest = Some(w);
2143 }
2144 }
2145 }
2146
2147 if nearest.is_some() {
2148 last_quad = search_quad;
2149 break;
2150 }
2151 }
2152
2153 if nearest.is_some() {
2154 match orientation {
2157 Orientation2D::Above => {
2158 let extra = last_quad.height() / Px(2);
2159 last_quad.max.y = last_quad.min.y;
2160 last_quad.min.y -= extra;
2161 }
2162 Orientation2D::Right => {
2163 let extra = last_quad.width() / Px(2);
2164 last_quad.min.x = last_quad.max.x;
2165 last_quad.max.x += extra;
2166 }
2167 Orientation2D::Below => {
2168 let extra = last_quad.height() / Px(2);
2169 last_quad.min.y = last_quad.max.y;
2170 last_quad.max.y += extra;
2171 }
2172 Orientation2D::Left => {
2173 let extra = last_quad.width() / Px(2);
2174 last_quad.max.x = last_quad.min.x;
2175 last_quad.min.x -= extra;
2176 }
2177 }
2178
2179 for w in self.center_contained(last_quad.to_rect()) {
2180 let w_dist = w.distance_key(origin);
2181 if w_dist < dist && filter(&w) {
2182 dist = w_dist;
2183 nearest = Some(w);
2184 }
2185 }
2186 }
2187
2188 nearest
2189 }
2190}
2191
2192#[derive(Debug)]
2196#[non_exhaustive]
2197pub struct InteractivityFilterArgs {
2198 pub info: WidgetInfo,
2200}
2201impl InteractivityFilterArgs {
2202 pub fn new(info: WidgetInfo) -> Self {
2204 Self { info }
2205 }
2206}
2207
2208type InteractivityFilters = Vec<Arc<dyn Fn(&InteractivityFilterArgs) -> Interactivity + Send + Sync>>;
2209
2210bitflags::bitflags! {
2211 #[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2213 #[serde(transparent)]
2214 pub struct Interactivity: u8 {
2215 const ENABLED = 0b00;
2219
2220 const DISABLED = 0b01;
2224
2225 const BLOCKED = 0b10;
2230
2231 const BLOCKED_DISABLED = Self::DISABLED.bits() | Self::BLOCKED.bits();
2233 }
2234}
2235impl Interactivity {
2236 pub fn is_enabled(self) -> bool {
2238 self == Self::ENABLED
2239 }
2240
2241 pub fn is_vis_enabled(self) -> bool {
2243 !self.contains(Self::DISABLED)
2244 }
2245
2246 pub fn is_disabled(self) -> bool {
2248 self == Self::DISABLED
2249 }
2250
2251 pub fn is_vis_disabled(self) -> bool {
2253 self.contains(Self::DISABLED)
2254 }
2255
2256 pub fn is_blocked(self) -> bool {
2258 self.contains(Self::BLOCKED)
2259 }
2260}
2261impl Default for Interactivity {
2262 fn default() -> Self {
2264 Interactivity::ENABLED
2265 }
2266}
2267impl_from_and_into_var! {
2268 fn from(enabled: bool) -> Interactivity {
2271 if enabled { Interactivity::ENABLED } else { Interactivity::DISABLED }
2272 }
2273}
2274impl fmt::Debug for Interactivity {
2275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2276 if self.is_enabled() {
2277 return write!(f, "ENABLED");
2278 }
2279 if *self == Self::BLOCKED_DISABLED {
2280 return write!(f, "BLOCKED_DISABLED");
2281 }
2282 if *self == Self::DISABLED {
2283 return write!(f, "DISABLED");
2284 }
2285 if *self == Self::BLOCKED {
2286 return write!(f, "BLOCKED");
2287 }
2288 write!(f, "Interactivity({:x})", self.bits())
2289 }
2290}
2291
2292#[derive(Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2302pub enum Visibility {
2303 Visible,
2307 Hidden,
2311 Collapsed,
2315}
2316impl Visibility {
2317 pub fn is_visible(self) -> bool {
2319 matches!(self, Self::Visible)
2320 }
2321
2322 pub fn is_hidden(self) -> bool {
2324 matches!(self, Self::Hidden)
2325 }
2326
2327 pub fn is_collapsed(self) -> bool {
2329 matches!(self, Self::Collapsed)
2330 }
2331}
2332impl fmt::Debug for Visibility {
2333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2334 if f.alternate() {
2335 write!(f, "Visibility::")?;
2336 }
2337 match self {
2338 Visibility::Visible => write!(f, "Visible"),
2339 Visibility::Hidden => write!(f, "Hidden"),
2340 Visibility::Collapsed => write!(f, "Collapsed"),
2341 }
2342 }
2343}
2344impl Default for Visibility {
2345 fn default() -> Self {
2347 Visibility::Visible
2348 }
2349}
2350impl ops::BitOr for Visibility {
2351 type Output = Self;
2352
2353 fn bitor(self, rhs: Self) -> Self::Output {
2355 use Visibility::*;
2356 match (self, rhs) {
2357 (Collapsed, _) | (_, Collapsed) => Collapsed,
2358 (Hidden, _) | (_, Hidden) => Hidden,
2359 _ => Visible,
2360 }
2361 }
2362}
2363impl ops::BitOrAssign for Visibility {
2364 fn bitor_assign(&mut self, rhs: Self) {
2365 *self = *self | rhs;
2366 }
2367}
2368impl_from_and_into_var! {
2369 fn from(visible: bool) -> Visibility {
2372 if visible { Visibility::Visible } else { Visibility::Collapsed }
2373 }
2374}
2375
2376#[derive(Clone, PartialEq, Eq, Default)]
2378pub struct WidgetDescendantsRange {
2379 tree: Option<WidgetInfoTree>,
2380 range: std::ops::Range<usize>,
2381}
2382impl WidgetDescendantsRange {
2383 pub fn contains(&self, wgt: &WidgetInfo) -> bool {
2385 self.range.contains(&wgt.node_id.get()) && self.tree.as_ref() == Some(&wgt.tree)
2386 }
2387}
2388
2389#[derive(Clone, Debug)]
2391#[non_exhaustive]
2392pub struct HitInfo {
2393 pub widget_id: WidgetId,
2395
2396 pub z_index: ZIndex,
2398}
2399
2400#[derive(Clone, Debug)]
2402pub struct HitTestInfo {
2403 window_id: WindowId,
2404 frame_id: FrameId,
2405 point: PxPoint,
2406 hits: Vec<HitInfo>,
2407}
2408impl HitTestInfo {
2409 pub fn no_hits(window_id: WindowId) -> Self {
2411 HitTestInfo {
2412 window_id,
2413 frame_id: FrameId::INVALID,
2414 point: PxPoint::new(Px(-1), Px(-1)),
2415 hits: vec![],
2416 }
2417 }
2418
2419 pub fn window_id(&self) -> WindowId {
2421 self.window_id
2422 }
2423
2424 pub fn frame_id(&self) -> FrameId {
2426 self.frame_id
2427 }
2428
2429 pub fn point(&self) -> PxPoint {
2431 self.point
2432 }
2433
2434 pub fn hits(&self) -> &[HitInfo] {
2436 &self.hits
2437 }
2438
2439 pub fn target(&self) -> Option<&HitInfo> {
2441 self.hits.first()
2442 }
2443
2444 pub fn find(&self, widget_id: WidgetId) -> Option<&HitInfo> {
2446 self.hits.iter().find(|h| h.widget_id == widget_id)
2447 }
2448
2449 pub fn contains(&self, widget_id: WidgetId) -> bool {
2451 self.hits.iter().any(|h| h.widget_id == widget_id)
2452 }
2453
2454 pub fn intersection(&self, other: &HitTestInfo) -> HitTestInfo {
2456 let mut hits: Vec<_> = self.hits.iter().filter(|h| other.contains(h.widget_id)).cloned().collect();
2457 hits.shrink_to_fit();
2458
2459 HitTestInfo {
2460 window_id: self.window_id,
2461 frame_id: self.frame_id,
2462 point: self.point,
2463 hits,
2464 }
2465 }
2466}