Skip to main content

cranpose_foundation/
modifier.rs

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