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(&self) -> Option<NodeDrawClosure> {
538        None
539    }
540
541    /// Like [`create_draw_closure`](Self::create_draw_closure), but the
542    /// primitives render BEHIND the node's content — e.g. a text field's
543    /// selection highlight, which must sit under the glyphs (a highlight
544    /// drawn over them tints the text with its translucent fill).
545    fn create_behind_draw_closure(&self) -> Option<NodeDrawClosure> {
546        None
547    }
548}
549
550/// A deferred draw closure returned by
551/// [`DrawModifierNode::create_draw_closure`]: it records into a scope the
552/// renderer provides at render time, so the recording's identity stays with
553/// the consumer rather than with a closure-owned vector.
554pub type NodeDrawClosure = Rc<dyn Fn(&mut cranpose_ui_graphics::DrawScopeDefault)>;
555
556/// Marker trait for pointer input modifier nodes.
557///
558/// Pointer input nodes participate in hit-testing and pointer event
559/// dispatch. They can intercept pointer events and handle them before
560/// they reach the wrapped content.
561pub trait PointerInputNode: ModifierNode {
562    /// Called when a pointer event occurs within the bounds of this node.
563    /// Returns true if the event was consumed and should not propagate further.
564    fn on_pointer_event(
565        &mut self,
566        _context: &mut dyn ModifierNodeContext,
567        _event: &PointerEvent,
568    ) -> bool {
569        false
570    }
571
572    /// Returns true if this node should participate in hit-testing for the
573    /// given pointer position.
574    fn hit_test(&self, _x: f32, _y: f32) -> bool {
575        true
576    }
577
578    /// Returns an event handler closure if the node wants to participate in pointer dispatch.
579    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
580        None
581    }
582
583    /// Returns the cell this node reads its owning layout node's resolved size
584    /// from, if it exposes a size to its handler (Compose's
585    /// `PointerInputScope.size`).
586    ///
587    /// The cell is shared with the node, so the layout pass publishes the size
588    /// into it once per pass and every read — including reads that happen
589    /// before any pointer event arrives — observes the current size. The size
590    /// is the node's layout box in the same local coordinate space the
591    /// dispatched [`PointerEvent`] positions use, so
592    /// `event.local_position / size` is a well-defined fraction of the node.
593    ///
594    /// Returns `None` for pointer nodes with no size-bearing scope.
595    fn layout_size_sink(&self) -> Option<Rc<Cell<Size>>> {
596        None
597    }
598}
599
600/// Marker trait for semantics modifier nodes.
601///
602/// Semantics nodes participate in the semantics tree construction. They can
603/// add or modify semantic properties of their wrapped content for
604/// accessibility and testing purposes.
605pub trait SemanticsNode: ModifierNode {
606    /// Merges semantic properties into the provided configuration.
607    fn merge_semantics(&self, _config: &mut SemanticsConfiguration) {
608        // Default: no semantics added
609    }
610}
611
612/// Focus state of a focus target node.
613///
614/// This mirrors Jetpack Compose's FocusState enum which tracks whether
615/// a node is focused, has a focused child, or is inactive.
616#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
617pub enum FocusState {
618    /// The focusable component is currently active (i.e. it receives key events).
619    Active,
620    /// One of the descendants of the focusable component is Active.
621    ActiveParent,
622    /// The focusable component is currently active (has focus), and is in a state
623    /// where it does not want to give up focus. (Eg. a text field with an invalid
624    /// phone number).
625    Captured,
626    /// The focusable component does not receive any key events. (ie it is not active,
627    /// nor are any of its descendants active).
628    #[default]
629    Inactive,
630}
631
632impl FocusState {
633    /// Returns whether the component is focused (Active or Captured).
634    pub fn is_focused(self) -> bool {
635        matches!(self, FocusState::Active | FocusState::Captured)
636    }
637
638    /// Returns whether this node or any descendant has focus.
639    pub fn has_focus(self) -> bool {
640        matches!(
641            self,
642            FocusState::Active | FocusState::ActiveParent | FocusState::Captured
643        )
644    }
645
646    /// Returns whether focus is captured.
647    pub fn is_captured(self) -> bool {
648        matches!(self, FocusState::Captured)
649    }
650}
651
652/// Marker trait for focus modifier nodes.
653///
654/// Focus nodes participate in focus management. They can request focus,
655/// track focus state, and participate in focus traversal.
656pub trait FocusNode: ModifierNode {
657    /// Returns the current focus state of this node.
658    fn focus_state(&self) -> FocusState;
659
660    /// Called when focus state changes for this node.
661    fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, _state: FocusState) {
662        // Default: no action on focus change
663    }
664}
665
666/// What kind of control a node is, as screen readers announce it.
667///
668/// This is Compose's `SemanticsProperties.Role` (`Modifier.semantics { role =
669/// Role.RadioButton }`), not a description of where the node sits in the tree.
670/// A screen reader turns it into the trailing noun it speaks after the label —
671/// TalkBack says "CAMPAIGN, radio button, selected" — and into the on/off
672/// wording it uses for a switch. Compose keeps this separate from the node's
673/// structural kind for the same reason Cranpose does: a `Row` that happens to
674/// be clickable is still a row, and a drawn ring segment that is a radio button
675/// has no layout node of its own at all.
676#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
677pub enum SemanticsWidgetRole {
678    Button,
679    Checkbox,
680    Switch,
681    RadioButton,
682    Tab,
683    Image,
684    /// Compose's `heading()`, which is a property rather than a `Role`, but
685    /// reaches the platform through the same field on every backend Cranpose
686    /// targets (`AccessibilityNodeInfo.setHeading`, `Role::Heading`,
687    /// `<h*>`/`UIAccessibilityTraitHeader`).
688    Header,
689}
690
691/// A screen-reader action that is not a click, e.g. Compose's
692/// `customActions = listOf(CustomAccessibilityAction("Pause") { … })`.
693///
694/// TalkBack surfaces these through its actions menu rather than by activating
695/// the node, which is the only way to reach a command that has no on-screen
696/// control — pausing a game whose whole surface is one tap-to-launch target.
697#[derive(Clone)]
698pub struct SemanticsCustomAction {
699    /// What the screen reader reads out in its actions menu.
700    pub label: String,
701    handler: Rc<dyn Fn()>,
702}
703
704impl SemanticsCustomAction {
705    pub fn new(label: impl Into<String>, handler: impl Fn() + 'static) -> Self {
706        Self {
707            label: label.into(),
708            handler: Rc::new(handler),
709        }
710    }
711
712    pub fn invoke(&self) {
713        (self.handler)();
714    }
715}
716
717impl fmt::Debug for SemanticsCustomAction {
718    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
719        f.debug_struct("SemanticsCustomAction")
720            .field("label", &self.label)
721            .finish_non_exhaustive()
722    }
723}
724
725/// Two custom actions are the same action when they read the same.
726///
727/// The handler is deliberately excluded. A semantics recorder runs on every
728/// collection, so the closure is a fresh `Rc` each time and comparing handler
729/// identity would report "the tree changed" on every frame — which on Android
730/// means re-serialising and re-publishing the whole virtual-view tree across
731/// JNI 60 times a second. Handlers are looked up in the live semantics tree at
732/// the moment the action fires (see `perform_custom_action`), so a handler that
733/// is newer than the last published snapshot is still the one that runs.
734impl PartialEq for SemanticsCustomAction {
735    fn eq(&self, other: &Self) -> bool {
736        self.label == other.label
737    }
738}
739
740impl Eq for SemanticsCustomAction {}
741
742/// A semantics node for content that is *drawn* rather than laid out.
743///
744/// An immediate-mode surface — one `Canvas` that paints a whole screen — has
745/// exactly one layout node, so the semantics tree built from layout has exactly
746/// one node to offer a screen reader. This is the escape hatch: the drawing
747/// code already knows where it put every control, so it publishes those
748/// rectangles as semantics directly. Android's own answer for a canvas-drawn
749/// `View` is the same shape (`ExploreByTouchHelper` feeding virtual view ids
750/// into an `AccessibilityNodeProvider`), and Cranpose's Android bridge is
751/// already an `AccessibilityNodeProvider`, so these land as first-class
752/// virtual views next to the ones layout produces.
753///
754/// `bounds` is in the publishing node's own coordinates (logical px, origin at
755/// that node's top-left), because that is what a draw scope works in.
756#[derive(Clone, Debug, PartialEq)]
757pub struct CanvasSemanticsNode {
758    /// Identity that must survive a redraw.
759    ///
760    /// A screen reader parks its cursor on a virtual view id; if the id for
761    /// "the Haptics switch" changes when the list scrolls, the cursor jumps.
762    /// Derive this from what the control *is* (a row index, an enum
763    /// discriminant), never from where it currently sits.
764    pub key: u64,
765    /// Where the control was drawn, relative to the publishing node.
766    pub bounds: cranpose_ui_graphics::Rect,
767    pub label: String,
768    pub role: Option<SemanticsWidgetRole>,
769    /// Compose's `stateDescription` — what the control currently reads as
770    /// ("CAMPAIGN", "3 of 18 gold"), spoken after the label and re-spoken on
771    /// its own when only the state changed.
772    pub state_description: Option<String>,
773    /// Compose's `onClick(label = …)`. TalkBack reads it as "double tap to
774    /// <label>", so it is a verb phrase, not a repeat of the label.
775    pub on_click_label: Option<String>,
776    pub clickable: bool,
777    /// Compose's `selected`, for `Role.RadioButton`/`Role.Tab`.
778    pub selected: Option<bool>,
779    /// Compose's `toggleableState`, for `Role.Switch`/`Role.Checkbox`.
780    pub toggled: Option<bool>,
781    pub enabled: bool,
782    pub custom_actions: Vec<SemanticsCustomAction>,
783}
784
785impl Default for CanvasSemanticsNode {
786    fn default() -> Self {
787        Self {
788            key: 0,
789            bounds: cranpose_ui_graphics::Rect {
790                x: 0.0,
791                y: 0.0,
792                width: 0.0,
793                height: 0.0,
794            },
795            label: String::new(),
796            role: None,
797            state_description: None,
798            on_click_label: None,
799            clickable: false,
800            selected: None,
801            toggled: None,
802            // Matches Compose: a node is enabled unless `disabled()` says
803            // otherwise, so an app that never thinks about it gets the right
804            // answer.
805            enabled: true,
806            custom_actions: Vec::new(),
807        }
808    }
809}
810
811impl CanvasSemanticsNode {
812    /// A clickable control drawn at `bounds`.
813    pub fn control(key: u64, bounds: cranpose_ui_graphics::Rect, label: impl Into<String>) -> Self {
814        Self {
815            key,
816            bounds,
817            label: label.into(),
818            clickable: true,
819            ..Self::default()
820        }
821    }
822
823    /// A drawn label that is read but not activated.
824    pub fn text(key: u64, bounds: cranpose_ui_graphics::Rect, label: impl Into<String>) -> Self {
825        Self {
826            key,
827            bounds,
828            label: label.into(),
829            ..Self::default()
830        }
831    }
832
833    pub fn with_role(mut self, role: SemanticsWidgetRole) -> Self {
834        self.role = Some(role);
835        self
836    }
837
838    pub fn with_state_description(mut self, state: impl Into<String>) -> Self {
839        self.state_description = Some(state.into());
840        self
841    }
842
843    pub fn with_click_label(mut self, label: impl Into<String>) -> Self {
844        self.on_click_label = Some(label.into());
845        self.clickable = true;
846        self
847    }
848
849    pub fn with_selected(mut self, selected: bool) -> Self {
850        self.selected = Some(selected);
851        self
852    }
853
854    pub fn with_toggled(mut self, toggled: bool) -> Self {
855        self.toggled = Some(toggled);
856        self
857    }
858
859    pub fn with_enabled(mut self, enabled: bool) -> Self {
860        self.enabled = enabled;
861        self
862    }
863
864    pub fn with_custom_action(mut self, action: SemanticsCustomAction) -> Self {
865        self.custom_actions.push(action);
866        self
867    }
868}
869
870/// Semantics configuration for accessibility.
871#[derive(Clone, Debug, PartialEq)]
872pub struct SemanticsConfiguration {
873    pub content_description: Option<String>,
874    /// Compose's `stateDescription`.
875    pub state_description: Option<String>,
876    /// Compose's `onClick(label = …)`; implies clickable.
877    pub on_click_label: Option<String>,
878    /// Compose's `Role`.
879    pub role: Option<SemanticsWidgetRole>,
880    pub selected: Option<bool>,
881    pub toggled: Option<bool>,
882    pub enabled: bool,
883    pub is_clickable: bool,
884    pub is_editable_text: bool,
885    pub text_selection: Option<crate::text::TextRange>,
886    pub custom_actions: Vec<SemanticsCustomAction>,
887    /// Controls this node drew itself instead of laying out. See
888    /// [`CanvasSemanticsNode`].
889    pub canvas_children: Vec<CanvasSemanticsNode>,
890}
891
892impl Default for SemanticsConfiguration {
893    fn default() -> Self {
894        Self {
895            content_description: None,
896            state_description: None,
897            on_click_label: None,
898            role: None,
899            selected: None,
900            toggled: None,
901            enabled: true,
902            is_clickable: false,
903            is_editable_text: false,
904            text_selection: None,
905            custom_actions: Vec::new(),
906            canvas_children: Vec::new(),
907        }
908    }
909}
910
911impl SemanticsConfiguration {
912    pub fn merge(&mut self, other: &SemanticsConfiguration) {
913        if let Some(description) = &other.content_description {
914            self.content_description = Some(description.clone());
915        }
916        if let Some(state) = &other.state_description {
917            self.state_description = Some(state.clone());
918        }
919        if let Some(label) = &other.on_click_label {
920            self.on_click_label = Some(label.clone());
921        }
922        if let Some(role) = other.role {
923            self.role = Some(role);
924        }
925        if let Some(selected) = other.selected {
926            self.selected = Some(selected);
927        }
928        if let Some(toggled) = other.toggled {
929            self.toggled = Some(toggled);
930        }
931        // Disabling wins: a chain that disables the node anywhere disables it,
932        // matching how Compose's `disabled()` is not undone by an inner node.
933        self.enabled &= other.enabled;
934        self.is_clickable |= other.is_clickable;
935        self.is_editable_text |= other.is_editable_text;
936        if let Some(selection) = other.text_selection {
937            self.text_selection = Some(selection);
938        }
939        self.custom_actions
940            .extend(other.custom_actions.iter().cloned());
941        self.canvas_children
942            .extend(other.canvas_children.iter().cloned());
943    }
944
945    /// Whether a screen reader should offer activation. A named click label is
946    /// how Compose declares `onClick`, so it implies the action the same way.
947    pub fn is_activatable(&self) -> bool {
948        self.is_clickable || self.on_click_label.is_some()
949    }
950}
951
952impl fmt::Debug for dyn ModifierNode {
953    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954        f.debug_struct("ModifierNode").finish_non_exhaustive()
955    }
956}
957
958impl dyn ModifierNode {
959    pub fn as_any(&self) -> &dyn Any {
960        self
961    }
962
963    pub fn as_any_mut(&mut self) -> &mut dyn Any {
964        self
965    }
966}
967
968/// Strongly typed modifier elements that can create and update nodes while
969/// exposing equality/hash/inspector contracts that mirror Jetpack Compose.
970pub trait ModifierNodeElement: fmt::Debug + Hash + PartialEq + 'static {
971    type Node: ModifierNode;
972
973    /// Creates a new modifier node instance for this element.
974    fn create(&self) -> Self::Node;
975
976    /// Brings an existing modifier node up to date with the element's data.
977    fn update(&self, node: &mut Self::Node);
978
979    /// Optional key used to disambiguate multiple instances of the same element type.
980    fn key(&self) -> Option<u64> {
981        None
982    }
983
984    /// Human readable name surfaced to inspector tooling.
985    fn inspector_name(&self) -> &'static str {
986        type_name::<Self>()
987    }
988
989    /// Records inspector properties for tooling.
990    fn inspector_properties(&self, _inspector: &mut dyn FnMut(&'static str, String)) {}
991
992    /// Returns the capabilities of nodes created by this element.
993    /// Override this to indicate which specialized traits the node implements.
994    fn capabilities(&self) -> NodeCapabilities {
995        NodeCapabilities::default()
996    }
997
998    /// Whether this element requires `update` to be called even if `eq` returns true.
999    ///
1000    /// This is useful for elements that ignore certain fields in `eq` (e.g. closures)
1001    /// to allow node reuse, but still need those fields updated in the existing node.
1002    /// Defaults to `false`.
1003    fn always_update(&self) -> bool {
1004        false
1005    }
1006
1007    /// Whether modifier reconciliation should request capability-wide invalidations
1008    /// after updating an existing node.
1009    fn auto_invalidate_on_update(&self) -> bool {
1010        true
1011    }
1012
1013    /// Optional targeted invalidation requested after updating an existing node.
1014    ///
1015    /// This is for nodes whose attach/remove capability is broader than the
1016    /// work needed for a value-only update. For example, an offset node
1017    /// participates in layout on attach but an x/y change only needs placement
1018    /// data and draw output refreshed.
1019    fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1020        None
1021    }
1022}
1023
1024/// Capability flags indicating which specialized traits a modifier node implements.
1025#[derive(Clone, Copy, PartialEq, Eq, Hash)]
1026pub struct NodeCapabilities(u32);
1027
1028impl NodeCapabilities {
1029    /// No capabilities.
1030    pub const NONE: Self = Self(0);
1031    /// Modifier participates in measure/layout.
1032    pub const LAYOUT: Self = Self(1 << 0);
1033    /// Modifier participates in draw.
1034    pub const DRAW: Self = Self(1 << 1);
1035    /// Modifier participates in pointer input.
1036    pub const POINTER_INPUT: Self = Self(1 << 2);
1037    /// Modifier participates in semantics tree construction.
1038    pub const SEMANTICS: Self = Self(1 << 3);
1039    /// Modifier participates in modifier locals.
1040    pub const MODIFIER_LOCALS: Self = Self(1 << 4);
1041    /// Modifier participates in focus management.
1042    pub const FOCUS: Self = Self(1 << 5);
1043
1044    /// Returns an empty capability set.
1045    pub const fn empty() -> Self {
1046        Self::NONE
1047    }
1048
1049    /// Returns whether all bits in `other` are present in `self`.
1050    pub const fn contains(self, other: Self) -> bool {
1051        (self.0 & other.0) == other.0
1052    }
1053
1054    /// Returns whether any bit in `other` is present in `self`.
1055    pub const fn intersects(self, other: Self) -> bool {
1056        (self.0 & other.0) != 0
1057    }
1058
1059    /// Inserts the requested capability bits.
1060    pub fn insert(&mut self, other: Self) {
1061        self.0 |= other.0;
1062    }
1063
1064    /// Returns the raw bit representation.
1065    pub const fn bits(self) -> u32 {
1066        self.0
1067    }
1068
1069    /// Returns true when no capabilities are set.
1070    pub const fn is_empty(self) -> bool {
1071        self.0 == 0
1072    }
1073
1074    /// Returns the capability bit mask required for the given invalidation.
1075    pub const fn for_invalidation(kind: InvalidationKind) -> Self {
1076        match kind {
1077            InvalidationKind::Layout => Self::LAYOUT,
1078            InvalidationKind::Draw => Self::DRAW,
1079            InvalidationKind::PointerInput => Self::POINTER_INPUT,
1080            InvalidationKind::Semantics => Self::SEMANTICS,
1081            InvalidationKind::Focus => Self::FOCUS,
1082        }
1083    }
1084}
1085
1086impl Default for NodeCapabilities {
1087    fn default() -> Self {
1088        Self::NONE
1089    }
1090}
1091
1092impl fmt::Debug for NodeCapabilities {
1093    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1094        f.debug_struct("NodeCapabilities")
1095            .field("layout", &self.contains(Self::LAYOUT))
1096            .field("draw", &self.contains(Self::DRAW))
1097            .field("pointer_input", &self.contains(Self::POINTER_INPUT))
1098            .field("semantics", &self.contains(Self::SEMANTICS))
1099            .field("modifier_locals", &self.contains(Self::MODIFIER_LOCALS))
1100            .field("focus", &self.contains(Self::FOCUS))
1101            .finish()
1102    }
1103}
1104
1105impl BitOr for NodeCapabilities {
1106    type Output = Self;
1107
1108    fn bitor(self, rhs: Self) -> Self::Output {
1109        Self(self.0 | rhs.0)
1110    }
1111}
1112
1113impl BitOrAssign for NodeCapabilities {
1114    fn bitor_assign(&mut self, rhs: Self) {
1115        self.0 |= rhs.0;
1116    }
1117}
1118
1119/// Records an invalidation request together with the capability mask that triggered it.
1120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1121pub struct ModifierInvalidation {
1122    kind: InvalidationKind,
1123    capabilities: NodeCapabilities,
1124}
1125
1126impl ModifierInvalidation {
1127    /// Creates a new modifier invalidation entry.
1128    pub const fn new(kind: InvalidationKind, capabilities: NodeCapabilities) -> Self {
1129        Self { kind, capabilities }
1130    }
1131
1132    /// Returns the invalidated pipeline kind.
1133    pub const fn kind(self) -> InvalidationKind {
1134        self.kind
1135    }
1136
1137    /// Returns the capability mask associated with the invalidation.
1138    pub const fn capabilities(self) -> NodeCapabilities {
1139        self.capabilities
1140    }
1141}
1142
1143/// Type-erased modifier element used by the runtime to reconcile chains.
1144pub trait AnyModifierElement: fmt::Debug {
1145    fn node_type(&self) -> TypeId;
1146
1147    fn element_type(&self) -> TypeId;
1148
1149    fn create_node(&self) -> Box<dyn ModifierNode>;
1150
1151    fn can_update_node(&self, node: &dyn ModifierNode) -> bool;
1152
1153    fn update_node(&self, node: &mut dyn ModifierNode);
1154
1155    fn key(&self) -> Option<u64>;
1156
1157    fn capabilities(&self) -> NodeCapabilities {
1158        NodeCapabilities::default()
1159    }
1160
1161    fn hash_code(&self) -> u64;
1162
1163    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool;
1164
1165    fn inspector_name(&self) -> &'static str;
1166
1167    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String));
1168
1169    fn requires_update(&self) -> bool;
1170
1171    fn auto_invalidates_on_update(&self) -> bool;
1172
1173    fn update_invalidation_kind(&self) -> Option<InvalidationKind>;
1174
1175    fn as_any(&self) -> &dyn Any;
1176}
1177
1178struct TypedModifierElement<E: ModifierNodeElement> {
1179    element: E,
1180    cached_hash: u64,
1181}
1182
1183impl<E: ModifierNodeElement> TypedModifierElement<E> {
1184    fn new(element: E) -> Self {
1185        let mut hasher = default::new();
1186        element.hash(&mut hasher);
1187        Self {
1188            element,
1189            cached_hash: hasher.finish(),
1190        }
1191    }
1192}
1193
1194impl<E> fmt::Debug for TypedModifierElement<E>
1195where
1196    E: ModifierNodeElement,
1197{
1198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1199        f.debug_struct("TypedModifierElement")
1200            .field("type", &type_name::<E>())
1201            .finish()
1202    }
1203}
1204
1205impl<E> AnyModifierElement for TypedModifierElement<E>
1206where
1207    E: ModifierNodeElement,
1208{
1209    fn node_type(&self) -> TypeId {
1210        TypeId::of::<E::Node>()
1211    }
1212
1213    fn element_type(&self) -> TypeId {
1214        TypeId::of::<E>()
1215    }
1216
1217    fn create_node(&self) -> Box<dyn ModifierNode> {
1218        Box::new(self.element.create())
1219    }
1220
1221    fn can_update_node(&self, node: &dyn ModifierNode) -> bool {
1222        node.as_any().is::<E::Node>()
1223    }
1224
1225    fn update_node(&self, node: &mut dyn ModifierNode) {
1226        if let Some(typed) = node.as_any_mut().downcast_mut::<E::Node>() {
1227            self.element.update(typed);
1228        }
1229    }
1230
1231    fn key(&self) -> Option<u64> {
1232        self.element.key()
1233    }
1234
1235    fn capabilities(&self) -> NodeCapabilities {
1236        self.element.capabilities()
1237    }
1238
1239    fn hash_code(&self) -> u64 {
1240        self.cached_hash
1241    }
1242
1243    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool {
1244        other
1245            .as_any()
1246            .downcast_ref::<Self>()
1247            .map(|typed| typed.element == self.element)
1248            .unwrap_or(false)
1249    }
1250
1251    fn inspector_name(&self) -> &'static str {
1252        self.element.inspector_name()
1253    }
1254
1255    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String)) {
1256        self.element.inspector_properties(visitor);
1257    }
1258
1259    fn requires_update(&self) -> bool {
1260        self.element.always_update()
1261    }
1262
1263    fn auto_invalidates_on_update(&self) -> bool {
1264        self.element.auto_invalidate_on_update()
1265    }
1266
1267    fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1268        self.element.update_invalidation_kind()
1269    }
1270
1271    fn as_any(&self) -> &dyn Any {
1272        self
1273    }
1274}
1275
1276fn request_update_auto_invalidations(
1277    element: &dyn AnyModifierElement,
1278    context: &mut dyn ModifierNodeContext,
1279    capabilities: NodeCapabilities,
1280) {
1281    if let Some(kind) = element.update_invalidation_kind() {
1282        let capabilities = NodeCapabilities::for_invalidation(kind);
1283        context.push_active_capabilities(capabilities);
1284        context.invalidate(kind);
1285        context.pop_active_capabilities();
1286    } else if element.auto_invalidates_on_update() {
1287        request_auto_invalidations(context, capabilities);
1288    }
1289}
1290
1291/// Convenience helper for callers to construct a type-erased modifier
1292/// element without having to mention the internal wrapper type.
1293pub fn modifier_element<E: ModifierNodeElement>(element: E) -> DynModifierElement {
1294    Rc::new(TypedModifierElement::new(element))
1295}
1296
1297/// Boxed type-erased modifier element.
1298pub type DynModifierElement = Rc<dyn AnyModifierElement>;
1299
1300#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1301enum TraversalDirection {
1302    Forward,
1303    Backward,
1304}
1305
1306/// Iterator walking a modifier chain by indexing into `ordered_nodes`.
1307///
1308/// This avoids the per-step `RefCell::borrow()` + `NodeLink::clone()` cost
1309/// of following the linked-list through `NodeState::child`/`parent`.
1310pub struct ModifierChainIter<'a> {
1311    chain: &'a ModifierNodeChain,
1312    /// Current position in `ordered_nodes`. For forward iteration, starts at 0
1313    /// and increments; for backward, starts at len-1 and decrements.
1314    cursor: usize,
1315    /// Number of elements remaining (avoids underflow on backward iteration).
1316    remaining: usize,
1317    direction: TraversalDirection,
1318}
1319
1320impl<'a> ModifierChainIter<'a> {
1321    fn forward(chain: &'a ModifierNodeChain) -> Self {
1322        Self {
1323            chain,
1324            cursor: 0,
1325            remaining: chain.ordered_nodes.len(),
1326            direction: TraversalDirection::Forward,
1327        }
1328    }
1329
1330    fn backward(chain: &'a ModifierNodeChain) -> Self {
1331        let len = chain.ordered_nodes.len();
1332        Self {
1333            chain,
1334            cursor: len.wrapping_sub(1),
1335            remaining: len,
1336            direction: TraversalDirection::Backward,
1337        }
1338    }
1339}
1340
1341impl<'a> Iterator for ModifierChainIter<'a> {
1342    type Item = ModifierChainNodeRef<'a>;
1343
1344    #[inline]
1345    fn next(&mut self) -> Option<Self::Item> {
1346        if self.remaining == 0 {
1347            return None;
1348        }
1349        let (link, caps, agg) = self.chain.ordered_nodes[self.cursor];
1350        let node_ref = self.chain.make_node_ref_with_caps(link, caps, agg);
1351        self.remaining -= 1;
1352        match self.direction {
1353            TraversalDirection::Forward => self.cursor += 1,
1354            TraversalDirection::Backward => self.cursor = self.cursor.wrapping_sub(1),
1355        }
1356        Some(node_ref)
1357    }
1358
1359    #[inline]
1360    fn size_hint(&self) -> (usize, Option<usize>) {
1361        (self.remaining, Some(self.remaining))
1362    }
1363}
1364
1365impl<'a> ExactSizeIterator for ModifierChainIter<'a> {}
1366impl<'a> std::iter::FusedIterator for ModifierChainIter<'a> {}
1367
1368#[derive(Debug)]
1369struct ModifierNodeEntry {
1370    element_type: TypeId,
1371    node_type: TypeId,
1372    key: Option<u64>,
1373    hash_code: u64,
1374    element: DynModifierElement,
1375    node: Rc<RefCell<Box<dyn ModifierNode>>>,
1376    capabilities: NodeCapabilities,
1377}
1378
1379impl ModifierNodeEntry {
1380    fn new(
1381        element_type: TypeId,
1382        node_type: TypeId,
1383        key: Option<u64>,
1384        element: DynModifierElement,
1385        node: Box<dyn ModifierNode>,
1386        hash_code: u64,
1387        capabilities: NodeCapabilities,
1388    ) -> Self {
1389        // Wrap the boxed node in Rc<RefCell<>> for shared ownership
1390        let node_rc = Rc::new(RefCell::new(node));
1391        let entry = Self {
1392            element_type,
1393            node_type,
1394            key,
1395            hash_code,
1396            element,
1397            node: Rc::clone(&node_rc),
1398            capabilities,
1399        };
1400        entry
1401            .node
1402            .borrow()
1403            .node_state()
1404            .set_capabilities(entry.capabilities);
1405        entry
1406    }
1407}
1408
1409fn visit_node_tree_mut(
1410    node: &mut dyn ModifierNode,
1411    visitor: &mut dyn FnMut(&mut dyn ModifierNode),
1412) {
1413    visitor(node);
1414    node.for_each_delegate_mut(&mut |child| visit_node_tree_mut(child, visitor));
1415}
1416
1417fn nth_delegate(node: &dyn ModifierNode, target: usize) -> Option<&dyn ModifierNode> {
1418    let mut current = 0usize;
1419    let mut result: Option<&dyn ModifierNode> = None;
1420    node.for_each_delegate(&mut |child| {
1421        if result.is_none() && current == target {
1422            result = Some(child);
1423        }
1424        current += 1;
1425    });
1426    result
1427}
1428
1429fn nth_delegate_mut(node: &mut dyn ModifierNode, target: usize) -> Option<&mut dyn ModifierNode> {
1430    let mut current = 0usize;
1431    let mut result: Option<&mut dyn ModifierNode> = None;
1432    node.for_each_delegate_mut(&mut |child| {
1433        if result.is_none() && current == target {
1434            result = Some(child);
1435        }
1436        current += 1;
1437    });
1438    result
1439}
1440
1441fn with_node_context<F, R>(
1442    node: &mut dyn ModifierNode,
1443    context: &mut dyn ModifierNodeContext,
1444    f: F,
1445) -> R
1446where
1447    F: FnOnce(&mut dyn ModifierNode, &mut dyn ModifierNodeContext) -> R,
1448{
1449    context.push_active_capabilities(node.node_state().capabilities());
1450    let result = f(node, context);
1451    context.pop_active_capabilities();
1452    result
1453}
1454
1455fn request_auto_invalidations(
1456    context: &mut dyn ModifierNodeContext,
1457    capabilities: NodeCapabilities,
1458) {
1459    if capabilities.is_empty() {
1460        return;
1461    }
1462
1463    context.push_active_capabilities(capabilities);
1464
1465    if capabilities.contains(NodeCapabilities::LAYOUT) {
1466        context.invalidate(InvalidationKind::Layout);
1467    }
1468    if capabilities.contains(NodeCapabilities::DRAW) {
1469        context.invalidate(InvalidationKind::Draw);
1470    }
1471    if capabilities.contains(NodeCapabilities::POINTER_INPUT) {
1472        context.invalidate(InvalidationKind::PointerInput);
1473    }
1474    if capabilities.contains(NodeCapabilities::SEMANTICS) {
1475        context.invalidate(InvalidationKind::Semantics);
1476    }
1477    if capabilities.contains(NodeCapabilities::FOCUS) {
1478        context.invalidate(InvalidationKind::Focus);
1479    }
1480
1481    context.pop_active_capabilities();
1482}
1483
1484/// Attaches a node tree by calling on_attach for all unattached nodes.
1485///
1486/// # Safety
1487/// Callers must ensure no immutable RefCell borrows are held on the node
1488/// when calling this function. The on_attach callback may trigger mutations
1489/// (invalidations, state updates, etc.) that require mutable access, which
1490/// would panic if an immutable borrow is held across the call.
1491fn attach_node_tree(node: &mut dyn ModifierNode, context: &mut dyn ModifierNodeContext) {
1492    visit_node_tree_mut(node, &mut |n| {
1493        if !n.node_state().is_attached() {
1494            n.node_state().set_attached(true);
1495            with_node_context(n, context, |node, ctx| node.on_attach(ctx));
1496        }
1497    });
1498}
1499
1500fn reset_node_tree(node: &mut dyn ModifierNode) {
1501    visit_node_tree_mut(node, &mut |n| n.on_reset());
1502}
1503
1504fn detach_node_tree(node: &mut dyn ModifierNode) {
1505    visit_node_tree_mut(node, &mut |n| {
1506        if n.node_state().is_attached() {
1507            n.on_detach();
1508            n.node_state().set_attached(false);
1509        }
1510        n.node_state().set_parent_link(None);
1511        n.node_state().set_child_link(None);
1512        n.node_state()
1513            .set_aggregate_child_capabilities(NodeCapabilities::empty());
1514    });
1515}
1516
1517/// Chain of modifier nodes attached to a layout node.
1518///
1519/// The chain tracks ownership of modifier nodes and reuses them across
1520/// updates when the incoming element list still contains a node of the
1521/// same type. Removed nodes detach automatically so callers do not need
1522/// to manually manage their lifetimes.
1523pub struct ModifierNodeChain {
1524    entries: Vec<ModifierNodeEntry>,
1525    aggregated_capabilities: NodeCapabilities,
1526    head_aggregate_child_capabilities: NodeCapabilities,
1527    head_sentinel: Box<SentinelNode>,
1528    tail_sentinel: Box<SentinelNode>,
1529    /// (link, own_capabilities, aggregate_child_capabilities)
1530    ordered_nodes: Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
1531    // Scratch buffers reused during update to avoid repeated allocations
1532    scratch_old_used: Vec<bool>,
1533    scratch_match_order: Vec<Option<usize>>,
1534    scratch_final_slots: Vec<Option<ModifierNodeEntry>>,
1535    scratch_elements: Vec<DynModifierElement>,
1536}
1537
1538struct SentinelNode {
1539    state: NodeState,
1540}
1541
1542impl SentinelNode {
1543    fn new() -> Self {
1544        Self {
1545            state: NodeState::sentinel(),
1546        }
1547    }
1548}
1549
1550impl DelegatableNode for SentinelNode {
1551    fn node_state(&self) -> &NodeState {
1552        &self.state
1553    }
1554}
1555
1556impl ModifierNode for SentinelNode {}
1557
1558#[derive(Clone)]
1559pub struct ModifierChainNodeRef<'a> {
1560    chain: &'a ModifierNodeChain,
1561    link: NodeLink,
1562    /// Capabilities cached from `ordered_nodes` build time — avoids RefCell borrow in kind_set().
1563    cached_capabilities: Option<NodeCapabilities>,
1564    /// Aggregate child capabilities cached from `ordered_nodes` — avoids RefCell borrow.
1565    cached_aggregate_child: Option<NodeCapabilities>,
1566}
1567
1568impl Default for ModifierNodeChain {
1569    fn default() -> Self {
1570        Self::new()
1571    }
1572}
1573
1574/// Index structure for O(1) modifier entry lookups during update.
1575///
1576/// This avoids O(n²) complexity by pre-building hash maps that allow constant-time
1577/// lookups for matching entries by key, hash, or type.
1578struct EntryIndex {
1579    /// Map (element type, node type, key) to keyed entries.
1580    keyed: HashMap<(TypeId, TypeId, u64), Vec<usize>>,
1581    /// Map (element type, node type, hash) to unkeyed entries with specific hash.
1582    hashed: HashMap<(TypeId, TypeId, u64), Vec<usize>>,
1583    /// Map (element type, node type) to unkeyed entries.
1584    typed: HashMap<(TypeId, TypeId), Vec<usize>>,
1585}
1586
1587struct EntryMatchQuery<'a> {
1588    element_type: TypeId,
1589    node_type: TypeId,
1590    key: Option<u64>,
1591    hash_code: u64,
1592    element: &'a DynModifierElement,
1593}
1594
1595impl EntryIndex {
1596    fn build(entries: &[ModifierNodeEntry]) -> Self {
1597        let mut keyed = HashMap::default();
1598        let mut hashed = HashMap::default();
1599        let mut typed = HashMap::default();
1600
1601        for (i, entry) in entries.iter().enumerate() {
1602            if let Some(key_value) = entry.key {
1603                // Keyed entry
1604                keyed
1605                    .entry((entry.element_type, entry.node_type, key_value))
1606                    .or_insert_with(Vec::new)
1607                    .push(i);
1608            } else {
1609                // Unkeyed entry - add to both hash and type indices
1610                hashed
1611                    .entry((entry.element_type, entry.node_type, entry.hash_code))
1612                    .or_insert_with(Vec::new)
1613                    .push(i);
1614                typed
1615                    .entry((entry.element_type, entry.node_type))
1616                    .or_insert_with(Vec::new)
1617                    .push(i);
1618            }
1619        }
1620
1621        Self {
1622            keyed,
1623            hashed,
1624            typed,
1625        }
1626    }
1627
1628    /// Find the best matching entry for reuse.
1629    ///
1630    /// Matching priority (from highest to lowest):
1631    /// 1. Keyed match: same element type, node type, and key.
1632    /// 2. Exact match: same retained identity, no key, same hash, and equal element.
1633    /// 3. Retained identity match without equality, which requires update.
1634    fn find_match(
1635        &self,
1636        entries: &[ModifierNodeEntry],
1637        used: &[bool],
1638        query: EntryMatchQuery<'_>,
1639    ) -> Option<usize> {
1640        if let Some(key_value) = query.key {
1641            // Priority 1: Keyed lookup - O(1)
1642            if let Some(candidates) =
1643                self.keyed
1644                    .get(&(query.element_type, query.node_type, key_value))
1645            {
1646                for &i in candidates {
1647                    if !used[i] {
1648                        return Some(i);
1649                    }
1650                }
1651            }
1652        } else {
1653            // Priority 2: Exact match (hash + equality) - O(1) lookup + O(k) equality checks
1654            if let Some(candidates) =
1655                self.hashed
1656                    .get(&(query.element_type, query.node_type, query.hash_code))
1657            {
1658                for &i in candidates {
1659                    if !used[i]
1660                        && entries[i]
1661                            .element
1662                            .as_ref()
1663                            .equals_element(query.element.as_ref())
1664                    {
1665                        return Some(i);
1666                    }
1667                }
1668            }
1669
1670            // Priority 3: Type match only - O(1) lookup + O(k) scan
1671            if let Some(candidates) = self.typed.get(&(query.element_type, query.node_type)) {
1672                for &i in candidates {
1673                    if !used[i] {
1674                        return Some(i);
1675                    }
1676                }
1677            }
1678        }
1679
1680        None
1681    }
1682}
1683
1684impl ModifierNodeChain {
1685    pub fn new() -> Self {
1686        let mut chain = Self {
1687            entries: Vec::new(),
1688            aggregated_capabilities: NodeCapabilities::empty(),
1689            head_aggregate_child_capabilities: NodeCapabilities::empty(),
1690            head_sentinel: Box::new(SentinelNode::new()),
1691            tail_sentinel: Box::new(SentinelNode::new()),
1692            ordered_nodes: Vec::new(),
1693            scratch_old_used: Vec::new(),
1694            scratch_match_order: Vec::new(),
1695            scratch_final_slots: Vec::new(),
1696            scratch_elements: Vec::new(),
1697        };
1698        chain.sync_chain_links();
1699        chain
1700    }
1701
1702    /// Detaches all nodes in the chain.
1703    pub fn detach_nodes(&mut self) {
1704        for entry in &self.entries {
1705            detach_node_tree(&mut **entry.node.borrow_mut());
1706        }
1707    }
1708
1709    /// Attaches all nodes in the chain.
1710    pub fn attach_nodes(&mut self, context: &mut dyn ModifierNodeContext) {
1711        for entry in &self.entries {
1712            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1713        }
1714    }
1715
1716    /// Rebuilds the internal chain links (parent/child relationships).
1717    /// This should be called if nodes have been detached but are intended to be reused.
1718    pub fn repair_chain(&mut self) {
1719        self.sync_chain_links();
1720    }
1721
1722    /// Reconcile the chain against the provided elements, attaching newly
1723    /// created nodes and detaching nodes that are no longer required.
1724    ///
1725    /// This method delegates to `update_from_ref_iter` which handles the
1726    /// actual reconciliation logic.
1727    pub fn update_from_slice(
1728        &mut self,
1729        elements: &[DynModifierElement],
1730        context: &mut dyn ModifierNodeContext,
1731    ) {
1732        self.update_from_ref_iter(elements.iter(), context);
1733    }
1734
1735    /// Reconcile the chain against the provided iterator of element references.
1736    ///
1737    /// This is the preferred method as it avoids requiring a collected slice,
1738    /// enabling zero-allocation traversal of modifier trees.
1739    pub fn update_from_ref_iter<'a, I>(
1740        &mut self,
1741        elements: I,
1742        context: &mut dyn ModifierNodeContext,
1743    ) where
1744        I: Iterator<Item = &'a DynModifierElement>,
1745    {
1746        // Fast path: try to match elements sequentially without building index.
1747        // If all elements match in order (same type and key at same position),
1748        // we skip the expensive EntryIndex building. This is O(n) instead of O(n + m).
1749        let old_len = self.entries.len();
1750        let mut fast_path_failed_at: Option<usize> = None;
1751        let mut elements_count = 0;
1752
1753        // Collect elements we need to process in slow path
1754        self.scratch_elements.clear();
1755
1756        for (idx, element) in elements.enumerate() {
1757            elements_count = idx + 1;
1758
1759            if fast_path_failed_at.is_none() && idx < old_len {
1760                let entry = &mut self.entries[idx];
1761                let same_type = entry.element_type == element.element_type();
1762                let same_node_type = entry.node_type == element.node_type();
1763                let same_key = entry.key == element.key();
1764                let same_hash = entry.hash_code == element.hash_code();
1765
1766                // Fast path requires same type, key, AND hash to ensure we're not
1767                // breaking reordering semantics (where elements can move positions)
1768                let positional_update = element.requires_update();
1769                if same_type && same_node_type && same_key && (same_hash || positional_update) {
1770                    let can_update_node = {
1771                        let node_borrow = entry.node.borrow();
1772                        element.can_update_node(&**node_borrow)
1773                    };
1774                    if !can_update_node {
1775                        fast_path_failed_at = Some(idx);
1776                        self.scratch_elements.push(element.clone());
1777                        continue;
1778                    }
1779
1780                    // Fast path: element matches at same position
1781                    let same_element = entry.element.as_ref().equals_element(element.as_ref());
1782                    let capabilities = element.capabilities();
1783
1784                    // Re-attach node if it was detached during a previous update
1785                    {
1786                        let node_borrow = entry.node.borrow();
1787                        if !node_borrow.node_state().is_attached() {
1788                            drop(node_borrow);
1789                            attach_node_tree(&mut **entry.node.borrow_mut(), context);
1790                        }
1791                    }
1792
1793                    // Optimize updates: only call update_node if element changed
1794                    let needs_update = !same_element || element.requires_update();
1795                    if needs_update {
1796                        element.update_node(&mut **entry.node.borrow_mut());
1797                        entry.element = element.clone();
1798                        entry.hash_code = element.hash_code();
1799                        request_update_auto_invalidations(element.as_ref(), context, capabilities);
1800                    }
1801
1802                    // Always update metadata
1803                    entry.capabilities = capabilities;
1804                    entry
1805                        .node
1806                        .borrow()
1807                        .node_state()
1808                        .set_capabilities(capabilities);
1809                    continue;
1810                }
1811                // Fast path failed - mark position and fall through to collect
1812                fast_path_failed_at = Some(idx);
1813            }
1814
1815            // Collect element for slow path processing
1816            self.scratch_elements.push(element.clone());
1817        }
1818
1819        // Fast path succeeded if:
1820        // 1. No mismatch was found (fast_path_failed_at is None)
1821        // 2. All elements were processed via fast path (scratch_elements is empty)
1822        // Note: If old_len=0 and we have new elements, scratch_elements won't be empty
1823        if fast_path_failed_at.is_none() && self.scratch_elements.is_empty() {
1824            // Detach any removed entries (elements_count <= old_len guaranteed here)
1825            if elements_count < self.entries.len() {
1826                for entry in self.entries.drain(elements_count..) {
1827                    request_auto_invalidations(context, entry.capabilities);
1828                    detach_node_tree(&mut **entry.node.borrow_mut());
1829                }
1830            }
1831            self.sync_chain_links();
1832            return;
1833        }
1834
1835        // Slow path: need full reconciliation starting from failure point
1836        // If no mismatch but we have extra elements, fail_idx is the old length
1837        let fail_idx = fast_path_failed_at.unwrap_or(old_len);
1838
1839        // Move entries that were already processed to a safe place
1840        let mut old_entries: Vec<ModifierNodeEntry> = self.entries.drain(fail_idx..).collect();
1841        let processed_entries_len = self.entries.len();
1842        let old_len = old_entries.len();
1843
1844        // Reuse scratch buffers for the remaining entries only
1845        self.scratch_old_used.clear();
1846        self.scratch_old_used.resize(old_len, false);
1847
1848        self.scratch_match_order.clear();
1849        self.scratch_match_order.resize(old_len, None);
1850
1851        // Build index only for unprocessed entries
1852        let index = EntryIndex::build(&old_entries);
1853
1854        let new_elements_count = self.scratch_elements.len();
1855        self.scratch_final_slots.clear();
1856        self.scratch_final_slots.reserve(new_elements_count);
1857
1858        // Process each remaining element
1859        for (new_pos, element) in self.scratch_elements.drain(..).enumerate() {
1860            self.scratch_final_slots.push(None);
1861            let element_type = element.element_type();
1862            let node_type = element.node_type();
1863            let key = element.key();
1864            let hash_code = element.hash_code();
1865            let capabilities = element.capabilities();
1866
1867            // Find best matching old entry via index
1868            let matched_idx = index.find_match(
1869                &old_entries,
1870                &self.scratch_old_used,
1871                EntryMatchQuery {
1872                    element_type,
1873                    node_type,
1874                    key,
1875                    hash_code,
1876                    element: &element,
1877                },
1878            );
1879
1880            if let Some(idx) = matched_idx {
1881                // Reuse existing entry
1882                let entry = &mut old_entries[idx];
1883                let can_update_node = {
1884                    let node_borrow = entry.node.borrow();
1885                    element.can_update_node(&**node_borrow)
1886                };
1887                if !can_update_node {
1888                    let replacement = ModifierNodeEntry::new(
1889                        element_type,
1890                        node_type,
1891                        key,
1892                        element.clone(),
1893                        element.create_node(),
1894                        hash_code,
1895                        capabilities,
1896                    );
1897                    attach_node_tree(&mut **replacement.node.borrow_mut(), context);
1898                    element.update_node(&mut **replacement.node.borrow_mut());
1899                    request_auto_invalidations(context, capabilities);
1900                    self.scratch_final_slots[new_pos] = Some(replacement);
1901                    continue;
1902                }
1903
1904                self.scratch_old_used[idx] = true;
1905                self.scratch_match_order[idx] = Some(new_pos);
1906                let moved = idx != new_pos;
1907
1908                // Check if element actually changed
1909                let same_element = entry.element.as_ref().equals_element(element.as_ref());
1910
1911                // Re-attach node if it was detached
1912                {
1913                    let node_borrow = entry.node.borrow();
1914                    if !node_borrow.node_state().is_attached() {
1915                        drop(node_borrow);
1916                        attach_node_tree(&mut **entry.node.borrow_mut(), context);
1917                    }
1918                }
1919
1920                // Optimize updates: only call update_node if element changed
1921                let needs_update = !same_element || element.requires_update();
1922                if needs_update {
1923                    element.update_node(&mut **entry.node.borrow_mut());
1924                    entry.element = element;
1925                    entry.hash_code = hash_code;
1926                    request_update_auto_invalidations(
1927                        entry.element.as_ref(),
1928                        context,
1929                        capabilities,
1930                    );
1931                }
1932                if moved {
1933                    request_auto_invalidations(context, capabilities);
1934                }
1935
1936                // Always update metadata
1937                entry.key = key;
1938                entry.element_type = element_type;
1939                entry.node_type = node_type;
1940                entry.capabilities = capabilities;
1941                entry
1942                    .node
1943                    .borrow()
1944                    .node_state()
1945                    .set_capabilities(capabilities);
1946            } else {
1947                // Create new entry
1948                let entry = ModifierNodeEntry::new(
1949                    element_type,
1950                    node_type,
1951                    key,
1952                    element.clone(),
1953                    element.create_node(),
1954                    hash_code,
1955                    capabilities,
1956                );
1957                attach_node_tree(&mut **entry.node.borrow_mut(), context);
1958                element.update_node(&mut **entry.node.borrow_mut());
1959                request_auto_invalidations(context, capabilities);
1960                self.scratch_final_slots[new_pos] = Some(entry);
1961            }
1962        }
1963
1964        // Place matched entries in their new positions
1965        for (i, entry) in old_entries.into_iter().enumerate() {
1966            if self.scratch_old_used[i] {
1967                if let Some(pos) = self.scratch_match_order[i] {
1968                    self.scratch_final_slots[pos] = Some(entry);
1969                } else {
1970                    request_auto_invalidations(context, entry.capabilities);
1971                    detach_node_tree(&mut **entry.node.borrow_mut());
1972                }
1973            } else {
1974                request_auto_invalidations(context, entry.capabilities);
1975                detach_node_tree(&mut **entry.node.borrow_mut());
1976            }
1977        }
1978
1979        // Append processed entries to self.entries
1980        self.entries.reserve(self.scratch_final_slots.len());
1981        for slot in self.scratch_final_slots.drain(..) {
1982            if let Some(entry) = slot {
1983                self.entries.push(entry);
1984            } else {
1985                log::error!("modifier reconciliation produced an empty final slot");
1986            }
1987        }
1988
1989        debug_assert_eq!(
1990            self.entries.len(),
1991            processed_entries_len + new_elements_count
1992        );
1993        self.sync_chain_links();
1994    }
1995
1996    /// Convenience wrapper that accepts any iterator of type-erased
1997    /// modifier elements. Elements are collected into a temporary vector
1998    /// before reconciliation.
1999    pub fn update<I>(&mut self, elements: I, context: &mut dyn ModifierNodeContext)
2000    where
2001        I: IntoIterator<Item = DynModifierElement>,
2002    {
2003        let collected: Vec<DynModifierElement> = elements.into_iter().collect();
2004        self.update_from_slice(&collected, context);
2005    }
2006
2007    /// Resets all nodes in the chain. This mirrors the behaviour of
2008    /// Jetpack Compose's `onReset` callback.
2009    pub fn reset(&mut self) {
2010        for entry in &mut self.entries {
2011            reset_node_tree(&mut **entry.node.borrow_mut());
2012        }
2013    }
2014
2015    /// Detaches every node in the chain and clears internal storage.
2016    pub fn detach_all(&mut self) {
2017        for entry in std::mem::take(&mut self.entries) {
2018            detach_node_tree(&mut **entry.node.borrow_mut());
2019            {
2020                let node_borrow = entry.node.borrow();
2021                let state = node_borrow.node_state();
2022                state.set_capabilities(NodeCapabilities::empty());
2023            }
2024        }
2025        self.aggregated_capabilities = NodeCapabilities::empty();
2026        self.head_aggregate_child_capabilities = NodeCapabilities::empty();
2027        self.ordered_nodes.clear();
2028        self.sync_chain_links();
2029    }
2030
2031    pub fn len(&self) -> usize {
2032        self.entries.len()
2033    }
2034
2035    pub fn is_empty(&self) -> bool {
2036        self.entries.is_empty()
2037    }
2038
2039    /// Returns the aggregated capability mask for the entire chain.
2040    pub fn capabilities(&self) -> NodeCapabilities {
2041        self.aggregated_capabilities
2042    }
2043
2044    /// Returns true if the chain contains at least one node with the requested capability.
2045    pub fn has_capability(&self, capability: NodeCapabilities) -> bool {
2046        self.aggregated_capabilities.contains(capability)
2047    }
2048
2049    /// Returns the sentinel head reference for traversal.
2050    pub fn head(&self) -> ModifierChainNodeRef<'_> {
2051        self.make_node_ref(NodeLink::Head)
2052    }
2053
2054    /// Returns the sentinel tail reference for traversal.
2055    pub fn tail(&self) -> ModifierChainNodeRef<'_> {
2056        self.make_node_ref(NodeLink::Tail)
2057    }
2058
2059    /// Iterates over the chain from head to tail, skipping sentinels.
2060    pub fn head_to_tail(&self) -> ModifierChainIter<'_> {
2061        ModifierChainIter::forward(self)
2062    }
2063
2064    /// Iterates over the chain from tail to head, skipping sentinels.
2065    pub fn tail_to_head(&self) -> ModifierChainIter<'_> {
2066        ModifierChainIter::backward(self)
2067    }
2068
2069    /// Calls `f` for every node in insertion order.
2070    pub fn for_each_forward<F>(&self, mut f: F)
2071    where
2072        F: FnMut(ModifierChainNodeRef<'_>),
2073    {
2074        for node in self.head_to_tail() {
2075            f(node);
2076        }
2077    }
2078
2079    /// Calls `f` for every node containing any capability from `mask`.
2080    pub fn for_each_forward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
2081    where
2082        F: FnMut(ModifierChainNodeRef<'_>),
2083    {
2084        if mask.is_empty() {
2085            self.for_each_forward(f);
2086            return;
2087        }
2088
2089        if !self.head().aggregate_child_capabilities().intersects(mask) {
2090            return;
2091        }
2092
2093        for node in self.head_to_tail() {
2094            if node.kind_set().intersects(mask) {
2095                f(node);
2096            }
2097        }
2098    }
2099
2100    /// Calls `f` for every node containing any capability from `mask`, providing the node ref.
2101    pub fn for_each_node_with_capability<F>(&self, mask: NodeCapabilities, mut f: F)
2102    where
2103        F: FnMut(ModifierChainNodeRef<'_>, &dyn ModifierNode),
2104    {
2105        self.for_each_forward_matching(mask, |node_ref| {
2106            node_ref.with_node(|node| f(node_ref.clone(), node));
2107        });
2108    }
2109
2110    /// Calls `f` for every node in reverse insertion order.
2111    pub fn for_each_backward<F>(&self, mut f: F)
2112    where
2113        F: FnMut(ModifierChainNodeRef<'_>),
2114    {
2115        for node in self.tail_to_head() {
2116            f(node);
2117        }
2118    }
2119
2120    /// Calls `f` for every node in reverse order that matches `mask`.
2121    pub fn for_each_backward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
2122    where
2123        F: FnMut(ModifierChainNodeRef<'_>),
2124    {
2125        if mask.is_empty() {
2126            self.for_each_backward(f);
2127            return;
2128        }
2129
2130        if !self.head().aggregate_child_capabilities().intersects(mask) {
2131            return;
2132        }
2133
2134        for node in self.tail_to_head() {
2135            if node.kind_set().intersects(mask) {
2136                f(node);
2137            }
2138        }
2139    }
2140
2141    /// Returns a node reference for the entry at `index`.
2142    pub fn node_ref_at(&self, index: usize) -> Option<ModifierChainNodeRef<'_>> {
2143        if index >= self.entries.len() {
2144            None
2145        } else {
2146            Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))))
2147        }
2148    }
2149
2150    /// Returns the node reference that owns `node`.
2151    pub fn find_node_ref(&self, node: &dyn ModifierNode) -> Option<ModifierChainNodeRef<'_>> {
2152        fn node_data_ptr(node: &dyn ModifierNode) -> *const () {
2153            node as *const dyn ModifierNode as *const ()
2154        }
2155
2156        let target = node_data_ptr(node);
2157        for (index, entry) in self.entries.iter().enumerate() {
2158            if node_data_ptr(&**entry.node.borrow()) == target {
2159                return Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))));
2160            }
2161        }
2162
2163        self.ordered_nodes.iter().find_map(|(link, _caps, _agg)| {
2164            if matches!(link, NodeLink::Entry(path) if path.delegates().is_empty()) {
2165                return None;
2166            }
2167            let matches_target = match link {
2168                NodeLink::Head => node_data_ptr(self.head_sentinel.as_ref()) == target,
2169                NodeLink::Tail => node_data_ptr(self.tail_sentinel.as_ref()) == target,
2170                NodeLink::Entry(path) => {
2171                    let node_borrow = self.entries[path.entry()].node.borrow();
2172                    node_data_ptr(&**node_borrow) == target
2173                }
2174            };
2175            if matches_target {
2176                Some(self.make_node_ref(*link))
2177            } else {
2178                None
2179            }
2180        })
2181    }
2182
2183    /// Downcasts the node at `index` to the requested type.
2184    /// Returns a `Ref` guard that dereferences to the node type.
2185    pub fn node<N: ModifierNode + 'static>(&self, index: usize) -> Option<std::cell::Ref<'_, N>> {
2186        self.entries.get(index).and_then(|entry| {
2187            std::cell::Ref::filter_map(entry.node.borrow(), |boxed_node| {
2188                boxed_node.as_any().downcast_ref::<N>()
2189            })
2190            .ok()
2191        })
2192    }
2193
2194    /// Downcasts the node at `index` to the requested mutable type.
2195    /// Returns a `RefMut` guard that dereferences to the node type.
2196    pub fn node_mut<N: ModifierNode + 'static>(
2197        &self,
2198        index: usize,
2199    ) -> Option<std::cell::RefMut<'_, N>> {
2200        self.entries.get(index).and_then(|entry| {
2201            std::cell::RefMut::filter_map(entry.node.borrow_mut(), |boxed_node| {
2202                boxed_node.as_any_mut().downcast_mut::<N>()
2203            })
2204            .ok()
2205        })
2206    }
2207
2208    /// Returns an Rc clone of the node at the given index for shared ownership.
2209    /// This is used by coordinators to hold direct references to nodes.
2210    pub fn get_node_rc(&self, index: usize) -> Option<Rc<RefCell<Box<dyn ModifierNode>>>> {
2211        self.entries.get(index).map(|entry| Rc::clone(&entry.node))
2212    }
2213
2214    /// Returns true if the chain contains any nodes matching the given invalidation kind.
2215    pub fn has_nodes_for_invalidation(&self, kind: InvalidationKind) -> bool {
2216        self.aggregated_capabilities
2217            .contains(NodeCapabilities::for_invalidation(kind))
2218    }
2219
2220    /// Visits every node in insertion order together with its capability mask.
2221    pub fn visit_nodes<F>(&self, mut f: F)
2222    where
2223        F: FnMut(&dyn ModifierNode, NodeCapabilities),
2224    {
2225        for (link, cached_caps, _agg) in &self.ordered_nodes {
2226            match link {
2227                NodeLink::Head => {
2228                    f(self.head_sentinel.as_ref(), *cached_caps);
2229                }
2230                NodeLink::Tail => {
2231                    f(self.tail_sentinel.as_ref(), *cached_caps);
2232                }
2233                NodeLink::Entry(path) => {
2234                    let node_borrow = self.entries[path.entry()].node.borrow();
2235                    if path.delegates().is_empty() {
2236                        f(&**node_borrow, *cached_caps);
2237                    } else {
2238                        let mut current: &dyn ModifierNode = &**node_borrow;
2239                        for &delegate_index in path.delegates() {
2240                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2241                                current = delegate;
2242                            } else {
2243                                return; // Invalid delegate path
2244                            }
2245                        }
2246                        f(current, *cached_caps);
2247                    }
2248                }
2249            }
2250        }
2251    }
2252
2253    /// Visits every node mutably in insertion order together with its capability mask.
2254    pub fn visit_nodes_mut<F>(&mut self, mut f: F)
2255    where
2256        F: FnMut(&mut dyn ModifierNode, NodeCapabilities),
2257    {
2258        for index in 0..self.ordered_nodes.len() {
2259            let (link, cached_caps, _agg) = self.ordered_nodes[index];
2260            match link {
2261                NodeLink::Head => {
2262                    f(self.head_sentinel.as_mut(), cached_caps);
2263                }
2264                NodeLink::Tail => {
2265                    f(self.tail_sentinel.as_mut(), cached_caps);
2266                }
2267                NodeLink::Entry(path) => {
2268                    let mut node_borrow = self.entries[path.entry()].node.borrow_mut();
2269                    if path.delegates().is_empty() {
2270                        f(&mut **node_borrow, cached_caps);
2271                    } else {
2272                        let mut current: &mut dyn ModifierNode = &mut **node_borrow;
2273                        for &delegate_index in path.delegates() {
2274                            if let Some(delegate) =
2275                                nth_delegate_mut(current, delegate_index as usize)
2276                            {
2277                                current = delegate;
2278                            } else {
2279                                return; // Invalid delegate path
2280                            }
2281                        }
2282                        f(current, cached_caps);
2283                    }
2284                }
2285            }
2286        }
2287    }
2288
2289    fn make_node_ref(&self, link: NodeLink) -> ModifierChainNodeRef<'_> {
2290        ModifierChainNodeRef {
2291            chain: self,
2292            link,
2293            cached_capabilities: None,
2294            cached_aggregate_child: None,
2295        }
2296    }
2297
2298    fn make_node_ref_with_caps(
2299        &self,
2300        link: NodeLink,
2301        caps: NodeCapabilities,
2302        aggregate_child: NodeCapabilities,
2303    ) -> ModifierChainNodeRef<'_> {
2304        ModifierChainNodeRef {
2305            chain: self,
2306            link,
2307            cached_capabilities: Some(caps),
2308            cached_aggregate_child: Some(aggregate_child),
2309        }
2310    }
2311
2312    fn sync_chain_links(&mut self) {
2313        self.rebuild_ordered_nodes();
2314
2315        self.head_sentinel.node_state().set_parent_link(None);
2316        self.tail_sentinel.node_state().set_child_link(None);
2317
2318        if self.ordered_nodes.is_empty() {
2319            self.head_sentinel
2320                .node_state()
2321                .set_child_link(Some(NodeLink::Tail));
2322            self.tail_sentinel
2323                .node_state()
2324                .set_parent_link(Some(NodeLink::Head));
2325            self.aggregated_capabilities = NodeCapabilities::empty();
2326            self.head_aggregate_child_capabilities = NodeCapabilities::empty();
2327            self.head_sentinel
2328                .node_state()
2329                .set_aggregate_child_capabilities(NodeCapabilities::empty());
2330            self.tail_sentinel
2331                .node_state()
2332                .set_aggregate_child_capabilities(NodeCapabilities::empty());
2333            return;
2334        }
2335
2336        let mut previous = NodeLink::Head;
2337        for (link, _caps, _agg) in self.ordered_nodes.iter().copied() {
2338            // Set child link on previous
2339            match &previous {
2340                NodeLink::Head => self.head_sentinel.node_state().set_child_link(Some(link)),
2341                NodeLink::Tail => self.tail_sentinel.node_state().set_child_link(Some(link)),
2342                NodeLink::Entry(path) => {
2343                    let node_borrow = self.entries[path.entry()].node.borrow();
2344                    // Navigate to delegate if needed
2345                    if path.delegates().is_empty() {
2346                        node_borrow.node_state().set_child_link(Some(link));
2347                    } else {
2348                        let mut current: &dyn ModifierNode = &**node_borrow;
2349                        for &delegate_index in path.delegates() {
2350                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2351                                current = delegate;
2352                            }
2353                        }
2354                        current.node_state().set_child_link(Some(link));
2355                    }
2356                }
2357            }
2358            // Set parent link on current
2359            match &link {
2360                NodeLink::Head => self
2361                    .head_sentinel
2362                    .node_state()
2363                    .set_parent_link(Some(previous)),
2364                NodeLink::Tail => self
2365                    .tail_sentinel
2366                    .node_state()
2367                    .set_parent_link(Some(previous)),
2368                NodeLink::Entry(path) => {
2369                    let node_borrow = self.entries[path.entry()].node.borrow();
2370                    // Navigate to delegate if needed
2371                    if path.delegates().is_empty() {
2372                        node_borrow.node_state().set_parent_link(Some(previous));
2373                    } else {
2374                        let mut current: &dyn ModifierNode = &**node_borrow;
2375                        for &delegate_index in path.delegates() {
2376                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2377                                current = delegate;
2378                            }
2379                        }
2380                        current.node_state().set_parent_link(Some(previous));
2381                    }
2382                }
2383            }
2384            previous = link;
2385        }
2386
2387        // Set child link on last node to Tail
2388        match &previous {
2389            NodeLink::Head => self
2390                .head_sentinel
2391                .node_state()
2392                .set_child_link(Some(NodeLink::Tail)),
2393            NodeLink::Tail => self
2394                .tail_sentinel
2395                .node_state()
2396                .set_child_link(Some(NodeLink::Tail)),
2397            NodeLink::Entry(path) => {
2398                let node_borrow = self.entries[path.entry()].node.borrow();
2399                // Navigate to delegate if needed
2400                if path.delegates().is_empty() {
2401                    node_borrow
2402                        .node_state()
2403                        .set_child_link(Some(NodeLink::Tail));
2404                } else {
2405                    let mut current: &dyn ModifierNode = &**node_borrow;
2406                    for &delegate_index in path.delegates() {
2407                        if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2408                            current = delegate;
2409                        }
2410                    }
2411                    current.node_state().set_child_link(Some(NodeLink::Tail));
2412                }
2413            }
2414        }
2415        self.tail_sentinel
2416            .node_state()
2417            .set_parent_link(Some(previous));
2418        self.tail_sentinel.node_state().set_child_link(None);
2419
2420        let mut aggregate = NodeCapabilities::empty();
2421        for (link, cached_caps, cached_aggregate) in self.ordered_nodes.iter_mut().rev() {
2422            aggregate |= *cached_caps;
2423            *cached_aggregate = aggregate;
2424            // Also update NodeState for code that reads through DelegatableNode
2425            match link {
2426                NodeLink::Head => {
2427                    self.head_sentinel
2428                        .node_state()
2429                        .set_aggregate_child_capabilities(aggregate);
2430                }
2431                NodeLink::Tail => {
2432                    self.tail_sentinel
2433                        .node_state()
2434                        .set_aggregate_child_capabilities(aggregate);
2435                }
2436                NodeLink::Entry(path) => {
2437                    let node_borrow = self.entries[path.entry()].node.borrow();
2438                    let state = if path.delegates().is_empty() {
2439                        node_borrow.node_state()
2440                    } else {
2441                        let mut current: &dyn ModifierNode = &**node_borrow;
2442                        for &delegate_index in path.delegates() {
2443                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2444                                current = delegate;
2445                            }
2446                        }
2447                        current.node_state()
2448                    };
2449                    state.set_aggregate_child_capabilities(aggregate);
2450                }
2451            }
2452        }
2453
2454        self.aggregated_capabilities = aggregate;
2455        self.head_aggregate_child_capabilities = aggregate;
2456        self.head_sentinel
2457            .node_state()
2458            .set_aggregate_child_capabilities(aggregate);
2459        self.tail_sentinel
2460            .node_state()
2461            .set_aggregate_child_capabilities(NodeCapabilities::empty());
2462    }
2463
2464    fn rebuild_ordered_nodes(&mut self) {
2465        self.ordered_nodes.clear();
2466        let mut path_buf = [0usize; MAX_DELEGATE_DEPTH];
2467        for (index, entry) in self.entries.iter().enumerate() {
2468            let node_borrow = entry.node.borrow();
2469            Self::enumerate_link_order(
2470                &**node_borrow,
2471                index,
2472                &mut path_buf,
2473                0,
2474                &mut self.ordered_nodes,
2475            );
2476        }
2477    }
2478
2479    fn enumerate_link_order(
2480        node: &dyn ModifierNode,
2481        entry: usize,
2482        path_buf: &mut [usize; MAX_DELEGATE_DEPTH],
2483        path_len: usize,
2484        out: &mut Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
2485    ) {
2486        let caps = node.node_state().capabilities();
2487        out.push((
2488            NodeLink::Entry(NodePath::from_slice(entry, &path_buf[..path_len])),
2489            caps,
2490            NodeCapabilities::empty(),
2491        ));
2492        let mut delegate_index = 0usize;
2493        node.for_each_delegate(&mut |child| {
2494            if path_len < MAX_DELEGATE_DEPTH {
2495                path_buf[path_len] = delegate_index;
2496                Self::enumerate_link_order(child, entry, path_buf, path_len + 1, out);
2497            }
2498            delegate_index += 1;
2499        });
2500    }
2501}
2502
2503impl<'a> ModifierChainNodeRef<'a> {
2504    /// Helper to get NodeState, properly handling RefCell for entries.
2505    /// Returns NodeState values by calling a closure with the state.
2506    fn with_state<R>(&self, f: impl FnOnce(&NodeState) -> R) -> R {
2507        match &self.link {
2508            NodeLink::Head => f(self.chain.head_sentinel.node_state()),
2509            NodeLink::Tail => f(self.chain.tail_sentinel.node_state()),
2510            NodeLink::Entry(path) => {
2511                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2512                // Navigate through delegates if path has them
2513                if path.delegates().is_empty() {
2514                    f(node_borrow.node_state())
2515                } else {
2516                    // Navigate to the delegate node
2517                    let mut current: &dyn ModifierNode = &**node_borrow;
2518                    for &delegate_index in path.delegates() {
2519                        if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2520                            current = delegate;
2521                        } else {
2522                            // Fallback to root node state if delegate path is invalid
2523                            return f(node_borrow.node_state());
2524                        }
2525                    }
2526                    f(current.node_state())
2527                }
2528            }
2529        }
2530    }
2531
2532    /// Provides access to the node via a closure, properly handling RefCell borrows.
2533    /// Returns None for sentinel nodes.
2534    pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
2535        match &self.link {
2536            NodeLink::Head => None, // Head sentinel
2537            NodeLink::Tail => None, // Tail sentinel
2538            NodeLink::Entry(path) => {
2539                let node_borrow = self.chain.entries[path.entry()].node.borrow();
2540                // Navigate through delegates if path has them
2541                if path.delegates().is_empty() {
2542                    Some(f(&**node_borrow))
2543                } else {
2544                    // Navigate to the delegate node
2545                    let mut current: &dyn ModifierNode = &**node_borrow;
2546                    for &delegate_index in path.delegates() {
2547                        // `?`: bail out with None if the delegate path is invalid.
2548                        current = nth_delegate(current, delegate_index as usize)?;
2549                    }
2550                    Some(f(current))
2551                }
2552            }
2553        }
2554    }
2555
2556    /// Returns the parent reference, including sentinel head when applicable.
2557    #[inline]
2558    pub fn parent(&self) -> Option<Self> {
2559        self.with_state(|state| state.parent_link())
2560            .map(|link| self.chain.make_node_ref(link))
2561    }
2562
2563    /// Returns the child reference, including sentinel tail for the last entry.
2564    #[inline]
2565    pub fn child(&self) -> Option<Self> {
2566        self.with_state(|state| state.child_link())
2567            .map(|link| self.chain.make_node_ref(link))
2568    }
2569
2570    /// Returns the capability mask for this specific node.
2571    #[inline]
2572    pub fn kind_set(&self) -> NodeCapabilities {
2573        if let Some(caps) = self.cached_capabilities {
2574            return caps;
2575        }
2576        match &self.link {
2577            NodeLink::Head | NodeLink::Tail => NodeCapabilities::empty(),
2578            NodeLink::Entry(_) => self.with_state(|state| state.capabilities()),
2579        }
2580    }
2581
2582    /// Returns the entry index backing this node when it is part of the chain.
2583    pub fn entry_index(&self) -> Option<usize> {
2584        match &self.link {
2585            NodeLink::Entry(path) => Some(path.entry()),
2586            _ => None,
2587        }
2588    }
2589
2590    /// Returns how many delegate hops separate this node from its root element.
2591    pub fn delegate_depth(&self) -> usize {
2592        match &self.link {
2593            NodeLink::Entry(path) => path.delegates().len(),
2594            _ => 0,
2595        }
2596    }
2597
2598    /// Returns the aggregated capability mask for the subtree rooted at this node.
2599    #[inline]
2600    pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
2601        if let Some(agg) = self.cached_aggregate_child {
2602            return agg;
2603        }
2604        if self.is_tail() {
2605            NodeCapabilities::empty()
2606        } else {
2607            self.with_state(|state| state.aggregate_child_capabilities())
2608        }
2609    }
2610
2611    /// Returns true if this reference targets the sentinel head.
2612    pub fn is_head(&self) -> bool {
2613        matches!(self.link, NodeLink::Head)
2614    }
2615
2616    /// Returns true if this reference targets the sentinel tail.
2617    pub fn is_tail(&self) -> bool {
2618        matches!(self.link, NodeLink::Tail)
2619    }
2620
2621    /// Returns true if this reference targets either sentinel.
2622    pub fn is_sentinel(&self) -> bool {
2623        matches!(self.link, NodeLink::Head | NodeLink::Tail)
2624    }
2625
2626    /// Returns true if this node has any capability bits present in `mask`.
2627    pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
2628        !mask.is_empty() && self.kind_set().intersects(mask)
2629    }
2630
2631    /// Visits descendant nodes, optionally including `self`, in insertion order.
2632    pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
2633    where
2634        F: FnMut(ModifierChainNodeRef<'a>),
2635    {
2636        let mut current = if include_self {
2637            Some(self)
2638        } else {
2639            self.child()
2640        };
2641        while let Some(node) = current {
2642            if node.is_tail() {
2643                break;
2644            }
2645            if !node.is_sentinel() {
2646                f(node.clone());
2647            }
2648            current = node.child();
2649        }
2650    }
2651
2652    /// Visits descendant nodes that match `mask`, short-circuiting when possible.
2653    pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2654    where
2655        F: FnMut(ModifierChainNodeRef<'a>),
2656    {
2657        if mask.is_empty() {
2658            self.visit_descendants(include_self, f);
2659            return;
2660        }
2661
2662        if !self.aggregate_child_capabilities().intersects(mask) {
2663            return;
2664        }
2665
2666        self.visit_descendants(include_self, |node| {
2667            if node.kind_set().intersects(mask) {
2668                f(node);
2669            }
2670        });
2671    }
2672
2673    /// Visits ancestor nodes up to (but excluding) the sentinel head.
2674    pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
2675    where
2676        F: FnMut(ModifierChainNodeRef<'a>),
2677    {
2678        let mut current = if include_self {
2679            Some(self)
2680        } else {
2681            self.parent()
2682        };
2683        while let Some(node) = current {
2684            if node.is_head() {
2685                break;
2686            }
2687            f(node.clone());
2688            current = node.parent();
2689        }
2690    }
2691
2692    /// Visits ancestor nodes that match `mask`.
2693    pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
2694    where
2695        F: FnMut(ModifierChainNodeRef<'a>),
2696    {
2697        if mask.is_empty() {
2698            self.visit_ancestors(include_self, f);
2699            return;
2700        }
2701
2702        self.visit_ancestors(include_self, |node| {
2703            if node.kind_set().intersects(mask) {
2704                f(node);
2705            }
2706        });
2707    }
2708
2709    /// Finds the nearest ancestor focus target node.
2710    ///
2711    /// This is useful for focus navigation to find the parent focusable
2712    /// component in the tree.
2713    pub fn find_parent_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2714        let mut result = None;
2715        self.clone()
2716            .visit_ancestors_matching(false, NodeCapabilities::FOCUS, |node| {
2717                if result.is_none() {
2718                    result = Some(node);
2719                }
2720            });
2721        result
2722    }
2723
2724    /// Finds the first descendant focus target node.
2725    ///
2726    /// This is useful for focus navigation to find the first focusable
2727    /// child component in the tree.
2728    pub fn find_first_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
2729        let mut result = None;
2730        self.clone()
2731            .visit_descendants_matching(false, NodeCapabilities::FOCUS, |node| {
2732                if result.is_none() {
2733                    result = Some(node);
2734                }
2735            });
2736        result
2737    }
2738
2739    /// Returns true if this node or any ancestor has focus capability.
2740    pub fn has_focus_capability_in_ancestors(&self) -> bool {
2741        let mut found = false;
2742        self.clone()
2743            .visit_ancestors_matching(true, NodeCapabilities::FOCUS, |_| {
2744                found = true;
2745            });
2746        found
2747    }
2748}
2749
2750#[cfg(test)]
2751#[path = "tests/modifier_tests.rs"]
2752mod tests;