1mod constraint;
2mod node;
3mod node_map;
4mod pointer;
5mod replay;
6mod scene;
7
8use std::{
9 cell::Cell,
10 collections::{BinaryHeap, VecDeque},
11 num::NonZero,
12 sync::Arc,
13};
14
15use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
16use smallvec::SmallVec;
17use tracing::{debug, warn};
18
19use crate::{
20 NodeId, PxRect,
21 clipboard::{self, ClipboardInput, ClipboardResponse, QueuedClipboardAction},
22 cursor::{CursorEventContent, MOUSE_POINTER_ID, PointerChange},
23 focus::{
24 FocusDirection, FocusHandleId, FocusOwner, PendingFocusCallbackInvocation, bind_focus_owner,
25 },
26 keyboard_state::QueuedInput,
27 layout::{LayoutResult, RenderInput},
28 modifier::{
29 DrawModifierContent, DrawModifierContext, OrderedModifierAction, PointerInputModifierNode,
30 },
31 px::{PxPosition, PxSize},
32 render_graph::RenderGraph,
33 runtime::{
34 LayoutDirtyNodes, RuntimePhase, StructureReconcileResult, TesseraRuntime,
35 push_current_component_instance_key, push_current_node_with_instance_logic_id, push_phase,
36 },
37 time::Instant,
38};
39
40pub use constraint::{AxisConstraint, Constraint, ParentConstraint};
41pub use node::{
42 ComputedData, GloballyPositionedHandler, ImeInput, ImeInputHandlerFn, ImeRequest, ImeSession,
43 KeyboardInput, KeyboardInputHandlerFn, MeasurementError, PointerEventPass, PointerInput,
44 PointerInputHandlerFn,
45};
46
47pub(crate) use node::{
48 ComponentNode, ComponentNodeMetaData, ComponentNodeMetaDatas, ComponentNodeTree, NodeRole,
49 WindowDragRequest, WindowRequests, direct_layout_children, measure_node, sibling_index,
50};
51pub(crate) use scene::SceneRope;
52
53#[cfg(feature = "profiling")]
54use crate::profiler::{NodeMeta, Phase as ProfilerPhase, ScopeGuard as ProfilerScopeGuard};
55
56#[derive(Clone)]
57pub(crate) struct LayoutSnapshotEntry {
58 pub constraint_key: Constraint,
59 pub consumed_insets: crate::Insets,
60 children: node::CachedLayoutChildren,
61 pub layout_result: LayoutResult,
62 pub base_placements: Option<Arc<[(u64, PxPosition)]>>,
63 pub intrinsic_dependencies: bool,
64 pub draw_layout: NodeDrawLayout,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq)]
68pub(crate) struct DrawLayoutBounds {
69 pub position: PxPosition,
70 pub size: ComputedData,
71}
72
73#[derive(Clone, PartialEq)]
74pub(crate) struct NodeDrawLayout {
75 pub layers: Option<Arc<[Option<DrawLayoutBounds>]>>,
78 pub base_constraint: Constraint,
79 pub base_size: ComputedData,
80}
81
82impl Default for NodeDrawLayout {
83 fn default() -> Self {
84 Self {
85 layers: None,
86 base_constraint: Constraint::NONE,
87 base_size: ComputedData::ZERO,
88 }
89 }
90}
91
92#[derive(Default)]
93pub(crate) struct LayoutSnapshotMap {
94 entries: HashMap<u64, Arc<LayoutSnapshotEntry>>,
95 deferred: HashMap<u64, DeferredLayoutInvalidation>,
96}
97
98#[derive(Clone, Default)]
99pub(crate) struct DeferredLayoutInvalidation {
100 pub measure: bool,
101 pub placement: bool,
102 pub children: HashSet<u64>,
103}
104
105impl LayoutSnapshotMap {
106 pub(crate) fn clear(&mut self) {
107 self.entries.clear();
108 self.deferred.clear();
109 }
110
111 pub(crate) fn remove(&mut self, instance_key: &u64) -> Option<Arc<LayoutSnapshotEntry>> {
112 self.deferred.remove(instance_key);
113 self.entries.remove(instance_key)
114 }
115
116 pub(crate) fn insert(
117 &mut self,
118 instance_key: u64,
119 entry: LayoutSnapshotEntry,
120 ) -> Option<Arc<LayoutSnapshotEntry>> {
121 self.deferred.remove(&instance_key);
122 self.entries.insert(instance_key, Arc::new(entry))
123 }
124
125 pub(crate) fn get_cloned(&self, instance_key: &u64) -> Option<Arc<LayoutSnapshotEntry>> {
126 self.entries.get(instance_key).cloned()
127 }
128}
129
130pub(crate) fn nearest_replay_boundary_instance_key(
131 node_id: NodeId,
132 tree: &ComponentNodeTree,
133) -> u64 {
134 let mut current_id = node_id;
135 let mut fallback = 0;
136
137 loop {
138 let Some(node_ref) = tree.get(current_id) else {
139 return fallback;
140 };
141 let node = node_ref.get();
142 if fallback == 0 {
143 fallback = node.instance_key;
144 }
145 if node.replay_boundary {
146 return node.instance_key;
147 }
148 let Some(parent_id) = node_ref.parent() else {
149 return fallback;
150 };
151 current_id = parent_id;
152 }
153}
154
155pub(crate) fn clear_layout_snapshots() {
156 TesseraRuntime::with_mut(|runtime| {
157 runtime.component_tree.clear_layout_snapshots();
158 });
159}
160
161pub(crate) fn flush_pending_globally_positioned_callbacks() {
166 let invocations = TesseraRuntime::with_mut(|runtime| {
167 runtime
168 .component_tree
169 .take_pending_globally_positioned_invocations()
170 });
171 for (handler, rect) in invocations {
172 handler.call(rect);
173 }
174 TesseraRuntime::with_mut(|runtime| runtime.component_tree.focus_owner_mut().finish_layout());
175}
176
177#[derive(Clone, Copy)]
178pub(crate) struct LayoutContext<'a> {
179 pub consumed_insets: &'a Cell<crate::Insets>,
180 pub state_reader_instance_key: Option<u64>,
181 snapshots: *mut LayoutSnapshotMap,
182 dirty_paths: Option<&'a HashMap<NodeId, DirtyLayoutPath>>,
183 diagnostics: *mut LayoutDiagnosticsCollector,
184 pub intrinsic: bool,
185}
186
187struct DirtyLayoutPath {
188 measure: bool,
189 placement: bool,
190 children: SmallVec<[NodeId; 1]>,
191}
192
193#[cfg_attr(not(feature = "profiling"), allow(dead_code))]
194#[derive(Clone, Copy, Debug, Default)]
195pub struct LayoutFrameDiagnostics {
199 pub geometry_nodes_updated: u64,
201 pub geometry_nodes_visited: u64,
204 pub measure_dirty_children_checked: u64,
206 pub pointer_nodes_updated: u64,
208 pub pointer_nodes_dispatched: u64,
210 pub pointer_hit_test_candidates: u64,
212 pub paint_nodes_recorded: u64,
214 pub scene_nodes_updated: u64,
216 pub accessibility_nodes_updated: u64,
218 pub dirty_nodes_param: u64,
220 pub dirty_nodes_structural: u64,
222 pub dirty_nodes_with_ancestors: u64,
224 pub dirty_expand_ns: u64,
226 pub measure_node_calls: u64,
228 pub cache_hits_direct: u64,
230 pub cache_hits_boundary: u64,
232 pub cache_miss_no_entry: u64,
234 pub cache_miss_constraint: u64,
236 pub cache_miss_dirty_self: u64,
238 pub cache_miss_child_size: u64,
240 pub cache_store_count: u64,
242 pub cache_drop_non_cacheable_count: u64,
245}
246
247impl LayoutFrameDiagnostics {
248 pub(crate) fn accumulate_pass(&mut self, pass: Self) {
249 self.geometry_nodes_updated += pass.geometry_nodes_updated;
250 self.geometry_nodes_visited += pass.geometry_nodes_visited;
251 self.measure_dirty_children_checked += pass.measure_dirty_children_checked;
252 self.pointer_nodes_updated += pass.pointer_nodes_updated;
253 self.pointer_nodes_dispatched = pass.pointer_nodes_dispatched;
255 self.pointer_hit_test_candidates += pass.pointer_hit_test_candidates;
256 self.paint_nodes_recorded += pass.paint_nodes_recorded;
257 self.scene_nodes_updated += pass.scene_nodes_updated;
258 self.accessibility_nodes_updated += pass.accessibility_nodes_updated;
259 self.dirty_nodes_param += pass.dirty_nodes_param;
260 self.dirty_nodes_structural += pass.dirty_nodes_structural;
261 self.dirty_nodes_with_ancestors += pass.dirty_nodes_with_ancestors;
262 self.dirty_expand_ns += pass.dirty_expand_ns;
263 self.measure_node_calls += pass.measure_node_calls;
264 self.cache_hits_direct += pass.cache_hits_direct;
265 self.cache_hits_boundary += pass.cache_hits_boundary;
266 self.cache_miss_no_entry += pass.cache_miss_no_entry;
267 self.cache_miss_constraint += pass.cache_miss_constraint;
268 self.cache_miss_dirty_self += pass.cache_miss_dirty_self;
269 self.cache_miss_child_size += pass.cache_miss_child_size;
270 self.cache_store_count += pass.cache_store_count;
271 self.cache_drop_non_cacheable_count += pass.cache_drop_non_cacheable_count;
272 }
273}
274
275#[derive(Default)]
276pub(crate) struct LayoutDiagnosticsCollector {
277 measure_dirty_children_checked: u64,
278 measure_node_calls: u64,
279 cache_hits_direct: u64,
280 cache_hits_boundary: u64,
281 cache_miss_no_entry: u64,
282 cache_miss_constraint: u64,
283 cache_miss_dirty_self: u64,
284 cache_miss_child_size: u64,
285 cache_store_count: u64,
286 cache_drop_non_cacheable_count: u64,
287}
288
289impl LayoutDiagnosticsCollector {
290 fn snapshot(
291 &self,
292 dirty_nodes_param: u64,
293 dirty_nodes_structural: u64,
294 dirty_nodes_with_ancestors: u64,
295 dirty_expand_ns: u64,
296 ) -> LayoutFrameDiagnostics {
297 let cache_hits_direct = self.cache_hits_direct;
298 let cache_hits_boundary = self.cache_hits_boundary;
299 LayoutFrameDiagnostics {
300 geometry_nodes_updated: 0,
301 geometry_nodes_visited: 0,
302 measure_dirty_children_checked: self.measure_dirty_children_checked,
303 pointer_nodes_updated: 0,
304 pointer_nodes_dispatched: 0,
305 pointer_hit_test_candidates: 0,
306 paint_nodes_recorded: 0,
307 scene_nodes_updated: 0,
308 accessibility_nodes_updated: 0,
309 dirty_nodes_param,
310 dirty_nodes_structural,
311 dirty_nodes_with_ancestors,
312 dirty_expand_ns,
313 measure_node_calls: self.measure_node_calls,
314 cache_hits_direct,
315 cache_hits_boundary,
316 cache_miss_no_entry: self.cache_miss_no_entry,
317 cache_miss_constraint: self.cache_miss_constraint,
318 cache_miss_dirty_self: self.cache_miss_dirty_self,
319 cache_miss_child_size: self.cache_miss_child_size,
320 cache_store_count: self.cache_store_count,
321 cache_drop_non_cacheable_count: self.cache_drop_non_cacheable_count,
322 }
323 }
324}
325
326impl LayoutContext<'_> {
327 pub(crate) fn with_consumed_insets<R>(
328 &self,
329 insets: crate::Insets,
330 measure: impl FnOnce() -> R,
331 ) -> R {
332 struct Restore<'a>(&'a Cell<crate::Insets>, crate::Insets);
333 impl Drop for Restore<'_> {
334 fn drop(&mut self) {
335 self.0.set(self.1);
336 }
337 }
338 let _restore = Restore(self.consumed_insets, self.consumed_insets.replace(insets));
339 measure()
340 }
341
342 fn new<'a>(
343 consumed_insets: &'a Cell<crate::Insets>,
344 snapshots: &'a mut LayoutSnapshotMap,
345 dirty_paths: &'a HashMap<NodeId, DirtyLayoutPath>,
346 diagnostics: &'a mut LayoutDiagnosticsCollector,
347 ) -> LayoutContext<'a> {
348 LayoutContext {
349 consumed_insets,
350 state_reader_instance_key: None,
351 snapshots,
352 dirty_paths: Some(dirty_paths),
353 diagnostics,
354 intrinsic: false,
355 }
356 }
357
358 pub(crate) fn intrinsic_probe<'a>(
359 consumed_insets: &'a Cell<crate::Insets>,
360 snapshots: &'a mut LayoutSnapshotMap,
361 diagnostics: &'a mut LayoutDiagnosticsCollector,
362 parent: Option<&'a LayoutContext<'_>>,
363 ) -> LayoutContext<'a> {
364 LayoutContext {
365 consumed_insets,
366 state_reader_instance_key: crate::runtime::current_measured_node_instance_key(),
367 snapshots,
368 dirty_paths: None,
369 diagnostics: parent.map_or(diagnostics, |parent| parent.diagnostics),
370 intrinsic: true,
371 }
372 }
373
374 #[inline]
375 pub(crate) fn snapshot(&self, instance_key: u64) -> Option<Arc<LayoutSnapshotEntry>> {
376 unsafe { (*self.snapshots).get_cloned(&instance_key) }
380 }
381
382 #[inline]
383 pub(crate) fn deferred_invalidation(
384 &self,
385 instance_key: u64,
386 ) -> Option<DeferredLayoutInvalidation> {
387 unsafe { (*self.snapshots).deferred.get(&instance_key).cloned() }
389 }
390
391 #[inline]
392 pub(crate) fn acknowledge_deferred_invalidation(&self, instance_key: u64) {
393 unsafe {
395 (*self.snapshots).deferred.remove(&instance_key);
396 }
397 }
398
399 #[inline]
400 pub(crate) fn insert_snapshot(&self, instance_key: u64, entry: LayoutSnapshotEntry) {
401 unsafe {
403 (*self.snapshots).insert(instance_key, entry);
404 }
405 }
406
407 #[inline]
408 pub(crate) fn remove_snapshot(&self, instance_key: u64) {
409 unsafe {
411 (*self.snapshots).remove(&instance_key);
412 }
413 }
414
415 #[inline]
416 pub(crate) fn inc_measure_node_calls(&self) {
417 unsafe {
422 (*self.diagnostics).measure_node_calls += 1;
423 }
424 }
425
426 pub(crate) fn inc_dirty_child_checked(&self) {
427 unsafe {
429 (*self.diagnostics).measure_dirty_children_checked += 1;
430 }
431 }
432
433 #[inline]
434 pub(crate) fn inc_cache_hit_direct(&self) {
435 unsafe {
436 (*self.diagnostics).cache_hits_direct += 1;
437 }
438 }
439
440 #[inline]
441 pub(crate) fn inc_cache_hit_boundary(&self) {
442 unsafe {
443 (*self.diagnostics).cache_hits_boundary += 1;
444 }
445 }
446
447 #[inline]
448 pub(crate) fn inc_cache_miss_no_entry(&self) {
449 unsafe {
450 (*self.diagnostics).cache_miss_no_entry += 1;
451 }
452 }
453
454 #[inline]
455 pub(crate) fn inc_cache_miss_constraint(&self) {
456 unsafe {
457 (*self.diagnostics).cache_miss_constraint += 1;
458 }
459 }
460
461 #[inline]
462 pub(crate) fn inc_cache_miss_dirty_self(&self) {
463 unsafe {
464 (*self.diagnostics).cache_miss_dirty_self += 1;
465 }
466 }
467
468 #[inline]
469 pub(crate) fn inc_cache_miss_child_size(&self) {
470 unsafe {
471 (*self.diagnostics).cache_miss_child_size += 1;
472 }
473 }
474
475 #[inline]
476 pub(crate) fn inc_cache_store_count(&self) {
477 unsafe {
478 (*self.diagnostics).cache_store_count += 1;
479 }
480 }
481
482 #[inline]
483 pub(crate) fn inc_cache_drop_non_cacheable_count(&self) {
484 unsafe {
485 (*self.diagnostics).cache_drop_non_cacheable_count += 1;
486 }
487 }
488}
489
490pub(crate) struct ComputeParams {
492 pub screen_size: PxSize,
493 pub cursor_position: Option<PxPosition>,
494 pub pointer_changes: Vec<PointerChange>,
495 pub keyboard_events: Vec<QueuedInput>,
496 pub ime_events: Vec<QueuedInput<crate::ime_state::ImeEvent>>,
497 pub retry_focus_move: Option<FocusDirection>,
498 pub retry_focus_reveal: bool,
499 pub modifiers: winit::keyboard::ModifiersState,
500 pub layout_dirty_nodes: LayoutDirtyNodes,
501}
502
503#[derive(Debug)]
504pub(crate) enum ComputeMode {
505 Full,
506 #[cfg(any(test, feature = "testing"))]
507 LayoutOnly,
508}
509
510struct GlobalPositionSnapshot {
511 geometry: node::GeometryObservation,
512 handlers: SmallVec<[GloballyPositionedHandler; 2]>,
513}
514
515enum QueuedFocusedInput {
516 Keyboard(QueuedInput),
517 Clipboard(QueuedClipboardAction),
518 Ime(QueuedInput<crate::ime_state::ImeEvent>),
519}
520
521impl QueuedFocusedInput {
522 fn sequence(&self) -> u64 {
523 match self {
524 Self::Keyboard(event) => event.sequence,
525 Self::Clipboard(event) => event.sequence,
526 Self::Ime(event) => event.sequence,
527 }
528 }
529}
530
531pub struct ComponentTree {
533 tree: indextree::Arena<ComponentNode>,
535 metadatas: ComponentNodeMetaDatas,
537 layout_snapshots: LayoutSnapshotMap,
538 frame_scene: scene::FrameScene,
539 pointer_index: pointer::PointerIndex,
540 accessibility_index: crate::accessibility::AccessibilityIndex,
541 #[cfg(feature = "profiling")]
542 profiler_nodes_cache: HashMap<NodeId, NodeMeta>,
543 #[cfg(feature = "profiling")]
544 profiler_dirty_nodes: HashSet<NodeId>,
545 scale_factor_bits: u64,
546 node_queue: Vec<indextree::NodeId>,
548 replay_range: Option<ReplayRange>,
549 replay_insertions: ReplayInsertions,
550 replay_ancestry: replay::ReplayAncestry,
551 replay_changed_parents: HashSet<NodeId>,
552 node_ids_by_instance_key: HashMap<u64, indextree::NodeId>,
553 changed_frame_nodes: HashSet<NodeId>,
554 removed_frame_nodes: HashSet<NodeId>,
555 previous_structure_change_counts: [usize; 2],
556 removed_focus_owners: HashSet<u64>,
557 active_pointer_paths: HashMap<u64, Vec<u64>>,
560 active_pointer_positions: HashMap<u64, Option<PxPosition>>,
561 pointer_work: (u64, HashSet<u64>),
562 focus_owner: FocusOwner,
564 pending_focused_input: VecDeque<QueuedFocusedInput>,
565 last_global_rects: HashMap<u64, GlobalPositionSnapshot>,
568 observer_viewport: Option<PxSize>,
569 position_observer_nodes: HashSet<NodeId>,
570 window_observer_nodes: HashSet<NodeId>,
571 pending_position_observers: HashSet<NodeId>,
572 observer_spaces: HashMap<NodeId, u64>,
573 space_observers: HashMap<u64, HashSet<NodeId>>,
574 pending_globally_positioned_invocations:
577 Vec<(GloballyPositionedHandler, node::GeometryObservation)>,
578}
579
580#[derive(Clone, PartialEq)]
581pub(crate) enum ReplayReplaceError {
582 NodeNotFound,
583 RootNodeNotReplaceable,
584 ReplayDidNotCreateRoot,
585}
586
587#[derive(Default)]
588pub(crate) struct ReplayReplaceResult {
589 pub removed_instance_keys: HashSet<u64>,
590 pub removed_instance_logic_ids: HashSet<u64>,
591 insertions: ReplayInsertions,
592}
593
594impl ReplayReplaceResult {
595 fn clear(&mut self) {
596 clear_replay_set(&mut self.removed_instance_keys);
597 clear_replay_set(&mut self.removed_instance_logic_ids);
598 self.insertions.clear();
599 }
600
601 pub(crate) fn executed_keys(&self) -> impl Iterator<Item = u64> + '_ {
602 self.insertions
603 .rebuilt
604 .iter()
605 .filter_map(|&(key, _)| (!self.insertions.retained.keys.contains(&key)).then_some(key))
606 }
607
608 pub(crate) fn recomposed_logic_ids(&self) -> impl Iterator<Item = u64> + '_ {
609 self.insertions.rebuilt.iter().filter_map(|&(_, logic_id)| {
610 (!self.insertions.retained.logic_ids.contains(&logic_id)).then_some(logic_id)
611 })
612 }
613
614 pub(crate) fn retained_count(&self) -> usize {
615 self.insertions.retained.keys.len()
616 }
617
618 pub(crate) fn inserted_count(&self) -> usize {
619 self.insertions.rebuilt.len() + self.retained_count()
620 }
621}
622
623const MAX_REPLAY_SCRATCH_CAPACITY: usize = 256;
625const INLINE_REPLAY_INSERTIONS: usize = 8;
626
627fn clear_replay_set(set: &mut HashSet<u64>) {
628 if set.capacity() > MAX_REPLAY_SCRATCH_CAPACITY {
629 *set = HashSet::default();
630 } else {
631 set.clear();
632 }
633}
634
635#[derive(Default)]
636struct ReplayIdentities {
637 keys: HashSet<u64>,
638 logic_ids: HashSet<u64>,
639}
640
641impl ReplayIdentities {
642 fn insert(&mut self, key: u64, logic_id: u64) {
643 self.keys.insert(key);
644 self.logic_ids.insert(logic_id);
645 }
646}
647
648#[derive(Default)]
649struct ReplayInsertions {
650 rebuilt: SmallVec<[(u64, u64); INLINE_REPLAY_INSERTIONS]>,
651 retained: ReplayIdentities,
652}
653
654impl ReplayInsertions {
655 fn clear(&mut self) {
656 if self.rebuilt.capacity() > MAX_REPLAY_SCRATCH_CAPACITY {
657 self.rebuilt = SmallVec::new();
658 } else {
659 self.rebuilt.clear();
660 }
661 clear_replay_set(&mut self.retained.keys);
662 clear_replay_set(&mut self.retained.logic_ids);
663 }
664}
665
666struct ReplayRange {
667 parent: NodeId,
668 next_sibling: Option<NodeId>,
669}
670
671pub(crate) struct ReplayReplaceContext {
672 parent_id: indextree::NodeId,
673 previous_sibling: Option<indextree::NodeId>,
674 previous_queue: Option<Vec<indextree::NodeId>>,
675}
676
677pub(crate) struct PreviousBuildNode {
678 pub(crate) node: ComponentNode,
679 same_parent: bool,
680 defer_consumer_change: bool,
681}
682
683fn has_frame_consumers(node: &ComponentNode) -> bool {
684 node.role == NodeRole::Layout
685 || node.interaction.is_some()
686 || !node.modifier.is_empty()
687 || node.layout_coordinate_space
688 || node.children_scale != 1.0
689}
690
691impl Default for ComponentTree {
692 fn default() -> Self {
693 Self::new()
694 }
695}
696
697impl ComponentTree {
698 pub fn new() -> Self {
700 let tree = indextree::Arena::new();
701 let node_queue = Vec::new();
702 let metadatas = ComponentNodeMetaDatas::new();
703 Self {
704 tree,
705 node_queue,
706 metadatas,
707 layout_snapshots: LayoutSnapshotMap::default(),
708 frame_scene: scene::FrameScene::default(),
709 pointer_index: pointer::PointerIndex::default(),
710 accessibility_index: crate::accessibility::AccessibilityIndex::default(),
711 #[cfg(feature = "profiling")]
712 profiler_nodes_cache: HashMap::default(),
713 #[cfg(feature = "profiling")]
714 profiler_dirty_nodes: HashSet::default(),
715 scale_factor_bits: crate::dp::scale_factor().to_bits(),
716 replay_range: None,
717 replay_insertions: ReplayInsertions::default(),
718 replay_ancestry: replay::ReplayAncestry::default(),
719 replay_changed_parents: HashSet::default(),
720 node_ids_by_instance_key: HashMap::default(),
721 changed_frame_nodes: HashSet::default(),
722 removed_frame_nodes: HashSet::default(),
723 previous_structure_change_counts: [0; 2],
724 removed_focus_owners: HashSet::default(),
725 active_pointer_paths: HashMap::default(),
726 active_pointer_positions: HashMap::default(),
727 pointer_work: (0, HashSet::default()),
728 focus_owner: FocusOwner::new(),
729 pending_focused_input: VecDeque::new(),
730 last_global_rects: HashMap::default(),
731 observer_viewport: None,
732 position_observer_nodes: HashSet::default(),
733 window_observer_nodes: HashSet::default(),
734 pending_position_observers: HashSet::default(),
735 observer_spaces: HashMap::default(),
736 space_observers: HashMap::default(),
737 pending_globally_positioned_invocations: Vec::new(),
738 }
739 }
740
741 pub fn clear(&mut self) {
743 crate::runtime::remove_layout_structure(
744 &self.node_ids_by_instance_key.keys().copied().collect(),
745 );
746 self.removed_focus_owners
747 .extend(self.node_ids_by_instance_key.keys().copied());
748 self.removed_frame_nodes
749 .extend(self.node_ids_by_instance_key.values().copied());
750 self.changed_frame_nodes.clear();
751 self.previous_structure_change_counts = [0; 2];
752 #[cfg(feature = "profiling")]
753 {
754 self.profiler_nodes_cache.clear();
755 self.profiler_dirty_nodes.clear();
756 }
757 self.tree.clear();
758 self.metadatas.clear();
759 self.layout_snapshots.clear();
760 self.frame_scene = scene::FrameScene::default();
761 self.pointer_index = pointer::PointerIndex::default();
762 self.stop_accessibility_updates();
763 self.node_queue.clear();
764 self.replay_range = None;
765 self.replay_insertions.clear();
766 self.replay_ancestry = replay::ReplayAncestry::default();
767 self.replay_changed_parents.clear();
768 self.node_ids_by_instance_key.clear();
769 self.last_global_rects.clear();
770 self.observer_viewport = None;
771 self.position_observer_nodes.clear();
772 self.window_observer_nodes.clear();
773 self.pending_position_observers.clear();
774 self.observer_spaces.clear();
775 self.space_observers.clear();
776 self.pending_globally_positioned_invocations.clear();
777 }
778
779 pub fn reset(&mut self) {
781 self.clear();
782 self.active_pointer_paths.clear();
783 self.active_pointer_positions.clear();
784 self.pointer_work.1.clear();
785 self.focus_owner.reset();
786 self.pending_focused_input.clear();
787 clipboard::clear_requests();
788 }
789
790 pub(crate) fn begin_frame_diagnostics(&mut self) {
791 self.pointer_work.1.clear();
792 }
793
794 pub(crate) fn refresh_scale_factor(&mut self) -> bool {
795 let current = crate::dp::scale_factor().to_bits();
796 let changed = self.scale_factor_bits != current;
797 self.scale_factor_bits = current;
798 changed
799 }
800
801 pub(crate) fn get(&self, node_id: indextree::NodeId) -> Option<&ComponentNode> {
803 self.tree
804 .get(node_id)
805 .filter(|node| !node.is_removed())
806 .map(|node| node.get())
807 }
808
809 pub(crate) fn get_mut(&mut self, node_id: indextree::NodeId) -> Option<&mut ComponentNode> {
811 self.tree
812 .get_mut(node_id)
813 .filter(|node| !node.is_removed())
814 .map(|node| node.get_mut())
815 }
816
817 pub(crate) fn find_node_id_by_instance_key(
819 &self,
820 instance_key: u64,
821 ) -> Option<indextree::NodeId> {
822 self.node_ids_by_instance_key.get(&instance_key).copied()
823 }
824
825 fn sync_accessibility(&mut self, root: NodeId, immediate: bool) -> usize {
826 self.accessibility_index
827 .invalidate_geometry(&self.metadatas.geometry_changed);
828 self.accessibility_index
829 .invalidate_placement_order(&self.metadatas.placement_order_changed);
830 self.accessibility_index
831 .invalidate(&self.metadatas.paint_changed, &HashSet::default());
832 if immediate {
833 self.accessibility_index
834 .update(&self.tree, &self.metadatas, root)
835 } else {
836 0
837 }
838 }
839
840 pub(crate) fn accessibility_update(
841 &mut self,
842 label: Option<&str>,
843 full: bool,
844 ) -> Option<accesskit::TreeUpdate> {
845 let root = self.tree.get_node_id_at(NonZero::new(1)?)?;
846 self.accessibility_index.set_label(label, root);
847 self.accessibility_index
848 .update(&self.tree, &self.metadatas, root);
849 self.accessibility_index.tree_update(
850 &self.tree,
851 self.focus_owner.active_component_node_id(),
852 full,
853 )
854 }
855
856 pub(crate) fn stop_accessibility_updates(&mut self) {
857 self.accessibility_index = crate::accessibility::AccessibilityIndex::default();
858 }
859
860 pub(crate) fn take_removed_focus_owners(&mut self) -> HashSet<u64> {
861 std::mem::take(&mut self.removed_focus_owners)
862 }
863
864 #[cfg(test)]
865 pub(crate) fn begin_replace_subtree_by_instance_key(
866 &mut self,
867 instance_key: u64,
868 ) -> Result<ReplayReplaceContext, ReplayReplaceError> {
869 let Some(target_node_id) = self.find_node_id_by_instance_key(instance_key) else {
870 return Err(ReplayReplaceError::NodeNotFound);
871 };
872 self.begin_replace_subtree(target_node_id)
873 }
874
875 pub(crate) fn begin_replace_subtree(
876 &mut self,
877 target_node_id: NodeId,
878 ) -> Result<ReplayReplaceContext, ReplayReplaceError> {
879 let node = self
880 .tree
881 .get(target_node_id)
882 .filter(|node| !node.is_removed())
883 .ok_or(ReplayReplaceError::NodeNotFound)?;
884 let parent_id = node
885 .parent()
886 .ok_or(ReplayReplaceError::RootNodeNotReplaceable)?;
887
888 let previous_sibling = target_node_id.previous_sibling(&self.tree);
889 let next_sibling = target_node_id.next_sibling(&self.tree);
890 self.replay_range = Some(ReplayRange {
891 parent: parent_id,
892 next_sibling,
893 });
894
895 self.queue_replay_children(parent_id, target_node_id, next_sibling);
896
897 let previous_queue =
898 (!self.node_queue.is_empty()).then(|| std::mem::take(&mut self.node_queue));
899 self.node_queue.push(parent_id);
900 Ok(ReplayReplaceContext {
901 parent_id,
902 previous_sibling,
903 previous_queue,
904 })
905 }
906
907 pub(crate) fn finish_replace_subtree(
908 &mut self,
909 context: ReplayReplaceContext,
910 result: &mut ReplayReplaceResult,
911 ) -> Result<(), ReplayReplaceError> {
912 result.clear();
913 let ReplayReplaceContext {
914 parent_id,
915 previous_sibling,
916 previous_queue,
917 } = context;
918 if let Some(previous_queue) = previous_queue {
919 self.restore_build_queue(previous_queue);
920 } else {
921 self.node_queue.clear();
922 }
923
924 self.detach_replay_suffix(parent_id);
925 let next_sibling = self
926 .replay_range
927 .take()
928 .and_then(|range| range.next_sibling);
929 let first_inserted = match previous_sibling {
930 Some(child) => child.next_sibling(&self.tree),
931 None => parent_id.first_child(&self.tree),
932 };
933 if first_inserted.is_none() || first_inserted == next_sibling {
934 self.replay_ancestry = replay::ReplayAncestry::default();
935 self.replay_insertions.clear();
936 return Err(ReplayReplaceError::ReplayDidNotCreateRoot);
937 }
938
939 std::mem::swap(&mut self.replay_insertions, &mut result.insertions);
940 let ReplayReplaceResult {
941 removed_instance_keys,
942 removed_instance_logic_ids,
943 insertions,
944 } = result;
945 let rebuilt = &insertions.rebuilt;
946 let retained = &insertions.retained;
947 let rebuilt_index = (!self.replay_ancestry.detached_roots.is_empty()
948 && rebuilt.len() > INLINE_REPLAY_INSERTIONS)
949 .then(|| {
950 let mut identities = ReplayIdentities::default();
951 for &(key, logic_id) in rebuilt {
952 identities.insert(key, logic_id);
953 }
954 identities
955 });
956
957 let mut detached_node_ids = SmallVec::<[_; 8]>::new();
958 for &root in self.replay_ancestry.detached_roots.keys() {
959 detached_node_ids.extend(root.descendants(&self.tree));
960 }
961 for &old_id in &detached_node_ids {
962 let old = &self.tree[old_id];
963 let instance_key = old.get().instance_key;
964 let instance_logic_id = old.get().instance_logic_id;
965 let previous = self.metadatas.remove(&old_id);
966 let logic_id_survives = retained.logic_ids.contains(&instance_logic_id)
967 || rebuilt_index.as_ref().map_or_else(
968 || rebuilt.iter().any(|&(_, id)| id == instance_logic_id),
969 |index| index.logic_ids.contains(&instance_logic_id),
970 );
971 let key_survives = retained.keys.contains(&instance_key)
972 || rebuilt_index.as_ref().map_or_else(
973 || rebuilt.iter().any(|&(key, _)| key == instance_key),
974 |index| index.keys.contains(&instance_key),
975 );
976 if !logic_id_survives {
977 removed_instance_logic_ids.insert(instance_logic_id);
978 }
979 if !key_survives {
980 removed_instance_keys.insert(instance_key);
981 } else if instance_key != 0
982 && let Some(previous) = previous
983 {
984 let new_id = self.node_ids_by_instance_key[&instance_key];
985 let new = &self.tree[new_id];
986 let input_geometry_changed =
987 !old.get().modifier.input_geometry_eq(&new.get().modifier);
988 let old_parent = old
989 .parent()
990 .or_else(|| self.replay_ancestry.detached_roots.get(&old_id).copied());
991 let parent_key = |id: NodeId| self.tree[id].get().instance_key;
992 let same_parent = old_parent.map(parent_key) == new.parent().map(parent_key);
993 let metadata = self.metadatas.entry_or_default(new_id);
994 metadata.computed_data = previous.computed_data;
995 metadata.draw_layout = previous.draw_layout;
996 metadata.rel_position = previous.rel_position;
997 metadata.placement_order = previous.placement_order;
998 metadata.base_abs_position = previous.base_abs_position;
999 metadata.local_to_window = previous.local_to_window;
1000 metadata.input_transforms = previous.input_transforms;
1001 metadata.event_clip_bounds = previous.event_clip_bounds;
1002 metadata.abs_position = previous.abs_position;
1003 metadata.prepared_layout = previous.prepared_layout;
1004 metadata.clip_regions = previous.clip_regions;
1005 metadata.geometry_initialized =
1006 previous.geometry_initialized && !input_geometry_changed;
1007 metadata.clips_children = previous.clips_children;
1008 metadata.opacity = previous.opacity;
1009 metadata.has_shape_clipping = previous.has_shape_clipping;
1010 if same_parent && metadata.geometry_initialized {
1011 self.metadatas.geometry_dirty.remove(&new_id);
1012 }
1013 }
1014 self.remove_position_observer(old_id);
1015 self.removed_frame_nodes.insert(old_id);
1016 self.changed_frame_nodes.remove(&old_id);
1017 }
1018 for key in removed_instance_keys.iter() {
1019 self.last_global_rects.remove(key);
1020 self.node_ids_by_instance_key.remove(key);
1021 }
1022 self.removed_focus_owners
1023 .extend(removed_instance_keys.iter().copied());
1024 crate::runtime::remove_layout_structure(removed_instance_keys);
1025 for &root in self.replay_ancestry.detached_roots.keys() {
1026 root.remove_subtree(&mut self.tree);
1027 }
1028 self.replay_ancestry.clear_detached();
1029
1030 self.replay_changed_parents.insert(parent_id);
1031 Ok(())
1032 }
1033
1034 pub(crate) fn finish_replay_round(&mut self) {
1035 let mut scheduled = std::mem::take(&mut self.replay_changed_parents);
1036 let mut pending: BinaryHeap<_> = scheduled
1038 .iter()
1039 .map(|&id| (id.ancestors(&self.tree).count(), id))
1040 .collect();
1041 while let Some((depth, id)) = pending.pop() {
1042 #[cfg(feature = "profiling")]
1043 let previous_len = self.tree[id].get().subtree_len;
1044 let changed = self.refresh_changed_layout_children(id);
1045 let node = &self.tree[id];
1046 let propagate = changed && node.get().role == NodeRole::Composition;
1047 #[cfg(feature = "profiling")]
1048 let propagate = propagate || previous_len != node.get().subtree_len;
1049 if propagate
1050 && let Some(parent) = node.parent()
1051 && scheduled.insert(parent)
1052 {
1053 pending.push((depth - 1, parent));
1054 }
1055 }
1056 scheduled.clear();
1057 self.replay_changed_parents = scheduled;
1058 self.finish_replay_ancestry();
1059 }
1060
1061 pub(crate) fn replace_build_parent(&mut self, parent: Option<NodeId>) -> Option<Vec<NodeId>> {
1062 if self.node_queue.last().copied() == parent {
1063 None
1064 } else {
1065 Some(std::mem::replace(
1066 &mut self.node_queue,
1067 parent.into_iter().collect(),
1068 ))
1069 }
1070 }
1071
1072 pub(crate) fn restore_build_queue(&mut self, queue: Vec<NodeId>) {
1073 self.node_queue = queue;
1074 }
1075
1076 pub(crate) fn current_build_node_id(&self) -> Option<NodeId> {
1077 self.node_queue.last().copied()
1078 }
1079
1080 pub(crate) fn current_node(&self) -> Option<&ComponentNode> {
1082 self.node_queue
1083 .last()
1084 .and_then(|node_id| self.get(*node_id))
1085 }
1086
1087 pub(crate) fn current_node_mut(&mut self) -> Option<&mut ComponentNode> {
1089 let node_id = self.node_queue.last()?;
1090 self.get_mut(*node_id)
1091 }
1092
1093 pub(crate) fn attach_retained_subtree(
1094 &mut self,
1095 instance_key: u64,
1096 instance_logic_id: u64,
1097 ) -> Option<NodeId> {
1098 let candidate = self.claim_replay_candidate(instance_key, |node| {
1099 node.instance_logic_id == instance_logic_id
1100 })?;
1101 let id = candidate.id;
1102 self.capture_reused_ancestry(id);
1103 self.attach_replayed_node(candidate, true);
1104 Some(id)
1105 }
1106
1107 pub(crate) fn add_node(&mut self, node_component: ComponentNode) -> indextree::NodeId {
1112 let instance_key = node_component.instance_key;
1113 let needs_geometry = node_component.role == NodeRole::Layout || self.node_queue.is_empty();
1114 let new_node_id = self.tree.new_node(node_component);
1115 if instance_key != 0 || self.node_queue.is_empty() {
1116 self.node_ids_by_instance_key
1117 .insert(instance_key, new_node_id);
1118 }
1119 if needs_geometry {
1120 self.metadatas.entry_or_default(new_node_id);
1121 }
1122 self.attach_build_node(new_node_id, false);
1123 new_node_id
1124 }
1125
1126 pub(crate) fn rebuild_node(
1127 &mut self,
1128 mut node: ComponentNode,
1129 ) -> (NodeId, Option<PreviousBuildNode>) {
1130 let reusable = self.claim_replay_candidate(node.instance_key, |old| {
1131 old.role == node.role && old.instance_logic_id == node.instance_logic_id
1132 });
1133 let Some(candidate) = reusable else {
1134 return (self.add_node(node), None);
1135 };
1136 let id = candidate.id;
1137
1138 self.preserve_replay_children(id);
1139 let same_parent = candidate.keeps_link || {
1140 let previous_parent = self.tree[id]
1141 .parent()
1142 .or_else(|| self.replay_ancestry.detached_roots.get(&id).copied());
1143 let new_parent = self.current_build_node_id();
1144 let parent_key = |id: NodeId| self.tree[id].get().instance_key;
1145 previous_parent.map(parent_key) == new_parent.map(parent_key)
1146 };
1147 self.remove_position_observer(id);
1148 let old = self.tree[id].get_mut();
1149 node.layout_children = old.layout_children.take();
1150 node.layout_children_dirty = old.layout_children_dirty;
1151 node.sibling_index = old.sibling_index;
1152 #[cfg(feature = "profiling")]
1153 {
1154 node.subtree_len = old.subtree_len;
1155 }
1156 let previous = std::mem::replace(old, node);
1157 let metadata = self.metadatas.get_mut(&id);
1158 let defer_consumer_change =
1159 candidate.keeps_link && metadata.is_none() && !has_frame_consumers(&previous);
1160 if let Some(metadata) = metadata {
1161 metadata.begin_accessibility_rebuild();
1162 }
1163 if defer_consumer_change {
1164 self.enter_build_node(id, false, false);
1165 } else {
1166 self.attach_replayed_node(candidate, false);
1167 }
1168 if previous.role == NodeRole::Layout {
1169 self.metadatas.mark_paint_dirty(id);
1170 } else {
1171 for descendant in id.descendants(&self.tree) {
1174 if self.tree[descendant].get().role == NodeRole::Layout {
1175 self.metadatas.mark_paint_dirty(descendant);
1176 }
1177 }
1178 }
1179 (
1180 id,
1181 Some(PreviousBuildNode {
1182 node: previous,
1183 same_parent,
1184 defer_consumer_change,
1185 }),
1186 )
1187 }
1188
1189 pub(crate) fn reconcile_build_node(&mut self, id: NodeId, previous: &PreviousBuildNode) {
1190 let metadata = self.metadatas.get_mut(&id);
1191 let node = self.tree[id].get();
1192 if previous.defer_consumer_change
1193 && (metadata.is_some()
1194 || has_frame_consumers(node)
1195 || previous.node.fn_name != node.fn_name)
1196 {
1197 self.changed_frame_nodes.insert(id);
1198 }
1199 let Some(metadata) = metadata else {
1200 return;
1201 };
1202 metadata.finish_accessibility_rebuild();
1203 let input_changed = !previous.node.modifier.input_geometry_eq(&node.modifier);
1204 metadata.geometry_initialized &= !input_changed;
1205 if input_changed
1206 || !previous.same_parent
1207 || previous.node.children_scale != node.children_scale
1208 {
1209 self.metadatas.mark_geometry_dirty(id);
1210 }
1211 }
1212
1213 fn attach_replayed_node(&mut self, candidate: replay::ReplayCandidate, retained: bool) {
1214 let replay::ReplayCandidate { id, keeps_link } = candidate;
1215 if keeps_link {
1216 self.enter_build_node(id, retained, true);
1217 return;
1218 }
1219 self.replay_ancestry.detached_roots.remove(&id);
1220 if let Some(parent) = self.tree[id].parent() {
1221 self.tree[parent].get_mut().layout_children_dirty = true;
1222 }
1223 id.detach(&mut self.tree);
1224 self.attach_build_node(id, retained);
1225 }
1226
1227 fn attach_build_node(&mut self, id: NodeId, retained: bool) {
1228 if let Some(&parent) = self.node_queue.last() {
1229 self.tree[parent].get_mut().layout_children_dirty = true;
1230 let next = self.next_replay_child(parent).or_else(|| {
1231 self.replay_range
1232 .as_ref()
1233 .filter(|range| parent == range.parent)
1234 .and_then(|range| range.next_sibling)
1235 });
1236 if let Some(sibling) = next {
1237 sibling.insert_before(id, &mut self.tree);
1238 } else {
1239 parent.append(id, &mut self.tree);
1240 }
1241 }
1242 self.enter_build_node(id, retained, true);
1243 }
1244
1245 fn enter_build_node(&mut self, id: NodeId, retained: bool, consumer_changed: bool) {
1246 if self.replay_range.is_some() {
1247 let node = self.tree[id].get();
1248 if retained {
1249 self.replay_insertions
1250 .retained
1251 .insert(node.instance_key, node.instance_logic_id);
1252 } else {
1253 self.replay_insertions
1254 .rebuilt
1255 .push((node.instance_key, node.instance_logic_id));
1256 }
1257 }
1258 if consumer_changed {
1259 self.changed_frame_nodes.insert(id);
1260 }
1261 self.node_queue.push(id);
1262 }
1263
1264 pub(crate) fn pop_node(&mut self) {
1266 if let Some(node_id) = self.node_queue.pop() {
1267 self.detach_replay_suffix(node_id);
1268 self.refresh_changed_layout_children(node_id);
1269 let node = self.tree[node_id].get();
1270 let interaction = node.interaction();
1271 let position_observers = &interaction.globally_positioned_handlers;
1272 let observes_window = position_observers
1273 .iter()
1274 .any(|handler| !matches!(handler, GloballyPositionedHandler::Size(_)));
1275 if let Some(metadata) = self.metadatas.get_mut(&node_id) {
1276 metadata.observes_size = position_observers
1277 .iter()
1278 .any(|handler| matches!(handler, GloballyPositionedHandler::Size(_)));
1279 metadata.observes_geometry = interaction.observes_input_geometry()
1280 || observes_window
1281 || node.layout_coordinate_space
1282 || node.modifier.has_input_geometry();
1283 }
1284 if !position_observers.is_empty() {
1285 self.position_observer_nodes.insert(node_id);
1286 self.pending_position_observers.insert(node_id);
1287 if observes_window {
1288 self.window_observer_nodes.insert(node_id);
1289 }
1290 }
1291 }
1292 }
1293
1294 fn refresh_changed_layout_children(&mut self, node_id: NodeId) -> bool {
1295 if !self.tree[node_id].get().layout_children_dirty {
1296 return false;
1297 }
1298 self.refresh_layout_children(node_id)
1299 }
1300
1301 fn refresh_layout_children(&mut self, node_id: indextree::NodeId) -> bool {
1302 #[cfg(feature = "profiling")]
1303 let previous_subtree_len = self.tree[node_id].get().subtree_len;
1304 let mut children = SmallVec::<[NodeId; 4]>::new();
1305 let mut shared_children = None;
1306 #[cfg(feature = "profiling")]
1307 let mut subtree_len = 1_usize;
1308 let first_child = node_id.first_child(&self.tree);
1309 let mut next_child = first_child;
1310 let mut index = NonZero::<usize>::MIN;
1311 while let Some(child_id) = next_child {
1312 next_child = child_id.next_sibling(&self.tree);
1313 if self.tree[child_id]
1314 .get_mut()
1315 .sibling_index
1316 .replace(index)
1317 .is_some_and(|previous| previous != index)
1318 {
1319 self.metadatas.placement_order_dirty.insert(child_id);
1320 self.metadatas.mark_geometry_dirty(child_id);
1321 }
1322 index = index.saturating_add(1);
1323 let child = self.tree[child_id].get();
1324 #[cfg(feature = "profiling")]
1325 {
1326 subtree_len = subtree_len.saturating_add(child.subtree_len);
1327 }
1328 if child.role == NodeRole::Layout {
1329 children.push(child_id);
1330 } else {
1331 let descendants = direct_layout_children(child_id, &self.tree);
1332 if first_child == Some(child_id) && next_child.is_none() {
1333 shared_children = Some(descendants);
1334 } else {
1335 children.extend_from_slice(&descendants);
1336 }
1337 }
1338 }
1339 let child_ids = shared_children.as_deref().unwrap_or(children.as_slice());
1340 let node_ref = &self.tree[node_id];
1341 let node = node_ref.get();
1342 let changed = node.layout_children.as_deref() != Some(child_ids);
1343 if changed && (node.role == NodeRole::Layout || node_ref.parent().is_none()) {
1344 self.frame_scene.invalidate_structure(node_id);
1345 crate::runtime::record_layout_structure(
1346 node.instance_key,
1347 child_ids.iter().map(|id| self.tree[*id].get().instance_key),
1348 );
1349 }
1350 let node = self.tree[node_id].get_mut();
1351 node.layout_children_dirty = false;
1352 #[cfg(feature = "profiling")]
1353 {
1354 node.subtree_len = subtree_len;
1355 }
1356 if changed {
1357 node.layout_children =
1358 Some(shared_children.unwrap_or_else(|| node::LayoutChildren::new(&children)));
1359 }
1360 let propagate = changed && node.role == NodeRole::Composition;
1361 #[cfg(feature = "profiling")]
1362 let propagate = propagate || previous_subtree_len != node.subtree_len;
1363 if propagate && let Some(parent) = self.tree[node_id].parent() {
1364 self.tree[parent].get_mut().layout_children_dirty = true;
1365 }
1366 changed
1367 }
1368
1369 pub(crate) fn tree(&self) -> &indextree::Arena<ComponentNode> {
1374 &self.tree
1375 }
1376
1377 #[cfg(any(
1382 test,
1383 feature = "testing",
1384 all(feature = "headless", not(target_family = "wasm")),
1385 feature = "debug-dirty-overlay"
1386 ))]
1387 pub(crate) fn metadatas(&self) -> &ComponentNodeMetaDatas {
1388 &self.metadatas
1389 }
1390
1391 pub(crate) fn metadatas_mut(&mut self) -> &mut ComponentNodeMetaDatas {
1392 &mut self.metadatas
1393 }
1394
1395 pub(crate) fn clear_layout_snapshots(&mut self) {
1396 self.layout_snapshots.clear();
1397 }
1398
1399 fn remove_layout_snapshots(&mut self, keys: &HashSet<u64>) {
1400 if keys.is_empty() {
1401 return;
1402 }
1403 for key in keys {
1404 self.layout_snapshots.remove(key);
1405 }
1406 }
1407
1408 fn defer_unplaced_layout_paths(
1409 &mut self,
1410 keys: impl Iterator<Item = u64>,
1411 placement_only: bool,
1412 ) {
1413 if self.layout_snapshots.entries.is_empty() {
1414 return;
1415 }
1416 let mut visited = HashSet::default();
1417 for key in keys {
1418 let Some(&node_id) = self.node_ids_by_instance_key.get(&key) else {
1419 continue;
1420 };
1421 let mut child = None;
1422 for id in node_id.ancestors(&self.tree) {
1423 let node = &self.tree[id];
1424 if node.get().role != NodeRole::Layout && node.parent().is_some() {
1425 continue;
1426 }
1427 if self
1428 .metadatas
1429 .get(&id)
1430 .is_some_and(|metadata| metadata.abs_position.is_some())
1431 {
1432 break;
1433 }
1434 let key = node.get().instance_key;
1435 if self.layout_snapshots.entries.contains_key(&key) {
1436 let invalidation = self.layout_snapshots.deferred.entry(key).or_default();
1437 if let Some(child) = child {
1438 if !invalidation.measure {
1439 invalidation.children.insert(child);
1440 }
1441 } else if placement_only {
1442 invalidation.placement = true;
1443 } else {
1444 invalidation.measure = true;
1445 invalidation.children.clear();
1446 }
1447 }
1448 if !visited.insert(id) {
1449 break;
1450 }
1451 child = Some(key);
1452 }
1453 }
1454 }
1455
1456 pub(crate) fn focus_owner(&self) -> &FocusOwner {
1457 &self.focus_owner
1458 }
1459
1460 pub(crate) fn focus_owner_mut(&mut self) -> &mut FocusOwner {
1461 &mut self.focus_owner
1462 }
1463
1464 pub(crate) fn has_pending_focus_reveal(&self) -> bool {
1465 self.focus_owner.has_pending_reveal()
1466 }
1467
1468 pub(crate) fn accessibility_dispatch_context(
1469 &mut self,
1470 ) -> (&ComponentNodeTree, &ComponentNodeMetaDatas, &mut FocusOwner) {
1471 (&self.tree, &self.metadatas, &mut self.focus_owner)
1472 }
1473
1474 fn remove_position_observer(&mut self, node_id: NodeId) {
1475 self.position_observer_nodes.remove(&node_id);
1476 self.window_observer_nodes.remove(&node_id);
1477 self.pending_position_observers.remove(&node_id);
1478 self.forget_observer_space(node_id);
1479 }
1480
1481 fn forget_observer_space(&mut self, node_id: NodeId) {
1482 if let Some(space) = self.observer_spaces.remove(&node_id)
1483 && let Some(observers) = self.space_observers.get_mut(&space)
1484 {
1485 observers.remove(&node_id);
1486 if observers.is_empty() {
1487 self.space_observers.remove(&space);
1488 }
1489 }
1490 }
1491
1492 fn sync_observer_space(&mut self, node_id: NodeId) {
1493 let space = self.tree.get(node_id).and_then(|node| {
1494 node.get()
1495 .interaction()
1496 .globally_positioned_handlers
1497 .iter()
1498 .any(|handler| matches!(handler, GloballyPositionedHandler::InSpace(_)))
1499 .then(|| {
1500 node_id.ancestors(&self.tree).find_map(|id| {
1501 let node = self.tree[id].get();
1502 (node.role == NodeRole::Layout && node.layout_coordinate_space)
1503 .then_some(node.instance_key)
1504 })
1505 })
1506 .flatten()
1507 });
1508 if self.observer_spaces.get(&node_id).copied() == space {
1509 return;
1510 }
1511 self.forget_observer_space(node_id);
1512 if let Some(space) = space {
1513 self.observer_spaces.insert(node_id, space);
1514 self.space_observers
1515 .entry(space)
1516 .or_default()
1517 .insert(node_id);
1518 }
1519 }
1520
1521 fn collect_globally_positioned_invocations(&mut self, viewport: PxSize) {
1522 let mut candidates = std::mem::take(&mut self.pending_position_observers);
1523 for &node_id in &candidates {
1524 self.sync_observer_space(node_id);
1525 }
1526 if self.observer_viewport != Some(viewport) {
1527 candidates.extend(
1528 self.window_observer_nodes
1529 .iter()
1530 .filter(|id| {
1531 self.tree.get(**id).is_some_and(|node| {
1532 node.get()
1533 .interaction()
1534 .globally_positioned_handlers
1535 .iter()
1536 .any(|handler| {
1537 matches!(handler, GloballyPositionedHandler::Visible(_))
1538 })
1539 })
1540 })
1541 .copied(),
1542 );
1543 self.observer_viewport = Some(viewport);
1544 }
1545 let changed = &self.metadatas.geometry_changed;
1546 if self.space_observers.len() < changed.len() {
1549 for (space, observers) in &self.space_observers {
1550 if self
1551 .node_ids_by_instance_key
1552 .get(space)
1553 .is_some_and(|id| changed.contains(id))
1554 {
1555 candidates.extend(observers.iter().copied());
1556 }
1557 }
1558 } else {
1559 for &node_id in changed {
1560 if let Some(observers) = self
1561 .tree
1562 .get(node_id)
1563 .and_then(|node| self.space_observers.get(&node.get().instance_key))
1564 {
1565 candidates.extend(observers.iter().copied());
1566 }
1567 }
1568 }
1569 if self.window_observer_nodes.len() < changed.len() {
1570 candidates.extend(
1571 self.window_observer_nodes
1572 .iter()
1573 .filter(|id| changed.contains(id))
1574 .copied(),
1575 );
1576 } else {
1577 candidates.extend(
1578 changed
1579 .iter()
1580 .filter(|id| self.window_observer_nodes.contains(id))
1581 .copied(),
1582 );
1583 }
1584 candidates.extend(self.metadatas.size_changed.iter().copied());
1585 let observer_candidates = candidates.len();
1586 let mut invocations = Vec::new();
1587 for node_id in candidates {
1588 let Some(node_ref) = self.tree.get(node_id) else {
1589 continue;
1590 };
1591 let node = node_ref.get();
1592 if node.interaction().globally_positioned_handlers.is_empty() {
1593 continue;
1594 }
1595 let Some(metadata) = self.metadatas.get(&node_id) else {
1596 continue;
1597 };
1598 let Some(position) = metadata.abs_position else {
1599 if let Some(previous) = self.last_global_rects.remove(&node.instance_key) {
1600 let mut geometry = previous.geometry;
1601 geometry.visible = None;
1602 for handler in &node.interaction().globally_positioned_handlers {
1603 if matches!(handler, GloballyPositionedHandler::Visible(_))
1604 && previous.geometry.visible.is_some()
1605 {
1606 invocations.push((*handler, geometry));
1607 }
1608 }
1609 }
1610 continue;
1611 };
1612 let size = metadata.computed_data.unwrap_or(ComputedData::ZERO);
1613 let rect = PxRect {
1614 x: position.x_px(),
1615 y: position.y_px(),
1616 width: size.width,
1617 height: size.height,
1618 };
1619 let transform = metadata
1620 .local_to_window
1621 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, position).unwrap());
1622 let precise = transform.map_rect(crate::FloatRect::from_position_size(
1623 PxPosition::ZERO,
1624 PxSize::new(size.width, size.height),
1625 ));
1626 let geometry = node::GeometryObservation {
1627 rounded: if transform.scale() == 1.0 {
1628 rect
1629 } else {
1630 precise.outward()
1631 },
1632 precise,
1633 visible: precise
1634 .intersection(metadata.event_clip_bounds.unwrap_or(precise))
1635 .and_then(|bounds| {
1636 bounds.intersection(crate::FloatRect::from_position_size(
1637 PxPosition::ZERO,
1638 viewport,
1639 ))
1640 }),
1641 transform,
1642 coordinates: crate::LayoutCoordinates {
1643 local_size: PxSize::new(size.width, size.height),
1644 local_to_window: transform,
1645 },
1646 in_space: if node
1647 .interaction()
1648 .globally_positioned_handlers
1649 .iter()
1650 .any(|handler| matches!(handler, GloballyPositionedHandler::InSpace(_)))
1651 {
1652 layout_rect_in_space(&self.tree, &self.metadatas, node_id).unwrap_or(precise)
1653 } else {
1654 precise
1655 },
1656 };
1657 let previous = self.last_global_rects.get(&node.instance_key);
1658 for handler in &node.interaction().globally_positioned_handlers {
1659 if previous.is_none_or(|previous| {
1660 handler.changed(previous.geometry, geometry)
1661 || !previous.handlers.contains(handler)
1662 }) {
1663 invocations.push((*handler, geometry));
1664 }
1665 }
1666 self.last_global_rects.insert(
1667 node.instance_key,
1668 GlobalPositionSnapshot {
1669 geometry,
1670 handlers: node
1671 .interaction()
1672 .globally_positioned_handlers
1673 .iter()
1674 .copied()
1675 .collect(),
1676 },
1677 );
1678 }
1679 tracing::debug!(
1680 target: "tessera_ui::frame_cost",
1681 callbacks = invocations.len(),
1682 candidates = observer_candidates,
1683 size_changes = self.metadatas.size_changed.len(),
1684 "position_observer_work"
1685 );
1686 self.pending_globally_positioned_invocations
1687 .extend(invocations);
1688 }
1689
1690 pub(crate) fn take_pending_globally_positioned_invocations(
1691 &mut self,
1692 ) -> Vec<(GloballyPositionedHandler, node::GeometryObservation)> {
1693 std::mem::take(&mut self.pending_globally_positioned_invocations)
1694 }
1695
1696 pub(crate) fn take_pending_focus_callback_invocations(
1697 &mut self,
1698 ) -> Vec<PendingFocusCallbackInvocation> {
1699 let notifications = self.focus_owner.take_pending_notifications();
1700 let mut invocations = Vec::new();
1701
1702 for notification in notifications {
1703 let Some(node_id) = self
1704 .focus_owner
1705 .component_node_id_of(notification.handle_id)
1706 else {
1707 continue;
1708 };
1709 let Some(node_ref) = self.tree.get(node_id) else {
1710 continue;
1711 };
1712 let node = node_ref.get();
1713
1714 if notification.changed
1715 && let Some(handler) = &node.interaction().focus_changed_handler
1716 {
1717 invocations.push(PendingFocusCallbackInvocation::new(
1718 *handler,
1719 notification.state,
1720 notification.direction,
1721 ));
1722 }
1723 if let Some(handler) = &node.interaction().focus_event_handler {
1724 invocations.push(PendingFocusCallbackInvocation::new(
1725 *handler,
1726 notification.state,
1727 notification.direction,
1728 ));
1729 }
1730 }
1731
1732 invocations
1733 }
1734
1735 #[cfg(feature = "profiling")]
1737 pub fn profiler_nodes(&mut self) -> Vec<NodeMeta> {
1738 self.profiler_dirty_nodes
1739 .extend(std::mem::take(&mut self.metadatas.profiling_dirty));
1740 self.profiler_dirty_nodes
1741 .extend(self.metadatas.geometry_changed.iter().copied());
1742 for node_id in std::mem::take(&mut self.profiler_dirty_nodes) {
1743 let Some(node) = self.tree.get(node_id).filter(|node| !node.is_removed()) else {
1744 self.profiler_nodes_cache.remove(&node_id);
1745 continue;
1746 };
1747 let metadata = self.metadatas.get(&node_id);
1748 self.profiler_nodes_cache.insert(
1749 node_id,
1750 NodeMeta {
1751 node_id: node_id.to_string(),
1752 parent: node.parent().map(|id| id.to_string()),
1753 fn_name: Some(node.get().fn_name.to_owned()),
1754 abs_pos: metadata
1755 .and_then(|metadata| metadata.abs_position)
1756 .map(|position| (position.x_px().0, position.y_px().0)),
1757 size: metadata
1758 .and_then(|metadata| metadata.computed_data)
1759 .map(|size| (size.width.0, size.height.0)),
1760 layout_cache_hit: metadata.map(|metadata| metadata.layout_cache_hit),
1761 },
1762 );
1763 }
1764 self.profiler_nodes_cache.values().cloned().collect()
1765 }
1766
1767 fn dispatch_pointer_input(
1770 &mut self,
1771 root_node: indextree::NodeId,
1772 pointer_changes: &mut [PointerChange],
1773 cursor_position: &mut Option<PxPosition>,
1774 modifiers: winit::keyboard::ModifiersState,
1775 ) -> WindowRequests {
1776 let mut window_requests = WindowRequests::default();
1777 self.pointer_index.invalidate(
1778 &self.tree,
1779 &self.changed_frame_nodes,
1780 &self.removed_frame_nodes,
1781 );
1782 self.pointer_work.0 = self.pointer_index.update(&self.tree, &self.metadatas) as u64;
1783 self.pointer_index.begin_dispatch();
1784 self.active_pointer_paths.retain(|_, path| {
1785 path.last().is_some_and(|key| {
1786 self.node_ids_by_instance_key.get(key).is_some_and(|id| {
1787 self.metadatas
1788 .get(id)
1789 .is_some_and(|metadata| metadata.abs_position.is_some())
1790 })
1791 })
1792 });
1793 self.active_pointer_positions
1794 .retain(|pointer, _| self.active_pointer_paths.contains_key(pointer));
1795 let dirty = crate::runtime::take_pointer_dirty_nodes();
1796 let mut selected: HashSet<_> = dirty
1797 .into_iter()
1798 .filter_map(|key| self.node_ids_by_instance_key.get(&key).copied())
1799 .collect();
1800 for path in self.active_pointer_paths.values() {
1801 selected.extend(
1802 path.iter()
1803 .filter_map(|key| self.node_ids_by_instance_key.get(key).copied()),
1804 );
1805 }
1806 for change in pointer_changes.iter() {
1807 selected.extend(self.pointer_index.sample_targets(
1808 &self.tree,
1809 &self.metadatas,
1810 change.resolved_position(*cursor_position),
1811 ));
1812 selected.extend(self.pointer_index.hit_path(
1813 &self.tree,
1814 &self.metadatas,
1815 change.resolved_position(*cursor_position),
1816 ));
1817 }
1818 let final_position = *cursor_position;
1819 let path = self
1820 .pointer_index
1821 .hit_path(&self.tree, &self.metadatas, final_position);
1822 window_requests.cursor_icon = path
1823 .into_iter()
1824 .rev()
1825 .find_map(|id| {
1826 let node = self.tree.get(id)?.get();
1827 let metadata = self.metadatas.get(&id)?;
1828 resolve_node_hover_cursor_icon(
1829 node,
1830 metadata.base_abs_position?,
1831 metadata.computed_data?,
1832 final_position?,
1833 metadata
1834 .local_to_window
1835 .map_or(1.0, |transform| transform.scale()),
1836 metadata.input_transforms.as_deref(),
1837 )
1838 })
1839 .unwrap_or_default();
1840 let plan = self.pointer_index.plan(
1841 &self.tree,
1842 &self.metadatas,
1843 final_position,
1844 selected.into_iter(),
1845 );
1846 self.pointer_work
1847 .1
1848 .extend(plan.nodes.values().map(|node| node.context.instance_key));
1849 for node in plan.nodes.values() {
1850 crate::runtime::clear_phase_state_reads(
1851 node.context.instance_key,
1852 crate::runtime::ReadPhase::Pointer,
1853 );
1854 }
1855 let mut last_position = None;
1856 for change in pointer_changes {
1857 if matches!(&change.content, CursorEventContent::Pressed(_)) {
1858 self.focus_owner.observe_pointer_press(change.timestamp);
1859 }
1860 if matches!(change.content, CursorEventContent::Scroll(_))
1861 && change.scroll_discrete.is_none()
1862 {
1863 change.scroll_discrete = Some(change.is_discrete_scroll());
1864 }
1865 if matches!(
1866 &change.content,
1867 CursorEventContent::Scroll(scroll)
1868 if scroll.delta_x.is_finite()
1869 && scroll.delta_y.is_finite()
1870 && (scroll.delta_x != 0.0 || scroll.delta_y != 0.0)
1871 ) {
1872 self.focus_owner.cancel_pending_reveal_for_scroll();
1873 }
1874 let mut position = change.resolved_position(final_position);
1875 last_position = Some(position);
1876 self.dispatch_pointer_sample(
1877 root_node,
1878 std::slice::from_mut(change),
1879 &mut position,
1880 modifiers,
1881 &plan,
1882 &mut window_requests,
1883 );
1884 }
1885 if last_position != Some(final_position) {
1886 self.dispatch_pointer_sample(
1887 root_node,
1888 &mut [],
1889 cursor_position,
1890 modifiers,
1891 &plan,
1892 &mut window_requests,
1893 );
1894 }
1895 window_requests
1896 }
1897
1898 fn dispatch_pointer_sample(
1899 &mut self,
1900 _root_node: indextree::NodeId,
1901 pointer_changes: &mut [PointerChange],
1902 cursor_position: &mut Option<PxPosition>,
1903 modifiers: winit::keyboard::ModifiersState,
1904 plan: &PointerDispatchPlan,
1905 window_requests: &mut WindowRequests,
1906 ) {
1907 let node_ids_preorder = &plan.preorder;
1908 let node_ids_postorder = &plan.postorder;
1909 let pointer_change_paths = build_pointer_change_paths(
1910 &self.pointer_index,
1911 &self.tree,
1912 &self.metadatas,
1913 pointer_changes,
1914 *cursor_position,
1915 &mut self.active_pointer_paths,
1916 );
1917 for change in pointer_changes.iter() {
1918 if self.active_pointer_paths.contains_key(&change.pointer_id) {
1919 self.active_pointer_positions.insert(
1920 change.pointer_id,
1921 change.resolved_position(*cursor_position),
1922 );
1923 } else {
1924 self.active_pointer_positions.remove(&change.pointer_id);
1925 }
1926 }
1927
1928 for node_id in node_ids_preorder.iter().copied() {
1929 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
1930 continue;
1931 };
1932 let mut dispatch_ctx = PointerInputDispatchContext {
1933 tree: &self.tree,
1934 metadatas: &self.metadatas,
1935 cursor_position: &mut *cursor_position,
1936 pointer_changes: &mut *pointer_changes,
1937 pointer_change_paths: &pointer_change_paths,
1938 nodes: &plan.nodes,
1939 active_pointer_paths: &self.active_pointer_paths,
1940 active_pointer_positions: &self.active_pointer_positions,
1941 modifiers,
1942 window_requests: &mut *window_requests,
1943 focus_owner: &mut self.focus_owner,
1944 };
1945 dispatch_pointer_modifiers_for_node_pass(
1946 &mut dispatch_ctx,
1947 node_id,
1948 PointerEventPass::Initial,
1949 );
1950 let handlers = &node.interaction().pointer_preview_handlers;
1951 for handler in handlers {
1952 let mut dispatch_ctx = PointerInputDispatchContext {
1953 tree: &self.tree,
1954 metadatas: &self.metadatas,
1955 cursor_position: &mut *cursor_position,
1956 pointer_changes: &mut *pointer_changes,
1957 pointer_change_paths: &pointer_change_paths,
1958 nodes: &plan.nodes,
1959 active_pointer_paths: &self.active_pointer_paths,
1960 active_pointer_positions: &self.active_pointer_positions,
1961 modifiers,
1962 window_requests: &mut *window_requests,
1963 focus_owner: &mut self.focus_owner,
1964 };
1965 run_pointer_handler_for_node(
1966 &mut dispatch_ctx,
1967 node_id,
1968 PointerEventPass::Initial,
1969 handler.as_ref(),
1970 );
1971 }
1972 }
1973
1974 for node_id in node_ids_postorder.iter().copied() {
1975 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
1976 continue;
1977 };
1978 let mut dispatch_ctx = PointerInputDispatchContext {
1979 tree: &self.tree,
1980 metadatas: &self.metadatas,
1981 cursor_position: &mut *cursor_position,
1982 pointer_changes: &mut *pointer_changes,
1983 pointer_change_paths: &pointer_change_paths,
1984 nodes: &plan.nodes,
1985 active_pointer_paths: &self.active_pointer_paths,
1986 active_pointer_positions: &self.active_pointer_positions,
1987 modifiers,
1988 window_requests: &mut *window_requests,
1989 focus_owner: &mut self.focus_owner,
1990 };
1991 dispatch_pointer_modifiers_for_node_pass(
1992 &mut dispatch_ctx,
1993 node_id,
1994 PointerEventPass::Main,
1995 );
1996 let handlers = &node.interaction().pointer_handlers;
1997 for handler in handlers {
1998 let mut dispatch_ctx = PointerInputDispatchContext {
1999 tree: &self.tree,
2000 metadatas: &self.metadatas,
2001 cursor_position: &mut *cursor_position,
2002 pointer_changes: &mut *pointer_changes,
2003 pointer_change_paths: &pointer_change_paths,
2004 nodes: &plan.nodes,
2005 active_pointer_paths: &self.active_pointer_paths,
2006 active_pointer_positions: &self.active_pointer_positions,
2007 modifiers,
2008 window_requests: &mut *window_requests,
2009 focus_owner: &mut self.focus_owner,
2010 };
2011 run_pointer_handler_for_node(
2012 &mut dispatch_ctx,
2013 node_id,
2014 PointerEventPass::Main,
2015 handler.as_ref(),
2016 );
2017 }
2018 }
2019
2020 for node_id in node_ids_preorder.iter().copied() {
2021 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
2022 continue;
2023 };
2024 let mut dispatch_ctx = PointerInputDispatchContext {
2025 tree: &self.tree,
2026 metadatas: &self.metadatas,
2027 cursor_position: &mut *cursor_position,
2028 pointer_changes: &mut *pointer_changes,
2029 pointer_change_paths: &pointer_change_paths,
2030 nodes: &plan.nodes,
2031 active_pointer_paths: &self.active_pointer_paths,
2032 active_pointer_positions: &self.active_pointer_positions,
2033 modifiers,
2034 window_requests: &mut *window_requests,
2035 focus_owner: &mut self.focus_owner,
2036 };
2037 dispatch_pointer_modifiers_for_node_pass(
2038 &mut dispatch_ctx,
2039 node_id,
2040 PointerEventPass::Final,
2041 );
2042 let handlers = &node.interaction().pointer_final_handlers;
2043 for handler in handlers {
2044 let mut dispatch_ctx = PointerInputDispatchContext {
2045 tree: &self.tree,
2046 metadatas: &self.metadatas,
2047 cursor_position: &mut *cursor_position,
2048 pointer_changes: &mut *pointer_changes,
2049 pointer_change_paths: &pointer_change_paths,
2050 nodes: &plan.nodes,
2051 active_pointer_paths: &self.active_pointer_paths,
2052 active_pointer_positions: &self.active_pointer_positions,
2053 modifiers,
2054 window_requests: &mut *window_requests,
2055 focus_owner: &mut self.focus_owner,
2056 };
2057 run_pointer_handler_for_node(
2058 &mut dispatch_ctx,
2059 node_id,
2060 PointerEventPass::Final,
2061 handler.as_ref(),
2062 );
2063 }
2064 }
2065 }
2066
2067 fn dispatch_keyboard_event(
2068 &mut self,
2069 root_node: NodeId,
2070 queued: QueuedInput,
2071 window_requests: &mut WindowRequests,
2072 ) -> Option<FocusDirection> {
2073 let path = collect_focus_chain_node_ids(root_node, &self.tree, &self.focus_owner);
2074 let mut events = vec![queued.event];
2075 let mut context = KeyboardInputDispatchContext {
2076 tree: &self.tree,
2077 metadatas: &self.metadatas,
2078 keyboard_events: &mut events,
2079 modifiers: queued.modifiers,
2080 ime_client: self.focus_owner.active_handle_id(),
2081 window_requests,
2082 focus_owner: &mut self.focus_owner,
2083 };
2084 for node_id in path.iter().copied() {
2085 let Some(node) = context.tree.get(node_id).map(|node| node.get()) else {
2086 continue;
2087 };
2088 for action in node.modifier.ordered_actions() {
2089 if let OrderedModifierAction::KeyboardPreviewInput(modifier) = action {
2090 run_keyboard_input_for_node(&mut context, node_id, |input| {
2091 modifier.on_keyboard_input(input)
2092 });
2093 }
2094 }
2095 for handler in &node.interaction().keyboard_preview_handlers {
2096 run_keyboard_input_for_node(&mut context, node_id, handler.as_ref());
2097 }
2098 }
2099 for node_id in path.iter().rev().copied() {
2100 let Some(node) = context.tree.get(node_id).map(|node| node.get()) else {
2101 continue;
2102 };
2103 for action in node.modifier.ordered_actions() {
2104 if let OrderedModifierAction::KeyboardInput(modifier) = action {
2105 run_keyboard_input_for_node(&mut context, node_id, |input| {
2106 modifier.on_keyboard_input(input)
2107 });
2108 }
2109 }
2110 for handler in &node.interaction().keyboard_handlers {
2111 run_keyboard_input_for_node(&mut context, node_id, handler.as_ref());
2112 }
2113 }
2114 dispatch_default_focus_keyboard_navigation(
2115 context.tree,
2116 context.keyboard_events,
2117 context.modifiers,
2118 context.focus_owner,
2119 )
2120 }
2121
2122 fn dispatch_clipboard_action(&mut self, root_node: NodeId, request: QueuedClipboardAction) {
2123 let path = collect_focus_chain_node_ids(root_node, &self.tree, &self.focus_owner);
2124 let mut response = None;
2125 for node_id in path.into_iter().rev() {
2126 let Some(context) = resolve_node_input_context(&self.tree, &self.metadatas, node_id)
2127 else {
2128 continue;
2129 };
2130 let node = self.tree.get(node_id).expect("focused node").get();
2131 for action in node.modifier.ordered_actions() {
2132 if let OrderedModifierAction::ClipboardInput(modifier) = action {
2133 let boundary = nearest_replay_boundary_instance_key(node_id, &self.tree);
2134 let _node = push_current_node_with_instance_logic_id(
2135 node_id,
2136 context.instance_logic_id,
2137 context.fn_name,
2138 );
2139 let _instance = push_current_component_instance_key(boundary);
2140 let _phase = push_phase(RuntimePhase::Input);
2141 let _focus = bind_focus_owner(&mut self.focus_owner);
2142 modifier.on_clipboard_input(ClipboardInput {
2143 action: &request.action,
2144 response: &mut response,
2145 });
2146 if response.is_some() {
2147 break;
2148 }
2149 }
2150 }
2151 if response.is_some() {
2152 break;
2153 }
2154 }
2155 (request.reply)(response.unwrap_or(ClipboardResponse::Ignored));
2156 }
2157
2158 fn dispatch_ime_input(
2159 &mut self,
2160 root_node: NodeId,
2161 events: &mut Vec<crate::ime_state::ImeEvent>,
2162 window_requests: &mut WindowRequests,
2163 ) {
2164 let path = collect_focus_chain_node_ids(root_node, &self.tree, &self.focus_owner);
2165 let client = self.focus_owner.active_handle_id();
2166 let mut pending = std::mem::take(events)
2167 .into_iter()
2168 .filter_map(|(source, event)| {
2169 source
2170 .is_none_or(|source| Some(source) == client)
2171 .then_some(event)
2172 })
2173 .collect();
2174 for node_id in path.iter().copied() {
2175 let Some(node) = self.tree.get(node_id).map(|node| node.get()) else {
2176 continue;
2177 };
2178 for action in node.modifier.ordered_actions() {
2179 if let OrderedModifierAction::ImePreviewInput(modifier) = action {
2180 run_ime_input_for_node(
2181 &self.tree,
2182 &self.metadatas,
2183 node_id,
2184 &mut pending,
2185 window_requests,
2186 &mut self.focus_owner,
2187 client,
2188 |input| modifier.on_ime_input(input),
2189 );
2190 }
2191 }
2192 for handler in &node.interaction().ime_preview_handlers {
2193 run_ime_input_for_node(
2194 &self.tree,
2195 &self.metadatas,
2196 node_id,
2197 &mut pending,
2198 window_requests,
2199 &mut self.focus_owner,
2200 client,
2201 handler.as_ref(),
2202 );
2203 }
2204 }
2205 for node_id in path.iter().rev().copied() {
2206 let Some(node) = self.tree.get(node_id).map(|node| node.get()) else {
2207 continue;
2208 };
2209 for action in node.modifier.ordered_actions() {
2210 if let OrderedModifierAction::ImeInput(modifier) = action {
2211 run_ime_input_for_node(
2212 &self.tree,
2213 &self.metadatas,
2214 node_id,
2215 &mut pending,
2216 window_requests,
2217 &mut self.focus_owner,
2218 client,
2219 |input| modifier.on_ime_input(input),
2220 );
2221 }
2222 }
2223 for handler in &node.interaction().ime_handlers {
2224 run_ime_input_for_node(
2225 &self.tree,
2226 &self.metadatas,
2227 node_id,
2228 &mut pending,
2229 window_requests,
2230 &mut self.focus_owner,
2231 client,
2232 handler.as_ref(),
2233 );
2234 }
2235 }
2236 }
2237
2238 fn queue_focused_input(
2239 &mut self,
2240 keyboard: Vec<QueuedInput>,
2241 ime: Vec<QueuedInput<crate::ime_state::ImeEvent>>,
2242 ) {
2243 self.pending_focused_input
2244 .extend(keyboard.into_iter().map(QueuedFocusedInput::Keyboard));
2245 self.pending_focused_input
2246 .extend(ime.into_iter().map(QueuedFocusedInput::Ime));
2247 self.pending_focused_input.extend(
2248 clipboard::take_requests()
2249 .into_iter()
2250 .map(QueuedFocusedInput::Clipboard),
2251 );
2252 self.pending_focused_input
2253 .make_contiguous()
2254 .sort_by_key(QueuedFocusedInput::sequence);
2255 }
2256
2257 fn dispatch_pending_focused_input(
2258 &mut self,
2259 root: NodeId,
2260 requests: &mut WindowRequests,
2261 ) -> Option<FocusDirection> {
2262 let mut events = std::mem::take(&mut self.pending_focused_input);
2263 let retry = if self.focus_owner.keyboard_is_deferred() {
2264 None
2265 } else {
2266 drain_pending_focused_input(&mut events, |event| {
2267 let cause = match &event {
2268 QueuedFocusedInput::Ime(event) => {
2269 let (client, event) = &event.event;
2270 (client.is_none_or(|client| {
2271 Some(client) == self.focus_owner.active_handle_id()
2272 }) && !matches!(
2273 event,
2274 winit::event::Ime::Enabled | winit::event::Ime::Disabled
2275 ))
2276 .then_some(winit::event::ImeChangeCause::InputMethod)
2277 }
2278 _ => Some(winit::event::ImeChangeCause::Other),
2279 };
2280 let retry = match event {
2281 QueuedFocusedInput::Keyboard(event) => {
2282 self.dispatch_keyboard_event(root, event, requests)
2283 }
2284 QueuedFocusedInput::Clipboard(request) => {
2285 self.dispatch_clipboard_action(root, request);
2286 None
2287 }
2288 QueuedFocusedInput::Ime(event) => {
2289 self.dispatch_ime_input(root, &mut vec![event.event], requests);
2290 None
2291 }
2292 };
2293 if let (Some(request), Some(cause)) = (&mut requests.ime_request, cause) {
2294 request.change_cause = cause;
2295 }
2296 self.focus_owner.commit_pending();
2297 (retry, self.focus_owner.keyboard_is_deferred())
2298 })
2299 };
2300 self.pending_focused_input = events;
2301 if retry.is_some()
2302 || self.focus_owner.keyboard_is_deferred()
2303 || self.focus_owner.has_pending_navigation()
2304 {
2305 requests.ime_request = None;
2306 } else {
2307 let cause = requests
2308 .ime_request
2309 .as_ref()
2310 .map_or(winit::event::ImeChangeCause::Other, |request| {
2311 request.change_cause
2312 });
2313 self.dispatch_ime_input(root, &mut Vec::new(), requests);
2314 if let Some(request) = &mut requests.ime_request {
2315 request.change_cause = cause;
2316 }
2317 }
2318 retry
2319 }
2320
2321 pub(crate) fn flush_focused_input(
2322 &mut self,
2323 cursor: &mut crate::cursor::CursorState,
2324 modifiers: winit::keyboard::ModifiersState,
2325 keyboard: Vec<QueuedInput>,
2326 ime: Vec<QueuedInput<crate::ime_state::ImeEvent>>,
2327 ) -> WindowRequests {
2328 self.queue_focused_input(keyboard, ime);
2329 let Some(root) = self.tree.get_node_id_at(NonZero::new(1).unwrap()) else {
2330 return WindowRequests::default();
2331 };
2332 if self
2333 .metadatas
2334 .get(&root)
2335 .is_none_or(|metadata| metadata.computed_data.is_none())
2336 {
2337 return WindowRequests::default();
2338 }
2339 let mut pointer = cursor.take_events();
2340 let mut position = cursor.position();
2341 let mut requests =
2342 self.dispatch_pointer_input(root, &mut pointer, &mut position, modifiers);
2343 self.focus_owner.commit_pending();
2344 if self.focus_owner.keyboard_is_deferred() || self.focus_owner.has_pending_navigation() {
2345 requests.ime_request = None;
2346 return requests;
2347 }
2348 let retry = self.dispatch_pending_focused_input(root, &mut requests);
2349 self.focus_owner.retain_navigation_retry(retry);
2350 requests
2351 }
2352
2353 fn clear_frame_structure_changes(&mut self) {
2354 let live_nodes = self.node_ids_by_instance_key.len();
2355 for (nodes, previous_count) in
2356 [&mut self.changed_frame_nodes, &mut self.removed_frame_nodes]
2357 .into_iter()
2358 .zip(&mut self.previous_structure_change_counts)
2359 {
2360 let recent_count = nodes
2362 .len()
2363 .min(std::mem::replace(previous_count, nodes.len()));
2364 let capacity_limit = recent_count.min(live_nodes).max(64).saturating_mul(2);
2365 if nodes.capacity() > capacity_limit {
2366 *nodes = HashSet::default();
2367 } else {
2368 nodes.clear();
2369 }
2370 }
2371 }
2372
2373 #[tracing::instrument(level = "debug", skip(self, params))]
2384 pub(crate) fn compute(
2385 &mut self,
2386 params: ComputeParams,
2387 mode: ComputeMode,
2388 ) -> (
2389 RenderGraph,
2390 WindowRequests,
2391 LayoutFrameDiagnostics,
2392 std::time::Duration,
2393 Option<FocusDirection>,
2394 bool,
2395 bool,
2396 ) {
2397 let mut stage_timer =
2398 tracing::enabled!(target: "tessera_ui::frame_cost", tracing::Level::DEBUG)
2399 .then(Instant::now);
2400 self.metadatas.geometry_visits = 0;
2401 self.metadatas.geometry_changed.clear();
2402 self.metadatas.size_changed.clear();
2403 self.metadatas.placement_order_changed.clear();
2404 self.metadatas.paint_changed.clear();
2405 self.focus_owner.begin_layout();
2406 let ComputeParams {
2407 screen_size,
2408 mut cursor_position,
2409 mut pointer_changes,
2410 keyboard_events,
2411 ime_events,
2412 retry_focus_move,
2413 retry_focus_reveal,
2414 modifiers,
2415 layout_dirty_nodes,
2416 } = params;
2417 let Some(root_node) = self
2418 .tree
2419 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))
2420 else {
2421 return (
2422 RenderGraph::default(),
2423 WindowRequests::default(),
2424 LayoutFrameDiagnostics {
2425 pointer_nodes_dispatched: self.pointer_work.1.len() as u64,
2426 ..Default::default()
2427 },
2428 std::time::Duration::ZERO,
2429 None,
2430 false,
2431 false,
2432 );
2433 };
2434 let screen_constraint = Constraint::exact(screen_size.width, screen_size.height);
2435 for key in &layout_dirty_nodes.transform_nodes {
2436 if let Some(&node_id) = self.node_ids_by_instance_key.get(key) {
2437 self.metadatas.mark_geometry_dirty(node_id);
2438 }
2439 }
2440 for key in &layout_dirty_nodes.paint_nodes {
2441 if let Some(&node_id) = self.node_ids_by_instance_key.get(key) {
2442 self.metadatas.mark_paint_dirty(node_id);
2443 }
2444 }
2445 let StructureReconcileResult {
2446 changed_nodes: structural_dirty_nodes,
2447 removed_nodes,
2448 } = crate::runtime::take_layout_structure_changes();
2449 self.remove_layout_snapshots(&removed_nodes);
2450 crate::runtime::remove_layout_state_read_dependencies(&removed_nodes);
2454
2455 let mut measure_self_nodes = layout_dirty_nodes.measure_self_nodes;
2456 for key in &layout_dirty_nodes.parent_data_nodes {
2457 if let Some(&node_id) = self.node_ids_by_instance_key.get(key) {
2458 let mut parent = self.tree.get(node_id).and_then(|node| node.parent());
2459 while let Some(parent_id) = parent {
2460 let node = &self.tree[parent_id];
2461 if node.get().role == NodeRole::Layout || node.parent().is_none() {
2462 measure_self_nodes.insert(node.get().instance_key);
2463 break;
2464 }
2465 parent = node.parent();
2466 }
2467 }
2468 }
2469 let dirty_nodes_param = (measure_self_nodes.len()
2470 + layout_dirty_nodes
2471 .placement_self_nodes
2472 .difference(&measure_self_nodes)
2473 .count()) as u64;
2474 let dirty_nodes_structural = structural_dirty_nodes.len() as u64;
2475 let dirty_prepare_start = Instant::now();
2476 measure_self_nodes.extend(structural_dirty_nodes);
2478 let dirty_paths = collect_dirty_layout_paths(
2479 &self.tree,
2480 &self.node_ids_by_instance_key,
2481 &measure_self_nodes,
2482 &layout_dirty_nodes.placement_self_nodes,
2483 );
2484 let dirty_expand_ns = dirty_prepare_start.elapsed().as_nanos() as u64;
2485 record_frame_stage(&mut stage_timer, "layout_dirty_paths");
2486 let mut diagnostics = LayoutDiagnosticsCollector::default();
2487
2488 tracing::debug!(
2489 target: "tessera_ui::frame_cost",
2490 changed = self.changed_frame_nodes.len(),
2491 removed = self.removed_frame_nodes.len(),
2492 "frame_consumer_candidates"
2493 );
2494
2495 #[cfg(feature = "profiling")]
2496 {
2497 self.profiler_dirty_nodes
2498 .extend(self.changed_frame_nodes.iter().copied());
2499 self.profiler_dirty_nodes
2500 .extend(self.removed_frame_nodes.iter().copied());
2501 self.profiler_dirty_nodes
2502 .extend(dirty_paths.keys().copied());
2503 self.profiler_dirty_nodes.insert(root_node);
2504 }
2505 self.pointer_index.invalidate(
2506 &self.tree,
2507 &self.changed_frame_nodes,
2508 &self.removed_frame_nodes,
2509 );
2510 self.accessibility_index
2511 .invalidate(&self.changed_frame_nodes, &self.removed_frame_nodes);
2512 self.focus_owner.sync_changes(
2513 &self.tree,
2514 &self.changed_frame_nodes,
2515 &self.removed_frame_nodes,
2516 );
2517 self.focus_owner.commit_pending();
2518
2519 record_frame_stage(&mut stage_timer, "consumer_attachments");
2520 {
2521 let consumed_insets = Cell::new(crate::Insets::default());
2522 let layout_ctx = LayoutContext::new(
2523 &consumed_insets,
2524 &mut self.layout_snapshots,
2525 &dirty_paths,
2526 &mut diagnostics,
2527 );
2528
2529 let measure_timer = Instant::now();
2530 debug!("Start measuring the component tree...");
2531
2532 match measure_node(
2533 root_node,
2534 &screen_constraint,
2535 &self.tree,
2536 &mut self.metadatas,
2537 Some(&layout_ctx),
2538 ) {
2539 Ok(_root_computed_data) => {
2540 debug!("Component tree measured in {:?}", measure_timer.elapsed());
2541 }
2542 Err(e) => {
2543 panic!(
2544 "Root node ({root_node:?}) measurement failed: {e:?}. Aborting draw command computation."
2545 );
2546 }
2547 }
2548 }
2549
2550 record_frame_stage(&mut stage_timer, "measure");
2551 let mut diagnostics_snapshot = diagnostics.snapshot(
2552 dirty_nodes_param,
2553 dirty_nodes_structural,
2554 dirty_paths.len() as u64,
2555 dirty_expand_ns,
2556 );
2557 diagnostics_snapshot.geometry_nodes_updated =
2558 populate_layout_metadata(root_node, &self.tree, &mut self.metadatas);
2559 self.defer_unplaced_layout_paths(measure_self_nodes.iter().copied(), false);
2560 self.defer_unplaced_layout_paths(
2561 layout_dirty_nodes.placement_self_nodes.iter().copied(),
2562 true,
2563 );
2564 for key in &layout_dirty_nodes.transform_nodes {
2565 if let Some(id) = self.node_ids_by_instance_key.get(key)
2566 && let Some(metadata) = self.metadatas.get_mut(id)
2567 && metadata.abs_position.is_none()
2568 {
2569 metadata.geometry_initialized = false;
2570 }
2571 }
2572 record_frame_stage(&mut stage_timer, "geometry");
2573
2574 match mode {
2575 ComputeMode::Full => {}
2576 #[cfg(any(test, feature = "testing"))]
2577 ComputeMode::LayoutOnly => {
2578 self.frame_scene.clear_structure_changes();
2579 self.collect_globally_positioned_invocations(screen_size);
2580 diagnostics_snapshot.accessibility_nodes_updated =
2581 self.sync_accessibility(root_node, true) as u64;
2582 self.focus_owner.sync_geometry_changes(
2583 &self.tree,
2584 &self.metadatas,
2585 &self.metadatas.geometry_changed,
2586 &self.changed_frame_nodes,
2587 );
2588 self.clear_frame_structure_changes();
2589 let _ = self.dispatch_pointer_input(
2590 root_node,
2591 pointer_changes.as_mut_slice(),
2592 &mut cursor_position,
2593 modifiers,
2594 );
2595 diagnostics_snapshot.geometry_nodes_visited = self.metadatas.geometry_visits;
2596 diagnostics_snapshot.pointer_nodes_updated = self.pointer_work.0;
2597 diagnostics_snapshot.pointer_nodes_dispatched = self.pointer_work.1.len() as u64;
2598 diagnostics_snapshot.pointer_hit_test_candidates =
2599 self.pointer_index.hit_candidates.get();
2600 return (
2601 RenderGraph::default(),
2602 WindowRequests::default(),
2603 diagnostics_snapshot,
2604 std::time::Duration::ZERO,
2605 None,
2606 false,
2607 false,
2608 );
2609 }
2610 };
2611
2612 let record_timer = Instant::now();
2613 let _focus_owner_guard = bind_focus_owner(&mut self.focus_owner);
2614 record_layout_commands(root_node, &self.tree, &mut self.metadatas);
2615 let record_cost = record_timer.elapsed();
2616 record_frame_stage(&mut stage_timer, "paint");
2617 diagnostics_snapshot.geometry_nodes_updated +=
2618 populate_layout_metadata(root_node, &self.tree, &mut self.metadatas);
2619 record_frame_stage(&mut stage_timer, "geometry_after_paint");
2620 tracing::debug!(
2621 target: "tessera_ui::frame_cost",
2622 geometry_journal_nodes = self.metadatas.geometry_changed.len(),
2623 "geometry_journal"
2624 );
2625 self.collect_globally_positioned_invocations(screen_size);
2626 record_frame_stage(&mut stage_timer, "position_observers");
2627 diagnostics_snapshot.accessibility_nodes_updated =
2628 self.sync_accessibility(root_node, false) as u64;
2629 record_frame_stage(&mut stage_timer, "accessibility");
2630 diagnostics_snapshot.paint_nodes_recorded = self.metadatas.paint_changed.len() as u64;
2631
2632 let compute_draw_timer = Instant::now();
2633 debug!("Start computing render graph...");
2634 let (graph, scene_updates) = self.frame_scene.update(
2635 root_node,
2636 &self.tree,
2637 &self.metadatas,
2638 &self.changed_frame_nodes,
2639 &self.removed_frame_nodes,
2640 screen_size,
2641 );
2642 diagnostics_snapshot.scene_nodes_updated = scene_updates;
2643 record_frame_stage(&mut stage_timer, "scene");
2644 self.focus_owner.sync_geometry_changes(
2645 &self.tree,
2646 &self.metadatas,
2647 &self.metadatas.geometry_changed,
2648 &self.changed_frame_nodes,
2649 );
2650 self.clear_frame_structure_changes();
2651 record_frame_stage(&mut stage_timer, "focus_geometry");
2652 debug!(
2653 "Render graph built in {:?}, total ops: {}",
2654 compute_draw_timer.elapsed(),
2655 graph.ops().len()
2656 );
2657
2658 let input_dispatch_timer = Instant::now();
2659 debug!("Start executing typed input dispatch...");
2660 let mut window_requests = self.dispatch_pointer_input(
2661 root_node,
2662 pointer_changes.as_mut_slice(),
2663 &mut cursor_position,
2664 modifiers,
2665 );
2666 record_frame_stage(&mut stage_timer, "pointer");
2667
2668 diagnostics_snapshot.geometry_nodes_visited = self.metadatas.geometry_visits;
2669 diagnostics_snapshot.pointer_nodes_updated = self.pointer_work.0;
2670 diagnostics_snapshot.pointer_nodes_dispatched = self.pointer_work.1.len() as u64;
2671 diagnostics_snapshot.pointer_hit_test_candidates = self.pointer_index.hit_candidates.get();
2672 self.focus_owner.commit_pending();
2673 let requested_move = self
2674 .focus_owner
2675 .take_pending_navigation()
2676 .or(retry_focus_move);
2677 let pending_focus_move_retry = requested_move.and_then(|direction| {
2678 match try_dispatch_focus_move_request(&self.tree, direction, &mut self.focus_owner) {
2679 FocusMoveRequestResult::Retry(direction) => Some(direction),
2680 FocusMoveRequestResult::Moved | FocusMoveRequestResult::NotHandled => None,
2681 }
2682 });
2683 self.queue_focused_input(keyboard_events, ime_events);
2684 let pending_focus_move_retry = pending_focus_move_retry
2685 .or_else(|| self.dispatch_pending_focused_input(root_node, &mut window_requests));
2686 self.focus_owner
2687 .retain_navigation_retry(pending_focus_move_retry);
2688 let pending_keyboard_retry =
2689 needs_deferred_focus_retry(&self.focus_owner, !self.pending_focused_input.is_empty());
2690 let pending_focus_reveal_retry =
2691 if pending_focus_move_retry.is_none() && !self.focus_owner.keyboard_is_deferred() {
2692 dispatch_pending_focus_reveal_request(
2693 &self.tree,
2694 &self.metadatas,
2695 &mut self.focus_owner,
2696 retry_focus_reveal,
2697 )
2698 } else {
2699 false
2700 };
2701
2702 debug!(
2703 "Typed input dispatch executed in {:?}",
2704 input_dispatch_timer.elapsed()
2705 );
2706 record_frame_stage(&mut stage_timer, "focused_input");
2707 (
2708 graph,
2709 window_requests,
2710 diagnostics_snapshot,
2711 record_cost,
2712 pending_focus_move_retry,
2713 pending_focus_reveal_retry,
2714 pending_keyboard_retry,
2715 )
2716 }
2717}
2718
2719fn drain_pending_focused_input<E>(
2720 queue: &mut VecDeque<E>,
2721 mut dispatch: impl FnMut(E) -> (Option<FocusDirection>, bool),
2722) -> Option<FocusDirection> {
2723 while let Some(event) = queue.pop_front() {
2724 let (retry, defer) = dispatch(event);
2725 if let Some(direction) = retry {
2726 return Some(direction);
2727 }
2728 if defer {
2729 return None;
2730 }
2731 }
2732 None
2733}
2734
2735#[derive(Clone)]
2736struct NodeInputContext {
2737 local_to_window: crate::LocalToWindow,
2738 input_transforms: Option<node::InputTransforms>,
2739 event_clip_bounds: Option<crate::FloatRect>,
2740 base_abs_pos: PxPosition,
2741 abs_pos: PxPosition,
2742 event_clip_rect: Option<PxRect>,
2743 node_computed_data: ComputedData,
2744 instance_logic_id: u64,
2745 instance_key: u64,
2746 fn_name: &'static str,
2747 parent_id: Option<indextree::NodeId>,
2748}
2749
2750fn resolve_node_input_context(
2751 tree: &ComponentNodeTree,
2752 metadatas: &ComponentNodeMetaDatas,
2753 node_id: indextree::NodeId,
2754) -> Option<NodeInputContext> {
2755 let Some(node_ref) = tree.get(node_id) else {
2756 warn!("Node not found for node {node_id:?}; skipping input handling");
2757 return None;
2758 };
2759 let node = node_ref.get();
2760 if node.role != NodeRole::Layout {
2761 return None;
2762 }
2763
2764 let Some(metadata) = metadatas.get(&node_id) else {
2765 warn!("Input metadata missing for node {node_id:?}; skipping input handling");
2766 return None;
2767 };
2768 let Some(base_abs_pos) = metadata.base_abs_position else {
2769 warn!("Base absolute position missing for node {node_id:?}; skipping input handling");
2770 return None;
2771 };
2772 let Some(abs_pos) = metadata.abs_position else {
2773 warn!("Absolute position missing for node {node_id:?}; skipping input handling");
2774 return None;
2775 };
2776 let event_clip_rect = metadata.event_clip_rect();
2777 let Some(node_computed_data) = metadata.computed_data else {
2778 warn!(
2779 "Computed data not found for node {:?} during input dispatch.",
2780 node_id
2781 );
2782 return None;
2783 };
2784 let instance_logic_id = node.instance_logic_id;
2785 let instance_key = node.instance_key;
2786 let parent_id = node_ref.parent();
2787
2788 Some(NodeInputContext {
2789 local_to_window: metadata
2790 .local_to_window
2791 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, abs_pos).unwrap()),
2792 input_transforms: metadata.input_transforms.as_deref().cloned(),
2793 event_clip_bounds: metadata.event_clip_bounds,
2794 base_abs_pos,
2795 abs_pos,
2796 event_clip_rect,
2797 node_computed_data,
2798 instance_logic_id,
2799 instance_key,
2800 fn_name: node.fn_name,
2801 parent_id,
2802 })
2803}
2804
2805fn attach_ime_position_if_needed(
2806 window_requests: &mut WindowRequests,
2807 transform: crate::LocalToWindow,
2808 source: Option<crate::focus::FocusHandleId>,
2809 active: Option<crate::focus::FocusHandleId>,
2810) {
2811 let Some(request) = window_requests.ime_request.as_mut() else {
2812 return;
2813 };
2814 let source = if request.position.is_none() {
2815 source
2816 } else {
2817 request.client_id
2818 };
2819 if source.is_none() || source != active {
2820 window_requests.ime_request = None;
2821 return;
2822 }
2823 if request.position.is_none() {
2824 request.client_id = source;
2825 request.position = Some(transform.map_point(request.local_position));
2826 let size = transform.map_size(request.size);
2827 request.size = PxSize::new(
2828 crate::Px(size[0].ceil() as i32),
2829 crate::Px(size[1].ceil() as i32),
2830 );
2831 }
2832}
2833
2834fn inside_shape_clips(
2835 tree: &ComponentNodeTree,
2836 metadatas: &ComponentNodeMetaDatas,
2837 node_id: indextree::NodeId,
2838 position: PxPosition,
2839) -> bool {
2840 if !metadatas
2841 .get(&node_id)
2842 .is_some_and(|metadata| metadata.has_shape_clipping)
2843 {
2844 return true;
2845 }
2846 node_id.ancestors(tree).all(|ancestor| {
2847 let Some(metadata) = metadatas.get(&ancestor) else {
2848 return true;
2849 };
2850 let (Some(®ion), Some(origin)) = (metadata.clip_region(), metadata.abs_position) else {
2851 return true;
2852 };
2853 let transform = metadata
2854 .local_to_window
2855 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, origin).unwrap());
2856 map_clip_region(region, transform).contains(position)
2857 })
2858}
2859
2860fn bounds_hit_path(
2861 node_id: indextree::NodeId,
2862 tree: &ComponentNodeTree,
2863 metadatas: &ComponentNodeMetaDatas,
2864 position: PxPosition,
2865) -> Option<indextree::NodeId> {
2866 let node = tree.get(node_id)?;
2867 if node.get().role != NodeRole::Layout {
2868 return None;
2869 }
2870 let metadata = metadatas.get(&node_id)?;
2871 let base_abs_pos = metadata.base_abs_position?;
2872 let abs_pos = metadata.abs_position?;
2873 let size = metadata.computed_data?;
2874 if size.width.0 <= 0 || size.height.0 <= 0 {
2875 return None;
2876 }
2877 if metadata
2878 .event_clip_bounds
2879 .is_some_and(|bounds| !bounds.contains(position))
2880 {
2881 return None;
2882 }
2883 if let Some(clip_rect) = metadata.event_clip_rect()
2884 && !clip_rect.contains(position)
2885 {
2886 return None;
2887 }
2888 if !inside_shape_clips(tree, metadatas, node_id, position) {
2889 return None;
2890 }
2891 let transform = metadata
2892 .local_to_window
2893 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, abs_pos).unwrap());
2894 let bounds = transform.map_rect(crate::FloatRect::from_position_size(
2895 PxPosition::ZERO,
2896 PxSize::new(size.width, size.height),
2897 ));
2898 let node_data = node.get();
2899 let has_pointer_modifier = node_data.modifier.ordered_actions().iter().any(|action| {
2900 matches!(
2901 action,
2902 OrderedModifierAction::Cursor(_)
2903 | OrderedModifierAction::PointerPreviewInput(_)
2904 | OrderedModifierAction::PointerInput(_)
2905 | OrderedModifierAction::PointerFinalInput(_)
2906 )
2907 });
2908 let has_pointer_handler = node_data.interaction().has_pointer_handlers();
2909 if !has_pointer_modifier && !has_pointer_handler {
2910 return None;
2911 }
2912 let node_handles_hover = has_pointer_modifier
2913 && node_handles_pointer_at_position(
2914 node_data,
2915 base_abs_pos,
2916 size,
2917 position,
2918 transform.scale(),
2919 metadata.input_transforms.as_deref(),
2920 )
2921 || bounds.contains(position) && has_pointer_handler;
2922 node_handles_hover.then_some(node_id)
2923}
2924
2925fn node_handles_pointer_at_position(
2926 node: &crate::component_tree::ComponentNode,
2927 base_abs_pos: PxPosition,
2928 size: ComputedData,
2929 position: PxPosition,
2930 scale: f32,
2931 transforms: Option<&node::InputTransforms>,
2932) -> bool {
2933 let mut current_abs_pos = base_abs_pos;
2934 let size = PxSize::new(size.width, size.height);
2935 for (index, action) in node.modifier.ordered_actions().into_iter().enumerate() {
2936 if let Some(transform) = transforms.and_then(|transforms| transforms.get(index)) {
2937 current_abs_pos = transform.translation();
2938 }
2939 match action {
2940 OrderedModifierAction::Placement(placement) => {
2941 if transforms.is_none() {
2942 crate::work::record(
2943 node.instance_logic_id,
2944 crate::WorkPhase::Transform,
2945 placement.node().diagnostic_name(),
2946 );
2947 current_abs_pos = placement.node().transform_position(current_abs_pos);
2948 }
2949 }
2950 OrderedModifierAction::Cursor(_)
2951 | OrderedModifierAction::PointerPreviewInput(_)
2952 | OrderedModifierAction::PointerInput(_)
2953 | OrderedModifierAction::PointerFinalInput(_) => {
2954 let bounds = crate::LocalToWindow::new(scale, current_abs_pos)
2955 .unwrap()
2956 .map_rect(crate::FloatRect::from_position_size(PxPosition::ZERO, size));
2957 if bounds.contains(position) {
2958 return true;
2959 }
2960 }
2961 _ => {}
2962 }
2963 }
2964 false
2965}
2966
2967fn resolve_node_hover_cursor_icon(
2968 node: &crate::component_tree::ComponentNode,
2969 base_abs_pos: PxPosition,
2970 size: ComputedData,
2971 position: PxPosition,
2972 scale: f32,
2973 transforms: Option<&node::InputTransforms>,
2974) -> Option<winit::window::CursorIcon> {
2975 let mut current_abs_pos = base_abs_pos;
2976 let size = PxSize::new(size.width, size.height);
2977 let mut resolved = None;
2978 for (index, action) in node.modifier.ordered_actions().into_iter().enumerate() {
2979 if let Some(transform) = transforms.and_then(|transforms| transforms.get(index)) {
2980 current_abs_pos = transform.translation();
2981 }
2982 match action {
2983 OrderedModifierAction::Placement(placement) => {
2984 if transforms.is_none() {
2985 crate::work::record(
2986 node.instance_logic_id,
2987 crate::WorkPhase::Transform,
2988 placement.node().diagnostic_name(),
2989 );
2990 current_abs_pos = placement.node().transform_position(current_abs_pos);
2991 }
2992 }
2993 OrderedModifierAction::Cursor(cursor) => {
2994 let bounds = crate::LocalToWindow::new(scale, current_abs_pos)
2995 .unwrap()
2996 .map_rect(crate::FloatRect::from_position_size(PxPosition::ZERO, size));
2997 if bounds.contains(position) {
2998 resolved = Some(cursor.cursor_icon());
2999 }
3000 }
3001 _ => {}
3002 }
3003 }
3004 resolved
3005}
3006
3007fn build_pointer_change_paths(
3008 index: &pointer::PointerIndex,
3009 tree: &ComponentNodeTree,
3010 metadatas: &ComponentNodeMetaDatas,
3011 pointer_changes: &[PointerChange],
3012 cursor_position: Option<PxPosition>,
3013 active_pointer_paths: &mut HashMap<u64, Vec<u64>>,
3014) -> Vec<Vec<u64>> {
3015 let mut paths = Vec::with_capacity(pointer_changes.len());
3016 for change in pointer_changes {
3017 let debug_position = change.resolved_position(cursor_position);
3018 let path = match &change.content {
3019 CursorEventContent::Pressed(_) => {
3020 let computed_path = hit_path_instance_keys(index, tree, metadatas, debug_position);
3021 if computed_path.is_empty() {
3022 active_pointer_paths.remove(&change.pointer_id);
3023 } else {
3024 active_pointer_paths.insert(change.pointer_id, computed_path.clone());
3025 }
3026 computed_path
3027 }
3028 CursorEventContent::Moved(position) => active_pointer_paths
3029 .get(&change.pointer_id)
3030 .cloned()
3031 .unwrap_or_else(|| hit_path_instance_keys(index, tree, metadatas, Some(*position))),
3032 CursorEventContent::Released(_) | CursorEventContent::Cancelled(_) => {
3033 let computed = active_pointer_paths
3034 .get(&change.pointer_id)
3035 .cloned()
3036 .unwrap_or_else(|| {
3037 hit_path_instance_keys(index, tree, metadatas, debug_position)
3038 });
3039 active_pointer_paths.remove(&change.pointer_id);
3040 computed
3041 }
3042 CursorEventContent::Scroll(_) => active_pointer_paths
3043 .get(&change.pointer_id)
3044 .cloned()
3045 .unwrap_or_else(|| hit_path_instance_keys(index, tree, metadatas, debug_position)),
3046 };
3047 paths.push(path);
3048 }
3049 paths
3050}
3051
3052fn hit_path_instance_keys(
3053 index: &pointer::PointerIndex,
3054 tree: &ComponentNodeTree,
3055 metadatas: &ComponentNodeMetaDatas,
3056 position: Option<PxPosition>,
3057) -> Vec<u64> {
3058 index
3059 .hit_path(tree, metadatas, position)
3060 .into_iter()
3061 .filter_map(|node_id| tree.get(node_id).map(|node| node.get().instance_key))
3062 .collect()
3063}
3064
3065fn collect_focus_chain_node_ids(
3066 root_node: indextree::NodeId,
3067 tree: &ComponentNodeTree,
3068 focus_owner: &FocusOwner,
3069) -> Vec<indextree::NodeId> {
3070 fn live_node(
3071 tree: &ComponentNodeTree,
3072 node_id: indextree::NodeId,
3073 ) -> Option<&indextree::Node<ComponentNode>> {
3074 tree.get(node_id).filter(|node| !node.is_removed())
3075 }
3076
3077 let Some(focused_node_id) = focus_owner.active_component_node_id() else {
3078 return Vec::new();
3079 };
3080 if live_node(tree, focused_node_id).is_none() {
3081 return Vec::new();
3082 }
3083
3084 let mut chain = Vec::new();
3085 let mut current = Some(focused_node_id);
3086 while let Some(node_id) = current {
3087 chain.push(node_id);
3088 if node_id == root_node {
3089 break;
3090 }
3091 current = live_node(tree, node_id).and_then(|node| node.parent());
3092 }
3093 if chain.last().copied() != Some(root_node) {
3094 return Vec::new();
3095 }
3096 chain.reverse();
3097 chain
3098}
3099
3100#[derive(Clone)]
3101struct PointerNodePlan {
3102 context: NodeInputContext,
3103 actions: Vec<OrderedModifierAction>,
3104}
3105
3106struct PointerDispatchPlan {
3107 preorder: Vec<indextree::NodeId>,
3108 postorder: Vec<indextree::NodeId>,
3109 nodes: HashMap<indextree::NodeId, Arc<PointerNodePlan>>,
3110}
3111
3112struct PointerInputDispatchContext<'a> {
3113 tree: &'a ComponentNodeTree,
3114 metadatas: &'a ComponentNodeMetaDatas,
3115 cursor_position: &'a mut Option<PxPosition>,
3116 pointer_changes: &'a mut [PointerChange],
3117 pointer_change_paths: &'a [Vec<u64>],
3118 nodes: &'a HashMap<indextree::NodeId, Arc<PointerNodePlan>>,
3119 active_pointer_paths: &'a HashMap<u64, Vec<u64>>,
3120 active_pointer_positions: &'a HashMap<u64, Option<PxPosition>>,
3121 modifiers: winit::keyboard::ModifiersState,
3122 window_requests: &'a mut WindowRequests,
3123 focus_owner: &'a mut FocusOwner,
3124}
3125
3126fn dispatch_pointer_modifiers_for_node_pass(
3127 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
3128 node_id: indextree::NodeId,
3129 pass: PointerEventPass,
3130) {
3131 let nodes = dispatch_ctx.nodes;
3132 let Some(plan) = nodes.get(&node_id) else {
3133 return;
3134 };
3135 let mut current_abs_pos = plan.context.base_abs_pos;
3136 for (index, action) in plan.actions.iter().enumerate() {
3137 if let Some(transform) = plan
3138 .context
3139 .input_transforms
3140 .as_ref()
3141 .and_then(|transforms| transforms.get(index))
3142 {
3143 current_abs_pos = transform.translation();
3144 }
3145 match action {
3146 OrderedModifierAction::Placement(placement) => {
3147 if plan.context.input_transforms.is_none() {
3148 crate::work::record(
3149 plan.context.instance_logic_id,
3150 crate::WorkPhase::Transform,
3151 placement.node().diagnostic_name(),
3152 );
3153 current_abs_pos = placement.node().transform_position(current_abs_pos);
3154 }
3155 }
3156 OrderedModifierAction::PointerPreviewInput(modifier)
3157 if pass == PointerEventPass::Initial =>
3158 {
3159 run_pointer_modifier_for_node(
3160 dispatch_ctx,
3161 node_id,
3162 pass,
3163 current_abs_pos,
3164 modifier.as_ref(),
3165 );
3166 }
3167 OrderedModifierAction::PointerInput(modifier) if pass == PointerEventPass::Main => {
3168 run_pointer_modifier_for_node(
3169 dispatch_ctx,
3170 node_id,
3171 pass,
3172 current_abs_pos,
3173 modifier.as_ref(),
3174 );
3175 }
3176 OrderedModifierAction::PointerFinalInput(modifier)
3177 if pass == PointerEventPass::Final =>
3178 {
3179 run_pointer_modifier_for_node(
3180 dispatch_ctx,
3181 node_id,
3182 pass,
3183 current_abs_pos,
3184 modifier.as_ref(),
3185 );
3186 }
3187 _ => {}
3188 }
3189 }
3190}
3191
3192fn run_pointer_handler_for_node(
3193 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
3194 node_id: indextree::NodeId,
3195 pass: PointerEventPass,
3196 pointer_handler: &PointerInputHandlerFn,
3197) {
3198 let Some(abs_pos) = dispatch_ctx
3199 .nodes
3200 .get(&node_id)
3201 .map(|node| node.context.abs_pos)
3202 else {
3203 return;
3204 };
3205 run_pointer_input_for_node(dispatch_ctx, node_id, pass, abs_pos, pointer_handler);
3206}
3207
3208fn run_pointer_modifier_for_node(
3209 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
3210 node_id: indextree::NodeId,
3211 pass: PointerEventPass,
3212 abs_pos_override: PxPosition,
3213 pointer_modifier: &dyn PointerInputModifierNode,
3214) {
3215 run_pointer_input_for_node(dispatch_ctx, node_id, pass, abs_pos_override, |input| {
3216 pointer_modifier.on_pointer_input(input)
3217 });
3218}
3219
3220fn run_pointer_input_for_node<F>(
3221 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
3222 node_id: indextree::NodeId,
3223 pass: PointerEventPass,
3224 abs_pos_override: PxPosition,
3225 dispatch: F,
3226) where
3227 F: FnOnce(PointerInput<'_>),
3228{
3229 let nodes = dispatch_ctx.nodes;
3230 let Some(NodeInputContext {
3231 local_to_window,
3232 input_transforms: _,
3233 event_clip_bounds,
3234 base_abs_pos: _,
3235 abs_pos: _,
3236 event_clip_rect,
3237 node_computed_data,
3238 instance_logic_id,
3239 instance_key,
3240 fn_name,
3241 parent_id,
3242 }) = nodes.get(&node_id).map(|node| &node.context)
3243 else {
3244 return;
3245 };
3246 let event_clip_rect = *event_clip_rect;
3247 let node_computed_data = *node_computed_data;
3248 let instance_logic_id = *instance_logic_id;
3249 let instance_key = *instance_key;
3250 let parent_id = *parent_id;
3251 #[cfg(not(feature = "profiling"))]
3252 let _ = parent_id;
3253
3254 let abs_pos = abs_pos_override;
3255 let transform = crate::LocalToWindow::new(local_to_window.scale(), abs_pos).unwrap();
3256
3257 let owns_sample = dispatch_ctx
3258 .pointer_change_paths
3259 .iter()
3260 .any(|path| path.contains(&instance_key));
3261 let captured_position = if owns_sample {
3262 None
3263 } else {
3264 dispatch_ctx
3265 .active_pointer_paths
3266 .iter()
3267 .filter_map(|(&pointer_id, path)| {
3268 if pointer_id == MOUSE_POINTER_ID || !path.contains(&instance_key) {
3269 return None;
3270 }
3271 dispatch_ctx
3272 .active_pointer_positions
3273 .get(&pointer_id)
3274 .map(|position| (pointer_id, *position))
3275 })
3276 .min_by_key(|(pointer_id, _)| *pointer_id)
3277 .map(|(_, position)| position)
3278 };
3279 let mut captured_cursor_position = captured_position.flatten();
3280 let mut cursor_position_ref = if captured_position.is_some() {
3281 &mut captured_cursor_position
3282 } else {
3283 &mut *dispatch_ctx.cursor_position
3284 };
3285 let mut dummy_cursor_position = None;
3286 if let (Some(cursor_pos), Some(clip_rect)) = (*cursor_position_ref, event_clip_rect)
3287 && !clip_rect.contains(cursor_pos)
3288 {
3289 cursor_position_ref = &mut dummy_cursor_position;
3290 }
3291 if let Some(position) = *cursor_position_ref
3292 && !inside_shape_clips(dispatch_ctx.tree, dispatch_ctx.metadatas, node_id, position)
3293 {
3294 cursor_position_ref = &mut dummy_cursor_position;
3295 }
3296 if let (Some(position), Some(bounds)) = (*cursor_position_ref, *event_clip_bounds) {
3297 if !bounds.contains(position) {
3298 cursor_position_ref = &mut dummy_cursor_position;
3299 }
3300 }
3301 let current_cursor_position = cursor_position_ref.map(|pos| transform.inverse_point(pos));
3302 let mut selected_change_indices = Vec::new();
3303 let mut local_pointer_changes = Vec::new();
3304 for (index, change) in dispatch_ctx.pointer_changes.iter().enumerate() {
3305 if change.is_consumed() {
3306 continue;
3307 }
3308 let Some(path) = dispatch_ctx.pointer_change_paths.get(index) else {
3309 continue;
3310 };
3311 if path.contains(&instance_key) {
3312 selected_change_indices.push(index);
3313 let mut local_change = change.clone();
3314 if transform.scale() != 1.0
3315 && let CursorEventContent::Scroll(scroll) = &mut local_change.content
3316 {
3317 scroll.normalize_to_pixels();
3318 scroll.delta_x /= transform.scale();
3319 scroll.delta_y /= transform.scale();
3320 }
3321 local_pointer_changes.push(local_change);
3322 }
3323 }
3324 #[cfg(feature = "profiling")]
3325 let mut profiler_guard = Some(ProfilerScopeGuard::new(
3326 ProfilerPhase::Input,
3327 Some(node_id),
3328 parent_id,
3329 Some(fn_name),
3330 ));
3331 let replay_boundary_instance_key =
3332 nearest_replay_boundary_instance_key(node_id, dispatch_ctx.tree);
3333 let _node_ctx_guard =
3334 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name);
3335 let _instance_ctx_guard = push_current_component_instance_key(replay_boundary_instance_key);
3336 let _phase_guard = push_phase(RuntimePhase::Input);
3337 let _focus_owner_guard = bind_focus_owner(dispatch_ctx.focus_owner);
3338 let input = PointerInput {
3339 pass,
3340 local_to_window: transform,
3341 computed_data: node_computed_data,
3342 cursor_position_rel: current_cursor_position,
3343 cursor_position_abs: cursor_position_ref,
3344 pointer_changes: &mut local_pointer_changes,
3345 key_modifiers: dispatch_ctx.modifiers,
3346 ime_request: &mut dispatch_ctx.window_requests.ime_request,
3347 request_window_drag: &mut dispatch_ctx.window_requests.request_window_drag,
3348 };
3349 crate::runtime::with_pointer_state_reader(instance_key, || dispatch(input));
3350 for (local_change, &original_index) in local_pointer_changes
3351 .iter()
3352 .zip(selected_change_indices.iter())
3353 {
3354 if let Some(original) = dispatch_ctx.pointer_changes.get_mut(original_index) {
3355 let mut window_change = local_change.clone();
3356 if let CursorEventContent::Scroll(scroll) = &mut window_change.content {
3357 scroll.delta_x *= transform.scale();
3358 scroll.delta_y *= transform.scale();
3359 }
3360 *original = window_change;
3361 }
3362 }
3363
3364 #[cfg(feature = "profiling")]
3365 {
3366 let abs_tuple = (abs_pos.x_px().0, abs_pos.y_px().0);
3367 if let Some(g) = &mut profiler_guard {
3368 g.set_positions(Some(abs_tuple));
3369 }
3370 let _ = profiler_guard.take();
3371 }
3372 let ime_client = dispatch_ctx
3373 .window_requests
3374 .ime_request
3375 .as_ref()
3376 .filter(|request| request.position.is_none())
3377 .and_then(|_| dispatch_ctx.focus_owner.active_component_node_id())
3378 .filter(|focused| {
3379 focused
3380 .ancestors(dispatch_ctx.tree)
3381 .any(|owner| owner == node_id)
3382 || node_id
3383 .ancestors(dispatch_ctx.tree)
3384 .any(|owner| owner == *focused)
3385 })
3386 .and(dispatch_ctx.focus_owner.active_handle_id());
3387 attach_ime_position_if_needed(
3388 dispatch_ctx.window_requests,
3389 transform,
3390 ime_client,
3391 dispatch_ctx.focus_owner.active_handle_id(),
3392 );
3393}
3394
3395struct KeyboardInputDispatchContext<'a> {
3396 tree: &'a ComponentNodeTree,
3397 metadatas: &'a ComponentNodeMetaDatas,
3398 keyboard_events: &'a mut Vec<crate::KeyboardEvent>,
3399 modifiers: winit::keyboard::ModifiersState,
3400 ime_client: Option<FocusHandleId>,
3401 window_requests: &'a mut WindowRequests,
3402 focus_owner: &'a mut FocusOwner,
3403}
3404
3405fn run_keyboard_input_for_node<F>(
3406 dispatch_ctx: &mut KeyboardInputDispatchContext<'_>,
3407 node_id: indextree::NodeId,
3408 dispatch: F,
3409) where
3410 F: FnOnce(KeyboardInput<'_>),
3411{
3412 let Some(NodeInputContext {
3413 local_to_window: transform,
3414 abs_pos,
3415 event_clip_rect: _,
3416 node_computed_data,
3417 instance_logic_id,
3418 instance_key: _,
3419 fn_name,
3420 parent_id,
3421 ..
3422 }) = resolve_node_input_context(dispatch_ctx.tree, dispatch_ctx.metadatas, node_id)
3423 else {
3424 return;
3425 };
3426 #[cfg(not(feature = "profiling"))]
3427 let _ = (parent_id, abs_pos);
3428
3429 #[cfg(feature = "profiling")]
3430 let mut profiler_guard = Some(ProfilerScopeGuard::new(
3431 ProfilerPhase::Input,
3432 Some(node_id),
3433 parent_id,
3434 Some(fn_name),
3435 ));
3436 let replay_boundary_instance_key =
3437 nearest_replay_boundary_instance_key(node_id, dispatch_ctx.tree);
3438 let _node_ctx_guard =
3439 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name);
3440 let _instance_ctx_guard = push_current_component_instance_key(replay_boundary_instance_key);
3441 let _phase_guard = push_phase(RuntimePhase::Input);
3442 let _focus_owner_guard = bind_focus_owner(dispatch_ctx.focus_owner);
3443 let input = KeyboardInput {
3444 computed_data: node_computed_data,
3445 keyboard_events: dispatch_ctx.keyboard_events,
3446 key_modifiers: dispatch_ctx.modifiers,
3447 ime_request: &mut dispatch_ctx.window_requests.ime_request,
3448 };
3449 dispatch(input);
3450
3451 #[cfg(feature = "profiling")]
3452 {
3453 let abs_tuple = (abs_pos.x_px().0, abs_pos.y_px().0);
3454 if let Some(g) = &mut profiler_guard {
3455 g.set_positions(Some(abs_tuple));
3456 }
3457 let _ = profiler_guard.take();
3458 }
3459 attach_ime_position_if_needed(
3460 dispatch_ctx.window_requests,
3461 transform,
3462 dispatch_ctx.ime_client,
3463 dispatch_ctx.focus_owner.active_handle_id(),
3464 );
3465}
3466
3467fn run_ime_input_for_node<F>(
3468 tree: &ComponentNodeTree,
3469 metadatas: &ComponentNodeMetaDatas,
3470 node_id: indextree::NodeId,
3471 ime_events: &mut Vec<winit::event::Ime>,
3472 window_requests: &mut WindowRequests,
3473 focus_owner: &mut FocusOwner,
3474 ime_client: Option<FocusHandleId>,
3475 dispatch: F,
3476) where
3477 F: FnOnce(ImeInput<'_>),
3478{
3479 let Some(NodeInputContext {
3480 local_to_window: transform,
3481 abs_pos,
3482 event_clip_rect: _,
3483 node_computed_data,
3484 instance_logic_id,
3485 instance_key: _,
3486 fn_name,
3487 parent_id,
3488 ..
3489 }) = resolve_node_input_context(tree, metadatas, node_id)
3490 else {
3491 return;
3492 };
3493 #[cfg(not(feature = "profiling"))]
3494 let _ = (parent_id, abs_pos);
3495
3496 #[cfg(feature = "profiling")]
3497 let mut profiler_guard = Some(ProfilerScopeGuard::new(
3498 ProfilerPhase::Input,
3499 Some(node_id),
3500 parent_id,
3501 Some(fn_name),
3502 ));
3503 let replay_boundary_instance_key = nearest_replay_boundary_instance_key(node_id, tree);
3504 let _node_ctx_guard =
3505 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name);
3506 let _instance_ctx_guard = push_current_component_instance_key(replay_boundary_instance_key);
3507 let _phase_guard = push_phase(RuntimePhase::Input);
3508 let _focus_owner_guard = bind_focus_owner(focus_owner);
3509 let input = ImeInput {
3510 computed_data: node_computed_data,
3511 ime_events,
3512 ime_request: &mut window_requests.ime_request,
3513 };
3514 dispatch(input);
3515
3516 #[cfg(feature = "profiling")]
3517 {
3518 let abs_tuple = (abs_pos.x_px().0, abs_pos.y_px().0);
3519 if let Some(g) = &mut profiler_guard {
3520 g.set_positions(Some(abs_tuple));
3521 }
3522 let _ = profiler_guard.take();
3523 }
3524 attach_ime_position_if_needed(
3525 window_requests,
3526 transform,
3527 ime_client,
3528 focus_owner.active_handle_id(),
3529 );
3530}
3531
3532fn dispatch_default_focus_keyboard_navigation(
3533 tree: &ComponentNodeTree,
3534 keyboard_events: &mut Vec<crate::KeyboardEvent>,
3535 modifiers: winit::keyboard::ModifiersState,
3536 focus_owner: &mut FocusOwner,
3537) -> Option<FocusDirection> {
3538 if keyboard_events.is_empty() {
3539 return None;
3540 }
3541
3542 let mut pending_focus_move_retry = None;
3543 keyboard_events.retain(|event| {
3544 if pending_focus_move_retry.is_some() {
3545 return false;
3546 }
3547 let Some(direction) = default_focus_navigation_direction(event, modifiers) else {
3548 return true;
3549 };
3550 match try_dispatch_focus_move_request(tree, direction, focus_owner) {
3551 FocusMoveRequestResult::Moved => false,
3552 FocusMoveRequestResult::Retry(direction) => {
3553 pending_focus_move_retry = Some(direction);
3554 false
3555 }
3556 FocusMoveRequestResult::NotHandled => true,
3557 }
3558 });
3559 pending_focus_move_retry
3560}
3561
3562fn default_focus_navigation_direction(
3563 event: &crate::KeyboardEvent,
3564 modifiers: winit::keyboard::ModifiersState,
3565) -> Option<FocusDirection> {
3566 if event.state != winit::event::ElementState::Pressed {
3567 return None;
3568 }
3569
3570 if modifiers.control_key() || modifiers.alt_key() || modifiers.super_key() {
3571 return None;
3572 }
3573
3574 match &event.logical_key {
3575 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab) => {
3576 if modifiers.shift_key() {
3577 Some(FocusDirection::Previous)
3578 } else {
3579 Some(FocusDirection::Next)
3580 }
3581 }
3582 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft) => {
3583 (!modifiers.shift_key()).then_some(FocusDirection::Left)
3584 }
3585 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowRight) => {
3586 (!modifiers.shift_key()).then_some(FocusDirection::Right)
3587 }
3588 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp) => {
3589 (!modifiers.shift_key()).then_some(FocusDirection::Up)
3590 }
3591 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown) => {
3592 (!modifiers.shift_key()).then_some(FocusDirection::Down)
3593 }
3594 _ => None,
3595 }
3596}
3597
3598enum FocusMoveRequestResult {
3599 NotHandled,
3600 Moved,
3601 Retry(FocusDirection),
3602}
3603
3604fn try_dispatch_focus_move_request(
3605 tree: &ComponentNodeTree,
3606 direction: FocusDirection,
3607 focus_owner: &mut FocusOwner,
3608) -> FocusMoveRequestResult {
3609 if !focus_owner.allows_navigation() {
3610 return FocusMoveRequestResult::NotHandled;
3611 }
3612 if focus_owner.move_focus(direction) {
3613 return FocusMoveRequestResult::Moved;
3614 }
3615 if dispatch_focus_beyond_bounds_request(tree, direction, focus_owner) {
3616 return FocusMoveRequestResult::Retry(direction);
3617 }
3618 FocusMoveRequestResult::NotHandled
3619}
3620
3621fn dispatch_focus_beyond_bounds_request(
3622 tree: &ComponentNodeTree,
3623 direction: FocusDirection,
3624 focus_owner: &FocusOwner,
3625) -> bool {
3626 let mut current = focus_owner.active_component_node_id();
3627 while let Some(node_id) = current {
3628 let Some(node_ref) = tree.get(node_id) else {
3629 break;
3630 };
3631 let node = node_ref.get();
3632 if let Some(handler) = &node.interaction().focus_beyond_bounds_handler
3633 && handler.call(direction)
3634 {
3635 return true;
3636 }
3637 current = node_ref.parent();
3638 }
3639 false
3640}
3641
3642fn needs_deferred_focus_retry(focus_owner: &FocusOwner, has_queued_input: bool) -> bool {
3643 focus_owner.keyboard_is_deferred() && (has_queued_input || focus_owner.has_pending_reveal())
3644}
3645
3646pub(crate) fn dispatch_pending_focus_reveal_request(
3647 tree: &ComponentNodeTree,
3648 metadatas: &ComponentNodeMetaDatas,
3649 focus_owner: &mut FocusOwner,
3650 retry_active_focus: bool,
3651) -> bool {
3652 let handle_id = focus_owner.take_pending_reveal().or_else(|| {
3653 retry_active_focus
3654 .then(|| focus_owner.active_handle_id())
3655 .flatten()
3656 });
3657 let Some(handle_id) = handle_id else {
3658 return false;
3659 };
3660 dispatch_focus_reveal_request(tree, metadatas, focus_owner, handle_id)
3661}
3662
3663fn dispatch_focus_reveal_request(
3664 tree: &ComponentNodeTree,
3665 metadatas: &ComponentNodeMetaDatas,
3666 focus_owner: &FocusOwner,
3667 handle_id: FocusHandleId,
3668) -> bool {
3669 let Some(target_node_id) = focus_owner.component_node_id_of(handle_id) else {
3670 return false;
3671 };
3672 let Some(target_bounds) = component_node_bounds_precise(metadatas, target_node_id) else {
3673 return false;
3674 };
3675
3676 let mut current = Some(target_node_id);
3677 while let Some(node_id) = current {
3678 let Some(node_ref) = tree.get(node_id) else {
3679 break;
3680 };
3681 let Some(viewport_rect) = component_node_viewport_rect(metadatas, node_id) else {
3682 current = node_ref.parent();
3683 continue;
3684 };
3685 let node = node_ref.get();
3686 if let Some(handler) = &node.interaction().focus_reveal_handler {
3687 let metadata = metadatas
3688 .get(&node_id)
3689 .expect("Reveal container must have metadata");
3690 let origin = metadata.abs_position.unwrap_or(PxPosition::ZERO);
3691 let transform = metadata
3692 .local_to_window
3693 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, origin).unwrap());
3694 let viewport_bounds = component_node_bounds_precise(metadatas, node_id)
3695 .and_then(|bounds| {
3696 metadata
3697 .event_clip_bounds
3698 .map_or(Some(bounds), |clip| clip.intersection(bounds))
3699 })
3700 .unwrap_or_else(|| viewport_rect.into());
3701 let handled = handler.call(crate::focus::FocusRevealRequest::with_geometry(
3702 target_bounds,
3703 viewport_bounds,
3704 transform,
3705 ));
3706 if handled {
3707 return true;
3708 }
3709 }
3710 current = node_ref.parent();
3711 }
3712 false
3713}
3714
3715fn component_node_bounds_precise(
3716 metadatas: &ComponentNodeMetaDatas,
3717 node_id: NodeId,
3718) -> Option<crate::FloatRect> {
3719 let metadata = metadatas.get(&node_id)?;
3720 let origin = metadata.abs_position?;
3721 let size = metadata.computed_data?;
3722 let transform = metadata
3723 .local_to_window
3724 .unwrap_or_else(|| crate::LocalToWindow::new(1.0, origin).unwrap());
3725 Some(transform.map_rect(crate::FloatRect::from_position_size(
3726 PxPosition::ZERO,
3727 PxSize::new(size.width, size.height),
3728 )))
3729}
3730
3731fn component_node_bounds_rect(
3732 metadatas: &ComponentNodeMetaDatas,
3733 node_id: indextree::NodeId,
3734) -> Option<PxRect> {
3735 let metadata = metadatas.get(&node_id)?;
3736 let _ = metadata;
3737 component_node_bounds_precise(metadatas, node_id).map(crate::FloatRect::outward)
3738}
3739
3740fn component_node_viewport_rect(
3741 metadatas: &ComponentNodeMetaDatas,
3742 node_id: indextree::NodeId,
3743) -> Option<PxRect> {
3744 let node_rect = component_node_bounds_rect(metadatas, node_id)?;
3745 let metadata = metadatas.get(&node_id)?;
3746 Some(
3747 metadata
3748 .event_clip_rect()
3749 .and_then(|clip_rect| clip_rect.intersection(&node_rect))
3750 .unwrap_or(node_rect),
3751 )
3752}
3753
3754fn collect_dirty_layout_paths(
3755 tree: &ComponentNodeTree,
3756 node_ids: &HashMap<u64, indextree::NodeId>,
3757 measure_self: &HashSet<u64>,
3758 placement_self: &HashSet<u64>,
3759) -> HashMap<NodeId, DirtyLayoutPath> {
3760 use std::collections::hash_map::Entry;
3761
3762 let mut paths: HashMap<_, DirtyLayoutPath> = HashMap::default();
3763 for dirty_key in measure_self
3764 .iter()
3765 .chain(placement_self.difference(measure_self))
3766 {
3767 let mut current = node_ids.get(dirty_key).copied();
3768 let mut layout_child = None;
3769 while let Some(node_id) = current {
3770 let Some(node) = tree.get(node_id) else { break };
3771 let data = node.get();
3772 current = node.parent();
3773 if data.role != NodeRole::Layout && current.is_some() {
3774 continue;
3775 }
3776 let (path, new) = match paths.entry(node_id) {
3777 Entry::Occupied(entry) => (entry.into_mut(), false),
3778 Entry::Vacant(entry) => (
3779 entry.insert(DirtyLayoutPath {
3780 measure: measure_self.contains(&data.instance_key),
3781 placement: placement_self.contains(&data.instance_key),
3782 children: SmallVec::new(),
3783 }),
3784 true,
3785 ),
3786 };
3787 if let Some(child) = layout_child
3788 && !path.measure
3789 {
3790 path.children.push(child);
3791 }
3792 if !new {
3794 break;
3795 }
3796 layout_child = Some(node_id);
3797 }
3798 }
3799 paths
3800}
3801
3802fn record_layout_commands(
3803 root_node: indextree::NodeId,
3804 tree: &ComponentNodeTree,
3805 metadatas: &mut ComponentNodeMetaDatas,
3806) {
3807 struct DrawModifierChainContent<'a> {
3808 instance: u64,
3809 draw_nodes: &'a [(Arc<dyn crate::modifier::DrawModifierNode>, DrawLayoutBounds)],
3810 node_draw: &'a dyn crate::modifier::DrawModifierNode,
3811 node_bounds: Option<DrawLayoutBounds>,
3812 parent_bounds: DrawLayoutBounds,
3813 pending_start: usize,
3814 }
3815
3816 struct TerminalDrawContent;
3817
3818 impl DrawModifierContent for TerminalDrawContent {
3819 fn draw(&mut self, _input: &mut RenderInput<'_>) {}
3820 }
3821
3822 impl DrawModifierContent for DrawModifierChainContent<'_> {
3823 fn draw(&mut self, input: &mut RenderInput<'_>) {
3824 input.finish_draw_range(self.pending_start, self.parent_bounds);
3825 if let Some(((draw_modifier, bounds), remaining)) = self.draw_nodes.split_first() {
3826 input.draw_bounds = *bounds;
3827 let start = input.recorded_op_count();
3828 let mut draw_ctx = DrawModifierContext {
3829 render_input: input,
3830 };
3831 let mut next = DrawModifierChainContent {
3832 instance: self.instance,
3833 draw_nodes: remaining,
3834 node_draw: self.node_draw,
3835 node_bounds: self.node_bounds,
3836 parent_bounds: *bounds,
3837 pending_start: start,
3838 };
3839 crate::work::record(
3840 self.instance,
3841 crate::WorkPhase::Paint,
3842 draw_modifier.diagnostic_name(),
3843 );
3844 draw_modifier.draw(&mut draw_ctx, &mut next);
3845 input.finish_draw_range(next.pending_start, *bounds);
3846 } else if let Some(bounds) = self.node_bounds {
3847 input.draw_bounds = bounds;
3848 let start = input.recorded_op_count();
3849 let mut draw_ctx = DrawModifierContext {
3850 render_input: input,
3851 };
3852 crate::work::record(
3853 self.instance,
3854 crate::WorkPhase::Paint,
3855 self.node_draw.diagnostic_name(),
3856 );
3857 self.node_draw.draw(&mut draw_ctx, &mut TerminalDrawContent);
3858 input.finish_draw_range(start, bounds);
3859 }
3860 input.draw_bounds = self.parent_bounds;
3861 self.pending_start = input.recorded_op_count();
3862 }
3863 }
3864
3865 let pending = std::mem::take(&mut metadatas.paint_dirty_nodes);
3866 for node_id in pending {
3867 let Some(node) = tree.get(node_id).filter(|node| !node.is_removed()) else {
3868 continue;
3869 };
3870 let visible = node_id.ancestors(tree).all(|ancestor| {
3871 tree[ancestor].get().role != NodeRole::Layout
3872 || metadatas.get(&ancestor).is_some_and(|metadata| {
3873 metadata.computed_data.is_some()
3874 && (ancestor == root_node || metadata.rel_position.is_some())
3875 })
3876 });
3877 if !visible {
3878 continue;
3879 }
3880 let _phase = push_phase(RuntimePhase::Paint);
3881 let _instance = push_current_component_instance_key(node.get().instance_key);
3882 crate::runtime::clear_phase_state_reads(
3883 node.get().instance_key,
3884 crate::runtime::ReadPhase::Paint,
3885 );
3886 #[cfg(feature = "profiling")]
3887 let _record_profiler_guard = {
3888 let parent_id = node.parent();
3889 Some(ProfilerScopeGuard::new(
3890 ProfilerPhase::Record,
3891 Some(node_id),
3892 parent_id,
3893 Some(node.get().fn_name),
3894 ))
3895 };
3896 let previous_visual = metadatas.get(&node_id).map(|metadata| {
3899 (
3900 metadata.clips_children,
3901 metadata.clip_region().copied(),
3902 metadata.opacity,
3903 )
3904 });
3905 if let Some(metadata) = metadatas.get_mut(&node_id) {
3906 metadata.fragment = Default::default();
3907 metadata.clips_children = false;
3908 metadata.set_clip_region(None);
3909 metadata.opacity = 1.0;
3910 }
3911 let mut input = RenderInput::new(node_id, metadatas);
3912 let node_ref = node.get();
3913 let mut bounds = Some(input.draw_bounds);
3914 let mut layer_index = 0;
3915 let mut draw_nodes = Vec::new();
3916 let layers = input.draw_layers();
3917 for action in node_ref.modifier.ordered_actions() {
3918 match action {
3919 OrderedModifierAction::Layout => {
3920 bounds = layers.get(layer_index).copied().flatten();
3921 layer_index += 1;
3922 }
3923 OrderedModifierAction::Draw(node) => {
3924 if let Some(bounds) = bounds {
3925 draw_nodes.push((node, bounds));
3926 }
3927 }
3928 _ => {}
3929 }
3930 }
3931 let mut content = DrawModifierChainContent {
3932 instance: node_ref.instance_logic_id,
3933 draw_nodes: &draw_nodes,
3934 node_draw: node_ref.draw_node.as_ref(),
3935 node_bounds: bounds,
3936 parent_bounds: input.draw_bounds,
3937 pending_start: 0,
3938 };
3939 content.draw(&mut input);
3940 metadatas.paint_changed.insert(node_id);
3941 if let Some(metadata) = metadatas.get_mut(&node_id) {
3942 metadata.paint_dirty = false;
3943 }
3944 let next_visual = metadatas.get(&node_id).map(|metadata| {
3945 (
3946 metadata.clips_children,
3947 metadata.clip_region().copied(),
3948 metadata.opacity,
3949 )
3950 });
3951 if previous_visual != next_visual {
3952 metadatas.mark_geometry_dirty(node_id);
3953 }
3954 }
3955}
3956
3957#[derive(Clone, Copy, PartialEq)]
3958struct PreparedLayoutMetadata {
3959 local_position: PxPosition,
3960 local_to_window: crate::LocalToWindow,
3961 child_local_to_window: crate::LocalToWindow,
3962 child_clip_bounds: Option<crate::FloatRect>,
3963 size: PxSize,
3964 clips_children: bool,
3965 cumulative_opacity: f32,
3966 has_shape_clipping: bool,
3967}
3968
3969impl PreparedLayoutMetadata {
3970 fn child_geometry_eq(&self, other: &Self) -> bool {
3971 self.child_local_to_window == other.child_local_to_window
3972 && self.child_clip_bounds == other.child_clip_bounds
3973 && self.cumulative_opacity == other.cumulative_opacity
3974 && self.has_shape_clipping == other.has_shape_clipping
3975 }
3976}
3977
3978fn map_clip_region(
3979 mut region: crate::ClipRegion,
3980 transform: crate::LocalToWindow,
3981) -> crate::ClipRegion {
3982 region.position = transform.map_point(region.position);
3983 region.size = [
3984 region.size[0] * transform.scale(),
3985 region.size[1] * transform.scale(),
3986 ];
3987 if let crate::ClipShape::RoundedRectangle { ref mut radii, .. } = region.shape {
3988 for radius in radii {
3989 *radius *= transform.scale();
3990 }
3991 }
3992 region
3993}
3994
3995fn record_frame_stage(timer: &mut Option<Instant>, stage: &'static str) {
3996 if let Some(start) = timer {
3997 tracing::debug!(target: "tessera_ui::frame_cost", stage, duration_ns = start.elapsed().as_nanos(), "frame_stage_cpu");
3998 *start = Instant::now();
3999 }
4000}
4001
4002fn geometry_parent(tree: &ComponentNodeTree, node_id: NodeId) -> Option<NodeId> {
4003 node_id
4004 .ancestors(tree)
4005 .skip(1)
4006 .find(|&id| tree[id].get().role == NodeRole::Layout || tree[id].parent().is_none())
4007}
4008
4009fn layout_rect_in_space(
4010 tree: &ComponentNodeTree,
4011 metadata: &ComponentNodeMetaDatas,
4012 node_id: NodeId,
4013) -> Option<crate::FloatRect> {
4014 let size = metadata.get(&node_id)?.prepared_layout?.size;
4015 let mut transform = crate::LocalToWindow::IDENTITY;
4016 let mut child_position = None;
4017 for id in node_id.ancestors(tree) {
4018 let node = tree[id].get();
4019 if node.role != NodeRole::Layout {
4020 continue;
4021 }
4022 let prepared = metadata.get(&id)?.prepared_layout?;
4023 if let Some(position) = child_position {
4024 transform = crate::LocalToWindow::new(node.children_scale, PxPosition::ZERO)?
4025 .with_local_origin(position)
4026 .compose(transform)?;
4027 }
4028 if node.layout_coordinate_space {
4029 return Some(
4030 transform.map_rect(crate::FloatRect::from_position_size(PxPosition::ZERO, size)),
4031 );
4032 }
4033 child_position = Some(prepared.local_position);
4034 }
4035 None
4036}
4037
4038fn prepare_layout_metadata_for_node(
4039 node: &ComponentNode,
4040 metadata: &mut ComponentNodeMetaData,
4041 parent_transform: crate::LocalToWindow,
4042 is_root: bool,
4043 parent_clip_bounds: Option<crate::FloatRect>,
4044 current_opacity: f32,
4045 parent_has_shape_clipping: bool,
4046 reuse_local_placement: bool,
4047) -> (Option<PreparedLayoutMetadata>, bool) {
4048 metadata.has_shape_clipping = parent_has_shape_clipping || metadata.clip_region().is_some();
4049 let rel_pos = match metadata.rel_position {
4050 Some(pos) => pos,
4051 None if is_root => PxPosition::ZERO,
4052 _ => {
4053 metadata.base_abs_position = None;
4054 metadata.abs_position = None;
4055 metadata.local_to_window = None;
4056 metadata.event_clip_bounds = None;
4057 metadata.prepared_layout = None;
4058 metadata.set_prepared_clip_region(None);
4059 metadata.geometry_initialized = true;
4060 return (None, false);
4061 }
4062 };
4063 let base_self_position = parent_transform.map_point(rel_pos);
4064 let cached_position = reuse_local_placement
4065 .then(|| {
4066 metadata
4067 .prepared_layout
4068 .as_ref()
4069 .map(|previous| previous.local_position)
4070 })
4071 .flatten();
4072 let local_position = if let Some(position) = cached_position {
4073 if let Some(transforms) = &mut metadata.input_transforms {
4074 transforms.parent = parent_transform;
4075 }
4076 position
4077 } else {
4078 let _phase = push_phase(RuntimePhase::Place);
4079 let _instance = push_current_component_instance_key(node.instance_key);
4080 crate::runtime::clear_phase_state_reads(
4081 node.instance_key,
4082 crate::runtime::ReadPhase::Transform,
4083 );
4084 let (position, inputs) = node
4085 .modifier
4086 .resolve_placement(rel_pos, node.instance_logic_id);
4087 metadata.set_input_transforms(parent_transform, inputs);
4088 position
4089 };
4090 let local_to_window = parent_transform.with_local_origin(local_position);
4091 let self_position = local_to_window.translation();
4092 let child_scale = node.children_scale;
4093 let child_local_to_window = if child_scale == 1.0 {
4094 local_to_window
4095 } else {
4096 local_to_window
4097 .compose(
4098 crate::LocalToWindow::new(child_scale, PxPosition::ZERO)
4099 .expect("Child scale must be finite and positive"),
4100 )
4101 .expect("Composed child transform must remain finite and invertible")
4102 };
4103 metadata.local_to_window = Some(local_to_window);
4104 let cumulative_opacity = current_opacity * metadata.opacity;
4105 metadata.base_abs_position = Some(base_self_position);
4106 metadata.abs_position = Some(self_position);
4107 metadata.event_clip_bounds = parent_clip_bounds;
4108
4109 let size = metadata
4110 .computed_data
4111 .map(|d| PxSize {
4112 width: d.width,
4113 height: d.height,
4114 })
4115 .unwrap_or_default();
4116 let clips_children = metadata.clips_children;
4117 let clip_region = metadata
4118 .clip_region()
4119 .map(|®ion| map_clip_region(region, local_to_window));
4120 let previous_shape = metadata
4121 .prepared_layout
4122 .as_ref()
4123 .filter(|layout| layout.clips_children)
4124 .and(metadata.prepared_clip_region());
4125 let next_shape = clips_children.then_some(clip_region.as_ref()).flatten();
4126 let shape_changed = previous_shape != next_shape;
4127 let child_clip_bounds = if clips_children {
4128 let own_clip_bounds = clip_region
4129 .map(|region| crate::FloatRect {
4130 x: region.position.x,
4131 y: region.position.y,
4132 width: region.size[0],
4133 height: region.size[1],
4134 })
4135 .unwrap_or_else(|| {
4136 local_to_window
4137 .map_rect(crate::FloatRect::from_position_size(PxPosition::ZERO, size))
4138 });
4139 Some(parent_clip_bounds.map_or(own_clip_bounds, |parent| {
4140 parent
4141 .intersection(own_clip_bounds)
4142 .unwrap_or(crate::FloatRect::ZERO)
4143 }))
4144 } else {
4145 parent_clip_bounds
4146 };
4147 let prepared = PreparedLayoutMetadata {
4148 local_position,
4149 local_to_window,
4150 child_local_to_window,
4151 child_clip_bounds,
4152 size,
4153 clips_children,
4154 cumulative_opacity,
4155 has_shape_clipping: metadata.has_shape_clipping,
4156 };
4157 metadata.prepared_layout = Some(prepared);
4158 metadata.set_prepared_clip_region(clip_region);
4159 metadata.geometry_initialized = true;
4160 (Some(prepared), shape_changed)
4161}
4162
4163fn populate_layout_metadata(
4164 root_node: indextree::NodeId,
4165 tree: &ComponentNodeTree,
4166 metadatas: &mut ComponentNodeMetaDatas,
4167) -> u64 {
4168 let order_dirty = std::mem::take(&mut metadatas.placement_order_dirty);
4169 for id in order_dirty {
4170 if let Some(node) = tree.get(id).filter(|node| !node.is_removed()) {
4171 let id = if node.get().role == NodeRole::Layout || id == root_node {
4172 Some(id)
4173 } else {
4174 geometry_parent(tree, id)
4175 };
4176 if let Some(id) = id {
4177 metadatas.placement_order_dirty.insert(id);
4178 metadatas.geometry_dirty.insert(id);
4179 }
4180 }
4181 }
4182 let requested = std::mem::take(&mut metadatas.geometry_dirty);
4183 let mut dirty: HashSet<_> = requested
4184 .into_iter()
4185 .filter_map(|id| {
4186 let node = tree.get(id).filter(|node| !node.is_removed())?;
4187 if node.get().role == NodeRole::Layout || id == root_node {
4188 Some(id)
4189 } else {
4190 None
4191 }
4192 })
4193 .collect();
4194 let mut dirty_children: HashMap<NodeId, HashSet<NodeId>> = HashMap::default();
4195 for mut id in dirty.iter().copied().collect::<Vec<_>>() {
4196 while let Some(parent) = geometry_parent(tree, id) {
4197 dirty_children.entry(parent).or_default().insert(id);
4198 if !dirty.insert(parent) {
4199 break;
4200 }
4201 id = parent;
4202 }
4203 }
4204 fn visit(
4205 tree: &ComponentNodeTree,
4206 metadatas: &mut ComponentNodeMetaDatas,
4207 dirty: &HashSet<indextree::NodeId>,
4208 dirty_children: &HashMap<NodeId, HashSet<NodeId>>,
4209 parent_transform: crate::LocalToWindow,
4210 is_root: bool,
4211 node_id: indextree::NodeId,
4212 clip_bounds: Option<crate::FloatRect>,
4213 current_opacity: f32,
4214 parent_has_shape_clipping: bool,
4215 inherited_order_changed: bool,
4216 inherited_shape_clip_changed: bool,
4217 inherited_changed: bool,
4218 updates: &mut u64,
4219 ) {
4220 metadatas.geometry_visits += 1;
4221 let Some(node) = tree.get(node_id).map(|node| node.get()) else {
4222 return;
4223 };
4224 let self_dirty = dirty.contains(&node_id);
4225 let (
4226 previous,
4227 was_hidden,
4228 initialized,
4229 paint_dirty,
4230 prepared,
4231 publishes_geometry,
4232 size_changed,
4233 own_shape_changed,
4234 ) = if let Some(metadata) = metadatas.get_mut(&node_id) {
4235 let initialized = metadata.geometry_initialized;
4236 if initialized && !inherited_changed && !self_dirty {
4237 return;
4238 }
4239 let previous = metadata.prepared_layout;
4240 let was_placed = metadata.abs_position.is_some();
4241 let was_hidden = metadata.prepared_layout.is_none();
4242 let paint_dirty = metadata.paint_dirty;
4243 let (prepared, own_shape_changed) = prepare_layout_metadata_for_node(
4244 node,
4245 metadata,
4246 parent_transform,
4247 is_root,
4248 clip_bounds,
4249 current_opacity,
4250 parent_has_shape_clipping,
4251 initialized && !self_dirty,
4252 );
4253 let publishes_geometry = metadata.observes_geometry
4254 || metadata.accessibility.is_some()
4255 || metadata.clips_children
4256 || !metadata.fragment.ops().is_empty();
4257 let size_changed = metadata.observes_size
4258 && (was_placed != prepared.is_some()
4259 || previous.map(|layout| layout.size) != prepared.map(|layout| layout.size));
4260 (
4261 previous,
4262 was_hidden,
4263 initialized,
4264 paint_dirty,
4265 prepared,
4266 publishes_geometry,
4267 size_changed,
4268 own_shape_changed,
4269 )
4270 } else {
4271 (None, true, false, false, None, false, false, false)
4272 };
4273 *updates += 1;
4274 if size_changed {
4275 metadatas.size_changed.insert(node_id);
4276 }
4277 if initialized && was_hidden && prepared.is_none() && !self_dirty {
4278 return;
4279 }
4280 let order_changed = metadatas.placement_order_dirty.remove(&node_id);
4281 let order_changed = order_changed || inherited_order_changed;
4282 if order_changed {
4283 metadatas.placement_order_changed.insert(node_id);
4284 }
4285 let changed = inherited_changed
4286 || !initialized
4287 || prepared != previous
4288 || order_changed
4289 || own_shape_changed;
4290 if changed && publishes_geometry {
4291 metadatas.geometry_changed.insert(node_id);
4292 }
4293 if prepared.is_some() && paint_dirty {
4294 metadatas.paint_dirty_nodes.insert(node_id);
4295 }
4296 if prepared.is_none() && node.role == NodeRole::Layout {
4297 for descendant in node_id.descendants(tree) {
4298 let size_hidden = if let Some(metadata) = metadatas.get_mut(&descendant) {
4299 let size_hidden = metadata.observes_size && metadata.abs_position.is_some();
4300 metadata.base_abs_position = None;
4301 metadata.abs_position = None;
4302 metadata.local_to_window = None;
4303 metadata.event_clip_bounds = None;
4304 size_hidden
4305 } else {
4306 false
4307 };
4308 if size_hidden {
4309 metadatas.size_changed.insert(descendant);
4310 }
4311 metadatas.geometry_changed.insert(descendant);
4312 }
4313 return;
4314 }
4315 let (transform, clip, opacity) = prepared.map_or(
4316 (parent_transform, clip_bounds, current_opacity),
4317 |prepared| {
4318 (
4319 prepared.child_local_to_window,
4320 prepared.child_clip_bounds,
4321 prepared.cumulative_opacity,
4322 )
4323 },
4324 );
4325 let has_shape_clipping = prepared.map_or(parent_has_shape_clipping, |prepared| {
4326 prepared.has_shape_clipping
4327 });
4328 let shape_clip_changed = inherited_shape_clip_changed || own_shape_changed;
4331 let children_changed = !initialized
4332 || order_changed
4333 || shape_clip_changed
4334 || match (previous, prepared) {
4335 (Some(previous), Some(prepared)) => !previous.child_geometry_eq(&prepared),
4336 (None, None) => false,
4337 _ => true,
4338 };
4339 let mut visit_child = |child| {
4340 visit(
4341 tree,
4342 metadatas,
4343 dirty,
4344 dirty_children,
4345 transform,
4346 false,
4347 child,
4348 clip,
4349 opacity,
4350 has_shape_clipping,
4351 order_changed,
4352 shape_clip_changed,
4353 children_changed,
4354 updates,
4355 );
4356 };
4357 if children_changed {
4358 if let Some(children) = node.layout_children.as_ref() {
4359 for &child in children.iter() {
4360 visit_child(child);
4361 }
4362 } else {
4363 for &child in direct_layout_children(node_id, tree).iter() {
4364 visit_child(child);
4365 }
4366 }
4367 } else if let Some(children) = dirty_children.get(&node_id) {
4368 for &child in children {
4369 visit_child(child);
4370 }
4371 }
4372 }
4373 let mut updates = 0;
4374 visit(
4375 tree,
4376 metadatas,
4377 &dirty,
4378 &dirty_children,
4379 crate::LocalToWindow::IDENTITY,
4380 true,
4381 root_node,
4382 None,
4383 1.0,
4384 false,
4385 false,
4386 false,
4387 false,
4388 &mut updates,
4389 );
4390 updates
4391}
4392
4393#[cfg(test)]
4394fn build_render_graph(
4395 root: NodeId,
4396 tree: &ComponentNodeTree,
4397 metadata: &mut ComponentNodeMetaDatas,
4398 viewport: PxSize,
4399) -> RenderGraph {
4400 let changed = root.descendants(tree).collect();
4401 scene::FrameScene::default()
4402 .update(
4403 root,
4404 tree,
4405 metadata,
4406 &changed,
4407 &HashSet::default(),
4408 viewport,
4409 )
4410 .0
4411}
4412
4413#[cfg(test)]
4414mod tests {
4415 use super::*;
4416
4417 use crate::{
4418 Px,
4419 component_tree::{ComponentNode, NodeRole},
4420 };
4421
4422 fn node_with_role(
4423 name: &'static str,
4424 role: NodeRole,
4425 instance_logic_id: u64,
4426 instance_key: u64,
4427 ) -> ComponentNode {
4428 ComponentNode {
4429 instance_logic_id,
4430 instance_key,
4431 ..ComponentNode::new(name, role)
4432 }
4433 }
4434
4435 fn node(name: &'static str, instance_logic_id: u64, instance_key: u64) -> ComponentNode {
4436 node_with_role(name, NodeRole::Layout, instance_logic_id, instance_key)
4437 }
4438
4439 #[test]
4440 fn unchanged_child_coordinates_stop_geometry_propagation_but_shape_changes_reach_inputs() {
4441 let _runtime_session = crate::RuntimeSession::new();
4442 let _runtime_scope = _runtime_session.enter();
4443 let mut tree = ComponentTree::new();
4444 let root = tree.add_node(node("root", 1, 1));
4445 let moving = tree.add_node(node("moving", 2, 2));
4446 let pinned = tree.add_node(node("pinned", 3, 3));
4447 let inner = tree.add_node(node("inner", 4, 4));
4448 let mut control = node("control", 5, 5);
4449 control
4450 .interaction_mut()
4451 .pointer_handlers
4452 .push(Box::new(|_| {}));
4453 let leaf = tree.add_node(control);
4454 for _ in 0..5 {
4455 tree.pop_node();
4456 }
4457 for id in [root, moving, pinned, inner, leaf] {
4458 let metadata = tree.metadatas.entry_or_default(id);
4459 metadata.computed_data = Some(ComputedData {
4460 width: Px(100),
4461 height: Px(100),
4462 });
4463 metadata.rel_position = Some(PxPosition::ZERO);
4464 metadata.placement_order = Some(0);
4465 }
4466 node::place_node(leaf, PxPosition::new(Px(2), Px(2)), 0, &mut tree.metadatas);
4467 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4468 tree.metadatas.geometry_changed.clear();
4469 tree.metadatas.geometry_visits = 0;
4470
4471 node::place_node(
4472 moving,
4473 PxPosition::new(Px(35), Px(0)),
4474 0,
4475 &mut tree.metadatas,
4476 );
4477 node::place_node(
4478 pinned,
4479 PxPosition::new(Px(-35), Px(0)),
4480 0,
4481 &mut tree.metadatas,
4482 );
4483 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4484 assert_eq!(
4485 tree.metadatas.get(&leaf).unwrap().abs_position,
4486 Some(PxPosition::new(Px(2), Px(2)))
4487 );
4488 assert!(
4489 !tree.metadatas.geometry_changed.contains(&leaf),
4490 "a counter-translated branch must not invalidate unchanged descendant input geometry"
4491 );
4492 assert_eq!(tree.metadatas.geometry_visits, 3);
4493
4494 tree.metadatas.geometry_changed.clear();
4495 tree.metadatas.entry_or_default(pinned).computed_data = Some(ComputedData {
4496 width: Px(120),
4497 height: Px(100),
4498 });
4499 tree.metadatas.mark_geometry_dirty(pinned);
4500 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4501 assert!(
4502 !tree.metadatas.geometry_changed.contains(&leaf),
4503 "an unclipped parent's size must not invalidate unchanged child coordinates"
4504 );
4505
4506 node::place_node(leaf, PxPosition::new(Px(3), Px(3)), 0, &mut tree.metadatas);
4507 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4508 assert_eq!(
4509 tree.metadatas.get(&leaf).unwrap().abs_position,
4510 Some(PxPosition::new(Px(3), Px(3)))
4511 );
4512 assert!(tree.metadatas.geometry_changed.contains(&leaf));
4513
4514 let clip = crate::ClipRegion {
4515 position: PxPosition::ZERO,
4516 size: [100.0, 100.0],
4517 shape: crate::ClipShape::RoundedRectangle {
4518 radii: [0.0; 4],
4519 exponents: [2.0; 4],
4520 },
4521 };
4522 let metadata = tree.metadatas.entry_or_default(root);
4523 metadata.clips_children = true;
4524 metadata.set_clip_region(Some(clip));
4525 tree.metadatas.mark_geometry_dirty(root);
4526 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4527 assert!(inside_shape_clips(
4528 &tree.tree,
4529 &tree.metadatas,
4530 leaf,
4531 PxPosition::new(Px(4), Px(4))
4532 ));
4533 tree.metadatas.geometry_changed.clear();
4534
4535 tree.metadatas
4536 .entry_or_default(root)
4537 .set_clip_region(Some(crate::ClipRegion {
4538 shape: crate::ClipShape::RoundedRectangle {
4539 radii: [40.0; 4],
4540 exponents: [2.0; 4],
4541 },
4542 ..clip
4543 }));
4544 tree.metadatas.mark_geometry_dirty(root);
4545 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4546 assert!(tree.metadatas.geometry_changed.contains(&leaf));
4547 assert!(!inside_shape_clips(
4548 &tree.tree,
4549 &tree.metadatas,
4550 leaf,
4551 PxPosition::new(Px(4), Px(4))
4552 ));
4553
4554 tree.metadatas.geometry_changed.clear();
4555 tree.metadatas.entry_or_default(root).set_clip_region(None);
4556 tree.metadatas.mark_geometry_dirty(root);
4557 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4558 assert!(tree.metadatas.geometry_changed.contains(&leaf));
4559 assert!(inside_shape_clips(
4560 &tree.tree,
4561 &tree.metadatas,
4562 leaf,
4563 PxPosition::new(Px(4), Px(4))
4564 ));
4565
4566 tree.metadatas.entry_or_default(pinned).rel_position = None;
4567 tree.metadatas.mark_geometry_dirty(pinned);
4568 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4569 assert!(tree.metadatas.get(&leaf).unwrap().abs_position.is_none());
4570 tree.metadatas.entry_or_default(inner).geometry_initialized = false;
4571 node::place_node(
4572 pinned,
4573 PxPosition::new(Px(-35), Px(0)),
4574 0,
4575 &mut tree.metadatas,
4576 );
4577 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4578 assert_eq!(
4579 tree.metadatas.get(&leaf).unwrap().abs_position,
4580 Some(PxPosition::new(Px(3), Px(3)))
4581 );
4582 }
4583
4584 #[derive(Clone, Default, PartialEq)]
4585 struct RevealProbe {
4586 handler: crate::testing::TestProbe<
4587 Option<crate::CallbackWith<crate::focus::FocusRevealRequest, bool>>,
4588 >,
4589 scroll: crate::testing::TestProbe<i32>,
4590 rects: crate::testing::TestProbe<Vec<crate::PxRect>>,
4591 }
4592
4593 #[crate::tessera(crate)]
4594 fn retry_reveal_callback_fixture(probe: RevealProbe) {
4595 let handler = crate::CallbackWith::new({
4596 let probe = probe.clone();
4597 move |request: crate::focus::FocusRevealRequest| {
4598 probe
4599 .rects
4600 .with_mut(|rects| rects.push(request.target_rect));
4601 probe.scroll.set(-request.target_rect.y.0);
4602 true
4603 }
4604 });
4605 probe.handler.set(Some(handler));
4606 crate::layout::layout();
4607 }
4608
4609 fn reveal_viewport_tree(
4610 probe: RevealProbe,
4611 ) -> (
4612 ComponentTree,
4613 NodeId,
4614 crate::focus::FocusRegistration,
4615 crate::testing::LayoutTestSession<impl Fn()>,
4616 ) {
4617 use crate::focus::{FocusNode, FocusRegistration};
4618
4619 let mut session = crate::testing::start_layout_test_session(crate::testing::layout_test({
4620 let probe = probe.clone();
4621 move || retry_reveal_callback_fixture(probe.clone())
4622 }));
4623 crate::testing::pump_layout_test_session(&mut session, 0);
4624 probe.rects.with_mut(Vec::clear);
4625 probe.scroll.set(0);
4626 let mut tree = ComponentTree::new();
4627 let mut viewport = node("viewport", 1, 1);
4628 viewport.interaction_mut().focus_reveal_handler = probe.handler.get();
4629 let root = tree.add_node(viewport);
4630 let focus = FocusRegistration::target(FocusNode::new());
4631 let mut target = node("focused control", 2, 2);
4632 target.interaction_mut().focus_registration = Some(focus);
4633 let target = tree.add_node(target);
4634 tree.pop_node();
4635 tree.pop_node();
4636 for (id, y, width, height) in [(root, 0, 200, 100), (target, 300, 80, 24)] {
4637 let metadata = tree.metadatas.get_mut(&id).unwrap();
4638 metadata.abs_position = Some(PxPosition::new(Px::ZERO, Px(y)));
4639 metadata.base_abs_position = metadata.abs_position;
4640 metadata.computed_data = Some(ComputedData {
4641 width: Px(width),
4642 height: Px(height),
4643 });
4644 }
4645 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
4646 (tree, root, focus, session)
4647 }
4648
4649 #[test]
4650 fn pending_focus_reveal_retries_a_keyboard_barrier_without_queued_input() {
4651 let _runtime_session = crate::RuntimeSession::new();
4652 let _runtime_scope = _runtime_session.enter();
4653 let probe = RevealProbe::default();
4654 use crate::focus::{FocusManager, bind_focus_owner};
4655
4656 let (mut tree, _, focus, _session) = reveal_viewport_tree(probe.clone());
4657 {
4658 let _binding = bind_focus_owner(&mut tree.focus_owner);
4659 FocusManager::current().defer_keyboard_until_layout();
4660 }
4661 assert!(
4662 !needs_deferred_focus_retry(&tree.focus_owner, false),
4663 "a barrier without pending work must not schedule an idle retry"
4664 );
4665 tree.focus_owner.request_focus(focus.id);
4666 assert!(
4667 needs_deferred_focus_retry(&tree.focus_owner, false),
4668 "a pending reveal must schedule another pass even without keyboard or IME events"
4669 );
4670 tree.focus_owner.begin_layout();
4671 assert!(needs_deferred_focus_retry(&tree.focus_owner, false));
4672 tree.focus_owner.finish_layout();
4673 assert!(
4674 tree.has_pending_focus_reveal(),
4675 "post-layout scheduling must still retry an unconsumed reveal even after the barrier clears"
4676 );
4677 assert!(tree.pending_focused_input.is_empty());
4678 assert!(dispatch_pending_focus_reveal_request(
4679 &tree.tree,
4680 &tree.metadatas,
4681 &mut tree.focus_owner,
4682 false
4683 ));
4684 assert_eq!(
4685 probe.scroll.get(),
4686 -300,
4687 "the settled barrier must allow the real viewport handler to reveal the pending target"
4688 );
4689 assert_eq!(
4690 probe.rects.with(|rects| rects.clone()),
4691 [crate::PxRect::new(Px::ZERO, Px(300), Px(80), Px(24))]
4692 );
4693 assert!(!needs_deferred_focus_retry(&tree.focus_owner, false));
4694 assert!(
4695 !tree.has_pending_focus_reveal(),
4696 "the post-flush scheduler must stop retrying once the viewport has handled the request"
4697 );
4698 }
4699
4700 #[test]
4701 fn reveal_retry_consumes_the_request_before_a_later_pointer_frame() {
4702 let _runtime_session = crate::RuntimeSession::new();
4703 let _runtime_scope = _runtime_session.enter();
4704 let probe = RevealProbe::default();
4705 let (mut tree, root, focus, _session) = reveal_viewport_tree(probe.clone());
4706 tree.focus_owner.request_focus(focus.id);
4707 assert!(dispatch_pending_focus_reveal_request(
4708 &tree.tree,
4709 &tree.metadatas,
4710 &mut tree.focus_owner,
4711 true
4712 ));
4713 assert_eq!(
4714 probe.scroll.get(),
4715 -300,
4716 "the retry must deliver the actual pending reveal to the viewport handler"
4717 );
4718 assert_eq!(
4719 probe.rects.with(|rects| rects.clone()),
4720 [crate::PxRect::new(Px::ZERO, Px(300), Px(80), Px(24))]
4721 );
4722 probe.scroll.set(-600);
4723 let mut pointer = Some(PxPosition::new(Px(180), Px(50)));
4724 let mut changes = [crate::PointerChange {
4725 timestamp: crate::time::Instant::now(),
4726 pointer_id: crate::MOUSE_POINTER_ID,
4727 content: crate::CursorEventContent::Moved(pointer.unwrap()),
4728 gesture_state: crate::GestureState::TapCandidate,
4729 sampled_position: Default::default(),
4730 consumed: false,
4731 tap_claimed: false,
4732 scroll_discrete: None,
4733 }];
4734 tree.dispatch_pointer_input(
4735 root,
4736 &mut changes,
4737 &mut pointer,
4738 winit::keyboard::ModifiersState::empty(),
4739 );
4740 assert!(
4741 !dispatch_pending_focus_reveal_request(
4742 &tree.tree,
4743 &tree.metadatas,
4744 &mut tree.focus_owner,
4745 false
4746 ),
4747 "an ordinary pointer frame must not repeat a reveal already handled by the retry"
4748 );
4749 assert_eq!(
4750 probe.scroll.get(),
4751 -600,
4752 "the later pointer frame preserves the user's new scroll position"
4753 );
4754 assert_eq!(probe.rects.with(Vec::len), 1);
4755 assert!(
4756 dispatch_pending_focus_reveal_request(
4757 &tree.tree,
4758 &tree.metadatas,
4759 &mut tree.focus_owner,
4760 true
4761 ),
4762 "an explicit retry without a new request still visits the active target"
4763 );
4764 assert_eq!(probe.rects.with(Vec::len), 2);
4765 }
4766
4767 #[test]
4768 fn newer_nonzero_scroll_suppresses_only_the_reveal_of_an_older_deferred_focus_command() {
4769 let _runtime_session = crate::RuntimeSession::new();
4770 let _runtime_scope = _runtime_session.enter();
4771 let probe = RevealProbe::default();
4772 use crate::focus::{FocusNode, FocusRegistration};
4773
4774 for delta_y in [0.0, -24.0] {
4775 for newer_request_before_attach in [false, true] {
4776 let (mut tree, root, original, _session) = reveal_viewport_tree(probe.clone());
4777 let original_node = tree.focus_owner.component_node_id_of(original.id).unwrap();
4778 let delayed = FocusRegistration::target(FocusNode::new());
4779 tree.focus_owner.request_focus(delayed.id);
4780 let mut pointer = Some(PxPosition::new(Px(180), Px(50)));
4781 let mut changes = [crate::PointerChange {
4782 timestamp: crate::time::Instant::now(),
4783 pointer_id: crate::MOUSE_POINTER_ID,
4784 content: crate::CursorEventContent::Scroll(crate::cursor::ScrollEventContent {
4785 delta_x: 0.0,
4786 delta_y,
4787 unit: crate::cursor::ScrollDeltaUnit::Pixel,
4788 source: crate::cursor::ScrollEventSource::Wheel,
4789 }),
4790 gesture_state: crate::GestureState::TapCandidate,
4791 sampled_position: Default::default(),
4792 consumed: false,
4793 tap_claimed: false,
4794 scroll_discrete: None,
4795 }];
4796 tree.dispatch_pointer_input(
4797 root,
4798 &mut changes,
4799 &mut pointer,
4800 winit::keyboard::ModifiersState::empty(),
4801 );
4802 if newer_request_before_attach {
4803 tree.focus_owner.request_focus(original.id);
4804 }
4805
4806 let mut target = node("delayed focus target", 3, 3);
4807 target.interaction_mut().focus_registration = Some(delayed);
4808 let target = tree.add_node(target);
4809 tree.pop_node();
4810 root.append(target, &mut tree.tree);
4811 let metadata = tree.metadatas.get_mut(&target).unwrap();
4812 metadata.abs_position = Some(PxPosition::new(Px(0), Px(500)));
4813 metadata.base_abs_position = metadata.abs_position;
4814 metadata.computed_data = Some(ComputedData {
4815 width: Px(80),
4816 height: Px(24),
4817 });
4818 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
4819 tree.focus_owner.commit_pending();
4820 assert_eq!(
4821 tree.focus_owner.active_component_node_id(),
4822 Some(if newer_request_before_attach {
4823 original_node
4824 } else {
4825 target
4826 })
4827 );
4828 assert_eq!(
4829 dispatch_pending_focus_reveal_request(
4830 &tree.tree,
4831 &tree.metadatas,
4832 &mut tree.focus_owner,
4833 false,
4834 ),
4835 newer_request_before_attach || delta_y == 0.0,
4836 "a zero scroll must preserve the deferred reveal; a newer nonzero scroll must suppress it"
4837 );
4838 assert_eq!(
4839 probe.rects.with(Vec::len),
4840 usize::from(newer_request_before_attach || delta_y == 0.0)
4841 );
4842
4843 tree.focus_owner
4844 .request_focus(if newer_request_before_attach {
4845 delayed.id
4846 } else {
4847 original.id
4848 });
4849 assert!(dispatch_pending_focus_reveal_request(
4850 &tree.tree,
4851 &tree.metadatas,
4852 &mut tree.focus_owner,
4853 false,
4854 ));
4855 let revealed_y = if newer_request_before_attach {
4856 500
4857 } else {
4858 300
4859 };
4860 assert_eq!(probe.scroll.get(), -revealed_y);
4861 assert_eq!(
4862 probe.rects.with(|rects| rects.last().copied()),
4863 Some(crate::PxRect::new(Px(0), Px(revealed_y), Px(80), Px(24)))
4864 );
4865 }
4866 }
4867 }
4868
4869 #[test]
4870 fn retained_scene_keeps_child_drawing_when_parent_paint_appears_and_disappears() {
4871 let _runtime_session = crate::RuntimeSession::new();
4872 let _runtime_scope = _runtime_session.enter();
4873 #[derive(Clone)]
4874 struct Marker(u32);
4875 impl crate::DrawCommand for Marker {
4876 fn apply_opacity(&mut self, _: f32) {}
4877 }
4878 let mut tree = ComponentTree::new();
4879 let root = tree.add_node(node("root", 1, 1));
4880 let wrapper = tree.add_node(node("wrapper", 2, 2));
4881 let visual = tree.add_node(node("visual", 3, 3));
4882 let label = tree.add_node(node("label", 4, 4));
4883 tree.pop_node();
4884 tree.pop_node();
4885 tree.pop_node();
4886 let sibling = tree.add_node(node("sibling", 5, 5));
4887 tree.pop_node();
4888 tree.pop_node();
4889 for id in [root, wrapper, visual, label, sibling] {
4890 let metadata = tree.metadatas.entry_or_default(id);
4891 metadata.computed_data = Some(ComputedData {
4892 width: Px(32),
4893 height: Px(32),
4894 });
4895 metadata.rel_position = Some(PxPosition::ZERO);
4896 }
4897 tree.metadatas
4898 .entry_or_default(label)
4899 .fragment
4900 .push_draw_command(Marker(1));
4901 tree.metadatas
4902 .entry_or_default(sibling)
4903 .fragment
4904 .push_draw_command(Marker(2));
4905 tree.metadatas.entry_or_default(label).rel_position = None;
4906 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4907 let mut scene = scene::FrameScene::default();
4908 let viewport = PxSize::new(Px(100), Px(100));
4909 scene.update(
4910 root,
4911 &tree.tree,
4912 &tree.metadatas,
4913 &root.descendants(&tree.tree).collect(),
4914 &HashSet::default(),
4915 viewport,
4916 );
4917 tree.metadatas.geometry_changed.clear();
4918 node::place_node(label, PxPosition::ZERO, 0, &mut tree.metadatas);
4919 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
4920 let (revealed, _) = scene.update(
4921 root,
4922 &tree.tree,
4923 &tree.metadatas,
4924 &HashSet::default(),
4925 &HashSet::default(),
4926 viewport,
4927 );
4928 assert_eq!(revealed.ops().len(), 2);
4929 tree.metadatas.geometry_changed.clear();
4930 for painted in [true, false, true, false] {
4931 let metadata = tree.metadatas.entry_or_default(visual);
4932 metadata.fragment = Default::default();
4933 if painted {
4934 metadata.fragment.push_draw_command(Marker(3));
4935 }
4936 tree.metadatas.paint_changed.insert(visual);
4937 let (graph, _) = scene.update(
4938 root,
4939 &tree.tree,
4940 &tree.metadatas,
4941 &HashSet::default(),
4942 &HashSet::default(),
4943 viewport,
4944 );
4945 let values = graph
4946 .ops()
4947 .iter()
4948 .filter_map(|op| op.command.downcast_draw_ref::<Marker>())
4949 .map(|marker| marker.0)
4950 .collect::<Vec<_>>();
4951 assert_eq!(values, if painted { vec![3, 1, 2] } else { vec![1, 2] });
4952 tree.metadatas.paint_changed.clear();
4953 }
4954 }
4955
4956 #[test]
4957 fn retained_scene_reuses_fragment_commands_on_translation_and_refreshes_paint() {
4958 let _runtime_session = crate::RuntimeSession::new();
4959 let _runtime_scope = _runtime_session.enter();
4960 use crate::testing::TestProbe;
4961
4962 struct Marker {
4963 clones: TestProbe<usize>,
4964 value: u32,
4965 opacity: f32,
4966 }
4967
4968 impl Clone for Marker {
4969 fn clone(&self) -> Self {
4970 self.clones.with_mut(|clones| *clones += 1);
4971 Self {
4972 clones: self.clones.clone(),
4973 value: self.value,
4974 opacity: self.opacity,
4975 }
4976 }
4977 }
4978
4979 impl crate::DrawCommand for Marker {
4980 fn apply_opacity(&mut self, opacity: f32) {
4981 self.opacity *= opacity;
4982 }
4983 }
4984
4985 let clones = TestProbe::new(0usize);
4986 let mut tree = ComponentTree::new();
4987 let root = tree.add_node(node("root", 1, 1));
4988 let leaf = tree.add_node(node("leaf", 2, 2));
4989 tree.pop_node();
4990 let outside = tree.add_node(node("outside", 3, 3));
4991 tree.pop_node();
4992 let sibling = tree.add_node(node("sibling", 4, 4));
4993 tree.pop_node();
4994 tree.pop_node();
4995 for id in [root, leaf, outside, sibling] {
4996 let metadata = tree.metadatas.entry_or_default(id);
4997 metadata.computed_data = Some(ComputedData {
4998 width: Px(32),
4999 height: Px(32),
5000 });
5001 metadata.rel_position = Some(PxPosition::ZERO);
5002 }
5003 let outside_clones = TestProbe::new(0usize);
5004 let metadata = tree.metadatas.entry_or_default(outside);
5005 metadata.rel_position = Some(PxPosition::from_f32_xy(150.0, 0.0));
5006 metadata.fragment.push_draw_command(Marker {
5007 clones: outside_clones.clone(),
5008 value: 3,
5009 opacity: 1.0,
5010 });
5011 tree.metadatas
5012 .entry_or_default(sibling)
5013 .fragment
5014 .push_draw_command(Marker {
5015 clones: TestProbe::new(0usize),
5016 value: 5,
5017 opacity: 1.0,
5018 });
5019 tree.metadatas
5020 .entry_or_default(leaf)
5021 .fragment
5022 .push_draw_command(Marker {
5023 clones: clones.clone(),
5024 value: 1,
5025 opacity: 1.0,
5026 });
5027 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5028 let viewport = PxSize::new(Px(100), Px(100));
5029 let mut scene = scene::FrameScene::default();
5030 scene.update(
5031 root,
5032 &tree.tree,
5033 &tree.metadatas,
5034 &root.descendants(&tree.tree).collect(),
5035 &HashSet::default(),
5036 viewport,
5037 );
5038 clones.set(0);
5039 tree.metadatas.geometry_changed.clear();
5040 let metadata = tree.metadatas.entry_or_default(outside);
5041 metadata.fragment = Default::default();
5042 metadata.fragment.push_draw_command(Marker {
5043 clones: outside_clones,
5044 value: 4,
5045 opacity: 1.0,
5046 });
5047 tree.metadatas.paint_changed.insert(outside);
5048 let (graph, _) = scene.update(
5049 root,
5050 &tree.tree,
5051 &tree.metadatas,
5052 &HashSet::default(),
5053 &HashSet::default(),
5054 viewport,
5055 );
5056 assert_eq!(
5057 clones.get(),
5058 0,
5059 "offscreen paint must not reassemble unchanged visible commands"
5060 );
5061 assert_eq!(
5062 graph
5063 .ops()
5064 .iter()
5065 .filter_map(|op| op.command.downcast_draw_ref::<Marker>())
5066 .map(|marker| marker.value)
5067 .collect::<Vec<_>>(),
5068 vec![1, 5]
5069 );
5070 tree.metadatas.paint_changed.clear();
5071 tree.metadatas.geometry_changed.insert(leaf);
5072 scene.update(
5073 root,
5074 &tree.tree,
5075 &tree.metadatas,
5076 &HashSet::default(),
5077 &HashSet::default(),
5078 viewport,
5079 );
5080 assert_eq!(
5081 clones.get(),
5082 0,
5083 "an unchanged draw in the geometry journal must retain its visible output"
5084 );
5085 for (offset, opacity) in [(1.25, 1.0), (2.5, 0.5), (3.75, 0.5)] {
5086 clones.set(0);
5087 tree.metadatas.geometry_changed.clear();
5088 tree.metadatas.mark_geometry_dirty(root);
5089 let metadata = tree.metadatas.entry_or_default(root);
5090 metadata.rel_position = Some(PxPosition::from_f32_xy(offset, 0.0));
5091 metadata.opacity = opacity;
5092 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5093 assert!(
5094 !tree.metadatas.geometry_changed.contains(&root),
5095 "a passive layout must not publish geometry notifications for its own translation"
5096 );
5097 assert_eq!(
5098 tree.metadatas.get(&root).unwrap().abs_position,
5099 Some(PxPosition::from_f32_xy(offset, 0.0)),
5100 "passive layout coordinates must remain available for inspection"
5101 );
5102 assert!(tree.metadatas.geometry_changed.contains(&leaf));
5103 let (graph, _) = scene.update(
5104 root,
5105 &tree.tree,
5106 &tree.metadatas,
5107 &HashSet::default(),
5108 &HashSet::default(),
5109 viewport,
5110 );
5111 assert_eq!(
5112 clones.get(),
5113 1,
5114 "translation must copy the command only into the frame output"
5115 );
5116 let op = graph
5117 .ops()
5118 .iter()
5119 .find(|op| op.command.downcast_draw_ref::<Marker>().is_some())
5120 .unwrap();
5121 let marker = op.command.downcast_draw_ref::<Marker>().unwrap();
5122 assert_eq!(marker.value, 1);
5123 assert_eq!(marker.opacity, opacity);
5124 assert_eq!(
5125 op.geometry.window_origin(),
5126 PxPosition::from_f32_xy(offset, 0.0)
5127 );
5128 }
5129 let metadata = tree.metadatas.entry_or_default(leaf);
5130 metadata.fragment = Default::default();
5131 metadata.fragment.push_draw_command(Marker {
5132 clones,
5133 value: 2,
5134 opacity: 1.0,
5135 });
5136 tree.metadatas.paint_changed.insert(leaf);
5137 let (graph, _) = scene.update(
5138 root,
5139 &tree.tree,
5140 &tree.metadatas,
5141 &HashSet::default(),
5142 &HashSet::default(),
5143 viewport,
5144 );
5145 let marker = graph
5146 .ops()
5147 .iter()
5148 .find_map(|op| op.command.downcast_draw_ref::<Marker>())
5149 .unwrap();
5150 assert_eq!(marker.value, 2);
5151 assert_eq!(marker.opacity, 0.5);
5152 tree.metadatas.paint_changed.clear();
5153 for (offset, width, visible) in [
5154 (150.0, 100, false),
5155 (160.0, 100, false),
5156 (160.0, 200, true),
5157 (160.0, 100, false),
5158 (0.25, 100, true),
5159 ] {
5160 tree.metadatas.geometry_changed.clear();
5161 tree.metadatas.mark_geometry_dirty(root);
5162 tree.metadatas.entry_or_default(root).rel_position =
5163 Some(PxPosition::from_f32_xy(offset, 0.0));
5164 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5165 let (graph, _) = scene.update(
5166 root,
5167 &tree.tree,
5168 &tree.metadatas,
5169 &HashSet::default(),
5170 &HashSet::default(),
5171 PxSize::new(Px(width), Px(100)),
5172 );
5173 assert_eq!(
5174 graph
5175 .ops()
5176 .iter()
5177 .any(|op| op.command.downcast_draw_ref::<Marker>().is_some()),
5178 visible,
5179 "retained visibility must follow translation and viewport resizing"
5180 );
5181 }
5182 tree.metadatas.geometry_changed.clear();
5183 tree.metadatas.mark_geometry_dirty(outside);
5184 tree.metadatas.entry_or_default(outside).rel_position =
5185 Some(PxPosition::from_f32_xy(40.0, 0.0));
5186 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5187 let (graph, _) = scene.update(
5188 root,
5189 &tree.tree,
5190 &tree.metadatas,
5191 &HashSet::default(),
5192 &HashSet::default(),
5193 viewport,
5194 );
5195 assert_eq!(
5196 graph
5197 .ops()
5198 .iter()
5199 .filter_map(|op| op.command.downcast_draw_ref::<Marker>())
5200 .map(|marker| marker.value)
5201 .collect::<Vec<_>>(),
5202 vec![2, 4, 5],
5203 "revealing an omitted branch must publish its latest paint in sibling order"
5204 );
5205 }
5206
5207 #[test]
5208 fn unplaced_layout_subtrees_hide_geometry_input_drawing_semantics_and_focus() {
5209 let _runtime_session = crate::RuntimeSession::new();
5210 let _runtime_scope = _runtime_session.enter();
5211 use crate::focus::{FocusNode, FocusRegistration};
5212
5213 #[derive(Clone)]
5214 struct Marker;
5215
5216 impl crate::DrawCommand for Marker {
5217 fn apply_opacity(&mut self, _opacity: f32) {}
5218 }
5219
5220 let mut tree = ComponentTree::new();
5221 let root = tree.add_node(node("root", 1, 1));
5222 tree.add_node(node_with_role("transparent", NodeRole::Composition, 2, 2));
5223 let panel = tree.add_node(node("panel", 3, 3));
5224 tree.add_node(node_with_role("content", NodeRole::Composition, 4, 4));
5225 let leaf_focus = FocusRegistration::target(FocusNode::new());
5226 let mut leaf_node = node("leaf", 5, 5);
5227 leaf_node.interaction_mut().focus_registration = Some(leaf_focus);
5228 let leaf = tree.add_node(leaf_node);
5229 for _ in 0..4 {
5230 tree.pop_node();
5231 }
5232 let outside_focus = FocusRegistration::target(FocusNode::new());
5233 let mut outside_node = node("outside", 6, 6);
5234 outside_node.interaction_mut().focus_registration = Some(outside_focus);
5235 let outside = tree.add_node(outside_node);
5236 tree.pop_node();
5237 tree.pop_node();
5238 for (id, length) in [(root, 200), (panel, 30), (leaf, 6), (outside, 6)] {
5239 tree.metadatas.entry_or_default(id).computed_data = Some(ComputedData {
5240 width: Px(length),
5241 height: Px(length),
5242 });
5243 }
5244 tree.metadatas.entry_or_default(leaf).rel_position = Some(PxPosition::new(Px(4), Px(5)));
5245 tree.metadatas.entry_or_default(outside).rel_position =
5246 Some(PxPosition::new(Px(240), Px(240)));
5247 tree.metadatas.entry_or_default(root).clips_children = true;
5248 tree.metadatas.entry_or_default(leaf).set_accessibility(
5249 Some(
5250 crate::accessibility::AccessibilityNode::new()
5251 .with_role(accesskit::Role::Button)
5252 .with_label("Panel action"),
5253 ),
5254 None,
5255 );
5256 for position in [Some(20), None, Some(40)] {
5257 tree.metadatas.mark_geometry_dirty(panel);
5258 tree.metadatas.entry_or_default(panel).rel_position =
5259 position.map(|position| PxPosition::new(Px(position), Px(position)));
5260 let metadata = tree.metadatas.entry_or_default(leaf);
5261 metadata.fragment = Default::default();
5262 metadata.fragment.push_draw_command(Marker);
5263 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5264 assert_eq!(
5265 tree.metadatas.get(&outside).unwrap().event_clip_rect(),
5266 Some(PxRect::new(Px::ZERO, Px::ZERO, Px(200), Px(200))),
5267 "the placed traversal target is outside its ancestor's clip"
5268 );
5269 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5270 tree.focus_owner
5271 .sync_layout_from_component_tree(&tree.tree, &tree.metadatas);
5272 if position.is_none() {
5273 assert_eq!(
5274 tree.focus_owner.active_component_node_id(),
5275 None,
5276 "hiding the active subtree releases focus, including capture"
5277 );
5278 }
5279 tree.focus_owner.clear_focus_global(true);
5280 tree.focus_owner.request_focus(leaf_focus.id);
5281 tree.focus_owner.commit_pending();
5282 assert_eq!(
5283 tree.focus_owner.active_component_node_id(),
5284 position.map(|_| leaf),
5285 "unplaced descendants reject direct focus requests"
5286 );
5287 tree.focus_owner.clear_focus_global(true);
5288 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
5289 assert_eq!(
5290 tree.focus_owner.active_component_node_id(),
5291 Some(if position.is_some() { leaf } else { outside }),
5292 "forward traversal skips the unplaced subtree"
5293 );
5294 if position.is_some() {
5295 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
5296 }
5297 assert_eq!(
5298 tree.focus_owner.move_focus(FocusDirection::Previous),
5299 position.is_some(),
5300 "reverse traversal also skips the unplaced subtree"
5301 );
5302 assert_eq!(
5303 tree.focus_owner.active_component_node_id(),
5304 Some(if position.is_some() { leaf } else { outside })
5305 );
5306 if position.is_some() {
5307 tree.focus_owner.capture_focus(leaf_focus.id);
5308 }
5309 let graph = build_render_graph(
5310 root,
5311 &tree.tree,
5312 &mut tree.metadatas,
5313 PxSize::new(Px(200), Px(200)),
5314 );
5315 let expected =
5316 position.map(|position| PxPosition::new(Px(position + 4), Px(position + 5)));
5317 assert_eq!(tree.metadatas.get(&leaf).unwrap().abs_position, expected);
5318 assert_eq!(
5319 tree.metadatas.get(&leaf).unwrap().base_abs_position,
5320 expected
5321 );
5322 assert_eq!(
5323 resolve_node_input_context(&tree.tree, &tree.metadatas, leaf).is_some(),
5324 expected.is_some()
5325 );
5326 let marker_ops: Vec<_> = graph
5327 .ops()
5328 .iter()
5329 .filter(|op| op.type_id == std::any::TypeId::of::<Marker>())
5330 .collect();
5331 assert_eq!(marker_ops.len(), usize::from(expected.is_some()));
5332 let accessibility = crate::accessibility::build_tree_update(
5333 &tree.tree,
5334 &tree.metadatas,
5335 root,
5336 None,
5337 Some(leaf),
5338 )
5339 .expect("accessibility tree");
5340 assert_eq!(
5341 accessibility
5342 .nodes
5343 .iter()
5344 .any(|(_, node)| node.label() == Some("Panel action")),
5345 expected.is_some()
5346 );
5347 assert_eq!(
5348 accessibility
5349 .nodes
5350 .iter()
5351 .find(|(id, _)| *id == accessibility.focus)
5352 .and_then(|(_, node)| node.label()),
5353 expected.map(|_| "Panel action")
5354 );
5355 if let Some(expected) = expected {
5356 assert_eq!(marker_ops[0].geometry.window_origin(), expected);
5357 } else {
5358 tree.metadatas.geometry_changed.clear();
5359 tree.metadatas.mark_geometry_dirty(root);
5360 tree.metadatas.entry_or_default(root).rel_position =
5361 Some(PxPosition::from_f32_xy(0.25, 0.5));
5362 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5363 assert!(
5364 !tree.metadatas.geometry_changed.contains(&leaf),
5365 "moving an ancestor must not invalidate an already hidden subtree"
5366 );
5367 assert!(tree.metadatas.get(&leaf).unwrap().abs_position.is_none());
5368 tree.metadatas.entry_or_default(root).rel_position = None;
5369 tree.metadatas.mark_geometry_dirty(root);
5370 }
5371 }
5372 }
5373
5374 #[test]
5375 fn pointer_samples_route_at_their_own_positions_and_complete_passes_in_order() {
5376 let _runtime_session = crate::RuntimeSession::new();
5377 let _runtime_scope = _runtime_session.enter();
5378 use crate::cursor::{GestureState, MOUSE_POINTER_ID, PressKeyEventType};
5379
5380 let log = Arc::new(std::sync::Mutex::new(Vec::new()));
5381 let mut tree = ComponentTree::new();
5382 let root = tree.add_node(node("root", 1, 1));
5383 let origin = Instant::now();
5384 for index in 0..2 {
5385 let mut target = node("target", index + 2, index + 2);
5386 for pass in [
5387 PointerEventPass::Initial,
5388 PointerEventPass::Main,
5389 PointerEventPass::Final,
5390 ] {
5391 let log = Arc::clone(&log);
5392 let handler: Box<PointerInputHandlerFn> = Box::new(move |input| {
5393 for change in input.pointer_changes.iter() {
5394 log.lock().unwrap().push((
5395 index,
5396 pass,
5397 change.timestamp.duration_since(origin).as_nanos(),
5398 input.cursor_position_rel,
5399 ));
5400 }
5401 });
5402 match pass {
5403 PointerEventPass::Initial => target
5404 .interaction_mut()
5405 .pointer_preview_handlers
5406 .push(handler),
5407 PointerEventPass::Main => {
5408 target.interaction_mut().pointer_handlers.push(handler)
5409 }
5410 PointerEventPass::Final => target
5411 .interaction_mut()
5412 .pointer_final_handlers
5413 .push(handler),
5414 }
5415 }
5416 let id = tree.add_node(target);
5417 let metadata = tree.metadatas.get_mut(&id).unwrap();
5418 let position = PxPosition::new(Px(index as i32 * 200), Px::ZERO);
5419 metadata.base_abs_position = Some(position);
5420 metadata.abs_position = Some(position);
5421 metadata.computed_data = Some(ComputedData {
5422 width: Px(100),
5423 height: Px(100),
5424 });
5425 tree.pop_node();
5426 }
5427 tree.pop_node();
5428 let metadata = tree.metadatas.get_mut(&root).unwrap();
5429 metadata.base_abs_position = Some(PxPosition::ZERO);
5430 metadata.abs_position = Some(PxPosition::ZERO);
5431 metadata.computed_data = Some(ComputedData {
5432 width: Px(400),
5433 height: Px(100),
5434 });
5435 let left = PxPosition::new(Px(20), Px(20));
5436 let right = PxPosition::new(Px(220), Px(20));
5437 let outside = PxPosition::new(Px(500), Px(20));
5438 let mut events: Vec<_> = [
5439 (
5440 CursorEventContent::Pressed(PressKeyEventType::Left),
5441 Some(left),
5442 ),
5443 (
5444 CursorEventContent::Released(PressKeyEventType::Left),
5445 Some(left),
5446 ),
5447 (CursorEventContent::Moved(outside), Some(outside)),
5448 (
5449 CursorEventContent::Pressed(PressKeyEventType::Left),
5450 Some(right),
5451 ),
5452 (
5453 CursorEventContent::Released(PressKeyEventType::Left),
5454 Some(right),
5455 ),
5456 (CursorEventContent::Moved(outside), Some(outside)),
5457 ]
5458 .into_iter()
5459 .enumerate()
5460 .map(|(index, (content, position))| {
5461 PointerChange::new(
5462 origin + std::time::Duration::from_nanos(index as u64),
5463 MOUSE_POINTER_ID,
5464 content,
5465 GestureState::TapCandidate,
5466 )
5467 .with_position(position)
5468 })
5469 .collect();
5470 tree.dispatch_pointer_input(
5471 root,
5472 &mut events,
5473 &mut Some(outside),
5474 winit::keyboard::ModifiersState::empty(),
5475 );
5476 let expected: Vec<_> = [(0, 0), (0, 1), (1, 3), (1, 4)]
5477 .into_iter()
5478 .flat_map(|(index, sample)| {
5479 [
5480 PointerEventPass::Initial,
5481 PointerEventPass::Main,
5482 PointerEventPass::Final,
5483 ]
5484 .map(|pass| (index, pass, sample, Some(left)))
5485 })
5486 .collect();
5487 assert_eq!(*log.lock().unwrap(), expected);
5488 assert!(tree.active_pointer_paths.is_empty());
5489 log.lock().unwrap().clear();
5490 let mut captured: Vec<_> = [
5491 (
5492 CursorEventContent::Pressed(PressKeyEventType::Left),
5493 Some(left),
5494 ),
5495 (CursorEventContent::Moved(outside), Some(outside)),
5496 (CursorEventContent::Released(PressKeyEventType::Left), None),
5497 ]
5498 .into_iter()
5499 .enumerate()
5500 .map(|(index, (content, position))| {
5501 PointerChange::new(
5502 origin + std::time::Duration::from_nanos(index as u64),
5503 MOUSE_POINTER_ID,
5504 content,
5505 GestureState::TapCandidate,
5506 )
5507 .with_position(position)
5508 })
5509 .collect();
5510 tree.dispatch_pointer_input(
5511 root,
5512 &mut captured,
5513 &mut Some(right),
5514 winit::keyboard::ModifiersState::empty(),
5515 );
5516 let expected: Vec<_> = [Some(left), Some(outside), None]
5517 .into_iter()
5518 .enumerate()
5519 .flat_map(|(sample, position)| {
5520 [
5521 PointerEventPass::Initial,
5522 PointerEventPass::Main,
5523 PointerEventPass::Final,
5524 ]
5525 .map(|pass| (0, pass, sample as u128, position))
5526 })
5527 .collect();
5528 assert_eq!(
5529 *log.lock().unwrap(),
5530 expected,
5531 "capture delivers outside movement and an unavailable-position release to the original target"
5532 );
5533 assert!(tree.active_pointer_paths.is_empty());
5534
5535 let mut press = [PointerChange::new(
5536 origin,
5537 MOUSE_POINTER_ID,
5538 CursorEventContent::Pressed(PressKeyEventType::Left),
5539 GestureState::TapCandidate,
5540 )
5541 .with_position(Some(left))];
5542 tree.dispatch_pointer_input(
5543 root,
5544 &mut press,
5545 &mut Some(left),
5546 winit::keyboard::ModifiersState::empty(),
5547 );
5548 let target = tree.find_node_id_by_instance_key(2).unwrap();
5549 tree.metadatas.get_mut(&target).unwrap().event_clip_bounds = Some(crate::FloatRect::ZERO);
5550 tree.metadatas.geometry_changed.insert(target);
5551 log.lock().unwrap().clear();
5552 let mut release = [PointerChange::new(
5553 origin + std::time::Duration::from_nanos(1),
5554 MOUSE_POINTER_ID,
5555 CursorEventContent::Released(PressKeyEventType::Left),
5556 GestureState::TapCandidate,
5557 )
5558 .with_position(Some(left))];
5559 tree.dispatch_pointer_input(
5560 root,
5561 &mut release,
5562 &mut Some(left),
5563 winit::keyboard::ModifiersState::empty(),
5564 );
5565 assert_eq!(
5566 *log.lock().unwrap(),
5567 [
5568 PointerEventPass::Initial,
5569 PointerEventPass::Main,
5570 PointerEventPass::Final
5571 ]
5572 .map(|pass| (0, pass, 1, None)),
5573 "a captured target scrolled outside its clip must still receive release"
5574 );
5575 assert!(tree.active_pointer_paths.is_empty());
5576 }
5577
5578 #[test]
5579 fn recomposition_preserves_pointer_capture_but_runtime_reset_discards_it() {
5580 let _runtime_session = crate::RuntimeSession::new();
5581 let _runtime_scope = _runtime_session.enter();
5582 let mut tree = ComponentTree::new();
5583 tree.active_pointer_paths.insert(7, vec![1, 2]);
5584 tree.active_pointer_positions
5585 .insert(7, Some(PxPosition::ZERO));
5586 tree.clear();
5587 assert_eq!(tree.active_pointer_paths.get(&7), Some(&vec![1, 2]));
5588 assert_eq!(
5589 tree.active_pointer_positions.get(&7),
5590 Some(&Some(PxPosition::ZERO))
5591 );
5592 tree.reset();
5593 assert!(tree.active_pointer_paths.is_empty());
5594 assert!(tree.active_pointer_positions.is_empty());
5595 }
5596
5597 fn focus_targets(attach: bool) -> (ComponentTree, Vec<(NodeId, FocusHandleId)>) {
5598 use crate::focus::{FocusNode, FocusRegistration};
5599
5600 let mut tree = ComponentTree::new();
5601 let root = tree.add_node(node("root", 1, 1));
5602 let mut targets = Vec::new();
5603 for index in 0..2 {
5604 let registration = FocusRegistration::target(FocusNode::new());
5605 let mut target = node("target", index + 2, index + 2);
5606 target.interaction_mut().focus_registration = Some(registration);
5607 let node_id = tree.add_node(target);
5608 targets.push((node_id, registration.id));
5609 tree.pop_node();
5610 }
5611 tree.pop_node();
5612 if attach {
5613 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5614 }
5615 (tree, targets)
5616 }
5617
5618 #[test]
5619 fn repeated_focus_request_does_not_replay_after_action_restores_another_target() {
5620 let _runtime_session = crate::RuntimeSession::new();
5621 let _runtime_scope = _runtime_session.enter();
5622 let (mut tree, targets) = focus_targets(true);
5623 let [(action, action_handle), (anchor, anchor_handle)] = targets.as_slice() else {
5624 unreachable!();
5625 };
5626 tree.focus_owner.request_focus(*action_handle);
5627 assert_eq!(tree.focus_owner.active_component_node_id(), Some(*action));
5628 tree.focus_owner.request_focus(*action_handle);
5629 tree.focus_owner.request_focus(*anchor_handle);
5630 tree.focus_owner.commit_pending();
5631 assert_eq!(tree.focus_owner.active_component_node_id(), Some(*anchor));
5632 tree.focus_owner.commit_pending();
5633 assert_eq!(tree.focus_owner.active_component_node_id(), Some(*anchor));
5634 }
5635
5636 #[test]
5637 fn clearing_an_unfocused_attached_target_does_not_clear_its_future_focus() {
5638 let _runtime_session = crate::RuntimeSession::new();
5639 let _runtime_scope = _runtime_session.enter();
5640 let (mut tree, targets) = focus_targets(true);
5641 tree.focus_owner.request_focus(targets[1].1);
5642 tree.focus_owner.clear_focus(targets[0].1);
5643 tree.focus_owner.request_focus(targets[0].1);
5644 tree.focus_owner.commit_pending();
5645 assert_eq!(
5646 tree.focus_owner.active_component_node_id(),
5647 Some(targets[0].0)
5648 );
5649 }
5650
5651 #[test]
5652 fn rejected_capture_does_not_capture_a_later_focus_request() {
5653 let _runtime_session = crate::RuntimeSession::new();
5654 let _runtime_scope = _runtime_session.enter();
5655 let (mut tree, targets) = focus_targets(true);
5656 tree.focus_owner.request_focus(targets[1].1);
5657 tree.focus_owner.capture_focus(targets[0].1);
5658 tree.focus_owner.request_focus(targets[0].1);
5659 tree.focus_owner.commit_pending();
5660 tree.focus_owner.request_focus(targets[1].1);
5661 tree.focus_owner.commit_pending();
5662 assert_eq!(
5663 tree.focus_owner.active_component_node_id(),
5664 Some(targets[1].0)
5665 );
5666 }
5667
5668 #[test]
5669 fn redundant_free_does_not_release_a_later_capture() {
5670 let _runtime_session = crate::RuntimeSession::new();
5671 let _runtime_scope = _runtime_session.enter();
5672 let (mut tree, targets) = focus_targets(true);
5673 tree.focus_owner.request_focus(targets[0].1);
5674 tree.focus_owner.free_focus(targets[0].1);
5675 tree.focus_owner.capture_focus(targets[0].1);
5676 tree.focus_owner.commit_pending();
5677 tree.focus_owner.request_focus(targets[1].1);
5678 tree.focus_owner.commit_pending();
5679 assert_eq!(
5680 tree.focus_owner.active_component_node_id(),
5681 Some(targets[0].0)
5682 );
5683 }
5684
5685 #[test]
5686 fn requests_before_attachment_are_consumed_once_the_target_is_attached() {
5687 let _runtime_session = crate::RuntimeSession::new();
5688 let _runtime_scope = _runtime_session.enter();
5689 let (mut tree, targets) = focus_targets(false);
5690 tree.focus_owner.request_focus(targets[0].1);
5691 tree.focus_owner.request_focus(targets[0].1);
5692 let root = tree.tree.get(targets[0].0).unwrap().parent().unwrap();
5693 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5694 tree.focus_owner.commit_pending();
5695 assert_eq!(
5696 tree.focus_owner.active_component_node_id(),
5697 Some(targets[0].0)
5698 );
5699 tree.focus_owner.request_focus(targets[1].1);
5700 tree.focus_owner.commit_pending();
5701 assert_eq!(
5702 tree.focus_owner.active_component_node_id(),
5703 Some(targets[1].0)
5704 );
5705 }
5706
5707 #[test]
5708 fn deferred_request_rejected_after_attachment_does_not_activate_on_enable() {
5709 let _runtime_session = crate::RuntimeSession::new();
5710 let _runtime_scope = _runtime_session.enter();
5711 let (mut tree, targets) = focus_targets(false);
5712 tree.focus_owner.request_focus(targets[0].1);
5713 let root = tree.tree.get(targets[0].0).unwrap().parent().unwrap();
5714 tree.tree
5715 .get_mut(targets[0].0)
5716 .unwrap()
5717 .get_mut()
5718 .interaction_mut()
5719 .focus_registration
5720 .as_mut()
5721 .unwrap()
5722 .properties
5723 .can_focus = false;
5724 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5725 tree.focus_owner.commit_pending();
5726 assert_eq!(tree.focus_owner.active_component_node_id(), None);
5727 tree.tree
5728 .get_mut(targets[0].0)
5729 .unwrap()
5730 .get_mut()
5731 .interaction_mut()
5732 .focus_registration
5733 .as_mut()
5734 .unwrap()
5735 .properties
5736 .can_focus = true;
5737 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5738 tree.focus_owner.commit_pending();
5739 assert_eq!(tree.focus_owner.active_component_node_id(), None);
5740 }
5741
5742 #[test]
5743 fn queued_navigation_routes_activation_to_the_new_focus_chain() {
5744 let _runtime_session = crate::RuntimeSession::new();
5745 let _runtime_scope = _runtime_session.enter();
5746 use crate::focus::{FocusNode, FocusRegistration};
5747
5748 let mut tree = ComponentTree::new();
5749 let root = tree.add_node(node("root", 1, 1));
5750 let mut trigger = node("trigger", 2, 2);
5751 trigger.interaction_mut().focus_registration =
5752 Some(FocusRegistration::target(FocusNode::new()));
5753 let trigger = tree.add_node(trigger);
5754 tree.pop_node();
5755 let popup = tree.add_node(node("popup", 3, 3));
5756 let mut action = node("action", 4, 4);
5757 action.interaction_mut().focus_registration =
5758 Some(FocusRegistration::target(FocusNode::new()));
5759 let action = tree.add_node(action);
5760 tree.pop_node();
5761 tree.pop_node();
5762 tree.pop_node();
5763 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5764 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
5765 let mut events = VecDeque::from(["Tab", "Enter", "Shift+Tab", "Enter"]);
5766 let mut activations = Vec::new();
5767 let retry = drain_pending_focused_input(&mut events, |event| {
5768 match event {
5769 "Tab" => assert!(tree.focus_owner.move_focus(FocusDirection::Next)),
5770 "Shift+Tab" => assert!(tree.focus_owner.move_focus(FocusDirection::Previous)),
5771 "Enter" => activations.push(collect_focus_chain_node_ids(
5772 root,
5773 &tree.tree,
5774 &tree.focus_owner,
5775 )),
5776 _ => unreachable!(),
5777 }
5778 tree.focus_owner.commit_pending();
5779 (None, false)
5780 });
5781 assert_eq!(retry, None);
5782 assert!(events.is_empty());
5783 assert_eq!(
5784 activations,
5785 [vec![root, popup, action], vec![root, trigger]]
5786 );
5787 }
5788
5789 #[test]
5790 fn skipped_focus_targets_anchor_navigation_without_becoming_destinations() {
5791 let _runtime_session = crate::RuntimeSession::new();
5792 let _runtime_scope = _runtime_session.enter();
5793 use crate::focus::{FocusNode, FocusRegistration, FocusScopeNode};
5794
5795 let mut tree = ComponentTree::new();
5796 let root = tree.add_node(node("root", 1, 1));
5797 let scope = FocusRegistration::scope(FocusScopeNode::new());
5798 let mut container = node("scope", 2, 2);
5799 container.interaction_mut().focus_registration = Some(scope);
5800 tree.add_node(container);
5801 let mut targets = Vec::new();
5802 for index in 0..3 {
5803 let mut registration = FocusRegistration::target(FocusNode::new());
5804 registration.properties.skip_traversal = index == 1;
5805 let mut target = node("target", index + 3, index + 3);
5806 target.interaction_mut().focus_registration = Some(registration);
5807 targets.push((tree.add_node(target), registration.id));
5808 tree.pop_node();
5809 }
5810 tree.pop_node();
5811 tree.pop_node();
5812 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5813 for scoped in [false, true] {
5814 for (direction, expected) in [(FocusDirection::Next, 2), (FocusDirection::Previous, 0)]
5815 {
5816 tree.focus_owner.request_focus(targets[1].1);
5817 assert_eq!(
5818 tree.focus_owner.active_component_node_id(),
5819 Some(targets[1].0)
5820 );
5821 assert!(if scoped {
5822 tree.focus_owner
5823 .move_focus_in_scope(scope.id, direction, true)
5824 } else {
5825 tree.focus_owner.move_focus(direction)
5826 });
5827 assert_eq!(
5828 tree.focus_owner.active_component_node_id(),
5829 Some(targets[expected].0)
5830 );
5831 }
5832 }
5833 tree.focus_owner.request_focus(targets[0].1);
5834 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
5835 assert_eq!(
5836 tree.focus_owner.active_component_node_id(),
5837 Some(targets[2].0)
5838 );
5839 for index in [0, 2] {
5840 tree.tree[targets[index].0]
5841 .get_mut()
5842 .interaction_mut()
5843 .focus_registration
5844 .as_mut()
5845 .unwrap()
5846 .properties
5847 .can_focus = false;
5848 }
5849 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5850 tree.focus_owner.request_focus(targets[1].1);
5851 for direction in [FocusDirection::Next, FocusDirection::Previous] {
5852 assert!(
5853 !tree
5854 .focus_owner
5855 .move_focus_in_scope(scope.id, direction, true)
5856 );
5857 }
5858 }
5859
5860 #[test]
5861 fn removing_unscoped_focus_does_not_activate_an_unrelated_control() {
5862 let _runtime_session = crate::RuntimeSession::new();
5863 let _runtime_scope = _runtime_session.enter();
5864 let (mut tree, targets) = focus_targets(true);
5865 tree.focus_owner.request_focus(targets[0].1);
5866 assert_eq!(
5867 tree.focus_owner.active_component_node_id(),
5868 Some(targets[0].0)
5869 );
5870 tree.focus_owner
5871 .remove_handles(&[targets[0].1].into_iter().collect(), &HashSet::default());
5872 assert_eq!(tree.focus_owner.active_component_node_id(), None);
5873 }
5874
5875 #[test]
5876 fn removing_focused_handles_restores_to_a_surviving_scope_target() {
5877 let _runtime_session = crate::RuntimeSession::new();
5878 let _runtime_scope = _runtime_session.enter();
5879 use crate::focus::{FocusNode, FocusRegistration, FocusRequester, FocusScopeNode};
5880
5881 let mut tree = ComponentTree::new();
5882 let root = tree.add_node(node("root", 1, 1));
5883 let scope = FocusRegistration::scope(FocusScopeNode::new());
5884 let fallback = FocusRequester::new();
5885 let mut container = node("scope", 2, 2);
5886 container.interaction_mut().focus_registration = Some(scope);
5887 container.interaction_mut().focus_restorer_fallback = Some(fallback);
5888 tree.add_node(container);
5889 let removed = FocusRegistration::target(FocusNode::new());
5890 let mut target = node("removed", 3, 3);
5891 target.interaction_mut().focus_registration = Some(removed);
5892 let removed_node = tree.add_node(target);
5893 tree.pop_node();
5894 let mut target = node("survivor", 4, 4);
5895 target.interaction_mut().focus_registration =
5896 Some(FocusRegistration::target(FocusNode::new()));
5897 target.interaction_mut().focus_requester_binding = Some(fallback);
5898 let survivor = tree.add_node(target);
5899 tree.pop_node();
5900 tree.pop_node();
5901 tree.pop_node();
5902 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5903 tree.focus_owner.request_focus(removed.id);
5904 assert_eq!(
5905 tree.focus_owner.active_component_node_id(),
5906 Some(removed_node)
5907 );
5908 tree.focus_owner
5909 .remove_handles(&[removed.id].into_iter().collect(), &HashSet::default());
5910 assert_eq!(tree.focus_owner.active_component_node_id(), Some(survivor));
5911 }
5912
5913 #[test]
5914 fn detached_focus_scopes_retain_their_target_through_layout_until_reattached() {
5915 let _runtime_session = crate::RuntimeSession::new();
5916 let _runtime_scope = _runtime_session.enter();
5917 use crate::focus::{FocusNode, FocusRegistration, FocusScopeNode};
5918
5919 let mut tree = ComponentTree::new();
5920 let root = tree.add_node(node("root", 1, 1));
5921 let mut container = node_with_role("scope", NodeRole::Composition, 2, 2);
5922 container.interaction_mut().focus_registration =
5923 Some(FocusRegistration::scope(FocusScopeNode::new()));
5924 let scope = tree.add_node(container);
5925 let registration = FocusRegistration::target(FocusNode::new());
5926 let mut target = node("target", 3, 3);
5927 target.interaction_mut().focus_registration = Some(registration);
5928 let target = tree.add_node(target);
5929 for _ in 0..3 {
5930 tree.pop_node();
5931 }
5932 for id in [root, target] {
5933 let metadata = tree.metadatas.entry_or_default(id);
5934 metadata.computed_data = Some(ComputedData {
5935 width: Px(20),
5936 height: Px(20),
5937 });
5938 metadata.rel_position = Some(PxPosition::ZERO);
5939 }
5940 populate_layout_metadata(root, &tree.tree, &mut tree.metadatas);
5941 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5942 tree.focus_owner
5943 .sync_layout_from_component_tree(&tree.tree, &tree.metadatas);
5944 tree.focus_owner.request_focus(registration.id);
5945 assert_eq!(tree.focus_owner.active_component_node_id(), Some(target));
5946
5947 scope.detach(&mut tree.tree);
5948 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5949 tree.focus_owner
5950 .sync_layout_from_component_tree(&tree.tree, &tree.metadatas);
5951 assert_eq!(tree.focus_owner.active_component_node_id(), None);
5952 root.append(scope, &mut tree.tree);
5953 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5954 tree.focus_owner
5955 .sync_layout_from_component_tree(&tree.tree, &tree.metadatas);
5956 assert_eq!(tree.focus_owner.active_component_node_id(), Some(target));
5957 }
5958
5959 #[test]
5960 fn redundant_scope_restore_does_not_pull_focus_back_from_another_scope() {
5961 let _runtime_session = crate::RuntimeSession::new();
5962 let _runtime_scope = _runtime_session.enter();
5963 use crate::focus::{FocusNode, FocusRegistration, FocusScopeNode};
5964
5965 let mut tree = ComponentTree::new();
5966 let root = tree.add_node(node("root", 1, 1));
5967 let scope = FocusRegistration::scope(FocusScopeNode::new());
5968 let mut container = node("scope", 2, 2);
5969 container.interaction_mut().focus_registration = Some(scope);
5970 tree.add_node(container);
5971 let inside = FocusRegistration::target(FocusNode::new());
5972 let mut target = node("inside", 3, 3);
5973 target.interaction_mut().focus_registration = Some(inside);
5974 tree.add_node(target);
5975 tree.pop_node();
5976 tree.pop_node();
5977 let outside = FocusRegistration::target(FocusNode::new());
5978 let mut target = node("outside", 4, 4);
5979 target.interaction_mut().focus_registration = Some(outside);
5980 let outside_node = tree.add_node(target);
5981 tree.pop_node();
5982 tree.pop_node();
5983 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
5984 tree.focus_owner.request_focus(inside.id);
5985 tree.focus_owner.restore_focus(scope.id);
5986 tree.focus_owner.request_focus(outside.id);
5987 tree.focus_owner.commit_pending();
5988 assert_eq!(
5989 tree.focus_owner.active_component_node_id(),
5990 Some(outside_node)
5991 );
5992 }
5993
5994 #[test]
5995 fn focus_retry_keeps_later_keys_before_newly_received_input() {
5996 let _runtime_session = crate::RuntimeSession::new();
5997 let _runtime_scope = _runtime_session.enter();
5998 let mut events = VecDeque::from(["Tab into lazy content", "Enter", "Shift+Tab"]);
5999 let mut dispatched = Vec::new();
6000 let retry = drain_pending_focused_input(&mut events, |event| {
6001 dispatched.push(event);
6002 (Some(FocusDirection::Next), false)
6003 });
6004 assert_eq!(retry, Some(FocusDirection::Next));
6005 assert_eq!(dispatched, ["Tab into lazy content"]);
6006 events.push_back("new input");
6007 assert_eq!(
6008 drain_pending_focused_input(&mut events, |event| {
6009 dispatched.push(event);
6010 (None, false)
6011 }),
6012 None
6013 );
6014 assert_eq!(
6015 dispatched,
6016 ["Tab into lazy content", "Enter", "Shift+Tab", "new input"]
6017 );
6018 assert!(events.is_empty());
6019 }
6020
6021 #[test]
6022 fn keyboard_barrier_preserves_order_until_a_subsequent_layout_finishes() {
6023 let _runtime_session = crate::RuntimeSession::new();
6024 let _runtime_scope = _runtime_session.enter();
6025 use crate::focus::{FocusManager, bind_focus_owner};
6026
6027 let mut owner = FocusOwner::new();
6028 let mut events = VecDeque::from(["close popup", "Enter", "Tab"]);
6029 let mut delivered = Vec::new();
6030 assert_eq!(
6031 drain_pending_focused_input(&mut events, |event| {
6032 delivered.push(event);
6033 {
6034 let _binding = bind_focus_owner(&mut owner);
6035 FocusManager::current().defer_keyboard_until_layout();
6036 }
6037 (None, owner.keyboard_is_deferred())
6038 }),
6039 None
6040 );
6041 assert_eq!(delivered, ["close popup"]);
6042 assert_eq!(events, ["Enter", "Tab"]);
6043 owner.finish_layout();
6044 assert!(
6045 owner.keyboard_is_deferred(),
6046 "the old layout cannot release the barrier"
6047 );
6048 owner.begin_layout();
6049 assert!(
6050 owner.keyboard_is_deferred(),
6051 "focus callbacks still have to finish"
6052 );
6053 {
6054 let _binding = bind_focus_owner(&mut owner);
6055 FocusManager::current().defer_keyboard_until_layout();
6056 }
6057 owner.finish_layout();
6058 assert!(
6059 owner.keyboard_is_deferred(),
6060 "a new transition survives the old layout callback"
6061 );
6062 owner.begin_layout();
6063 owner.finish_layout();
6064 assert!(!owner.keyboard_is_deferred());
6065 events.push_back("new input");
6066 drain_pending_focused_input(&mut events, |event| {
6067 delivered.push(event);
6068 (None, owner.keyboard_is_deferred())
6069 });
6070 assert_eq!(delivered, ["close popup", "Enter", "Tab", "new input"]);
6071 assert!(events.is_empty());
6072 }
6073
6074 #[test]
6075 fn recomposition_retains_deferred_input_but_runtime_reset_discards_it() {
6076 let _runtime_session = crate::RuntimeSession::new();
6077 let _runtime_scope = _runtime_session.enter();
6078 use crate::focus::{FocusManager, bind_focus_owner};
6079
6080 let mut tree = ComponentTree::new();
6081 {
6082 let _binding = bind_focus_owner(&mut tree.focus_owner);
6083 FocusManager::current().defer_keyboard_until_layout();
6084 assert!(FocusManager::current().request_move_focus(FocusDirection::Previous));
6085 assert!(FocusManager::current().request_move_focus(FocusDirection::Next));
6086 }
6087 assert_eq!(
6088 tree.focus_owner.take_pending_navigation(),
6089 Some(FocusDirection::Next)
6090 );
6091 tree.focus_owner
6092 .retain_navigation_retry(Some(FocusDirection::Next));
6093 let mut ime = crate::ime_state::ImeState::default();
6094 ime.push_event_for(None, winit::event::Ime::Commit("pending text".into()));
6095 tree.queue_focused_input(Vec::new(), ime.take_events());
6096 clipboard::request_clipboard_action(
6097 crate::clipboard::ClipboardAction::Paste("pending paste".into()),
6098 |_| panic!("reset clipboard request must not execute"),
6099 );
6100 tree.pending_focused_input.extend(
6101 clipboard::take_requests()
6102 .into_iter()
6103 .map(QueuedFocusedInput::Clipboard),
6104 );
6105 tree.clear();
6106 assert_eq!(tree.pending_focused_input.len(), 2);
6107 assert!(tree.focus_owner.has_pending_navigation());
6108 assert!(tree.focus_owner.keyboard_is_deferred());
6109 assert!(matches!(
6110 tree.pending_focused_input.front(),
6111 Some(QueuedFocusedInput::Ime(QueuedInput { event: (None, winit::event::Ime::Commit(text)), .. })) if text == "pending text"
6112 ));
6113 tree.focus_owner.set_owner_focused(false);
6114 assert!(!tree.focus_owner.has_pending_navigation());
6115 {
6116 let _binding = bind_focus_owner(&mut tree.focus_owner);
6117 assert!(!FocusManager::current().request_move_focus(FocusDirection::Previous));
6118 }
6119 tree.focus_owner.set_owner_focused(true);
6120 {
6121 let _binding = bind_focus_owner(&mut tree.focus_owner);
6122 assert!(FocusManager::current().request_move_focus(FocusDirection::Previous));
6123 }
6124 tree.reset();
6125 assert!(!tree.focus_owner.has_pending_navigation());
6126 assert!(!tree.focus_owner.keyboard_is_deferred());
6127 assert!(tree.pending_focused_input.is_empty());
6128 }
6129
6130 #[test]
6131 fn ime_commit_is_delivered_once_to_its_focused_client() {
6132 let _runtime_session = crate::RuntimeSession::new();
6133 let _runtime_scope = _runtime_session.enter();
6134 use crate::focus::{FocusNode, FocusRegistration};
6135
6136 let delivered = Arc::new(std::sync::Mutex::new(Vec::new()));
6137 let mut tree = ComponentTree::new();
6138 let root = tree.add_node(node("root", 1, 1));
6139 for index in 0..2 {
6140 let mut input = node("input", index + 2, index + 2);
6141 input.interaction_mut().focus_registration =
6142 Some(FocusRegistration::target(FocusNode::new()));
6143 let keys = Arc::clone(&delivered);
6144 input
6145 .interaction_mut()
6146 .keyboard_handlers
6147 .push(Box::new(move |input| {
6148 for _ in input.keyboard_events.drain(..) {
6149 keys.lock().unwrap().push((index, "key".into()));
6150 }
6151 }));
6152 let delivered = Arc::clone(&delivered);
6153 input
6154 .interaction_mut()
6155 .ime_handlers
6156 .push(Box::new(move |input| {
6157 for event in input.ime_events.drain(..) {
6158 if let winit::event::Ime::Commit(text) = event {
6159 delivered.lock().unwrap().push((index, text));
6160 }
6161 }
6162 }));
6163 tree.add_node(input);
6164 if index == 1 {
6165 let mut content = node("content", 10, 10);
6166 content
6167 .interaction_mut()
6168 .pointer_handlers
6169 .push(Box::new(|mut input| {
6170 input
6171 .ime_session()
6172 .show(ImeRequest::new(PxSize::new(Px(1), Px(1))));
6173 }));
6174 tree.add_node(content);
6175 tree.pop_node();
6176 }
6177 tree.pop_node();
6178 }
6179 tree.pop_node();
6180 for id in root.descendants(&tree.tree) {
6181 let metadata = tree.metadatas.get_mut(&id).unwrap();
6182 metadata.base_abs_position = Some(PxPosition::ZERO);
6183 metadata.abs_position = Some(PxPosition::ZERO);
6184 metadata.computed_data = Some(ComputedData {
6185 width: Px(100),
6186 height: Px(40),
6187 });
6188 }
6189 tree.focus_owner.sync_from_component_tree(root, &tree.tree);
6190 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
6191 let stale_client = tree.focus_owner.active_handle_id();
6192 assert!(tree.focus_owner.move_focus(FocusDirection::Next));
6193 let requests = tree.dispatch_pointer_input(
6194 root,
6195 &mut [],
6196 &mut Some(PxPosition::new(Px(5), Px(5))),
6197 winit::keyboard::ModifiersState::empty(),
6198 );
6199 let request = requests.ime_request.unwrap();
6200 assert_eq!(request.client_id, tree.focus_owner.active_handle_id());
6201 assert!(request.show_keyboard);
6202 let mut events = vec![
6203 (stale_client, winit::event::Ime::Commit("stale text".into())),
6204 (
6205 tree.focus_owner.active_handle_id(),
6206 winit::event::Ime::Commit("composed text".into()),
6207 ),
6208 ];
6209 let mut requests = WindowRequests::default();
6210 tree.dispatch_ime_input(root, &mut events, &mut requests);
6211 tree.dispatch_ime_input(root, &mut events, &mut requests);
6212 assert_eq!(*delivered.lock().unwrap(), [(1, "composed text".into())]);
6213 let mut ime = crate::ime_state::ImeState::default();
6214 let mut keyboard = crate::keyboard_state::KeyboardState::default();
6215 ime.push_event_for(None, winit::event::Ime::Commit("before".into()));
6216 keyboard.push_event(crate::KeyboardEvent::named(
6217 winit::keyboard::NamedKey::Backspace,
6218 winit::event::ElementState::Pressed,
6219 ));
6220 ime.push_event_for(None, winit::event::Ime::Commit("after".into()));
6221 tree.flush_focused_input(
6222 &mut crate::cursor::CursorState::default(),
6223 winit::keyboard::ModifiersState::empty(),
6224 keyboard.take_events(),
6225 ime.take_events(),
6226 );
6227 assert_eq!(
6228 &delivered.lock().unwrap()[1..],
6229 [(1, "before".into()), (1, "key".into()), (1, "after".into())]
6230 );
6231 let inputs: Vec<_> = root.children(&tree.tree).collect();
6232 tree.tree
6233 .get_mut(inputs[0])
6234 .unwrap()
6235 .get_mut()
6236 .interaction_mut()
6237 .ime_handlers = vec![Box::new(|mut input| {
6238 assert!(crate::FocusManager::current().move_focus(FocusDirection::Next));
6239 input
6240 .ime_session()
6241 .update(ImeRequest::new(PxSize::new(Px(1), Px(1))));
6242 input.block_ime();
6243 })];
6244 tree.tree
6245 .get_mut(inputs[1])
6246 .unwrap()
6247 .get_mut()
6248 .interaction_mut()
6249 .ime_handlers
6250 .push(Box::new(|mut input| {
6251 input
6252 .ime_session()
6253 .update(ImeRequest::new(PxSize::new(Px(1), Px(1))));
6254 }));
6255 assert!(tree.focus_owner.move_focus(FocusDirection::Previous));
6256 events.push((
6257 tree.focus_owner.active_handle_id(),
6258 winit::event::Ime::Commit("switch".into()),
6259 ));
6260 tree.dispatch_ime_input(root, &mut events, &mut requests);
6261 assert!(requests.ime_request.is_none());
6262 events.push((
6263 tree.focus_owner.active_handle_id(),
6264 winit::event::Ime::Commit("next text".into()),
6265 ));
6266 tree.dispatch_ime_input(root, &mut events, &mut requests);
6267 assert_eq!(
6268 requests.ime_request.unwrap().client_id,
6269 tree.focus_owner.active_handle_id()
6270 );
6271 assert_eq!(
6272 delivered.lock().unwrap().last(),
6273 Some(&(1, "next text".into()))
6274 );
6275 requests = WindowRequests::default();
6276 tree.dispatch_ime_input(root, &mut events, &mut requests);
6277 assert_eq!(
6278 requests.ime_request.unwrap().client_id,
6279 tree.focus_owner.active_handle_id()
6280 );
6281 for (source, expected) in [
6282 (stale_client, winit::event::ImeChangeCause::Other),
6283 (
6284 tree.focus_owner.active_handle_id(),
6285 winit::event::ImeChangeCause::InputMethod,
6286 ),
6287 ] {
6288 let mut ime = crate::ime_state::ImeState::default();
6289 ime.push_event_for(source, winit::event::Ime::Commit("source".into()));
6290 let requests = tree.flush_focused_input(
6291 &mut crate::cursor::CursorState::default(),
6292 winit::keyboard::ModifiersState::empty(),
6293 Vec::new(),
6294 ime.take_events(),
6295 );
6296 assert_eq!(requests.ime_request.unwrap().change_cause, expected);
6297 }
6298 tree.tree
6299 .get_mut(inputs[0])
6300 .unwrap()
6301 .get_mut()
6302 .interaction_mut()
6303 .ime_handlers = vec![Box::new(|mut input| {
6304 input
6305 .ime_session()
6306 .update(ImeRequest::new(PxSize::new(Px(1), Px(1))));
6307 })];
6308 tree.tree
6309 .get_mut(root)
6310 .unwrap()
6311 .get_mut()
6312 .interaction_mut()
6313 .ime_preview_handlers = vec![Box::new(|_| {
6314 assert!(crate::FocusManager::current().move_focus(FocusDirection::Next));
6315 })];
6316 assert!(tree.focus_owner.move_focus(FocusDirection::Previous));
6317 requests = WindowRequests::default();
6318 tree.dispatch_ime_input(root, &mut events, &mut requests);
6319 assert!(requests.ime_request.is_none());
6320 }
6321
6322 #[test]
6323 fn begin_replace_subtree_rejects_root_instance_key() {
6324 let _runtime_session = crate::RuntimeSession::new();
6325 let _runtime_scope = _runtime_session.enter();
6326 let mut tree = ComponentTree::new();
6327
6328 let root = tree.add_node(node("root", 1, 1));
6329 tree.pop_node();
6330
6331 let result = tree.begin_replace_subtree_by_instance_key(1);
6332 assert!(matches!(
6333 result,
6334 Err(ReplayReplaceError::RootNodeNotReplaceable)
6335 ));
6336 assert!(tree.get(root).is_some());
6337 }
6338
6339 #[test]
6340 fn finish_replace_subtree_keeps_inserted_roots_before_next_sibling() {
6341 let _runtime_session = crate::RuntimeSession::new();
6342 let _runtime_scope = _runtime_session.enter();
6343 let mut tree = ComponentTree::new();
6344
6345 let root = tree.add_node(node("root", 1, 1));
6346
6347 let _first = tree.add_node(node("first", 2, 2));
6348 let _first_child = tree.add_node(node("first_child", 3, 3));
6349 tree.pop_node();
6350 tree.pop_node();
6351
6352 let second = tree.add_node(node("second", 4, 4));
6353
6354 let context = match tree.begin_replace_subtree_by_instance_key(2) {
6355 Ok(context) => context,
6356 Err(_) => panic!("replace context should be created"),
6357 };
6358
6359 let new_a = tree.add_node(node("new_a", 2, 5));
6360 let _new_a_child = tree.add_node(node("new_a_child", 60, 6));
6361 tree.pop_node();
6362 tree.pop_node();
6363 let new_b = tree.add_node(node("new_b", 70, 7));
6364 tree.pop_node();
6365
6366 let mut replace_result = ReplayReplaceResult::default();
6367 assert!(
6368 tree.finish_replace_subtree(context, &mut replace_result)
6369 .is_ok()
6370 );
6371 assert_eq!(tree.current_build_node_id(), Some(second));
6372 tree.pop_node();
6373 tree.pop_node();
6374 tree.finish_replay_round();
6375
6376 let root_children = root.children(tree.tree()).collect::<Vec<_>>();
6377 assert_eq!(root_children, vec![new_a, new_b, second]);
6378 #[cfg(feature = "profiling")]
6379 assert_eq!(tree.get(root).unwrap().subtree_len, 5);
6380
6381 assert!(replace_result.removed_instance_keys.contains(&2));
6382 assert!(replace_result.removed_instance_keys.contains(&3));
6383 assert_eq!(
6384 replace_result.executed_keys().collect::<HashSet<_>>(),
6385 [5, 6, 7].into_iter().collect()
6386 );
6387 assert_eq!(
6388 replace_result.removed_instance_logic_ids,
6389 [3].into_iter().collect()
6390 );
6391 assert_eq!(
6392 replace_result
6393 .recomposed_logic_ids()
6394 .collect::<HashSet<_>>(),
6395 [2, 60, 70].into_iter().collect()
6396 );
6397
6398 assert!(tree.get(second).is_some());
6399 }
6400
6401 #[test]
6402 fn finish_replace_subtree_keeps_reused_subtree_and_excludes_it_from_removed_sets() {
6403 let _runtime_session = crate::RuntimeSession::new();
6404 let _runtime_scope = _runtime_session.enter();
6405 let mut tree = ComponentTree::new();
6406
6407 let root = tree.add_node(node("root", 1, 1));
6408
6409 let _first = tree.add_node(node("first", 2, 2));
6410 let _first_child = tree.add_node(node("first_child", 3, 3));
6411 tree.pop_node();
6412 tree.pop_node();
6413
6414 let second = tree.add_node(node("second", 4, 4));
6415 tree.pop_node();
6416 tree.pop_node();
6417
6418 let context = match tree.begin_replace_subtree_by_instance_key(2) {
6419 Ok(context) => context,
6420 Err(_) => panic!("replace context should be created"),
6421 };
6422
6423 assert_eq!(tree.attach_retained_subtree(2, 2), Some(_first));
6424 tree.pop_node();
6425
6426 let mut replace_result = ReplayReplaceResult::default();
6427 assert!(
6428 tree.finish_replace_subtree(context, &mut replace_result)
6429 .is_ok()
6430 );
6431 tree.finish_replay_round();
6432
6433 let root_children = root
6434 .children(tree.tree())
6435 .map(|id| tree.get(id).expect("child must exist").fn_name)
6436 .collect::<Vec<_>>();
6437 assert_eq!(root_children, vec!["first", "second"]);
6438 #[cfg(feature = "profiling")]
6439 assert_eq!(tree.get(root).unwrap().subtree_len, 4);
6440 assert!(tree.find_node_id_by_instance_key(3).is_some());
6441 assert!(tree.get(second).is_some());
6442
6443 assert_eq!(replace_result.executed_keys().next(), None);
6444 assert_eq!(tree.find_node_id_by_instance_key(3), Some(_first_child));
6445 assert_eq!(replace_result.recomposed_logic_ids().next(), None);
6446 assert!(tree.removed_frame_nodes.is_empty());
6447 assert!(!replace_result.removed_instance_keys.contains(&2));
6448 assert!(!replace_result.removed_instance_keys.contains(&3));
6449 assert!(!replace_result.removed_instance_logic_ids.contains(&2));
6450 assert!(!replace_result.removed_instance_logic_ids.contains(&3));
6451
6452 let Ok(context) = tree.begin_replace_subtree_by_instance_key(4) else {
6453 panic!("second replace context should be created");
6454 };
6455 tree.add_node(node("new_second", 5, 5));
6456 tree.pop_node();
6457 assert!(
6458 tree.finish_replace_subtree(context, &mut replace_result)
6459 .is_ok()
6460 );
6461 assert!(replace_result.executed_keys().eq([5]));
6462 assert!(replace_result.recomposed_logic_ids().eq([5]));
6463 assert_eq!(
6464 replace_result.removed_instance_keys,
6465 [4].into_iter().collect()
6466 );
6467 }
6468
6469 #[test]
6470 fn replay_preserves_nested_layout_adjacency_and_ancestor_counts() {
6471 let _runtime_session = crate::RuntimeSession::new();
6472 let _runtime_scope = _runtime_session.enter();
6473 let mut tree = ComponentTree::new();
6474 let root = tree.add_node(node_with_role("root", NodeRole::Composition, 1, 1));
6475 let outer = tree.add_node(node("outer", 2, 2));
6476 let inner = tree.add_node(node("inner", 3, 3));
6477 tree.add_node(node_with_role("wrapper", NodeRole::Composition, 4, 4));
6478 let leaf = tree.add_node(node("leaf", 5, 5));
6479 for _ in 0..5 {
6480 tree.pop_node();
6481 }
6482 for add_extra in [true, false] {
6483 let context = tree
6484 .begin_replace_subtree_by_instance_key(4)
6485 .unwrap_or_else(|_| panic!("wrapper exists"));
6486 let wrapper = tree.add_node(node_with_role("wrapper", NodeRole::Composition, 4, 4));
6487 assert_eq!(tree.attach_retained_subtree(5, 5), Some(leaf));
6488 tree.pop_node();
6489 let extra = add_extra.then(|| {
6490 let extra = tree.add_node(node("extra", 6, 6));
6491 tree.pop_node();
6492 extra
6493 });
6494 tree.pop_node();
6495 assert!(
6496 tree.finish_replace_subtree(context, &mut ReplayReplaceResult::default())
6497 .is_ok()
6498 );
6499 tree.finish_replay_round();
6500 assert_eq!(direct_layout_children(root, &tree.tree).as_ref(), &[outer]);
6501 assert_eq!(direct_layout_children(outer, &tree.tree).as_ref(), &[inner]);
6502 let expected = extra.map_or_else(|| vec![leaf], |extra| vec![leaf, extra]);
6503 let actual = direct_layout_children(inner, &tree.tree);
6504 assert_eq!(actual.as_ref(), expected.as_slice());
6505 let wrapped = direct_layout_children(wrapper, &tree.tree);
6506 match (extra, actual, wrapped) {
6507 (Some(_), node::LayoutChildren::Shared(a), node::LayoutChildren::Shared(b)) => {
6508 assert!(Arc::ptr_eq(&a, &b));
6509 }
6510 (None, node::LayoutChildren::One(a), node::LayoutChildren::One(b)) => {
6511 assert_eq!((a, b), (leaf, leaf));
6512 }
6513 _ => panic!("layout adjacency must store a singleton inline and share wider lists"),
6514 }
6515 #[cfg(feature = "profiling")]
6516 for (id, base) in [(root, 5), (outer, 4), (inner, 3), (wrapper, 2)] {
6517 assert_eq!(
6518 tree.get(id).unwrap().subtree_len,
6519 base + usize::from(add_extra)
6520 );
6521 }
6522 }
6523 }
6524
6525 #[test]
6526 fn layout_adjacency_tracks_transparent_subtree_replacement() {
6527 let _runtime_session = crate::RuntimeSession::new();
6528 let _runtime_scope = _runtime_session.enter();
6529 let mut tree = ComponentTree::new();
6530
6531 let root = tree.add_node(node_with_role("root", NodeRole::Composition, 1, 1));
6532 let layout_a = tree.add_node(node("layout_a", 2, 2));
6533 let size = ComputedData {
6534 width: Px(31),
6535 height: Px(17),
6536 };
6537 tree.metadatas.entry_or_default(layout_a).computed_data = Some(size);
6538 tree.pop_node();
6539
6540 let composition = tree.add_node(node_with_role("wrapper", NodeRole::Composition, 3, 3));
6541 let layout_b = tree.add_node(node("layout_b", 4, 4));
6542 tree.pop_node();
6543 tree.pop_node();
6544 tree.pop_node();
6545
6546 assert_eq!(composition.children(tree.tree()).count(), 1);
6547 assert_eq!(
6548 direct_layout_children(root, tree.tree()).as_ref(),
6549 &[layout_a, layout_b]
6550 );
6551
6552 let context = tree
6553 .begin_replace_subtree_by_instance_key(3)
6554 .unwrap_or_else(|_| panic!("wrapper exists"));
6555 tree.add_node(node_with_role("replacement", NodeRole::Composition, 9, 9));
6556 assert_eq!(tree.attach_retained_subtree(4, 4), Some(layout_b));
6557 tree.pop_node();
6558 let layout_c = tree.add_node(node("layout_c", 10, 10));
6559 tree.pop_node();
6560 tree.pop_node();
6561 assert!(
6562 tree.finish_replace_subtree(context, &mut ReplayReplaceResult::default())
6563 .is_ok()
6564 );
6565
6566 let context = tree
6567 .begin_replace_subtree_by_instance_key(2)
6568 .unwrap_or_else(|_| panic!("first layout exists"));
6569 let layout_a = tree.add_node(node("replaced_layout_a", 2, 2));
6570 tree.pop_node();
6571 assert!(
6572 tree.finish_replace_subtree(context, &mut ReplayReplaceResult::default())
6573 .is_ok()
6574 );
6575 assert_eq!(
6576 tree.metadatas.get(&layout_a).unwrap().computed_data,
6577 Some(size)
6578 );
6579 tree.finish_replay_round();
6580 assert_eq!(
6581 direct_layout_children(root, tree.tree()).as_ref(),
6582 &[layout_a, layout_b, layout_c]
6583 );
6584
6585 let context = tree
6586 .begin_replace_subtree_by_instance_key(9)
6587 .unwrap_or_else(|_| panic!("replacement exists"));
6588 tree.add_node(node_with_role("reordered", NodeRole::Composition, 9, 9));
6589 for (key, id) in [(10, layout_c), (4, layout_b)] {
6590 assert_eq!(tree.attach_retained_subtree(key, key), Some(id));
6591 tree.pop_node();
6592 }
6593 tree.pop_node();
6594 assert!(
6595 tree.finish_replace_subtree(context, &mut ReplayReplaceResult::default())
6596 .is_ok()
6597 );
6598 tree.finish_replay_round();
6599 assert_eq!(
6600 direct_layout_children(root, tree.tree()).as_ref(),
6601 &[layout_a, layout_c, layout_b]
6602 );
6603 }
6604}
6605
6606#[cfg(all(test, feature = "testing"))]
6607#[path = "replay_journal_tests.rs"]
6608mod replay_journal_tests;