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    /// A control that opens a list of choices and holds the one that is
676    /// picked. Compose's `Role.DropdownList`.
677    DropdownList,
678    /// A control that holds one value out of an ordered set and steps through
679    /// them. Compose's `Role.ValuePicker`.
680    ValuePicker,
681    /// Compose's `heading()`, which is a property rather than a `Role`, but
682    /// reaches the platform through the same field on every backend Cranpose
683    /// targets (`AccessibilityNodeInfo.setHeading`, `Role::Heading`,
684    /// `<h*>`/`UIAccessibilityTraitHeader`).
685    Header,
686    /// A modal surface that takes over the screen until it is dismissed.
687    /// Screen readers announce it and confine their traversal to it, which is
688    /// the accessible half of what makes a dialog modal.
689    Dialog,
690}
691
692/// The value a control holds inside a range, for a slider, a dial or a
693/// progress bar.
694///
695/// This is Compose's `ProgressBarRangeInfo` (`Modifier.progressSemantics(value,
696/// range, steps)`). A control without it reads as plain text: a screen reader
697/// user hears "47 percent" and has no way to change it, because nothing tells
698/// the platform the control is adjustable.
699#[derive(Clone, Copy, Debug, PartialEq)]
700pub struct ProgressBarRangeInfo {
701    pub current: f32,
702    pub start: f32,
703    pub end: f32,
704    /// How many stops sit between `start` and `end`, as Compose counts them.
705    /// Zero means the value moves without stops.
706    pub steps: u32,
707}
708
709impl ProgressBarRangeInfo {
710    pub fn new(current: f32, start: f32, end: f32, steps: u32) -> Self {
711        Self {
712            current,
713            start,
714            end,
715            steps,
716        }
717    }
718
719    /// Where the value sits between the two ends, from 0 to 1.
720    pub fn fraction(&self) -> f32 {
721        let span = self.end - self.start;
722        if span.abs() < f32::EPSILON {
723            return 0.0;
724        }
725        ((self.current - self.start) / span).clamp(0.0, 1.0)
726    }
727
728    /// How far one stop moves the value. With no stops, one tenth of the
729    /// range, which is what a screen reader's swipe up and down expects.
730    pub fn step(&self) -> f32 {
731        let span = self.end - self.start;
732        if self.steps == 0 {
733            span / 10.0
734        } else {
735            span / (self.steps as f32 + 1.0)
736        }
737    }
738}
739
740/// How far a container has scrolled along one axis and how far it can go.
741///
742/// How many rows and columns a list holds, so a screen reader can say
743/// "list, 12 items" as its cursor enters. Compose's `CollectionInfo`.
744#[derive(Clone, Copy, Debug, PartialEq, Eq)]
745pub struct CollectionInfo {
746    pub rows: usize,
747    pub columns: usize,
748}
749
750/// This is Compose's `ScrollAxisRange` (`verticalScrollAxisRange`,
751/// `horizontalScrollAxisRange`). A lazy list has no whole extent to give, so
752/// it reports the first visible item as the value and one more than that as
753/// the end while it can still scroll; a reader only needs to know whether it
754/// can page on.
755#[derive(Clone, Copy, Debug, PartialEq)]
756pub struct ScrollAxisRange {
757    pub value: f32,
758    pub max_value: f32,
759    pub reverse: bool,
760}
761
762impl ScrollAxisRange {
763    pub fn new(value: f32, max_value: f32, reverse: bool) -> Self {
764        Self {
765            value,
766            max_value,
767            reverse,
768        }
769    }
770
771    pub fn can_scroll_forward(&self) -> bool {
772        self.value < self.max_value
773    }
774
775    pub fn can_scroll_backward(&self) -> bool {
776        self.value > 0.0
777    }
778}
779
780/// What a container does when a screen reader pages it, e.g. TalkBack's
781/// scroll forward action or an accesskit scroll down. The two deltas are in
782/// layout pixels; the answer says whether anything moved.
783///
784/// This is Compose's `SemanticsActions.ScrollBy`.
785#[derive(Clone)]
786pub struct SemanticsScrollBy {
787    handler: Rc<dyn Fn(f32, f32) -> bool>,
788}
789
790impl SemanticsScrollBy {
791    pub fn new(handler: impl Fn(f32, f32) -> bool + 'static) -> Self {
792        Self {
793            handler: Rc::new(handler),
794        }
795    }
796
797    pub fn invoke(&self, dx: f32, dy: f32) -> bool {
798        (self.handler)(dx, dy)
799    }
800}
801
802impl fmt::Debug for SemanticsScrollBy {
803    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804        f.debug_struct("SemanticsScrollBy").finish_non_exhaustive()
805    }
806}
807
808/// Two scroll actions always read as the same action, for the reason
809/// [`SemanticsCustomAction`]'s own comparison gives.
810impl PartialEq for SemanticsScrollBy {
811    fn eq(&self, _other: &Self) -> bool {
812        true
813    }
814}
815
816impl Eq for SemanticsScrollBy {}
817
818/// What a list does when a screen reader asks for the row at an index, e.g. a
819/// TalkBack scroll-to-position action.
820///
821/// This is Compose's `SemanticsActions.ScrollToIndex`. The index counts rows
822/// from zero, and the answer says whether the list moved.
823#[derive(Clone)]
824pub struct SemanticsScrollToIndex {
825    handler: Rc<dyn Fn(usize) -> bool>,
826}
827
828impl SemanticsScrollToIndex {
829    pub fn new(handler: impl Fn(usize) -> bool + 'static) -> Self {
830        Self {
831            handler: Rc::new(handler),
832        }
833    }
834
835    pub fn invoke(&self, index: usize) -> bool {
836        (self.handler)(index)
837    }
838}
839
840impl fmt::Debug for SemanticsScrollToIndex {
841    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842        f.debug_struct("SemanticsScrollToIndex")
843            .finish_non_exhaustive()
844    }
845}
846
847/// Two jump actions always read as the same action, for the reason
848/// [`SemanticsCustomAction`]'s own comparison gives.
849impl PartialEq for SemanticsScrollToIndex {
850    fn eq(&self, _other: &Self) -> bool {
851        true
852    }
853}
854
855impl Eq for SemanticsScrollToIndex {}
856
857/// What a control does when a screen reader moves its value, e.g. a VoiceOver
858/// swipe up or a TalkBack set-progress action.
859///
860/// This is Compose's `SemanticsActions.SetProgress`. The value comes in the
861/// control's own range, and the answer says whether the control took it.
862#[derive(Clone)]
863pub struct SemanticsSetProgress {
864    handler: Rc<dyn Fn(f32) -> bool>,
865}
866
867impl SemanticsSetProgress {
868    pub fn new(handler: impl Fn(f32) -> bool + 'static) -> Self {
869        Self {
870            handler: Rc::new(handler),
871        }
872    }
873
874    pub fn invoke(&self, value: f32) -> bool {
875        (self.handler)(value)
876    }
877}
878
879impl fmt::Debug for SemanticsSetProgress {
880    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
881        f.debug_struct("SemanticsSetProgress")
882            .finish_non_exhaustive()
883    }
884}
885
886/// What a text field does when a screen reader or a voice tool hands it new
887/// text. This is Compose's `SemanticsActions.SetText`; the answer says
888/// whether the field took the text.
889#[derive(Clone)]
890pub struct SemanticsSetText(Rc<dyn Fn(&str) -> bool>);
891
892impl SemanticsSetText {
893    pub fn new(handler: impl Fn(&str) -> bool + 'static) -> Self {
894        Self(Rc::new(handler))
895    }
896
897    pub fn invoke(&self, text: &str) -> bool {
898        (self.0)(text)
899    }
900}
901
902impl fmt::Debug for SemanticsSetText {
903    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
904        f.write_str("SemanticsSetText")
905    }
906}
907
908/// What a control does when a screen reader asks it to open or to close.
909/// Compose's `expand` and `collapse` actions.
910#[derive(Clone)]
911pub struct SemanticsExpand(Rc<dyn Fn() -> bool>);
912
913impl SemanticsExpand {
914    pub fn new(handler: impl Fn() -> bool + 'static) -> Self {
915        Self(Rc::new(handler))
916    }
917
918    pub fn invoke(&self) -> bool {
919        (self.0)()
920    }
921}
922
923impl fmt::Debug for SemanticsExpand {
924    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
925        f.write_str("SemanticsExpand")
926    }
927}
928
929/// What a control does when a screen reader asks for its long press. This is
930/// Compose's `SemanticsActions.OnLongClick`; the answer says whether the
931/// control took the ask.
932#[derive(Clone)]
933pub struct SemanticsLongClick(Rc<dyn Fn() -> bool>);
934
935impl SemanticsLongClick {
936    pub fn new(handler: impl Fn() -> bool + 'static) -> Self {
937        Self(Rc::new(handler))
938    }
939
940    pub fn invoke(&self) -> bool {
941        (self.0)()
942    }
943}
944
945impl fmt::Debug for SemanticsLongClick {
946    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
947        f.write_str("SemanticsLongClick")
948    }
949}
950
951impl PartialEq for SemanticsLongClick {
952    fn eq(&self, _other: &Self) -> bool {
953        true
954    }
955}
956
957impl PartialEq for SemanticsExpand {
958    fn eq(&self, _other: &Self) -> bool {
959        true
960    }
961}
962
963/// What a control does when a screen reader asks to send it away: a row a
964/// sighted person swipes off, a sheet a sighted person taps outside of.
965/// Compose's `dismiss` action.
966#[derive(Clone)]
967pub struct SemanticsDismiss(Rc<dyn Fn() -> bool>);
968
969impl SemanticsDismiss {
970    pub fn new(handler: impl Fn() -> bool + 'static) -> Self {
971        Self(Rc::new(handler))
972    }
973
974    pub fn invoke(&self) -> bool {
975        (self.0)()
976    }
977}
978
979impl fmt::Debug for SemanticsDismiss {
980    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981        f.write_str("SemanticsDismiss")
982    }
983}
984
985impl PartialEq for SemanticsDismiss {
986    fn eq(&self, _other: &Self) -> bool {
987        true
988    }
989}
990
991impl PartialEq for SemanticsSetText {
992    fn eq(&self, _other: &Self) -> bool {
993        true
994    }
995}
996
997/// Two set-progress actions always read as the same action, for the reason
998/// [`SemanticsCustomAction`]'s own comparison gives: the closure is rebuilt on
999/// every semantics collection, so comparing handler identity would report a
1000/// changed tree on every frame.
1001impl PartialEq for SemanticsSetProgress {
1002    fn eq(&self, _other: &Self) -> bool {
1003        true
1004    }
1005}
1006
1007impl Eq for SemanticsSetProgress {}
1008
1009/// How urgently a screen reader reads a node whose text changed on its own.
1010///
1011/// This is Compose's `LiveRegionMode` (`Modifier.semantics { liveRegion =
1012/// LiveRegionMode.Polite }`). A node without it stays silent until the reader
1013/// lands on it, which is wrong for a timer, a countdown, a status line, or an
1014/// error that appears next to a text field: a blind user hears nothing.
1015#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1016pub enum LiveRegionMode {
1017    /// Read the new text once the reader finishes what it says now.
1018    Polite,
1019    /// Cut off what the reader says now and read the new text at once. For
1020    /// text a user must hear immediately, such as an error that stops them.
1021    Assertive,
1022}
1023
1024/// A screen-reader action that is not a click, e.g. Compose's
1025/// `customActions = listOf(CustomAccessibilityAction("Pause") { … })`.
1026///
1027/// TalkBack surfaces these through its actions menu rather than by activating
1028/// the node, which is the only way to reach a command that has no on-screen
1029/// control — pausing a game whose whole surface is one tap-to-launch target.
1030#[derive(Clone)]
1031pub struct SemanticsCustomAction {
1032    /// What the screen reader reads out in its actions menu.
1033    pub label: String,
1034    handler: Rc<dyn Fn()>,
1035}
1036
1037impl SemanticsCustomAction {
1038    pub fn new(label: impl Into<String>, handler: impl Fn() + 'static) -> Self {
1039        Self {
1040            label: label.into(),
1041            handler: Rc::new(handler),
1042        }
1043    }
1044
1045    pub fn invoke(&self) {
1046        (self.handler)();
1047    }
1048}
1049
1050impl fmt::Debug for SemanticsCustomAction {
1051    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1052        f.debug_struct("SemanticsCustomAction")
1053            .field("label", &self.label)
1054            .finish_non_exhaustive()
1055    }
1056}
1057
1058/// Two custom actions are the same action when they read the same.
1059///
1060/// The handler is deliberately excluded. A semantics recorder runs on every
1061/// collection, so the closure is a fresh `Rc` each time and comparing handler
1062/// identity would report "the tree changed" on every frame — which on Android
1063/// means re-serialising and re-publishing the whole virtual-view tree across
1064/// JNI 60 times a second. Handlers are looked up in the live semantics tree at
1065/// the moment the action fires (see `perform_custom_action`), so a handler that
1066/// is newer than the last published snapshot is still the one that runs.
1067impl PartialEq for SemanticsCustomAction {
1068    fn eq(&self, other: &Self) -> bool {
1069        self.label == other.label
1070    }
1071}
1072
1073impl Eq for SemanticsCustomAction {}
1074
1075/// A semantics node for content that is *drawn* rather than laid out.
1076///
1077/// An immediate-mode surface — one `Canvas` that paints a whole screen — has
1078/// exactly one layout node, so the semantics tree built from layout has exactly
1079/// one node to offer a screen reader. This is the escape hatch: the drawing
1080/// code already knows where it put every control, so it publishes those
1081/// rectangles as semantics directly. Android's own answer for a canvas-drawn
1082/// `View` is the same shape (`ExploreByTouchHelper` feeding virtual view ids
1083/// into an `AccessibilityNodeProvider`), and Cranpose's Android bridge is
1084/// already an `AccessibilityNodeProvider`, so these land as first-class
1085/// virtual views next to the ones layout produces.
1086///
1087/// `bounds` is in the publishing node's own coordinates (logical px, origin at
1088/// that node's top-left), because that is what a draw scope works in.
1089#[derive(Clone, Debug, PartialEq)]
1090pub struct CanvasSemanticsNode {
1091    /// Identity that must survive a redraw.
1092    ///
1093    /// A screen reader parks its cursor on a virtual view id; if the id for
1094    /// "the Haptics switch" changes when the list scrolls, the cursor jumps.
1095    /// Derive this from what the control *is* (a row index, an enum
1096    /// discriminant), never from where it currently sits.
1097    pub key: u64,
1098    /// Where the control was drawn, relative to the publishing node.
1099    pub bounds: cranpose_ui_graphics::Rect,
1100    pub label: String,
1101    pub role: Option<SemanticsWidgetRole>,
1102    /// Compose's `stateDescription` — what the control currently reads as
1103    /// ("CAMPAIGN", "3 of 18 gold"), spoken after the label and re-spoken on
1104    /// its own when only the state changed.
1105    pub state_description: Option<String>,
1106    /// Compose's `onClick(label = …)`. TalkBack reads it as "double tap to
1107    /// `<label>`", so it is a verb phrase, not a repeat of the label.
1108    pub on_click_label: Option<String>,
1109    pub clickable: bool,
1110    /// Compose's `selected`, for `Role.RadioButton`/`Role.Tab`.
1111    pub selected: Option<bool>,
1112    /// Compose's `toggleableState`, for `Role.Switch`/`Role.Checkbox`.
1113    pub toggled: Option<bool>,
1114    pub enabled: bool,
1115    pub custom_actions: Vec<SemanticsCustomAction>,
1116}
1117
1118impl Default for CanvasSemanticsNode {
1119    fn default() -> Self {
1120        Self {
1121            key: 0,
1122            bounds: cranpose_ui_graphics::Rect {
1123                x: 0.0,
1124                y: 0.0,
1125                width: 0.0,
1126                height: 0.0,
1127            },
1128            label: String::new(),
1129            role: None,
1130            state_description: None,
1131            on_click_label: None,
1132            clickable: false,
1133            selected: None,
1134            toggled: None,
1135            enabled: true,
1136            custom_actions: Vec::new(),
1137        }
1138    }
1139}
1140
1141impl CanvasSemanticsNode {
1142    /// A clickable control drawn at `bounds`.
1143    pub fn control(key: u64, bounds: cranpose_ui_graphics::Rect, label: impl Into<String>) -> Self {
1144        Self {
1145            key,
1146            bounds,
1147            label: label.into(),
1148            clickable: true,
1149            ..Self::default()
1150        }
1151    }
1152
1153    /// A drawn label that is read but not activated.
1154    pub fn text(key: u64, bounds: cranpose_ui_graphics::Rect, label: impl Into<String>) -> Self {
1155        Self {
1156            key,
1157            bounds,
1158            label: label.into(),
1159            ..Self::default()
1160        }
1161    }
1162
1163    pub fn with_role(mut self, role: SemanticsWidgetRole) -> Self {
1164        self.role = Some(role);
1165        self
1166    }
1167
1168    pub fn with_state_description(mut self, state: impl Into<String>) -> Self {
1169        self.state_description = Some(state.into());
1170        self
1171    }
1172
1173    pub fn with_click_label(mut self, label: impl Into<String>) -> Self {
1174        self.on_click_label = Some(label.into());
1175        self.clickable = true;
1176        self
1177    }
1178
1179    pub fn with_selected(mut self, selected: bool) -> Self {
1180        self.selected = Some(selected);
1181        self
1182    }
1183
1184    pub fn with_toggled(mut self, toggled: bool) -> Self {
1185        self.toggled = Some(toggled);
1186        self
1187    }
1188
1189    pub fn with_enabled(mut self, enabled: bool) -> Self {
1190        self.enabled = enabled;
1191        self
1192    }
1193
1194    pub fn with_custom_action(mut self, action: SemanticsCustomAction) -> Self {
1195        self.custom_actions.push(action);
1196        self
1197    }
1198}
1199
1200/// Semantics configuration for accessibility.
1201#[derive(Clone, Debug, PartialEq)]
1202pub struct SemanticsConfiguration {
1203    pub content_description: Option<String>,
1204    /// Compose's `stateDescription`.
1205    pub state_description: Option<String>,
1206    /// Compose's `onClick(label = …)`; implies clickable.
1207    pub on_click_label: Option<String>,
1208    /// What this control does when a screen reader asks for its long press.
1209    /// Compose's `onLongClick`.
1210    pub on_long_click: Option<SemanticsLongClick>,
1211    /// What the long press does, as a verb phrase a reader reads out:
1212    /// "Remove receipt". Compose's `onLongClick(label = …)`.
1213    pub on_long_click_label: Option<String>,
1214    /// Compose's `Role`.
1215    pub role: Option<SemanticsWidgetRole>,
1216    pub selected: Option<bool>,
1217    pub toggled: Option<bool>,
1218    pub enabled: bool,
1219    pub is_clickable: bool,
1220    pub is_editable_text: bool,
1221    /// The text an editable field holds, read as its value. Compose's
1222    /// `editableText`.
1223    pub text: Option<String>,
1224    pub text_selection: Option<crate::text::TextRange>,
1225    pub custom_actions: Vec<SemanticsCustomAction>,
1226    /// Controls this node drew itself instead of laying out. See
1227    /// [`CanvasSemanticsNode`].
1228    pub canvas_children: Vec<CanvasSemanticsNode>,
1229    /// Whether this node takes over the screen: everything outside it is
1230    /// inert, and a screen reader keeps its traversal inside.
1231    pub is_modal: bool,
1232    /// Whether a screen reader skips this node and everything under it: a
1233    /// decorative image, or a placeholder drawn under a named field. Compose's
1234    /// `hideFromAccessibility`.
1235    pub hidden: bool,
1236    /// Whether a screen reader takes this node and the text under it as one
1237    /// stop, the way it does for a button: a row whose name, count and price
1238    /// belong together. Compose's `mergeDescendants`.
1239    pub merge_descendants: bool,
1240    /// Whether the selectable controls under this node form one group, so a
1241    /// screen reader says which of how many a tab or a radio button is.
1242    /// Compose's `selectableGroup`.
1243    pub selectable_group: bool,
1244    /// The title of the screen or pane this node is the root of, read out when
1245    /// the app moves to it. Compose's `paneTitle`.
1246    pub pane_title: Option<String>,
1247    /// Why the control's content is wrong, read after its state: "invalid,
1248    /// the amount needs a number". Compose's `error`.
1249    pub error: Option<String>,
1250    /// Whether this field holds a secret, so no screen reader reads its text
1251    /// out and no platform mirror carries it. Compose's `password`.
1252    pub password: bool,
1253    /// Where a screen reader visits this node among the ones beside it: a
1254    /// smaller number comes first, and nodes left at zero keep the order the
1255    /// app laid them out in. Compose's `traversalIndex`.
1256    pub traversal_index: f32,
1257    /// Compose's `liveRegion`. When set, a screen reader reads this node again
1258    /// whenever its text changes, without the user moving to it.
1259    pub live_region: Option<LiveRegionMode>,
1260    /// Compose's `progressBarRangeInfo`. A control with it is adjustable: a
1261    /// screen reader offers its own way to move the value.
1262    pub progress: Option<ProgressBarRangeInfo>,
1263    /// What the control does when a screen reader moves its value. Compose's
1264    /// `setProgress`.
1265    pub set_progress: Option<SemanticsSetProgress>,
1266    /// What this field does when a screen reader or a voice tool hands it
1267    /// text. Compose's `setText`.
1268    pub set_text: Option<SemanticsSetText>,
1269    /// What this control does when a screen reader asks it to open. A control
1270    /// that says so reads as closed. Compose's `expand`.
1271    pub expand: Option<SemanticsExpand>,
1272    /// What this control does when a screen reader asks to send it away: a row
1273    /// a sighted person swipes off, a sheet a sighted person taps outside of.
1274    /// Compose's `dismiss`.
1275    pub dismiss: Option<SemanticsDismiss>,
1276    /// What this control does when a screen reader asks it to close. A control
1277    /// that says so reads as open. Compose's `collapse`.
1278    pub collapse: Option<SemanticsExpand>,
1279    /// How far this container scrolled up and down. Compose's
1280    /// `verticalScrollAxisRange`.
1281    pub vertical_scroll: Option<ScrollAxisRange>,
1282    /// How far this container scrolled left and right. Compose's
1283    /// `horizontalScrollAxisRange`.
1284    pub horizontal_scroll: Option<ScrollAxisRange>,
1285    /// What this container does when a screen reader pages it. Compose's
1286    /// `scrollBy`.
1287    pub scroll_by: Option<SemanticsScrollBy>,
1288    /// What this list does when a screen reader asks for the row at an index,
1289    /// so a reader reaches row 300 without paging to it. Compose's
1290    /// `scrollToIndex`.
1291    pub scroll_to_index: Option<SemanticsScrollToIndex>,
1292    /// How many rows and columns this list holds. Compose's `collectionInfo`.
1293    pub collection: Option<CollectionInfo>,
1294}
1295
1296impl Default for SemanticsConfiguration {
1297    fn default() -> Self {
1298        Self {
1299            content_description: None,
1300            state_description: None,
1301            on_click_label: None,
1302            on_long_click: None,
1303            on_long_click_label: None,
1304            role: None,
1305            selected: None,
1306            toggled: None,
1307            enabled: true,
1308            is_clickable: false,
1309            is_editable_text: false,
1310            text: None,
1311            text_selection: None,
1312            custom_actions: Vec::new(),
1313            canvas_children: Vec::new(),
1314            is_modal: false,
1315            hidden: false,
1316            merge_descendants: false,
1317            selectable_group: false,
1318            pane_title: None,
1319            error: None,
1320            password: false,
1321            traversal_index: 0.0,
1322            live_region: None,
1323            progress: None,
1324            set_progress: None,
1325            set_text: None,
1326            expand: None,
1327            dismiss: None,
1328            collapse: None,
1329            vertical_scroll: None,
1330            horizontal_scroll: None,
1331            scroll_by: None,
1332            scroll_to_index: None,
1333            collection: None,
1334        }
1335    }
1336}
1337
1338/// One value that says what a screen reader reads for a node, built up with
1339/// the methods below and handed to `Modifier::semantics_spec`.
1340///
1341/// Compose has no such value: it takes a receiver lambda, which Kotlin makes
1342/// read well and Rust has no match for. `SemanticsSpec::new().content_description("Save")`
1343/// reads better than `|config| config.content_description = Some("Save".into())`,
1344/// it is one chain element rather than one per property, and two specs can be
1345/// compared. The closure form stays as `Modifier::semantics` for parity.
1346pub type SemanticsSpec = SemanticsConfiguration;
1347
1348impl SemanticsConfiguration {
1349    /// An empty spec to build on. Every field is what it is with no semantics
1350    /// declared at all.
1351    pub fn new() -> Self {
1352        Self::default()
1353    }
1354
1355    /// The name a screen reader reads for the control. Compose's
1356    /// `contentDescription`.
1357    pub fn content_description(mut self, name: impl Into<String>) -> Self {
1358        self.content_description = Some(name.into());
1359        self
1360    }
1361
1362    /// What the control says about itself after its name. Compose's
1363    /// `stateDescription`.
1364    pub fn state_description(mut self, state: impl Into<String>) -> Self {
1365        self.state_description = Some(state.into());
1366        self
1367    }
1368
1369    /// A screen reader offers to activate the control. Compose's `onClick`.
1370    pub fn clickable(mut self) -> Self {
1371        self.is_clickable = true;
1372        self
1373    }
1374
1375    /// What the control does when a screen reader asks for its long press,
1376    /// and the verb phrase a reader reads out for it. Compose's
1377    /// `onLongClick(label) { … }`.
1378    pub fn on_long_click(
1379        mut self,
1380        label: impl Into<String>,
1381        action: impl Fn() -> bool + 'static,
1382    ) -> Self {
1383        self.on_long_click_label = Some(label.into());
1384        self.on_long_click = Some(SemanticsLongClick::new(action));
1385        self
1386    }
1387
1388    /// Whether the control is on or off. Compose's `toggleableState`.
1389    pub fn toggled(mut self, toggled: bool) -> Self {
1390        self.toggled = Some(toggled);
1391        self
1392    }
1393
1394    /// Whether the control is the one picked out of a group. Compose's
1395    /// `selected`.
1396    pub fn selected(mut self, selected: bool) -> Self {
1397        self.selected = Some(selected);
1398        self
1399    }
1400
1401    /// What kind of control a screen reader reads this as. Compose's `Role`.
1402    pub fn role(mut self, role: SemanticsWidgetRole) -> Self {
1403        self.role = Some(role);
1404        self
1405    }
1406
1407    /// Reads as a heading, so a reader can jump between the headings of a
1408    /// screen. Compose's `heading()`.
1409    pub fn heading(self) -> Self {
1410        self.role(SemanticsWidgetRole::Header)
1411    }
1412
1413    /// Why the control's content is wrong. Compose's `error`.
1414    pub fn error(mut self, message: impl Into<String>) -> Self {
1415        self.error = Some(message.into());
1416        self
1417    }
1418
1419    /// Holds a secret, so no reader reads the text out. Compose's `password`.
1420    pub fn password(mut self) -> Self {
1421        self.password = true;
1422        self
1423    }
1424
1425    /// Names the screen or pane this node is the root of. Compose's
1426    /// `paneTitle`.
1427    pub fn pane_title(mut self, title: impl Into<String>) -> Self {
1428        self.pane_title = Some(title.into());
1429        self
1430    }
1431
1432    /// Where a reader visits this node among the ones beside it. Compose's
1433    /// `traversalIndex`.
1434    pub fn traversal_index(mut self, index: f32) -> Self {
1435        self.traversal_index = index;
1436        self
1437    }
1438
1439    /// Skips this node and everything under it. Compose's
1440    /// `hideFromAccessibility`.
1441    pub fn hidden(mut self) -> Self {
1442        self.hidden = true;
1443        self
1444    }
1445
1446    /// Takes this node and the text under it as one stop. Compose's
1447    /// `mergeDescendants`.
1448    pub fn merge_descendants(mut self) -> Self {
1449        self.merge_descendants = true;
1450        self
1451    }
1452
1453    /// Makes the selectable controls under this node one group. Compose's
1454    /// `selectableGroup`.
1455    pub fn selectable_group(mut self) -> Self {
1456        self.selectable_group = true;
1457        self
1458    }
1459
1460    /// Reads this node again whenever its text changes. Compose's
1461    /// `liveRegion`.
1462    pub fn live_region(mut self, mode: LiveRegionMode) -> Self {
1463        self.live_region = Some(mode);
1464        self
1465    }
1466    pub fn merge(&mut self, other: &SemanticsConfiguration) {
1467        if let Some(description) = &other.content_description {
1468            self.content_description = Some(description.clone());
1469        }
1470        if let Some(state) = &other.state_description {
1471            self.state_description = Some(state.clone());
1472        }
1473        if let Some(label) = &other.on_click_label {
1474            self.on_click_label = Some(label.clone());
1475        }
1476        if let Some(label) = &other.on_long_click_label {
1477            self.on_long_click_label = Some(label.clone());
1478        }
1479        if let Some(role) = other.role {
1480            self.role = Some(role);
1481        }
1482        if let Some(selected) = other.selected {
1483            self.selected = Some(selected);
1484        }
1485        if let Some(toggled) = other.toggled {
1486            self.toggled = Some(toggled);
1487        }
1488        self.enabled &= other.enabled;
1489        self.is_clickable |= other.is_clickable;
1490        self.is_editable_text |= other.is_editable_text;
1491        if let Some(text) = &other.text {
1492            self.text = Some(text.clone());
1493        }
1494        self.is_modal |= other.is_modal;
1495        self.hidden |= other.hidden;
1496        self.merge_descendants |= other.merge_descendants;
1497        self.selectable_group |= other.selectable_group;
1498        self.password |= other.password;
1499        if other.traversal_index != 0.0 {
1500            self.traversal_index = other.traversal_index;
1501        }
1502        if let Some(live_region) = other.live_region {
1503            self.live_region = Some(live_region);
1504        }
1505        self.merge_words(other);
1506        self.merge_actions(other);
1507        self.merge_ranges(other);
1508    }
1509
1510    /// The lines a screen reader reads out that stand on their own: the title
1511    /// of a pane, and the reason a control's content is wrong.
1512    fn merge_words(&mut self, other: &SemanticsConfiguration) {
1513        if let Some(title) = &other.pane_title {
1514            self.pane_title = Some(title.clone());
1515        }
1516        if let Some(error) = &other.error {
1517            self.error = Some(error.clone());
1518        }
1519    }
1520
1521    /// What a screen reader can ask the node to do, and the controls the node
1522    /// drew rather than laid out.
1523    fn merge_actions(&mut self, other: &SemanticsConfiguration) {
1524        self.custom_actions
1525            .extend(other.custom_actions.iter().cloned());
1526        self.canvas_children
1527            .extend(other.canvas_children.iter().cloned());
1528        if let Some(set_progress) = &other.set_progress {
1529            self.set_progress = Some(set_progress.clone());
1530        }
1531        if let Some(set_text) = &other.set_text {
1532            self.set_text = Some(set_text.clone());
1533        }
1534        if let Some(expand) = &other.expand {
1535            self.expand = Some(expand.clone());
1536        }
1537        if let Some(collapse) = &other.collapse {
1538            self.collapse = Some(collapse.clone());
1539        }
1540        if let Some(dismiss) = &other.dismiss {
1541            self.dismiss = Some(dismiss.clone());
1542        }
1543        if let Some(long_click) = &other.on_long_click {
1544            self.on_long_click = Some(long_click.clone());
1545        }
1546        if let Some(scroll_by) = &other.scroll_by {
1547            self.scroll_by = Some(scroll_by.clone());
1548        }
1549        if let Some(scroll_to_index) = &other.scroll_to_index {
1550            self.scroll_to_index = Some(scroll_to_index.clone());
1551        }
1552    }
1553
1554    /// The numbers behind a control: where a value sits in its range, how far
1555    /// a container scrolled, how much a list holds, and what text is picked.
1556    fn merge_ranges(&mut self, other: &SemanticsConfiguration) {
1557        if let Some(selection) = other.text_selection {
1558            self.text_selection = Some(selection);
1559        }
1560        if let Some(progress) = other.progress {
1561            self.progress = Some(progress);
1562        }
1563        if let Some(range) = other.vertical_scroll {
1564            self.vertical_scroll = Some(range);
1565        }
1566        if let Some(range) = other.horizontal_scroll {
1567            self.horizontal_scroll = Some(range);
1568        }
1569        if let Some(collection) = other.collection {
1570            self.collection = Some(collection);
1571        }
1572    }
1573
1574    /// Whether a screen reader should offer activation. A named click label is
1575    /// how Compose declares `onClick`, so it implies the action the same way.
1576    pub fn is_activatable(&self) -> bool {
1577        self.is_clickable || self.on_click_label.is_some()
1578    }
1579}
1580
1581impl fmt::Debug for dyn ModifierNode {
1582    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1583        f.debug_struct("ModifierNode").finish_non_exhaustive()
1584    }
1585}
1586
1587impl dyn ModifierNode {
1588    pub fn as_any(&self) -> &dyn Any {
1589        self
1590    }
1591
1592    pub fn as_any_mut(&mut self) -> &mut dyn Any {
1593        self
1594    }
1595}
1596
1597/// Strongly typed modifier elements that can create and update nodes while
1598/// exposing equality/hash/inspector contracts that mirror Jetpack Compose.
1599pub trait ModifierNodeElement: fmt::Debug + Hash + PartialEq + 'static {
1600    type Node: ModifierNode;
1601
1602    /// Creates a new modifier node instance for this element.
1603    fn create(&self) -> Self::Node;
1604
1605    /// Brings an existing modifier node up to date with the element's data.
1606    fn update(&self, node: &mut Self::Node);
1607
1608    /// Optional key used to disambiguate multiple instances of the same element type.
1609    fn key(&self) -> Option<u64> {
1610        None
1611    }
1612
1613    /// Human readable name surfaced to inspector tooling.
1614    fn inspector_name(&self) -> &'static str {
1615        type_name::<Self>()
1616    }
1617
1618    /// Records inspector properties for tooling.
1619    fn inspector_properties(&self, _inspector: &mut dyn FnMut(&'static str, String)) {}
1620
1621    /// Returns the capabilities of nodes created by this element.
1622    /// Override this to indicate which specialized traits the node implements.
1623    fn capabilities(&self) -> NodeCapabilities {
1624        NodeCapabilities::default()
1625    }
1626
1627    /// Whether this element requires `update` to be called even if `eq` returns true.
1628    ///
1629    /// This is useful for elements that ignore certain fields in `eq` (e.g. closures)
1630    /// to allow node reuse, but still need those fields updated in the existing node.
1631    /// Defaults to `false`.
1632    fn always_update(&self) -> bool {
1633        false
1634    }
1635
1636    /// Whether modifier reconciliation should request capability-wide invalidations
1637    /// after updating an existing node.
1638    fn auto_invalidate_on_update(&self) -> bool {
1639        true
1640    }
1641
1642    /// Optional targeted invalidation requested after updating an existing node.
1643    ///
1644    /// This is for nodes whose attach/remove capability is broader than the
1645    /// work needed for a value-only update. For example, an offset node
1646    /// participates in layout on attach but an x/y change only needs placement
1647    /// data and draw output refreshed.
1648    fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1649        None
1650    }
1651}
1652
1653/// Capability flags indicating which specialized traits a modifier node implements.
1654#[derive(Clone, Copy, PartialEq, Eq, Hash)]
1655pub struct NodeCapabilities(u32);
1656
1657impl NodeCapabilities {
1658    /// No capabilities.
1659    pub const NONE: Self = Self(0);
1660    /// Modifier participates in measure/layout.
1661    pub const LAYOUT: Self = Self(1 << 0);
1662    /// Modifier participates in draw.
1663    pub const DRAW: Self = Self(1 << 1);
1664    /// Modifier participates in pointer input.
1665    pub const POINTER_INPUT: Self = Self(1 << 2);
1666    /// Modifier participates in semantics tree construction.
1667    pub const SEMANTICS: Self = Self(1 << 3);
1668    /// Modifier participates in modifier locals.
1669    pub const MODIFIER_LOCALS: Self = Self(1 << 4);
1670    /// Modifier participates in focus management.
1671    pub const FOCUS: Self = Self(1 << 5);
1672
1673    /// Returns an empty capability set.
1674    pub const fn empty() -> Self {
1675        Self::NONE
1676    }
1677
1678    /// Returns whether all bits in `other` are present in `self`.
1679    pub const fn contains(self, other: Self) -> bool {
1680        (self.0 & other.0) == other.0
1681    }
1682
1683    /// Returns whether any bit in `other` is present in `self`.
1684    pub const fn intersects(self, other: Self) -> bool {
1685        (self.0 & other.0) != 0
1686    }
1687
1688    /// Inserts the requested capability bits.
1689    pub fn insert(&mut self, other: Self) {
1690        self.0 |= other.0;
1691    }
1692
1693    /// Returns the raw bit representation.
1694    pub const fn bits(self) -> u32 {
1695        self.0
1696    }
1697
1698    /// Returns true when no capabilities are set.
1699    pub const fn is_empty(self) -> bool {
1700        self.0 == 0
1701    }
1702
1703    /// Returns the capability bit mask required for the given invalidation.
1704    pub const fn for_invalidation(kind: InvalidationKind) -> Self {
1705        match kind {
1706            InvalidationKind::Layout => Self::LAYOUT,
1707            InvalidationKind::Draw => Self::DRAW,
1708            InvalidationKind::PointerInput => Self::POINTER_INPUT,
1709            InvalidationKind::Semantics => Self::SEMANTICS,
1710            InvalidationKind::Focus => Self::FOCUS,
1711        }
1712    }
1713}
1714
1715impl Default for NodeCapabilities {
1716    fn default() -> Self {
1717        Self::NONE
1718    }
1719}
1720
1721impl fmt::Debug for NodeCapabilities {
1722    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1723        f.debug_struct("NodeCapabilities")
1724            .field("layout", &self.contains(Self::LAYOUT))
1725            .field("draw", &self.contains(Self::DRAW))
1726            .field("pointer_input", &self.contains(Self::POINTER_INPUT))
1727            .field("semantics", &self.contains(Self::SEMANTICS))
1728            .field("modifier_locals", &self.contains(Self::MODIFIER_LOCALS))
1729            .field("focus", &self.contains(Self::FOCUS))
1730            .finish()
1731    }
1732}
1733
1734impl BitOr for NodeCapabilities {
1735    type Output = Self;
1736
1737    fn bitor(self, rhs: Self) -> Self::Output {
1738        Self(self.0 | rhs.0)
1739    }
1740}
1741
1742impl BitOrAssign for NodeCapabilities {
1743    fn bitor_assign(&mut self, rhs: Self) {
1744        self.0 |= rhs.0;
1745    }
1746}
1747
1748/// Records an invalidation request together with the capability mask that triggered it.
1749#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1750pub struct ModifierInvalidation {
1751    kind: InvalidationKind,
1752    capabilities: NodeCapabilities,
1753}
1754
1755impl ModifierInvalidation {
1756    /// Creates a new modifier invalidation entry.
1757    pub const fn new(kind: InvalidationKind, capabilities: NodeCapabilities) -> Self {
1758        Self { kind, capabilities }
1759    }
1760
1761    /// Returns the invalidated pipeline kind.
1762    pub const fn kind(self) -> InvalidationKind {
1763        self.kind
1764    }
1765
1766    /// Returns the capability mask associated with the invalidation.
1767    pub const fn capabilities(self) -> NodeCapabilities {
1768        self.capabilities
1769    }
1770}
1771
1772/// Type-erased modifier element used by the runtime to reconcile chains.
1773pub trait AnyModifierElement: fmt::Debug {
1774    fn node_type(&self) -> TypeId;
1775
1776    fn element_type(&self) -> TypeId;
1777
1778    fn create_node(&self) -> Box<dyn ModifierNode>;
1779
1780    fn can_update_node(&self, node: &dyn ModifierNode) -> bool;
1781
1782    fn update_node(&self, node: &mut dyn ModifierNode);
1783
1784    fn key(&self) -> Option<u64>;
1785
1786    fn capabilities(&self) -> NodeCapabilities {
1787        NodeCapabilities::default()
1788    }
1789
1790    fn hash_code(&self) -> u64;
1791
1792    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool;
1793
1794    fn inspector_name(&self) -> &'static str;
1795
1796    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String));
1797
1798    fn requires_update(&self) -> bool;
1799
1800    fn auto_invalidates_on_update(&self) -> bool;
1801
1802    fn update_invalidation_kind(&self) -> Option<InvalidationKind>;
1803
1804    fn as_any(&self) -> &dyn Any;
1805}
1806
1807struct TypedModifierElement<E: ModifierNodeElement> {
1808    element: E,
1809    cached_hash: u64,
1810}
1811
1812impl<E: ModifierNodeElement> TypedModifierElement<E> {
1813    fn new(element: E) -> Self {
1814        let mut hasher = default::new();
1815        element.hash(&mut hasher);
1816        Self {
1817            element,
1818            cached_hash: hasher.finish(),
1819        }
1820    }
1821}
1822
1823impl<E> fmt::Debug for TypedModifierElement<E>
1824where
1825    E: ModifierNodeElement,
1826{
1827    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1828        f.debug_struct("TypedModifierElement")
1829            .field("type", &type_name::<E>())
1830            .finish()
1831    }
1832}
1833
1834impl<E> AnyModifierElement for TypedModifierElement<E>
1835where
1836    E: ModifierNodeElement,
1837{
1838    fn node_type(&self) -> TypeId {
1839        TypeId::of::<E::Node>()
1840    }
1841
1842    fn element_type(&self) -> TypeId {
1843        TypeId::of::<E>()
1844    }
1845
1846    fn create_node(&self) -> Box<dyn ModifierNode> {
1847        Box::new(self.element.create())
1848    }
1849
1850    fn can_update_node(&self, node: &dyn ModifierNode) -> bool {
1851        node.as_any().is::<E::Node>()
1852    }
1853
1854    fn update_node(&self, node: &mut dyn ModifierNode) {
1855        if let Some(typed) = node.as_any_mut().downcast_mut::<E::Node>() {
1856            self.element.update(typed);
1857        }
1858    }
1859
1860    fn key(&self) -> Option<u64> {
1861        self.element.key()
1862    }
1863
1864    fn capabilities(&self) -> NodeCapabilities {
1865        self.element.capabilities()
1866    }
1867
1868    fn hash_code(&self) -> u64 {
1869        self.cached_hash
1870    }
1871
1872    fn equals_element(&self, other: &dyn AnyModifierElement) -> bool {
1873        other
1874            .as_any()
1875            .downcast_ref::<Self>()
1876            .map(|typed| typed.element == self.element)
1877            .unwrap_or(false)
1878    }
1879
1880    fn inspector_name(&self) -> &'static str {
1881        self.element.inspector_name()
1882    }
1883
1884    fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String)) {
1885        self.element.inspector_properties(visitor);
1886    }
1887
1888    fn requires_update(&self) -> bool {
1889        self.element.always_update()
1890    }
1891
1892    fn auto_invalidates_on_update(&self) -> bool {
1893        self.element.auto_invalidate_on_update()
1894    }
1895
1896    fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1897        self.element.update_invalidation_kind()
1898    }
1899
1900    fn as_any(&self) -> &dyn Any {
1901        self
1902    }
1903}
1904
1905fn request_update_auto_invalidations(
1906    element: &dyn AnyModifierElement,
1907    context: &mut dyn ModifierNodeContext,
1908    capabilities: NodeCapabilities,
1909) {
1910    if let Some(kind) = element.update_invalidation_kind() {
1911        let capabilities = NodeCapabilities::for_invalidation(kind);
1912        context.push_active_capabilities(capabilities);
1913        context.invalidate(kind);
1914        context.pop_active_capabilities();
1915    } else if element.auto_invalidates_on_update() {
1916        request_auto_invalidations(context, capabilities);
1917    }
1918}
1919
1920/// Convenience helper for callers to construct a type-erased modifier
1921/// element without having to mention the internal wrapper type.
1922pub fn modifier_element<E: ModifierNodeElement>(element: E) -> DynModifierElement {
1923    Rc::new(TypedModifierElement::new(element))
1924}
1925
1926/// Boxed type-erased modifier element.
1927pub type DynModifierElement = Rc<dyn AnyModifierElement>;
1928
1929#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1930enum TraversalDirection {
1931    Forward,
1932    Backward,
1933}
1934
1935/// Iterator walking a modifier chain by indexing into `ordered_nodes`.
1936///
1937/// This avoids the per-step `RefCell::borrow()` + `NodeLink::clone()` cost
1938/// of following the linked-list through `NodeState::child`/`parent`.
1939pub struct ModifierChainIter<'a> {
1940    chain: &'a ModifierNodeChain,
1941    cursor: usize,
1942    remaining: usize,
1943    direction: TraversalDirection,
1944}
1945
1946impl<'a> ModifierChainIter<'a> {
1947    fn forward(chain: &'a ModifierNodeChain) -> Self {
1948        Self {
1949            chain,
1950            cursor: 0,
1951            remaining: chain.ordered_nodes.len(),
1952            direction: TraversalDirection::Forward,
1953        }
1954    }
1955
1956    fn backward(chain: &'a ModifierNodeChain) -> Self {
1957        let len = chain.ordered_nodes.len();
1958        Self {
1959            chain,
1960            cursor: len.wrapping_sub(1),
1961            remaining: len,
1962            direction: TraversalDirection::Backward,
1963        }
1964    }
1965}
1966
1967impl<'a> Iterator for ModifierChainIter<'a> {
1968    type Item = ModifierChainNodeRef<'a>;
1969
1970    #[inline]
1971    fn next(&mut self) -> Option<Self::Item> {
1972        if self.remaining == 0 {
1973            return None;
1974        }
1975        let (link, caps, agg) = self.chain.ordered_nodes[self.cursor];
1976        let node_ref = self.chain.make_node_ref_with_caps(link, caps, agg);
1977        self.remaining -= 1;
1978        match self.direction {
1979            TraversalDirection::Forward => self.cursor += 1,
1980            TraversalDirection::Backward => self.cursor = self.cursor.wrapping_sub(1),
1981        }
1982        Some(node_ref)
1983    }
1984
1985    #[inline]
1986    fn size_hint(&self) -> (usize, Option<usize>) {
1987        (self.remaining, Some(self.remaining))
1988    }
1989}
1990
1991impl<'a> ExactSizeIterator for ModifierChainIter<'a> {}
1992impl<'a> std::iter::FusedIterator for ModifierChainIter<'a> {}
1993
1994#[derive(Debug)]
1995struct ModifierNodeEntry {
1996    element_type: TypeId,
1997    node_type: TypeId,
1998    key: Option<u64>,
1999    hash_code: u64,
2000    element: DynModifierElement,
2001    node: Rc<RefCell<Box<dyn ModifierNode>>>,
2002    capabilities: NodeCapabilities,
2003}
2004
2005impl ModifierNodeEntry {
2006    fn new(
2007        element_type: TypeId,
2008        node_type: TypeId,
2009        key: Option<u64>,
2010        element: DynModifierElement,
2011        node: Box<dyn ModifierNode>,
2012        hash_code: u64,
2013        capabilities: NodeCapabilities,
2014    ) -> Self {
2015        let node_rc = Rc::new(RefCell::new(node));
2016        let entry = Self {
2017            element_type,
2018            node_type,
2019            key,
2020            hash_code,
2021            element,
2022            node: Rc::clone(&node_rc),
2023            capabilities,
2024        };
2025        entry
2026            .node
2027            .borrow()
2028            .node_state()
2029            .set_capabilities(entry.capabilities);
2030        entry
2031    }
2032}
2033
2034fn visit_node_tree_mut(
2035    node: &mut dyn ModifierNode,
2036    visitor: &mut dyn FnMut(&mut dyn ModifierNode),
2037) {
2038    visitor(node);
2039    node.for_each_delegate_mut(&mut |child| visit_node_tree_mut(child, visitor));
2040}
2041
2042fn nth_delegate(node: &dyn ModifierNode, target: usize) -> Option<&dyn ModifierNode> {
2043    let mut current = 0usize;
2044    let mut result: Option<&dyn ModifierNode> = None;
2045    node.for_each_delegate(&mut |child| {
2046        if result.is_none() && current == target {
2047            result = Some(child);
2048        }
2049        current += 1;
2050    });
2051    result
2052}
2053
2054fn nth_delegate_mut(node: &mut dyn ModifierNode, target: usize) -> Option<&mut dyn ModifierNode> {
2055    let mut current = 0usize;
2056    let mut result: Option<&mut dyn ModifierNode> = None;
2057    node.for_each_delegate_mut(&mut |child| {
2058        if result.is_none() && current == target {
2059            result = Some(child);
2060        }
2061        current += 1;
2062    });
2063    result
2064}
2065
2066fn with_node_context<F, R>(
2067    node: &mut dyn ModifierNode,
2068    context: &mut dyn ModifierNodeContext,
2069    f: F,
2070) -> R
2071where
2072    F: FnOnce(&mut dyn ModifierNode, &mut dyn ModifierNodeContext) -> R,
2073{
2074    context.push_active_capabilities(node.node_state().capabilities());
2075    let result = f(node, context);
2076    context.pop_active_capabilities();
2077    result
2078}
2079
2080fn request_auto_invalidations(
2081    context: &mut dyn ModifierNodeContext,
2082    capabilities: NodeCapabilities,
2083) {
2084    if capabilities.is_empty() {
2085        return;
2086    }
2087
2088    context.push_active_capabilities(capabilities);
2089
2090    if capabilities.contains(NodeCapabilities::LAYOUT) {
2091        context.invalidate(InvalidationKind::Layout);
2092    }
2093    if capabilities.contains(NodeCapabilities::DRAW) {
2094        context.invalidate(InvalidationKind::Draw);
2095    }
2096    if capabilities.contains(NodeCapabilities::POINTER_INPUT) {
2097        context.invalidate(InvalidationKind::PointerInput);
2098    }
2099    if capabilities.contains(NodeCapabilities::SEMANTICS) {
2100        context.invalidate(InvalidationKind::Semantics);
2101    }
2102    if capabilities.contains(NodeCapabilities::FOCUS) {
2103        context.invalidate(InvalidationKind::Focus);
2104    }
2105
2106    context.pop_active_capabilities();
2107}
2108
2109/// Attaches a node tree by calling on_attach for all unattached nodes.
2110///
2111/// # Safety
2112/// Callers must ensure no immutable RefCell borrows are held on the node
2113/// when calling this function. The on_attach callback may trigger mutations
2114/// (invalidations, state updates, etc.) that require mutable access, which
2115/// would panic if an immutable borrow is held across the call.
2116fn attach_node_tree(node: &mut dyn ModifierNode, context: &mut dyn ModifierNodeContext) {
2117    visit_node_tree_mut(node, &mut |n| {
2118        if !n.node_state().is_attached() {
2119            n.node_state().set_attached(true);
2120            with_node_context(n, context, |node, ctx| node.on_attach(ctx));
2121        }
2122    });
2123}
2124
2125fn reset_node_tree(node: &mut dyn ModifierNode) {
2126    visit_node_tree_mut(node, &mut |n| n.on_reset());
2127}
2128
2129fn detach_node_tree(node: &mut dyn ModifierNode) {
2130    visit_node_tree_mut(node, &mut |n| {
2131        if n.node_state().is_attached() {
2132            n.on_detach();
2133            n.node_state().set_attached(false);
2134        }
2135        n.node_state().set_parent_link(None);
2136        n.node_state().set_child_link(None);
2137        n.node_state()
2138            .set_aggregate_child_capabilities(NodeCapabilities::empty());
2139    });
2140}
2141
2142/// Chain of modifier nodes attached to a layout node.
2143///
2144/// The chain tracks ownership of modifier nodes and reuses them across
2145/// updates when the incoming element list still contains a node of the
2146/// same type. Removed nodes detach automatically so callers do not need
2147/// to manually manage their lifetimes.
2148pub struct ModifierNodeChain {
2149    entries: Vec<ModifierNodeEntry>,
2150    aggregated_capabilities: NodeCapabilities,
2151    head_aggregate_child_capabilities: NodeCapabilities,
2152    head_sentinel: Box<SentinelNode>,
2153    tail_sentinel: Box<SentinelNode>,
2154    ordered_nodes: Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
2155    scratch_old_used: Vec<bool>,
2156    scratch_match_order: Vec<Option<usize>>,
2157    scratch_final_slots: Vec<Option<ModifierNodeEntry>>,
2158    scratch_elements: Vec<DynModifierElement>,
2159}
2160
2161struct SentinelNode {
2162    state: NodeState,
2163}
2164
2165impl SentinelNode {
2166    fn new() -> Self {
2167        Self {
2168            state: NodeState::sentinel(),
2169        }
2170    }
2171}
2172
2173impl DelegatableNode for SentinelNode {
2174    fn node_state(&self) -> &NodeState {
2175        &self.state
2176    }
2177}
2178
2179impl ModifierNode for SentinelNode {}
2180
2181#[derive(Clone)]
2182pub struct ModifierChainNodeRef<'a> {
2183    chain: &'a ModifierNodeChain,
2184    link: NodeLink,
2185    cached_capabilities: Option<NodeCapabilities>,
2186    cached_aggregate_child: Option<NodeCapabilities>,
2187}
2188
2189impl Default for ModifierNodeChain {
2190    fn default() -> Self {
2191        Self::new()
2192    }
2193}
2194
2195/// Index structure for O(1) modifier entry lookups during update.
2196///
2197/// This avoids O(n²) complexity by pre-building hash maps that allow constant-time
2198/// lookups for matching entries by key, hash, or type.
2199struct EntryIndex {
2200    keyed: HashMap<(TypeId, TypeId, u64), Vec<usize>>,
2201    hashed: HashMap<(TypeId, TypeId, u64), Vec<usize>>,
2202    typed: HashMap<(TypeId, TypeId), Vec<usize>>,
2203}
2204
2205struct EntryMatchQuery<'a> {
2206    element_type: TypeId,
2207    node_type: TypeId,
2208    key: Option<u64>,
2209    hash_code: u64,
2210    element: &'a DynModifierElement,
2211}
2212
2213impl EntryIndex {
2214    fn build(entries: &[ModifierNodeEntry]) -> Self {
2215        let mut keyed = HashMap::default();
2216        let mut hashed = HashMap::default();
2217        let mut typed = HashMap::default();
2218
2219        for (i, entry) in entries.iter().enumerate() {
2220            if let Some(key_value) = entry.key {
2221                keyed
2222                    .entry((entry.element_type, entry.node_type, key_value))
2223                    .or_insert_with(Vec::new)
2224                    .push(i);
2225            } else {
2226                hashed
2227                    .entry((entry.element_type, entry.node_type, entry.hash_code))
2228                    .or_insert_with(Vec::new)
2229                    .push(i);
2230                typed
2231                    .entry((entry.element_type, entry.node_type))
2232                    .or_insert_with(Vec::new)
2233                    .push(i);
2234            }
2235        }
2236
2237        Self {
2238            keyed,
2239            hashed,
2240            typed,
2241        }
2242    }
2243
2244    fn find_match(
2245        &self,
2246        entries: &[ModifierNodeEntry],
2247        used: &[bool],
2248        query: EntryMatchQuery<'_>,
2249    ) -> Option<usize> {
2250        if let Some(key_value) = query.key {
2251            if let Some(candidates) =
2252                self.keyed
2253                    .get(&(query.element_type, query.node_type, key_value))
2254            {
2255                for &i in candidates {
2256                    if !used[i] {
2257                        return Some(i);
2258                    }
2259                }
2260            }
2261        } else {
2262            if let Some(candidates) =
2263                self.hashed
2264                    .get(&(query.element_type, query.node_type, query.hash_code))
2265            {
2266                for &i in candidates {
2267                    if !used[i]
2268                        && entries[i]
2269                            .element
2270                            .as_ref()
2271                            .equals_element(query.element.as_ref())
2272                    {
2273                        return Some(i);
2274                    }
2275                }
2276            }
2277
2278            if let Some(candidates) = self.typed.get(&(query.element_type, query.node_type)) {
2279                for &i in candidates {
2280                    if !used[i] {
2281                        return Some(i);
2282                    }
2283                }
2284            }
2285        }
2286
2287        None
2288    }
2289}
2290
2291impl ModifierNodeChain {
2292    pub fn new() -> Self {
2293        let mut chain = Self {
2294            entries: Vec::new(),
2295            aggregated_capabilities: NodeCapabilities::empty(),
2296            head_aggregate_child_capabilities: NodeCapabilities::empty(),
2297            head_sentinel: Box::new(SentinelNode::new()),
2298            tail_sentinel: Box::new(SentinelNode::new()),
2299            ordered_nodes: Vec::new(),
2300            scratch_old_used: Vec::new(),
2301            scratch_match_order: Vec::new(),
2302            scratch_final_slots: Vec::new(),
2303            scratch_elements: Vec::new(),
2304        };
2305        chain.sync_chain_links();
2306        chain
2307    }
2308
2309    /// Detaches all nodes in the chain.
2310    pub fn detach_nodes(&mut self) {
2311        for entry in &self.entries {
2312            detach_node_tree(&mut **entry.node.borrow_mut());
2313        }
2314    }
2315
2316    /// Attaches all nodes in the chain.
2317    pub fn attach_nodes(&mut self, context: &mut dyn ModifierNodeContext) {
2318        for entry in &self.entries {
2319            attach_node_tree(&mut **entry.node.borrow_mut(), context);
2320        }
2321    }
2322
2323    /// Rebuilds the internal chain links (parent/child relationships).
2324    /// This should be called if nodes have been detached but are intended to be reused.
2325    pub fn repair_chain(&mut self) {
2326        self.sync_chain_links();
2327    }
2328
2329    /// Reconcile the chain against the provided elements, attaching newly
2330    /// created nodes and detaching nodes that are no longer required.
2331    ///
2332    /// This method delegates to `update_from_ref_iter` which handles the
2333    /// actual reconciliation logic.
2334    pub fn update_from_slice(
2335        &mut self,
2336        elements: &[DynModifierElement],
2337        context: &mut dyn ModifierNodeContext,
2338    ) {
2339        self.update_from_ref_iter(elements.iter(), context);
2340    }
2341
2342    /// Reconcile the chain against the provided iterator of element references.
2343    ///
2344    /// This is the preferred method as it avoids requiring a collected slice,
2345    /// enabling zero-allocation traversal of modifier trees.
2346    pub fn update_from_ref_iter<'a, I>(
2347        &mut self,
2348        elements: I,
2349        context: &mut dyn ModifierNodeContext,
2350    ) where
2351        I: Iterator<Item = &'a DynModifierElement>,
2352    {
2353        let old_len = self.entries.len();
2354        let mut fast_path_failed_at: Option<usize> = None;
2355        let mut elements_count = 0;
2356
2357        self.scratch_elements.clear();
2358
2359        for (idx, element) in elements.enumerate() {
2360            elements_count = idx + 1;
2361
2362            if fast_path_failed_at.is_none() && idx < old_len {
2363                let entry = &mut self.entries[idx];
2364                let same_type = entry.element_type == element.element_type();
2365                let same_node_type = entry.node_type == element.node_type();
2366                let same_key = entry.key == element.key();
2367                let same_hash = entry.hash_code == element.hash_code();
2368
2369                let positional_update = element.requires_update();
2370                if same_type && same_node_type && same_key && (same_hash || positional_update) {
2371                    let can_update_node = {
2372                        let node_borrow = entry.node.borrow();
2373                        element.can_update_node(&**node_borrow)
2374                    };
2375                    if !can_update_node {
2376                        fast_path_failed_at = Some(idx);
2377                        self.scratch_elements.push(element.clone());
2378                        continue;
2379                    }
2380
2381                    let same_element = entry.element.as_ref().equals_element(element.as_ref());
2382                    let capabilities = element.capabilities();
2383
2384                    {
2385                        let node_borrow = entry.node.borrow();
2386                        if !node_borrow.node_state().is_attached() {
2387                            drop(node_borrow);
2388                            attach_node_tree(&mut **entry.node.borrow_mut(), context);
2389                        }
2390                    }
2391
2392                    let needs_update = !same_element || element.requires_update();
2393                    if needs_update {
2394                        element.update_node(&mut **entry.node.borrow_mut());
2395                        entry.element = element.clone();
2396                        entry.hash_code = element.hash_code();
2397                        request_update_auto_invalidations(element.as_ref(), context, capabilities);
2398                    }
2399
2400                    entry.capabilities = capabilities;
2401                    entry
2402                        .node
2403                        .borrow()
2404                        .node_state()
2405                        .set_capabilities(capabilities);
2406                    continue;
2407                }
2408                fast_path_failed_at = Some(idx);
2409            }
2410
2411            self.scratch_elements.push(element.clone());
2412        }
2413
2414        if fast_path_failed_at.is_none() && self.scratch_elements.is_empty() {
2415            if elements_count < self.entries.len() {
2416                for entry in self.entries.drain(elements_count..) {
2417                    request_auto_invalidations(context, entry.capabilities);
2418                    detach_node_tree(&mut **entry.node.borrow_mut());
2419                }
2420            }
2421            self.sync_chain_links();
2422            return;
2423        }
2424
2425        let fail_idx = fast_path_failed_at.unwrap_or(old_len);
2426
2427        let mut old_entries: Vec<ModifierNodeEntry> = self.entries.drain(fail_idx..).collect();
2428        let processed_entries_len = self.entries.len();
2429        let old_len = old_entries.len();
2430
2431        self.scratch_old_used.clear();
2432        self.scratch_old_used.resize(old_len, false);
2433
2434        self.scratch_match_order.clear();
2435        self.scratch_match_order.resize(old_len, None);
2436
2437        let index = EntryIndex::build(&old_entries);
2438
2439        let new_elements_count = self.scratch_elements.len();
2440        self.scratch_final_slots.clear();
2441        self.scratch_final_slots.reserve(new_elements_count);
2442
2443        for (new_pos, element) in self.scratch_elements.drain(..).enumerate() {
2444            self.scratch_final_slots.push(None);
2445            let element_type = element.element_type();
2446            let node_type = element.node_type();
2447            let key = element.key();
2448            let hash_code = element.hash_code();
2449            let capabilities = element.capabilities();
2450
2451            let matched_idx = index.find_match(
2452                &old_entries,
2453                &self.scratch_old_used,
2454                EntryMatchQuery {
2455                    element_type,
2456                    node_type,
2457                    key,
2458                    hash_code,
2459                    element: &element,
2460                },
2461            );
2462
2463            if let Some(idx) = matched_idx {
2464                let entry = &mut old_entries[idx];
2465                let can_update_node = {
2466                    let node_borrow = entry.node.borrow();
2467                    element.can_update_node(&**node_borrow)
2468                };
2469                if !can_update_node {
2470                    let replacement = ModifierNodeEntry::new(
2471                        element_type,
2472                        node_type,
2473                        key,
2474                        element.clone(),
2475                        element.create_node(),
2476                        hash_code,
2477                        capabilities,
2478                    );
2479                    attach_node_tree(&mut **replacement.node.borrow_mut(), context);
2480                    element.update_node(&mut **replacement.node.borrow_mut());
2481                    request_auto_invalidations(context, capabilities);
2482                    self.scratch_final_slots[new_pos] = Some(replacement);
2483                    continue;
2484                }
2485
2486                self.scratch_old_used[idx] = true;
2487                self.scratch_match_order[idx] = Some(new_pos);
2488                let moved = idx != new_pos;
2489
2490                let same_element = entry.element.as_ref().equals_element(element.as_ref());
2491
2492                {
2493                    let node_borrow = entry.node.borrow();
2494                    if !node_borrow.node_state().is_attached() {
2495                        drop(node_borrow);
2496                        attach_node_tree(&mut **entry.node.borrow_mut(), context);
2497                    }
2498                }
2499
2500                let needs_update = !same_element || element.requires_update();
2501                if needs_update {
2502                    element.update_node(&mut **entry.node.borrow_mut());
2503                    entry.element = element;
2504                    entry.hash_code = hash_code;
2505                    request_update_auto_invalidations(
2506                        entry.element.as_ref(),
2507                        context,
2508                        capabilities,
2509                    );
2510                }
2511                if moved {
2512                    request_auto_invalidations(context, capabilities);
2513                }
2514
2515                entry.key = key;
2516                entry.element_type = element_type;
2517                entry.node_type = node_type;
2518                entry.capabilities = capabilities;
2519                entry
2520                    .node
2521                    .borrow()
2522                    .node_state()
2523                    .set_capabilities(capabilities);
2524            } else {
2525                let entry = ModifierNodeEntry::new(
2526                    element_type,
2527                    node_type,
2528                    key,
2529                    element.clone(),
2530                    element.create_node(),
2531                    hash_code,
2532                    capabilities,
2533                );
2534                attach_node_tree(&mut **entry.node.borrow_mut(), context);
2535                element.update_node(&mut **entry.node.borrow_mut());
2536                request_auto_invalidations(context, capabilities);
2537                self.scratch_final_slots[new_pos] = Some(entry);
2538            }
2539        }
2540
2541        for (i, entry) in old_entries.into_iter().enumerate() {
2542            if self.scratch_old_used[i] {
2543                if let Some(pos) = self.scratch_match_order[i] {
2544                    self.scratch_final_slots[pos] = Some(entry);
2545                } else {
2546                    request_auto_invalidations(context, entry.capabilities);
2547                    detach_node_tree(&mut **entry.node.borrow_mut());
2548                }
2549            } else {
2550                request_auto_invalidations(context, entry.capabilities);
2551                detach_node_tree(&mut **entry.node.borrow_mut());
2552            }
2553        }
2554
2555        self.entries.reserve(self.scratch_final_slots.len());
2556        for slot in self.scratch_final_slots.drain(..) {
2557            if let Some(entry) = slot {
2558                self.entries.push(entry);
2559            } else {
2560                log::error!("modifier reconciliation produced an empty final slot");
2561            }
2562        }
2563
2564        debug_assert_eq!(
2565            self.entries.len(),
2566            processed_entries_len + new_elements_count
2567        );
2568        self.sync_chain_links();
2569    }
2570
2571    /// Convenience wrapper that accepts any iterator of type-erased
2572    /// modifier elements. Elements are collected into a temporary vector
2573    /// before reconciliation.
2574    pub fn update<I>(&mut self, elements: I, context: &mut dyn ModifierNodeContext)
2575    where
2576        I: IntoIterator<Item = DynModifierElement>,
2577    {
2578        let collected: Vec<DynModifierElement> = elements.into_iter().collect();
2579        self.update_from_slice(&collected, context);
2580    }
2581
2582    /// Resets all nodes in the chain. This mirrors the behaviour of
2583    /// Jetpack Compose's `onReset` callback.
2584    pub fn reset(&mut self) {
2585        for entry in &mut self.entries {
2586            reset_node_tree(&mut **entry.node.borrow_mut());
2587        }
2588    }
2589
2590    /// Detaches every node in the chain and clears internal storage.
2591    pub fn detach_all(&mut self) {
2592        for entry in std::mem::take(&mut self.entries) {
2593            detach_node_tree(&mut **entry.node.borrow_mut());
2594            {
2595                let node_borrow = entry.node.borrow();
2596                let state = node_borrow.node_state();
2597                state.set_capabilities(NodeCapabilities::empty());
2598            }
2599        }
2600        self.aggregated_capabilities = NodeCapabilities::empty();
2601        self.head_aggregate_child_capabilities = NodeCapabilities::empty();
2602        self.ordered_nodes.clear();
2603        self.sync_chain_links();
2604    }
2605
2606    pub fn len(&self) -> usize {
2607        self.entries.len()
2608    }
2609
2610    pub fn is_empty(&self) -> bool {
2611        self.entries.is_empty()
2612    }
2613
2614    /// Returns the aggregated capability mask for the entire chain.
2615    pub fn capabilities(&self) -> NodeCapabilities {
2616        self.aggregated_capabilities
2617    }
2618
2619    /// Returns true if the chain contains at least one node with the requested capability.
2620    pub fn has_capability(&self, capability: NodeCapabilities) -> bool {
2621        self.aggregated_capabilities.contains(capability)
2622    }
2623
2624    /// Returns the sentinel head reference for traversal.
2625    pub fn head(&self) -> ModifierChainNodeRef<'_> {
2626        self.make_node_ref(NodeLink::Head)
2627    }
2628
2629    /// Returns the sentinel tail reference for traversal.
2630    pub fn tail(&self) -> ModifierChainNodeRef<'_> {
2631        self.make_node_ref(NodeLink::Tail)
2632    }
2633
2634    /// Iterates over the chain from head to tail, skipping sentinels.
2635    pub fn head_to_tail(&self) -> ModifierChainIter<'_> {
2636        ModifierChainIter::forward(self)
2637    }
2638
2639    /// Iterates over the chain from tail to head, skipping sentinels.
2640    pub fn tail_to_head(&self) -> ModifierChainIter<'_> {
2641        ModifierChainIter::backward(self)
2642    }
2643
2644    /// Calls `f` for every node in insertion order.
2645    pub fn for_each_forward<F>(&self, mut f: F)
2646    where
2647        F: FnMut(ModifierChainNodeRef<'_>),
2648    {
2649        for node in self.head_to_tail() {
2650            f(node);
2651        }
2652    }
2653
2654    /// Calls `f` for every node containing any capability from `mask`.
2655    pub fn for_each_forward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
2656    where
2657        F: FnMut(ModifierChainNodeRef<'_>),
2658    {
2659        if mask.is_empty() {
2660            self.for_each_forward(f);
2661            return;
2662        }
2663
2664        if !self.head().aggregate_child_capabilities().intersects(mask) {
2665            return;
2666        }
2667
2668        for node in self.head_to_tail() {
2669            if node.kind_set().intersects(mask) {
2670                f(node);
2671            }
2672        }
2673    }
2674
2675    /// Calls `f` for every node containing any capability from `mask`, providing the node ref.
2676    pub fn for_each_node_with_capability<F>(&self, mask: NodeCapabilities, mut f: F)
2677    where
2678        F: FnMut(ModifierChainNodeRef<'_>, &dyn ModifierNode),
2679    {
2680        self.for_each_forward_matching(mask, |node_ref| {
2681            node_ref.with_node(|node| f(node_ref.clone(), node));
2682        });
2683    }
2684
2685    /// Calls `f` for every node in reverse insertion order.
2686    pub fn for_each_backward<F>(&self, mut f: F)
2687    where
2688        F: FnMut(ModifierChainNodeRef<'_>),
2689    {
2690        for node in self.tail_to_head() {
2691            f(node);
2692        }
2693    }
2694
2695    /// Returns the node reference that owns `node`.
2696    pub fn find_node_ref(&self, node: &dyn ModifierNode) -> Option<ModifierChainNodeRef<'_>> {
2697        fn node_data_ptr(node: &dyn ModifierNode) -> *const () {
2698            node as *const dyn ModifierNode as *const ()
2699        }
2700
2701        let target = node_data_ptr(node);
2702        for (index, entry) in self.entries.iter().enumerate() {
2703            if node_data_ptr(&**entry.node.borrow()) == target {
2704                return Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))));
2705            }
2706        }
2707
2708        self.ordered_nodes.iter().find_map(|(link, _caps, _agg)| {
2709            if matches!(link, NodeLink::Entry(path) if path.delegates().is_empty()) {
2710                return None;
2711            }
2712            let matches_target = match link {
2713                NodeLink::Head => node_data_ptr(self.head_sentinel.as_ref()) == target,
2714                NodeLink::Tail => node_data_ptr(self.tail_sentinel.as_ref()) == target,
2715                NodeLink::Entry(path) => {
2716                    let node_borrow = self.entries[path.entry()].node.borrow();
2717                    node_data_ptr(&**node_borrow) == target
2718                }
2719            };
2720            if matches_target {
2721                Some(self.make_node_ref(*link))
2722            } else {
2723                None
2724            }
2725        })
2726    }
2727
2728    /// Downcasts the node at `index` to the requested type.
2729    /// Returns a `Ref` guard that dereferences to the node type.
2730    pub fn node<N: ModifierNode + 'static>(&self, index: usize) -> Option<std::cell::Ref<'_, N>> {
2731        self.entries.get(index).and_then(|entry| {
2732            std::cell::Ref::filter_map(entry.node.borrow(), |boxed_node| {
2733                boxed_node.as_any().downcast_ref::<N>()
2734            })
2735            .ok()
2736        })
2737    }
2738
2739    /// Downcasts the node at `index` to the requested mutable type.
2740    /// Returns a `RefMut` guard that dereferences to the node type.
2741    pub fn node_mut<N: ModifierNode + 'static>(
2742        &self,
2743        index: usize,
2744    ) -> Option<std::cell::RefMut<'_, N>> {
2745        self.entries.get(index).and_then(|entry| {
2746            std::cell::RefMut::filter_map(entry.node.borrow_mut(), |boxed_node| {
2747                boxed_node.as_any_mut().downcast_mut::<N>()
2748            })
2749            .ok()
2750        })
2751    }
2752
2753    /// Returns an Rc clone of the node at the given index for shared ownership.
2754    /// This is used by coordinators to hold direct references to nodes.
2755    pub fn get_node_rc(&self, index: usize) -> Option<Rc<RefCell<Box<dyn ModifierNode>>>> {
2756        self.entries.get(index).map(|entry| Rc::clone(&entry.node))
2757    }
2758
2759    /// Returns true if the chain contains any nodes matching the given invalidation kind.
2760    pub fn has_nodes_for_invalidation(&self, kind: InvalidationKind) -> bool {
2761        self.aggregated_capabilities
2762            .contains(NodeCapabilities::for_invalidation(kind))
2763    }
2764
2765    /// Visits every node mutably in insertion order together with its capability mask.
2766    pub fn visit_nodes_mut<F>(&mut self, mut f: F)
2767    where
2768        F: FnMut(&mut dyn ModifierNode, NodeCapabilities),
2769    {
2770        for index in 0..self.ordered_nodes.len() {
2771            let (link, cached_caps, _agg) = self.ordered_nodes[index];
2772            match link {
2773                NodeLink::Head => {
2774                    f(self.head_sentinel.as_mut(), cached_caps);
2775                }
2776                NodeLink::Tail => {
2777                    f(self.tail_sentinel.as_mut(), cached_caps);
2778                }
2779                NodeLink::Entry(path) => {
2780                    let mut node_borrow = self.entries[path.entry()].node.borrow_mut();
2781                    if path.delegates().is_empty() {
2782                        f(&mut **node_borrow, cached_caps);
2783                    } else {
2784                        let mut current: &mut dyn ModifierNode = &mut **node_borrow;
2785                        for &delegate_index in path.delegates() {
2786                            if let Some(delegate) =
2787                                nth_delegate_mut(current, delegate_index as usize)
2788                            {
2789                                current = delegate;
2790                            } else {
2791                                return;
2792                            }
2793                        }
2794                        f(current, cached_caps);
2795                    }
2796                }
2797            }
2798        }
2799    }
2800
2801    fn make_node_ref(&self, link: NodeLink) -> ModifierChainNodeRef<'_> {
2802        ModifierChainNodeRef {
2803            chain: self,
2804            link,
2805            cached_capabilities: None,
2806            cached_aggregate_child: None,
2807        }
2808    }
2809
2810    fn make_node_ref_with_caps(
2811        &self,
2812        link: NodeLink,
2813        caps: NodeCapabilities,
2814        aggregate_child: NodeCapabilities,
2815    ) -> ModifierChainNodeRef<'_> {
2816        ModifierChainNodeRef {
2817            chain: self,
2818            link,
2819            cached_capabilities: Some(caps),
2820            cached_aggregate_child: Some(aggregate_child),
2821        }
2822    }
2823
2824    fn sync_chain_links(&mut self) {
2825        self.rebuild_ordered_nodes();
2826
2827        self.head_sentinel.node_state().set_parent_link(None);
2828        self.tail_sentinel.node_state().set_child_link(None);
2829
2830        if self.ordered_nodes.is_empty() {
2831            self.head_sentinel
2832                .node_state()
2833                .set_child_link(Some(NodeLink::Tail));
2834            self.tail_sentinel
2835                .node_state()
2836                .set_parent_link(Some(NodeLink::Head));
2837            self.aggregated_capabilities = NodeCapabilities::empty();
2838            self.head_aggregate_child_capabilities = NodeCapabilities::empty();
2839            self.head_sentinel
2840                .node_state()
2841                .set_aggregate_child_capabilities(NodeCapabilities::empty());
2842            self.tail_sentinel
2843                .node_state()
2844                .set_aggregate_child_capabilities(NodeCapabilities::empty());
2845            return;
2846        }
2847
2848        let mut previous = NodeLink::Head;
2849        for (link, _caps, _agg) in self.ordered_nodes.iter().copied() {
2850            match &previous {
2851                NodeLink::Head => self.head_sentinel.node_state().set_child_link(Some(link)),
2852                NodeLink::Tail => self.tail_sentinel.node_state().set_child_link(Some(link)),
2853                NodeLink::Entry(path) => {
2854                    let node_borrow = self.entries[path.entry()].node.borrow();
2855                    if path.delegates().is_empty() {
2856                        node_borrow.node_state().set_child_link(Some(link));
2857                    } else {
2858                        let mut current: &dyn ModifierNode = &**node_borrow;
2859                        for &delegate_index in path.delegates() {
2860                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2861                                current = delegate;
2862                            }
2863                        }
2864                        current.node_state().set_child_link(Some(link));
2865                    }
2866                }
2867            }
2868            match &link {
2869                NodeLink::Head => self
2870                    .head_sentinel
2871                    .node_state()
2872                    .set_parent_link(Some(previous)),
2873                NodeLink::Tail => self
2874                    .tail_sentinel
2875                    .node_state()
2876                    .set_parent_link(Some(previous)),
2877                NodeLink::Entry(path) => {
2878                    let node_borrow = self.entries[path.entry()].node.borrow();
2879                    if path.delegates().is_empty() {
2880                        node_borrow.node_state().set_parent_link(Some(previous));
2881                    } else {
2882                        let mut current: &dyn ModifierNode = &**node_borrow;
2883                        for &delegate_index in path.delegates() {
2884                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2885                                current = delegate;
2886                            }
2887                        }
2888                        current.node_state().set_parent_link(Some(previous));
2889                    }
2890                }
2891            }
2892            previous = link;
2893        }
2894
2895        match &previous {
2896            NodeLink::Head => self
2897                .head_sentinel
2898                .node_state()
2899                .set_child_link(Some(NodeLink::Tail)),
2900            NodeLink::Tail => self
2901                .tail_sentinel
2902                .node_state()
2903                .set_child_link(Some(NodeLink::Tail)),
2904            NodeLink::Entry(path) => {
2905                let node_borrow = self.entries[path.entry()].node.borrow();
2906                if path.delegates().is_empty() {
2907                    node_borrow
2908                        .node_state()
2909                        .set_child_link(Some(NodeLink::Tail));
2910                } else {
2911                    let mut current: &dyn ModifierNode = &**node_borrow;
2912                    for &delegate_index in path.delegates() {
2913                        if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2914                            current = delegate;
2915                        }
2916                    }
2917                    current.node_state().set_child_link(Some(NodeLink::Tail));
2918                }
2919            }
2920        }
2921        self.tail_sentinel
2922            .node_state()
2923            .set_parent_link(Some(previous));
2924        self.tail_sentinel.node_state().set_child_link(None);
2925
2926        let mut aggregate = NodeCapabilities::empty();
2927        for (link, cached_caps, cached_aggregate) in self.ordered_nodes.iter_mut().rev() {
2928            aggregate |= *cached_caps;
2929            *cached_aggregate = aggregate;
2930            match link {
2931                NodeLink::Head => {
2932                    self.head_sentinel
2933                        .node_state()
2934                        .set_aggregate_child_capabilities(aggregate);
2935                }
2936                NodeLink::Tail => {
2937                    self.tail_sentinel
2938                        .node_state()
2939                        .set_aggregate_child_capabilities(aggregate);
2940                }
2941                NodeLink::Entry(path) => {
2942                    let node_borrow = self.entries[path.entry()].node.borrow();
2943                    let state = if path.delegates().is_empty() {
2944                        node_borrow.node_state()
2945                    } else {
2946                        let mut current: &dyn ModifierNode = &**node_borrow;
2947                        for &delegate_index in path.delegates() {
2948                            if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
2949                                current = delegate;
2950                            }
2951                        }
2952                        current.node_state()
2953                    };
2954                    state.set_aggregate_child_capabilities(aggregate);
2955                }
2956            }
2957        }
2958
2959        self.aggregated_capabilities = aggregate;
2960        self.head_aggregate_child_capabilities = aggregate;
2961        self.head_sentinel
2962            .node_state()
2963            .set_aggregate_child_capabilities(aggregate);
2964        self.tail_sentinel
2965            .node_state()
2966            .set_aggregate_child_capabilities(NodeCapabilities::empty());
2967    }
2968
2969    fn rebuild_ordered_nodes(&mut self) {
2970        self.ordered_nodes.clear();
2971        let mut path_buf = [0usize; MAX_DELEGATE_DEPTH];
2972        for (index, entry) in self.entries.iter().enumerate() {
2973            let node_borrow = entry.node.borrow();
2974            Self::enumerate_link_order(
2975                &**node_borrow,
2976                index,
2977                &mut path_buf,
2978                0,
2979                &mut self.ordered_nodes,
2980            );
2981        }
2982    }
2983
2984    fn enumerate_link_order(
2985        node: &dyn ModifierNode,
2986        entry: usize,
2987        path_buf: &mut [usize; MAX_DELEGATE_DEPTH],
2988        path_len: usize,
2989        out: &mut Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
2990    ) {
2991        let caps = node.node_state().capabilities();
2992        out.push((
2993            NodeLink::Entry(NodePath::from_slice(entry, &path_buf[..path_len])),
2994            caps,
2995            NodeCapabilities::empty(),
2996        ));
2997        let mut delegate_index = 0usize;
2998        node.for_each_delegate(&mut |child| {
2999            if path_len < MAX_DELEGATE_DEPTH {
3000                path_buf[path_len] = delegate_index;
3001                Self::enumerate_link_order(child, entry, path_buf, path_len + 1, out);
3002            }
3003            delegate_index += 1;
3004        });
3005    }
3006}
3007
3008impl<'a> ModifierChainNodeRef<'a> {
3009    fn with_state<R>(&self, f: impl FnOnce(&NodeState) -> R) -> R {
3010        match &self.link {
3011            NodeLink::Head => f(self.chain.head_sentinel.node_state()),
3012            NodeLink::Tail => f(self.chain.tail_sentinel.node_state()),
3013            NodeLink::Entry(path) => {
3014                let node_borrow = self.chain.entries[path.entry()].node.borrow();
3015                if path.delegates().is_empty() {
3016                    f(node_borrow.node_state())
3017                } else {
3018                    let mut current: &dyn ModifierNode = &**node_borrow;
3019                    for &delegate_index in path.delegates() {
3020                        if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
3021                            current = delegate;
3022                        } else {
3023                            return f(node_borrow.node_state());
3024                        }
3025                    }
3026                    f(current.node_state())
3027                }
3028            }
3029        }
3030    }
3031
3032    /// Provides access to the node via a closure, properly handling RefCell borrows.
3033    /// Returns None for sentinel nodes.
3034    pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
3035        match &self.link {
3036            NodeLink::Head => None,
3037            NodeLink::Tail => None,
3038            NodeLink::Entry(path) => {
3039                let node_borrow = self.chain.entries[path.entry()].node.borrow();
3040                if path.delegates().is_empty() {
3041                    Some(f(&**node_borrow))
3042                } else {
3043                    let mut current: &dyn ModifierNode = &**node_borrow;
3044                    for &delegate_index in path.delegates() {
3045                        current = nth_delegate(current, delegate_index as usize)?;
3046                    }
3047                    Some(f(current))
3048                }
3049            }
3050        }
3051    }
3052
3053    /// Returns the parent reference, including sentinel head when applicable.
3054    #[inline]
3055    pub fn parent(&self) -> Option<Self> {
3056        self.with_state(|state| state.parent_link())
3057            .map(|link| self.chain.make_node_ref(link))
3058    }
3059
3060    /// Returns the child reference, including sentinel tail for the last entry.
3061    #[inline]
3062    pub fn child(&self) -> Option<Self> {
3063        self.with_state(|state| state.child_link())
3064            .map(|link| self.chain.make_node_ref(link))
3065    }
3066
3067    /// Returns the capability mask for this specific node.
3068    #[inline]
3069    pub fn kind_set(&self) -> NodeCapabilities {
3070        if let Some(caps) = self.cached_capabilities {
3071            return caps;
3072        }
3073        match &self.link {
3074            NodeLink::Head | NodeLink::Tail => NodeCapabilities::empty(),
3075            NodeLink::Entry(_) => self.with_state(|state| state.capabilities()),
3076        }
3077    }
3078
3079    /// Returns the entry index backing this node when it is part of the chain.
3080    pub fn entry_index(&self) -> Option<usize> {
3081        match &self.link {
3082            NodeLink::Entry(path) => Some(path.entry()),
3083            _ => None,
3084        }
3085    }
3086
3087    /// Returns how many delegate hops separate this node from its root element.
3088    pub fn delegate_depth(&self) -> usize {
3089        match &self.link {
3090            NodeLink::Entry(path) => path.delegates().len(),
3091            _ => 0,
3092        }
3093    }
3094
3095    /// Returns the aggregated capability mask for the subtree rooted at this node.
3096    #[inline]
3097    pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
3098        if let Some(agg) = self.cached_aggregate_child {
3099            return agg;
3100        }
3101        if self.is_tail() {
3102            NodeCapabilities::empty()
3103        } else {
3104            self.with_state(|state| state.aggregate_child_capabilities())
3105        }
3106    }
3107
3108    /// Returns true if this reference targets the sentinel head.
3109    pub fn is_head(&self) -> bool {
3110        matches!(self.link, NodeLink::Head)
3111    }
3112
3113    /// Returns true if this reference targets the sentinel tail.
3114    pub fn is_tail(&self) -> bool {
3115        matches!(self.link, NodeLink::Tail)
3116    }
3117
3118    /// Returns true if this reference targets either sentinel.
3119    pub fn is_sentinel(&self) -> bool {
3120        matches!(self.link, NodeLink::Head | NodeLink::Tail)
3121    }
3122
3123    /// Returns true if this node has any capability bits present in `mask`.
3124    pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
3125        !mask.is_empty() && self.kind_set().intersects(mask)
3126    }
3127
3128    /// Visits descendant nodes, optionally including `self`, in insertion order.
3129    pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
3130    where
3131        F: FnMut(ModifierChainNodeRef<'a>),
3132    {
3133        let mut current = if include_self {
3134            Some(self)
3135        } else {
3136            self.child()
3137        };
3138        while let Some(node) = current {
3139            if node.is_tail() {
3140                break;
3141            }
3142            if !node.is_sentinel() {
3143                f(node.clone());
3144            }
3145            current = node.child();
3146        }
3147    }
3148
3149    /// Visits descendant nodes that match `mask`, short-circuiting when possible.
3150    pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
3151    where
3152        F: FnMut(ModifierChainNodeRef<'a>),
3153    {
3154        if mask.is_empty() {
3155            self.visit_descendants(include_self, f);
3156            return;
3157        }
3158
3159        if !self.aggregate_child_capabilities().intersects(mask) {
3160            return;
3161        }
3162
3163        self.visit_descendants(include_self, |node| {
3164            if node.kind_set().intersects(mask) {
3165                f(node);
3166            }
3167        });
3168    }
3169
3170    /// Visits ancestor nodes up to (but excluding) the sentinel head.
3171    pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
3172    where
3173        F: FnMut(ModifierChainNodeRef<'a>),
3174    {
3175        let mut current = if include_self {
3176            Some(self)
3177        } else {
3178            self.parent()
3179        };
3180        while let Some(node) = current {
3181            if node.is_head() {
3182                break;
3183            }
3184            f(node.clone());
3185            current = node.parent();
3186        }
3187    }
3188
3189    /// Visits ancestor nodes that match `mask`.
3190    pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
3191    where
3192        F: FnMut(ModifierChainNodeRef<'a>),
3193    {
3194        if mask.is_empty() {
3195            self.visit_ancestors(include_self, f);
3196            return;
3197        }
3198
3199        self.visit_ancestors(include_self, |node| {
3200            if node.kind_set().intersects(mask) {
3201                f(node);
3202            }
3203        });
3204    }
3205}
3206
3207#[cfg(test)]
3208#[path = "tests/modifier_tests.rs"]
3209mod tests;