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