Skip to main content

tessera_ui/component_tree/
node.rs

1use std::{
2    collections::HashMap,
3    num::NonZeroUsize,
4    ops::{Add, AddAssign, Deref},
5    sync::Arc,
6};
7
8use indextree::NodeId;
9use smallvec::SmallVec;
10use tracing::debug;
11use winit::window::CursorIcon;
12
13use crate::{
14    ClipRegion, Px,
15    accessibility::{AccessibilityActionHandler, AccessibilityNode, StoredSemantics},
16    cursor::{CursorEventContent, PointerChange},
17    focus::{
18        FocusDirection, FocusRegistration, FocusRequester, FocusRevealRequest, FocusState,
19        FocusTraversalPolicy,
20    },
21    layout::{
22        ChildMeasure, DefaultLayoutNode, LayoutInput, LayoutModifierDyn, LayoutModifierHandle,
23        LayoutResult, NoopDrawModifier, PlacementResult, PlacementScope,
24    },
25    modifier::{
26        DrawModifierNode, LayoutModifierChild, LayoutModifierInput, LayoutModifierNode, Modifier,
27    },
28    prop::CallbackWith,
29    px::{PxPosition, PxRect, PxSize},
30    render_graph::RenderFragment,
31    runtime::{
32        RuntimePhase, push_current_component_instance_key,
33        push_current_node_with_instance_logic_id, push_measured_node_instance_key, push_phase,
34    },
35    time::Instant,
36};
37
38use super::{
39    DrawLayoutBounds, LayoutContext, LayoutSnapshotEntry, NodeDrawLayout,
40    constraint::{Constraint, ParentConstraint},
41    nearest_replay_boundary_instance_key,
42    node_map::DenseNodeMap,
43};
44
45#[cfg(feature = "profiling")]
46use crate::profiler::{Phase as ProfilerPhase, ScopeGuard as ProfilerScopeGuard};
47
48/// A ComponentNode is a node in the component tree.
49/// It represents all information about a component.
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub(crate) enum NodeRole {
52    Composition,
53    Layout,
54}
55
56pub(crate) struct ComponentNode {
57    /// Component function's name, for debugging purposes.
58    pub(crate) fn_name: &'static str,
59    /// Whether this tree node represents a composition boundary or an explicit
60    /// layout node.
61    pub(crate) role: NodeRole,
62    pub(super) layout_children: Option<LayoutChildren>,
63    pub(super) layout_children_dirty: bool,
64    pub(crate) sibling_index: Option<NonZeroUsize>,
65    #[cfg(feature = "profiling")]
66    pub(crate) subtree_len: usize,
67    pub(crate) children_scale: f32,
68    /// Stable logic identifier of this concrete component instance.
69    pub(crate) instance_logic_id: u64,
70    /// Stable instance identifier for this node in the current frame.
71    pub(crate) instance_key: u64,
72    pub(crate) interaction: Option<Box<NodeInteraction>>,
73    pub(crate) layout_coordinate_space: bool,
74    /// Node-local modifier chain attached during build.
75    pub(crate) modifier: Modifier,
76    /// The unified layout node applied at the innermost layer.
77    pub(crate) layout_node: Box<dyn LayoutModifierDyn>,
78    pub(crate) layout_input_policy: LayoutModifierHandle,
79    /// Draw modifier node used to record draw and compute commands.
80    pub(crate) draw_node: Box<dyn DrawModifierNode>,
81    pub(crate) replay_boundary: bool,
82    /// Whether props are equal to the previous frame snapshot.
83    pub(crate) props_unchanged_from_previous: bool,
84}
85
86impl ComponentNode {
87    pub(crate) fn new(fn_name: &'static str, role: NodeRole) -> Self {
88        Self {
89            fn_name,
90            role,
91            layout_children: None,
92            layout_children_dirty: true,
93            sibling_index: None,
94            #[cfg(feature = "profiling")]
95            subtree_len: 1,
96            children_scale: 1.0,
97            instance_logic_id: 0,
98            instance_key: 0,
99            interaction: None,
100            layout_coordinate_space: false,
101            modifier: Modifier::default(),
102            layout_node: Box::new(DefaultLayoutNode),
103            layout_input_policy: LayoutModifierHandle::default(),
104            draw_node: Box::new(NoopDrawModifier),
105            replay_boundary: false,
106            props_unchanged_from_previous: false,
107        }
108    }
109
110    pub(crate) fn interaction(&self) -> &NodeInteraction {
111        static EMPTY: NodeInteraction = NodeInteraction::EMPTY;
112        self.interaction.as_deref().unwrap_or(&EMPTY)
113    }
114
115    pub(crate) fn layout_policy(&self) -> &dyn LayoutModifierDyn {
116        self.layout_input_policy.as_policy()
117    }
118
119    pub(crate) fn interaction_mut(&mut self) -> &mut NodeInteraction {
120        self.interaction
121            .get_or_insert_with(|| Box::new(NodeInteraction::EMPTY))
122    }
123}
124
125pub(crate) struct NodeInteraction {
126    /// Pointer input handlers for capture stage (root to leaf).
127    pub(crate) pointer_preview_handlers: Vec<Box<PointerInputHandlerFn>>,
128    /// Pointer input handlers for bubble stage (leaf to root).
129    pub(crate) pointer_handlers: Vec<Box<PointerInputHandlerFn>>,
130    /// Pointer input handlers for final stage (root to leaf).
131    pub(crate) pointer_final_handlers: Vec<Box<PointerInputHandlerFn>>,
132    /// Keyboard input handlers for preview stage (root to leaf).
133    pub(crate) keyboard_preview_handlers: Vec<Box<KeyboardInputHandlerFn>>,
134    /// Keyboard input handlers for bubble stage (leaf to root).
135    pub(crate) keyboard_handlers: Vec<Box<KeyboardInputHandlerFn>>,
136    /// IME input handlers for preview stage (root to leaf).
137    pub(crate) ime_preview_handlers: Vec<Box<ImeInputHandlerFn>>,
138    /// IME input handlers for bubble stage (leaf to root).
139    pub(crate) ime_handlers: Vec<Box<ImeInputHandlerFn>>,
140    /// Optional focus requester bound to this component node.
141    pub(crate) focus_requester_binding: Option<FocusRequester>,
142    /// Optional focus registration attached to this component node.
143    pub(crate) focus_registration: Option<FocusRegistration>,
144    /// Optional fallback target used when a focus scope restores focus.
145    pub(crate) focus_restorer_fallback: Option<FocusRequester>,
146    /// Optional traversal policy used when directional navigation starts inside
147    /// this focus group or scope.
148    pub(crate) focus_traversal_policy: Option<FocusTraversalPolicy>,
149    /// Optional callback invoked when the node's focus state changes.
150    pub(crate) focus_changed_handler: Option<FocusChangedHandler>,
151    /// Optional callback invoked when the node participates in a focus event.
152    pub(crate) focus_event_handler: Option<FocusEventHandler>,
153    /// Optional callback used to expand virtualized content for focus search.
154    pub(crate) focus_beyond_bounds_handler: Option<FocusBeyondBoundsHandler>,
155    /// Optional callback used to reveal the focused rectangle inside a
156    /// container.
157    pub(crate) focus_reveal_handler: Option<FocusRevealHandler>,
158    /// Callbacks notified when this node's window rectangle changes.
159    pub(crate) globally_positioned_handlers: Vec<GloballyPositionedHandler>,
160}
161
162impl NodeInteraction {
163    const EMPTY: Self = Self {
164        pointer_preview_handlers: Vec::new(),
165        pointer_handlers: Vec::new(),
166        pointer_final_handlers: Vec::new(),
167        keyboard_preview_handlers: Vec::new(),
168        keyboard_handlers: Vec::new(),
169        ime_preview_handlers: Vec::new(),
170        ime_handlers: Vec::new(),
171        focus_requester_binding: None,
172        focus_registration: None,
173        focus_restorer_fallback: None,
174        focus_traversal_policy: None,
175        focus_changed_handler: None,
176        focus_event_handler: None,
177        focus_beyond_bounds_handler: None,
178        focus_reveal_handler: None,
179        globally_positioned_handlers: Vec::new(),
180    };
181
182    pub(super) fn has_pointer_handlers(&self) -> bool {
183        !self.pointer_preview_handlers.is_empty()
184            || !self.pointer_handlers.is_empty()
185            || !self.pointer_final_handlers.is_empty()
186    }
187
188    pub(super) fn observes_input_geometry(&self) -> bool {
189        self.has_pointer_handlers()
190            || !self.keyboard_preview_handlers.is_empty()
191            || !self.keyboard_handlers.is_empty()
192            || !self.ime_preview_handlers.is_empty()
193            || !self.ime_handlers.is_empty()
194            || self.focus_registration.is_some()
195    }
196}
197
198#[derive(Clone)]
199pub(super) struct InputTransforms {
200    pub(super) parent: crate::LocalToWindow,
201    positions: Arc<[PxPosition]>,
202}
203
204impl InputTransforms {
205    pub(super) fn get(&self, index: usize) -> Option<crate::LocalToWindow> {
206        self.positions
207            .get(index)
208            .map(|position| self.parent.with_local_origin(*position))
209    }
210}
211
212/// Contains metadata of the component node.
213pub(crate) struct ComponentNodeMetaData {
214    pub(super) prepared_layout: Option<super::PreparedLayoutMetadata>,
215    pub(super) clip_regions: Option<Box<ClipRegions>>,
216    pub(super) geometry_initialized: bool,
217    pub(super) observes_geometry: bool,
218    pub(super) observes_size: bool,
219    pub(super) paint_dirty: bool,
220    accessibility_written: bool,
221    /// The computed data (size) of the node.
222    /// None if the node is not computed yet.
223    pub computed_data: Option<ComputedData>,
224    pub draw_layout: NodeDrawLayout,
225    /// Whether the most recent visit reused this node's measured layout.
226    pub layout_cache_hit: bool,
227    /// Placement order among siblings within the parent layout result.
228    pub placement_order: Option<u64>,
229    /// The node's start position, relative to its parent.
230    /// None if the node is not placed yet.
231    pub rel_position: Option<PxPosition>,
232    /// The node's start position, relative to the root window.
233    /// Before placement modifiers are applied.
234    /// This will be computed during drawing command's generation.
235    /// None if the node is not drawn yet.
236    pub base_abs_position: Option<PxPosition>,
237    pub local_to_window: Option<crate::LocalToWindow>,
238    pub(super) input_transforms: Option<Box<InputTransforms>>,
239    pub event_clip_bounds: Option<crate::FloatRect>,
240    /// The node's start position, relative to the root window.
241    /// After placement modifiers are applied.
242    /// This will be computed during drawing command's generation.
243    /// None if the node is not drawn yet.
244    pub abs_position: Option<PxPosition>,
245    /// Render fragment associated with this node.
246    ///
247    /// Commands are collected during the record phase and merged into the frame
248    /// graph during rendering.
249    pub(crate) fragment: RenderFragment,
250    /// Whether this node clips its children.
251    pub clips_children: bool,
252    pub(crate) has_shape_clipping: bool,
253    /// Opacity multiplier applied to this node and its descendants.
254    pub opacity: f32,
255    /// Accessibility information for this node.
256    pub accessibility: Option<StoredSemantics>,
257    /// Handler for accessibility actions on this node.
258    pub accessibility_action_handler: Option<AccessibilityActionHandler>,
259}
260
261#[derive(Default)]
262pub(super) struct ClipRegions {
263    local: Option<ClipRegion>,
264    prepared: Option<ClipRegion>,
265}
266
267impl ComponentNodeMetaData {
268    /// Creates a new `ComponentNodeMetaData` with default values.
269    pub fn none() -> Self {
270        Self {
271            prepared_layout: None,
272            clip_regions: None,
273            geometry_initialized: false,
274            observes_geometry: false,
275            observes_size: false,
276            paint_dirty: true,
277            accessibility_written: false,
278            computed_data: None,
279            draw_layout: NodeDrawLayout::default(),
280            layout_cache_hit: false,
281            placement_order: None,
282            rel_position: None,
283            base_abs_position: None,
284            local_to_window: None,
285            input_transforms: None,
286            event_clip_bounds: None,
287            abs_position: None,
288            fragment: RenderFragment::default(),
289            clips_children: false,
290            has_shape_clipping: false,
291            opacity: 1.0,
292            accessibility: None,
293            accessibility_action_handler: None,
294        }
295    }
296
297    pub(crate) fn set_accessibility(
298        &mut self,
299        node: Option<AccessibilityNode>,
300        handler: Option<AccessibilityActionHandler>,
301    ) {
302        if let Some(node) = node {
303            StoredSemantics::write(&mut self.accessibility, node);
304        } else {
305            self.accessibility = None;
306        }
307        self.accessibility_action_handler = handler;
308        self.accessibility_written = true;
309    }
310
311    pub(super) fn begin_accessibility_rebuild(&mut self) {
312        self.accessibility_written = false;
313        self.accessibility_action_handler = None;
314    }
315
316    pub(super) fn finish_accessibility_rebuild(&mut self) {
317        if !self.accessibility_written {
318            self.accessibility = None;
319        }
320    }
321
322    /// Returns a mutable render fragment for this node.
323    pub fn fragment_mut(&mut self) -> &mut RenderFragment {
324        &mut self.fragment
325    }
326
327    pub(crate) fn event_clip_rect(&self) -> Option<PxRect> {
328        self.event_clip_bounds.map(crate::FloatRect::outward)
329    }
330
331    pub(super) fn set_input_transforms(
332        &mut self,
333        parent: crate::LocalToWindow,
334        positions: Vec<PxPosition>,
335    ) {
336        if positions.is_empty() {
337            self.input_transforms = None;
338        } else if let Some(transforms) = &mut self.input_transforms {
339            transforms.parent = parent;
340            if transforms.positions.as_ref() != positions.as_slice() {
341                transforms.positions = positions.into();
342            }
343        } else {
344            self.input_transforms = Some(Box::new(InputTransforms {
345                parent,
346                positions: positions.into(),
347            }));
348        }
349    }
350
351    pub(crate) fn clip_region(&self) -> Option<&ClipRegion> {
352        self.clip_regions.as_ref()?.local.as_ref()
353    }
354
355    pub(crate) fn set_clip_region(&mut self, region: Option<ClipRegion>) {
356        if region.is_some() {
357            self.clip_regions.get_or_insert_with(Box::default).local = region;
358        } else if let Some(regions) = &mut self.clip_regions {
359            regions.local = None;
360        }
361    }
362
363    pub(super) fn prepared_clip_region(&self) -> Option<&ClipRegion> {
364        self.clip_regions.as_ref()?.prepared.as_ref()
365    }
366
367    pub(super) fn set_prepared_clip_region(&mut self, region: Option<ClipRegion>) {
368        if let Some(regions) = &mut self.clip_regions {
369            if regions.prepared != region {
370                regions.prepared = region;
371            }
372            if regions.local.is_none() && region.is_none() {
373                self.clip_regions = None;
374            }
375        }
376    }
377}
378
379impl Default for ComponentNodeMetaData {
380    fn default() -> Self {
381        Self::none()
382    }
383}
384
385pub(crate) fn sibling_index(id: NodeId, tree: &ComponentNodeTree) -> usize {
386    tree[id].get().sibling_index.map_or_else(
387        || id.preceding_siblings(tree).skip(1).count(),
388        |index| index.get() - 1,
389    )
390}
391
392#[derive(Clone)]
393pub(crate) enum LayoutChildren {
394    Empty,
395    One(NodeId),
396    Shared(Arc<[NodeId]>),
397}
398
399impl LayoutChildren {
400    pub(super) fn new(children: &[NodeId]) -> Self {
401        match children {
402            [] => Self::Empty,
403            [child] => Self::One(*child),
404            _ => Self::Shared(children.into()),
405        }
406    }
407}
408
409impl AsRef<[NodeId]> for LayoutChildren {
410    fn as_ref(&self) -> &[NodeId] {
411        self
412    }
413}
414
415impl Deref for LayoutChildren {
416    type Target = [NodeId];
417
418    fn deref(&self) -> &[NodeId] {
419        match self {
420            Self::Empty => &[],
421            Self::One(child) => std::slice::from_ref(child),
422            Self::Shared(children) => children,
423        }
424    }
425}
426
427pub(crate) fn direct_layout_children(node_id: NodeId, tree: &ComponentNodeTree) -> LayoutChildren {
428    if let Some(children) = tree
429        .get(node_id)
430        .and_then(|node| node.get().layout_children.as_ref())
431    {
432        return children.clone();
433    }
434    fn collect(node_id: NodeId, tree: &ComponentNodeTree, output: &mut SmallVec<[NodeId; 4]>) {
435        let Some(node_ref) = tree.get(node_id) else {
436            return;
437        };
438        if node_ref.get().role == NodeRole::Layout {
439            output.push(node_id);
440            return;
441        }
442
443        for child_id in node_id.children(tree) {
444            collect(child_id, tree, output);
445        }
446    }
447
448    let mut children = SmallVec::new();
449    for child_id in node_id.children(tree) {
450        collect(child_id, tree, &mut children);
451    }
452    LayoutChildren::new(&children)
453}
454
455fn reset_frame_metadata(
456    node_id: NodeId,
457    component_node_metadatas: &mut ComponentNodeMetaDatas,
458) -> Option<ComputedData> {
459    component_node_metadatas.mark_paint_dirty(node_id);
460    let metadata = component_node_metadatas.entry_or_default(node_id);
461    metadata.draw_layout = NodeDrawLayout::default();
462    metadata.layout_cache_hit = false;
463    metadata.paint_dirty = true;
464    metadata.computed_data.take()
465}
466
467/// A tree of component nodes, using `indextree::Arena` for storage.
468pub(crate) type ComponentNodeTree = indextree::Arena<ComponentNode>;
469
470/// Stores component metadata for a single UI tree.
471#[derive(Default)]
472pub(crate) struct ComponentNodeMetaDatas {
473    nodes: DenseNodeMap<ComponentNodeMetaData>,
474    #[cfg(feature = "profiling")]
475    pub(super) profiling_dirty: rustc_hash::FxHashSet<NodeId>,
476    pub(super) geometry_visits: u64,
477    pub(super) geometry_dirty: rustc_hash::FxHashSet<NodeId>,
478    pub(super) geometry_changed: rustc_hash::FxHashSet<NodeId>,
479    pub(super) size_changed: rustc_hash::FxHashSet<NodeId>,
480    pub(super) paint_changed: rustc_hash::FxHashSet<NodeId>,
481    pub(super) placement_order_dirty: rustc_hash::FxHashSet<NodeId>,
482    pub(crate) placement_order_changed: rustc_hash::FxHashSet<NodeId>,
483    pub(super) paint_dirty_nodes: rustc_hash::FxHashSet<NodeId>,
484}
485
486impl ComponentNodeMetaDatas {
487    pub(crate) fn new() -> Self {
488        Self::default()
489    }
490
491    pub(crate) fn clear(&mut self) {
492        self.nodes.clear();
493        #[cfg(feature = "profiling")]
494        self.profiling_dirty.clear();
495        self.geometry_dirty.clear();
496        self.geometry_changed.clear();
497        self.size_changed.clear();
498        self.paint_dirty_nodes.clear();
499        self.paint_changed.clear();
500        self.placement_order_dirty.clear();
501        self.placement_order_changed.clear();
502    }
503
504    pub(crate) fn remove(&mut self, node_id: &NodeId) -> Option<ComponentNodeMetaData> {
505        #[cfg(feature = "profiling")]
506        self.profiling_dirty.remove(node_id);
507        self.geometry_dirty.remove(node_id);
508        self.geometry_changed.remove(node_id);
509        self.size_changed.remove(node_id);
510        self.paint_dirty_nodes.remove(node_id);
511        self.paint_changed.remove(node_id);
512        self.placement_order_dirty.remove(node_id);
513        self.placement_order_changed.remove(node_id);
514        self.nodes.remove(*node_id)
515    }
516
517    pub(crate) fn get(&self, node_id: &NodeId) -> Option<&ComponentNodeMetaData> {
518        self.nodes.get(*node_id)
519    }
520
521    pub(crate) fn get_mut(&mut self, node_id: &NodeId) -> Option<&mut ComponentNodeMetaData> {
522        self.nodes.get_mut(*node_id)
523    }
524
525    pub(crate) fn entry_or_default(&mut self, node_id: NodeId) -> &mut ComponentNodeMetaData {
526        if let Some(previous) = self.nodes.id_at_slot(node_id) {
527            if previous == node_id {
528                return self.nodes.get_mut(node_id).expect("occupied node slot");
529            }
530            self.remove(&previous);
531        }
532        self.geometry_dirty.insert(node_id);
533        self.paint_dirty_nodes.insert(node_id);
534        self.nodes.insert(node_id, ComponentNodeMetaData::default())
535    }
536
537    pub(crate) fn mark_geometry_dirty(&mut self, node_id: NodeId) {
538        self.geometry_dirty.insert(node_id);
539    }
540
541    pub(crate) fn mark_paint_dirty(&mut self, node_id: NodeId) {
542        self.paint_dirty_nodes.insert(node_id);
543        if let Some(metadata) = self.get_mut(&node_id) {
544            metadata.paint_dirty = true;
545        }
546    }
547}
548
549/// Represents errors that can occur during node measurement.
550#[derive(Debug, Clone, PartialEq)]
551pub enum MeasurementError {
552    /// Indicates that the specified node was not found in the component tree.
553    NodeNotFoundInTree,
554    /// Indicates that metadata for the specified node was not found (currently
555    /// not a primary error source in measure_node).
556    NodeNotFoundInMeta,
557    /// Indicates that the layout policy for a node failed. Contains a string
558    /// detailing the failure.
559    MeasureFnFailed(String),
560    /// Indicates that the measurement of a child node failed during a
561    /// parent's layout calculation. Contains the `NodeId` of the child
562    /// that failed.
563    ChildMeasurementFailed(NodeId),
564}
565
566/// Pointer input dispatch pass.
567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
568pub enum PointerEventPass {
569    /// Dispatch from root to leaf before the main pointer pass.
570    Initial,
571    /// Main pointer dispatch from leaf to root.
572    Main,
573    /// Final pointer dispatch from root to leaf.
574    Final,
575}
576
577/// Pointer-specific input handler.
578pub type PointerInputHandlerFn = dyn Fn(PointerInput) + Send + Sync;
579/// Keyboard-specific input handler.
580pub type KeyboardInputHandlerFn = dyn Fn(KeyboardInput) + Send + Sync;
581/// IME-specific input handler.
582pub type ImeInputHandlerFn = dyn Fn(ImeInput) + Send + Sync;
583/// Focus-changed callback attached to a component node.
584pub type FocusChangedHandler = CallbackWith<FocusState>;
585/// Focus-event callback attached to a component node.
586pub type FocusEventHandler = CallbackWith<FocusState>;
587/// Beyond-bounds focus callback attached to a virtualized container node.
588pub type FocusBeyondBoundsHandler = CallbackWith<FocusDirection, bool>;
589/// Reveal callback attached to a scrollable container node.
590pub type FocusRevealHandler = CallbackWith<FocusRevealRequest, bool>;
591/// Callback invoked after a layout's observed geometry changes.
592#[derive(Clone, Copy, PartialEq)]
593pub enum GloballyPositionedHandler {
594    Pixels(CallbackWith<PxRect>),
595    Precise(CallbackWith<crate::FloatRect>),
596    Transform(CallbackWith<crate::LocalToWindow>),
597    Coordinates(CallbackWith<crate::LayoutCoordinates>),
598    /// Observes the window-space rectangle remaining after rectangular
599    /// clipping.
600    Visible(CallbackWith<Option<crate::FloatRect>>),
601    /// Observes bounds in the closest declared layout coordinate space.
602    InSpace(CallbackWith<crate::FloatRect>),
603    /// Observes measured dimensions in local layout units.
604    Size(CallbackWith<PxSize>),
605}
606
607#[derive(Clone, Copy)]
608pub(crate) struct GeometryObservation {
609    pub rounded: PxRect,
610    pub precise: crate::FloatRect,
611    pub transform: crate::LocalToWindow,
612    pub coordinates: crate::LayoutCoordinates,
613    pub in_space: crate::FloatRect,
614    pub visible: Option<crate::FloatRect>,
615}
616
617impl GloballyPositionedHandler {
618    pub(crate) fn changed(self, previous: GeometryObservation, next: GeometryObservation) -> bool {
619        match self {
620            Self::Pixels(_) => previous.rounded != next.rounded,
621            Self::Precise(_) => previous.precise != next.precise,
622            Self::Transform(_) => previous.transform != next.transform,
623            Self::Coordinates(_) => previous.coordinates != next.coordinates,
624            Self::Visible(_) => previous.visible != next.visible,
625            Self::InSpace(_) => previous.in_space != next.in_space,
626            Self::Size(_) => previous.coordinates.local_size != next.coordinates.local_size,
627        }
628    }
629
630    pub(crate) fn call(self, geometry: GeometryObservation) {
631        match self {
632            Self::Pixels(handler) => handler.call(geometry.rounded),
633            Self::Precise(handler) => handler.call(geometry.precise),
634            Self::Transform(handler) => handler.call(geometry.transform),
635            Self::Coordinates(handler) => handler.call(geometry.coordinates),
636            Self::Visible(handler) => handler.call(geometry.visible),
637            Self::InSpace(handler) => handler.call(geometry.in_space),
638            Self::Size(handler) => handler.call(geometry.coordinates.local_size),
639        }
640    }
641}
642
643/// Input for pointer handlers.
644///
645/// This type carries pointer event payload, local geometry, and event
646/// consumption helpers. Side effects that are not pointer-specific should use
647/// dedicated APIs such as semantics modifiers, hover cursor modifiers, window
648/// action helpers, or the IME session bridge instead of a generic request bag.
649pub struct PointerInput<'a> {
650    /// Maps this handler's local geometry and displacement units into the
651    /// window.
652    pub local_to_window: crate::LocalToWindow,
653    /// Current pointer dispatch pass.
654    pub pass: PointerEventPass,
655    /// The size of the component node, computed during the measure stage.
656    pub computed_data: ComputedData,
657    /// The position of the cursor, if available.
658    /// Relative to the root position of the component.
659    pub cursor_position_rel: Option<PxPosition>,
660    /// Absolute cursor position in window coordinates.
661    pub(crate) cursor_position_abs: &'a mut Option<PxPosition>,
662    /// The current pointer sample, or an empty list during hover updates.
663    pub pointer_changes: &'a mut Vec<PointerChange>,
664    /// The current state of the keyboard modifiers at the time of the event.
665    pub key_modifiers: winit::keyboard::ModifiersState,
666    pub(crate) ime_request: &'a mut Option<ImeRequest>,
667    pub(crate) request_window_drag: &'a mut Option<WindowDragRequest>,
668}
669
670impl PointerInput<'_> {
671    /// Converts a displacement observed in window coordinates into local units.
672    pub fn local_delta(&self, dx: f32, dy: f32) -> (f32, f32) {
673        let delta = self
674            .local_to_window
675            .inverse_vector(PxPosition::from_f32_xy(dx, dy));
676        (delta.x, delta.y)
677    }
678
679    /// Marks all current pointer changes as consumed.
680    pub fn consume_pointer_changes(&mut self) {
681        for change in self.pointer_changes.iter_mut() {
682            if matches!(change.content, CursorEventContent::Scroll(_)) {
683                continue;
684            }
685            change.consume();
686        }
687    }
688
689    /// Returns whether any current pointer change is an unconsumed release.
690    pub fn has_unconsumed_release(&self) -> bool {
691        self.pointer_changes.iter().any(|change| {
692            !change.is_consumed() && matches!(change.content, CursorEventContent::Released(_))
693        })
694    }
695
696    /// Returns the absolute cursor position in window coordinates.
697    pub fn cursor_position_abs(&self) -> Option<PxPosition> {
698        *self.cursor_position_abs
699    }
700
701    /// Blocks pointer input to other components.
702    ///
703    /// Scroll events are never consumed, so the cursor is only hidden from
704    /// ancestors when this dispatch carries non-scroll pointer changes.
705    /// This keeps wheel events reaching ancestor scroll containers.
706    pub fn block_cursor(&mut self) {
707        let blocks_non_scroll = self.pointer_changes.iter().any(|change| {
708            !change.is_consumed() && !matches!(change.content, CursorEventContent::Scroll(_))
709        });
710        if blocks_non_scroll {
711            self.cursor_position_abs.take();
712        }
713        self.consume_pointer_changes();
714    }
715
716    /// Blocks pointer changes to other components.
717    pub fn block_all(&mut self) {
718        self.block_cursor();
719    }
720
721    /// Begins a system drag move for the current window.
722    pub fn drag_window(&mut self) {
723        *self.request_window_drag = Some(WindowDragRequest {
724            #[cfg(target_os = "macos")]
725            mouse_press: self.pointer_changes.iter().find_map(|change| {
726                (change.pointer_id == crate::cursor::MOUSE_POINTER_ID
727                    && matches!(
728                        change.content,
729                        CursorEventContent::Pressed(crate::cursor::PressKeyEventType::Left)
730                    ))
731                .then_some(change.timestamp)
732            }),
733        });
734    }
735
736    /// Returns the IME session bridge for the current frame.
737    pub fn ime_session(&mut self) -> ImeSession<'_> {
738        ImeSession {
739            request: self.ime_request,
740        }
741    }
742}
743
744/// Input for keyboard handlers.
745///
746/// This type carries keyboard event payload and event consumption helpers.
747/// IME updates, semantics, and other side effects are exposed through narrower
748/// dedicated APIs rather than a general-purpose request sink.
749pub struct KeyboardInput<'a> {
750    /// The size of the component node, computed during the measure stage.
751    pub computed_data: ComputedData,
752    /// Keyboard events from the event loop, if any.
753    pub keyboard_events: &'a mut Vec<crate::KeyboardEvent>,
754    /// The current state of the keyboard modifiers at the time of the event.
755    pub key_modifiers: winit::keyboard::ModifiersState,
756    pub(crate) ime_request: &'a mut Option<ImeRequest>,
757}
758
759impl KeyboardInput<'_> {
760    /// Blocks keyboard events to other components.
761    pub fn block_keyboard(&mut self) {
762        self.keyboard_events.clear();
763    }
764
765    /// Returns the IME session bridge for the current frame.
766    pub fn ime_session(&mut self) -> ImeSession<'_> {
767        ImeSession {
768            request: self.ime_request,
769        }
770    }
771}
772
773/// Input for IME handlers.
774///
775/// This type carries IME event payload and event consumption helpers. IME
776/// snapshot publication is performed through [`ImeSession`].
777pub struct ImeInput<'a> {
778    /// The size of the component node, computed during the measure stage.
779    pub computed_data: ComputedData,
780    /// IME events from the event loop, if any.
781    pub ime_events: &'a mut Vec<winit::event::Ime>,
782    pub(crate) ime_request: &'a mut Option<ImeRequest>,
783}
784
785impl ImeInput<'_> {
786    /// Blocks IME events to other components.
787    pub fn block_ime(&mut self) {
788        self.ime_events.clear();
789    }
790
791    /// Returns the IME session bridge for the current frame.
792    pub fn ime_session(&mut self) -> ImeSession<'_> {
793        ImeSession {
794            request: self.ime_request,
795        }
796    }
797}
798
799/// A collection of requests that components can make to the windowing system
800/// for the current frame. This struct's lifecycle is confined to a single
801/// `compute` pass.
802#[derive(Default, Debug)]
803pub(crate) struct WindowRequests {
804    /// The cursor icon requested by a component. If multiple components request
805    /// a cursor, the last one to make a request in a frame "wins", since
806    /// it's executed later.
807    pub cursor_icon: CursorIcon,
808    /// An Input Method Editor (IME) request.
809    /// If multiple components request IME, the one from the "newer" component
810    /// (which is processed later in the state handling pass) will overwrite
811    /// previous requests.
812    pub ime_request: Option<ImeRequest>,
813    pub request_window_drag: Option<WindowDragRequest>,
814}
815
816#[derive(Clone, Copy, Debug)]
817pub(crate) struct WindowDragRequest {
818    #[cfg(target_os = "macos")]
819    pub mouse_press: Option<Instant>,
820}
821
822impl WindowRequests {
823    pub(crate) fn settle(&mut self, mut next: Self) {
824        next.request_window_drag = next.request_window_drag.or(self.request_window_drag);
825        if let (Some(previous), Some(request)) = (&self.ime_request, &mut next.ime_request) {
826            request.show_keyboard |=
827                previous.show_keyboard && previous.client_id == request.client_id;
828            if previous.client_id == request.client_id && previous.text_state == request.text_state
829            {
830                request.change_cause = previous.change_cause;
831            }
832        }
833        *self = next;
834    }
835}
836
837/// Publishes the focused editor's text, selection and input configuration.
838pub struct ImeSession<'a> {
839    request: &'a mut Option<ImeRequest>,
840}
841
842impl ImeSession<'_> {
843    /// Publishes the IME snapshot for the current frame.
844    pub fn update(&mut self, mut request: ImeRequest) {
845        request.show_keyboard |= self
846            .request
847            .as_ref()
848            .is_some_and(|request| request.show_keyboard);
849        *self.request = Some(request);
850    }
851
852    /// Opens the software keyboard and publishes the current editor snapshot.
853    pub fn show(&mut self, mut request: ImeRequest) {
854        request.show_keyboard = true;
855        self.update(request);
856    }
857
858    /// Clears the IME snapshot for the current frame.
859    pub fn clear(&mut self) {
860        *self.request = None;
861    }
862}
863
864/// A request to the windowing system to open an Input Method Editor (IME).
865/// This is typically used for text input components.
866#[derive(Clone, Debug, PartialEq)]
867pub struct ImeRequest {
868    /// The size of the area where the IME is requested.
869    pub size: PxSize,
870    /// The position of the IME anchor relative to the requesting node.
871    pub local_position: PxPosition,
872    /// The complete text snapshot, including directional selection and composition.
873    pub text_state: winit::event::ImeTextState,
874    /// The input purpose and return-key action.
875    pub options: winit::window::ImeOptions,
876    pub(crate) change_cause: winit::event::ImeChangeCause,
877    pub(crate) show_keyboard: bool,
878    pub(crate) client_id: Option<crate::focus::FocusHandleId>,
879    pub(crate) position: Option<PxPosition>,
880}
881
882impl ImeRequest {
883    /// Creates a new IME request with the target input area size.
884    pub fn new(size: PxSize) -> Self {
885        Self {
886            size,
887            local_position: PxPosition::ZERO,
888            text_state: winit::event::ImeTextState::default(),
889            options: winit::window::ImeOptions::default(),
890            change_cause: winit::event::ImeChangeCause::Other,
891            show_keyboard: false,
892            client_id: None,
893            position: None,
894        }
895    }
896
897    /// Sets the position of the IME anchor relative to the requesting node.
898    pub fn with_local_position(mut self, local_position: PxPosition) -> Self {
899        self.local_position = local_position;
900        self
901    }
902
903    /// Sets the complete editable text snapshot using UTF-8 byte offsets.
904    pub fn with_text_state(mut self, text_state: winit::event::ImeTextState) -> Self {
905        self.text_state = text_state;
906        self
907    }
908
909    /// Sets the input purpose and return-key action.
910    pub fn with_options(mut self, options: winit::window::ImeOptions) -> Self {
911        self.options = options;
912        self
913    }
914}
915
916fn apply_layout_placements(
917    placements: &[(u64, PxPosition)],
918    tree: &ComponentNodeTree,
919    children: &[NodeId],
920    component_node_metadatas: &mut ComponentNodeMetaDatas,
921) {
922    let positions: HashMap<_, _> = placements
923        .iter()
924        .enumerate()
925        .map(|(order, (key, position))| (*key, (*position, order as u64)))
926        .collect();
927    for child_id in children {
928        let placement = tree
929            .get(*child_id)
930            .and_then(|child| positions.get(&child.get().instance_key))
931            .copied();
932        if let Some((position, order)) = placement {
933            place_node(*child_id, position, order, component_node_metadatas);
934            continue;
935        }
936        let metadata = component_node_metadatas.entry_or_default(*child_id);
937        let changed = metadata.rel_position.is_some();
938        metadata.rel_position = None;
939        metadata.placement_order = None;
940        if changed {
941            component_node_metadatas.mark_geometry_dirty(*child_id);
942        }
943    }
944}
945
946#[derive(Clone, Default)]
947pub(super) struct CachedLayoutChildren(Option<Arc<CachedLayoutChildList>>);
948
949struct CachedLayoutChildList {
950    entries: Box<[(u64, ChildMeasure)]>,
951    indices: HashMap<u64, usize>,
952}
953
954impl CachedLayoutChildren {
955    fn new(entries: Vec<(u64, ChildMeasure)>) -> Self {
956        if entries.is_empty() {
957            return Self::default();
958        }
959        let indices = if entries.len() <= 4 {
960            HashMap::default()
961        } else {
962            entries
963                .iter()
964                .enumerate()
965                .map(|(index, &(key, _))| (key, index))
966                .collect()
967        };
968        Self(Some(Arc::new(CachedLayoutChildList {
969            entries: entries.into_boxed_slice(),
970            indices,
971        })))
972    }
973
974    fn entries(&self) -> &[(u64, ChildMeasure)] {
975        self.0.as_deref().map_or(&[], |list| &list.entries)
976    }
977
978    fn index_of(&self, key: u64) -> Option<usize> {
979        let list = self.0.as_ref()?;
980        if list.indices.is_empty() {
981            list.entries.iter().position(|&(entry, _)| entry == key)
982        } else {
983            list.indices.get(&key).copied()
984        }
985    }
986}
987
988fn share_placements(placements: &[(u64, PxPosition)]) -> Option<Arc<[(u64, PxPosition)]>> {
989    (!placements.is_empty()).then(|| Arc::from(placements))
990}
991
992#[derive(Clone)]
993struct MeasuredNodeLayout {
994    size: ComputedData,
995    placements: Vec<(u64, PxPosition)>,
996    base_placements: Option<Arc<[(u64, PxPosition)]>>,
997    measured_children: HashMap<NodeId, crate::layout::ChildMeasure>,
998    intrinsic_dependencies: bool,
999    draw_layout: NodeDrawLayout,
1000}
1001
1002struct MeasureLayoutContext<'a, 'ctx> {
1003    tree: &'a ComponentNodeTree,
1004    children: &'a [NodeId],
1005    layout_ctx: Option<&'a LayoutContext<'ctx>>,
1006}
1007
1008fn measure_base_layout(
1009    policy: &dyn LayoutModifierDyn,
1010    layout_ctx: &MeasureLayoutContext<'_, '_>,
1011    metadatas: &mut ComponentNodeMetaDatas,
1012    constraint: &Constraint,
1013) -> Result<MeasuredNodeLayout, MeasurementError> {
1014    let mut input = LayoutInput::new(
1015        layout_ctx.tree,
1016        ParentConstraint::new(constraint),
1017        layout_ctx.children,
1018        layout_ctx.layout_ctx,
1019    );
1020    let layout_result = {
1021        let mut scope = input.measure_scope(metadatas);
1022        policy.measure_dyn(&mut scope)?
1023    };
1024    Ok(MeasuredNodeLayout {
1025        size: layout_result.size,
1026        base_placements: share_placements(&layout_result.placements),
1027        placements: layout_result.placements,
1028        intrinsic_dependencies: input.has_intrinsic_dependencies(),
1029        measured_children: input.take_measured_children(),
1030        draw_layout: NodeDrawLayout {
1031            layers: None,
1032            base_constraint: *constraint,
1033            base_size: layout_result.size,
1034        },
1035    })
1036}
1037
1038fn measure_with_layout_modifiers(
1039    modifiers: &[Arc<dyn LayoutModifierNode>],
1040    layout_ctx: &MeasureLayoutContext<'_, '_>,
1041    constraint: &Constraint,
1042    base: &mut dyn FnMut(&Constraint) -> Result<MeasuredNodeLayout, MeasurementError>,
1043) -> Result<MeasuredNodeLayout, MeasurementError> {
1044    if modifiers.is_empty() {
1045        return base(constraint);
1046    }
1047
1048    struct ModifierChildRunner<'a, 'b> {
1049        next: &'b mut dyn FnMut(&Constraint) -> Result<MeasuredNodeLayout, MeasurementError>,
1050        last: Option<MeasuredNodeLayout>,
1051        placements: Vec<(u64, PxPosition)>,
1052        position: Option<PxPosition>,
1053        _marker: std::marker::PhantomData<&'a ()>,
1054    }
1055
1056    impl LayoutModifierChild for ModifierChildRunner<'_, '_> {
1057        fn measure(&mut self, constraint: &Constraint) -> Result<ComputedData, MeasurementError> {
1058            let measured = (self.next)(constraint)?;
1059            let size = measured.size;
1060            self.last = Some(measured);
1061            self.placements.clear();
1062            self.position = None;
1063            Ok(size)
1064        }
1065
1066        fn place(&mut self, position: PxPosition) {
1067            self.position = Some(position);
1068            let measured = self
1069                .last
1070                .as_ref()
1071                .expect("layout modifier child must be measured before placement");
1072            for (instance_key, child_position) in &measured.placements {
1073                self.placements
1074                    .push((*instance_key, position + *child_position));
1075            }
1076        }
1077    }
1078
1079    let head = &modifiers[0];
1080    let tail = &modifiers[1..];
1081    let mut next = |next_constraint: &Constraint| {
1082        measure_with_layout_modifiers(tail, layout_ctx, next_constraint, base)
1083    };
1084    let mut input = LayoutInput::new(
1085        layout_ctx.tree,
1086        ParentConstraint::new(constraint),
1087        layout_ctx.children,
1088        layout_ctx.layout_ctx,
1089    );
1090    let modifier_input = LayoutModifierInput {
1091        layout_input: &input,
1092    };
1093    let mut child = ModifierChildRunner {
1094        next: &mut next,
1095        last: None,
1096        placements: Vec::new(),
1097        position: None,
1098        _marker: std::marker::PhantomData,
1099    };
1100    let size = head.measure_content(&modifier_input, &mut child)?.size;
1101    if let Some(measured) = child.last.as_ref() {
1102        input.extend_measured_children(measured.measured_children.clone());
1103    }
1104    let mut draw_layout = child
1105        .last
1106        .as_ref()
1107        .map(|measured| measured.draw_layout.clone())
1108        .unwrap_or_default();
1109    let bounds = child
1110        .position
1111        .zip(child.last.as_ref())
1112        .map(|(position, measured)| DrawLayoutBounds {
1113            position,
1114            size: measured.size,
1115        });
1116    let mut layers = draw_layout.layers.as_deref().unwrap_or_default().to_vec();
1117    for layer in &mut layers {
1118        *layer = layer.zip(child.position).map(|(mut layer, position)| {
1119            layer.position = layer.position + position;
1120            layer
1121        });
1122    }
1123    layers.insert(0, bounds);
1124    draw_layout.layers = Some(layers.into());
1125    Ok(MeasuredNodeLayout {
1126        size,
1127        placements: child.placements,
1128        base_placements: child
1129            .last
1130            .as_ref()
1131            .and_then(|measured| measured.base_placements.clone()),
1132        intrinsic_dependencies: input.has_intrinsic_dependencies()
1133            || child
1134                .last
1135                .as_ref()
1136                .is_some_and(|measured| measured.intrinsic_dependencies),
1137        measured_children: input.take_measured_children(),
1138        draw_layout,
1139    })
1140}
1141
1142fn relayout_base_layout(
1143    policy: &dyn LayoutModifierDyn,
1144    modifiers: &[Arc<dyn LayoutModifierNode>],
1145    layout_ctx: &MeasureLayoutContext<'_, '_>,
1146    entry: &LayoutSnapshotEntry,
1147    parent_constraint: &Constraint,
1148    update_placements: bool,
1149) -> Option<MeasuredNodeLayout> {
1150    let _phase = push_phase(RuntimePhase::Place);
1151    let children = layout_ctx.children;
1152    if entry.children.entries().len() != children.len() {
1153        return None;
1154    }
1155
1156    let child_sizes: HashMap<NodeId, ComputedData> = children
1157        .iter()
1158        .copied()
1159        .zip(
1160            entry
1161                .children
1162                .entries()
1163                .iter()
1164                .map(|(_, measure)| measure.size),
1165        )
1166        .collect();
1167    let scope = PlacementScope::new(
1168        layout_ctx.tree,
1169        ParentConstraint::new(&entry.draw_layout.base_constraint),
1170        children,
1171        &child_sizes,
1172        entry.draw_layout.base_size,
1173    );
1174    let placements = if update_placements {
1175        policy
1176            .place_children_dyn(&scope, &PlacementResult::new())?
1177            .into_placements()
1178    } else {
1179        entry
1180            .base_placements
1181            .as_deref()
1182            .unwrap_or_default()
1183            .to_vec()
1184    };
1185    let measured_children = children
1186        .iter()
1187        .copied()
1188        .zip(entry.children.entries().iter().map(|&(_, measure)| measure))
1189        .collect();
1190    let base_layout = MeasuredNodeLayout {
1191        size: entry.draw_layout.base_size,
1192        base_placements: share_placements(&placements),
1193        placements,
1194        measured_children,
1195        intrinsic_dependencies: entry.intrinsic_dependencies,
1196        draw_layout: NodeDrawLayout {
1197            layers: None,
1198            base_constraint: entry.draw_layout.base_constraint,
1199            base_size: entry.draw_layout.base_size,
1200        },
1201    };
1202    // Replay only the wrappers so their content bounds stay synchronized with
1203    // placement changes without measuring the policy or its child subtree.
1204    let mut base = |constraint: &Constraint| {
1205        if *constraint != entry.draw_layout.base_constraint {
1206            return Err(MeasurementError::MeasureFnFailed(
1207                "cached content constraints changed".into(),
1208            ));
1209        }
1210        Ok(base_layout.clone())
1211    };
1212    let measured =
1213        measure_with_layout_modifiers(modifiers, layout_ctx, parent_constraint, &mut base).ok()?;
1214    (measured.size == entry.layout_result.size).then_some(measured)
1215}
1216
1217fn save_measured_snapshot(
1218    instance_key: u64,
1219    constraint: &Constraint,
1220    layout: &MeasureLayoutContext<'_, '_>,
1221    previous: Option<&LayoutSnapshotEntry>,
1222    measured: MeasuredNodeLayout,
1223) {
1224    let Some(layout_ctx) = layout.layout_ctx else {
1225        return;
1226    };
1227    let consumed_insets = layout_ctx.consumed_insets.get();
1228    let child_measurements = layout.children.iter().map(|id| {
1229        let measurement = measured.measured_children.get(id)?;
1230        measurement
1231            .consistent
1232            .then(|| (layout.tree[*id].get().instance_key, *measurement))
1233    });
1234    let unchanged = previous.is_some_and(|previous| {
1235        previous.constraint_key == *constraint
1236            && previous.consumed_insets == consumed_insets
1237            && previous.layout_result.size == measured.size
1238            && previous.layout_result.placements == measured.placements
1239            && previous.base_placements == measured.base_placements
1240            && previous.intrinsic_dependencies == measured.intrinsic_dependencies
1241            && previous.draw_layout == measured.draw_layout
1242            && child_measurements
1243                .clone()
1244                .eq(previous.children.entries().iter().copied().map(Some))
1245    });
1246    if unchanged {
1247        layout_ctx.acknowledge_deferred_invalidation(instance_key);
1248    } else if let Some(children) = child_measurements.collect::<Option<Vec<_>>>() {
1249        layout_ctx.insert_snapshot(
1250            instance_key,
1251            LayoutSnapshotEntry {
1252                constraint_key: *constraint,
1253                consumed_insets,
1254                children: CachedLayoutChildren::new(children),
1255                layout_result: LayoutResult {
1256                    size: measured.size,
1257                    placements: measured.placements,
1258                },
1259                base_placements: measured.base_placements,
1260                intrinsic_dependencies: measured.intrinsic_dependencies,
1261                draw_layout: measured.draw_layout,
1262            },
1263        );
1264        layout_ctx.inc_cache_store_count();
1265    } else {
1266        layout_ctx.remove_snapshot(instance_key);
1267        layout_ctx.inc_cache_drop_non_cacheable_count();
1268    }
1269}
1270
1271/// Measures a single node recursively, returning its size or an error.
1272pub(crate) fn measure_node(
1273    node_id: NodeId,
1274    parent_constraint: &Constraint,
1275    tree: &ComponentNodeTree,
1276    component_node_metadatas: &mut ComponentNodeMetaDatas,
1277    layout_ctx: Option<&LayoutContext<'_>>,
1278) -> Result<ComputedData, MeasurementError> {
1279    let node_data_ref = tree
1280        .get(node_id)
1281        .ok_or(MeasurementError::NodeNotFoundInTree)?;
1282    let node_data = node_data_ref.get();
1283    #[cfg(feature = "profiling")]
1284    let mut profiler_guard = Some(ProfilerScopeGuard::new(
1285        ProfilerPhase::Measure,
1286        Some(node_id),
1287        node_data_ref.parent(),
1288        Some(node_data.fn_name),
1289    ));
1290
1291    #[cfg(feature = "profiling")]
1292    if !layout_ctx.is_some_and(|context| context.intrinsic) {
1293        component_node_metadatas.profiling_dirty.insert(node_id);
1294    }
1295    let children = direct_layout_children(node_id, tree);
1296    let timer = Instant::now();
1297
1298    debug!(
1299        "Measuring node {} with {} children, parent constraint: {:?}",
1300        node_data.fn_name,
1301        children.len(),
1302        parent_constraint
1303    );
1304
1305    let prepare_measurement = || {
1306        let node = push_current_node_with_instance_logic_id(
1307            node_id,
1308            node_data.instance_logic_id,
1309            node_data.fn_name,
1310        );
1311        let instance = push_current_component_instance_key(nearest_replay_boundary_instance_key(
1312            node_id, tree,
1313        ));
1314        let measured = push_measured_node_instance_key(
1315            layout_ctx
1316                .and_then(|context| context.state_reader_instance_key)
1317                .unwrap_or(node_data.instance_key),
1318        );
1319        let phase = push_phase(RuntimePhase::Measure);
1320        (
1321            (phase, measured, instance, node),
1322            node_data.modifier.layout_modifiers(),
1323        )
1324    };
1325    let mut measurement = None;
1326    let layout_node = &node_data.layout_node;
1327    let snapshot = layout_ctx.and_then(|context| context.snapshot(node_data.instance_key));
1328    if let Some(layout_ctx) = layout_ctx {
1329        layout_ctx.inc_measure_node_calls();
1330        if let Some(entry) = snapshot.as_deref() {
1331            let path = layout_ctx.dirty_paths.and_then(|paths| paths.get(&node_id));
1332            let deferred = layout_ctx.deferred_invalidation(node_data.instance_key);
1333            let same_constraint = entry.constraint_key == *parent_constraint;
1334            let node_self_measure_dirty = entry.consumed_insets != layout_ctx.consumed_insets.get()
1335                || path.is_some_and(|path| path.measure)
1336                || deferred.as_ref().is_some_and(|pending| pending.measure);
1337            let node_self_placement_dirty = path.is_some_and(|path| path.placement)
1338                || deferred.as_ref().is_some_and(|pending| pending.placement);
1339            let node_effective_dirty = path.is_some()
1340                || deferred
1341                    .as_ref()
1342                    .is_some_and(|pending| !pending.children.is_empty());
1343            let cached_children = entry.children.entries();
1344            let can_try_reuse = !node_self_measure_dirty
1345                && (!node_effective_dirty || !entry.intrinsic_dependencies)
1346                && cached_children.len() == children.len();
1347            if can_try_reuse {
1348                if same_constraint
1349                    && !node_self_placement_dirty
1350                    && !node_effective_dirty
1351                    && component_node_metadatas
1352                        .get(&node_id)
1353                        .is_some_and(|metadata| {
1354                            metadata.computed_data == Some(entry.layout_result.size)
1355                        })
1356                {
1357                    component_node_metadatas
1358                        .entry_or_default(node_id)
1359                        .layout_cache_hit = true;
1360                    layout_ctx.inc_cache_hit_direct();
1361                    return Ok(entry.layout_result.size);
1362                }
1363                let dirty_children = path
1364                    .map(|path| path.children.as_slice())
1365                    .into_iter()
1366                    .flatten()
1367                    .filter_map(|&id| {
1368                        layout_ctx.inc_dirty_child_checked();
1369                        let index = entry.children.index_of(tree[id].get().instance_key)?;
1370                        Some((index, id))
1371                    })
1372                    .chain(
1373                        deferred
1374                            .iter()
1375                            .flat_map(|pending| pending.children.iter())
1376                            .filter_map(|key| {
1377                                let index = entry.children.index_of(*key)?;
1378                                let &id = children.get(index)?;
1379                                if tree[id].get().instance_key != *key
1380                                    || layout_ctx
1381                                        .dirty_paths
1382                                        .is_some_and(|paths| paths.contains_key(&id))
1383                                {
1384                                    return None;
1385                                }
1386                                layout_ctx.inc_dirty_child_checked();
1387                                Some((index, id))
1388                            }),
1389                    );
1390
1391                let mut child_size_changed = false;
1392                for (index, child_id) in dirty_children {
1393                    let (_, previous) = cached_children[index];
1394                    let size = layout_ctx.with_consumed_insets(previous.consumed_insets, || {
1395                        measure_node(
1396                            child_id,
1397                            &previous.constraint,
1398                            tree,
1399                            component_node_metadatas,
1400                            Some(layout_ctx),
1401                        )
1402                    })?;
1403                    if size != previous.size {
1404                        child_size_changed = true;
1405                        break;
1406                    }
1407                }
1408                if child_size_changed {
1409                    layout_ctx.inc_cache_miss_child_size();
1410                } else {
1411                    if same_constraint
1412                        && !node_self_placement_dirty
1413                        && component_node_metadatas
1414                            .get(&node_id)
1415                            .is_some_and(|metadata| {
1416                                metadata.computed_data == Some(entry.layout_result.size)
1417                            })
1418                    {
1419                        component_node_metadatas
1420                            .entry_or_default(node_id)
1421                            .layout_cache_hit = true;
1422                        layout_ctx.acknowledge_deferred_invalidation(node_data.instance_key);
1423                        layout_ctx.inc_cache_hit_boundary();
1424                        return Ok(entry.layout_result.size);
1425                    }
1426                    let cached_result = &entry.layout_result;
1427                    let needs_relayout = node_self_placement_dirty || !same_constraint;
1428                    let relayout = if needs_relayout {
1429                        let (_, layout_modifiers) =
1430                            measurement.get_or_insert_with(&prepare_measurement);
1431                        crate::runtime::clear_phase_state_reads(
1432                            node_data.instance_key,
1433                            crate::runtime::ReadPhase::Place,
1434                        );
1435                        relayout_base_layout(
1436                            layout_node.as_ref(),
1437                            layout_modifiers,
1438                            &MeasureLayoutContext {
1439                                tree,
1440                                children: &children,
1441                                layout_ctx: Some(layout_ctx),
1442                            },
1443                            entry,
1444                            parent_constraint,
1445                            node_self_placement_dirty,
1446                        )
1447                    } else {
1448                        None
1449                    };
1450                    if needs_relayout && relayout.is_none() {
1451                        if node_self_placement_dirty {
1452                            layout_ctx.inc_cache_miss_dirty_self();
1453                        }
1454                    } else {
1455                        let (placements, draw_layout, base_placements) = relayout
1456                            .map(|measured| {
1457                                (
1458                                    measured.placements,
1459                                    measured.draw_layout,
1460                                    measured.base_placements,
1461                                )
1462                            })
1463                            .unwrap_or_else(|| {
1464                                (
1465                                    cached_result.placements.clone(),
1466                                    entry.draw_layout.clone(),
1467                                    entry.base_placements.clone(),
1468                                )
1469                            });
1470                        apply_layout_placements(
1471                            &placements,
1472                            tree,
1473                            &children,
1474                            component_node_metadatas,
1475                        );
1476                        if component_node_metadatas
1477                            .get(&node_id)
1478                            .is_some_and(|metadata| metadata.draw_layout != draw_layout)
1479                        {
1480                            component_node_metadatas.mark_paint_dirty(node_id);
1481                        }
1482                        if let Some(metadata) = component_node_metadatas.get_mut(&node_id) {
1483                            metadata.computed_data = Some(cached_result.size);
1484                            metadata.paint_dirty |= metadata.draw_layout != draw_layout;
1485                            metadata.draw_layout = draw_layout.clone();
1486                            metadata.layout_cache_hit = true;
1487                        }
1488                        if needs_relayout
1489                            && (!same_constraint
1490                                || placements != cached_result.placements
1491                                || draw_layout != entry.draw_layout
1492                                || base_placements != entry.base_placements)
1493                        {
1494                            layout_ctx.insert_snapshot(
1495                                node_data.instance_key,
1496                                LayoutSnapshotEntry {
1497                                    constraint_key: *parent_constraint,
1498                                    consumed_insets: layout_ctx.consumed_insets.get(),
1499                                    children: entry.children.clone(),
1500                                    layout_result: LayoutResult {
1501                                        size: cached_result.size,
1502                                        placements,
1503                                    },
1504                                    base_placements,
1505                                    intrinsic_dependencies: entry.intrinsic_dependencies,
1506                                    draw_layout,
1507                                },
1508                            );
1509                            layout_ctx.inc_cache_store_count();
1510                        }
1511                        layout_ctx.acknowledge_deferred_invalidation(node_data.instance_key);
1512                        layout_ctx.inc_cache_hit_boundary();
1513                        return Ok(cached_result.size);
1514                    }
1515                }
1516            }
1517            if !same_constraint {
1518                layout_ctx.inc_cache_miss_constraint();
1519            } else if node_self_measure_dirty || node_self_placement_dirty {
1520                layout_ctx.inc_cache_miss_dirty_self();
1521            } else if node_effective_dirty {
1522                layout_ctx.inc_cache_miss_child_size();
1523            } else {
1524                layout_ctx.inc_cache_miss_no_entry();
1525            }
1526        } else {
1527            layout_ctx.inc_cache_miss_no_entry();
1528        }
1529    }
1530
1531    let (_, layout_modifiers) = measurement.get_or_insert_with(prepare_measurement);
1532    if !layout_ctx.is_some_and(|context| context.intrinsic) {
1533        crate::runtime::clear_phase_state_reads(
1534            node_data.instance_key,
1535            crate::runtime::ReadPhase::Measure,
1536        );
1537        crate::runtime::clear_phase_state_reads(
1538            node_data.instance_key,
1539            crate::runtime::ReadPhase::Place,
1540        );
1541    }
1542    let previous_size = reset_frame_metadata(node_id, component_node_metadatas);
1543    let measure_layout_ctx = MeasureLayoutContext {
1544        tree,
1545        children: &children,
1546        layout_ctx,
1547    };
1548    let measured = measure_with_layout_modifiers(
1549        layout_modifiers,
1550        &measure_layout_ctx,
1551        parent_constraint,
1552        &mut |constraint| {
1553            measure_base_layout(
1554                layout_node.as_ref(),
1555                &measure_layout_ctx,
1556                component_node_metadatas,
1557                constraint,
1558            )
1559        },
1560    )
1561    .inspect_err(|_| component_node_metadatas.mark_geometry_dirty(node_id))?;
1562    let size = measured.size;
1563    apply_layout_placements(
1564        &measured.placements,
1565        tree,
1566        &children,
1567        component_node_metadatas,
1568    );
1569
1570    if previous_size != Some(size) {
1571        component_node_metadatas.mark_geometry_dirty(node_id);
1572    }
1573    let metadata = component_node_metadatas.entry_or_default(node_id);
1574    metadata.computed_data = Some(size);
1575    metadata.draw_layout = measured.draw_layout.clone();
1576
1577    #[cfg(feature = "profiling")]
1578    if let Some(guard) = &mut profiler_guard {
1579        guard.set_computed_size(size.width.0, size.height.0);
1580    }
1581
1582    save_measured_snapshot(
1583        node_data.instance_key,
1584        parent_constraint,
1585        &measure_layout_ctx,
1586        snapshot.as_deref(),
1587        measured,
1588    );
1589
1590    debug!(
1591        "Measured node {} in {:?} with size {:?}",
1592        node_data.fn_name,
1593        timer.elapsed(),
1594        size
1595    );
1596
1597    #[cfg(feature = "profiling")]
1598    if let Some(guard) = &mut profiler_guard {
1599        guard.set_computed_size(size.width.0, size.height.0);
1600    }
1601
1602    Ok(size)
1603}
1604
1605/// Places a node at the specified relative position within its parent.
1606pub(crate) fn place_node(
1607    node: indextree::NodeId,
1608    rel_position: PxPosition,
1609    placement_order: u64,
1610    component_node_metadatas: &mut ComponentNodeMetaDatas,
1611) {
1612    let metadata = component_node_metadatas.entry_or_default(node);
1613    let changed = metadata.rel_position != Some(rel_position);
1614    let order_changed = metadata.placement_order != Some(placement_order);
1615    metadata.rel_position = Some(rel_position);
1616    metadata.placement_order = Some(placement_order);
1617    if order_changed {
1618        component_node_metadatas.placement_order_dirty.insert(node);
1619        component_node_metadatas.mark_geometry_dirty(node);
1620    }
1621    if changed {
1622        component_node_metadatas.mark_geometry_dirty(node);
1623    }
1624}
1625
1626/// Layout information computed at the measure stage, representing the size of a
1627/// node.
1628#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1629pub struct ComputedData {
1630    /// The resolved width of the node in physical pixels.
1631    pub width: Px,
1632    /// The resolved height of the node in physical pixels.
1633    pub height: Px,
1634}
1635
1636impl Add for ComputedData {
1637    type Output = Self;
1638    fn add(self, rhs: Self) -> Self::Output {
1639        Self {
1640            width: self.width + rhs.width,
1641            height: self.height + rhs.height,
1642        }
1643    }
1644}
1645
1646impl AddAssign for ComputedData {
1647    fn add_assign(&mut self, rhs: Self) {
1648        *self = *self + rhs;
1649    }
1650}
1651
1652impl ComputedData {
1653    /// Zero-sized layout data.
1654    pub const ZERO: Self = Self {
1655        width: Px(0),
1656        height: Px(0),
1657    };
1658
1659    /// Calculates the minimum size guaranteed by a constraint interval.
1660    pub fn min_from_constraint(constraint: &Constraint) -> Self {
1661        let width = constraint.width.min;
1662        let height = constraint.height.min;
1663        Self { width, height }
1664    }
1665
1666    /// Returns the component-wise minimum of two computed sizes.
1667    pub fn min(self, rhs: Self) -> Self {
1668        Self {
1669            width: self.width.min(rhs.width),
1670            height: self.height.min(rhs.height),
1671        }
1672    }
1673
1674    /// Returns the component-wise maximum of two computed sizes.
1675    pub fn max(self, rhs: Self) -> Self {
1676        Self {
1677            width: self.width.max(rhs.width),
1678            height: self.height.max(rhs.height),
1679        }
1680    }
1681}