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    cached_hash: u64,
881}
882
883impl<E: ModifierNodeElement> TypedModifierElement<E> {
884    fn new(element: E) -> Self {
885        let mut hasher = DefaultHasher::new();
886        element.hash(&mut hasher);
887        Self {
888            element,
889            cached_hash: hasher.finish(),
890        }
891    }
892}
893
894impl<E> fmt::Debug for TypedModifierElement<E>
895where
896    E: ModifierNodeElement,
897{
898    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899        f.debug_struct("TypedModifierElement")
900            .field("type", &type_name::<E>())
901            .finish()
902    }
903}
904
905impl<E> AnyModifierElement for TypedModifierElement<E>
906where
907    E: ModifierNodeElement,
908{
909    fn node_type(&self) -> TypeId {
910        TypeId::of::<E::Node>()
911    }
912
913    fn element_type(&self) -> TypeId {
914        TypeId::of::<E>()
915    }
916
917    fn create_node(&self) -> Box<dyn ModifierNode> {
918        Box::new(self.element.create())
919    }
920
921    fn update_node(&self, node: &mut dyn ModifierNode) {
922        let typed = node
923            .as_any_mut()
924            .downcast_mut::<E::Node>()
925            .expect("modifier node type mismatch");
926        self.element.update(typed);
927    }
928
929    fn key(&self) -> Option<u64> {
930        self.element.key()
931    }
932
933    fn capabilities(&self) -> NodeCapabilities {
934        self.element.capabilities()
935    }
936
937    fn hash_code(&self) -> u64 {
938        self.cached_hash
939    }
940
941    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool {
942        other
943            .as_any()
944            .downcast_ref::<Self>()
945            .map(|typed| typed.element == self.element)
946            .unwrap_or(false)
947    }
948
949    fn inspector_name(&self) -> &'static str {
950        self.element.inspector_name()
951    }
952
953    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String)) {
954        self.element.inspector_properties(visitor);
955    }
956
957    fn requires_update(&self) -> bool {
958        self.element.always_update()
959    }
960
961    fn as_any(&self) -> &dyn Any {
962        self
963    }
964}
965
966/// Convenience helper for callers to construct a type-erased modifier
967/// element without having to mention the internal wrapper type.
968pub fn modifier_element<E: ModifierNodeElement>(element: E) -> DynModifierElement {
969    Rc::new(TypedModifierElement::new(element))
970}
971
972/// Boxed type-erased modifier element.
973pub type DynModifierElement = Rc<dyn AnyModifierElement>;
974
975#[derive(Clone, Copy, Debug, PartialEq, Eq)]
976enum TraversalDirection {
977    Forward,
978    Backward,
979}
980
981/// Iterator walking a modifier chain either from head-to-tail or tail-to-head.
982pub struct ModifierChainIter<'a> {
983    next: Option<ModifierChainNodeRef<'a>>,
984    direction: TraversalDirection,
985}
986
987impl<'a> ModifierChainIter<'a> {
988    fn new(start: Option<ModifierChainNodeRef<'a>>, direction: TraversalDirection) -> Self {
989        Self {
990            next: start,
991            direction,
992        }
993    }
994}
995
996impl<'a> Iterator for ModifierChainIter<'a> {
997    type Item = ModifierChainNodeRef<'a>;
998
999    #[inline]
1000    fn next(&mut self) -> Option<Self::Item> {
1001        let current = self.next.take()?;
1002        if current.is_sentinel() {
1003            self.next = None;
1004            return None;
1005        }
1006        self.next = match self.direction {
1007            TraversalDirection::Forward => current.child(),
1008            TraversalDirection::Backward => current.parent(),
1009        };
1010        Some(current)
1011    }
1012}
1013
1014impl<'a> std::iter::FusedIterator for ModifierChainIter<'a> {}
1015
1016#[derive(Debug)]
1017struct ModifierNodeEntry {
1018    element_type: TypeId,
1019    key: Option<u64>,
1020    hash_code: u64,
1021    element: DynModifierElement,
1022    node: Rc<RefCell<Box<dyn ModifierNode>>>,
1023    capabilities: NodeCapabilities,
1024}
1025
1026impl ModifierNodeEntry {
1027    fn new(
1028        element_type: TypeId,
1029        key: Option<u64>,
1030        element: DynModifierElement,
1031        node: Box<dyn ModifierNode>,
1032        hash_code: u64,
1033        capabilities: NodeCapabilities,
1034    ) -> Self {
1035        // Wrap the boxed node in Rc<RefCell<>> for shared ownership
1036        let node_rc = Rc::new(RefCell::new(node));
1037        let entry = Self {
1038            element_type,
1039            key,
1040            hash_code,
1041            element,
1042            node: Rc::clone(&node_rc),
1043            capabilities,
1044        };
1045        entry
1046            .node
1047            .borrow()
1048            .node_state()
1049            .set_capabilities(entry.capabilities);
1050        entry
1051    }
1052}
1053
1054fn visit_node_tree_mut(
1055    node: &mut dyn ModifierNode,
1056    visitor: &mut dyn FnMut(&mut dyn ModifierNode),
1057) {
1058    visitor(node);
1059    node.for_each_delegate_mut(&mut |child| visit_node_tree_mut(child, visitor));
1060}
1061
1062fn nth_delegate(node: &dyn ModifierNode, target: usize) -> Option<&dyn ModifierNode> {
1063    let mut current = 0usize;
1064    let mut result: Option<&dyn ModifierNode> = None;
1065    node.for_each_delegate(&mut |child| {
1066        if result.is_none() && current == target {
1067            result = Some(child);
1068        }
1069        current += 1;
1070    });
1071    result
1072}
1073
1074fn nth_delegate_mut(node: &mut dyn ModifierNode, target: usize) -> Option<&mut dyn ModifierNode> {
1075    let mut current = 0usize;
1076    let mut result: Option<&mut dyn ModifierNode> = None;
1077    node.for_each_delegate_mut(&mut |child| {
1078        if result.is_none() && current == target {
1079            result = Some(child);
1080        }
1081        current += 1;
1082    });
1083    result
1084}
1085
1086fn with_node_context<F, R>(
1087    node: &mut dyn ModifierNode,
1088    context: &mut dyn ModifierNodeContext,
1089    f: F,
1090) -> R
1091where
1092    F: FnOnce(&mut dyn ModifierNode, &mut dyn ModifierNodeContext) -> R,
1093{
1094    context.push_active_capabilities(node.node_state().capabilities());
1095    let result = f(node, context);
1096    context.pop_active_capabilities();
1097    result
1098}
1099
1100fn request_auto_invalidations(
1101    context: &mut dyn ModifierNodeContext,
1102    capabilities: NodeCapabilities,
1103) {
1104    if capabilities.is_empty() {
1105        return;
1106    }
1107
1108    context.push_active_capabilities(capabilities);
1109
1110    if capabilities.contains(NodeCapabilities::LAYOUT) {
1111        context.invalidate(InvalidationKind::Layout);
1112    }
1113    if capabilities.contains(NodeCapabilities::DRAW) {
1114        context.invalidate(InvalidationKind::Draw);
1115    }
1116    if capabilities.contains(NodeCapabilities::POINTER_INPUT) {
1117        context.invalidate(InvalidationKind::PointerInput);
1118    }
1119    if capabilities.contains(NodeCapabilities::SEMANTICS) {
1120        context.invalidate(InvalidationKind::Semantics);
1121    }
1122    if capabilities.contains(NodeCapabilities::FOCUS) {
1123        context.invalidate(InvalidationKind::Focus);
1124    }
1125
1126    context.pop_active_capabilities();
1127}
1128
1129/// Attaches a node tree by calling on_attach for all unattached nodes.
1130///
1131/// # Safety
1132/// Callers must ensure no immutable RefCell borrows are held on the node
1133/// when calling this function. The on_attach callback may trigger mutations
1134/// (invalidations, state updates, etc.) that require mutable access, which
1135/// would panic if an immutable borrow is held across the call.
1136fn attach_node_tree(node: &mut dyn ModifierNode, context: &mut dyn ModifierNodeContext) {
1137    visit_node_tree_mut(node, &mut |n| {
1138        if !n.node_state().is_attached() {
1139            n.node_state().set_attached(true);
1140            with_node_context(n, context, |node, ctx| node.on_attach(ctx));
1141        }
1142    });
1143}
1144
1145fn reset_node_tree(node: &mut dyn ModifierNode) {
1146    visit_node_tree_mut(node, &mut |n| n.on_reset());
1147}
1148
1149fn detach_node_tree(node: &mut dyn ModifierNode) {
1150    visit_node_tree_mut(node, &mut |n| {
1151        if n.node_state().is_attached() {
1152            n.on_detach();
1153            n.node_state().set_attached(false);
1154        }
1155        n.node_state().set_parent_link(None);
1156        n.node_state().set_child_link(None);
1157        n.node_state()
1158            .set_aggregate_child_capabilities(NodeCapabilities::empty());
1159    });
1160}
1161
1162/// Chain of modifier nodes attached to a layout node.
1163///
1164/// The chain tracks ownership of modifier nodes and reuses them across
1165/// updates when the incoming element list still contains a node of the
1166/// same type. Removed nodes detach automatically so callers do not need
1167/// to manually manage their lifetimes.
1168pub struct ModifierNodeChain {
1169    entries: Vec<ModifierNodeEntry>,
1170    aggregated_capabilities: NodeCapabilities,
1171    head_aggregate_child_capabilities: NodeCapabilities,
1172    head_sentinel: Box<SentinelNode>,
1173    tail_sentinel: Box<SentinelNode>,
1174    ordered_nodes: Vec<NodeLink>,
1175    // Scratch buffers reused during update to avoid repeated allocations
1176    scratch_old_used: Vec<bool>,
1177    scratch_match_order: Vec<Option<usize>>,
1178    scratch_final_slots: Vec<Option<ModifierNodeEntry>>,
1179    scratch_elements: Vec<DynModifierElement>,
1180}
1181
1182struct SentinelNode {
1183    state: NodeState,
1184}
1185
1186impl SentinelNode {
1187    fn new() -> Self {
1188        Self {
1189            state: NodeState::sentinel(),
1190        }
1191    }
1192}
1193
1194impl DelegatableNode for SentinelNode {
1195    fn node_state(&self) -> &NodeState {
1196        &self.state
1197    }
1198}
1199
1200impl ModifierNode for SentinelNode {}
1201
1202#[derive(Clone)]
1203pub struct ModifierChainNodeRef<'a> {
1204    chain: &'a ModifierNodeChain,
1205    link: NodeLink,
1206}
1207
1208impl Default for ModifierNodeChain {
1209    fn default() -> Self {
1210        Self::new()
1211    }
1212}
1213
1214/// Index structure for O(1) modifier entry lookups during update.
1215///
1216/// This avoids O(n²) complexity by pre-building hash maps that allow constant-time
1217/// lookups for matching entries by key, hash, or type.
1218/// Uses FxHashMap for faster hashing of TypeId keys.
1219struct EntryIndex {
1220    /// Map (TypeId, key) → index for keyed entries
1221    keyed: FxHashMap<(TypeId, u64), Vec<usize>>,
1222    /// Map (TypeId, hash) → indices for unkeyed entries with specific hash
1223    hashed: FxHashMap<(TypeId, u64), Vec<usize>>,
1224    /// Map TypeId → indices for all unkeyed entries of that type
1225    typed: FxHashMap<TypeId, Vec<usize>>,
1226}
1227
1228impl EntryIndex {
1229    fn build(entries: &[ModifierNodeEntry]) -> Self {
1230        let mut keyed = FxHashMap::default();
1231        let mut hashed = FxHashMap::default();
1232        let mut typed = FxHashMap::default();
1233
1234        for (i, entry) in entries.iter().enumerate() {
1235            if let Some(key_value) = entry.key {
1236                // Keyed entry
1237                keyed
1238                    .entry((entry.element_type, key_value))
1239                    .or_insert_with(Vec::new)
1240                    .push(i);
1241            } else {
1242                // Unkeyed entry - add to both hash and type indices
1243                hashed
1244                    .entry((entry.element_type, entry.hash_code))
1245                    .or_insert_with(Vec::new)
1246                    .push(i);
1247                typed
1248                    .entry(entry.element_type)
1249                    .or_insert_with(Vec::new)
1250                    .push(i);
1251            }
1252        }
1253
1254        Self {
1255            keyed,
1256            hashed,
1257            typed,
1258        }
1259    }
1260
1261    /// Find the best matching entry for reuse.
1262    ///
1263    /// Matching priority (from highest to lowest):
1264    /// 1. Keyed match: same type + same key
1265    /// 2. Exact match: same type + no key + same hash + equals_element
1266    /// 3. Type match: same type + no key (will require update)
1267    fn find_match(
1268        &self,
1269        entries: &[ModifierNodeEntry],
1270        used: &[bool],
1271        element_type: TypeId,
1272        key: Option<u64>,
1273        hash_code: u64,
1274        element: &DynModifierElement,
1275    ) -> Option<usize> {
1276        if let Some(key_value) = key {
1277            // Priority 1: Keyed lookup - O(1)
1278            if let Some(candidates) = self.keyed.get(&(element_type, key_value)) {
1279                for &i in candidates {
1280                    if !used[i] {
1281                        return Some(i);
1282                    }
1283                }
1284            }
1285        } else {
1286            // Priority 2: Exact match (hash + equality) - O(1) lookup + O(k) equality checks
1287            if let Some(candidates) = self.hashed.get(&(element_type, hash_code)) {
1288                for &i in candidates {
1289                    if !used[i] && entries[i].element.as_ref().equals_element(element.as_ref()) {
1290                        return Some(i);
1291                    }
1292                }
1293            }
1294
1295            // Priority 3: Type match only - O(1) lookup + O(k) scan
1296            if let Some(candidates) = self.typed.get(&element_type) {
1297                for &i in candidates {
1298                    if !used[i] {
1299                        return Some(i);
1300                    }
1301                }
1302            }
1303        }
1304
1305        None
1306    }
1307}
1308
1309impl ModifierNodeChain {
1310    pub fn new() -> Self {
1311        let mut chain = Self {
1312            entries: Vec::new(),
1313            aggregated_capabilities: NodeCapabilities::empty(),
1314            head_aggregate_child_capabilities: NodeCapabilities::empty(),
1315            head_sentinel: Box::new(SentinelNode::new()),
1316            tail_sentinel: Box::new(SentinelNode::new()),
1317            ordered_nodes: Vec::new(),
1318            scratch_old_used: Vec::new(),
1319            scratch_match_order: Vec::new(),
1320            scratch_final_slots: Vec::new(),
1321            scratch_elements: Vec::new(),
1322        };
1323        chain.sync_chain_links();
1324        chain
1325    }
1326
1327    /// Detaches all nodes in the chain.
1328    pub fn detach_nodes(&mut self) {
1329        for entry in &self.entries {
1330            detach_node_tree(&mut **entry.node.borrow_mut());
1331        }
1332    }
1333
1334    /// Attaches all nodes in the chain.
1335    pub fn attach_nodes(&mut self, context: &mut dyn ModifierNodeContext) {
1336        for entry in &self.entries {
1337            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1338        }
1339    }
1340
1341    /// Rebuilds the internal chain links (parent/child relationships).
1342    /// This should be called if nodes have been detached but are intended to be reused.
1343    pub fn repair_chain(&mut self) {
1344        self.sync_chain_links();
1345    }
1346
1347    /// Reconcile the chain against the provided elements, attaching newly
1348    /// created nodes and detaching nodes that are no longer required.
1349    ///
1350    /// This method delegates to `update_from_ref_iter` which handles the
1351    /// actual reconciliation logic.
1352    pub fn update_from_slice(
1353        &mut self,
1354        elements: &[DynModifierElement],
1355        context: &mut dyn ModifierNodeContext,
1356    ) {
1357        self.update_from_ref_iter(elements.iter(), context);
1358    }
1359
1360    /// Reconcile the chain against the provided iterator of element references.
1361    ///
1362    /// This is the preferred method as it avoids requiring a collected slice,
1363    /// enabling zero-allocation traversal of modifier trees.
1364    pub fn update_from_ref_iter<'a, I>(
1365        &mut self,
1366        elements: I,
1367        context: &mut dyn ModifierNodeContext,
1368    ) where
1369        I: Iterator<Item = &'a DynModifierElement>,
1370    {
1371        // Fast path: try to match elements sequentially without building index.
1372        // If all elements match in order (same type and key at same position),
1373        // we skip the expensive EntryIndex building. This is O(n) instead of O(n + m).
1374        let old_len = self.entries.len();
1375        let mut fast_path_failed_at: Option<usize> = None;
1376        let mut elements_count = 0;
1377
1378        // Collect elements we need to process in slow path
1379        self.scratch_elements.clear();
1380
1381        for (idx, element) in elements.enumerate() {
1382            elements_count = idx + 1;
1383
1384            if fast_path_failed_at.is_none() && idx < old_len {
1385                let entry = &mut self.entries[idx];
1386                let same_type = entry.element_type == element.element_type();
1387                let same_key = entry.key == element.key();
1388                let same_hash = entry.hash_code == element.hash_code();
1389
1390                // Fast path requires same type, key, AND hash to ensure we're not
1391                // breaking reordering semantics (where elements can move positions)
1392                if same_type && same_key && same_hash {
1393                    // Fast path: element matches at same position
1394                    let same_element = entry.element.as_ref().equals_element(element.as_ref());
1395                    let capabilities = element.capabilities();
1396
1397                    // Re-attach node if it was detached during a previous update
1398                    {
1399                        let node_borrow = entry.node.borrow();
1400                        if !node_borrow.node_state().is_attached() {
1401                            drop(node_borrow);
1402                            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1403                        }
1404                    }
1405
1406                    // Optimize updates: only call update_node if element changed
1407                    let needs_update = !same_element || element.requires_update();
1408                    if needs_update {
1409                        element.update_node(&mut **entry.node.borrow_mut());
1410                        entry.element = element.clone();
1411                        entry.hash_code = element.hash_code();
1412                        request_auto_invalidations(context, capabilities);
1413                    }
1414
1415                    // Always update metadata
1416                    entry.capabilities = capabilities;
1417                    entry
1418                        .node
1419                        .borrow()
1420                        .node_state()
1421                        .set_capabilities(capabilities);
1422                    continue;
1423                }
1424                // Fast path failed - mark position and fall through to collect
1425                fast_path_failed_at = Some(idx);
1426            }
1427
1428            // Collect element for slow path processing
1429            self.scratch_elements.push(element.clone());
1430        }
1431
1432        // Fast path succeeded if:
1433        // 1. No mismatch was found (fast_path_failed_at is None)
1434        // 2. All elements were processed via fast path (scratch_elements is empty)
1435        // Note: If old_len=0 and we have new elements, scratch_elements won't be empty
1436        if fast_path_failed_at.is_none() && self.scratch_elements.is_empty() {
1437            // Detach any removed entries (elements_count <= old_len guaranteed here)
1438            if elements_count < self.entries.len() {
1439                for entry in self.entries.drain(elements_count..) {
1440                    detach_node_tree(&mut **entry.node.borrow_mut());
1441                }
1442            }
1443            self.sync_chain_links();
1444            return;
1445        }
1446
1447        // Slow path: need full reconciliation starting from failure point
1448        // If no mismatch but we have extra elements, fail_idx is the old length
1449        let fail_idx = fast_path_failed_at.unwrap_or(old_len);
1450
1451        // Move entries that were already processed to a safe place
1452        let mut old_entries: Vec<ModifierNodeEntry> = self.entries.drain(fail_idx..).collect();
1453        let processed_entries_len = self.entries.len();
1454        let old_len = old_entries.len();
1455
1456        // Reuse scratch buffers for the remaining entries only
1457        self.scratch_old_used.clear();
1458        self.scratch_old_used.resize(old_len, false);
1459
1460        self.scratch_match_order.clear();
1461        self.scratch_match_order.resize(old_len, None);
1462
1463        // Build index only for unprocessed entries
1464        let index = EntryIndex::build(&old_entries);
1465
1466        let new_elements_count = self.scratch_elements.len();
1467        self.scratch_final_slots.clear();
1468        self.scratch_final_slots.reserve(new_elements_count);
1469
1470        // Process each remaining element
1471        for (new_pos, element) in self.scratch_elements.drain(..).enumerate() {
1472            self.scratch_final_slots.push(None);
1473            let element_type = element.element_type();
1474            let key = element.key();
1475            let hash_code = element.hash_code();
1476            let capabilities = element.capabilities();
1477
1478            // Find best matching old entry via index
1479            let matched_idx = index.find_match(
1480                &old_entries,
1481                &self.scratch_old_used,
1482                element_type,
1483                key,
1484                hash_code,
1485                &element,
1486            );
1487
1488            if let Some(idx) = matched_idx {
1489                // Reuse existing entry
1490                self.scratch_old_used[idx] = true;
1491                self.scratch_match_order[idx] = Some(new_pos);
1492                let entry = &mut old_entries[idx];
1493
1494                // Check if element actually changed
1495                let same_element = entry.element.as_ref().equals_element(element.as_ref());
1496
1497                // Re-attach node if it was detached
1498                {
1499                    let node_borrow = entry.node.borrow();
1500                    if !node_borrow.node_state().is_attached() {
1501                        drop(node_borrow);
1502                        attach_node_tree(&mut **entry.node.borrow_mut(), context);
1503                    }
1504                }
1505
1506                // Optimize updates: only call update_node if element changed
1507                let needs_update = !same_element || element.requires_update();
1508                if needs_update {
1509                    element.update_node(&mut **entry.node.borrow_mut());
1510                    entry.element = element;
1511                    entry.hash_code = hash_code;
1512                    request_auto_invalidations(context, capabilities);
1513                }
1514
1515                // Always update metadata
1516                entry.key = key;
1517                entry.element_type = element_type;
1518                entry.capabilities = capabilities;
1519                entry
1520                    .node
1521                    .borrow()
1522                    .node_state()
1523                    .set_capabilities(capabilities);
1524            } else {
1525                // Create new entry
1526                let entry = ModifierNodeEntry::new(
1527                    element_type,
1528                    key,
1529                    element.clone(),
1530                    element.create_node(),
1531                    hash_code,
1532                    capabilities,
1533                );
1534                attach_node_tree(&mut **entry.node.borrow_mut(), context);
1535                element.update_node(&mut **entry.node.borrow_mut());
1536                request_auto_invalidations(context, capabilities);
1537                self.scratch_final_slots[new_pos] = Some(entry);
1538            }
1539        }
1540
1541        // Place matched entries in their new positions
1542        for (i, entry) in old_entries.into_iter().enumerate() {
1543            if self.scratch_old_used[i] {
1544                let pos = self.scratch_match_order[i]
1545                    .expect("Missing match order for used modifier entry");
1546                self.scratch_final_slots[pos] = Some(entry);
1547            } else {
1548                detach_node_tree(&mut **entry.node.borrow_mut());
1549            }
1550        }
1551
1552        // Append processed entries to self.entries
1553        self.entries.reserve(self.scratch_final_slots.len());
1554        for slot in self.scratch_final_slots.drain(..) {
1555            let entry = slot.expect("Missing modifier entry for reconciled position");
1556            self.entries.push(entry);
1557        }
1558
1559        debug_assert_eq!(
1560            self.entries.len(),
1561            processed_entries_len + new_elements_count
1562        );
1563        self.sync_chain_links();
1564    }
1565
1566    /// Convenience wrapper that accepts any iterator of type-erased
1567    /// modifier elements. Elements are collected into a temporary vector
1568    /// before reconciliation.
1569    pub fn update<I>(&mut self, elements: I, context: &mut dyn ModifierNodeContext)
1570    where
1571        I: IntoIterator<Item = DynModifierElement>,
1572    {
1573        let collected: Vec<DynModifierElement> = elements.into_iter().collect();
1574        self.update_from_slice(&collected, context);
1575    }
1576
1577    /// Resets all nodes in the chain. This mirrors the behaviour of
1578    /// Jetpack Compose's `onReset` callback.
1579    pub fn reset(&mut self) {
1580        for entry in &mut self.entries {
1581            reset_node_tree(&mut **entry.node.borrow_mut());
1582        }
1583    }
1584
1585    /// Detaches every node in the chain and clears internal storage.
1586    pub fn detach_all(&mut self) {
1587        for entry in std::mem::take(&mut self.entries) {
1588            detach_node_tree(&mut **entry.node.borrow_mut());
1589            {
1590                let node_borrow = entry.node.borrow();
1591                let state = node_borrow.node_state();
1592                state.set_capabilities(NodeCapabilities::empty());
1593            }
1594        }
1595        self.aggregated_capabilities = NodeCapabilities::empty();
1596        self.head_aggregate_child_capabilities = NodeCapabilities::empty();
1597        self.ordered_nodes.clear();
1598        self.sync_chain_links();
1599    }
1600
1601    pub fn len(&self) -> usize {
1602        self.entries.len()
1603    }
1604
1605    pub fn is_empty(&self) -> bool {
1606        self.entries.is_empty()
1607    }
1608
1609    /// Returns the aggregated capability mask for the entire chain.
1610    pub fn capabilities(&self) -> NodeCapabilities {
1611        self.aggregated_capabilities
1612    }
1613
1614    /// Returns true if the chain contains at least one node with the requested capability.
1615    pub fn has_capability(&self, capability: NodeCapabilities) -> bool {
1616        self.aggregated_capabilities.contains(capability)
1617    }
1618
1619    /// Returns the sentinel head reference for traversal.
1620    pub fn head(&self) -> ModifierChainNodeRef<'_> {
1621        self.make_node_ref(NodeLink::Head)
1622    }
1623
1624    /// Returns the sentinel tail reference for traversal.
1625    pub fn tail(&self) -> ModifierChainNodeRef<'_> {
1626        self.make_node_ref(NodeLink::Tail)
1627    }
1628
1629    /// Iterates over the chain from head to tail, skipping sentinels.
1630    pub fn head_to_tail(&self) -> ModifierChainIter<'_> {
1631        ModifierChainIter::new(self.head().child(), TraversalDirection::Forward)
1632    }
1633
1634    /// Iterates over the chain from tail to head, skipping sentinels.
1635    pub fn tail_to_head(&self) -> ModifierChainIter<'_> {
1636        ModifierChainIter::new(self.tail().parent(), TraversalDirection::Backward)
1637    }
1638
1639    /// Calls `f` for every node in insertion order.
1640    pub fn for_each_forward<F>(&self, mut f: F)
1641    where
1642        F: FnMut(ModifierChainNodeRef<'_>),
1643    {
1644        for node in self.head_to_tail() {
1645            f(node);
1646        }
1647    }
1648
1649    /// Calls `f` for every node containing any capability from `mask`.
1650    pub fn for_each_forward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
1651    where
1652        F: FnMut(ModifierChainNodeRef<'_>),
1653    {
1654        if mask.is_empty() {
1655            self.for_each_forward(f);
1656            return;
1657        }
1658
1659        if !self.head().aggregate_child_capabilities().intersects(mask) {
1660            return;
1661        }
1662
1663        for node in self.head_to_tail() {
1664            if node.kind_set().intersects(mask) {
1665                f(node);
1666            }
1667        }
1668    }
1669
1670    /// Calls `f` for every node containing any capability from `mask`, providing the node ref.
1671    pub fn for_each_node_with_capability<F>(&self, mask: NodeCapabilities, mut f: F)
1672    where
1673        F: FnMut(ModifierChainNodeRef<'_>, &dyn ModifierNode),
1674    {
1675        self.for_each_forward_matching(mask, |node_ref| {
1676            node_ref.with_node(|node| f(node_ref.clone(), node));
1677        });
1678    }
1679
1680    /// Calls `f` for every node in reverse insertion order.
1681    pub fn for_each_backward<F>(&self, mut f: F)
1682    where
1683        F: FnMut(ModifierChainNodeRef<'_>),
1684    {
1685        for node in self.tail_to_head() {
1686            f(node);
1687        }
1688    }
1689
1690    /// Calls `f` for every node in reverse order that matches `mask`.
1691    pub fn for_each_backward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
1692    where
1693        F: FnMut(ModifierChainNodeRef<'_>),
1694    {
1695        if mask.is_empty() {
1696            self.for_each_backward(f);
1697            return;
1698        }
1699
1700        if !self.head().aggregate_child_capabilities().intersects(mask) {
1701            return;
1702        }
1703
1704        for node in self.tail_to_head() {
1705            if node.kind_set().intersects(mask) {
1706                f(node);
1707            }
1708        }
1709    }
1710
1711    /// Returns a node reference for the entry at `index`.
1712    pub fn node_ref_at(&self, index: usize) -> Option<ModifierChainNodeRef<'_>> {
1713        if index >= self.entries.len() {
1714            None
1715        } else {
1716            Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))))
1717        }
1718    }
1719
1720    /// Returns the node reference that owns `node`.
1721    pub fn find_node_ref(&self, node: &dyn ModifierNode) -> Option<ModifierChainNodeRef<'_>> {
1722        fn node_data_ptr(node: &dyn ModifierNode) -> *const () {
1723            node as *const dyn ModifierNode as *const ()
1724        }
1725
1726        let target = node_data_ptr(node);
1727        for (index, entry) in self.entries.iter().enumerate() {
1728            if node_data_ptr(&**entry.node.borrow()) == target {
1729                return Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))));
1730            }
1731        }
1732
1733        self.ordered_nodes.iter().find_map(|link| {
1734            if matches!(link, NodeLink::Entry(path) if path.delegates().is_empty()) {
1735                return None;
1736            }
1737            let matches_target = match link {
1738                NodeLink::Head => node_data_ptr(self.head_sentinel.as_ref()) == target,
1739                NodeLink::Tail => node_data_ptr(self.tail_sentinel.as_ref()) == target,
1740                NodeLink::Entry(path) => {
1741                    let node_borrow = self.entries[path.entry()].node.borrow();
1742                    node_data_ptr(&**node_borrow) == target
1743                }
1744            };
1745            if matches_target {
1746                Some(self.make_node_ref(link.clone()))
1747            } else {
1748                None
1749            }
1750        })
1751    }
1752
1753    /// Downcasts the node at `index` to the requested type.
1754    /// Returns a `Ref` guard that dereferences to the node type.
1755    pub fn node<N: ModifierNode + 'static>(&self, index: usize) -> Option<std::cell::Ref<'_, N>> {
1756        self.entries.get(index).and_then(|entry| {
1757            std::cell::Ref::filter_map(entry.node.borrow(), |boxed_node| {
1758                boxed_node.as_any().downcast_ref::<N>()
1759            })
1760            .ok()
1761        })
1762    }
1763
1764    /// Downcasts the node at `index` to the requested mutable type.
1765    /// Returns a `RefMut` guard that dereferences to the node type.
1766    pub fn node_mut<N: ModifierNode + 'static>(
1767        &self,
1768        index: usize,
1769    ) -> Option<std::cell::RefMut<'_, N>> {
1770        self.entries.get(index).and_then(|entry| {
1771            std::cell::RefMut::filter_map(entry.node.borrow_mut(), |boxed_node| {
1772                boxed_node.as_any_mut().downcast_mut::<N>()
1773            })
1774            .ok()
1775        })
1776    }
1777
1778    /// Returns an Rc clone of the node at the given index for shared ownership.
1779    /// This is used by coordinators to hold direct references to nodes.
1780    pub fn get_node_rc(&self, index: usize) -> Option<Rc<RefCell<Box<dyn ModifierNode>>>> {
1781        self.entries.get(index).map(|entry| Rc::clone(&entry.node))
1782    }
1783
1784    /// Returns true if the chain contains any nodes matching the given invalidation kind.
1785    pub fn has_nodes_for_invalidation(&self, kind: InvalidationKind) -> bool {
1786        self.aggregated_capabilities
1787            .contains(NodeCapabilities::for_invalidation(kind))
1788    }
1789
1790    /// Visits every node in insertion order together with its capability mask.
1791    pub fn visit_nodes<F>(&self, mut f: F)
1792    where
1793        F: FnMut(&dyn ModifierNode, NodeCapabilities),
1794    {
1795        for link in &self.ordered_nodes {
1796            match link {
1797                NodeLink::Head => {
1798                    let node = self.head_sentinel.as_ref();
1799                    f(node, node.node_state().capabilities());
1800                }
1801                NodeLink::Tail => {
1802                    let node = self.tail_sentinel.as_ref();
1803                    f(node, node.node_state().capabilities());
1804                }
1805                NodeLink::Entry(path) => {
1806                    let node_borrow = self.entries[path.entry()].node.borrow();
1807                    // Navigate through delegates if path has them
1808                    if path.delegates().is_empty() {
1809                        f(&**node_borrow, node_borrow.node_state().capabilities());
1810                    } else {
1811                        // Navigate to the delegate node
1812                        let mut current: &dyn ModifierNode = &**node_borrow;
1813                        for &delegate_index in path.delegates() {
1814                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1815                                current = delegate;
1816                            } else {
1817                                return; // Invalid delegate path
1818                            }
1819                        }
1820                        f(current, current.node_state().capabilities());
1821                    }
1822                }
1823            }
1824        }
1825    }
1826
1827    /// Visits every node mutably in insertion order together with its capability mask.
1828    pub fn visit_nodes_mut<F>(&mut self, mut f: F)
1829    where
1830        F: FnMut(&mut dyn ModifierNode, NodeCapabilities),
1831    {
1832        for index in 0..self.ordered_nodes.len() {
1833            let link = self.ordered_nodes[index].clone();
1834            match link {
1835                NodeLink::Head => {
1836                    let node = self.head_sentinel.as_mut();
1837                    let capabilities = node.node_state().capabilities();
1838                    f(node, capabilities);
1839                }
1840                NodeLink::Tail => {
1841                    let node = self.tail_sentinel.as_mut();
1842                    let capabilities = node.node_state().capabilities();
1843                    f(node, capabilities);
1844                }
1845                NodeLink::Entry(path) => {
1846                    let mut node_borrow = self.entries[path.entry()].node.borrow_mut();
1847                    // Navigate through delegates if path has them
1848                    if path.delegates().is_empty() {
1849                        let capabilities = node_borrow.node_state().capabilities();
1850                        f(&mut **node_borrow, capabilities);
1851                    } else {
1852                        // Navigate to the delegate node mutably
1853                        let mut current: &mut dyn ModifierNode = &mut **node_borrow;
1854                        for &delegate_index in path.delegates() {
1855                            if let Some(delegate) = nth_delegate_mut(current, delegate_index) {
1856                                current = delegate;
1857                            } else {
1858                                return; // Invalid delegate path
1859                            }
1860                        }
1861                        let capabilities = current.node_state().capabilities();
1862                        f(current, capabilities);
1863                    }
1864                }
1865            }
1866        }
1867    }
1868
1869    fn make_node_ref(&self, link: NodeLink) -> ModifierChainNodeRef<'_> {
1870        ModifierChainNodeRef { chain: self, link }
1871    }
1872
1873    fn sync_chain_links(&mut self) {
1874        self.rebuild_ordered_nodes();
1875
1876        self.head_sentinel.node_state().set_parent_link(None);
1877        self.tail_sentinel.node_state().set_child_link(None);
1878
1879        if self.ordered_nodes.is_empty() {
1880            self.head_sentinel
1881                .node_state()
1882                .set_child_link(Some(NodeLink::Tail));
1883            self.tail_sentinel
1884                .node_state()
1885                .set_parent_link(Some(NodeLink::Head));
1886            self.aggregated_capabilities = NodeCapabilities::empty();
1887            self.head_aggregate_child_capabilities = NodeCapabilities::empty();
1888            self.head_sentinel
1889                .node_state()
1890                .set_aggregate_child_capabilities(NodeCapabilities::empty());
1891            self.tail_sentinel
1892                .node_state()
1893                .set_aggregate_child_capabilities(NodeCapabilities::empty());
1894            return;
1895        }
1896
1897        let mut previous = NodeLink::Head;
1898        for link in self.ordered_nodes.iter().cloned() {
1899            // Set child link on previous
1900            match &previous {
1901                NodeLink::Head => self
1902                    .head_sentinel
1903                    .node_state()
1904                    .set_child_link(Some(link.clone())),
1905                NodeLink::Tail => self
1906                    .tail_sentinel
1907                    .node_state()
1908                    .set_child_link(Some(link.clone())),
1909                NodeLink::Entry(path) => {
1910                    let node_borrow = self.entries[path.entry()].node.borrow();
1911                    // Navigate to delegate if needed
1912                    if path.delegates().is_empty() {
1913                        node_borrow.node_state().set_child_link(Some(link.clone()));
1914                    } else {
1915                        let mut current: &dyn ModifierNode = &**node_borrow;
1916                        for &delegate_index in path.delegates() {
1917                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1918                                current = delegate;
1919                            }
1920                        }
1921                        current.node_state().set_child_link(Some(link.clone()));
1922                    }
1923                }
1924            }
1925            // Set parent link on current
1926            match &link {
1927                NodeLink::Head => self
1928                    .head_sentinel
1929                    .node_state()
1930                    .set_parent_link(Some(previous.clone())),
1931                NodeLink::Tail => self
1932                    .tail_sentinel
1933                    .node_state()
1934                    .set_parent_link(Some(previous.clone())),
1935                NodeLink::Entry(path) => {
1936                    let node_borrow = self.entries[path.entry()].node.borrow();
1937                    // Navigate to delegate if needed
1938                    if path.delegates().is_empty() {
1939                        node_borrow
1940                            .node_state()
1941                            .set_parent_link(Some(previous.clone()));
1942                    } else {
1943                        let mut current: &dyn ModifierNode = &**node_borrow;
1944                        for &delegate_index in path.delegates() {
1945                            if let Some(delegate) = nth_delegate(current, delegate_index) {
1946                                current = delegate;
1947                            }
1948                        }
1949                        current.node_state().set_parent_link(Some(previous.clone()));
1950                    }
1951                }
1952            }
1953            previous = link;
1954        }
1955
1956        // Set child link on last node to Tail
1957        match &previous {
1958            NodeLink::Head => self
1959                .head_sentinel
1960                .node_state()
1961                .set_child_link(Some(NodeLink::Tail)),
1962            NodeLink::Tail => self
1963                .tail_sentinel
1964                .node_state()
1965                .set_child_link(Some(NodeLink::Tail)),
1966            NodeLink::Entry(path) => {
1967                let node_borrow = self.entries[path.entry()].node.borrow();
1968                // Navigate to delegate if needed
1969                if path.delegates().is_empty() {
1970                    node_borrow
1971                        .node_state()
1972                        .set_child_link(Some(NodeLink::Tail));
1973                } else {
1974                    let mut current: &dyn ModifierNode = &**node_borrow;
1975                    for &delegate_index in path.delegates() {
1976                        if let Some(delegate) = nth_delegate(current, delegate_index) {
1977                            current = delegate;
1978                        }
1979                    }
1980                    current.node_state().set_child_link(Some(NodeLink::Tail));
1981                }
1982            }
1983        }
1984        self.tail_sentinel
1985            .node_state()
1986            .set_parent_link(Some(previous.clone()));
1987        self.tail_sentinel.node_state().set_child_link(None);
1988
1989        let mut aggregate = NodeCapabilities::empty();
1990        for link in self.ordered_nodes.iter().rev() {
1991            match link {
1992                NodeLink::Head => {
1993                    let state = self.head_sentinel.node_state();
1994                    aggregate |= state.capabilities();
1995                    state.set_aggregate_child_capabilities(aggregate);
1996                }
1997                NodeLink::Tail => {
1998                    let state = self.tail_sentinel.node_state();
1999                    aggregate |= state.capabilities();
2000                    state.set_aggregate_child_capabilities(aggregate);
2001                }
2002                NodeLink::Entry(path) => {
2003                    let node_borrow = self.entries[path.entry()].node.borrow();
2004                    // Navigate to delegate if needed
2005                    let state = if path.delegates().is_empty() {
2006                        node_borrow.node_state()
2007                    } else {
2008                        let mut current: &dyn ModifierNode = &**node_borrow;
2009                        for &delegate_index in path.delegates() {
2010                            if let Some(delegate) = nth_delegate(current, delegate_index) {
2011                                current = delegate;
2012                            }
2013                        }
2014                        current.node_state()
2015                    };
2016                    aggregate |= state.capabilities();
2017                    state.set_aggregate_child_capabilities(aggregate);
2018                }
2019            }
2020        }
2021
2022        self.aggregated_capabilities = aggregate;
2023        self.head_aggregate_child_capabilities = aggregate;
2024        self.head_sentinel
2025            .node_state()
2026            .set_aggregate_child_capabilities(aggregate);
2027        self.tail_sentinel
2028            .node_state()
2029            .set_aggregate_child_capabilities(NodeCapabilities::empty());
2030    }
2031
2032    fn rebuild_ordered_nodes(&mut self) {
2033        self.ordered_nodes.clear();
2034        for (index, entry) in self.entries.iter().enumerate() {
2035            let mut path = Vec::new();
2036            let node_borrow = entry.node.borrow();
2037            Self::enumerate_link_order(&**node_borrow, index, &mut path, &mut self.ordered_nodes);
2038        }
2039    }
2040
2041    fn enumerate_link_order(
2042        node: &dyn ModifierNode,
2043        entry: usize,
2044        path: &mut Vec<usize>,
2045        out: &mut Vec<NodeLink>,
2046    ) {
2047        out.push(NodeLink::Entry(NodePath::from_slice(entry, path)));
2048        let mut delegate_index = 0usize;
2049        node.for_each_delegate(&mut |child| {
2050            path.push(delegate_index);
2051            Self::enumerate_link_order(child, entry, path, out);
2052            path.pop();
2053            delegate_index += 1;
2054        });
2055    }
2056}
2057
2058impl<'a> ModifierChainNodeRef<'a> {
2059    /// Helper to get NodeState, properly handling RefCell for entries.
2060    /// Returns NodeState values by calling a closure with the state.
2061    fn with_state<R>(&self, f: impl FnOnce(&NodeState) -> R) -> R {
2062        match &self.link {
2063            NodeLink::Head => f(self.chain.head_sentinel.node_state()),
2064            NodeLink::Tail => f(self.chain.tail_sentinel.node_state()),
2065            NodeLink::Entry(path) => {
2066                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2067                // Navigate through delegates if path has them
2068                if path.delegates().is_empty() {
2069                    f(node_borrow.node_state())
2070                } else {
2071                    // Navigate to the delegate node
2072                    let mut current: &dyn ModifierNode = &**node_borrow;
2073                    for &delegate_index in path.delegates() {
2074                        if let Some(delegate) = nth_delegate(current, delegate_index) {
2075                            current = delegate;
2076                        } else {
2077                            // Fallback to root node state if delegate path is invalid
2078                            return f(node_borrow.node_state());
2079                        }
2080                    }
2081                    f(current.node_state())
2082                }
2083            }
2084        }
2085    }
2086
2087    /// Provides access to the node via a closure, properly handling RefCell borrows.
2088    /// Returns None for sentinel nodes.
2089    pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
2090        match &self.link {
2091            NodeLink::Head => None, // Head sentinel
2092            NodeLink::Tail => None, // Tail sentinel
2093            NodeLink::Entry(path) => {
2094                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2095                // Navigate through delegates if path has them
2096                if path.delegates().is_empty() {
2097                    Some(f(&**node_borrow))
2098                } else {
2099                    // Navigate to the delegate node
2100                    let mut current: &dyn ModifierNode = &**node_borrow;
2101                    for &delegate_index in path.delegates() {
2102                        if let Some(delegate) = nth_delegate(current, delegate_index) {
2103                            current = delegate;
2104                        } else {
2105                            // Return None if delegate path is invalid
2106                            return None;
2107                        }
2108                    }
2109                    Some(f(current))
2110                }
2111            }
2112        }
2113    }
2114
2115    /// Returns the parent reference, including sentinel head when applicable.
2116    #[inline]
2117    pub fn parent(&self) -> Option<Self> {
2118        self.with_state(|state| state.parent_link())
2119            .map(|link| self.chain.make_node_ref(link))
2120    }
2121
2122    /// Returns the child reference, including sentinel tail for the last entry.
2123    #[inline]
2124    pub fn child(&self) -> Option<Self> {
2125        self.with_state(|state| state.child_link())
2126            .map(|link| self.chain.make_node_ref(link))
2127    }
2128
2129    /// Returns the capability mask for this specific node.
2130    pub fn kind_set(&self) -> NodeCapabilities {
2131        match &self.link {
2132            NodeLink::Head | NodeLink::Tail => NodeCapabilities::empty(),
2133            NodeLink::Entry(_) => self.with_state(|state| state.capabilities()),
2134        }
2135    }
2136
2137    /// Returns the entry index backing this node when it is part of the chain.
2138    pub fn entry_index(&self) -> Option<usize> {
2139        match &self.link {
2140            NodeLink::Entry(path) => Some(path.entry()),
2141            _ => None,
2142        }
2143    }
2144
2145    /// Returns how many delegate hops separate this node from its root element.
2146    pub fn delegate_depth(&self) -> usize {
2147        match &self.link {
2148            NodeLink::Entry(path) => path.delegates().len(),
2149            _ => 0,
2150        }
2151    }
2152
2153    /// Returns the aggregated capability mask for the subtree rooted at this node.
2154    pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
2155        if self.is_tail() {
2156            NodeCapabilities::empty()
2157        } else {
2158            self.with_state(|state| state.aggregate_child_capabilities())
2159        }
2160    }
2161
2162    /// Returns true if this reference targets the sentinel head.
2163    pub fn is_head(&self) -> bool {
2164        matches!(self.link, NodeLink::Head)
2165    }
2166
2167    /// Returns true if this reference targets the sentinel tail.
2168    pub fn is_tail(&self) -> bool {
2169        matches!(self.link, NodeLink::Tail)
2170    }
2171
2172    /// Returns true if this reference targets either sentinel.
2173    pub fn is_sentinel(&self) -> bool {
2174        matches!(self.link, NodeLink::Head | NodeLink::Tail)
2175    }
2176
2177    /// Returns true if this node has any capability bits present in `mask`.
2178    pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
2179        !mask.is_empty() && self.kind_set().intersects(mask)
2180    }
2181
2182    /// Visits descendant nodes, optionally including `self`, in insertion order.
2183    pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
2184    where
2185        F: FnMut(ModifierChainNodeRef<'a>),
2186    {
2187        let mut current = if include_self {
2188            Some(self)
2189        } else {
2190            self.child()
2191        };
2192        while let Some(node) = current {
2193            if node.is_tail() {
2194                break;
2195            }
2196            if !node.is_sentinel() {
2197                f(node.clone());
2198            }
2199            current = node.child();
2200        }
2201    }
2202
2203    /// Visits descendant nodes that match `mask`, short-circuiting when possible.
2204    pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2205    where
2206        F: FnMut(ModifierChainNodeRef<'a>),
2207    {
2208        if mask.is_empty() {
2209            self.visit_descendants(include_self, f);
2210            return;
2211        }
2212
2213        if !self.aggregate_child_capabilities().intersects(mask) {
2214            return;
2215        }
2216
2217        self.visit_descendants(include_self, |node| {
2218            if node.kind_set().intersects(mask) {
2219                f(node);
2220            }
2221        });
2222    }
2223
2224    /// Visits ancestor nodes up to (but excluding) the sentinel head.
2225    pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
2226    where
2227        F: FnMut(ModifierChainNodeRef<'a>),
2228    {
2229        let mut current = if include_self {
2230            Some(self)
2231        } else {
2232            self.parent()
2233        };
2234        while let Some(node) = current {
2235            if node.is_head() {
2236                break;
2237            }
2238            f(node.clone());
2239            current = node.parent();
2240        }
2241    }
2242
2243    /// Visits ancestor nodes that match `mask`.
2244    pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2245    where
2246        F: FnMut(ModifierChainNodeRef<'a>),
2247    {
2248        if mask.is_empty() {
2249            self.visit_ancestors(include_self, f);
2250            return;
2251        }
2252
2253        self.visit_ancestors(include_self, |node| {
2254            if node.kind_set().intersects(mask) {
2255                f(node);
2256            }
2257        });
2258    }
2259
2260    /// Finds the nearest ancestor focus target node.
2261    ///
2262    /// This is useful for focus navigation to find the parent focusable
2263    /// component in the tree.
2264    pub fn find_parent_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2265        let mut result = None;
2266        self.clone()
2267            .visit_ancestors_matching(false, NodeCapabilities::FOCUS, |node| {
2268                if result.is_none() {
2269                    result = Some(node);
2270                }
2271            });
2272        result
2273    }
2274
2275    /// Finds the first descendant focus target node.
2276    ///
2277    /// This is useful for focus navigation to find the first focusable
2278    /// child component in the tree.
2279    pub fn find_first_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2280        let mut result = None;
2281        self.clone()
2282            .visit_descendants_matching(false, NodeCapabilities::FOCUS, |node| {
2283                if result.is_none() {
2284                    result = Some(node);
2285                }
2286            });
2287        result
2288    }
2289
2290    /// Returns true if this node or any ancestor has focus capability.
2291    pub fn has_focus_capability_in_ancestors(&self) -> bool {
2292        let mut found = false;
2293        self.clone()
2294            .visit_ancestors_matching(true, NodeCapabilities::FOCUS, |_| {
2295                found = true;
2296            });
2297        found
2298    }
2299}
2300
2301#[cfg(test)]
2302#[path = "tests/modifier_tests.rs"]
2303mod tests;