tessera_ui/component_tree/
node.rs

1use std::{
2    collections::HashMap,
3    ops::{Add, AddAssign},
4    sync::Arc,
5};
6
7use dashmap::DashMap;
8use indextree::NodeId;
9use rayon::prelude::*;
10use rustc_hash::FxBuildHasher;
11use tracing::debug;
12use winit::window::CursorIcon;
13
14use crate::{
15    Px,
16    accessibility::{AccessibilityActionHandler, AccessibilityNode},
17    cursor::{CursorEventContent, PointerChange},
18    focus::{
19        FocusDirection, FocusRegistration, FocusRequester, FocusRevealRequest, FocusState,
20        FocusTraversalPolicy,
21    },
22    layout::{
23        LayoutInput, LayoutOutput, LayoutPolicyDyn, LayoutResult, PlacementInput, RenderPolicyDyn,
24    },
25    modifier::{LayoutModifierChild, LayoutModifierInput, LayoutModifierNode, Modifier},
26    prop::CallbackWith,
27    px::{PxPosition, PxSize},
28    render_graph::RenderFragment,
29    runtime::{
30        RuntimePhase, push_current_component_instance_key,
31        push_current_node_with_instance_logic_id, push_phase,
32    },
33    time::Instant,
34};
35
36use super::{
37    LayoutContext, LayoutSnapshotEntry, LayoutSnapshotMap,
38    constraint::{Constraint, DimensionValue, ParentConstraint},
39};
40
41#[cfg(feature = "profiling")]
42use crate::profiler::{Phase as ProfilerPhase, ScopeGuard as ProfilerScopeGuard};
43
44/// A ComponentNode is a node in the component tree.
45/// It represents all information about a component.
46pub(crate) struct ComponentNode {
47    /// Component function's name, for debugging purposes.
48    pub(crate) fn_name: String,
49    /// Stable logic identifier of this concrete component instance.
50    pub(crate) instance_logic_id: u64,
51    /// Stable instance identifier for this node in the current frame.
52    pub(crate) instance_key: u64,
53    /// Pointer input handlers for capture stage (root to leaf).
54    pub(crate) pointer_preview_handlers: Vec<Box<PointerInputHandlerFn>>,
55    /// Pointer input handlers for bubble stage (leaf to root).
56    pub(crate) pointer_handlers: Vec<Box<PointerInputHandlerFn>>,
57    /// Pointer input handlers for final stage (root to leaf).
58    pub(crate) pointer_final_handlers: Vec<Box<PointerInputHandlerFn>>,
59    /// Keyboard input handlers for preview stage (root to leaf).
60    pub(crate) keyboard_preview_handlers: Vec<Box<KeyboardInputHandlerFn>>,
61    /// Keyboard input handlers for bubble stage (leaf to root).
62    pub(crate) keyboard_handlers: Vec<Box<KeyboardInputHandlerFn>>,
63    /// IME input handlers for preview stage (root to leaf).
64    pub(crate) ime_preview_handlers: Vec<Box<ImeInputHandlerFn>>,
65    /// IME input handlers for bubble stage (leaf to root).
66    pub(crate) ime_handlers: Vec<Box<ImeInputHandlerFn>>,
67    /// Optional focus requester bound to this component node.
68    pub(crate) focus_requester_binding: Option<FocusRequester>,
69    /// Optional focus registration attached to this component node.
70    pub(crate) focus_registration: Option<FocusRegistration>,
71    /// Optional fallback target used when a focus scope restores focus.
72    pub(crate) focus_restorer_fallback: Option<FocusRequester>,
73    /// Optional traversal policy used when directional navigation starts inside
74    /// this focus group or scope.
75    pub(crate) focus_traversal_policy: Option<FocusTraversalPolicy>,
76    /// Optional callback invoked when the node's focus state changes.
77    pub(crate) focus_changed_handler: Option<FocusChangedHandler>,
78    /// Optional callback invoked when the node participates in a focus event.
79    pub(crate) focus_event_handler: Option<FocusEventHandler>,
80    /// Optional callback used to expand virtualized content for focus search.
81    pub(crate) focus_beyond_bounds_handler: Option<FocusBeyondBoundsHandler>,
82    /// Optional callback used to reveal the focused rectangle inside a
83    /// container.
84    pub(crate) focus_reveal_handler: Option<FocusRevealHandler>,
85    /// Node-local modifier chain attached during build.
86    pub(crate) modifier: Modifier,
87    /// Pure layout policy for measure and placement passes.
88    pub(crate) layout_policy: Box<dyn LayoutPolicyDyn>,
89    /// Render policy for recording draw and compute commands.
90    pub(crate) render_policy: Box<dyn RenderPolicyDyn>,
91    /// Optional replay metadata for subtree-level rerun.
92    pub(crate) replay: Option<crate::prop::ComponentReplayData>,
93    /// Whether props are equal to the previous frame snapshot.
94    pub(crate) props_unchanged_from_previous: bool,
95}
96
97/// Contains metadata of the component node.
98pub(crate) struct ComponentNodeMetaData {
99    /// The computed data (size) of the node.
100    /// None if the node is not computed yet.
101    pub computed_data: Option<ComputedData>,
102    /// Whether the layout cache was hit for this node in the current frame.
103    pub layout_cache_hit: bool,
104    /// Placement order among siblings within the parent layout result.
105    pub placement_order: Option<u64>,
106    /// The node's start position, relative to its parent.
107    /// None if the node is not placed yet.
108    pub rel_position: Option<PxPosition>,
109    /// The node's start position, relative to the root window.
110    /// This will be computed during drawing command's generation.
111    /// None if the node is not drawn yet.
112    pub abs_position: Option<PxPosition>,
113    /// The effective clipping rectangle for this node, considering all its
114    /// ancestors. This is calculated once per frame before event handling.
115    pub event_clip_rect: Option<crate::PxRect>,
116    /// Render fragment associated with this node.
117    ///
118    /// Commands are collected during the record phase and merged into the frame
119    /// graph during rendering.
120    pub(crate) fragment: RenderFragment,
121    /// Whether this node clips its children.
122    pub clips_children: bool,
123    /// Opacity multiplier applied to this node and its descendants.
124    pub opacity: f32,
125    /// Accessibility information for this node.
126    pub accessibility: Option<AccessibilityNode>,
127    /// Handler for accessibility actions on this node.
128    pub accessibility_action_handler: Option<AccessibilityActionHandler>,
129}
130
131impl ComponentNodeMetaData {
132    /// Creates a new `ComponentNodeMetaData` with default values.
133    pub fn none() -> Self {
134        Self {
135            computed_data: None,
136            layout_cache_hit: false,
137            placement_order: None,
138            rel_position: None,
139            abs_position: None,
140            event_clip_rect: None,
141            fragment: RenderFragment::default(),
142            clips_children: false,
143            opacity: 1.0,
144            accessibility: None,
145            accessibility_action_handler: None,
146        }
147    }
148
149    /// Returns a mutable render fragment for this node.
150    pub fn fragment_mut(&mut self) -> &mut RenderFragment {
151        &mut self.fragment
152    }
153
154    /// Takes the current fragment and replaces it with an empty one.
155    pub(crate) fn take_fragment(&mut self) -> RenderFragment {
156        std::mem::take(&mut self.fragment)
157    }
158}
159
160impl Default for ComponentNodeMetaData {
161    fn default() -> Self {
162        Self::none()
163    }
164}
165
166fn reset_frame_metadata(node_id: NodeId, component_node_metadatas: &ComponentNodeMetaDatas) {
167    let mut metadata = component_node_metadatas.entry(node_id).or_default();
168    metadata.computed_data = None;
169    metadata.layout_cache_hit = false;
170    metadata.placement_order = None;
171    metadata.rel_position = None;
172    metadata.abs_position = None;
173    metadata.event_clip_rect = None;
174    metadata.fragment = RenderFragment::default();
175    metadata.clips_children = false;
176    metadata.opacity = 1.0;
177}
178
179/// A tree of component nodes, using `indextree::Arena` for storage.
180pub(crate) type ComponentNodeTree = indextree::Arena<ComponentNode>;
181/// Contains all component nodes' metadatas, using a thread-safe `DashMap`.
182pub(crate) type ComponentNodeMetaDatas = DashMap<NodeId, ComponentNodeMetaData, FxBuildHasher>;
183
184/// Represents errors that can occur during node measurement.
185#[derive(Debug, Clone, PartialEq)]
186pub enum MeasurementError {
187    /// Indicates that the specified node was not found in the component tree.
188    NodeNotFoundInTree,
189    /// Indicates that metadata for the specified node was not found (currently
190    /// not a primary error source in measure_node).
191    NodeNotFoundInMeta,
192    /// Indicates that the layout policy for a node failed. Contains a string
193    /// detailing the failure.
194    MeasureFnFailed(String),
195    /// Indicates that the measurement of a child node failed during a
196    /// parent's layout calculation. Contains the `NodeId` of the child
197    /// that failed.
198    ChildMeasurementFailed(NodeId),
199}
200
201/// Pointer input dispatch pass.
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum PointerEventPass {
204    /// Dispatch from root to leaf before the main pointer pass.
205    Initial,
206    /// Main pointer dispatch from leaf to root.
207    Main,
208    /// Final pointer dispatch from root to leaf.
209    Final,
210}
211
212/// Pointer-specific input handler.
213pub type PointerInputHandlerFn = dyn Fn(PointerInput) + Send + Sync;
214/// Keyboard-specific input handler.
215pub type KeyboardInputHandlerFn = dyn Fn(KeyboardInput) + Send + Sync;
216/// IME-specific input handler.
217pub type ImeInputHandlerFn = dyn Fn(ImeInput) + Send + Sync;
218/// Focus-changed callback attached to a component node.
219pub type FocusChangedHandler = CallbackWith<FocusState>;
220/// Focus-event callback attached to a component node.
221pub type FocusEventHandler = CallbackWith<FocusState>;
222/// Beyond-bounds focus callback attached to a virtualized container node.
223pub type FocusBeyondBoundsHandler = CallbackWith<FocusDirection, bool>;
224/// Reveal callback attached to a scrollable container node.
225pub type FocusRevealHandler = CallbackWith<FocusRevealRequest, bool>;
226
227/// Input for pointer handlers.
228///
229/// This type carries pointer event payload, local geometry, and event
230/// consumption helpers. Side effects that are not pointer-specific should use
231/// dedicated APIs such as semantics modifiers, hover cursor modifiers, window
232/// action helpers, or the IME session bridge instead of a generic request bag.
233pub struct PointerInput<'a> {
234    /// Current pointer dispatch pass.
235    pub pass: PointerEventPass,
236    /// The size of the component node, computed during the measure stage.
237    pub computed_data: ComputedData,
238    /// The position of the cursor, if available.
239    /// Relative to the root position of the component.
240    pub cursor_position_rel: Option<PxPosition>,
241    /// Absolute cursor position in window coordinates.
242    pub(crate) cursor_position_abs: &'a mut Option<PxPosition>,
243    /// Pointer changes from the event loop, if any.
244    pub pointer_changes: &'a mut Vec<PointerChange>,
245    /// The current state of the keyboard modifiers at the time of the event.
246    pub key_modifiers: winit::keyboard::ModifiersState,
247    pub(crate) ime_request: &'a mut Option<ImeRequest>,
248    pub(crate) window_action: &'a mut Option<WindowAction>,
249}
250
251impl PointerInput<'_> {
252    /// Marks all current pointer changes as consumed.
253    pub fn consume_pointer_changes(&mut self) {
254        for change in self.pointer_changes.iter_mut() {
255            change.consume();
256        }
257    }
258
259    /// Returns whether any current pointer change is an unconsumed release.
260    pub fn has_unconsumed_release(&self) -> bool {
261        self.pointer_changes.iter().any(|change| {
262            !change.is_consumed() && matches!(change.content, CursorEventContent::Released(_))
263        })
264    }
265
266    /// Blocks pointer input to other components.
267    pub fn block_cursor(&mut self) {
268        self.cursor_position_abs.take();
269        self.consume_pointer_changes();
270    }
271
272    /// Blocks pointer changes to other components.
273    pub fn block_all(&mut self) {
274        self.block_cursor();
275    }
276
277    fn set_window_action(&mut self, action: WindowAction) {
278        *self.window_action = Some(action);
279    }
280
281    /// Begins a system drag move for the current window.
282    pub fn drag_window(&mut self) {
283        self.set_window_action(WindowAction::DragWindow);
284    }
285
286    /// Requests that the current window be minimized.
287    pub fn minimize_window(&mut self) {
288        self.set_window_action(WindowAction::Minimize);
289    }
290
291    /// Requests that the current window be maximized.
292    pub fn maximize_window(&mut self) {
293        self.set_window_action(WindowAction::Maximize);
294    }
295
296    /// Requests that the current window toggle maximized state.
297    pub fn toggle_maximize_window(&mut self) {
298        self.set_window_action(WindowAction::ToggleMaximize);
299    }
300
301    /// Requests that the current window close.
302    pub fn close_window(&mut self) {
303        self.set_window_action(WindowAction::Close);
304    }
305
306    /// Returns the IME session bridge for the current frame.
307    pub fn ime_session(&mut self) -> ImeSession<'_> {
308        ImeSession {
309            request: self.ime_request,
310        }
311    }
312}
313
314/// Input for keyboard handlers.
315///
316/// This type carries keyboard event payload and event consumption helpers.
317/// IME updates, semantics, and other side effects are exposed through narrower
318/// dedicated APIs rather than a general-purpose request sink.
319pub struct KeyboardInput<'a> {
320    /// The size of the component node, computed during the measure stage.
321    pub computed_data: ComputedData,
322    /// Keyboard events from the event loop, if any.
323    pub keyboard_events: &'a mut Vec<winit::event::KeyEvent>,
324    /// The current state of the keyboard modifiers at the time of the event.
325    pub key_modifiers: winit::keyboard::ModifiersState,
326    pub(crate) ime_request: &'a mut Option<ImeRequest>,
327}
328
329impl KeyboardInput<'_> {
330    /// Blocks keyboard events to other components.
331    pub fn block_keyboard(&mut self) {
332        self.keyboard_events.clear();
333    }
334
335    /// Returns the IME session bridge for the current frame.
336    pub fn ime_session(&mut self) -> ImeSession<'_> {
337        ImeSession {
338            request: self.ime_request,
339        }
340    }
341}
342
343/// Input for IME handlers.
344///
345/// This type carries IME event payload and event consumption helpers. IME
346/// snapshot publication is performed through [`ImeSession`].
347pub struct ImeInput<'a> {
348    /// The size of the component node, computed during the measure stage.
349    pub computed_data: ComputedData,
350    /// IME events from the event loop, if any.
351    pub ime_events: &'a mut Vec<winit::event::Ime>,
352    pub(crate) ime_request: &'a mut Option<ImeRequest>,
353}
354
355impl ImeInput<'_> {
356    /// Blocks IME events to other components.
357    pub fn block_ime(&mut self) {
358        self.ime_events.clear();
359    }
360
361    /// Returns the IME session bridge for the current frame.
362    pub fn ime_session(&mut self) -> ImeSession<'_> {
363        ImeSession {
364            request: self.ime_request,
365        }
366    }
367}
368
369/// A collection of requests that components can make to the windowing system
370/// for the current frame. This struct's lifecycle is confined to a single
371/// `compute` pass.
372#[derive(Default, Debug)]
373pub(crate) struct WindowRequests {
374    /// The cursor icon requested by a component. If multiple components request
375    /// a cursor, the last one to make a request in a frame "wins", since
376    /// it's executed later.
377    pub cursor_icon: CursorIcon,
378    /// An Input Method Editor (IME) request.
379    /// If multiple components request IME, the one from the "newer" component
380    /// (which is processed later in the state handling pass) will overwrite
381    /// previous requests.
382    pub ime_request: Option<ImeRequest>,
383    /// A window action request for the current frame.
384    pub window_action: Option<WindowAction>,
385}
386
387/// Window actions that components can request.
388#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389pub enum WindowAction {
390    /// Begin a system drag move for the window.
391    DragWindow,
392    /// Minimize the window.
393    Minimize,
394    /// Maximize the window.
395    Maximize,
396    /// Toggle maximized state.
397    ToggleMaximize,
398    /// Request application close.
399    Close,
400}
401
402/// Frame-local IME bridge used by input handlers to publish text input state.
403///
404/// Use this bridge when an input handler needs to expose the current text-input
405/// snapshot to the renderer-facing IME integration without mutating renderer
406/// aggregation state directly.
407pub struct ImeSession<'a> {
408    request: &'a mut Option<ImeRequest>,
409}
410
411impl ImeSession<'_> {
412    /// Publishes the IME snapshot for the current frame.
413    pub fn update(&mut self, request: ImeRequest) {
414        *self.request = Some(request);
415    }
416
417    /// Clears the IME snapshot for the current frame.
418    pub fn clear(&mut self) {
419        *self.request = None;
420    }
421}
422
423/// A request to the windowing system to open an Input Method Editor (IME).
424/// This is typically used for text input components.
425#[derive(Clone, Debug, PartialEq, Eq)]
426pub struct ImeRequest {
427    /// The size of the area where the IME is requested.
428    pub size: PxSize,
429    /// The position of the IME anchor relative to the requesting node.
430    pub local_position: PxPosition,
431    /// The current ordered selection range in the backing text buffer.
432    pub selection_range: Option<std::ops::Range<usize>>,
433    /// The current ordered composition range in the backing text buffer.
434    pub composition_range: Option<std::ops::Range<usize>>,
435    /// The absolute position where the IME should be placed.
436    /// This is set internally by the component tree during the compute pass.
437    pub(crate) position: Option<PxPosition>, // should be setted in tessera node tree compute
438}
439
440impl ImeRequest {
441    /// Creates a new IME request with the target input area size.
442    ///
443    /// The absolute position is injected during the compute pass.
444    pub fn new(size: PxSize) -> Self {
445        Self {
446            size,
447            local_position: PxPosition::ZERO,
448            selection_range: None,
449            composition_range: None,
450            position: None, // Position will be set during the compute phase
451        }
452    }
453
454    /// Sets the position of the IME anchor relative to the requesting node.
455    pub fn with_local_position(mut self, local_position: PxPosition) -> Self {
456        self.local_position = local_position;
457        self
458    }
459
460    /// Sets the current ordered text selection range.
461    pub fn with_selection_range(mut self, selection_range: Option<std::ops::Range<usize>>) -> Self {
462        self.selection_range = selection_range;
463        self
464    }
465
466    /// Sets the current ordered text composition range.
467    pub fn with_composition_range(
468        mut self,
469        composition_range: Option<std::ops::Range<usize>>,
470    ) -> Self {
471        self.composition_range = composition_range;
472        self
473    }
474}
475
476fn apply_layout_placements(
477    placements: &[(u64, PxPosition)],
478    tree: &ComponentNodeTree,
479    children: &[NodeId],
480    component_node_metadatas: &ComponentNodeMetaDatas,
481) {
482    if placements.is_empty() || children.is_empty() {
483        return;
484    }
485    let mut child_map = HashMap::new();
486    for child_id in children {
487        if let Some(child) = tree.get(*child_id) {
488            child_map.insert(child.get().instance_key, *child_id);
489        }
490    }
491    for (placement_order, (instance_key, position)) in placements.iter().enumerate() {
492        if let Some(child_id) = child_map.get(instance_key) {
493            place_node(
494                *child_id,
495                *position,
496                placement_order as u64,
497                component_node_metadatas,
498            );
499        }
500    }
501}
502
503fn restore_cached_subtree_metadata(
504    node_id: NodeId,
505    rel_position: Option<PxPosition>,
506    tree: &ComponentNodeTree,
507    component_node_metadatas: &ComponentNodeMetaDatas,
508    snapshots: &LayoutSnapshotMap,
509) -> bool {
510    let Some(node) = tree.get(node_id) else {
511        return false;
512    };
513    let instance_key = node.get().instance_key;
514    let Some(entry) = snapshots.get(&instance_key) else {
515        return false;
516    };
517
518    let size = entry.layout_result.size;
519    let placements = entry.layout_result.placements.clone();
520    drop(entry);
521
522    {
523        let mut metadata = component_node_metadatas.entry(node_id).or_default();
524        metadata.computed_data = Some(size);
525        metadata.layout_cache_hit = true;
526        if let Some(position) = rel_position {
527            metadata.rel_position = Some(position);
528        }
529    }
530
531    let mut child_positions = HashMap::new();
532    let mut child_orders = HashMap::new();
533    for (placement_order, (child_key, child_pos)) in placements.into_iter().enumerate() {
534        child_positions.insert(child_key, child_pos);
535        child_orders.insert(child_key, placement_order as u64);
536    }
537
538    for child_id in node_id.children(tree) {
539        let child_layout = tree.get(child_id).map(|child| {
540            let instance_key = child.get().instance_key;
541            (
542                child_positions.get(&instance_key).copied(),
543                child_orders.get(&instance_key).copied(),
544            )
545        });
546        let (child_rel_position, child_placement_order) = child_layout.unwrap_or((None, None));
547        if let Some(placement_order) = child_placement_order {
548            component_node_metadatas
549                .entry(child_id)
550                .or_default()
551                .placement_order = Some(placement_order);
552        }
553        if !restore_cached_subtree_metadata(
554            child_id,
555            child_rel_position,
556            tree,
557            component_node_metadatas,
558            snapshots,
559        ) {
560            return false;
561        }
562    }
563
564    true
565}
566
567#[derive(Clone)]
568struct MeasuredNodeLayout {
569    size: ComputedData,
570    placements: Vec<(u64, PxPosition)>,
571    measured_children: HashMap<NodeId, crate::layout::ChildMeasure>,
572}
573
574struct MeasureLayoutContext<'a, 'ctx> {
575    tree: &'a ComponentNodeTree,
576    children: &'a [NodeId],
577    component_node_metadatas: &'a ComponentNodeMetaDatas,
578    layout_ctx: Option<&'a LayoutContext<'ctx>>,
579    resolve_instance_key: &'a dyn Fn(NodeId) -> u64,
580}
581
582fn measure_base_layout(
583    layout_policy: &dyn LayoutPolicyDyn,
584    layout_ctx: &MeasureLayoutContext<'_, '_>,
585    constraint: &Constraint,
586) -> Result<MeasuredNodeLayout, MeasurementError> {
587    let input = LayoutInput::new(
588        layout_ctx.tree,
589        ParentConstraint::new(constraint),
590        layout_ctx.children,
591        layout_ctx.component_node_metadatas,
592        layout_ctx.layout_ctx,
593    );
594    let mut output = LayoutOutput::new(layout_ctx.resolve_instance_key);
595    let size = layout_policy.measure_dyn(&input, &mut output)?;
596    Ok(MeasuredNodeLayout {
597        size,
598        placements: output.finish(),
599        measured_children: input.take_measured_children(),
600    })
601}
602
603fn measure_with_layout_modifiers(
604    modifiers: &[Arc<dyn LayoutModifierNode>],
605    layout_policy: &dyn LayoutPolicyDyn,
606    layout_ctx: &MeasureLayoutContext<'_, '_>,
607    constraint: &Constraint,
608) -> Result<MeasuredNodeLayout, MeasurementError> {
609    if modifiers.is_empty() {
610        return measure_base_layout(layout_policy, layout_ctx, constraint);
611    }
612
613    struct ModifierChildRunner<'a, 'b> {
614        next: &'b mut dyn FnMut(&Constraint) -> Result<MeasuredNodeLayout, MeasurementError>,
615        last: Option<MeasuredNodeLayout>,
616        _marker: std::marker::PhantomData<&'a ()>,
617    }
618
619    impl LayoutModifierChild for ModifierChildRunner<'_, '_> {
620        fn measure(&mut self, constraint: &Constraint) -> Result<ComputedData, MeasurementError> {
621            let measured = (self.next)(constraint)?;
622            let size = measured.size;
623            self.last = Some(measured);
624            Ok(size)
625        }
626
627        fn place(&mut self, position: PxPosition, output: &mut LayoutOutput<'_>) {
628            let measured = self
629                .last
630                .as_ref()
631                .expect("layout modifier child must be measured before placement");
632            for (instance_key, child_position) in &measured.placements {
633                output.place_instance_key(*instance_key, position + *child_position);
634            }
635        }
636    }
637
638    let head = &modifiers[0];
639    let tail = &modifiers[1..];
640    let mut next = |next_constraint: &Constraint| {
641        measure_with_layout_modifiers(tail, layout_policy, layout_ctx, next_constraint)
642    };
643    let input = LayoutInput::new(
644        layout_ctx.tree,
645        ParentConstraint::new(constraint),
646        layout_ctx.children,
647        layout_ctx.component_node_metadatas,
648        layout_ctx.layout_ctx,
649    );
650    let modifier_input = LayoutModifierInput {
651        layout_input: &input,
652    };
653    let mut output = LayoutOutput::new(layout_ctx.resolve_instance_key);
654    let mut child = ModifierChildRunner {
655        next: &mut next,
656        last: None,
657        _marker: std::marker::PhantomData,
658    };
659    let size = head.measure(&modifier_input, &mut child, &mut output)?.size;
660    if let Some(measured) = child.last.as_ref() {
661        input.extend_measured_children(measured.measured_children.clone());
662    }
663    Ok(MeasuredNodeLayout {
664        size,
665        placements: output.finish(),
666        measured_children: input.take_measured_children(),
667    })
668}
669
670fn relayout_base_layout(
671    layout_policy: &dyn LayoutPolicyDyn,
672    tree: &ComponentNodeTree,
673    children: &[NodeId],
674    cached_child_sizes: &[ComputedData],
675    constraint: &Constraint,
676    cached_size: ComputedData,
677    resolve_instance_key: &dyn Fn(NodeId) -> u64,
678) -> Option<Vec<(u64, PxPosition)>> {
679    if cached_child_sizes.len() != children.len() {
680        return None;
681    }
682
683    let child_sizes: HashMap<NodeId, ComputedData> = children
684        .iter()
685        .copied()
686        .zip(cached_child_sizes.iter().copied())
687        .collect();
688    let input = PlacementInput::new(
689        tree,
690        ParentConstraint::new(constraint),
691        children,
692        &child_sizes,
693        cached_size,
694    );
695    let mut output = LayoutOutput::new(resolve_instance_key);
696    if !layout_policy.place_children_dyn(&input, &mut output) {
697        return None;
698    }
699    Some(output.finish())
700}
701
702/// Measures a single node recursively, returning its size or an error.
703///
704/// See [`measure_nodes`] for concurrent measurement of multiple nodes.
705/// Which is very recommended for most cases. You should only use this function
706/// when your're very sure that you only need to measure a single node.
707pub(crate) fn measure_node(
708    node_id: NodeId,
709    parent_constraint: &Constraint,
710    tree: &ComponentNodeTree,
711    component_node_metadatas: &ComponentNodeMetaDatas,
712    layout_ctx: Option<&LayoutContext<'_>>,
713) -> Result<ComputedData, MeasurementError> {
714    let node_data_ref = tree
715        .get(node_id)
716        .ok_or(MeasurementError::NodeNotFoundInTree)?;
717    let node_data = node_data_ref.get();
718    #[cfg(feature = "profiling")]
719    let mut profiler_guard = Some(ProfilerScopeGuard::new(
720        ProfilerPhase::Measure,
721        Some(node_id),
722        node_data_ref.parent(),
723        Some(node_data.fn_name.as_str()),
724    ));
725
726    let children: Vec<_> = node_id.children(tree).collect(); // No .as_ref() needed for &Arena
727    let timer = Instant::now();
728
729    debug!(
730        "Measuring node {} with {} children, parent constraint: {:?}",
731        node_data.fn_name,
732        children.len(),
733        parent_constraint
734    );
735
736    // Ensure thread-local current node context for nested control-flow
737    // instrumentation.
738    let _node_ctx_guard = push_current_node_with_instance_logic_id(
739        node_id,
740        node_data.instance_logic_id,
741        node_data.fn_name.as_str(),
742    );
743    let _instance_ctx_guard = push_current_component_instance_key(node_data.instance_key);
744    let _phase_guard = push_phase(RuntimePhase::Measure);
745
746    let resolve_instance_key = |child_id: NodeId| {
747        if let Some(child) = tree.get(child_id) {
748            child.get().instance_key
749        } else {
750            debug_assert!(
751                false,
752                "Child node must exist when resolving layout placements"
753            );
754            0
755        }
756    };
757    let layout_policy = &node_data.layout_policy;
758    let layout_modifiers = node_data.modifier.layout_nodes();
759    if let Some(layout_ctx) = layout_ctx {
760        layout_ctx.diagnostics.inc_measure_node_calls();
761        if let Some(entry) = layout_ctx.snapshots.get(&node_data.instance_key) {
762            let same_constraint = entry.constraint_key == *parent_constraint;
763            let node_self_measure_dirty = layout_ctx
764                .measure_self_nodes
765                .contains(&node_data.instance_key);
766            let node_self_placement_dirty = layout_ctx
767                .placement_self_nodes
768                .contains(&node_data.instance_key);
769            let node_effective_dirty = layout_ctx
770                .dirty_effective_nodes
771                .contains(&node_data.instance_key);
772            let has_all_child_constraints = entry.child_constraints.len() == children.len();
773            let has_all_child_sizes = entry.child_sizes.len() == children.len();
774            let can_try_reuse = same_constraint
775                && !node_self_measure_dirty
776                && has_all_child_constraints
777                && (!node_effective_dirty || has_all_child_sizes);
778            if can_try_reuse {
779                let cached_result = entry.layout_result.clone();
780                let cached_child_constraints = entry.child_constraints.clone();
781                let cached_child_sizes = entry.child_sizes.clone();
782                drop(entry);
783
784                if !node_effective_dirty
785                    && restore_cached_subtree_metadata(
786                        node_id,
787                        None,
788                        tree,
789                        component_node_metadatas,
790                        layout_ctx.snapshots,
791                    )
792                {
793                    apply_layout_placements(
794                        &cached_result.placements,
795                        tree,
796                        &children,
797                        component_node_metadatas,
798                    );
799                    layout_ctx.diagnostics.inc_cache_hit_direct();
800                    return Ok(cached_result.size);
801                }
802
803                let dirty_children: Vec<(usize, NodeId, Constraint)> = children
804                    .iter()
805                    .enumerate()
806                    .filter_map(|(index, child_id)| {
807                        tree.get(*child_id).and_then(|child| {
808                            if layout_ctx
809                                .dirty_effective_nodes
810                                .contains(&child.get().instance_key)
811                            {
812                                Some((index, *child_id, cached_child_constraints[index]))
813                            } else {
814                                None
815                            }
816                        })
817                    })
818                    .collect();
819
820                let nodes_to_measure = dirty_children
821                    .iter()
822                    .map(|(_, child_id, constraint)| (*child_id, *constraint))
823                    .collect();
824                let measured = measure_nodes(
825                    nodes_to_measure,
826                    tree,
827                    component_node_metadatas,
828                    Some(layout_ctx),
829                );
830                let mut child_size_changed = false;
831                for (index, child_id, _constraint) in &dirty_children {
832                    let Some(result) = measured.get(child_id) else {
833                        layout_ctx.diagnostics.inc_cache_miss_no_entry();
834                        return Err(MeasurementError::NodeNotFoundInTree);
835                    };
836                    match result {
837                        Ok(size) => {
838                            if *size != cached_child_sizes[*index] {
839                                child_size_changed = true;
840                                break;
841                            }
842                        }
843                        Err(err) => return Err(err.clone()),
844                    }
845                }
846                if child_size_changed {
847                    layout_ctx.diagnostics.inc_cache_miss_child_size();
848                } else {
849                    let mut restored = true;
850                    for child_id in &children {
851                        let Some(child) = tree.get(*child_id) else {
852                            restored = false;
853                            break;
854                        };
855                        if layout_ctx
856                            .dirty_effective_nodes
857                            .contains(&child.get().instance_key)
858                        {
859                            continue;
860                        }
861                        if !restore_cached_subtree_metadata(
862                            *child_id,
863                            None,
864                            tree,
865                            component_node_metadatas,
866                            layout_ctx.snapshots,
867                        ) {
868                            restored = false;
869                            break;
870                        }
871                    }
872                    if restored {
873                        reset_frame_metadata(node_id, component_node_metadatas);
874                        let placements = if node_self_placement_dirty {
875                            match relayout_base_layout(
876                                layout_policy.as_ref(),
877                                tree,
878                                &children,
879                                &cached_child_sizes,
880                                parent_constraint,
881                                cached_result.size,
882                                &resolve_instance_key,
883                            ) {
884                                Some(placements) => placements,
885                                None => {
886                                    layout_ctx.diagnostics.inc_cache_miss_dirty_self();
887                                    // This node can reuse measurement but not placements; fall back
888                                    // to a full measure+place pass below.
889                                    Vec::new()
890                                }
891                            }
892                        } else {
893                            cached_result.placements.clone()
894                        };
895                        if node_self_placement_dirty && placements.is_empty() {
896                            // Fall through to the full measure path below.
897                        } else {
898                            apply_layout_placements(
899                                &placements,
900                                tree,
901                                &children,
902                                component_node_metadatas,
903                            );
904                            if let Some(mut metadata) = component_node_metadatas.get_mut(&node_id) {
905                                metadata.computed_data = Some(cached_result.size);
906                                metadata.layout_cache_hit = true;
907                            }
908                            if node_self_placement_dirty {
909                                layout_ctx.snapshots.insert(
910                                    node_data.instance_key,
911                                    LayoutSnapshotEntry {
912                                        constraint_key: *parent_constraint,
913                                        layout_result: LayoutResult {
914                                            size: cached_result.size,
915                                            placements,
916                                        },
917                                        child_constraints: cached_child_constraints,
918                                        child_sizes: cached_child_sizes,
919                                    },
920                                );
921                            }
922                            layout_ctx.diagnostics.inc_cache_hit_boundary();
923                            return Ok(cached_result.size);
924                        }
925                    }
926                }
927            }
928            if !same_constraint {
929                layout_ctx.diagnostics.inc_cache_miss_constraint();
930            } else if node_self_measure_dirty || node_self_placement_dirty {
931                layout_ctx.diagnostics.inc_cache_miss_dirty_self();
932            } else if node_effective_dirty {
933                layout_ctx.diagnostics.inc_cache_miss_child_size();
934            } else {
935                layout_ctx.diagnostics.inc_cache_miss_no_entry();
936            }
937        } else {
938            layout_ctx.diagnostics.inc_cache_miss_no_entry();
939        }
940    }
941
942    reset_frame_metadata(node_id, component_node_metadatas);
943    let measure_layout_ctx = MeasureLayoutContext {
944        tree,
945        children: &children,
946        component_node_metadatas,
947        layout_ctx,
948        resolve_instance_key: &resolve_instance_key,
949    };
950    let measured = measure_with_layout_modifiers(
951        &layout_modifiers,
952        layout_policy.as_ref(),
953        &measure_layout_ctx,
954        parent_constraint,
955    )?;
956    let size = measured.size;
957    let measured_children = measured.measured_children;
958    let placements = measured.placements;
959    apply_layout_placements(&placements, tree, &children, component_node_metadatas);
960
961    component_node_metadatas
962        .entry(node_id)
963        .or_default()
964        .computed_data = Some(size);
965
966    #[cfg(feature = "profiling")]
967    if let Some(guard) = &mut profiler_guard {
968        guard.set_computed_size(size.width.0, size.height.0);
969    }
970
971    if let Some(layout_ctx) = layout_ctx {
972        let mut cacheable = true;
973        let mut child_constraints = Vec::with_capacity(children.len());
974        let mut child_sizes = Vec::with_capacity(children.len());
975        for child_id in &children {
976            let Some(measurement) = measured_children.get(child_id) else {
977                cacheable = false;
978                break;
979            };
980            if !measurement.consistent {
981                cacheable = false;
982                break;
983            }
984            child_constraints.push(measurement.constraint);
985            child_sizes.push(measurement.size);
986        }
987        if cacheable {
988            let layout_result = LayoutResult { size, placements };
989            layout_ctx.snapshots.insert(
990                node_data.instance_key,
991                LayoutSnapshotEntry {
992                    constraint_key: *parent_constraint,
993                    layout_result,
994                    child_constraints,
995                    child_sizes,
996                },
997            );
998            layout_ctx.diagnostics.inc_cache_store_count();
999        } else {
1000            layout_ctx.snapshots.remove(&node_data.instance_key);
1001            layout_ctx.diagnostics.inc_cache_drop_non_cacheable_count();
1002        }
1003    }
1004
1005    debug!(
1006        "Measured node {} in {:?} with size {:?}",
1007        node_data.fn_name,
1008        timer.elapsed(),
1009        size
1010    );
1011
1012    #[cfg(feature = "profiling")]
1013    if let Some(guard) = &mut profiler_guard {
1014        guard.set_computed_size(size.width.0, size.height.0);
1015    }
1016
1017    Ok(size)
1018}
1019
1020/// Places a node at the specified relative position within its parent.
1021pub(crate) fn place_node(
1022    node: indextree::NodeId,
1023    rel_position: PxPosition,
1024    placement_order: u64,
1025    component_node_metadatas: &ComponentNodeMetaDatas,
1026) {
1027    let mut metadata = component_node_metadatas.entry(node).or_default();
1028    metadata.rel_position = Some(rel_position);
1029    metadata.placement_order = Some(placement_order);
1030}
1031
1032/// Concurrently measures multiple nodes using Rayon for parallelism.
1033pub(crate) fn measure_nodes(
1034    nodes_to_measure: Vec<(NodeId, Constraint)>,
1035    tree: &ComponentNodeTree,
1036    component_node_metadatas: &ComponentNodeMetaDatas,
1037    layout_ctx: Option<&LayoutContext<'_>>,
1038) -> HashMap<NodeId, Result<ComputedData, MeasurementError>> {
1039    if nodes_to_measure.is_empty() {
1040        return HashMap::new();
1041    }
1042    // metadata must be reseted and initialized for each node to measure.
1043    for (node_id, _) in &nodes_to_measure {
1044        reset_frame_metadata(*node_id, component_node_metadatas);
1045    }
1046    nodes_to_measure
1047        .into_par_iter()
1048        .map(|(node_id, parent_constraint)| {
1049            let result = measure_node(
1050                node_id,
1051                &parent_constraint,
1052                tree,
1053                component_node_metadatas,
1054                layout_ctx,
1055            );
1056            (node_id, result)
1057        })
1058        .collect::<HashMap<NodeId, Result<ComputedData, MeasurementError>>>()
1059}
1060
1061/// Layout information computed at the measure stage, representing the size of a
1062/// node.
1063#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1064pub struct ComputedData {
1065    /// The resolved width of the node in physical pixels.
1066    pub width: Px,
1067    /// The resolved height of the node in physical pixels.
1068    pub height: Px,
1069}
1070
1071impl Add for ComputedData {
1072    type Output = Self;
1073    fn add(self, rhs: Self) -> Self::Output {
1074        Self {
1075            width: self.width + rhs.width,
1076            height: self.height + rhs.height,
1077        }
1078    }
1079}
1080
1081impl AddAssign for ComputedData {
1082    fn add_assign(&mut self, rhs: Self) {
1083        *self = *self + rhs;
1084    }
1085}
1086
1087impl ComputedData {
1088    /// Zero-sized layout data.
1089    pub const ZERO: Self = Self {
1090        width: Px(0),
1091        height: Px(0),
1092    };
1093
1094    /// Calculates a "minimum" size based on a constraint.
1095    /// For Fixed, it's the fixed value. For Wrap/Fill, it's their 'min' if
1096    /// Some, else 0.
1097    pub fn min_from_constraint(constraint: &Constraint) -> Self {
1098        let width = match constraint.width {
1099            DimensionValue::Fixed(w) => w,
1100            DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
1101            DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
1102        };
1103        let height = match constraint.height {
1104            DimensionValue::Fixed(h) => h,
1105            DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
1106            DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
1107        };
1108        Self { width, height }
1109    }
1110
1111    /// Returns the component-wise minimum of two computed sizes.
1112    pub fn min(self, rhs: Self) -> Self {
1113        Self {
1114            width: self.width.min(rhs.width),
1115            height: self.height.min(rhs.height),
1116        }
1117    }
1118
1119    /// Returns the component-wise maximum of two computed sizes.
1120    pub fn max(self, rhs: Self) -> Self {
1121        Self {
1122            width: self.width.max(rhs.width),
1123            height: self.height.max(rhs.height),
1124        }
1125    }
1126}