1mod constraint;
2mod node;
3
4use std::{
5 num::NonZero,
6 sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9 },
10};
11
12use dashmap::DashMap;
13use parking_lot::RwLock;
14use rustc_hash::{FxBuildHasher, FxHashMap as HashMap, FxHashSet as HashSet};
15use tracing::{debug, warn};
16
17use crate::{
18 ComputeResourceManager, Px, PxRect,
19 cursor::{CursorEventContent, PointerChange},
20 focus::{
21 FocusDirection, FocusHandleId, FocusOwner, PendingFocusCallbackInvocation, bind_focus_owner,
22 },
23 layout::{LayoutResult, RenderInput},
24 modifier::{
25 DrawModifierContent, DrawModifierContext, ImeInputModifierNode, KeyboardInputModifierNode,
26 PointerInputModifierNode,
27 },
28 px::{PxPosition, PxSize},
29 render_graph::{RenderGraph, RenderGraphBuilder},
30 runtime::{
31 LayoutDirtyNodes, RuntimePhase, StructureReconcileResult,
32 push_current_component_instance_key, push_current_node_with_instance_logic_id, push_phase,
33 },
34 time::Instant,
35};
36
37pub use constraint::{Constraint, DimensionValue, ParentConstraint};
38pub use node::{
39 ComputedData, ImeInput, ImeInputHandlerFn, ImeRequest, ImeSession, KeyboardInput,
40 KeyboardInputHandlerFn, MeasurementError, PointerEventPass, PointerInput,
41 PointerInputHandlerFn, WindowAction,
42};
43
44pub(crate) use node::{
45 ComponentNode, ComponentNodeMetaData, ComponentNodeMetaDatas, ComponentNodeTree,
46 WindowRequests, measure_node, measure_nodes,
47};
48
49#[cfg(feature = "profiling")]
50use crate::profiler::{NodeMeta, Phase as ProfilerPhase, ScopeGuard as ProfilerScopeGuard};
51
52pub(crate) struct LayoutSnapshotEntry {
53 pub constraint_key: Constraint,
54 pub layout_result: LayoutResult,
55 pub child_constraints: Vec<Constraint>,
56 pub child_sizes: Vec<ComputedData>,
57}
58
59pub(crate) type LayoutSnapshotMap = DashMap<u64, LayoutSnapshotEntry, FxBuildHasher>;
60
61#[derive(Default)]
62struct LayoutSnapshotStore {
63 entries: LayoutSnapshotMap,
64}
65
66thread_local! {
67 static LAYOUT_SNAPSHOT_STORE: LayoutSnapshotStore = LayoutSnapshotStore::default();
68}
69
70fn with_layout_snapshot_entries<R>(f: impl FnOnce(&LayoutSnapshotMap) -> R) -> R {
71 LAYOUT_SNAPSHOT_STORE.with(|store| f(&store.entries))
72}
73
74pub(crate) fn clear_layout_snapshots() {
75 with_layout_snapshot_entries(LayoutSnapshotMap::clear);
76}
77
78fn remove_layout_snapshots(keys: &HashSet<u64>) {
79 if keys.is_empty() {
80 return;
81 }
82 with_layout_snapshot_entries(|snapshots| {
83 for key in keys {
84 snapshots.remove(key);
85 }
86 });
87}
88
89#[derive(Clone, Copy)]
90pub(crate) struct LayoutContext<'a> {
91 pub snapshots: &'a LayoutSnapshotMap,
92 pub measure_self_nodes: &'a HashSet<u64>,
93 pub placement_self_nodes: &'a HashSet<u64>,
94 pub dirty_effective_nodes: &'a HashSet<u64>,
95 pub diagnostics: &'a LayoutDiagnosticsCollector,
96}
97
98#[cfg_attr(not(feature = "profiling"), allow(dead_code))]
99#[derive(Clone, Copy, Debug, Default)]
100pub struct LayoutFrameDiagnostics {
101 pub dirty_nodes_param: u64,
102 pub dirty_nodes_structural: u64,
103 pub dirty_nodes_with_ancestors: u64,
104 pub dirty_expand_ns: u64,
105 pub measure_node_calls: u64,
106 pub cache_hits_direct: u64,
107 pub cache_hits_boundary: u64,
108 pub cache_miss_no_entry: u64,
109 pub cache_miss_constraint: u64,
110 pub cache_miss_dirty_self: u64,
111 pub cache_miss_child_size: u64,
112 pub cache_store_count: u64,
113 pub cache_drop_non_cacheable_count: u64,
114}
115
116#[derive(Default)]
117pub(crate) struct LayoutDiagnosticsCollector {
118 measure_node_calls: AtomicU64,
119 cache_hits_direct: AtomicU64,
120 cache_hits_boundary: AtomicU64,
121 cache_miss_no_entry: AtomicU64,
122 cache_miss_constraint: AtomicU64,
123 cache_miss_dirty_self: AtomicU64,
124 cache_miss_child_size: AtomicU64,
125 cache_store_count: AtomicU64,
126 cache_drop_non_cacheable_count: AtomicU64,
127}
128
129impl LayoutDiagnosticsCollector {
130 #[inline]
131 pub(crate) fn inc_measure_node_calls(&self) {
132 self.measure_node_calls.fetch_add(1, Ordering::Relaxed);
133 }
134
135 #[inline]
136 pub(crate) fn inc_cache_hit_direct(&self) {
137 self.cache_hits_direct.fetch_add(1, Ordering::Relaxed);
138 }
139
140 #[inline]
141 pub(crate) fn inc_cache_hit_boundary(&self) {
142 self.cache_hits_boundary.fetch_add(1, Ordering::Relaxed);
143 }
144
145 #[inline]
146 pub(crate) fn inc_cache_miss_no_entry(&self) {
147 self.cache_miss_no_entry.fetch_add(1, Ordering::Relaxed);
148 }
149
150 #[inline]
151 pub(crate) fn inc_cache_miss_constraint(&self) {
152 self.cache_miss_constraint.fetch_add(1, Ordering::Relaxed);
153 }
154
155 #[inline]
156 pub(crate) fn inc_cache_miss_dirty_self(&self) {
157 self.cache_miss_dirty_self.fetch_add(1, Ordering::Relaxed);
158 }
159
160 #[inline]
161 pub(crate) fn inc_cache_miss_child_size(&self) {
162 self.cache_miss_child_size.fetch_add(1, Ordering::Relaxed);
163 }
164
165 #[inline]
166 pub(crate) fn inc_cache_store_count(&self) {
167 self.cache_store_count.fetch_add(1, Ordering::Relaxed);
168 }
169
170 #[inline]
171 pub(crate) fn inc_cache_drop_non_cacheable_count(&self) {
172 self.cache_drop_non_cacheable_count
173 .fetch_add(1, Ordering::Relaxed);
174 }
175
176 fn snapshot(
177 &self,
178 dirty_nodes_param: u64,
179 dirty_nodes_structural: u64,
180 dirty_nodes_with_ancestors: u64,
181 dirty_expand_ns: u64,
182 ) -> LayoutFrameDiagnostics {
183 let cache_hits_direct = self.cache_hits_direct.load(Ordering::Relaxed);
184 let cache_hits_boundary = self.cache_hits_boundary.load(Ordering::Relaxed);
185 LayoutFrameDiagnostics {
186 dirty_nodes_param,
187 dirty_nodes_structural,
188 dirty_nodes_with_ancestors,
189 dirty_expand_ns,
190 measure_node_calls: self.measure_node_calls.load(Ordering::Relaxed),
191 cache_hits_direct,
192 cache_hits_boundary,
193 cache_miss_no_entry: self.cache_miss_no_entry.load(Ordering::Relaxed),
194 cache_miss_constraint: self.cache_miss_constraint.load(Ordering::Relaxed),
195 cache_miss_dirty_self: self.cache_miss_dirty_self.load(Ordering::Relaxed),
196 cache_miss_child_size: self.cache_miss_child_size.load(Ordering::Relaxed),
197 cache_store_count: self.cache_store_count.load(Ordering::Relaxed),
198 cache_drop_non_cacheable_count: self
199 .cache_drop_non_cacheable_count
200 .load(Ordering::Relaxed),
201 }
202 }
203}
204
205pub(crate) struct ComputeParams<'a> {
207 pub screen_size: PxSize,
208 pub cursor_position: Option<PxPosition>,
209 pub pointer_changes: Vec<PointerChange>,
210 pub keyboard_events: Vec<winit::event::KeyEvent>,
211 pub ime_events: Vec<winit::event::Ime>,
212 pub retry_focus_move: Option<FocusDirection>,
213 pub retry_focus_reveal: bool,
214 pub modifiers: winit::keyboard::ModifiersState,
215 pub layout_dirty_nodes: &'a LayoutDirtyNodes,
216}
217
218#[derive(Debug)]
219pub(crate) enum ComputeMode<'a> {
220 Full {
221 compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
222 gpu: &'a wgpu::Device,
223 },
224 #[cfg(feature = "testing")]
225 LayoutOnly,
226}
227
228pub struct ComponentTree {
230 tree: indextree::Arena<ComponentNode>,
232 metadatas: ComponentNodeMetaDatas,
234 node_queue: Vec<indextree::NodeId>,
236 replay_reuse_candidates: HashMap<u64, indextree::NodeId>,
238 active_pointer_paths: HashMap<u64, Vec<u64>>,
241 focus_owner: FocusOwner,
243}
244
245#[derive(Clone, PartialEq)]
246pub(crate) enum ReplayReplaceError {
247 NodeNotFound,
248 RootNodeNotReplaceable,
249 ReplayDidNotCreateRoot,
250}
251
252#[derive(Default)]
253pub(crate) struct ReplayReplaceResult {
254 pub removed_instance_keys: HashSet<u64>,
255 pub removed_instance_logic_ids: HashSet<u64>,
256 pub inserted_instance_keys: HashSet<u64>,
257 pub inserted_instance_logic_ids: HashSet<u64>,
258 pub reused_instance_logic_ids: HashSet<u64>,
259}
260
261pub(crate) struct ReplayReplaceContext {
262 parent_id: indextree::NodeId,
263 next_sibling: Option<indextree::NodeId>,
264 existing_children: HashSet<indextree::NodeId>,
265 previous_queue: Vec<indextree::NodeId>,
266 detached_root_id: indextree::NodeId,
267 detached_node_ids: HashSet<indextree::NodeId>,
268 removed_instance_keys: HashSet<u64>,
269 removed_instance_logic_ids: HashSet<u64>,
270}
271
272impl Default for ComponentTree {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278impl ComponentTree {
279 pub fn new() -> Self {
281 let tree = indextree::Arena::new();
282 let node_queue = Vec::new();
283 let metadatas = ComponentNodeMetaDatas::with_hasher(FxBuildHasher);
284 Self {
285 tree,
286 node_queue,
287 metadatas,
288 replay_reuse_candidates: HashMap::default(),
289 active_pointer_paths: HashMap::default(),
290 focus_owner: FocusOwner::new(),
291 }
292 }
293
294 pub fn clear(&mut self) {
296 self.tree.clear();
297 self.metadatas.clear();
298 self.node_queue.clear();
299 self.replay_reuse_candidates.clear();
300 self.active_pointer_paths.clear();
301 }
302
303 pub fn reset(&mut self) {
305 self.clear();
306 self.focus_owner.reset();
307 }
308
309 pub(crate) fn get(&self, node_id: indextree::NodeId) -> Option<&ComponentNode> {
311 self.tree
312 .get(node_id)
313 .filter(|node| !node.is_removed())
314 .map(|node| node.get())
315 }
316
317 pub(crate) fn get_mut(&mut self, node_id: indextree::NodeId) -> Option<&mut ComponentNode> {
319 self.tree
320 .get_mut(node_id)
321 .filter(|node| !node.is_removed())
322 .map(|node| node.get_mut())
323 }
324
325 pub(crate) fn find_node_id_by_instance_key(
327 &self,
328 instance_key: u64,
329 ) -> Option<indextree::NodeId> {
330 let root = self
331 .tree
332 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))?;
333 for edge in root.traverse(&self.tree) {
334 let indextree::NodeEdge::Start(node_id) = edge else {
335 continue;
336 };
337 let Some(node) = self.tree.get(node_id) else {
338 continue;
339 };
340 if node.get().instance_key == instance_key {
341 return Some(node_id);
342 }
343 }
344 None
345 }
346
347 pub(crate) fn live_instance_keys(&self) -> HashSet<u64> {
348 let Some(root) = self
349 .tree
350 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))
351 else {
352 return HashSet::default();
353 };
354
355 let mut instance_keys = HashSet::default();
356 for edge in root.traverse(&self.tree) {
357 let indextree::NodeEdge::Start(node_id) = edge else {
358 continue;
359 };
360 let Some(node) = self.tree.get(node_id) else {
361 continue;
362 };
363 instance_keys.insert(node.get().instance_key);
364 }
365 instance_keys
366 }
367
368 pub(crate) fn begin_replace_subtree_by_instance_key(
369 &mut self,
370 instance_key: u64,
371 ) -> Result<ReplayReplaceContext, ReplayReplaceError> {
372 self.replay_reuse_candidates.clear();
373 let Some(target_node_id) = self.find_node_id_by_instance_key(instance_key) else {
374 return Err(ReplayReplaceError::NodeNotFound);
375 };
376
377 let Some(parent_id) = self.tree.get(target_node_id).and_then(|n| n.parent()) else {
378 return Err(ReplayReplaceError::RootNodeNotReplaceable);
379 };
380
381 let mut next_sibling = None;
382 let mut seen_target = false;
383 for child in parent_id.children(&self.tree) {
384 if seen_target {
385 next_sibling = Some(child);
386 break;
387 }
388 if child == target_node_id {
389 seen_target = true;
390 }
391 }
392
393 let removed_node_ids: Vec<_> = target_node_id
394 .traverse(&self.tree)
395 .filter_map(|edge| match edge {
396 indextree::NodeEdge::Start(id) => Some(id),
397 indextree::NodeEdge::End(_) => None,
398 })
399 .collect();
400 let detached_node_ids = removed_node_ids.iter().copied().collect::<HashSet<_>>();
401 let mut removed_instance_keys = HashSet::default();
402 let mut removed_instance_logic_ids = HashSet::default();
403 for id in &removed_node_ids {
404 if let Some(node) = self.tree.get(*id) {
405 removed_instance_keys.insert(node.get().instance_key);
406 removed_instance_logic_ids.insert(node.get().instance_logic_id);
407 self.replay_reuse_candidates
408 .insert(node.get().instance_key, *id);
409 }
410 }
411 target_node_id.detach(&mut self.tree);
412
413 let existing_children = parent_id.children(&self.tree).collect();
414
415 let previous_queue = std::mem::replace(&mut self.node_queue, vec![parent_id]);
416 Ok(ReplayReplaceContext {
417 parent_id,
418 next_sibling,
419 existing_children,
420 previous_queue,
421 detached_root_id: target_node_id,
422 detached_node_ids,
423 removed_instance_keys,
424 removed_instance_logic_ids,
425 })
426 }
427
428 pub(crate) fn finish_replace_subtree(
429 &mut self,
430 context: ReplayReplaceContext,
431 ) -> Result<ReplayReplaceResult, ReplayReplaceError> {
432 let ReplayReplaceContext {
433 parent_id,
434 next_sibling,
435 existing_children,
436 previous_queue,
437 detached_root_id,
438 detached_node_ids,
439 removed_instance_keys,
440 removed_instance_logic_ids,
441 } = context;
442
443 let mut inserted_root_ids = parent_id
444 .children(&self.tree)
445 .filter(|child_id| !existing_children.contains(child_id))
446 .collect::<Vec<_>>();
447 if inserted_root_ids.is_empty() {
448 self.node_queue = previous_queue;
449 return Err(ReplayReplaceError::ReplayDidNotCreateRoot);
450 }
451
452 if let Some(next_sibling) = next_sibling {
453 for inserted_root_id in &inserted_root_ids {
454 if *inserted_root_id != next_sibling {
455 next_sibling.insert_before(*inserted_root_id, &mut self.tree);
456 }
457 }
458 inserted_root_ids = parent_id
459 .children(&self.tree)
460 .filter(|child_id| !existing_children.contains(child_id))
461 .collect::<Vec<_>>();
462 }
463
464 let detached_root_reused = inserted_root_ids.contains(&detached_root_id);
465
466 let mut inserted_instance_keys = HashSet::default();
467 let mut inserted_instance_logic_ids = HashSet::default();
468 let mut reused_instance_logic_ids = HashSet::default();
469 for inserted_root_id in &inserted_root_ids {
470 for edge in inserted_root_id.traverse(&self.tree) {
471 let indextree::NodeEdge::Start(id) = edge else {
472 continue;
473 };
474 if let Some(node) = self.tree.get(id) {
475 inserted_instance_keys.insert(node.get().instance_key);
476 inserted_instance_logic_ids.insert(node.get().instance_logic_id);
477 if detached_node_ids.contains(&id) {
478 reused_instance_logic_ids.insert(node.get().instance_logic_id);
479 }
480 }
481 }
482 }
483
484 let removed_instance_keys = removed_instance_keys
485 .difference(&inserted_instance_keys)
486 .copied()
487 .collect::<HashSet<_>>();
488 let removed_instance_logic_ids = removed_instance_logic_ids
489 .difference(&inserted_instance_logic_ids)
490 .copied()
491 .collect::<HashSet<_>>();
492 if !detached_root_reused {
493 let detached_node_ids = detached_root_id
494 .traverse(&self.tree)
495 .filter_map(|edge| match edge {
496 indextree::NodeEdge::Start(id) => Some(id),
497 indextree::NodeEdge::End(_) => None,
498 })
499 .collect::<Vec<_>>();
500 for node_id in &detached_node_ids {
501 self.metadatas.remove(node_id);
502 }
503 detached_root_id.remove_subtree(&mut self.tree);
504 }
505 self.replay_reuse_candidates.clear();
506
507 self.node_queue = previous_queue;
508 Ok(ReplayReplaceResult {
509 removed_instance_keys,
510 removed_instance_logic_ids,
511 inserted_instance_keys,
512 inserted_instance_logic_ids,
513 reused_instance_logic_ids,
514 })
515 }
516
517 pub(crate) fn current_node(&self) -> Option<&ComponentNode> {
519 self.node_queue
520 .last()
521 .and_then(|node_id| self.get(*node_id))
522 }
523
524 pub(crate) fn current_node_mut(&mut self) -> Option<&mut ComponentNode> {
526 let node_id = self.node_queue.last()?;
527 self.get_mut(*node_id)
528 }
529
530 pub(crate) fn try_reuse_current_subtree(
531 &mut self,
532 instance_key: u64,
533 instance_logic_id: u64,
534 ) -> bool {
535 let Some(&candidate_node_id) = self.replay_reuse_candidates.get(&instance_key) else {
536 return false;
537 };
538 let Some(candidate_instance_logic_id) = self
539 .tree
540 .get(candidate_node_id)
541 .map(|node| node.get().instance_logic_id)
542 else {
543 self.replay_reuse_candidates.remove(&instance_key);
544 return false;
545 };
546 if candidate_instance_logic_id != instance_logic_id {
547 return false;
548 }
549
550 let Some(current_node_id) = self.node_queue.last().copied() else {
551 return false;
552 };
553 if current_node_id == candidate_node_id {
554 self.replay_reuse_candidates.remove(&instance_key);
555 return true;
556 }
557
558 candidate_node_id.detach(&mut self.tree);
559 current_node_id.insert_before(candidate_node_id, &mut self.tree);
560 self.metadatas.remove(¤t_node_id);
561 current_node_id.remove_subtree(&mut self.tree);
562 if let Some(current_node) = self.node_queue.last_mut() {
563 *current_node = candidate_node_id;
564 }
565 self.replay_reuse_candidates.remove(&instance_key);
566 true
567 }
568
569 pub(crate) fn add_node(&mut self, node_component: ComponentNode) -> indextree::NodeId {
574 let new_node_id = self.tree.new_node(node_component);
575 if let Some(current_node_id) = self.node_queue.last_mut() {
576 current_node_id.append(new_node_id, &mut self.tree);
577 }
578 let metadata = ComponentNodeMetaData::none();
579 self.metadatas.insert(new_node_id, metadata);
580 self.node_queue.push(new_node_id);
581 new_node_id
582 }
583
584 pub(crate) fn pop_node(&mut self) {
586 self.node_queue.pop();
587 }
588
589 pub(crate) fn tree(&self) -> &indextree::Arena<ComponentNode> {
594 &self.tree
595 }
596
597 pub(crate) fn metadatas(&self) -> &ComponentNodeMetaDatas {
602 &self.metadatas
603 }
604
605 pub(crate) fn focus_owner(&self) -> &FocusOwner {
606 &self.focus_owner
607 }
608
609 pub(crate) fn focus_owner_mut(&mut self) -> &mut FocusOwner {
610 &mut self.focus_owner
611 }
612
613 pub(crate) fn accessibility_dispatch_context(
614 &mut self,
615 ) -> (&ComponentNodeTree, &ComponentNodeMetaDatas, &mut FocusOwner) {
616 (&self.tree, &self.metadatas, &mut self.focus_owner)
617 }
618
619 pub(crate) fn take_pending_focus_callback_invocations(
620 &mut self,
621 ) -> Vec<PendingFocusCallbackInvocation> {
622 let notifications = self.focus_owner.take_pending_notifications();
623 let mut invocations = Vec::new();
624
625 for notification in notifications {
626 let Some(node_id) = self
627 .focus_owner
628 .component_node_id_of(notification.handle_id)
629 else {
630 continue;
631 };
632 let Some(node_ref) = self.tree.get(node_id) else {
633 continue;
634 };
635 let node = node_ref.get();
636
637 if notification.changed
638 && let Some(handler) = &node.focus_changed_handler
639 {
640 invocations.push(PendingFocusCallbackInvocation::new(
641 *handler,
642 notification.state,
643 ));
644 }
645 if let Some(handler) = &node.focus_event_handler {
646 invocations.push(PendingFocusCallbackInvocation::new(
647 *handler,
648 notification.state,
649 ));
650 }
651 }
652
653 invocations
654 }
655
656 #[cfg(feature = "profiling")]
658 pub fn profiler_nodes(&self) -> Vec<NodeMeta> {
659 let Some(root_node) = self
660 .tree
661 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))
662 else {
663 return Vec::new();
664 };
665
666 let mut stack = vec![root_node];
667 let mut nodes = Vec::new();
668 while let Some(node_id) = stack.pop() {
669 if let Some(node) = self.tree.get(node_id) {
670 let parent = node.parent().map(|p| p.to_string());
671 let fn_name = node.get().fn_name.clone();
672 let metadata = self.metadatas.get(&node_id);
673 let abs_pos = metadata
674 .as_ref()
675 .and_then(|m| m.abs_position)
676 .map(|p| (p.x.0, p.y.0));
677 let size = metadata
678 .as_ref()
679 .and_then(|m| m.computed_data)
680 .map(|d| (d.width.0, d.height.0));
681 let layout_cache_hit = metadata.as_ref().map(|m| m.layout_cache_hit);
682
683 nodes.push(NodeMeta {
684 node_id: node_id.to_string(),
685 parent,
686 fn_name: Some(fn_name.clone()),
687 abs_pos,
688 size,
689 layout_cache_hit,
690 });
691 }
692 stack.extend(node_id.children(&self.tree));
693 }
694
695 nodes
696 }
697
698 #[tracing::instrument(level = "debug", skip(self, params))]
709 pub(crate) fn compute(
710 &mut self,
711 params: ComputeParams<'_>,
712 mode: ComputeMode<'_>,
713 ) -> (
714 RenderGraph,
715 WindowRequests,
716 LayoutFrameDiagnostics,
717 std::time::Duration,
718 Option<FocusDirection>,
719 bool,
720 ) {
721 let ComputeParams {
722 screen_size,
723 mut cursor_position,
724 mut pointer_changes,
725 mut keyboard_events,
726 mut ime_events,
727 retry_focus_move,
728 retry_focus_reveal,
729 modifiers,
730 layout_dirty_nodes,
731 } = params;
732 let Some(root_node) = self
733 .tree
734 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))
735 else {
736 return (
737 RenderGraph::default(),
738 WindowRequests::default(),
739 LayoutFrameDiagnostics::default(),
740 std::time::Duration::ZERO,
741 None,
742 false,
743 );
744 };
745 let screen_constraint = Constraint::new(
746 DimensionValue::Fixed(screen_size.width),
747 DimensionValue::Fixed(screen_size.height),
748 );
749 let current_children_by_node = collect_children_by_instance_key(root_node, &self.tree);
750 let StructureReconcileResult {
751 changed_nodes: structural_dirty_nodes,
752 removed_nodes,
753 } = crate::runtime::reconcile_layout_structure(¤t_children_by_node);
754 remove_layout_snapshots(&removed_nodes);
755
756 let mut dirty_nodes_self = layout_dirty_nodes.measure_self_nodes.clone();
757 dirty_nodes_self.extend(layout_dirty_nodes.placement_self_nodes.iter().copied());
758 let dirty_nodes_param = dirty_nodes_self.len() as u64;
759 let dirty_nodes_structural = structural_dirty_nodes.len() as u64;
760 let dirty_prepare_start = Instant::now();
761 dirty_nodes_self.extend(structural_dirty_nodes.iter().copied());
762 let dirty_nodes_effective =
763 expand_dirty_nodes_with_ancestors(root_node, &self.tree, &dirty_nodes_self);
764 let dirty_expand_ns = dirty_prepare_start.elapsed().as_nanos() as u64;
765 let diagnostics = LayoutDiagnosticsCollector::default();
766
767 self.focus_owner
768 .sync_from_component_tree(root_node, &self.tree);
769 self.focus_owner.commit_pending();
770
771 let diagnostics_snapshot = with_layout_snapshot_entries(|snapshots| {
772 let layout_ctx = LayoutContext {
773 snapshots,
774 measure_self_nodes: &layout_dirty_nodes.measure_self_nodes,
775 placement_self_nodes: &layout_dirty_nodes.placement_self_nodes,
776 dirty_effective_nodes: &dirty_nodes_effective,
777 diagnostics: &diagnostics,
778 };
779
780 let measure_timer = Instant::now();
781 debug!("Start measuring the component tree...");
782
783 match measure_node(
784 root_node,
785 &screen_constraint,
786 &self.tree,
787 &self.metadatas,
788 Some(&layout_ctx),
789 ) {
790 Ok(_root_computed_data) => {
791 debug!("Component tree measured in {:?}", measure_timer.elapsed());
792 }
793 Err(e) => {
794 panic!(
795 "Root node ({root_node:?}) measurement failed: {e:?}. Aborting draw command computation."
796 );
797 }
798 }
799
800 diagnostics.snapshot(
801 dirty_nodes_param,
802 dirty_nodes_structural,
803 dirty_nodes_effective.len() as u64,
804 dirty_expand_ns,
805 )
806 });
807
808 let (compute_resource_manager, gpu) = match mode {
809 ComputeMode::Full {
810 compute_resource_manager,
811 gpu,
812 } => (compute_resource_manager, gpu),
813 #[cfg(feature = "testing")]
814 ComputeMode::LayoutOnly => {
815 populate_layout_metadata(root_node, &self.tree, &self.metadatas);
816 return (
817 RenderGraph::default(),
818 WindowRequests::default(),
819 diagnostics_snapshot,
820 std::time::Duration::ZERO,
821 None,
822 false,
823 );
824 }
825 };
826
827 let record_timer = Instant::now();
828 record_layout_commands(
829 root_node,
830 &self.tree,
831 &self.metadatas,
832 compute_resource_manager.clone(),
833 gpu,
834 );
835 let record_cost = record_timer.elapsed();
836 populate_layout_metadata(root_node, &self.tree, &self.metadatas);
837
838 let compute_draw_timer = Instant::now();
839 debug!("Start computing render graph...");
840 let graph = build_render_graph(root_node, &self.tree, &self.metadatas, screen_size);
841 self.focus_owner
842 .sync_layout_from_component_tree(root_node, &self.tree, &self.metadatas);
843 debug!(
844 "Render graph built in {:?}, total ops: {}",
845 compute_draw_timer.elapsed(),
846 graph.ops().len()
847 );
848
849 let input_dispatch_timer = Instant::now();
850 let mut window_requests = WindowRequests::default();
851 debug!("Start executing typed input dispatch...");
852
853 let node_ids_preorder: Vec<_> = root_node
854 .traverse(&self.tree)
855 .filter_map(|edge| match edge {
856 indextree::NodeEdge::Start(id) => Some(id),
857 indextree::NodeEdge::End(_) => None,
858 })
859 .collect();
860 let node_ids_postorder: Vec<_> = root_node
861 .reverse_traverse(&self.tree)
862 .filter_map(|edge| match edge {
863 indextree::NodeEdge::Start(id) => Some(id),
864 indextree::NodeEdge::End(_) => None,
865 })
866 .collect();
867 let pointer_change_paths = build_pointer_change_paths(
868 root_node,
869 &self.tree,
870 &self.metadatas,
871 &pointer_changes,
872 cursor_position,
873 &mut self.active_pointer_paths,
874 );
875 window_requests.cursor_icon =
876 resolve_hover_cursor_icon(root_node, &self.tree, &self.metadatas, cursor_position)
877 .unwrap_or_default();
878
879 for node_id in node_ids_preorder.iter().copied() {
880 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
881 continue;
882 };
883 for modifier in node.modifier.pointer_preview_input_nodes() {
884 let mut dispatch_ctx = PointerInputDispatchContext {
885 tree: &self.tree,
886 metadatas: &self.metadatas,
887 cursor_position: &mut cursor_position,
888 pointer_changes: pointer_changes.as_mut_slice(),
889 pointer_change_paths: &pointer_change_paths,
890 modifiers,
891 window_requests: &mut window_requests,
892 focus_owner: &mut self.focus_owner,
893 };
894 run_pointer_modifier_for_node(
895 &mut dispatch_ctx,
896 node_id,
897 PointerEventPass::Initial,
898 modifier.as_ref(),
899 );
900 }
901 let handlers = &node.pointer_preview_handlers;
902 for handler in handlers {
903 let mut dispatch_ctx = PointerInputDispatchContext {
904 tree: &self.tree,
905 metadatas: &self.metadatas,
906 cursor_position: &mut cursor_position,
907 pointer_changes: pointer_changes.as_mut_slice(),
908 pointer_change_paths: &pointer_change_paths,
909 modifiers,
910 window_requests: &mut window_requests,
911 focus_owner: &mut self.focus_owner,
912 };
913 run_pointer_handler_for_node(
914 &mut dispatch_ctx,
915 node_id,
916 PointerEventPass::Initial,
917 handler.as_ref(),
918 );
919 }
920 }
921
922 for node_id in node_ids_postorder.iter().copied() {
923 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
924 continue;
925 };
926 for modifier in node.modifier.pointer_input_nodes() {
927 let mut dispatch_ctx = PointerInputDispatchContext {
928 tree: &self.tree,
929 metadatas: &self.metadatas,
930 cursor_position: &mut cursor_position,
931 pointer_changes: pointer_changes.as_mut_slice(),
932 pointer_change_paths: &pointer_change_paths,
933 modifiers,
934 window_requests: &mut window_requests,
935 focus_owner: &mut self.focus_owner,
936 };
937 run_pointer_modifier_for_node(
938 &mut dispatch_ctx,
939 node_id,
940 PointerEventPass::Main,
941 modifier.as_ref(),
942 );
943 }
944 let handlers = &node.pointer_handlers;
945 for handler in handlers {
946 let mut dispatch_ctx = PointerInputDispatchContext {
947 tree: &self.tree,
948 metadatas: &self.metadatas,
949 cursor_position: &mut cursor_position,
950 pointer_changes: pointer_changes.as_mut_slice(),
951 pointer_change_paths: &pointer_change_paths,
952 modifiers,
953 window_requests: &mut window_requests,
954 focus_owner: &mut self.focus_owner,
955 };
956 run_pointer_handler_for_node(
957 &mut dispatch_ctx,
958 node_id,
959 PointerEventPass::Main,
960 handler.as_ref(),
961 );
962 }
963 }
964
965 for node_id in node_ids_preorder.iter().copied() {
966 let Some(node) = self.tree.get(node_id).map(|n| n.get()) else {
967 continue;
968 };
969 for modifier in node.modifier.pointer_final_input_nodes() {
970 let mut dispatch_ctx = PointerInputDispatchContext {
971 tree: &self.tree,
972 metadatas: &self.metadatas,
973 cursor_position: &mut cursor_position,
974 pointer_changes: pointer_changes.as_mut_slice(),
975 pointer_change_paths: &pointer_change_paths,
976 modifiers,
977 window_requests: &mut window_requests,
978 focus_owner: &mut self.focus_owner,
979 };
980 run_pointer_modifier_for_node(
981 &mut dispatch_ctx,
982 node_id,
983 PointerEventPass::Final,
984 modifier.as_ref(),
985 );
986 }
987 let handlers = &node.pointer_final_handlers;
988 for handler in handlers {
989 let mut dispatch_ctx = PointerInputDispatchContext {
990 tree: &self.tree,
991 metadatas: &self.metadatas,
992 cursor_position: &mut cursor_position,
993 pointer_changes: pointer_changes.as_mut_slice(),
994 pointer_change_paths: &pointer_change_paths,
995 modifiers,
996 window_requests: &mut window_requests,
997 focus_owner: &mut self.focus_owner,
998 };
999 run_pointer_handler_for_node(
1000 &mut dispatch_ctx,
1001 node_id,
1002 PointerEventPass::Final,
1003 handler.as_ref(),
1004 );
1005 }
1006 }
1007
1008 self.focus_owner.commit_pending();
1009 let pending_focus_move_retry = retry_focus_move.and_then(|direction| {
1010 match try_dispatch_focus_move_request(&self.tree, direction, &mut self.focus_owner) {
1011 FocusMoveRequestResult::Retry(direction) => Some(direction),
1012 FocusMoveRequestResult::Moved | FocusMoveRequestResult::NotHandled => None,
1013 }
1014 });
1015 let focus_chain_node_ids =
1016 collect_focus_chain_node_ids(root_node, &self.tree, &self.focus_owner);
1017
1018 let pending_focus_move_retry = if pending_focus_move_retry.is_none() {
1019 let mut keyboard_dispatch_ctx = KeyboardInputDispatchContext {
1020 tree: &self.tree,
1021 metadatas: &self.metadatas,
1022 keyboard_events: &mut keyboard_events,
1023 modifiers,
1024 window_requests: &mut window_requests,
1025 focus_owner: &mut self.focus_owner,
1026 };
1027
1028 for node_id in focus_chain_node_ids.iter().copied() {
1029 let Some(node) = keyboard_dispatch_ctx.tree.get(node_id).map(|n| n.get()) else {
1030 continue;
1031 };
1032 for modifier in node.modifier.keyboard_preview_input_nodes() {
1033 run_keyboard_modifier_for_node(
1034 &mut keyboard_dispatch_ctx,
1035 node_id,
1036 modifier.as_ref(),
1037 );
1038 }
1039 for handler in &node.keyboard_preview_handlers {
1040 run_keyboard_handler_for_node(
1041 &mut keyboard_dispatch_ctx,
1042 node_id,
1043 handler.as_ref(),
1044 );
1045 }
1046 for modifier in node.modifier.ime_preview_input_nodes() {
1047 run_ime_modifier_for_node(
1048 keyboard_dispatch_ctx.tree,
1049 keyboard_dispatch_ctx.metadatas,
1050 node_id,
1051 modifier.as_ref(),
1052 &mut ime_events,
1053 keyboard_dispatch_ctx.window_requests,
1054 keyboard_dispatch_ctx.focus_owner,
1055 );
1056 }
1057 for handler in &node.ime_preview_handlers {
1058 run_ime_handler_for_node(
1059 keyboard_dispatch_ctx.tree,
1060 keyboard_dispatch_ctx.metadatas,
1061 node_id,
1062 handler.as_ref(),
1063 &mut ime_events,
1064 keyboard_dispatch_ctx.window_requests,
1065 keyboard_dispatch_ctx.focus_owner,
1066 );
1067 }
1068 }
1069
1070 for node_id in focus_chain_node_ids.iter().rev().copied() {
1071 let Some(node) = keyboard_dispatch_ctx.tree.get(node_id).map(|n| n.get()) else {
1072 continue;
1073 };
1074 for modifier in node.modifier.keyboard_input_nodes() {
1075 run_keyboard_modifier_for_node(
1076 &mut keyboard_dispatch_ctx,
1077 node_id,
1078 modifier.as_ref(),
1079 );
1080 }
1081 for handler in &node.keyboard_handlers {
1082 run_keyboard_handler_for_node(
1083 &mut keyboard_dispatch_ctx,
1084 node_id,
1085 handler.as_ref(),
1086 );
1087 }
1088 for modifier in node.modifier.ime_input_nodes() {
1089 run_ime_modifier_for_node(
1090 keyboard_dispatch_ctx.tree,
1091 keyboard_dispatch_ctx.metadatas,
1092 node_id,
1093 modifier.as_ref(),
1094 &mut ime_events,
1095 keyboard_dispatch_ctx.window_requests,
1096 keyboard_dispatch_ctx.focus_owner,
1097 );
1098 }
1099 for handler in &node.ime_handlers {
1100 run_ime_handler_for_node(
1101 keyboard_dispatch_ctx.tree,
1102 keyboard_dispatch_ctx.metadatas,
1103 node_id,
1104 handler.as_ref(),
1105 &mut ime_events,
1106 keyboard_dispatch_ctx.window_requests,
1107 keyboard_dispatch_ctx.focus_owner,
1108 );
1109 }
1110 }
1111
1112 dispatch_default_focus_keyboard_navigation(
1113 keyboard_dispatch_ctx.tree,
1114 keyboard_dispatch_ctx.keyboard_events,
1115 keyboard_dispatch_ctx.modifiers,
1116 keyboard_dispatch_ctx.focus_owner,
1117 )
1118 } else {
1119 pending_focus_move_retry
1120 };
1121 let pending_focus_reveal_retry = if pending_focus_move_retry.is_none() {
1122 dispatch_pending_focus_reveal_request(
1123 &self.tree,
1124 &self.metadatas,
1125 &mut self.focus_owner,
1126 retry_focus_reveal,
1127 )
1128 } else {
1129 false
1130 };
1131
1132 debug!(
1133 "Typed input dispatch executed in {:?}",
1134 input_dispatch_timer.elapsed()
1135 );
1136 (
1137 graph,
1138 window_requests,
1139 diagnostics_snapshot,
1140 record_cost,
1141 pending_focus_move_retry,
1142 pending_focus_reveal_retry,
1143 )
1144 }
1145}
1146
1147struct NodeInputContext {
1148 abs_pos: PxPosition,
1149 event_clip_rect: Option<PxRect>,
1150 node_computed_data: ComputedData,
1151 instance_logic_id: u64,
1152 instance_key: u64,
1153 fn_name: String,
1154 parent_id: Option<indextree::NodeId>,
1155}
1156
1157fn resolve_node_input_context(
1158 tree: &ComponentNodeTree,
1159 metadatas: &ComponentNodeMetaDatas,
1160 node_id: indextree::NodeId,
1161) -> Option<NodeInputContext> {
1162 let Some(metadata) = metadatas.get(&node_id) else {
1163 warn!("Input metadata missing for node {node_id:?}; skipping input handling");
1164 return None;
1165 };
1166 let Some(abs_pos) = metadata.abs_position else {
1167 warn!("Absolute position missing for node {node_id:?}; skipping input handling");
1168 return None;
1169 };
1170 let event_clip_rect = metadata.event_clip_rect;
1171 let Some(node_computed_data) = metadata.computed_data else {
1172 warn!(
1173 "Computed data not found for node {:?} during input dispatch.",
1174 node_id
1175 );
1176 return None;
1177 };
1178 drop(metadata);
1179
1180 let Some(node_ref) = tree.get(node_id) else {
1181 warn!("Node not found for node {node_id:?}; skipping input handling");
1182 return None;
1183 };
1184 let node = node_ref.get();
1185 let instance_logic_id = node.instance_logic_id;
1186 let instance_key = node.instance_key;
1187 let fn_name = node.fn_name.as_str().to_owned();
1188 let parent_id = node_ref.parent();
1189
1190 Some(NodeInputContext {
1191 abs_pos,
1192 event_clip_rect,
1193 node_computed_data,
1194 instance_logic_id,
1195 instance_key,
1196 fn_name,
1197 parent_id,
1198 })
1199}
1200
1201fn attach_ime_position_if_needed(window_requests: &mut WindowRequests, abs_pos: PxPosition) {
1202 if let Some(ref mut ime_request) = window_requests.ime_request
1203 && ime_request.position.is_none()
1204 {
1205 ime_request.position = Some(abs_pos + ime_request.local_position);
1206 }
1207}
1208
1209fn hit_path_node_ids(
1210 root_node: indextree::NodeId,
1211 tree: &ComponentNodeTree,
1212 metadatas: &ComponentNodeMetaDatas,
1213 position: Option<PxPosition>,
1214) -> Vec<indextree::NodeId> {
1215 fn collect_hit_path(
1216 node_id: indextree::NodeId,
1217 tree: &ComponentNodeTree,
1218 metadatas: &ComponentNodeMetaDatas,
1219 position: PxPosition,
1220 ) -> Option<Vec<indextree::NodeId>> {
1221 let metadata = metadatas.get(&node_id)?;
1222 let abs_pos = metadata.abs_position?;
1223 let size = metadata.computed_data?;
1224 if size.width.0 <= 0 || size.height.0 <= 0 {
1225 return None;
1226 }
1227 if let Some(clip_rect) = metadata.event_clip_rect
1228 && !clip_rect.contains(position)
1229 {
1230 return None;
1231 }
1232 let bounds = PxRect::from_position_size(abs_pos, PxSize::new(size.width, size.height));
1233 let bounds_contains = bounds.contains(position);
1234 let node_handles_hover = tree.get(node_id).is_some_and(|node| {
1235 let node = node.get();
1236 !node.pointer_preview_handlers.is_empty()
1237 || !node.pointer_handlers.is_empty()
1238 || !node.pointer_final_handlers.is_empty()
1239 || node.modifier.has_pointer_input_nodes()
1240 || node.modifier.has_cursor_icon()
1241 });
1242
1243 let children: Vec<_> = node_id.children(tree).collect();
1244 for child_id in children.into_iter().rev() {
1245 if let Some(mut child_path) = collect_hit_path(child_id, tree, metadatas, position) {
1246 let mut path = Vec::with_capacity(child_path.len() + 1);
1247 path.push(node_id);
1248 path.append(&mut child_path);
1249 return Some(path);
1250 }
1251 }
1252
1253 (bounds_contains && node_handles_hover).then_some(vec![node_id])
1254 }
1255
1256 let Some(position) = position else {
1257 return Vec::new();
1258 };
1259 collect_hit_path(root_node, tree, metadatas, position).unwrap_or_default()
1260}
1261
1262fn resolve_hover_cursor_icon(
1263 root_node: indextree::NodeId,
1264 tree: &ComponentNodeTree,
1265 metadatas: &ComponentNodeMetaDatas,
1266 position: Option<PxPosition>,
1267) -> Option<winit::window::CursorIcon> {
1268 hit_path_node_ids(root_node, tree, metadatas, position)
1269 .into_iter()
1270 .rev()
1271 .find_map(|node_id| {
1272 tree.get(node_id)
1273 .and_then(|node| node.get().modifier.cursor_icon())
1274 })
1275}
1276
1277fn build_pointer_change_paths(
1278 root_node: indextree::NodeId,
1279 tree: &ComponentNodeTree,
1280 metadatas: &ComponentNodeMetaDatas,
1281 pointer_changes: &[PointerChange],
1282 cursor_position: Option<PxPosition>,
1283 active_pointer_paths: &mut HashMap<u64, Vec<u64>>,
1284) -> Vec<Vec<u64>> {
1285 let mut paths = Vec::with_capacity(pointer_changes.len());
1286 for change in pointer_changes {
1287 let debug_position = match &change.content {
1288 CursorEventContent::Moved(position) => Some(*position),
1289 _ => cursor_position,
1290 };
1291 let path = match &change.content {
1292 CursorEventContent::Pressed(_) => {
1293 let computed_path =
1294 hit_path_instance_keys(root_node, tree, metadatas, debug_position);
1295 if computed_path.is_empty() {
1296 active_pointer_paths.remove(&change.pointer_id);
1297 } else {
1298 active_pointer_paths.insert(change.pointer_id, computed_path.clone());
1299 }
1300 computed_path
1301 }
1302 CursorEventContent::Moved(position) => active_pointer_paths
1303 .get(&change.pointer_id)
1304 .cloned()
1305 .unwrap_or_else(|| {
1306 hit_path_instance_keys(root_node, tree, metadatas, Some(*position))
1307 }),
1308 CursorEventContent::Released(_) => {
1309 let computed = active_pointer_paths
1310 .get(&change.pointer_id)
1311 .cloned()
1312 .unwrap_or_else(|| {
1313 hit_path_instance_keys(root_node, tree, metadatas, debug_position)
1314 });
1315 active_pointer_paths.remove(&change.pointer_id);
1316 computed
1317 }
1318 CursorEventContent::Scroll(_) => active_pointer_paths
1319 .get(&change.pointer_id)
1320 .cloned()
1321 .unwrap_or_else(|| {
1322 hit_path_instance_keys(root_node, tree, metadatas, debug_position)
1323 }),
1324 };
1325 paths.push(path);
1326 }
1327 paths
1328}
1329
1330fn hit_path_instance_keys(
1331 root_node: indextree::NodeId,
1332 tree: &ComponentNodeTree,
1333 metadatas: &ComponentNodeMetaDatas,
1334 position: Option<PxPosition>,
1335) -> Vec<u64> {
1336 hit_path_node_ids(root_node, tree, metadatas, position)
1337 .into_iter()
1338 .filter_map(|node_id| tree.get(node_id).map(|node| node.get().instance_key))
1339 .collect()
1340}
1341
1342fn collect_focus_chain_node_ids(
1343 root_node: indextree::NodeId,
1344 tree: &ComponentNodeTree,
1345 focus_owner: &FocusOwner,
1346) -> Vec<indextree::NodeId> {
1347 fn live_node(
1348 tree: &ComponentNodeTree,
1349 node_id: indextree::NodeId,
1350 ) -> Option<&indextree::Node<ComponentNode>> {
1351 tree.get(node_id).filter(|node| !node.is_removed())
1352 }
1353
1354 let Some(focused_node_id) = focus_owner.active_component_node_id() else {
1355 return Vec::new();
1356 };
1357 if live_node(tree, focused_node_id).is_none() {
1358 return Vec::new();
1359 }
1360
1361 let mut chain = Vec::new();
1362 let mut current = Some(focused_node_id);
1363 while let Some(node_id) = current {
1364 chain.push(node_id);
1365 if node_id == root_node {
1366 break;
1367 }
1368 current = live_node(tree, node_id).and_then(|node| node.parent());
1369 }
1370 if chain.last().copied() != Some(root_node) {
1371 return Vec::new();
1372 }
1373 chain.reverse();
1374 chain
1375}
1376
1377struct PointerInputDispatchContext<'a> {
1378 tree: &'a ComponentNodeTree,
1379 metadatas: &'a ComponentNodeMetaDatas,
1380 cursor_position: &'a mut Option<PxPosition>,
1381 pointer_changes: &'a mut [PointerChange],
1382 pointer_change_paths: &'a [Vec<u64>],
1383 modifiers: winit::keyboard::ModifiersState,
1384 window_requests: &'a mut WindowRequests,
1385 focus_owner: &'a mut FocusOwner,
1386}
1387
1388fn run_pointer_handler_for_node(
1389 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
1390 node_id: indextree::NodeId,
1391 pass: PointerEventPass,
1392 pointer_handler: &PointerInputHandlerFn,
1393) {
1394 run_pointer_input_for_node(dispatch_ctx, node_id, pass, pointer_handler);
1395}
1396
1397fn run_pointer_modifier_for_node(
1398 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
1399 node_id: indextree::NodeId,
1400 pass: PointerEventPass,
1401 pointer_modifier: &dyn PointerInputModifierNode,
1402) {
1403 run_pointer_input_for_node(dispatch_ctx, node_id, pass, |input| {
1404 pointer_modifier.on_pointer_input(input)
1405 });
1406}
1407
1408fn run_pointer_input_for_node<F>(
1409 dispatch_ctx: &mut PointerInputDispatchContext<'_>,
1410 node_id: indextree::NodeId,
1411 pass: PointerEventPass,
1412 dispatch: F,
1413) where
1414 F: FnOnce(PointerInput<'_>),
1415{
1416 let Some(NodeInputContext {
1417 abs_pos,
1418 event_clip_rect,
1419 node_computed_data,
1420 instance_logic_id,
1421 instance_key,
1422 fn_name,
1423 parent_id,
1424 }) = resolve_node_input_context(dispatch_ctx.tree, dispatch_ctx.metadatas, node_id)
1425 else {
1426 return;
1427 };
1428 #[cfg(not(feature = "profiling"))]
1429 let _ = parent_id;
1430
1431 let mut cursor_position_ref = &mut *dispatch_ctx.cursor_position;
1432 let mut dummy_cursor_position = None;
1433 if let (Some(cursor_pos), Some(clip_rect)) = (*cursor_position_ref, event_clip_rect)
1434 && !clip_rect.contains(cursor_pos)
1435 {
1436 cursor_position_ref = &mut dummy_cursor_position;
1437 }
1438 let current_cursor_position = cursor_position_ref.map(|pos| pos - abs_pos);
1439 let mut selected_change_indices = Vec::new();
1440 let mut local_pointer_changes = Vec::new();
1441 for (index, change) in dispatch_ctx.pointer_changes.iter().enumerate() {
1442 if change.is_consumed() {
1443 continue;
1444 }
1445 let Some(path) = dispatch_ctx.pointer_change_paths.get(index) else {
1446 continue;
1447 };
1448 if path.contains(&instance_key) {
1449 selected_change_indices.push(index);
1450 local_pointer_changes.push(change.clone());
1451 }
1452 }
1453
1454 #[cfg(feature = "profiling")]
1455 let mut profiler_guard = Some(ProfilerScopeGuard::new(
1456 ProfilerPhase::Input,
1457 Some(node_id),
1458 parent_id,
1459 Some(fn_name.as_str()),
1460 ));
1461 let _node_ctx_guard =
1462 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name.as_str());
1463 let _instance_ctx_guard = push_current_component_instance_key(instance_key);
1464 let _phase_guard = push_phase(RuntimePhase::Input);
1465 let _focus_owner_guard = bind_focus_owner(dispatch_ctx.focus_owner);
1466 let input = PointerInput {
1467 pass,
1468 computed_data: node_computed_data,
1469 cursor_position_rel: current_cursor_position,
1470 cursor_position_abs: cursor_position_ref,
1471 pointer_changes: &mut local_pointer_changes,
1472 key_modifiers: dispatch_ctx.modifiers,
1473 ime_request: &mut dispatch_ctx.window_requests.ime_request,
1474 window_action: &mut dispatch_ctx.window_requests.window_action,
1475 };
1476 dispatch(input);
1477 for (local_change, &original_index) in local_pointer_changes
1478 .iter()
1479 .zip(selected_change_indices.iter())
1480 {
1481 if let Some(original) = dispatch_ctx.pointer_changes.get_mut(original_index) {
1482 *original = local_change.clone();
1483 }
1484 }
1485
1486 #[cfg(feature = "profiling")]
1487 {
1488 let abs_tuple = (abs_pos.x.0, abs_pos.y.0);
1489 if let Some(g) = &mut profiler_guard {
1490 g.set_positions(Some(abs_tuple));
1491 }
1492 let _ = profiler_guard.take();
1493 }
1494 attach_ime_position_if_needed(dispatch_ctx.window_requests, abs_pos);
1495}
1496
1497fn run_keyboard_handler_for_node(
1498 dispatch_ctx: &mut KeyboardInputDispatchContext<'_>,
1499 node_id: indextree::NodeId,
1500 keyboard_handler: &KeyboardInputHandlerFn,
1501) {
1502 run_keyboard_input_for_node(dispatch_ctx, node_id, keyboard_handler);
1503}
1504
1505struct KeyboardInputDispatchContext<'a> {
1506 tree: &'a ComponentNodeTree,
1507 metadatas: &'a ComponentNodeMetaDatas,
1508 keyboard_events: &'a mut Vec<winit::event::KeyEvent>,
1509 modifiers: winit::keyboard::ModifiersState,
1510 window_requests: &'a mut WindowRequests,
1511 focus_owner: &'a mut FocusOwner,
1512}
1513
1514fn run_keyboard_modifier_for_node(
1515 dispatch_ctx: &mut KeyboardInputDispatchContext<'_>,
1516 node_id: indextree::NodeId,
1517 keyboard_modifier: &dyn KeyboardInputModifierNode,
1518) {
1519 run_keyboard_input_for_node(dispatch_ctx, node_id, |input| {
1520 keyboard_modifier.on_keyboard_input(input)
1521 });
1522}
1523
1524fn run_keyboard_input_for_node<F>(
1525 dispatch_ctx: &mut KeyboardInputDispatchContext<'_>,
1526 node_id: indextree::NodeId,
1527 dispatch: F,
1528) where
1529 F: FnOnce(KeyboardInput<'_>),
1530{
1531 let Some(NodeInputContext {
1532 abs_pos,
1533 event_clip_rect: _,
1534 node_computed_data,
1535 instance_logic_id,
1536 instance_key,
1537 fn_name,
1538 parent_id,
1539 }) = resolve_node_input_context(dispatch_ctx.tree, dispatch_ctx.metadatas, node_id)
1540 else {
1541 return;
1542 };
1543 #[cfg(not(feature = "profiling"))]
1544 let _ = parent_id;
1545
1546 #[cfg(feature = "profiling")]
1547 let mut profiler_guard = Some(ProfilerScopeGuard::new(
1548 ProfilerPhase::Input,
1549 Some(node_id),
1550 parent_id,
1551 Some(fn_name.as_str()),
1552 ));
1553 let _node_ctx_guard =
1554 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name.as_str());
1555 let _instance_ctx_guard = push_current_component_instance_key(instance_key);
1556 let _phase_guard = push_phase(RuntimePhase::Input);
1557 let _focus_owner_guard = bind_focus_owner(dispatch_ctx.focus_owner);
1558 let input = KeyboardInput {
1559 computed_data: node_computed_data,
1560 keyboard_events: dispatch_ctx.keyboard_events,
1561 key_modifiers: dispatch_ctx.modifiers,
1562 ime_request: &mut dispatch_ctx.window_requests.ime_request,
1563 };
1564 dispatch(input);
1565
1566 #[cfg(feature = "profiling")]
1567 {
1568 let abs_tuple = (abs_pos.x.0, abs_pos.y.0);
1569 if let Some(g) = &mut profiler_guard {
1570 g.set_positions(Some(abs_tuple));
1571 }
1572 let _ = profiler_guard.take();
1573 }
1574 attach_ime_position_if_needed(dispatch_ctx.window_requests, abs_pos);
1575}
1576
1577fn run_ime_handler_for_node(
1578 tree: &ComponentNodeTree,
1579 metadatas: &ComponentNodeMetaDatas,
1580 node_id: indextree::NodeId,
1581 ime_handler: &ImeInputHandlerFn,
1582 ime_events: &mut Vec<winit::event::Ime>,
1583 window_requests: &mut WindowRequests,
1584 focus_owner: &mut FocusOwner,
1585) {
1586 run_ime_input_for_node(
1587 tree,
1588 metadatas,
1589 node_id,
1590 ime_events,
1591 window_requests,
1592 focus_owner,
1593 ime_handler,
1594 );
1595}
1596
1597fn run_ime_modifier_for_node(
1598 tree: &ComponentNodeTree,
1599 metadatas: &ComponentNodeMetaDatas,
1600 node_id: indextree::NodeId,
1601 ime_modifier: &dyn ImeInputModifierNode,
1602 ime_events: &mut Vec<winit::event::Ime>,
1603 window_requests: &mut WindowRequests,
1604 focus_owner: &mut FocusOwner,
1605) {
1606 run_ime_input_for_node(
1607 tree,
1608 metadatas,
1609 node_id,
1610 ime_events,
1611 window_requests,
1612 focus_owner,
1613 |input| ime_modifier.on_ime_input(input),
1614 );
1615}
1616
1617fn run_ime_input_for_node<F>(
1618 tree: &ComponentNodeTree,
1619 metadatas: &ComponentNodeMetaDatas,
1620 node_id: indextree::NodeId,
1621 ime_events: &mut Vec<winit::event::Ime>,
1622 window_requests: &mut WindowRequests,
1623 focus_owner: &mut FocusOwner,
1624 dispatch: F,
1625) where
1626 F: FnOnce(ImeInput<'_>),
1627{
1628 let Some(NodeInputContext {
1629 abs_pos,
1630 event_clip_rect: _,
1631 node_computed_data,
1632 instance_logic_id,
1633 instance_key,
1634 fn_name,
1635 parent_id,
1636 }) = resolve_node_input_context(tree, metadatas, node_id)
1637 else {
1638 return;
1639 };
1640 #[cfg(not(feature = "profiling"))]
1641 let _ = parent_id;
1642
1643 #[cfg(feature = "profiling")]
1644 let mut profiler_guard = Some(ProfilerScopeGuard::new(
1645 ProfilerPhase::Input,
1646 Some(node_id),
1647 parent_id,
1648 Some(fn_name.as_str()),
1649 ));
1650 let _node_ctx_guard =
1651 push_current_node_with_instance_logic_id(node_id, instance_logic_id, fn_name.as_str());
1652 let _instance_ctx_guard = push_current_component_instance_key(instance_key);
1653 let _phase_guard = push_phase(RuntimePhase::Input);
1654 let _focus_owner_guard = bind_focus_owner(focus_owner);
1655 let input = ImeInput {
1656 computed_data: node_computed_data,
1657 ime_events,
1658 ime_request: &mut window_requests.ime_request,
1659 };
1660 dispatch(input);
1661
1662 #[cfg(feature = "profiling")]
1663 {
1664 let abs_tuple = (abs_pos.x.0, abs_pos.y.0);
1665 if let Some(g) = &mut profiler_guard {
1666 g.set_positions(Some(abs_tuple));
1667 }
1668 let _ = profiler_guard.take();
1669 }
1670 attach_ime_position_if_needed(window_requests, abs_pos);
1671}
1672
1673fn dispatch_default_focus_keyboard_navigation(
1674 tree: &ComponentNodeTree,
1675 keyboard_events: &mut Vec<winit::event::KeyEvent>,
1676 modifiers: winit::keyboard::ModifiersState,
1677 focus_owner: &mut FocusOwner,
1678) -> Option<FocusDirection> {
1679 if keyboard_events.is_empty() {
1680 return None;
1681 }
1682
1683 let mut pending_focus_move_retry = None;
1684 keyboard_events.retain(|event| {
1685 if pending_focus_move_retry.is_some() {
1686 return false;
1687 }
1688 let Some(direction) = default_focus_navigation_direction(event, modifiers) else {
1689 return true;
1690 };
1691 match try_dispatch_focus_move_request(tree, direction, focus_owner) {
1692 FocusMoveRequestResult::Moved => false,
1693 FocusMoveRequestResult::Retry(direction) => {
1694 pending_focus_move_retry = Some(direction);
1695 false
1696 }
1697 FocusMoveRequestResult::NotHandled => true,
1698 }
1699 });
1700 pending_focus_move_retry
1701}
1702
1703fn default_focus_navigation_direction(
1704 event: &winit::event::KeyEvent,
1705 modifiers: winit::keyboard::ModifiersState,
1706) -> Option<FocusDirection> {
1707 if event.state != winit::event::ElementState::Pressed {
1708 return None;
1709 }
1710
1711 if modifiers.control_key() || modifiers.alt_key() || modifiers.super_key() {
1712 return None;
1713 }
1714
1715 match &event.logical_key {
1716 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab) => {
1717 if modifiers.shift_key() {
1718 Some(FocusDirection::Previous)
1719 } else {
1720 Some(FocusDirection::Next)
1721 }
1722 }
1723 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft) => {
1724 (!modifiers.shift_key()).then_some(FocusDirection::Left)
1725 }
1726 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowRight) => {
1727 (!modifiers.shift_key()).then_some(FocusDirection::Right)
1728 }
1729 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp) => {
1730 (!modifiers.shift_key()).then_some(FocusDirection::Up)
1731 }
1732 winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown) => {
1733 (!modifiers.shift_key()).then_some(FocusDirection::Down)
1734 }
1735 _ => None,
1736 }
1737}
1738
1739enum FocusMoveRequestResult {
1740 NotHandled,
1741 Moved,
1742 Retry(FocusDirection),
1743}
1744
1745fn try_dispatch_focus_move_request(
1746 tree: &ComponentNodeTree,
1747 direction: FocusDirection,
1748 focus_owner: &mut FocusOwner,
1749) -> FocusMoveRequestResult {
1750 if focus_owner.move_focus(direction) {
1751 return FocusMoveRequestResult::Moved;
1752 }
1753 if dispatch_focus_beyond_bounds_request(tree, direction, focus_owner) {
1754 return FocusMoveRequestResult::Retry(direction);
1755 }
1756 FocusMoveRequestResult::NotHandled
1757}
1758
1759fn dispatch_focus_beyond_bounds_request(
1760 tree: &ComponentNodeTree,
1761 direction: FocusDirection,
1762 focus_owner: &FocusOwner,
1763) -> bool {
1764 let mut current = focus_owner.active_component_node_id();
1765 while let Some(node_id) = current {
1766 let Some(node_ref) = tree.get(node_id) else {
1767 break;
1768 };
1769 let node = node_ref.get();
1770 if let Some(handler) = &node.focus_beyond_bounds_handler
1771 && handler.call(direction)
1772 {
1773 return true;
1774 }
1775 current = node_ref.parent();
1776 }
1777 false
1778}
1779
1780fn dispatch_pending_focus_reveal_request(
1781 tree: &ComponentNodeTree,
1782 metadatas: &ComponentNodeMetaDatas,
1783 focus_owner: &mut FocusOwner,
1784 retry_active_focus: bool,
1785) -> bool {
1786 let handle_id = if retry_active_focus {
1787 focus_owner.active_handle_id()
1788 } else {
1789 focus_owner.take_pending_reveal()
1790 };
1791 let Some(handle_id) = handle_id else {
1792 return false;
1793 };
1794 dispatch_focus_reveal_request(tree, metadatas, focus_owner, handle_id)
1795}
1796
1797fn dispatch_focus_reveal_request(
1798 tree: &ComponentNodeTree,
1799 metadatas: &ComponentNodeMetaDatas,
1800 focus_owner: &FocusOwner,
1801 handle_id: FocusHandleId,
1802) -> bool {
1803 let Some(target_node_id) = focus_owner.component_node_id_of(handle_id) else {
1804 return false;
1805 };
1806 let Some(target_rect) = component_node_bounds_rect(metadatas, target_node_id) else {
1807 return false;
1808 };
1809
1810 let mut current = Some(target_node_id);
1811 while let Some(node_id) = current {
1812 let Some(node_ref) = tree.get(node_id) else {
1813 break;
1814 };
1815 let Some(viewport_rect) = component_node_viewport_rect(metadatas, node_id) else {
1816 current = node_ref.parent();
1817 continue;
1818 };
1819 let node = node_ref.get();
1820 if let Some(handler) = &node.focus_reveal_handler {
1821 let handled = handler.call(crate::focus::FocusRevealRequest::new(
1822 target_rect,
1823 viewport_rect,
1824 ));
1825 if handled {
1826 return true;
1827 }
1828 }
1829 current = node_ref.parent();
1830 }
1831 false
1832}
1833
1834fn component_node_bounds_rect(
1835 metadatas: &ComponentNodeMetaDatas,
1836 node_id: indextree::NodeId,
1837) -> Option<PxRect> {
1838 let metadata = metadatas.get(&node_id)?;
1839 let abs_position = metadata.abs_position?;
1840 let computed_data = metadata.computed_data?;
1841 Some(PxRect::from_position_size(
1842 abs_position,
1843 PxSize::new(computed_data.width, computed_data.height),
1844 ))
1845}
1846
1847fn component_node_viewport_rect(
1848 metadatas: &ComponentNodeMetaDatas,
1849 node_id: indextree::NodeId,
1850) -> Option<PxRect> {
1851 let node_rect = component_node_bounds_rect(metadatas, node_id)?;
1852 let metadata = metadatas.get(&node_id)?;
1853 Some(
1854 metadata
1855 .event_clip_rect
1856 .and_then(|clip_rect| clip_rect.intersection(&node_rect))
1857 .unwrap_or(node_rect),
1858 )
1859}
1860
1861fn expand_dirty_nodes_with_ancestors(
1862 root_node: indextree::NodeId,
1863 tree: &ComponentNodeTree,
1864 dirty_nodes_self: &HashSet<u64>,
1865) -> HashSet<u64> {
1866 if dirty_nodes_self.is_empty() {
1867 return HashSet::default();
1868 }
1869
1870 let mut parent_by_key: HashMap<u64, u64> = HashMap::default();
1871 for edge in root_node.traverse(tree) {
1872 let indextree::NodeEdge::Start(node_id) = edge else {
1873 continue;
1874 };
1875 let Some(node) = tree.get(node_id) else {
1876 continue;
1877 };
1878 let instance_key = node.get().instance_key;
1879 let Some(parent_id) = node.parent() else {
1880 continue;
1881 };
1882 if let Some(parent) = tree.get(parent_id) {
1883 parent_by_key.insert(instance_key, parent.get().instance_key);
1884 }
1885 }
1886
1887 let mut expanded = dirty_nodes_self.clone();
1888 for dirty_key in dirty_nodes_self {
1889 let mut current = *dirty_key;
1890 while let Some(parent_key) = parent_by_key.get(¤t).copied() {
1891 expanded.insert(parent_key);
1892 current = parent_key;
1893 }
1894 }
1895
1896 expanded
1897}
1898
1899fn collect_children_by_instance_key(
1900 root_node: indextree::NodeId,
1901 tree: &ComponentNodeTree,
1902) -> HashMap<u64, Vec<u64>> {
1903 let mut children_by_node = HashMap::default();
1904 for edge in root_node.traverse(tree) {
1905 let indextree::NodeEdge::Start(node_id) = edge else {
1906 continue;
1907 };
1908 let Some(node) = tree.get(node_id) else {
1909 continue;
1910 };
1911 let instance_key = node.get().instance_key;
1912 let child_keys = node_id
1913 .children(tree)
1914 .filter_map(|child_id| tree.get(child_id).map(|child| child.get().instance_key))
1915 .collect();
1916 children_by_node.insert(instance_key, child_keys);
1917 }
1918 children_by_node
1919}
1920
1921fn record_layout_commands(
1922 root_node: indextree::NodeId,
1923 tree: &ComponentNodeTree,
1924 metadatas: &ComponentNodeMetaDatas,
1925 compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
1926 gpu: &wgpu::Device,
1927) {
1928 struct DrawModifierChainContent<'a> {
1929 draw_nodes: &'a [Arc<dyn crate::modifier::DrawModifierNode>],
1930 render_policy: &'a dyn crate::layout::RenderPolicyDyn,
1931 }
1932
1933 impl DrawModifierContent for DrawModifierChainContent<'_> {
1934 fn draw(&mut self, input: &RenderInput<'_>) {
1935 if let Some((draw_modifier, remaining)) = self.draw_nodes.split_first() {
1936 let mut draw_ctx = DrawModifierContext {
1937 render_input: input,
1938 };
1939 let mut next = DrawModifierChainContent {
1940 draw_nodes: remaining,
1941 render_policy: self.render_policy,
1942 };
1943 draw_modifier.draw(&mut draw_ctx, &mut next);
1944 } else {
1945 self.render_policy.record_dyn(input);
1946 }
1947 }
1948 }
1949
1950 let mut stack = vec![root_node];
1951 while let Some(node_id) = stack.pop() {
1952 let Some(node) = tree.get(node_id) else {
1953 continue;
1954 };
1955 #[cfg(feature = "profiling")]
1956 let _record_profiler_guard = {
1957 let parent_id = node.parent();
1958 Some(ProfilerScopeGuard::new(
1959 ProfilerPhase::Record,
1960 Some(node_id),
1961 parent_id,
1962 Some(node.get().fn_name.as_str()),
1963 ))
1964 };
1965 let input = RenderInput::new(node_id, metadatas, compute_resource_manager.clone(), gpu);
1966 let node_ref = node.get();
1967 let mut draw_nodes = node_ref.modifier.draw_nodes();
1968 draw_nodes.reverse();
1969 let mut content = DrawModifierChainContent {
1970 draw_nodes: &draw_nodes,
1971 render_policy: node_ref.render_policy.as_ref(),
1972 };
1973 content.draw(&input);
1974 stack.extend(node_id.children(tree));
1975 }
1976}
1977
1978#[derive(Clone, Copy)]
1979struct PreparedLayoutMetadata {
1980 self_position: PxPosition,
1981 size: PxSize,
1982 node_rect: PxRect,
1983 clips_children: bool,
1984 child_clip_rect: Option<PxRect>,
1985 cumulative_opacity: f32,
1986}
1987
1988fn prepare_layout_metadata_for_node(
1989 _tree: &ComponentNodeTree,
1990 metadatas: &ComponentNodeMetaDatas,
1991 start_pos: PxPosition,
1992 is_root: bool,
1993 node_id: indextree::NodeId,
1994 parent_clip_rect: Option<PxRect>,
1995 current_opacity: f32,
1996) -> Option<PreparedLayoutMetadata> {
1997 let mut metadata = metadatas.get_mut(&node_id)?;
1998 let rel_pos = match metadata.rel_position {
1999 Some(pos) => pos,
2000 None if is_root => PxPosition::ZERO,
2001 _ => {
2002 metadata.abs_position = None;
2003 metadata.event_clip_rect = None;
2004 return None;
2005 }
2006 };
2007 let self_position = start_pos + rel_pos;
2008 let cumulative_opacity = current_opacity * metadata.opacity;
2009 metadata.abs_position = Some(self_position);
2010 metadata.event_clip_rect = parent_clip_rect;
2011
2012 let size = metadata
2013 .computed_data
2014 .map(|d| PxSize {
2015 width: d.width,
2016 height: d.height,
2017 })
2018 .unwrap_or_default();
2019 let node_rect = PxRect {
2020 x: self_position.x,
2021 y: self_position.y,
2022 width: size.width,
2023 height: size.height,
2024 };
2025 let clips_children = metadata.clips_children;
2026 let child_clip_rect = if clips_children {
2027 Some(
2028 parent_clip_rect
2029 .and_then(|existing| existing.intersection(&node_rect))
2030 .unwrap_or(node_rect),
2031 )
2032 } else {
2033 parent_clip_rect
2034 };
2035
2036 Some(PreparedLayoutMetadata {
2037 self_position,
2038 size,
2039 node_rect,
2040 clips_children,
2041 child_clip_rect,
2042 cumulative_opacity,
2043 })
2044}
2045
2046fn populate_layout_metadata(
2047 root_node: indextree::NodeId,
2048 tree: &ComponentNodeTree,
2049 metadatas: &ComponentNodeMetaDatas,
2050) {
2051 fn visit(
2052 tree: &ComponentNodeTree,
2053 metadatas: &ComponentNodeMetaDatas,
2054 start_pos: PxPosition,
2055 is_root: bool,
2056 node_id: indextree::NodeId,
2057 clip_rect: Option<PxRect>,
2058 current_opacity: f32,
2059 ) {
2060 let Some(prepared) = prepare_layout_metadata_for_node(
2061 tree,
2062 metadatas,
2063 start_pos,
2064 is_root,
2065 node_id,
2066 clip_rect,
2067 current_opacity,
2068 ) else {
2069 return;
2070 };
2071
2072 for child in node_id.children(tree) {
2073 visit(
2074 tree,
2075 metadatas,
2076 prepared.self_position,
2077 false,
2078 child,
2079 prepared.child_clip_rect,
2080 prepared.cumulative_opacity,
2081 );
2082 }
2083 }
2084
2085 visit(
2086 tree,
2087 metadatas,
2088 PxPosition::ZERO,
2089 true,
2090 root_node,
2091 None,
2092 1.0,
2093 );
2094}
2095
2096#[tracing::instrument(level = "trace", skip(tree, metadatas))]
2098fn build_render_graph(
2099 node_id: indextree::NodeId,
2100 tree: &ComponentNodeTree,
2101 metadatas: &ComponentNodeMetaDatas,
2102 screen_size: PxSize,
2103) -> RenderGraph {
2104 let screen_rect = PxRect {
2105 x: Px(0),
2106 y: Px(0),
2107 width: screen_size.width,
2108 height: screen_size.height,
2109 };
2110
2111 let mut builder = RenderGraphBuilder::new();
2112 let mut context = RenderGraphBuildContext {
2113 tree,
2114 metadatas,
2115 builder: &mut builder,
2116 screen_rect,
2117 };
2118 build_render_graph_inner(&mut context, PxPosition::ZERO, true, node_id, None, 1.0);
2119 builder.finish()
2120}
2121
2122struct RenderGraphBuildContext<'a> {
2123 tree: &'a ComponentNodeTree,
2124 metadatas: &'a ComponentNodeMetaDatas,
2125 builder: &'a mut RenderGraphBuilder,
2126 screen_rect: PxRect,
2127}
2128
2129#[tracing::instrument(level = "trace", skip(context))]
2130fn build_render_graph_inner(
2131 context: &mut RenderGraphBuildContext<'_>,
2132 start_pos: PxPosition,
2133 is_root: bool,
2134 node_id: indextree::NodeId,
2135 clip_rect: Option<PxRect>,
2136 current_opacity: f32,
2137) {
2138 let Some(prepared) = prepare_layout_metadata_for_node(
2139 context.tree,
2140 context.metadatas,
2141 start_pos,
2142 is_root,
2143 node_id,
2144 clip_rect,
2145 current_opacity,
2146 ) else {
2147 return;
2148 };
2149
2150 if prepared.clips_children {
2151 context
2152 .builder
2153 .push_clip_push(prepared.child_clip_rect.unwrap_or(PxRect::ZERO));
2154 }
2155
2156 let fragment = match context.metadatas.get_mut(&node_id) {
2157 Some(mut metadata) => metadata.take_fragment(),
2158 None => {
2159 warn!("Missing metadata for node {node_id:?}; skipping render graph build");
2160 return;
2161 }
2162 };
2163
2164 if prepared.size.width.0 > 0
2165 && prepared.size.height.0 > 0
2166 && !prepared.node_rect.is_orthogonal(&context.screen_rect)
2167 {
2168 context.builder.append_fragment(
2169 fragment,
2170 prepared.size,
2171 prepared.self_position,
2172 prepared.cumulative_opacity,
2173 );
2174 }
2175
2176 for child in node_id.children(context.tree) {
2177 build_render_graph_inner(
2178 context,
2179 prepared.self_position,
2180 false,
2181 child,
2182 prepared.child_clip_rect,
2183 prepared.cumulative_opacity,
2184 );
2185 }
2186
2187 if prepared.clips_children {
2188 context.builder.push_clip_pop();
2189 }
2190}
2191
2192#[cfg(test)]
2193mod tests {
2194 use super::*;
2195
2196 use crate::{
2197 component_tree::ComponentNode,
2198 layout::{DefaultLayoutPolicy, NoopRenderPolicy},
2199 modifier::Modifier,
2200 };
2201
2202 fn node(name: &str, instance_logic_id: u64, instance_key: u64) -> ComponentNode {
2203 ComponentNode {
2204 fn_name: name.to_string(),
2205 instance_logic_id,
2206 instance_key,
2207 pointer_preview_handlers: Vec::new(),
2208 pointer_handlers: Vec::new(),
2209 pointer_final_handlers: Vec::new(),
2210 keyboard_preview_handlers: Vec::new(),
2211 keyboard_handlers: Vec::new(),
2212 ime_preview_handlers: Vec::new(),
2213 ime_handlers: Vec::new(),
2214 focus_requester_binding: None,
2215 focus_registration: None,
2216 focus_restorer_fallback: None,
2217 focus_traversal_policy: None,
2218 focus_changed_handler: None,
2219 focus_event_handler: None,
2220 focus_beyond_bounds_handler: None,
2221 focus_reveal_handler: None,
2222 modifier: Modifier::default(),
2223 layout_policy: Box::new(DefaultLayoutPolicy),
2224 render_policy: Box::new(NoopRenderPolicy),
2225 replay: None,
2226 props_unchanged_from_previous: false,
2227 }
2228 }
2229
2230 #[test]
2231 fn begin_replace_subtree_rejects_root_instance_key() {
2232 let mut tree = ComponentTree::new();
2233
2234 let root = tree.add_node(node("root", 1, 1));
2235 tree.pop_node();
2236
2237 let result = tree.begin_replace_subtree_by_instance_key(1);
2238 assert!(matches!(
2239 result,
2240 Err(ReplayReplaceError::RootNodeNotReplaceable)
2241 ));
2242 assert!(tree.get(root).is_some());
2243 }
2244
2245 #[test]
2246 fn finish_replace_subtree_keeps_inserted_roots_before_next_sibling() {
2247 let mut tree = ComponentTree::new();
2248
2249 let root = tree.add_node(node("root", 1, 1));
2250
2251 let _first = tree.add_node(node("first", 2, 2));
2252 let _first_child = tree.add_node(node("first_child", 3, 3));
2253 tree.pop_node();
2254 tree.pop_node();
2255
2256 let second = tree.add_node(node("second", 4, 4));
2257 tree.pop_node();
2258 tree.pop_node();
2259
2260 let context = match tree.begin_replace_subtree_by_instance_key(2) {
2261 Ok(context) => context,
2262 Err(_) => panic!("replace context should be created"),
2263 };
2264
2265 let new_a = tree.add_node(node("new_a", 5, 5));
2266 let _new_a_child = tree.add_node(node("new_a_child", 6, 6));
2267 tree.pop_node();
2268 tree.pop_node();
2269 let new_b = tree.add_node(node("new_b", 7, 7));
2270 tree.pop_node();
2271
2272 let before_finish = root.children(tree.tree()).collect::<Vec<_>>();
2273 assert_eq!(before_finish, vec![second, new_a, new_b]);
2274
2275 let replace_result = match tree.finish_replace_subtree(context) {
2276 Ok(result) => result,
2277 Err(_) => panic!("finish replace should succeed"),
2278 };
2279
2280 let root_children = root
2281 .children(tree.tree())
2282 .map(|id| tree.get(id).expect("child must exist").fn_name.clone())
2283 .collect::<Vec<_>>();
2284 assert_eq!(root_children, vec!["new_a", "new_b", "second"]);
2285
2286 assert!(replace_result.removed_instance_keys.contains(&2));
2287 assert!(replace_result.removed_instance_keys.contains(&3));
2288 assert!(replace_result.inserted_instance_keys.contains(&5));
2289 assert!(replace_result.inserted_instance_keys.contains(&6));
2290 assert!(replace_result.inserted_instance_keys.contains(&7));
2291 assert!(replace_result.removed_instance_logic_ids.contains(&2));
2292 assert!(replace_result.removed_instance_logic_ids.contains(&3));
2293 assert!(replace_result.inserted_instance_logic_ids.contains(&5));
2294 assert!(replace_result.inserted_instance_logic_ids.contains(&6));
2295 assert!(replace_result.inserted_instance_logic_ids.contains(&7));
2296
2297 assert!(tree.get(second).is_some());
2298 }
2299
2300 #[test]
2301 fn finish_replace_subtree_keeps_reused_subtree_and_excludes_it_from_removed_sets() {
2302 let mut tree = ComponentTree::new();
2303
2304 let root = tree.add_node(node("root", 1, 1));
2305
2306 let _first = tree.add_node(node("first", 2, 2));
2307 let _first_child = tree.add_node(node("first_child", 3, 3));
2308 tree.pop_node();
2309 tree.pop_node();
2310
2311 let second = tree.add_node(node("second", 4, 4));
2312 tree.pop_node();
2313 tree.pop_node();
2314
2315 let context = match tree.begin_replace_subtree_by_instance_key(2) {
2316 Ok(context) => context,
2317 Err(_) => panic!("replace context should be created"),
2318 };
2319
2320 let _replacement = tree.add_node(node("replacement", 2, 2));
2321 assert!(tree.try_reuse_current_subtree(2, 2));
2322 tree.pop_node();
2323
2324 let replace_result = match tree.finish_replace_subtree(context) {
2325 Ok(result) => result,
2326 Err(_) => panic!("finish replace should succeed"),
2327 };
2328
2329 let root_children = root
2330 .children(tree.tree())
2331 .map(|id| tree.get(id).expect("child must exist").fn_name.clone())
2332 .collect::<Vec<_>>();
2333 assert_eq!(root_children, vec!["first", "second"]);
2334 assert!(tree.find_node_id_by_instance_key(3).is_some());
2335 assert!(tree.get(second).is_some());
2336
2337 assert!(replace_result.inserted_instance_keys.contains(&2));
2338 assert!(replace_result.inserted_instance_keys.contains(&3));
2339 assert!(replace_result.reused_instance_logic_ids.contains(&2));
2340 assert!(replace_result.reused_instance_logic_ids.contains(&3));
2341 assert!(!replace_result.removed_instance_keys.contains(&2));
2342 assert!(!replace_result.removed_instance_keys.contains(&3));
2343 assert!(!replace_result.removed_instance_logic_ids.contains(&2));
2344 assert!(!replace_result.removed_instance_logic_ids.contains(&3));
2345 }
2346}