1use super::super::*;
2use super::{OverlayRootOptions, UiLayer, UiLayerId};
3
4impl<H: UiHost> UiTree<H> {
5 pub fn layer_ids_in_paint_order(&self) -> &[UiLayerId] {
9 self.layer_order.as_slice()
10 }
11
12 pub fn reorder_layers_in_paint_order(&mut self, desired: Vec<UiLayerId>) {
22 if self.layer_order.is_empty() {
23 return;
24 }
25
26 let mut seen = std::collections::HashSet::<UiLayerId>::new();
27 let mut next: Vec<UiLayerId> = Vec::with_capacity(self.layer_order.len());
28
29 for id in desired {
30 if !self.layers.contains_key(id) {
31 continue;
32 }
33 if !seen.insert(id) {
34 continue;
35 }
36 next.push(id);
37 }
38
39 for &id in &self.layer_order {
41 if !self.layers.contains_key(id) {
42 continue;
43 }
44 if seen.insert(id) {
45 next.push(id);
46 }
47 }
48
49 if let Some(base) = self.base_layer {
50 next.retain(|&id| id != base);
51 next.insert(0, base);
52 }
53
54 if next == self.layer_order {
55 return;
56 }
57
58 self.layer_order = next;
59 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
60
61 let (active_roots, barrier_root) = self.active_input_layers();
64 if barrier_root.is_some() {
65 self.enforce_modal_barrier_scope(&active_roots);
66 }
67 let (active_focus_roots, focus_barrier_root) = self.active_focus_layers();
68 if focus_barrier_root.is_some() {
69 self.enforce_focus_barrier_scope(&active_focus_roots);
70 }
71 }
72
73 pub(in crate::tree) fn enforce_modal_barrier_scope(&mut self, active_roots: &[NodeId]) {
74 let (focus_roots, focus_barrier_root) = self.active_focus_layers();
75 if focus_barrier_root.is_some()
76 && self.focus.is_some_and(|n| {
77 !self.is_reachable_from_any_root_via_children(n, focus_roots.as_slice())
78 })
79 {
80 self.set_focus_unchecked(None, "layers: enforce modal barrier scope");
81 }
82 let to_remove: Vec<PointerId> = self
83 .captured
84 .iter()
85 .filter_map(|(p, n)| {
86 (!self.is_reachable_from_any_root_via_children(*n, active_roots)).then_some(*p)
87 })
88 .collect();
89 for p in to_remove {
90 self.captured.remove(&p);
91 }
92 }
93
94 pub(in crate::tree) fn enforce_focus_barrier_scope(&mut self, active_roots: &[NodeId]) {
95 if self
96 .focus
97 .is_some_and(|n| !self.is_reachable_from_any_root_via_children(n, active_roots))
98 {
99 self.set_focus_unchecked(None, "layers: enforce focus barrier scope");
100 }
101 }
102
103 pub(in crate::tree) fn prune_interaction_state_outside_active_layers(
104 &mut self,
105 focus_reason: &'static str,
106 ) {
107 let (active_input_roots, _) = self.active_input_layers();
108 let to_remove: Vec<PointerId> = self
109 .captured
110 .iter()
111 .filter_map(|(pointer, node)| {
112 (!self
113 .is_reachable_from_any_root_via_children(*node, active_input_roots.as_slice()))
114 .then_some(*pointer)
115 })
116 .collect();
117 for pointer in to_remove {
118 self.captured.remove(&pointer);
119 }
120
121 let (active_focus_roots, _) = self.active_focus_layers();
122 if self.focus.is_some_and(|node| {
123 !self.is_reachable_from_any_root_via_children(node, active_focus_roots.as_slice())
124 }) {
125 self.set_focus_unchecked(None, focus_reason);
126 }
127 }
128
129 pub fn base_root(&self) -> Option<NodeId> {
130 self.base_layer
131 .and_then(|id| self.layers.get(id).map(|l| l.root))
132 }
133
134 pub fn set_base_root(&mut self, root: NodeId) -> UiLayerId {
135 if let Some(id) = self.base_layer {
136 self.update_layer_root(id, root);
137 return id;
138 }
139
140 let id = self.layers.insert(UiLayer {
141 root,
142 visible: true,
143 blocks_underlay_input: false,
144 blocks_underlay_focus: false,
145 hit_testable: true,
146 pointer_occlusion: PointerOcclusion::None,
147 wants_pointer_down_outside_events: false,
148 consume_pointer_down_outside_events: false,
149 pointer_down_outside_branches: Vec::new(),
150 scroll_dismiss_elements: Vec::new(),
151 wants_pointer_move_events: false,
152 wants_timer_events: true,
153 });
154 self.root_to_layer.insert(root, id);
155 self.layer_order.insert(0, id);
156 self.base_layer = Some(id);
157 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
158 id
159 }
160
161 pub fn push_overlay_root(&mut self, root: NodeId, blocks_underlay_input: bool) -> UiLayerId {
162 self.push_overlay_root_with_options(root, OverlayRootOptions::new(blocks_underlay_input))
163 }
164
165 pub fn push_overlay_root_with_options(
166 &mut self,
167 root: NodeId,
168 options: OverlayRootOptions,
169 ) -> UiLayerId {
170 let id = self.layers.insert(UiLayer {
171 root,
172 visible: true,
173 blocks_underlay_input: options.blocks_underlay_input,
174 blocks_underlay_focus: options.blocks_underlay_input,
175 hit_testable: options.hit_testable,
176 pointer_occlusion: PointerOcclusion::None,
177 wants_pointer_down_outside_events: false,
178 consume_pointer_down_outside_events: false,
179 pointer_down_outside_branches: Vec::new(),
180 scroll_dismiss_elements: Vec::new(),
181 wants_pointer_move_events: false,
182 wants_timer_events: false,
183 });
184 self.root_to_layer.insert(root, id);
185 self.layer_order.push(id);
186 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
187
188 if options.blocks_underlay_input {
189 let (active_roots, _barrier_root) = self.active_input_layers();
190 self.enforce_modal_barrier_scope(&active_roots);
191 }
192
193 id
194 }
195
196 pub fn remove_layer(
207 &mut self,
208 services: &mut dyn UiServices,
209 layer: UiLayerId,
210 ) -> Option<NodeId> {
211 if self.base_layer == Some(layer) {
212 return None;
213 }
214 let root = self.layers.get(layer).map(|l| l.root)?;
215
216 self.root_to_layer.remove(&root);
219
220 self.layer_order.retain(|&id| id != layer);
221 let _ = self.layers.remove(layer);
222 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
223
224 let mut removed: Vec<NodeId> = Vec::new();
225 self.remove_subtree_inner(services, root, &mut removed);
226
227 Some(root)
228 }
229
230 #[track_caller]
231 pub fn set_layer_visible(&mut self, layer: UiLayerId, visible: bool) {
232 let prev_visible = self.layers.get(layer).map(|l| l.visible);
233 let Some(l) = self.layers.get_mut(layer) else {
234 return;
235 };
236 l.visible = visible;
237
238 if !visible {
239 let to_remove: Vec<fret_core::PointerId> = self
240 .captured
241 .iter()
242 .filter_map(|(p, n)| {
243 (self.node_layer(*n).is_some_and(|lid| lid == layer)).then_some(*p)
244 })
245 .collect();
246 for p in to_remove {
247 self.captured.remove(&p);
248 }
249 if self
250 .focus
251 .is_some_and(|n| self.node_layer(n).is_some_and(|lid| lid == layer))
252 {
253 self.set_focus_unchecked(None, "layers: set_layer_visible(false)");
254 }
255 }
256
257 if prev_visible != Some(visible) {
265 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
266 let (active_roots, barrier_root) = self.active_input_layers();
267 if barrier_root.is_some() {
268 self.enforce_modal_barrier_scope(&active_roots);
269 }
270
271 #[cfg(feature = "diagnostics")]
272 if self.debug_enabled {
273 let caller = std::panic::Location::caller();
274 self.debug_layer_visible_writes
275 .push(UiDebugSetLayerVisibleWrite {
276 layer,
277 frame_id: self.debug_stats.frame_id,
278 prev_visible,
279 visible,
280 file: caller.file(),
281 line: caller.line(),
282 column: caller.column(),
283 });
284 }
285 }
286 }
287
288 pub fn set_layer_hit_testable(&mut self, layer: UiLayerId, hit_testable: bool) {
289 let prev_hit_testable = self.layers.get(layer).map(|l| l.hit_testable);
290 let Some(l) = self.layers.get_mut(layer) else {
291 return;
292 };
293 l.hit_testable = hit_testable;
294
295 if !hit_testable {
296 let to_remove: Vec<fret_core::PointerId> = self
297 .captured
298 .iter()
299 .filter_map(|(p, n)| {
300 (self.node_layer(*n).is_some_and(|lid| lid == layer)).then_some(*p)
301 })
302 .collect();
303 for p in to_remove {
304 self.captured.remove(&p);
305 }
306 if self
307 .focus
308 .is_some_and(|n| self.node_layer(n).is_some_and(|lid| lid == layer))
309 {
310 self.set_focus_unchecked(None, "layers: set_layer_hit_testable(false)");
311 }
312 }
313
314 if prev_hit_testable != Some(hit_testable) {
315 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
316 let (active_roots, barrier_root) = self.active_input_layers();
317 if barrier_root.is_some() {
318 self.enforce_modal_barrier_scope(&active_roots);
319 }
320 }
321 }
322
323 pub fn set_layer_pointer_occlusion(&mut self, layer: UiLayerId, occlusion: PointerOcclusion) {
324 let Some(l) = self.layers.get_mut(layer) else {
325 return;
326 };
327 if l.pointer_occlusion == occlusion {
328 return;
329 }
330 l.pointer_occlusion = occlusion;
331 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
332 }
333
334 pub fn set_layer_blocks_underlay_focus(&mut self, layer: UiLayerId, blocks: bool) {
335 let prev = self.layers.get(layer).map(|l| l.blocks_underlay_focus);
336 let Some(l) = self.layers.get_mut(layer) else {
337 return;
338 };
339 l.blocks_underlay_focus = blocks;
340
341 if prev != Some(blocks) {
342 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
343 let (active_roots, barrier_root) = self.active_focus_layers();
344 if barrier_root.is_some() {
345 self.enforce_focus_barrier_scope(&active_roots);
346 }
347 }
348 }
349
350 pub fn is_layer_visible(&self, layer: UiLayerId) -> bool {
351 self.layers.get(layer).is_some_and(|l| l.visible)
352 }
353
354 pub fn layer_root(&self, layer: UiLayerId) -> Option<NodeId> {
355 self.layers.get(layer).map(|l| l.root)
356 }
357
358 pub(crate) fn all_layer_roots(&self) -> Vec<NodeId> {
359 self.layer_order
360 .iter()
361 .filter_map(|layer| self.layers.get(*layer).map(|l| l.root))
362 .collect()
363 }
364
365 pub fn set_layer_wants_pointer_move_events(&mut self, layer: UiLayerId, wants: bool) {
366 let Some(l) = self.layers.get_mut(layer) else {
367 return;
368 };
369 if l.wants_pointer_move_events == wants {
370 return;
371 }
372 l.wants_pointer_move_events = wants;
373 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
374 }
375
376 pub fn set_layer_wants_pointer_down_outside_events(&mut self, layer: UiLayerId, wants: bool) {
377 let Some(l) = self.layers.get_mut(layer) else {
378 return;
379 };
380 if l.wants_pointer_down_outside_events == wants {
381 return;
382 }
383 l.wants_pointer_down_outside_events = wants;
384 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
385 }
386
387 pub fn set_layer_consume_pointer_down_outside_events(
388 &mut self,
389 layer: UiLayerId,
390 consume: bool,
391 ) {
392 let Some(l) = self.layers.get_mut(layer) else {
393 return;
394 };
395 if l.consume_pointer_down_outside_events == consume {
396 return;
397 }
398 l.consume_pointer_down_outside_events = consume;
399 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
400 }
401
402 pub fn set_layer_pointer_down_outside_branches(
403 &mut self,
404 layer: UiLayerId,
405 branches: Vec<NodeId>,
406 ) {
407 let Some(l) = self.layers.get_mut(layer) else {
408 return;
409 };
410 if l.pointer_down_outside_branches == branches {
411 return;
412 }
413 l.pointer_down_outside_branches = branches;
414 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
415 }
416
417 pub fn set_layer_scroll_dismiss_elements(
423 &mut self,
424 layer: UiLayerId,
425 elements: Vec<crate::GlobalElementId>,
426 ) {
427 let Some(l) = self.layers.get_mut(layer) else {
428 return;
429 };
430 if l.scroll_dismiss_elements == elements {
431 return;
432 }
433 l.scroll_dismiss_elements = elements;
434 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
435 }
436
437 pub fn set_layer_wants_timer_events(&mut self, layer: UiLayerId, wants: bool) {
438 let Some(l) = self.layers.get_mut(layer) else {
439 return;
440 };
441 if l.wants_timer_events == wants {
442 return;
443 }
444 l.wants_timer_events = wants;
445 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
446 }
447
448 pub fn node_layer(&self, node: NodeId) -> Option<UiLayerId> {
449 let root = self.node_root(node)?;
450 self.root_to_layer.get(&root).copied()
451 }
452
453 pub(in crate::tree) fn visible_layers_in_paint_order(
454 &self,
455 ) -> impl Iterator<Item = UiLayerId> + '_ {
456 self.layer_order
457 .iter()
458 .copied()
459 .filter(|id| self.layers.get(*id).is_some_and(|l| l.visible))
460 }
461
462 pub(in crate::tree) fn topmost_pointer_occlusion_layer(
463 &self,
464 barrier_root: Option<NodeId>,
465 ) -> Option<(UiLayerId, PointerOcclusion)> {
466 let mut hit_barrier = false;
467 for &layer_id in self.layer_order.iter().rev() {
468 let Some(layer) = self.layers.get(layer_id) else {
469 continue;
470 };
471 if !layer.visible {
472 continue;
473 }
474 if barrier_root.is_some() && hit_barrier {
475 break;
476 }
477
478 let occlusion = layer.pointer_occlusion;
479 if occlusion != PointerOcclusion::None {
480 return Some((layer_id, occlusion));
481 }
482
483 if barrier_root == Some(layer.root) {
484 hit_barrier = true;
485 }
486 }
487 None
488 }
489
490 pub(in crate::tree) fn active_input_layers(&self) -> (Vec<NodeId>, Option<NodeId>) {
491 let mut any_visible = false;
492 let mut barrier_root: Option<NodeId> = None;
493 for &layer_id in &self.layer_order {
494 let Some(layer) = self.layers.get(layer_id) else {
495 continue;
496 };
497 if !layer.visible {
498 continue;
499 }
500 any_visible = true;
501
502 if layer.blocks_underlay_input {
505 barrier_root = Some(layer.root);
506 }
507 }
508
509 if !any_visible {
510 return (Vec::new(), None);
511 }
512
513 let mut roots: Vec<NodeId> = Vec::new();
514 for &layer_id in self.layer_order.iter().rev() {
515 let Some(layer) = self.layers.get(layer_id) else {
516 continue;
517 };
518 if !layer.visible {
519 continue;
520 }
521
522 if layer.hit_testable || barrier_root == Some(layer.root) {
527 roots.push(layer.root);
528 }
529
530 if barrier_root == Some(layer.root) {
531 break;
532 }
533 }
534 (roots, barrier_root)
535 }
536
537 pub(in crate::tree) fn active_pointer_down_outside_layer_roots(
538 &self,
539 barrier_root: Option<NodeId>,
540 ) -> Vec<NodeId> {
541 let mut any_visible = false;
542 for &layer_id in &self.layer_order {
543 let Some(layer) = self.layers.get(layer_id) else {
544 continue;
545 };
546 if !layer.visible {
547 continue;
548 }
549 any_visible = true;
550 if layer.blocks_underlay_input {
551 break;
552 }
553 }
554
555 if !any_visible {
556 return Vec::new();
557 }
558
559 let mut roots: Vec<NodeId> = Vec::new();
560 for &layer_id in self.layer_order.iter().rev() {
561 let Some(layer) = self.layers.get(layer_id) else {
562 continue;
563 };
564 if !layer.visible {
565 continue;
566 }
567
568 roots.push(layer.root);
569
570 if barrier_root == Some(layer.root) {
571 break;
572 }
573 }
574 roots
575 }
576
577 pub(in crate::tree) fn active_focus_layers(&self) -> (Vec<NodeId>, Option<NodeId>) {
578 let mut any_visible = false;
579 let mut barrier_root: Option<NodeId> = None;
580 let focused_layer = self.focus.and_then(|node| self.node_layer(node));
581 for &layer_id in &self.layer_order {
582 let Some(layer) = self.layers.get(layer_id) else {
583 continue;
584 };
585 if !layer.visible {
586 continue;
587 }
588 any_visible = true;
589
590 if layer.blocks_underlay_focus {
591 barrier_root = Some(layer.root);
592 }
593 }
594
595 if !any_visible {
596 return (Vec::new(), None);
597 }
598
599 let mut roots: Vec<NodeId> = Vec::new();
600 for &layer_id in self.layer_order.iter().rev() {
601 let Some(layer) = self.layers.get(layer_id) else {
602 continue;
603 };
604 if !layer.visible {
605 continue;
606 }
607
608 if layer.hit_testable
617 || barrier_root == Some(layer.root)
618 || focused_layer == Some(layer_id)
619 {
620 roots.push(layer.root);
621 }
622
623 if barrier_root == Some(layer.root) {
624 break;
625 }
626 }
627 (roots, barrier_root)
628 }
629
630 fn update_layer_root(&mut self, layer: UiLayerId, root: NodeId) {
631 let Some(old_root) = self.layers.get(layer).map(|layer| layer.root) else {
632 return;
633 };
634 if old_root == root {
635 return;
636 }
637
638 self.root_to_layer.remove(&old_root);
639 let Some(layer_entry) = self.layers.get_mut(layer) else {
640 return;
641 };
642 layer_entry.root = root;
643 self.root_to_layer.insert(root, layer);
644 self.prune_interaction_state_outside_active_layers("layers: update_layer_root");
645 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
646 }
647}