Skip to main content

cranpose_foundation/
modifier.rs

1//! Modifier node scaffolding for Cranpose.
2//!
3//! This module defines the foundational pieces of the future
4//! `Modifier.Node` system described in the project roadmap. It introduces
5//! traits for modifier nodes and their contexts as well as a light-weight
6//! chain container that can reconcile nodes across updates. The
7//! implementation focuses on the core runtime plumbing so UI crates can
8//! begin migrating without expanding the public API surface.
9
10use std::any::{type_name, Any, TypeId};
11use std::cell::{Cell, RefCell};
12use std::collections::hash_map::DefaultHasher;
13use std::fmt;
14use std::hash::{Hash, Hasher};
15use std::ops::{BitOr, BitOrAssign};
16use std::rc::Rc;
17
18use rustc_hash::FxHashMap;
19use smallvec::SmallVec;
20
21pub use cranpose_ui_graphics::DrawScope;
22pub use cranpose_ui_graphics::Size;
23pub use cranpose_ui_layout::{Constraints, Measurable};
24
25use crate::nodes::input::types::PointerEvent;
26// use cranpose_core::NodeId;
27
28/// Identifies which part of the rendering pipeline should be invalidated
29/// after a modifier node changes state.
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31pub enum InvalidationKind {
32    Layout,
33    Draw,
34    PointerInput,
35    Semantics,
36    Focus,
37}
38
39/// Runtime services exposed to modifier nodes while attached to a tree.
40pub trait ModifierNodeContext {
41    /// Requests that a particular pipeline stage be invalidated.
42    fn invalidate(&mut self, _kind: InvalidationKind) {}
43
44    /// Requests that the node's `update` method run again outside of a
45    /// regular composition pass.
46    fn request_update(&mut self) {}
47
48    /// Returns the ID of the layout node this modifier is attached to, if known.
49    /// This is used by modifiers that need to register callbacks for invalidation (e.g. Scroll).
50    fn node_id(&self) -> Option<cranpose_core::NodeId> {
51        None
52    }
53
54    /// Signals that a node with `capabilities` is about to interact with this context.
55    fn push_active_capabilities(&mut self, _capabilities: NodeCapabilities) {}
56
57    /// Signals that the most recent node interaction has completed.
58    fn pop_active_capabilities(&mut self) {}
59}
60
61/// Lightweight [`ModifierNodeContext`] implementation that records
62/// invalidation requests and update signals.
63///
64/// The context intentionally avoids leaking runtime details so the core
65/// crate can evolve independently from higher level UI crates. It simply
66/// stores the sequence of requested invalidation kinds and whether an
67/// explicit update was requested. Callers can inspect or drain this state
68/// after driving a [`ModifierNodeChain`] reconciliation pass.
69#[derive(Default, Debug, Clone)]
70pub struct BasicModifierNodeContext {
71    invalidations: Vec<ModifierInvalidation>,
72    update_requested: bool,
73    active_capabilities: Vec<NodeCapabilities>,
74    node_id: Option<cranpose_core::NodeId>,
75}
76
77impl BasicModifierNodeContext {
78    /// Creates a new empty context.
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// Returns the ordered list of invalidation kinds that were requested
84    /// since the last call to [`clear_invalidations`]. Duplicate requests for
85    /// the same kind are coalesced.
86    pub fn invalidations(&self) -> &[ModifierInvalidation] {
87        &self.invalidations
88    }
89
90    /// Removes all currently recorded invalidation kinds.
91    pub fn clear_invalidations(&mut self) {
92        self.invalidations.clear();
93    }
94
95    /// Drains the recorded invalidations and returns them to the caller.
96    pub fn take_invalidations(&mut self) -> Vec<ModifierInvalidation> {
97        std::mem::take(&mut self.invalidations)
98    }
99
100    /// Returns whether an update was requested since the last call to
101    /// [`take_update_requested`].
102    pub fn update_requested(&self) -> bool {
103        self.update_requested
104    }
105
106    /// Returns whether an update was requested and clears the flag.
107    pub fn take_update_requested(&mut self) -> bool {
108        std::mem::take(&mut self.update_requested)
109    }
110
111    /// Sets the node ID associated with this context.
112    pub fn set_node_id(&mut self, id: Option<cranpose_core::NodeId>) {
113        self.node_id = id;
114    }
115
116    fn push_invalidation(&mut self, kind: InvalidationKind) {
117        let mut capabilities = self.current_capabilities();
118        capabilities.insert(NodeCapabilities::for_invalidation(kind));
119        if let Some(existing) = self
120            .invalidations
121            .iter_mut()
122            .find(|entry| entry.kind() == kind)
123        {
124            let updated = existing.capabilities() | capabilities;
125            *existing = ModifierInvalidation::new(kind, updated);
126        } else {
127            self.invalidations
128                .push(ModifierInvalidation::new(kind, capabilities));
129        }
130    }
131
132    fn current_capabilities(&self) -> NodeCapabilities {
133        self.active_capabilities
134            .last()
135            .copied()
136            .unwrap_or_else(NodeCapabilities::empty)
137    }
138}
139
140impl ModifierNodeContext for BasicModifierNodeContext {
141    fn invalidate(&mut self, kind: InvalidationKind) {
142        self.push_invalidation(kind);
143    }
144
145    fn request_update(&mut self) {
146        self.update_requested = true;
147    }
148
149    fn push_active_capabilities(&mut self, capabilities: NodeCapabilities) {
150        self.active_capabilities.push(capabilities);
151    }
152
153    fn pop_active_capabilities(&mut self) {
154        self.active_capabilities.pop();
155    }
156
157    fn node_id(&self) -> Option<cranpose_core::NodeId> {
158        self.node_id
159    }
160}
161
162/// Path to a node within a modifier chain, supporting delegate navigation.
163/// Uses SmallVec to avoid heap allocation for paths with 0-2 delegates.
164#[derive(Clone, Debug, PartialEq, Eq)]
165pub(crate) struct NodePath {
166    entry: usize,
167    delegates: SmallVec<[usize; 2]>,
168}
169
170impl NodePath {
171    #[inline]
172    fn root(entry: usize) -> Self {
173        Self {
174            entry,
175            delegates: SmallVec::new(),
176        }
177    }
178
179    #[inline]
180    fn from_slice(entry: usize, path: &[usize]) -> Self {
181        Self {
182            entry,
183            delegates: SmallVec::from_slice(path),
184        }
185    }
186
187    #[inline]
188    fn entry(&self) -> usize {
189        self.entry
190    }
191
192    #[inline]
193    fn delegates(&self) -> &[usize] {
194        &self.delegates
195    }
196}
197
198#[derive(Clone, Debug, PartialEq, Eq)]
199pub(crate) enum NodeLink {
200    Head,
201    Tail,
202    Entry(NodePath),
203}
204
205/// Runtime state tracked for every [`ModifierNode`].
206///
207/// This type is part of the internal node system API and should not be directly
208/// constructed or manipulated by external code. Modifier nodes automatically receive
209/// and manage their NodeState through the modifier chain infrastructure.
210#[derive(Debug)]
211pub struct NodeState {
212    aggregate_child_capabilities: Cell<NodeCapabilities>,
213    capabilities: Cell<NodeCapabilities>,
214    parent: RefCell<Option<NodeLink>>,
215    child: RefCell<Option<NodeLink>>,
216    attached: Cell<bool>,
217    is_sentinel: bool,
218}
219
220impl Default for NodeState {
221    fn default() -> Self {
222        Self::new()
223    }
224}
225
226impl NodeState {
227    pub const fn new() -> Self {
228        Self {
229            aggregate_child_capabilities: Cell::new(NodeCapabilities::empty()),
230            capabilities: Cell::new(NodeCapabilities::empty()),
231            parent: RefCell::new(None),
232            child: RefCell::new(None),
233            attached: Cell::new(false),
234            is_sentinel: false,
235        }
236    }
237
238    pub const fn sentinel() -> Self {
239        Self {
240            aggregate_child_capabilities: Cell::new(NodeCapabilities::empty()),
241            capabilities: Cell::new(NodeCapabilities::empty()),
242            parent: RefCell::new(None),
243            child: RefCell::new(None),
244            attached: Cell::new(true),
245            is_sentinel: true,
246        }
247    }
248
249    pub fn set_capabilities(&self, capabilities: NodeCapabilities) {
250        self.capabilities.set(capabilities);
251    }
252
253    #[inline]
254    pub fn capabilities(&self) -> NodeCapabilities {
255        self.capabilities.get()
256    }
257
258    pub fn set_aggregate_child_capabilities(&self, capabilities: NodeCapabilities) {
259        self.aggregate_child_capabilities.set(capabilities);
260    }
261
262    #[inline]
263    pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
264        self.aggregate_child_capabilities.get()
265    }
266
267    pub(crate) fn set_parent_link(&self, parent: Option<NodeLink>) {
268        *self.parent.borrow_mut() = parent;
269    }
270
271    #[inline]
272    pub(crate) fn parent_link(&self) -> Option<NodeLink> {
273        self.parent.borrow().clone()
274    }
275
276    pub(crate) fn set_child_link(&self, child: Option<NodeLink>) {
277        *self.child.borrow_mut() = child;
278    }
279
280    #[inline]
281    pub(crate) fn child_link(&self) -> Option<NodeLink> {
282        self.child.borrow().clone()
283    }
284
285    pub fn set_attached(&self, attached: bool) {
286        self.attached.set(attached);
287    }
288
289    pub fn is_attached(&self) -> bool {
290        self.attached.get()
291    }
292
293    pub fn is_sentinel(&self) -> bool {
294        self.is_sentinel
295    }
296}
297
298/// Provides traversal helpers that mirror Jetpack Compose's [`DelegatableNode`] contract.
299pub trait DelegatableNode {
300    fn node_state(&self) -> &NodeState;
301    fn aggregate_child_capabilities(&self) -> NodeCapabilities {
302        self.node_state().aggregate_child_capabilities()
303    }
304}
305
306/// Core trait implemented by modifier nodes.
307///
308/// # Capability-Driven Architecture
309///
310/// This trait follows Jetpack Compose's `Modifier.Node` pattern where nodes declare
311/// their capabilities via [`NodeCapabilities`] and implement specialized traits
312/// ([`DrawModifierNode`], [`PointerInputNode`], [`SemanticsNode`], [`FocusNode`], etc.)
313/// to participate in specific pipeline stages.
314///
315/// ## How to Implement a Modifier Node
316///
317/// 1. **Declare capabilities** in your [`ModifierNodeElement::capabilities()`] implementation
318/// 2. **Implement specialized traits** for the capabilities you declared
319/// 3. **Use helper macros** to reduce boilerplate (recommended)
320///
321/// ### Example: Draw Node
322///
323/// ```text
324/// use cranpose_foundation::*;
325///
326/// struct MyDrawNode {
327///     state: NodeState,
328///     color: Color,
329/// }
330///
331/// impl DelegatableNode for MyDrawNode {
332///     fn node_state(&self) -> &NodeState {
333///         &self.state
334///     }
335/// }
336///
337/// impl ModifierNode for MyDrawNode {
338///     // Use the helper macro instead of manual as_* implementations
339///     impl_modifier_node!(draw);
340/// }
341///
342/// impl DrawModifierNode for MyDrawNode {
343///     fn draw(&mut self, _context: &mut dyn ModifierNodeContext, draw_scope: &mut dyn DrawScope) {
344///         // Drawing logic here
345///     }
346/// }
347/// ```
348///
349/// ### Example: Multi-Capability Node
350///
351/// ```text
352/// impl ModifierNode for MyComplexNode {
353///     // This node participates in draw, pointer input, and semantics
354///     impl_modifier_node!(draw, pointer_input, semantics);
355/// }
356/// ```
357///
358/// ## Lifecycle Callbacks
359///
360/// Nodes receive lifecycle callbacks when they attach to or detach from a
361/// composition and may optionally react to resets triggered by the runtime
362/// (for example, when reusing nodes across modifier list changes).
363pub trait ModifierNode: Any + DelegatableNode {
364    fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {}
365
366    fn on_detach(&mut self) {}
367
368    fn on_reset(&mut self) {}
369
370    /// Returns this node as a draw modifier if it implements the trait.
371    fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
372        None
373    }
374
375    /// Returns this node as a mutable draw modifier if it implements the trait.
376    fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
377        None
378    }
379
380    /// Returns this node as a pointer-input modifier if it implements the trait.
381    fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
382        None
383    }
384
385    /// Returns this node as a mutable pointer-input modifier if it implements the trait.
386    fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
387        None
388    }
389
390    /// Returns this node as a semantics modifier if it implements the trait.
391    fn as_semantics_node(&self) -> Option<&dyn SemanticsNode> {
392        None
393    }
394
395    /// Returns this node as a mutable semantics modifier if it implements the trait.
396    fn as_semantics_node_mut(&mut self) -> Option<&mut dyn SemanticsNode> {
397        None
398    }
399
400    /// Returns this node as a focus modifier if it implements the trait.
401    fn as_focus_node(&self) -> Option<&dyn FocusNode> {
402        None
403    }
404
405    /// Returns this node as a mutable focus modifier if it implements the trait.
406    fn as_focus_node_mut(&mut self) -> Option<&mut dyn FocusNode> {
407        None
408    }
409
410    /// Returns this node as a layout modifier if it implements the trait.
411    fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
412        None
413    }
414
415    /// Returns this node as a mutable layout modifier if it implements the trait.
416    fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
417        None
418    }
419
420    /// Visits every delegate node owned by this modifier.
421    fn for_each_delegate<'b>(&'b self, _visitor: &mut dyn FnMut(&'b dyn ModifierNode)) {}
422
423    /// Visits every delegate node mutably.
424    fn for_each_delegate_mut<'b>(&'b mut self, _visitor: &mut dyn FnMut(&'b mut dyn ModifierNode)) {
425    }
426}
427
428/// Marker trait for layout-specific modifier nodes.
429///
430/// Layout nodes participate in the measure and layout passes of the render
431/// pipeline. They can intercept and modify the measurement and placement of
432/// their wrapped content.
433pub trait LayoutModifierNode: ModifierNode {
434    /// Measures the wrapped content and returns both the size this modifier
435    /// occupies and where the wrapped content should be placed.
436    ///
437    /// The node receives a measurable representing the wrapped content and
438    /// the incoming constraints from the parent.
439    ///
440    /// Returns a `LayoutModifierMeasureResult` containing:
441    /// - `size`: The final size this modifier will occupy
442    /// - `placement_offset_x/y`: Where to place the wrapped content relative
443    ///   to this modifier's top-left corner
444    ///
445    /// For example, a padding modifier would:
446    /// - Measure child with deflated constraints
447    /// - Return size = child size + padding
448    /// - Return placement offset = (padding.left, padding.top)
449    ///
450    /// The default implementation delegates to the wrapped content without
451    /// modification (size = child size, offset = 0).
452    ///
453    /// NOTE: This takes `&self` not `&mut self` to match Jetpack Compose semantics.
454    /// Nodes that need mutable state should use interior mutability (Cell/RefCell).
455    fn measure(
456        &self,
457        _context: &mut dyn ModifierNodeContext,
458        measurable: &dyn Measurable,
459        constraints: Constraints,
460    ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
461        // Default: pass through to wrapped content by measuring the child.
462        let placeable = measurable.measure(constraints);
463        cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
464            width: placeable.width(),
465            height: placeable.height(),
466        })
467    }
468
469    /// Returns the minimum intrinsic width of this modifier node.
470    fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
471        0.0
472    }
473
474    /// Returns the maximum intrinsic width of this modifier node.
475    fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
476        0.0
477    }
478
479    /// Returns the minimum intrinsic height of this modifier node.
480    fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
481        0.0
482    }
483
484    /// Returns the maximum intrinsic height of this modifier node.
485    fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
486        0.0
487    }
488
489    /// Creates a measurement proxy for this node that can perform measurement
490    /// without holding a borrow to the modifier chain.
491    ///
492    /// This method enables custom layout modifiers to work with the coordinator
493    /// chain while respecting Rust's borrow checker constraints. The proxy
494    /// should capture a snapshot of the node's current configuration.
495    ///
496    /// The default implementation returns `None`, which causes the coordinator
497    /// to use a passthrough strategy that delegates directly to the wrapped
498    /// content.
499    ///
500    /// # Example
501    ///
502    /// ```text
503    /// impl LayoutModifierNode for MyCustomLayoutNode {
504    ///     fn create_measurement_proxy(&self) -> Option<Box<dyn MeasurementProxy>> {
505    ///         Some(Box::new(MyCustomLayoutProxy {
506    ///             // Snapshot node configuration here
507    ///             padding: self.padding,
508    ///         }))
509    ///     }
510    /// }
511    /// ```
512    fn create_measurement_proxy(
513        &self,
514    ) -> Option<Box<dyn crate::measurement_proxy::MeasurementProxy>> {
515        None
516    }
517}
518
519/// Marker trait for draw-specific modifier nodes.
520///
521/// Draw nodes participate in the draw pass of the render pipeline. They can
522/// intercept and modify the drawing operations of their wrapped content.
523///
524/// Following Jetpack Compose's design, `draw()` is called during the actual
525/// render pass with a live DrawScope, not during layout/slice collection.
526pub trait DrawModifierNode: ModifierNode {
527    /// Draws this modifier node into the provided DrawScope.
528    ///
529    /// This is called during the render pass for each node with DRAW capability.
530    /// The node should draw directly into the scope using methods like
531    /// `draw_scope.draw_rect_at()`.
532    ///
533    /// Takes `&self` to work with immutable chain iteration - use interior
534    /// mutability (RefCell) for any state that needs mutation during draw.
535    fn draw(&self, _draw_scope: &mut dyn DrawScope) {
536        // Default: no custom drawing
537    }
538
539    /// Creates a closure for deferred drawing that will be evaluated at render time.
540    ///
541    /// This is the preferred method for nodes with dynamic content like:
542    /// - Blinking cursors (visibility changes over time)
543    /// - Live selection during drag (selection changes during mouse move)
544    ///
545    /// The returned closure captures the node's internal state (via Rc) and
546    /// evaluates at render time, not at slice collection time.
547    ///
548    /// Returns None by default. Override for nodes needing deferred draw.
549    fn create_draw_closure(
550        &self,
551    ) -> Option<Rc<dyn Fn(Size) -> Vec<cranpose_ui_graphics::DrawPrimitive>>> {
552        None
553    }
554}
555
556/// Marker trait for pointer input modifier nodes.
557///
558/// Pointer input nodes participate in hit-testing and pointer event
559/// dispatch. They can intercept pointer events and handle them before
560/// they reach the wrapped content.
561pub trait PointerInputNode: ModifierNode {
562    /// Called when a pointer event occurs within the bounds of this node.
563    /// Returns true if the event was consumed and should not propagate further.
564    fn on_pointer_event(
565        &mut self,
566        _context: &mut dyn ModifierNodeContext,
567        _event: &PointerEvent,
568    ) -> bool {
569        false
570    }
571
572    /// Returns true if this node should participate in hit-testing for the
573    /// given pointer position.
574    fn hit_test(&self, _x: f32, _y: f32) -> bool {
575        true
576    }
577
578    /// Returns an event handler closure if the node wants to participate in pointer dispatch.
579    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
580        None
581    }
582}
583
584/// Marker trait for semantics modifier nodes.
585///
586/// Semantics nodes participate in the semantics tree construction. They can
587/// add or modify semantic properties of their wrapped content for
588/// accessibility and testing purposes.
589pub trait SemanticsNode: ModifierNode {
590    /// Merges semantic properties into the provided configuration.
591    fn merge_semantics(&self, _config: &mut SemanticsConfiguration) {
592        // Default: no semantics added
593    }
594}
595
596/// Focus state of a focus target node.
597///
598/// This mirrors Jetpack Compose's FocusState enum which tracks whether
599/// a node is focused, has a focused child, or is inactive.
600#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
601pub enum FocusState {
602    /// The focusable component is currently active (i.e. it receives key events).
603    Active,
604    /// One of the descendants of the focusable component is Active.
605    ActiveParent,
606    /// The focusable component is currently active (has focus), and is in a state
607    /// where it does not want to give up focus. (Eg. a text field with an invalid
608    /// phone number).
609    Captured,
610    /// The focusable component does not receive any key events. (ie it is not active,
611    /// nor are any of its descendants active).
612    #[default]
613    Inactive,
614}
615
616impl FocusState {
617    /// Returns whether the component is focused (Active or Captured).
618    pub fn is_focused(self) -> bool {
619        matches!(self, FocusState::Active | FocusState::Captured)
620    }
621
622    /// Returns whether this node or any descendant has focus.
623    pub fn has_focus(self) -> bool {
624        matches!(
625            self,
626            FocusState::Active | FocusState::ActiveParent | FocusState::Captured
627        )
628    }
629
630    /// Returns whether focus is captured.
631    pub fn is_captured(self) -> bool {
632        matches!(self, FocusState::Captured)
633    }
634}
635
636/// Marker trait for focus modifier nodes.
637///
638/// Focus nodes participate in focus management. They can request focus,
639/// track focus state, and participate in focus traversal.
640pub trait FocusNode: ModifierNode {
641    /// Returns the current focus state of this node.
642    fn focus_state(&self) -> FocusState;
643
644    /// Called when focus state changes for this node.
645    fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, _state: FocusState) {
646        // Default: no action on focus change
647    }
648}
649
650/// Semantics configuration for accessibility.
651#[derive(Clone, Debug, Default, PartialEq)]
652pub struct SemanticsConfiguration {
653    pub content_description: Option<String>,
654    pub is_button: bool,
655    pub is_clickable: bool,
656}
657
658impl SemanticsConfiguration {
659    pub fn merge(&mut self, other: &SemanticsConfiguration) {
660        if let Some(description) = &other.content_description {
661            self.content_description = Some(description.clone());
662        }
663        self.is_button |= other.is_button;
664        self.is_clickable |= other.is_clickable;
665    }
666}
667
668impl fmt::Debug for dyn ModifierNode {
669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670        f.debug_struct("ModifierNode").finish_non_exhaustive()
671    }
672}
673
674impl dyn ModifierNode {
675    pub fn as_any(&self) -> &dyn Any {
676        self
677    }
678
679    pub fn as_any_mut(&mut self) -> &mut dyn Any {
680        self
681    }
682}
683
684/// Strongly typed modifier elements that can create and update nodes while
685/// exposing equality/hash/inspector contracts that mirror Jetpack Compose.
686pub trait ModifierNodeElement: fmt::Debug + Hash + PartialEq + 'static {
687    type Node: ModifierNode;
688
689    /// Creates a new modifier node instance for this element.
690    fn create(&self) -> Self::Node;
691
692    /// Brings an existing modifier node up to date with the element's data.
693    fn update(&self, node: &mut Self::Node);
694
695    /// Optional key used to disambiguate multiple instances of the same element type.
696    fn key(&self) -> Option<u64> {
697        None
698    }
699
700    /// Human readable name surfaced to inspector tooling.
701    fn inspector_name(&self) -> &'static str {
702        type_name::<Self>()
703    }
704
705    /// Records inspector properties for tooling.
706    fn inspector_properties(&self, _inspector: &mut dyn FnMut(&'static str, String)) {}
707
708    /// Returns the capabilities of nodes created by this element.
709    /// Override this to indicate which specialized traits the node implements.
710    fn capabilities(&self) -> NodeCapabilities {
711        NodeCapabilities::default()
712    }
713
714    /// Whether this element requires `update` to be called even if `eq` returns true.
715    ///
716    /// This is useful for elements that ignore certain fields in `eq` (e.g. closures)
717    /// to allow node reuse, but still need those fields updated in the existing node.
718    /// Defaults to `false`.
719    fn always_update(&self) -> bool {
720        false
721    }
722}
723
724/// Transitional alias so existing call sites that refer to `ModifierElement`
725/// keep compiling while the ecosystem migrates to `ModifierNodeElement`.
726pub trait ModifierElement: ModifierNodeElement {}
727
728impl<T> ModifierElement for T where T: ModifierNodeElement {}
729
730/// Capability flags indicating which specialized traits a modifier node implements.
731#[derive(Clone, Copy, PartialEq, Eq, Hash)]
732pub struct NodeCapabilities(u32);
733
734impl NodeCapabilities {
735    /// No capabilities.
736    pub const NONE: Self = Self(0);
737    /// Modifier participates in measure/layout.
738    pub const LAYOUT: Self = Self(1 << 0);
739    /// Modifier participates in draw.
740    pub const DRAW: Self = Self(1 << 1);
741    /// Modifier participates in pointer input.
742    pub const POINTER_INPUT: Self = Self(1 << 2);
743    /// Modifier participates in semantics tree construction.
744    pub const SEMANTICS: Self = Self(1 << 3);
745    /// Modifier participates in modifier locals.
746    pub const MODIFIER_LOCALS: Self = Self(1 << 4);
747    /// Modifier participates in focus management.
748    pub const FOCUS: Self = Self(1 << 5);
749
750    /// Returns an empty capability set.
751    pub const fn empty() -> Self {
752        Self::NONE
753    }
754
755    /// Returns whether all bits in `other` are present in `self`.
756    pub const fn contains(self, other: Self) -> bool {
757        (self.0 & other.0) == other.0
758    }
759
760    /// Returns whether any bit in `other` is present in `self`.
761    pub const fn intersects(self, other: Self) -> bool {
762        (self.0 & other.0) != 0
763    }
764
765    /// Inserts the requested capability bits.
766    pub fn insert(&mut self, other: Self) {
767        self.0 |= other.0;
768    }
769
770    /// Returns the raw bit representation.
771    pub const fn bits(self) -> u32 {
772        self.0
773    }
774
775    /// Returns true when no capabilities are set.
776    pub const fn is_empty(self) -> bool {
777        self.0 == 0
778    }
779
780    /// Returns the capability bit mask required for the given invalidation.
781    pub const fn for_invalidation(kind: InvalidationKind) -> Self {
782        match kind {
783            InvalidationKind::Layout => Self::LAYOUT,
784            InvalidationKind::Draw => Self::DRAW,
785            InvalidationKind::PointerInput => Self::POINTER_INPUT,
786            InvalidationKind::Semantics => Self::SEMANTICS,
787            InvalidationKind::Focus => Self::FOCUS,
788        }
789    }
790}
791
792impl Default for NodeCapabilities {
793    fn default() -> Self {
794        Self::NONE
795    }
796}
797
798impl fmt::Debug for NodeCapabilities {
799    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
800        f.debug_struct("NodeCapabilities")
801            .field("layout", &self.contains(Self::LAYOUT))
802            .field("draw", &self.contains(Self::DRAW))
803            .field("pointer_input", &self.contains(Self::POINTER_INPUT))
804            .field("semantics", &self.contains(Self::SEMANTICS))
805            .field("modifier_locals", &self.contains(Self::MODIFIER_LOCALS))
806            .field("focus", &self.contains(Self::FOCUS))
807            .finish()
808    }
809}
810
811impl BitOr for NodeCapabilities {
812    type Output = Self;
813
814    fn bitor(self, rhs: Self) -> Self::Output {
815        Self(self.0 | rhs.0)
816    }
817}
818
819impl BitOrAssign for NodeCapabilities {
820    fn bitor_assign(&mut self, rhs: Self) {
821        self.0 |= rhs.0;
822    }
823}
824
825/// Records an invalidation request together with the capability mask that triggered it.
826#[derive(Clone, Copy, Debug, PartialEq, Eq)]
827pub struct ModifierInvalidation {
828    kind: InvalidationKind,
829    capabilities: NodeCapabilities,
830}
831
832impl ModifierInvalidation {
833    /// Creates a new modifier invalidation entry.
834    pub const fn new(kind: InvalidationKind, capabilities: NodeCapabilities) -> Self {
835        Self { kind, capabilities }
836    }
837
838    /// Returns the invalidated pipeline kind.
839    pub const fn kind(self) -> InvalidationKind {
840        self.kind
841    }
842
843    /// Returns the capability mask associated with the invalidation.
844    pub const fn capabilities(self) -> NodeCapabilities {
845        self.capabilities
846    }
847}
848
849/// Type-erased modifier element used by the runtime to reconcile chains.
850pub trait AnyModifierElement: fmt::Debug {
851    fn node_type(&self) -> TypeId;
852
853    fn element_type(&self) -> TypeId;
854
855    fn create_node(&self) -> Box<dyn ModifierNode>;
856
857    fn update_node(&self, node: &mut dyn ModifierNode);
858
859    fn key(&self) -> Option<u64>;
860
861    fn capabilities(&self) -> NodeCapabilities {
862        NodeCapabilities::default()
863    }
864
865    fn hash_code(&self) -> u64;
866
867    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool;
868
869    fn inspector_name(&self) -> &'static str;
870
871    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String));
872
873    fn requires_update(&self) -> bool;
874
875    fn as_any(&self) -> &dyn Any;
876}
877
878struct TypedModifierElement<E: ModifierNodeElement> {
879    element: E,
880}
881
882impl<E: ModifierNodeElement> TypedModifierElement<E> {
883    fn new(element: E) -> Self {
884        Self { element }
885    }
886}
887
888impl<E> fmt::Debug for TypedModifierElement<E>
889where
890    E: ModifierNodeElement,
891{
892    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
893        f.debug_struct("TypedModifierElement")
894            .field("type", &type_name::<E>())
895            .finish()
896    }
897}
898
899impl<E> AnyModifierElement for TypedModifierElement<E>
900where
901    E: ModifierNodeElement,
902{
903    fn node_type(&self) -> TypeId {
904        TypeId::of::<E::Node>()
905    }
906
907    fn element_type(&self) -> TypeId {
908        TypeId::of::<E>()
909    }
910
911    fn create_node(&self) -> Box<dyn ModifierNode> {
912        Box::new(self.element.create())
913    }
914
915    fn update_node(&self, node: &mut dyn ModifierNode) {
916        let typed = node
917            .as_any_mut()
918            .downcast_mut::<E::Node>()
919            .expect("modifier node type mismatch");
920        self.element.update(typed);
921    }
922
923    fn key(&self) -> Option<u64> {
924        self.element.key()
925    }
926
927    fn capabilities(&self) -> NodeCapabilities {
928        self.element.capabilities()
929    }
930
931    fn hash_code(&self) -> u64 {
932        let mut hasher = DefaultHasher::new();
933        self.element.hash(&mut hasher);
934        hasher.finish()
935    }
936
937    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool {
938        other
939            .as_any()
940            .downcast_ref::<Self>()
941            .map(|typed| typed.element == self.element)
942            .unwrap_or(false)
943    }
944
945    fn inspector_name(&self) -> &'static str {
946        self.element.inspector_name()
947    }
948
949    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String)) {
950        self.element.inspector_properties(visitor);
951    }
952
953    fn requires_update(&self) -> bool {
954        self.element.always_update()
955    }
956
957    fn as_any(&self) -> &dyn Any {
958        self
959    }
960}
961
962/// Convenience helper for callers to construct a type-erased modifier
963/// element without having to mention the internal wrapper type.
964pub fn modifier_element<E: ModifierNodeElement>(element: E) -> DynModifierElement {
965    Rc::new(TypedModifierElement::new(element))
966}
967
968/// Boxed type-erased modifier element.
969pub type DynModifierElement = Rc<dyn AnyModifierElement>;
970
971#[derive(Clone, Copy, Debug, PartialEq, Eq)]
972enum TraversalDirection {
973    Forward,
974    Backward,
975}
976
977/// Iterator walking a modifier chain either from head-to-tail or tail-to-head.
978pub struct ModifierChainIter<'a> {
979    next: Option<ModifierChainNodeRef<'a>>,
980    direction: TraversalDirection,
981}
982
983impl<'a> ModifierChainIter<'a> {
984    fn new(start: Option<ModifierChainNodeRef<'a>>, direction: TraversalDirection) -> Self {
985        Self {
986            next: start,
987            direction,
988        }
989    }
990}
991
992impl<'a> Iterator for ModifierChainIter<'a> {
993    type Item = ModifierChainNodeRef<'a>;
994
995    #[inline]
996    fn next(&mut self) -> Option<Self::Item> {
997        let current = self.next.take()?;
998        if current.is_sentinel() {
999            self.next = None;
1000            return None;
1001        }
1002        self.next = match self.direction {
1003            TraversalDirection::Forward => current.child(),
1004            TraversalDirection::Backward => current.parent(),
1005        };
1006        Some(current)
1007    }
1008}
1009
1010impl<'a> std::iter::FusedIterator for ModifierChainIter<'a> {}
1011
1012#[derive(Debug)]
1013struct ModifierNodeEntry {
1014    element_type: TypeId,
1015    key: Option<u64>,
1016    hash_code: u64,
1017    element: DynModifierElement,
1018    node: Rc<RefCell<Box<dyn ModifierNode>>>,
1019    capabilities: NodeCapabilities,
1020}
1021
1022impl ModifierNodeEntry {
1023    fn new(
1024        element_type: TypeId,
1025        key: Option<u64>,
1026        element: DynModifierElement,
1027        node: Box<dyn ModifierNode>,
1028        hash_code: u64,
1029        capabilities: NodeCapabilities,
1030    ) -> Self {
1031        // Wrap the boxed node in Rc<RefCell<>> for shared ownership
1032        let node_rc = Rc::new(RefCell::new(node));
1033        let entry = Self {
1034            element_type,
1035            key,
1036            hash_code,
1037            element,
1038            node: Rc::clone(&node_rc),
1039            capabilities,
1040        };
1041        entry
1042            .node
1043            .borrow()
1044            .node_state()
1045            .set_capabilities(entry.capabilities);
1046        entry
1047    }
1048}
1049
1050fn visit_node_tree_mut(
1051    node: &mut dyn ModifierNode,
1052    visitor: &mut dyn FnMut(&mut dyn ModifierNode),
1053) {
1054    visitor(node);
1055    node.for_each_delegate_mut(&mut |child| visit_node_tree_mut(child, visitor));
1056}
1057
1058fn nth_delegate(node: &dyn ModifierNode, target: usize) -> Option<&dyn ModifierNode> {
1059    let mut current = 0usize;
1060    let mut result: Option<&dyn ModifierNode> = None;
1061    node.for_each_delegate(&mut |child| {
1062        if result.is_none() && current == target {
1063            result = Some(child);
1064        }
1065        current += 1;
1066    });
1067    result
1068}
1069
1070fn nth_delegate_mut(node: &mut dyn ModifierNode, target: usize) -> Option<&mut dyn ModifierNode> {
1071    let mut current = 0usize;
1072    let mut result: Option<&mut dyn ModifierNode> = None;
1073    node.for_each_delegate_mut(&mut |child| {
1074        if result.is_none() && current == target {
1075            result = Some(child);
1076        }
1077        current += 1;
1078    });
1079    result
1080}
1081
1082fn with_node_context<F, R>(
1083    node: &mut dyn ModifierNode,
1084    context: &mut dyn ModifierNodeContext,
1085    f: F,
1086) -> R
1087where
1088    F: FnOnce(&mut dyn ModifierNode, &mut dyn ModifierNodeContext) -> R,
1089{
1090    context.push_active_capabilities(node.node_state().capabilities());
1091    let result = f(node, context);
1092    context.pop_active_capabilities();
1093    result
1094}
1095
1096fn request_auto_invalidations(
1097    context: &mut dyn ModifierNodeContext,
1098    capabilities: NodeCapabilities,
1099) {
1100    if capabilities.is_empty() {
1101        return;
1102    }
1103
1104    context.push_active_capabilities(capabilities);
1105
1106    if capabilities.contains(NodeCapabilities::LAYOUT) {
1107        context.invalidate(InvalidationKind::Layout);
1108    }
1109    if capabilities.contains(NodeCapabilities::DRAW) {
1110        context.invalidate(InvalidationKind::Draw);
1111    }
1112    if capabilities.contains(NodeCapabilities::POINTER_INPUT) {
1113        context.invalidate(InvalidationKind::PointerInput);
1114    }
1115    if capabilities.contains(NodeCapabilities::SEMANTICS) {
1116        context.invalidate(InvalidationKind::Semantics);
1117    }
1118    if capabilities.contains(NodeCapabilities::FOCUS) {
1119        context.invalidate(InvalidationKind::Focus);
1120    }
1121
1122    context.pop_active_capabilities();
1123}
1124
1125/// Attaches a node tree by calling on_attach for all unattached nodes.
1126///
1127/// # Safety
1128/// Callers must ensure no immutable RefCell borrows are held on the node
1129/// when calling this function. The on_attach callback may trigger mutations
1130/// (invalidations, state updates, etc.) that require mutable access, which
1131/// would panic if an immutable borrow is held across the call.
1132fn attach_node_tree(node: &mut dyn ModifierNode, context: &mut dyn ModifierNodeContext) {
1133    visit_node_tree_mut(node, &mut |n| {
1134        if !n.node_state().is_attached() {
1135            n.node_state().set_attached(true);
1136            with_node_context(n, context, |node, ctx| node.on_attach(ctx));
1137        }
1138    });
1139}
1140
1141fn reset_node_tree(node: &mut dyn ModifierNode) {
1142    visit_node_tree_mut(node, &mut |n| n.on_reset());
1143}
1144
1145fn detach_node_tree(node: &mut dyn ModifierNode) {
1146    visit_node_tree_mut(node, &mut |n| {
1147        if n.node_state().is_attached() {
1148            n.on_detach();
1149            n.node_state().set_attached(false);
1150        }
1151        n.node_state().set_parent_link(None);
1152        n.node_state().set_child_link(None);
1153        n.node_state()
1154            .set_aggregate_child_capabilities(NodeCapabilities::empty());
1155    });
1156}
1157
1158/// Chain of modifier nodes attached to a layout node.
1159///
1160/// The chain tracks ownership of modifier nodes and reuses them across
1161/// updates when the incoming element list still contains a node of the
1162/// same type. Removed nodes detach automatically so callers do not need
1163/// to manually manage their lifetimes.
1164pub struct ModifierNodeChain {
1165    entries: Vec<ModifierNodeEntry>,
1166    aggregated_capabilities: NodeCapabilities,
1167    head_aggregate_child_capabilities: NodeCapabilities,
1168    head_sentinel: Box<SentinelNode>,
1169    tail_sentinel: Box<SentinelNode>,
1170    ordered_nodes: Vec<NodeLink>,
1171    // Scratch buffers reused during update to avoid repeated allocations
1172    scratch_old_used: Vec<bool>,
1173    scratch_match_order: Vec<Option<usize>>,
1174    scratch_final_slots: Vec<Option<ModifierNodeEntry>>,
1175    scratch_elements: Vec<DynModifierElement>,
1176}
1177
1178struct SentinelNode {
1179    state: NodeState,
1180}
1181
1182impl SentinelNode {
1183    fn new() -> Self {
1184        Self {
1185            state: NodeState::sentinel(),
1186        }
1187    }
1188}
1189
1190impl DelegatableNode for SentinelNode {
1191    fn node_state(&self) -> &NodeState {
1192        &self.state
1193    }
1194}
1195
1196impl ModifierNode for SentinelNode {}
1197
1198#[derive(Clone)]
1199pub struct ModifierChainNodeRef<'a> {
1200    chain: &'a ModifierNodeChain,
1201    link: NodeLink,
1202}
1203
1204impl Default for ModifierNodeChain {
1205    fn default() -> Self {
1206        Self::new()
1207    }
1208}
1209
1210/// Index structure for O(1) modifier entry lookups during update.
1211///
1212/// This avoids O(n²) complexity by pre-building hash maps that allow constant-time
1213/// lookups for matching entries by key, hash, or type.
1214/// Uses FxHashMap for faster hashing of TypeId keys.
1215struct EntryIndex {
1216    /// Map (TypeId, key) → index for keyed entries
1217    keyed: FxHashMap<(TypeId, u64), Vec<usize>>,
1218    /// Map (TypeId, hash) → indices for unkeyed entries with specific hash
1219    hashed: FxHashMap<(TypeId, u64), Vec<usize>>,
1220    /// Map TypeId → indices for all unkeyed entries of that type
1221    typed: FxHashMap<TypeId, Vec<usize>>,
1222}
1223
1224impl EntryIndex {
1225    fn build(entries: &[ModifierNodeEntry]) -> Self {
1226        let mut keyed = FxHashMap::default();
1227        let mut hashed = FxHashMap::default();
1228        let mut typed = FxHashMap::default();
1229
1230        for (i, entry) in entries.iter().enumerate() {
1231            if let Some(key_value) = entry.key {
1232                // Keyed entry
1233                keyed
1234                    .entry((entry.element_type, key_value))
1235                    .or_insert_with(Vec::new)
1236                    .push(i);
1237            } else {
1238                // Unkeyed entry - add to both hash and type indices
1239                hashed
1240                    .entry((entry.element_type, entry.hash_code))
1241                    .or_insert_with(Vec::new)
1242                    .push(i);
1243                typed
1244                    .entry(entry.element_type)
1245                    .or_insert_with(Vec::new)
1246                    .push(i);
1247            }
1248        }
1249
1250        Self {
1251            keyed,
1252            hashed,
1253            typed,
1254        }
1255    }
1256
1257    /// Find the best matching entry for reuse.
1258    ///
1259    /// Matching priority (from highest to lowest):
1260    /// 1. Keyed match: same type + same key
1261    /// 2. Exact match: same type + no key + same hash + equals_element
1262    /// 3. Type match: same type + no key (will require update)
1263    fn find_match(
1264        &self,
1265        entries: &[ModifierNodeEntry],
1266        used: &[bool],
1267        element_type: TypeId,
1268        key: Option<u64>,
1269        hash_code: u64,
1270        element: &DynModifierElement,
1271    ) -> Option<usize> {
1272        if let Some(key_value) = key {
1273            // Priority 1: Keyed lookup - O(1)
1274            if let Some(candidates) = self.keyed.get(&(element_type, key_value)) {
1275                for &i in candidates {
1276                    if !used[i] {
1277                        return Some(i);
1278                    }
1279                }
1280            }
1281        } else {
1282            // Priority 2: Exact match (hash + equality) - O(1) lookup + O(k) equality checks
1283            if let Some(candidates) = self.hashed.get(&(element_type, hash_code)) {
1284                for &i in candidates {
1285                    if !used[i] && entries[i].element.as_ref().equals_element(element.as_ref()) {
1286                        return Some(i);
1287                    }
1288                }
1289            }
1290
1291            // Priority 3: Type match only - O(1) lookup + O(k) scan
1292            if let Some(candidates) = self.typed.get(&element_type) {
1293                for &i in candidates {
1294                    if !used[i] {
1295                        return Some(i);
1296                    }
1297                }
1298            }
1299        }
1300
1301        None
1302    }
1303}
1304
1305impl ModifierNodeChain {
1306    pub fn new() -> Self {
1307        let mut chain = Self {
1308            entries: Vec::new(),
1309            aggregated_capabilities: NodeCapabilities::empty(),
1310            head_aggregate_child_capabilities: NodeCapabilities::empty(),
1311            head_sentinel: Box::new(SentinelNode::new()),
1312            tail_sentinel: Box::new(SentinelNode::new()),
1313            ordered_nodes: Vec::new(),
1314            scratch_old_used: Vec::new(),
1315            scratch_match_order: Vec::new(),
1316            scratch_final_slots: Vec::new(),
1317            scratch_elements: Vec::new(),
1318        };
1319        chain.sync_chain_links();
1320        chain
1321    }
1322
1323    /// Detaches all nodes in the chain.
1324    pub fn detach_nodes(&mut self) {
1325        for entry in &self.entries {
1326            detach_node_tree(&mut **entry.node.borrow_mut());
1327        }
1328    }
1329
1330    /// Attaches all nodes in the chain.
1331    pub fn attach_nodes(&mut self, context: &mut dyn ModifierNodeContext) {
1332        for entry in &self.entries {
1333            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1334        }
1335    }
1336
1337    /// Rebuilds the internal chain links (parent/child relationships).
1338    /// This should be called if nodes have been detached but are intended to be reused.
1339    pub fn repair_chain(&mut self) {
1340        self.sync_chain_links();
1341    }
1342
1343    /// Reconcile the chain against the provided elements, attaching newly
1344    /// created nodes and detaching nodes that are no longer required.
1345    ///
1346    /// This method delegates to `update_from_ref_iter` which handles the
1347    /// actual reconciliation logic.
1348    pub fn update_from_slice(
1349        &mut self,
1350        elements: &[DynModifierElement],
1351        context: &mut dyn ModifierNodeContext,
1352    ) {
1353        self.update_from_ref_iter(elements.iter(), context);
1354    }
1355
1356    /// Reconcile the chain against the provided iterator of element references.
1357    ///
1358    /// This is the preferred method as it avoids requiring a collected slice,
1359    /// enabling zero-allocation traversal of modifier trees.
1360    pub fn update_from_ref_iter<'a, I>(
1361        &mut self,
1362        elements: I,
1363        context: &mut dyn ModifierNodeContext,
1364    ) where
1365        I: Iterator<Item = &'a DynModifierElement>,
1366    {
1367        // Fast path: try to match elements sequentially without building index.
1368        // If all elements match in order (same type and key at same position),
1369        // we skip the expensive EntryIndex building. This is O(n) instead of O(n + m).
1370        let old_len = self.entries.len();
1371        let mut fast_path_failed_at: Option<usize> = None;
1372        let mut elements_count = 0;
1373
1374        // Collect elements we need to process in slow path
1375        self.scratch_elements.clear();
1376
1377        for (idx, element) in elements.enumerate() {
1378            elements_count = idx + 1;
1379
1380            if fast_path_failed_at.is_none() && idx < old_len {
1381                let entry = &mut self.entries[idx];
1382                let same_type = entry.element_type == element.element_type();
1383                let same_key = entry.key == element.key();
1384                let same_hash = entry.hash_code == element.hash_code();
1385
1386                // Fast path requires same type, key, AND hash to ensure we're not
1387                // breaking reordering semantics (where elements can move positions)
1388                if same_type && same_key && same_hash {
1389                    // Fast path: element matches at same position
1390                    let same_element = entry.element.as_ref().equals_element(element.as_ref());
1391                    let capabilities = element.capabilities();
1392
1393                    // Re-attach node if it was detached during a previous update
1394                    {
1395                        let node_borrow = entry.node.borrow();
1396                        if !node_borrow.node_state().is_attached() {
1397                            drop(node_borrow);
1398                            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1399                        }
1400                    }
1401
1402                    // Optimize updates: only call update_node if element changed
1403                    let needs_update = !same_element || element.requires_update();
1404                    if needs_update {
1405                        element.update_node(&mut **entry.node.borrow_mut());
1406                        entry.element = element.clone();
1407                        entry.hash_code = element.hash_code();
1408                        request_auto_invalidations(context, capabilities);
1409                    }
1410
1411                    // Always update metadata
1412                    entry.capabilities = capabilities;
1413                    entry
1414                        .node
1415                        .borrow()
1416                        .node_state()
1417                        .set_capabilities(capabilities);
1418                    continue;
1419                }
1420                // Fast path failed - mark position and fall through to collect
1421                fast_path_failed_at = Some(idx);
1422            }
1423
1424            // Collect element for slow path processing
1425            self.scratch_elements.push(element.clone());
1426        }
1427
1428        // Fast path succeeded if:
1429        // 1. No mismatch was found (fast_path_failed_at is None)
1430        // 2. All elements were processed via fast path (scratch_elements is empty)
1431        // Note: If old_len=0 and we have new elements, scratch_elements won't be empty
1432        if fast_path_failed_at.is_none() && self.scratch_elements.is_empty() {
1433            // Detach any removed entries (elements_count <= old_len guaranteed here)
1434            if elements_count < self.entries.len() {
1435                for entry in self.entries.drain(elements_count..) {
1436                    detach_node_tree(&mut **entry.node.borrow_mut());
1437                }
1438            }
1439            self.sync_chain_links();
1440            return;
1441        }
1442
1443        // Slow path: need full reconciliation starting from failure point
1444        // If no mismatch but we have extra elements, fail_idx is the old length
1445        let fail_idx = fast_path_failed_at.unwrap_or(old_len);
1446
1447        // Move entries that were already processed to a safe place
1448        let mut old_entries: Vec<ModifierNodeEntry> = self.entries.drain(fail_idx..).collect();
1449        let processed_entries_len = self.entries.len();
1450        let old_len = old_entries.len();
1451
1452        // Reuse scratch buffers for the remaining entries only
1453        self.scratch_old_used.clear();
1454        self.scratch_old_used.resize(old_len, false);
1455
1456        self.scratch_match_order.clear();
1457        self.scratch_match_order.resize(old_len, None);
1458
1459        // Build index only for unprocessed entries
1460        let index = EntryIndex::build(&old_entries);
1461
1462        let new_elements_count = self.scratch_elements.len();
1463        self.scratch_final_slots.clear();
1464        self.scratch_final_slots.reserve(new_elements_count);
1465
1466        // Process each remaining element
1467        for (new_pos, element) in self.scratch_elements.drain(..).enumerate() {
1468            self.scratch_final_slots.push(None);
1469            let element_type = element.element_type();
1470            let key = element.key();
1471            let hash_code = element.hash_code();
1472            let capabilities = element.capabilities();
1473
1474            // Find best matching old entry via index
1475            let matched_idx = index.find_match(
1476                &old_entries,
1477                &self.scratch_old_used,
1478                element_type,
1479                key,
1480                hash_code,
1481                &element,
1482            );
1483
1484            if let Some(idx) = matched_idx {
1485                // Reuse existing entry
1486                self.scratch_old_used[idx] = true;
1487                self.scratch_match_order[idx] = Some(new_pos);
1488                let entry = &mut old_entries[idx];
1489
1490                // Check if element actually changed
1491                let same_element = entry.element.as_ref().equals_element(element.as_ref());
1492
1493                // Re-attach node if it was detached
1494                {
1495                    let node_borrow = entry.node.borrow();
1496                    if !node_borrow.node_state().is_attached() {
1497                        drop(node_borrow);
1498                        attach_node_tree(&mut **entry.node.borrow_mut(), context);
1499                    }
1500                }
1501
1502                // Optimize updates: only call update_node if element changed
1503                let needs_update = !same_element || element.requires_update();
1504                if needs_update {
1505                    element.update_node(&mut **entry.node.borrow_mut());
1506                    entry.element = element;
1507                    entry.hash_code = hash_code;
1508                    request_auto_invalidations(context, capabilities);
1509                }
1510
1511                // Always update metadata
1512                entry.key = key;
1513                entry.element_type = element_type;
1514                entry.capabilities = capabilities;
1515                entry
1516                    .node
1517                    .borrow()
1518                    .node_state()
1519                    .set_capabilities(capabilities);
1520            } else {
1521                // Create new entry
1522                let entry = ModifierNodeEntry::new(
1523                    element_type,
1524                    key,
1525                    element.clone(),
1526                    element.create_node(),
1527                    hash_code,
1528                    capabilities,
1529                );
1530                attach_node_tree(&mut **entry.node.borrow_mut(), context);
1531                element.update_node(&mut **entry.node.borrow_mut());
1532                request_auto_invalidations(context, capabilities);
1533                self.scratch_final_slots[new_pos] = Some(entry);
1534            }
1535        }
1536
1537        // Place matched entries in their new positions
1538        for (i, entry) in old_entries.into_iter().enumerate() {
1539            if self.scratch_old_used[i] {
1540                let pos = self.scratch_match_order[i]
1541                    .expect("Missing match order for used modifier entry");
1542                self.scratch_final_slots[pos] = Some(entry);
1543            } else {
1544                detach_node_tree(&mut **entry.node.borrow_mut());
1545            }
1546        }
1547
1548        // Append processed entries to self.entries
1549        self.entries.reserve(self.scratch_final_slots.len());
1550        for slot in self.scratch_final_slots.drain(..) {
1551            let entry = slot.expect("Missing modifier entry for reconciled position");
1552            self.entries.push(entry);
1553        }
1554
1555        debug_assert_eq!(
1556            self.entries.len(),
1557            processed_entries_len + new_elements_count
1558        );
1559        self.sync_chain_links();
1560    }
1561
1562    /// Convenience wrapper that accepts any iterator of type-erased
1563    /// modifier elements. Elements are collected into a temporary vector
1564    /// before reconciliation.
1565    pub fn update<I>(&mut self, elements: I, context: &mut dyn ModifierNodeContext)
1566    where
1567        I: IntoIterator<Item = DynModifierElement>,
1568    {
1569        let collected: Vec<DynModifierElement> = elements.into_iter().collect();
1570        self.update_from_slice(&collected, context);
1571    }
1572
1573    /// Resets all nodes in the chain. This mirrors the behaviour of
1574    /// Jetpack Compose's `onReset` callback.
1575    pub fn reset(&mut self) {
1576        for entry in &mut self.entries {
1577            reset_node_tree(&mut **entry.node.borrow_mut());
1578        }
1579    }
1580
1581    /// Detaches every node in the chain and clears internal storage.
1582    pub fn detach_all(&mut self) {
1583        for entry in std::mem::take(&mut self.entries) {
1584            detach_node_tree(&mut **entry.node.borrow_mut());
1585            {
1586                let node_borrow = entry.node.borrow();
1587                let state = node_borrow.node_state();
1588                state.set_capabilities(NodeCapabilities::empty());
1589            }
1590        }
1591        self.aggregated_capabilities = NodeCapabilities::empty();
1592        self.head_aggregate_child_capabilities = NodeCapabilities::empty();
1593        self.ordered_nodes.clear();
1594        self.sync_chain_links();
1595    }
1596
1597    pub fn len(&self) -> usize {
1598        self.entries.len()
1599    }
1600
1601    pub fn is_empty(&self) -> bool {
1602        self.entries.is_empty()
1603    }
1604
1605    /// Returns the aggregated capability mask for the entire chain.
1606    pub fn capabilities(&self) -> NodeCapabilities {
1607        self.aggregated_capabilities
1608    }
1609
1610    /// Returns true if the chain contains at least one node with the requested capability.
1611    pub fn has_capability(&self, capability: NodeCapabilities) -> bool {
1612        self.aggregated_capabilities.contains(capability)
1613    }
1614
1615    /// Returns the sentinel head reference for traversal.
1616    pub fn head(&self) -> ModifierChainNodeRef<'_> {
1617        self.make_node_ref(NodeLink::Head)
1618    }
1619
1620    /// Returns the sentinel tail reference for traversal.
1621    pub fn tail(&self) -> ModifierChainNodeRef<'_> {
1622        self.make_node_ref(NodeLink::Tail)
1623    }
1624
1625    /// Iterates over the chain from head to tail, skipping sentinels.
1626    pub fn head_to_tail(&self) -> ModifierChainIter<'_> {
1627        ModifierChainIter::new(self.head().child(), TraversalDirection::Forward)
1628    }
1629
1630    /// Iterates over the chain from tail to head, skipping sentinels.
1631    pub fn tail_to_head(&self) -> ModifierChainIter<'_> {
1632        ModifierChainIter::new(self.tail().parent(), TraversalDirection::Backward)
1633    }
1634
1635    /// Calls `f` for every node in insertion order.
1636    pub fn for_each_forward<F>(&self, mut f: F)
1637    where
1638        F: FnMut(ModifierChainNodeRef<'_>),
1639    {
1640        for node in self.head_to_tail() {
1641            f(node);
1642        }
1643    }
1644
1645    /// Calls `f` for every node containing any capability from `mask`.
1646    pub fn for_each_forward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
1647    where
1648        F: FnMut(ModifierChainNodeRef<'_>),
1649    {
1650        if mask.is_empty() {
1651            self.for_each_forward(f);
1652            return;
1653        }
1654
1655        if !self.head().aggregate_child_capabilities().intersects(mask) {
1656            return;
1657        }
1658
1659        for node in self.head_to_tail() {
1660            if node.kind_set().intersects(mask) {
1661                f(node);
1662            }
1663        }
1664    }
1665
1666    /// Calls `f` for every node containing any capability from `mask`, providing the node ref.
1667    pub fn for_each_node_with_capability<F>(&self, mask: NodeCapabilities, mut f: F)
1668    where
1669        F: FnMut(ModifierChainNodeRef<'_>, &dyn ModifierNode),
1670    {
1671        self.for_each_forward_matching(mask, |node_ref| {
1672            node_ref.with_node(|node| f(node_ref.clone(), node));
1673        });
1674    }
1675
1676    /// Calls `f` for every node in reverse insertion order.
1677    pub fn for_each_backward<F>(&self, mut f: F)
1678    where
1679        F: FnMut(ModifierChainNodeRef<'_>),
1680    {
1681        for node in self.tail_to_head() {
1682            f(node);
1683        }
1684    }
1685
1686    /// Calls `f` for every node in reverse order that matches `mask`.
1687    pub fn for_each_backward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
1688    where
1689        F: FnMut(ModifierChainNodeRef<'_>),
1690    {
1691        if mask.is_empty() {
1692            self.for_each_backward(f);
1693            return;
1694        }
1695
1696        if !self.head().aggregate_child_capabilities().intersects(mask) {
1697            return;
1698        }
1699
1700        for node in self.tail_to_head() {
1701            if node.kind_set().intersects(mask) {
1702                f(node);
1703            }
1704        }
1705    }
1706
1707    /// Returns a node reference for the entry at `index`.
1708    pub fn node_ref_at(&self, index: usize) -> Option<ModifierChainNodeRef<'_>> {
1709        if index >= self.entries.len() {
1710            None
1711        } else {
1712            Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))))
1713        }
1714    }
1715
1716    /// Returns the node reference that owns `node`.
1717    pub fn find_node_ref(&self, node: &dyn ModifierNode) -> Option<ModifierChainNodeRef<'_>> {
1718        fn node_data_ptr(node: &dyn ModifierNode) -> *const () {
1719            node as *const dyn ModifierNode as *const ()
1720        }
1721
1722        let target = node_data_ptr(node);
1723        for (index, entry) in self.entries.iter().enumerate() {
1724            if node_data_ptr(&**entry.node.borrow()) == target {
1725                return Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))));
1726            }
1727        }
1728
1729        self.ordered_nodes.iter().find_map(|link| {
1730            if matches!(link, NodeLink::Entry(path) if path.delegates().is_empty()) {
1731                return None;
1732            }
1733            let matches_target = match link {
1734                NodeLink::Head => node_data_ptr(self.head_sentinel.as_ref()) == target,
1735                NodeLink::Tail => node_data_ptr(self.tail_sentinel.as_ref()) == target,
1736                NodeLink::Entry(path) => {
1737                    let node_borrow = self.entries[path.entry()].node.borrow();
1738                    node_data_ptr(&**node_borrow) == target
1739                }
1740            };
1741            if matches_target {
1742                Some(self.make_node_ref(link.clone()))
1743            } else {
1744                None
1745            }
1746        })
1747    }
1748
1749    /// Downcasts the node at `index` to the requested type.
1750    /// Returns a `Ref` guard that dereferences to the node type.
1751    pub fn node<N: ModifierNode + 'static>(&self, index: usize) -> Option<std::cell::Ref<'_, N>> {
1752        self.entries.get(index).and_then(|entry| {
1753            std::cell::Ref::filter_map(entry.node.borrow(), |boxed_node| {
1754                boxed_node.as_any().downcast_ref::<N>()
1755            })
1756            .ok()
1757        })
1758    }
1759
1760    /// Downcasts the node at `index` to the requested mutable type.
1761    /// Returns a `RefMut` guard that dereferences to the node type.
1762    pub fn node_mut<N: ModifierNode + 'static>(
1763        &self,
1764        index: usize,
1765    ) -> Option<std::cell::RefMut<'_, N>> {
1766        self.entries.get(index).and_then(|entry| {
1767            std::cell::RefMut::filter_map(entry.node.borrow_mut(), |boxed_node| {
1768                boxed_node.as_any_mut().downcast_mut::<N>()
1769            })
1770            .ok()
1771        })
1772    }
1773
1774    /// Returns an Rc clone of the node at the given index for shared ownership.
1775    /// This is used by coordinators to hold direct references to nodes.
1776    pub fn get_node_rc(&self, index: usize) -> Option<Rc<RefCell<Box<dyn ModifierNode>>>> {
1777        self.entries.get(index).map(|entry| Rc::clone(&entry.node))
1778    }
1779
1780    /// Returns true if the chain contains any nodes matching the given invalidation kind.
1781    pub fn has_nodes_for_invalidation(&self, kind: InvalidationKind) -> bool {
1782        self.aggregated_capabilities
1783            .contains(NodeCapabilities::for_invalidation(kind))
1784    }
1785
1786    /// Visits every node in insertion order together with its capability mask.
1787    pub fn visit_nodes<F>(&self, mut f: F)
1788    where
1789        F: FnMut(&dyn ModifierNode, NodeCapabilities),
1790    {
1791        for link in &self.ordered_nodes {
1792            match link {
1793                NodeLink::Head => {
1794                    let node = self.head_sentinel.as_ref();
1795                    f(node, node.node_state().capabilities());
1796                }
1797                NodeLink::Tail => {
1798                    let node = self.tail_sentinel.as_ref();
1799                    f(node, node.node_state().capabilities());
1800                }
1801                NodeLink::Entry(path) => {
1802                    let node_borrow = self.entries[path.entry()].node.borrow();
1803                    // Navigate through delegates if path has them
1804                    if path.delegates().is_empty() {
1805                        f(&**node_borrow, node_borrow.node_state().capabilities());
1806                    } else {
1807                        // Navigate to the delegate node
1808                        let mut current: &dyn ModifierNode = &**node_borrow;
1809                        for &delegate_index in path.delegates() {
1810                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1811                                current = delegate;
1812                            } else {
1813                                return; // Invalid delegate path
1814                            }
1815                        }
1816                        f(current, current.node_state().capabilities());
1817                    }
1818                }
1819            }
1820        }
1821    }
1822
1823    /// Visits every node mutably in insertion order together with its capability mask.
1824    pub fn visit_nodes_mut<F>(&mut self, mut f: F)
1825    where
1826        F: FnMut(&mut dyn ModifierNode, NodeCapabilities),
1827    {
1828        for index in 0..self.ordered_nodes.len() {
1829            let link = self.ordered_nodes[index].clone();
1830            match link {
1831                NodeLink::Head => {
1832                    let node = self.head_sentinel.as_mut();
1833                    let capabilities = node.node_state().capabilities();
1834                    f(node, capabilities);
1835                }
1836                NodeLink::Tail => {
1837                    let node = self.tail_sentinel.as_mut();
1838                    let capabilities = node.node_state().capabilities();
1839                    f(node, capabilities);
1840                }
1841                NodeLink::Entry(path) => {
1842                    let mut node_borrow = self.entries[path.entry()].node.borrow_mut();
1843                    // Navigate through delegates if path has them
1844                    if path.delegates().is_empty() {
1845                        let capabilities = node_borrow.node_state().capabilities();
1846                        f(&mut **node_borrow, capabilities);
1847                    } else {
1848                        // Navigate to the delegate node mutably
1849                        let mut current: &mut dyn ModifierNode = &mut **node_borrow;
1850                        for &delegate_index in path.delegates() {
1851                            if let Some(delegate) = nth_delegate_mut(current, delegate_index) {
1852                                current = delegate;
1853                            } else {
1854                                return; // Invalid delegate path
1855                            }
1856                        }
1857                        let capabilities = current.node_state().capabilities();
1858                        f(current, capabilities);
1859                    }
1860                }
1861            }
1862        }
1863    }
1864
1865    fn make_node_ref(&self, link: NodeLink) -> ModifierChainNodeRef<'_> {
1866        ModifierChainNodeRef { chain: self, link }
1867    }
1868
1869    fn sync_chain_links(&mut self) {
1870        self.rebuild_ordered_nodes();
1871
1872        self.head_sentinel.node_state().set_parent_link(None);
1873        self.tail_sentinel.node_state().set_child_link(None);
1874
1875        if self.ordered_nodes.is_empty() {
1876            self.head_sentinel
1877                .node_state()
1878                .set_child_link(Some(NodeLink::Tail));
1879            self.tail_sentinel
1880                .node_state()
1881                .set_parent_link(Some(NodeLink::Head));
1882            self.aggregated_capabilities = NodeCapabilities::empty();
1883            self.head_aggregate_child_capabilities = NodeCapabilities::empty();
1884            self.head_sentinel
1885                .node_state()
1886                .set_aggregate_child_capabilities(NodeCapabilities::empty());
1887            self.tail_sentinel
1888                .node_state()
1889                .set_aggregate_child_capabilities(NodeCapabilities::empty());
1890            return;
1891        }
1892
1893        let mut previous = NodeLink::Head;
1894        for link in self.ordered_nodes.iter().cloned() {
1895            // Set child link on previous
1896            match &previous {
1897                NodeLink::Head => self
1898                    .head_sentinel
1899                    .node_state()
1900                    .set_child_link(Some(link.clone())),
1901                NodeLink::Tail => self
1902                    .tail_sentinel
1903                    .node_state()
1904                    .set_child_link(Some(link.clone())),
1905                NodeLink::Entry(path) => {
1906                    let node_borrow = self.entries[path.entry()].node.borrow();
1907                    // Navigate to delegate if needed
1908                    if path.delegates().is_empty() {
1909                        node_borrow.node_state().set_child_link(Some(link.clone()));
1910                    } else {
1911                        let mut current: &dyn ModifierNode = &**node_borrow;
1912                        for &delegate_index in path.delegates() {
1913                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1914                                current = delegate;
1915                            }
1916                        }
1917                        current.node_state().set_child_link(Some(link.clone()));
1918                    }
1919                }
1920            }
1921            // Set parent link on current
1922            match &link {
1923                NodeLink::Head => self
1924                    .head_sentinel
1925                    .node_state()
1926                    .set_parent_link(Some(previous.clone())),
1927                NodeLink::Tail => self
1928                    .tail_sentinel
1929                    .node_state()
1930                    .set_parent_link(Some(previous.clone())),
1931                NodeLink::Entry(path) => {
1932                    let node_borrow = self.entries[path.entry()].node.borrow();
1933                    // Navigate to delegate if needed
1934                    if path.delegates().is_empty() {
1935                        node_borrow
1936                            .node_state()
1937                            .set_parent_link(Some(previous.clone()));
1938                    } else {
1939                        let mut current: &dyn ModifierNode = &**node_borrow;
1940                        for &delegate_index in path.delegates() {
1941                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1942                                current = delegate;
1943                            }
1944                        }
1945                        current.node_state().set_parent_link(Some(previous.clone()));
1946                    }
1947                }
1948            }
1949            previous = link;
1950        }
1951
1952        // Set child link on last node to Tail
1953        match &previous {
1954            NodeLink::Head => self
1955                .head_sentinel
1956                .node_state()
1957                .set_child_link(Some(NodeLink::Tail)),
1958            NodeLink::Tail => self
1959                .tail_sentinel
1960                .node_state()
1961                .set_child_link(Some(NodeLink::Tail)),
1962            NodeLink::Entry(path) => {
1963                let node_borrow = self.entries[path.entry()].node.borrow();
1964                // Navigate to delegate if needed
1965                if path.delegates().is_empty() {
1966                    node_borrow
1967                        .node_state()
1968                        .set_child_link(Some(NodeLink::Tail));
1969                } else {
1970                    let mut current: &dyn ModifierNode = &**node_borrow;
1971                    for &delegate_index in path.delegates() {
1972                        if let Some(delegate) = nth_delegate(current, delegate_index) {
1973                            current = delegate;
1974                        }
1975                    }
1976                    current.node_state().set_child_link(Some(NodeLink::Tail));
1977                }
1978            }
1979        }
1980        self.tail_sentinel
1981            .node_state()
1982            .set_parent_link(Some(previous.clone()));
1983        self.tail_sentinel.node_state().set_child_link(None);
1984
1985        let mut aggregate = NodeCapabilities::empty();
1986        for link in self.ordered_nodes.iter().rev() {
1987            match link {
1988                NodeLink::Head => {
1989                    let state = self.head_sentinel.node_state();
1990                    aggregate |= state.capabilities();
1991                    state.set_aggregate_child_capabilities(aggregate);
1992                }
1993                NodeLink::Tail => {
1994                    let state = self.tail_sentinel.node_state();
1995                    aggregate |= state.capabilities();
1996                    state.set_aggregate_child_capabilities(aggregate);
1997                }
1998                NodeLink::Entry(path) => {
1999                    let node_borrow = self.entries[path.entry()].node.borrow();
2000                    // Navigate to delegate if needed
2001                    let state = if path.delegates().is_empty() {
2002                        node_borrow.node_state()
2003                    } else {
2004                        let mut current: &dyn ModifierNode = &**node_borrow;
2005                        for &delegate_index in path.delegates() {
2006                            if let Some(delegate) = nth_delegate(current, delegate_index) {
2007                                current = delegate;
2008                            }
2009                        }
2010                        current.node_state()
2011                    };
2012                    aggregate |= state.capabilities();
2013                    state.set_aggregate_child_capabilities(aggregate);
2014                }
2015            }
2016        }
2017
2018        self.aggregated_capabilities = aggregate;
2019        self.head_aggregate_child_capabilities = aggregate;
2020        self.head_sentinel
2021            .node_state()
2022            .set_aggregate_child_capabilities(aggregate);
2023        self.tail_sentinel
2024            .node_state()
2025            .set_aggregate_child_capabilities(NodeCapabilities::empty());
2026    }
2027
2028    fn rebuild_ordered_nodes(&mut self) {
2029        self.ordered_nodes.clear();
2030        for (index, entry) in self.entries.iter().enumerate() {
2031            let mut path = Vec::new();
2032            let node_borrow = entry.node.borrow();
2033            Self::enumerate_link_order(&**node_borrow, index, &mut path, &mut self.ordered_nodes);
2034        }
2035    }
2036
2037    fn enumerate_link_order(
2038        node: &dyn ModifierNode,
2039        entry: usize,
2040        path: &mut Vec<usize>,
2041        out: &mut Vec<NodeLink>,
2042    ) {
2043        out.push(NodeLink::Entry(NodePath::from_slice(entry, path)));
2044        let mut delegate_index = 0usize;
2045        node.for_each_delegate(&mut |child| {
2046            path.push(delegate_index);
2047            Self::enumerate_link_order(child, entry, path, out);
2048            path.pop();
2049            delegate_index += 1;
2050        });
2051    }
2052}
2053
2054impl<'a> ModifierChainNodeRef<'a> {
2055    /// Helper to get NodeState, properly handling RefCell for entries.
2056    /// Returns NodeState values by calling a closure with the state.
2057    fn with_state<R>(&self, f: impl FnOnce(&NodeState) -> R) -> R {
2058        match &self.link {
2059            NodeLink::Head => f(self.chain.head_sentinel.node_state()),
2060            NodeLink::Tail => f(self.chain.tail_sentinel.node_state()),
2061            NodeLink::Entry(path) => {
2062                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2063                // Navigate through delegates if path has them
2064                if path.delegates().is_empty() {
2065                    f(node_borrow.node_state())
2066                } else {
2067                    // Navigate to the delegate node
2068                    let mut current: &dyn ModifierNode = &**node_borrow;
2069                    for &delegate_index in path.delegates() {
2070                        if let Some(delegate) = nth_delegate(current, delegate_index) {
2071                            current = delegate;
2072                        } else {
2073                            // Fallback to root node state if delegate path is invalid
2074                            return f(node_borrow.node_state());
2075                        }
2076                    }
2077                    f(current.node_state())
2078                }
2079            }
2080        }
2081    }
2082
2083    /// Provides access to the node via a closure, properly handling RefCell borrows.
2084    /// Returns None for sentinel nodes.
2085    pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
2086        match &self.link {
2087            NodeLink::Head => None, // Head sentinel
2088            NodeLink::Tail => None, // Tail sentinel
2089            NodeLink::Entry(path) => {
2090                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2091                // Navigate through delegates if path has them
2092                if path.delegates().is_empty() {
2093                    Some(f(&**node_borrow))
2094                } else {
2095                    // Navigate to the delegate node
2096                    let mut current: &dyn ModifierNode = &**node_borrow;
2097                    for &delegate_index in path.delegates() {
2098                        if let Some(delegate) = nth_delegate(current, delegate_index) {
2099                            current = delegate;
2100                        } else {
2101                            // Return None if delegate path is invalid
2102                            return None;
2103                        }
2104                    }
2105                    Some(f(current))
2106                }
2107            }
2108        }
2109    }
2110
2111    /// Returns the parent reference, including sentinel head when applicable.
2112    #[inline]
2113    pub fn parent(&self) -> Option<Self> {
2114        self.with_state(|state| state.parent_link())
2115            .map(|link| self.chain.make_node_ref(link))
2116    }
2117
2118    /// Returns the child reference, including sentinel tail for the last entry.
2119    #[inline]
2120    pub fn child(&self) -> Option<Self> {
2121        self.with_state(|state| state.child_link())
2122            .map(|link| self.chain.make_node_ref(link))
2123    }
2124
2125    /// Returns the capability mask for this specific node.
2126    pub fn kind_set(&self) -> NodeCapabilities {
2127        match &self.link {
2128            NodeLink::Head | NodeLink::Tail => NodeCapabilities::empty(),
2129            NodeLink::Entry(_) => self.with_state(|state| state.capabilities()),
2130        }
2131    }
2132
2133    /// Returns the entry index backing this node when it is part of the chain.
2134    pub fn entry_index(&self) -> Option<usize> {
2135        match &self.link {
2136            NodeLink::Entry(path) => Some(path.entry()),
2137            _ => None,
2138        }
2139    }
2140
2141    /// Returns how many delegate hops separate this node from its root element.
2142    pub fn delegate_depth(&self) -> usize {
2143        match &self.link {
2144            NodeLink::Entry(path) => path.delegates().len(),
2145            _ => 0,
2146        }
2147    }
2148
2149    /// Returns the aggregated capability mask for the subtree rooted at this node.
2150    pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
2151        if self.is_tail() {
2152            NodeCapabilities::empty()
2153        } else {
2154            self.with_state(|state| state.aggregate_child_capabilities())
2155        }
2156    }
2157
2158    /// Returns true if this reference targets the sentinel head.
2159    pub fn is_head(&self) -> bool {
2160        matches!(self.link, NodeLink::Head)
2161    }
2162
2163    /// Returns true if this reference targets the sentinel tail.
2164    pub fn is_tail(&self) -> bool {
2165        matches!(self.link, NodeLink::Tail)
2166    }
2167
2168    /// Returns true if this reference targets either sentinel.
2169    pub fn is_sentinel(&self) -> bool {
2170        matches!(self.link, NodeLink::Head | NodeLink::Tail)
2171    }
2172
2173    /// Returns true if this node has any capability bits present in `mask`.
2174    pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
2175        !mask.is_empty() && self.kind_set().intersects(mask)
2176    }
2177
2178    /// Visits descendant nodes, optionally including `self`, in insertion order.
2179    pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
2180    where
2181        F: FnMut(ModifierChainNodeRef<'a>),
2182    {
2183        let mut current = if include_self {
2184            Some(self)
2185        } else {
2186            self.child()
2187        };
2188        while let Some(node) = current {
2189            if node.is_tail() {
2190                break;
2191            }
2192            if !node.is_sentinel() {
2193                f(node.clone());
2194            }
2195            current = node.child();
2196        }
2197    }
2198
2199    /// Visits descendant nodes that match `mask`, short-circuiting when possible.
2200    pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2201    where
2202        F: FnMut(ModifierChainNodeRef<'a>),
2203    {
2204        if mask.is_empty() {
2205            self.visit_descendants(include_self, f);
2206            return;
2207        }
2208
2209        if !self.aggregate_child_capabilities().intersects(mask) {
2210            return;
2211        }
2212
2213        self.visit_descendants(include_self, |node| {
2214            if node.kind_set().intersects(mask) {
2215                f(node);
2216            }
2217        });
2218    }
2219
2220    /// Visits ancestor nodes up to (but excluding) the sentinel head.
2221    pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
2222    where
2223        F: FnMut(ModifierChainNodeRef<'a>),
2224    {
2225        let mut current = if include_self {
2226            Some(self)
2227        } else {
2228            self.parent()
2229        };
2230        while let Some(node) = current {
2231            if node.is_head() {
2232                break;
2233            }
2234            f(node.clone());
2235            current = node.parent();
2236        }
2237    }
2238
2239    /// Visits ancestor nodes that match `mask`.
2240    pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2241    where
2242        F: FnMut(ModifierChainNodeRef<'a>),
2243    {
2244        if mask.is_empty() {
2245            self.visit_ancestors(include_self, f);
2246            return;
2247        }
2248
2249        self.visit_ancestors(include_self, |node| {
2250            if node.kind_set().intersects(mask) {
2251                f(node);
2252            }
2253        });
2254    }
2255
2256    /// Finds the nearest ancestor focus target node.
2257    ///
2258    /// This is useful for focus navigation to find the parent focusable
2259    /// component in the tree.
2260    pub fn find_parent_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2261        let mut result = None;
2262        self.clone()
2263            .visit_ancestors_matching(false, NodeCapabilities::FOCUS, |node| {
2264                if result.is_none() {
2265                    result = Some(node);
2266                }
2267            });
2268        result
2269    }
2270
2271    /// Finds the first descendant focus target node.
2272    ///
2273    /// This is useful for focus navigation to find the first focusable
2274    /// child component in the tree.
2275    pub fn find_first_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2276        let mut result = None;
2277        self.clone()
2278            .visit_descendants_matching(false, NodeCapabilities::FOCUS, |node| {
2279                if result.is_none() {
2280                    result = Some(node);
2281                }
2282            });
2283        result
2284    }
2285
2286    /// Returns true if this node or any ancestor has focus capability.
2287    pub fn has_focus_capability_in_ancestors(&self) -> bool {
2288        let mut found = false;
2289        self.clone()
2290            .visit_ancestors_matching(true, NodeCapabilities::FOCUS, |_| {
2291                found = true;
2292            });
2293        found
2294    }
2295}
2296
2297#[cfg(test)]
2298#[path = "tests/modifier_tests.rs"]
2299mod tests;