Skip to main content

cranpose_ui/widgets/nodes/
layout_node.rs

1use std::{
2    any::TypeId,
3    cell::{Cell, RefCell},
4    collections::HashMap,
5    hash::{Hash, Hasher},
6    rc::Rc,
7};
8
9use cranpose_core::{Node, NodeId};
10use cranpose_foundation::{
11    InvalidationKind, ModifierInvalidation, NodeCapabilities, SemanticsConfiguration,
12};
13use cranpose_ui_layout::{Constraints, MeasurePolicy};
14
15#[cfg(test)]
16use crate::layout::LayoutRuntimeDebugStats;
17use crate::{
18    layout::{LayoutRuntimeState, MeasuredNode},
19    modifier::{
20        Modifier, ModifierChainHandle, ModifierLocalSource, ModifierLocalToken,
21        ModifierLocalsHandle, ModifierNodeSlices, Point, ResolvedModifierLocal, ResolvedModifiers,
22        Size,
23    },
24};
25
26#[derive(Clone, Copy)]
27enum LayoutInvalidationDispatchDiag {
28    Disabled,
29    All,
30    Node(NodeId),
31}
32
33fn layout_invalidation_dispatch_diag() -> LayoutInvalidationDispatchDiag {
34    static MODE: std::sync::OnceLock<LayoutInvalidationDispatchDiag> = std::sync::OnceLock::new();
35    *MODE.get_or_init(|| {
36        let Some(value) = std::env::var_os("CRANPOSE_LAYOUT_INVALIDATION_DISPATCH_DIAG") else {
37            return LayoutInvalidationDispatchDiag::Disabled;
38        };
39        if value == "all" {
40            return LayoutInvalidationDispatchDiag::All;
41        }
42        value.to_string_lossy().parse::<NodeId>().map_or(
43            LayoutInvalidationDispatchDiag::Disabled,
44            LayoutInvalidationDispatchDiag::Node,
45        )
46    })
47}
48
49fn log_layout_invalidation_dispatch(
50    id: NodeId,
51    invalidation: &ModifierInvalidation,
52    curr_caps: NodeCapabilities,
53    prev_caps: NodeCapabilities,
54    modifier: &Modifier,
55) {
56    let enabled = match layout_invalidation_dispatch_diag() {
57        LayoutInvalidationDispatchDiag::Disabled => false,
58        LayoutInvalidationDispatchDiag::All => true,
59        LayoutInvalidationDispatchDiag::Node(target) => target == id,
60    };
61    if enabled {
62        log::warn!(
63            "[layout-invalidation-dispatch] node={id} invalidation={invalidation:?} curr_caps={curr_caps:?} prev_caps={prev_caps:?} modifier={modifier}"
64        );
65    }
66}
67
68/// Retained layout state for a LayoutNode.
69/// This mirrors Jetpack Compose's approach where each node stores its own
70/// measured size and placed position, eliminating the need for per-frame
71/// LayoutTree reconstruction.
72#[derive(Clone, Debug)]
73pub struct LayoutState {
74    size: Size,
75    position: Point,
76    is_placed: bool,
77    node_id: Option<NodeId>,
78    /// The constraints used for the last measurement.
79    pub measurement_constraints: Constraints,
80    /// Offset of the content box relative to the node origin (e.g. due to padding).
81    pub content_offset: Point,
82}
83
84impl Default for LayoutState {
85    fn default() -> Self {
86        Self {
87            size: Size::default(),
88            position: Point::default(),
89            is_placed: false,
90            node_id: None,
91            measurement_constraints: Constraints {
92                min_width: 0.0,
93                max_width: f32::INFINITY,
94                min_height: 0.0,
95                max_height: f32::INFINITY,
96            },
97            content_offset: Point::default(),
98        }
99    }
100}
101
102impl LayoutState {
103    pub fn size(&self) -> Size {
104        self.size
105    }
106
107    pub fn position(&self) -> Point {
108        self.position
109    }
110
111    /// The same state placed at the origin: what a window root's own scene
112    /// starts from, since the position its parent gave it belongs to the
113    /// parent's window.
114    pub fn at_origin(mut self) -> Self {
115        self.position = Point::default();
116        self
117    }
118
119    pub fn is_placed(&self) -> bool {
120        self.is_placed
121    }
122
123    pub(crate) fn set_node_id(&mut self, node_id: NodeId) {
124        self.node_id = Some(node_id);
125    }
126
127    /// Writes the measured size, self-reporting an actual change to the
128    /// scene phase.
129    pub fn set_size(&mut self, size: Size) {
130        if self.size != size {
131            if let Some(id) = self.node_id {
132                crate::render_state::record_geometry_scene_node(id);
133            }
134            self.size = size;
135        }
136    }
137
138    /// Writes the placed position and marks the node placed, self-reporting
139    /// an actual move to the scene phase.
140    pub fn place(&mut self, position: Point) {
141        if self.position != position {
142            if let Some(id) = self.node_id {
143                crate::render_state::record_geometry_scene_node(id);
144            }
145            self.position = position;
146        }
147        self.is_placed = true;
148    }
149
150    /// Clears the placed flag at the start of a layout pass.
151    pub fn clear_placed(&mut self) {
152        self.is_placed = false;
153    }
154}
155
156#[derive(Clone)]
157struct MeasurementCacheEntry {
158    constraints: Constraints,
159    measured: Rc<MeasuredNode>,
160}
161
162#[derive(Clone, Copy, Debug)]
163pub enum IntrinsicKind {
164    MinWidth(f32),
165    MaxWidth(f32),
166    MinHeight(f32),
167    MaxHeight(f32),
168}
169
170impl IntrinsicKind {
171    fn discriminant(&self) -> u8 {
172        match self {
173            IntrinsicKind::MinWidth(_) => 0,
174            IntrinsicKind::MaxWidth(_) => 1,
175            IntrinsicKind::MinHeight(_) => 2,
176            IntrinsicKind::MaxHeight(_) => 3,
177        }
178    }
179
180    fn value_bits(&self) -> u32 {
181        match self {
182            IntrinsicKind::MinWidth(value)
183            | IntrinsicKind::MaxWidth(value)
184            | IntrinsicKind::MinHeight(value)
185            | IntrinsicKind::MaxHeight(value) => value.to_bits(),
186        }
187    }
188}
189
190impl PartialEq for IntrinsicKind {
191    fn eq(&self, other: &Self) -> bool {
192        self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
193    }
194}
195
196impl Eq for IntrinsicKind {}
197
198impl Hash for IntrinsicKind {
199    fn hash<H: Hasher>(&self, state: &mut H) {
200        self.discriminant().hash(state);
201        self.value_bits().hash(state);
202    }
203}
204
205#[derive(Default)]
206struct NodeCacheState {
207    epoch: u64,
208    measurements: Vec<MeasurementCacheEntry>,
209    intrinsics: Vec<(IntrinsicKind, f32)>,
210}
211
212#[derive(Clone, Default)]
213pub(crate) struct LayoutNodeCacheHandles {
214    state: Rc<RefCell<NodeCacheState>>,
215}
216
217impl LayoutNodeCacheHandles {
218    pub(crate) fn clear(&self) {
219        let mut state = self.state.borrow_mut();
220        state.measurements.clear();
221        state.intrinsics.clear();
222        state.epoch = 0;
223    }
224
225    pub(crate) fn activate(&self, epoch: u64) {
226        let mut state = self.state.borrow_mut();
227        if state.epoch != epoch {
228            state.measurements.clear();
229            state.intrinsics.clear();
230            state.epoch = epoch;
231        }
232    }
233
234    pub(crate) fn epoch(&self) -> u64 {
235        self.state.borrow().epoch
236    }
237
238    pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
239        let state = self.state.borrow();
240        state
241            .measurements
242            .iter()
243            .find(|entry| entry.constraints == constraints)
244            .map(|entry| Rc::clone(&entry.measured))
245    }
246
247    pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
248        let mut state = self.state.borrow_mut();
249        if let Some(entry) = state
250            .measurements
251            .iter_mut()
252            .find(|entry| entry.constraints == constraints)
253        {
254            entry.measured = measured;
255        } else {
256            state.measurements.push(MeasurementCacheEntry {
257                constraints,
258                measured,
259            });
260        }
261    }
262
263    pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
264        let state = self.state.borrow();
265        state
266            .intrinsics
267            .iter()
268            .find(|(stored_kind, _)| stored_kind == kind)
269            .map(|(_, value)| *value)
270    }
271
272    pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
273        let mut state = self.state.borrow_mut();
274        if let Some((_, existing)) = state
275            .intrinsics
276            .iter_mut()
277            .find(|(stored_kind, _)| stored_kind == &kind)
278        {
279            *existing = value;
280        } else {
281            state.intrinsics.push((kind, value));
282        }
283    }
284}
285
286pub struct LayoutNode {
287    pub modifier: Modifier,
288    modifier_chain: ModifierChainHandle,
289    resolved_modifiers: ResolvedModifiers,
290    modifier_capabilities: NodeCapabilities,
291    modifier_child_capabilities: NodeCapabilities,
292    pub measure_policy: Rc<dyn MeasurePolicy>,
293    density: crate::density::Density,
294    /// The actual children of this node (folded view - includes virtual nodes as-is)
295    pub children: Vec<NodeId>,
296    cache: LayoutNodeCacheHandles,
297    needs_measure: Cell<bool>,
298    needs_layout: Cell<bool>,
299    needs_semantics: Cell<bool>,
300    needs_redraw: Cell<bool>,
301    needs_pointer_pass: Cell<bool>,
302    needs_focus_sync: Cell<bool>,
303    parent: Cell<Option<NodeId>>,
304    folded_parent: Cell<Option<NodeId>>,
305    id: Cell<Option<NodeId>>,
306    owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
307    debug_modifiers: Cell<bool>,
308    is_virtual: bool,
309    virtual_children_count: Cell<usize>,
310
311    modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
312    modifier_slices_dirty: Cell<bool>,
313
314    layout_state: Rc<RefCell<LayoutState>>,
315    layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
316}
317
318pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
319
320thread_local! {
321    static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
322        Rc::new(crate::layout::policies::EmptyMeasurePolicy);
323}
324
325fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
326    EMPTY_MEASURE_POLICY.with(Rc::clone)
327}
328
329impl LayoutNode {
330    pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
331        Self::new_with_virtual(modifier, measure_policy, false)
332    }
333
334    /// Create a virtual LayoutNode for subcomposition slot containers.
335    /// Virtual nodes are transparent - their children are flattened into parent's children list.
336    pub fn new_virtual() -> Self {
337        Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
338    }
339
340    fn new_recycled_shell(is_virtual: bool) -> Self {
341        let mut shell =
342            Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
343        shell.needs_measure.set(false);
344        shell.needs_layout.set(false);
345        shell.needs_semantics.set(false);
346        shell.needs_redraw.set(false);
347        shell.needs_pointer_pass.set(false);
348        shell.needs_focus_sync.set(false);
349        shell.parent.set(None);
350        shell.folded_parent.set(None);
351        shell.id.set(None);
352        shell.owner_context_id.set(None);
353        shell.debug_modifiers.set(false);
354        shell.virtual_children_count.set(0);
355        shell.cache = LayoutNodeCacheHandles::default();
356        shell.modifier_slices_snapshot = RefCell::new(Rc::default());
357        shell.modifier_slices_dirty = Cell::new(true);
358        shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
359        shell.layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::default()));
360        shell
361    }
362
363    fn new_with_virtual(
364        modifier: Modifier,
365        measure_policy: Rc<dyn MeasurePolicy>,
366        is_virtual: bool,
367    ) -> Self {
368        let mut node = Self {
369            modifier,
370            modifier_chain: ModifierChainHandle::new(),
371            resolved_modifiers: ResolvedModifiers::default(),
372            modifier_capabilities: NodeCapabilities::default(),
373            modifier_child_capabilities: NodeCapabilities::default(),
374            measure_policy,
375            density: crate::density::Density::default(),
376            children: Vec::new(),
377            cache: LayoutNodeCacheHandles::default(),
378            needs_measure: Cell::new(true),
379            needs_layout: Cell::new(true),
380            needs_semantics: Cell::new(true),
381            needs_redraw: Cell::new(true),
382            needs_pointer_pass: Cell::new(false),
383            needs_focus_sync: Cell::new(false),
384            parent: Cell::new(None),
385            folded_parent: Cell::new(None),
386            id: Cell::new(None),
387            owner_context_id: Cell::new(None),
388            debug_modifiers: Cell::new(false),
389            is_virtual,
390            virtual_children_count: Cell::new(0),
391            modifier_slices_snapshot: RefCell::new(Rc::default()),
392            modifier_slices_dirty: Cell::new(true),
393            layout_state: Rc::new(RefCell::new(LayoutState::default())),
394            layout_runtime_state: Rc::new(RefCell::new(LayoutRuntimeState::default())),
395        };
396        node.sync_modifier_chain();
397        node
398    }
399
400    pub fn set_modifier(&mut self, modifier: Modifier) {
401        let modifier_changed = !self.modifier.structural_eq(&modifier);
402        self.modifier = modifier;
403        self.sync_modifier_chain();
404        if modifier_changed {
405            self.cache.clear();
406            self.request_semantics_update();
407        }
408    }
409
410    fn sync_modifier_chain(&mut self) {
411        let prev_caps = self.modifier_capabilities;
412        let start_parent = self.parent();
413        let mut resolver = move |token: &ModifierLocalToken| {
414            resolve_modifier_local_from_parent_chain(start_parent, token)
415        };
416        self.modifier_chain
417            .set_debug_logging(self.debug_modifiers.get());
418        self.modifier_chain.set_node_id(self.id.get());
419        let modifier_local_invalidations = self
420            .modifier_chain
421            .update_with_resolver(&self.modifier, &mut resolver);
422        self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
423        self.modifier_capabilities = self.modifier_chain.capabilities();
424        self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
425
426        self.update_modifier_slices_cache();
427
428        let mut invalidations = self.modifier_chain.take_invalidations();
429        invalidations.extend(modifier_local_invalidations);
430        self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps);
431        self.refresh_registry_state();
432    }
433
434    fn update_modifier_slices_cache(&self) {
435        use crate::modifier::collect_modifier_slices_into;
436
437        let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
438        collect_modifier_slices_into(self.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
439        self.modifier_slices_dirty.set(false);
440    }
441
442    pub(crate) fn mark_modifier_slices_dirty(&self) {
443        self.modifier_slices_dirty.set(true);
444    }
445
446    #[cfg(test)]
447    fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
448        self.dispatch_modifier_invalidations_with_prev(invalidations, NodeCapabilities::empty());
449    }
450
451    fn dispatch_modifier_invalidations_with_prev(
452        &self,
453        invalidations: &[ModifierInvalidation],
454        prev_caps: NodeCapabilities,
455    ) {
456        let curr_caps = self.modifier_capabilities;
457        for invalidation in invalidations {
458            self.modifier_slices_dirty.set(true);
459            let has_capability =
460                |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
461            match invalidation.kind() {
462                InvalidationKind::Layout => {
463                    if has_capability(NodeCapabilities::LAYOUT) {
464                        self.mark_needs_measure();
465                        if let Some(id) = self.id.get() {
466                            log_layout_invalidation_dispatch(
467                                id,
468                                invalidation,
469                                curr_caps,
470                                prev_caps,
471                                &self.modifier,
472                            );
473                            let inside_composition =
474                                cranpose_core::composer_context::try_with_composer(|_| ())
475                                    .is_some();
476                            if inside_composition {
477                                cranpose_core::bubble_measure_dirty_in_composer(id);
478                            } else {
479                                crate::schedule_layout_repass(id);
480                            }
481                        }
482                    }
483                }
484                InvalidationKind::Draw => {
485                    if has_capability(NodeCapabilities::DRAW)
486                        || invalidation.capabilities().contains(NodeCapabilities::DRAW)
487                    {
488                        self.mark_needs_redraw();
489                    }
490                }
491                InvalidationKind::PointerInput => {
492                    if has_capability(NodeCapabilities::POINTER_INPUT) {
493                        self.mark_needs_pointer_pass();
494                        crate::request_pointer_invalidation();
495                        if let Some(id) = self.id.get() {
496                            crate::schedule_pointer_repass(id);
497                        }
498                    }
499                }
500                InvalidationKind::Semantics => {
501                    self.request_semantics_update();
502                }
503                InvalidationKind::Focus => {
504                    if has_capability(NodeCapabilities::FOCUS) {
505                        self.mark_needs_focus_sync();
506                        crate::request_focus_invalidation();
507                        if let Some(id) = self.id.get() {
508                            crate::schedule_focus_invalidation(id);
509                        }
510                    }
511                }
512            }
513        }
514    }
515
516    /// The grid this node was composed against.
517    pub fn density(&self) -> crate::density::Density {
518        self.density
519    }
520
521    /// Records the grid the composition provided, re-measuring if it moved.
522    pub fn set_density(&mut self, density: crate::density::Density) {
523        if self.density != density {
524            self.density = density;
525            self.cache.clear();
526            self.mark_needs_measure();
527        }
528    }
529
530    pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
531        if !Rc::ptr_eq(&self.measure_policy, &policy) {
532            self.measure_policy = policy;
533            self.cache.clear();
534            self.mark_needs_measure();
535            if let Some(id) = self.id.get() {
536                cranpose_core::bubble_measure_dirty_in_composer(id);
537            }
538        }
539    }
540
541    /// Mark this node as needing measure. Also marks it as needing layout.
542    pub fn mark_needs_measure(&self) {
543        self.needs_measure.set(true);
544        self.needs_layout.set(true);
545    }
546
547    /// Mark this node as needing layout (but not necessarily measure).
548    pub fn mark_needs_layout(&self) {
549        self.needs_layout.set(true);
550    }
551
552    /// Mark this node as needing redraw without forcing measure/layout.
553    pub fn mark_needs_redraw(&self) {
554        self.needs_redraw.set(true);
555        if let Some(id) = self.id.get() {
556            crate::schedule_draw_repass(id);
557        }
558        crate::request_render_invalidation();
559    }
560
561    /// Check if this node needs measure.
562    pub fn needs_measure(&self) -> bool {
563        self.needs_measure.get()
564    }
565
566    /// Check if this node needs layout.
567    pub fn needs_layout(&self) -> bool {
568        self.needs_layout.get()
569    }
570
571    /// Mark this node as needing semantics recomputation.
572    pub fn mark_needs_semantics(&self) {
573        self.needs_semantics.set(true);
574    }
575
576    pub(crate) fn clear_needs_semantics(&self) {
577        self.needs_semantics.set(false);
578    }
579
580    /// Returns true when semantics need to be recomputed.
581    pub fn needs_semantics(&self) -> bool {
582        self.needs_semantics.get()
583    }
584
585    /// Returns true when this node requested a redraw since the last render pass.
586    pub fn needs_redraw(&self) -> bool {
587        self.needs_redraw.get()
588    }
589
590    pub fn clear_needs_redraw(&self) {
591        self.needs_redraw.set(false);
592    }
593
594    fn request_semantics_update(&self) {
595        let already_dirty = self.needs_semantics.replace(true);
596        if already_dirty {
597            return;
598        }
599
600        if let Some(id) = self.id.get() {
601            cranpose_core::queue_semantics_invalidation(id);
602        }
603    }
604
605    pub(crate) fn clear_needs_measure(&self) {
606        self.needs_measure.set(false);
607    }
608
609    pub(crate) fn clear_needs_layout(&self) {
610        self.needs_layout.set(false);
611    }
612
613    /// Marks this node as needing a fresh pointer-input pass.
614    pub fn mark_needs_pointer_pass(&self) {
615        self.needs_pointer_pass.set(true);
616    }
617
618    /// Returns true when pointer-input state needs to be recomputed.
619    pub fn needs_pointer_pass(&self) -> bool {
620        self.needs_pointer_pass.get()
621    }
622
623    /// Clears the pointer-input dirty flag after hosts service it.
624    pub fn clear_needs_pointer_pass(&self) {
625        self.needs_pointer_pass.set(false);
626    }
627
628    /// Marks this node as needing a focus synchronization.
629    pub fn mark_needs_focus_sync(&self) {
630        self.needs_focus_sync.set(true);
631    }
632
633    /// Returns true when focus state needs to be synchronized.
634    pub fn needs_focus_sync(&self) -> bool {
635        self.needs_focus_sync.get()
636    }
637
638    /// Clears the focus dirty flag after the focus manager processes it.
639    pub fn clear_needs_focus_sync(&self) {
640        self.needs_focus_sync.set(false);
641    }
642
643    /// Set this node's ID (called by applier after creation).
644    pub fn set_node_id(&mut self, id: NodeId) {
645        if let Some(existing) = self.id.replace(Some(id))
646            && let Some(owner_context_id) = self.owner_context_id.take()
647        {
648            unregister_layout_node(owner_context_id, existing);
649        }
650        self.layout_state.borrow_mut().set_node_id(id);
651        let owner_context_id = register_layout_node(id, self);
652        self.owner_context_id.set(Some(owner_context_id));
653        self.refresh_registry_state();
654
655        self.modifier_chain.set_node_id(Some(id));
656        let invalidations = self.modifier_chain.take_invalidations();
657        self.dispatch_modifier_invalidations_with_prev(&invalidations, NodeCapabilities::empty());
658        self.update_modifier_slices_cache();
659    }
660
661    /// Get this node's ID.
662    pub fn node_id(&self) -> Option<NodeId> {
663        self.id.get()
664    }
665
666    /// Set this node's parent (called when node is added as child).
667    /// Sets both folded_parent (direct) and parent (first non-virtual ancestor for bubbling).
668    pub fn set_parent(&self, parent: NodeId) {
669        self.folded_parent.set(Some(parent));
670        self.parent.set(Some(parent));
671        self.refresh_registry_state();
672    }
673
674    /// Clear this node's parent (called when node is removed from parent).
675    pub fn clear_parent(&self) {
676        self.folded_parent.set(None);
677        self.parent.set(None);
678        self.refresh_registry_state();
679    }
680
681    /// Get this node's parent for dirty flag bubbling (may skip virtual nodes).
682    pub fn parent(&self) -> Option<NodeId> {
683        self.parent.get()
684    }
685
686    /// Get this node's direct parent (may be a virtual node).
687    pub fn folded_parent(&self) -> Option<NodeId> {
688        self.folded_parent.get()
689    }
690
691    /// Returns true if this is a virtual node (transparent container for subcomposition).
692    pub fn is_virtual(&self) -> bool {
693        self.is_virtual
694    }
695
696    pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
697        self.cache.clone()
698    }
699
700    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
701        self.resolved_modifiers
702    }
703
704    pub fn modifier_capabilities(&self) -> NodeCapabilities {
705        self.modifier_capabilities
706    }
707
708    /// Whether this node's modifier chain makes it the root of a separate
709    /// window. Its parent lays out as if it had no size and its parent's
710    /// scene skips it; its own scene starts here.
711    pub fn is_window_root(&self) -> bool {
712        self.modifier_capabilities
713            .contains(NodeCapabilities::WINDOW_ROOT)
714    }
715
716    pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
717        self.modifier_child_capabilities
718    }
719
720    pub fn set_debug_modifiers(&mut self, enabled: bool) {
721        self.debug_modifiers.set(enabled);
722        self.modifier_chain.set_debug_logging(enabled);
723    }
724
725    pub fn debug_modifiers_enabled(&self) -> bool {
726        self.debug_modifiers.get()
727    }
728
729    pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
730        self.modifier_chain.modifier_locals_handle()
731    }
732
733    pub fn has_layout_modifier_nodes(&self) -> bool {
734        self.modifier_capabilities
735            .contains(NodeCapabilities::LAYOUT)
736    }
737
738    pub fn has_draw_modifier_nodes(&self) -> bool {
739        self.modifier_capabilities.contains(NodeCapabilities::DRAW)
740    }
741
742    pub fn has_pointer_input_modifier_nodes(&self) -> bool {
743        self.modifier_capabilities
744            .contains(NodeCapabilities::POINTER_INPUT)
745    }
746
747    pub fn has_semantics_modifier_nodes(&self) -> bool {
748        self.modifier_capabilities
749            .contains(NodeCapabilities::SEMANTICS)
750    }
751
752    pub fn has_focus_modifier_nodes(&self) -> bool {
753        self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
754    }
755
756    fn refresh_registry_state(&self) {
757        if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
758            let parent = self.parent();
759            let capabilities = self.modifier_child_capabilities();
760            let modifier_locals = self.modifier_locals_handle();
761            let _ = crate::render_state::with_layout_node_registry_by_app_context(
762                owner_context_id,
763                |registry| {
764                    registry.update_entry(id, parent, capabilities, modifier_locals);
765                },
766            );
767        }
768    }
769
770    pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
771        if self.modifier_slices_dirty.get() {
772            self.update_modifier_slices_cache();
773        }
774        self.modifier_slices_snapshot.borrow().clone()
775    }
776
777    /// Returns a clone of the current layout state.
778    pub fn layout_state(&self) -> LayoutState {
779        self.layout_state.borrow().clone()
780    }
781
782    /// Returns the measured size of this node.
783    pub fn measured_size(&self) -> Size {
784        self.layout_state.borrow().size
785    }
786
787    /// Returns the position of this node relative to its parent.
788    pub fn position(&self) -> Point {
789        self.layout_state.borrow().position
790    }
791
792    /// Returns true if this node has been placed in the current layout pass.
793    pub fn is_placed(&self) -> bool {
794        self.layout_state.borrow().is_placed
795    }
796
797    /// Updates the measured size of this node. Called during measurement.
798    /// [`LayoutState::set_size`] self-reports actual changes to the scene
799    /// phase.
800    pub fn set_measured_size(&self, size: Size) {
801        self.layout_state.borrow_mut().set_size(size);
802    }
803
804    /// Updates the position of this node. Called during placement.
805    /// [`LayoutState::place`] self-reports actual moves to the scene phase.
806    pub fn set_position(&self, position: Point) {
807        self.layout_state.borrow_mut().place(position);
808    }
809
810    /// Records the constraints used for measurement. Used for relayout optimization.
811    pub fn set_measurement_constraints(&self, constraints: Constraints) {
812        self.layout_state.borrow_mut().measurement_constraints = constraints;
813    }
814
815    /// Records the content offset (e.g. from padding).
816    pub fn set_content_offset(&self, offset: Point) {
817        self.layout_state.borrow_mut().content_offset = offset;
818    }
819
820    /// Clears the is_placed flag. Called at the start of a layout pass.
821    pub fn clear_placed(&self) {
822        self.layout_state.borrow_mut().is_placed = false;
823    }
824
825    pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
826        crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
827    }
828
829    pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
830        &self.modifier_chain
831    }
832
833    /// Access the text field modifier node (if present) with a mutable callback.
834    ///
835    /// This is used for keyboard event dispatch to text fields.
836    /// Returns `None` if no text field modifier is found in the chain.
837    pub fn with_text_field_modifier_mut<R>(
838        &mut self,
839        f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
840    ) -> Option<R> {
841        self.modifier_chain.with_text_field_modifier_mut(f)
842    }
843
844    /// Returns a handle to the shared layout state.
845    /// Used by layout system to update state without borrowing the Applier.
846    pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
847        self.layout_state.clone()
848    }
849
850    pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
851        self.layout_runtime_state.clone()
852    }
853
854    #[cfg(test)]
855    pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
856        self.layout_runtime_state.borrow().debug_stats()
857    }
858}
859impl Clone for LayoutNode {
860    fn clone(&self) -> Self {
861        let mut node = Self {
862            modifier: self.modifier.clone(),
863            modifier_chain: ModifierChainHandle::new(),
864            resolved_modifiers: ResolvedModifiers::default(),
865            modifier_capabilities: self.modifier_capabilities,
866            modifier_child_capabilities: self.modifier_child_capabilities,
867            measure_policy: self.measure_policy.clone(),
868            density: self.density,
869            children: self.children.clone(),
870            cache: self.cache.clone(),
871            needs_measure: Cell::new(self.needs_measure.get()),
872            needs_layout: Cell::new(self.needs_layout.get()),
873            needs_semantics: Cell::new(self.needs_semantics.get()),
874            needs_redraw: Cell::new(self.needs_redraw.get()),
875            needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
876            needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
877            parent: Cell::new(self.parent.get()),
878            folded_parent: Cell::new(self.folded_parent.get()),
879            id: Cell::new(None),
880            owner_context_id: Cell::new(None),
881            debug_modifiers: Cell::new(self.debug_modifiers.get()),
882            is_virtual: self.is_virtual,
883            virtual_children_count: Cell::new(self.virtual_children_count.get()),
884            modifier_slices_snapshot: RefCell::new(Rc::default()),
885            modifier_slices_dirty: Cell::new(true),
886            layout_state: self.layout_state.clone(),
887            layout_runtime_state: self.layout_runtime_state.clone(),
888        };
889        node.sync_modifier_chain();
890        node
891    }
892}
893
894impl Node for LayoutNode {
895    fn mount(&mut self) {
896        let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
897        chain.repair_chain();
898        chain.attach_nodes(&mut *context);
899    }
900
901    fn unmount(&mut self) {
902        self.modifier_chain.chain_mut().detach_nodes();
903    }
904
905    fn set_node_id(&mut self, id: NodeId) {
906        LayoutNode::set_node_id(self, id);
907    }
908
909    fn insert_child(&mut self, child: NodeId) -> bool {
910        if self.children.contains(&child) {
911            return false;
912        }
913        if is_virtual_node(child) {
914            let count = self.virtual_children_count.get();
915            self.virtual_children_count.set(count + 1);
916        }
917        self.children.push(child);
918        self.cache.clear();
919        self.mark_needs_measure();
920        true
921    }
922
923    fn remove_child(&mut self, child: NodeId) -> bool {
924        let before = self.children.len();
925        self.children.retain(|&id| id != child);
926        let removed = self.children.len() < before;
927        if removed {
928            if is_virtual_node(child) {
929                let count = self.virtual_children_count.get();
930                if count > 0 {
931                    self.virtual_children_count.set(count - 1);
932                }
933            }
934            self.cache.clear();
935            self.mark_needs_measure();
936        }
937        removed
938    }
939
940    fn move_child(&mut self, from: usize, to: usize) {
941        if from == to || from >= self.children.len() {
942            return;
943        }
944        let child = self.children.remove(from);
945        let target = to.min(self.children.len());
946        self.children.insert(target, child);
947        self.cache.clear();
948        self.mark_needs_measure();
949    }
950
951    fn update_children(&mut self, children: &[NodeId]) {
952        self.children.clear();
953        self.children.extend_from_slice(children);
954        self.cache.clear();
955        self.mark_needs_measure();
956    }
957
958    fn children(&self) -> Vec<NodeId> {
959        self.children.clone()
960    }
961
962    fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
963        out.clear();
964        out.extend(self.children.iter().copied());
965    }
966
967    fn on_attached_to_parent(&mut self, parent: NodeId) {
968        self.set_parent(parent);
969    }
970
971    fn on_removed_from_parent(&mut self) {
972        self.clear_parent();
973    }
974
975    fn parent(&self) -> Option<NodeId> {
976        self.parent.get()
977    }
978
979    fn mark_needs_layout(&self) {
980        self.needs_layout.set(true);
981    }
982
983    fn needs_layout(&self) -> bool {
984        self.needs_layout.get()
985    }
986
987    fn mark_needs_measure(&self) {
988        self.needs_measure.set(true);
989        self.needs_layout.set(true);
990    }
991
992    fn needs_measure(&self) -> bool {
993        self.needs_measure.get()
994    }
995
996    fn mark_needs_semantics(&self) {
997        self.needs_semantics.set(true);
998    }
999
1000    fn needs_semantics(&self) -> bool {
1001        self.needs_semantics.get()
1002    }
1003
1004    fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1005        if self.parent.get().is_none() {
1006            self.parent.set(Some(parent));
1007        }
1008    }
1009
1010    fn recycle_key(&self) -> Option<TypeId> {
1011        Some(TypeId::of::<Self>())
1012    }
1013
1014    fn recycle_pool_limit(&self) -> Option<usize> {
1015        Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1016    }
1017
1018    fn prepare_for_recycle(&mut self) {
1019        *self = Self::new_recycled_shell(self.is_virtual);
1020    }
1021
1022    fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1023        Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1024    }
1025
1026    fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1027        let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1028        let node_id = previous.id.replace(None);
1029        let parent = previous.parent.get();
1030        let folded_parent = previous.folded_parent.get();
1031        let debug_modifiers = previous.debug_modifiers.get();
1032        let needs_measure = previous.needs_measure.get();
1033        let needs_layout = previous.needs_layout.get();
1034        let needs_semantics = previous.needs_semantics.get();
1035        let needs_redraw = previous.needs_redraw.get();
1036        let needs_pointer_pass = previous.needs_pointer_pass.get();
1037        let needs_focus_sync = previous.needs_focus_sync.get();
1038        let virtual_children_count = previous.virtual_children_count.get();
1039        let children = previous.children.to_vec();
1040        let modifier = previous.modifier.rehouse_for_live_compaction();
1041        let measure_policy = previous.measure_policy.clone();
1042        let layout_state = previous.layout_state.clone();
1043        let layout_runtime_state = previous.layout_runtime_state.clone();
1044
1045        previous.modifier_chain.chain_mut().detach_nodes();
1046
1047        let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
1048        compact.children = children;
1049        compact.parent.set(parent);
1050        compact.folded_parent.set(folded_parent);
1051        compact.id.set(node_id);
1052        compact.debug_modifiers.set(debug_modifiers);
1053        compact.needs_measure.set(needs_measure);
1054        compact.needs_layout.set(needs_layout);
1055        compact.needs_semantics.set(needs_semantics);
1056        compact.needs_redraw.set(needs_redraw);
1057        compact.needs_pointer_pass.set(needs_pointer_pass);
1058        compact.needs_focus_sync.set(needs_focus_sync);
1059        compact.virtual_children_count.set(virtual_children_count);
1060        compact.layout_state = layout_state;
1061        compact.layout_runtime_state = layout_runtime_state;
1062        compact.sync_modifier_chain();
1063        if let Some(id) = node_id {
1064            let owner_context_id = register_layout_node(id, &compact);
1065            compact.owner_context_id.set(Some(owner_context_id));
1066        }
1067
1068        Some(Box::new(compact))
1069    }
1070}
1071
1072impl Drop for LayoutNode {
1073    fn drop(&mut self) {
1074        if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1075            unregister_layout_node(owner_context_id, id);
1076        }
1077    }
1078}
1079
1080const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1081const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1082
1083#[cfg(test)]
1084#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1085struct LayoutNodeRegistryDebugStats {
1086    len: usize,
1087    capacity: usize,
1088}
1089
1090struct LayoutNodeRegistryEntry {
1091    parent: Option<NodeId>,
1092    modifier_child_capabilities: NodeCapabilities,
1093    modifier_locals: ModifierLocalsHandle,
1094    is_virtual: bool,
1095}
1096
1097pub(crate) struct LayoutNodeRegistryState {
1098    entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1099    virtual_node_id_counter: Cell<NodeId>,
1100}
1101
1102impl LayoutNodeRegistryState {
1103    pub(crate) fn new() -> Self {
1104        Self {
1105            entries: RefCell::new(HashMap::new()),
1106            virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1107        }
1108    }
1109
1110    fn register(&self, id: NodeId, node: &LayoutNode) {
1111        self.entries.borrow_mut().insert(
1112            id,
1113            LayoutNodeRegistryEntry {
1114                parent: node.parent(),
1115                modifier_child_capabilities: node.modifier_child_capabilities(),
1116                modifier_locals: node.modifier_locals_handle(),
1117                is_virtual: node.is_virtual(),
1118            },
1119        );
1120    }
1121
1122    fn unregister(&self, id: NodeId) {
1123        let mut entries = self.entries.borrow_mut();
1124        entries.remove(&id);
1125        let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1126            && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1127            || entries.capacity()
1128                > entries
1129                    .len()
1130                    .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1131                    .saturating_mul(4);
1132        if should_shrink {
1133            let retained = entries
1134                .len()
1135                .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1136            let mut rebuilt = HashMap::new();
1137            rebuilt.reserve(retained);
1138            rebuilt.extend(entries.drain());
1139            *entries = rebuilt;
1140        }
1141    }
1142
1143    fn update_entry(
1144        &self,
1145        id: NodeId,
1146        parent: Option<NodeId>,
1147        modifier_child_capabilities: NodeCapabilities,
1148        modifier_locals: ModifierLocalsHandle,
1149    ) {
1150        if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1151            entry.parent = parent;
1152            entry.modifier_child_capabilities = modifier_child_capabilities;
1153            entry.modifier_locals = modifier_locals;
1154        }
1155    }
1156
1157    #[cfg(test)]
1158    fn stats(&self) -> LayoutNodeRegistryDebugStats {
1159        let entries = self.entries.borrow();
1160        LayoutNodeRegistryDebugStats {
1161            len: entries.len(),
1162            capacity: entries.capacity(),
1163        }
1164    }
1165
1166    fn is_virtual_node(&self, id: NodeId) -> bool {
1167        self.entries
1168            .borrow()
1169            .get(&id)
1170            .is_some_and(|entry| entry.is_virtual)
1171    }
1172
1173    fn allocate_virtual_node_id(&self) -> NodeId {
1174        let id = self.virtual_node_id_counter.get();
1175        self.virtual_node_id_counter.set(id.wrapping_add(1));
1176        id
1177    }
1178
1179    fn resolve_modifier_local_from_parent_chain(
1180        &self,
1181        start: Option<NodeId>,
1182        token: &ModifierLocalToken,
1183    ) -> Option<ResolvedModifierLocal> {
1184        let mut current = start;
1185        while let Some(parent_id) = current {
1186            let (next_parent, resolved) = {
1187                let entries = self.entries.borrow();
1188                if let Some(entry) = entries.get(&parent_id) {
1189                    let resolved = if entry
1190                        .modifier_child_capabilities
1191                        .contains(NodeCapabilities::MODIFIER_LOCALS)
1192                    {
1193                        entry
1194                            .modifier_locals
1195                            .borrow()
1196                            .resolve(token)
1197                            .map(|value| value.with_source(ModifierLocalSource::Ancestor))
1198                    } else {
1199                        None
1200                    };
1201                    (entry.parent, resolved)
1202                } else {
1203                    (None, None)
1204                }
1205            };
1206            if let Some(value) = resolved {
1207                return Some(value);
1208            }
1209            current = next_parent;
1210        }
1211        None
1212    }
1213}
1214
1215pub(crate) fn register_layout_node(
1216    id: NodeId,
1217    node: &LayoutNode,
1218) -> crate::render_state::AppContextId {
1219    let owner_context_id = crate::render_state::current_app_context_id();
1220    let _ = crate::render_state::with_layout_node_registry_by_app_context(
1221        owner_context_id,
1222        |registry| {
1223            registry.register(id, node);
1224        },
1225    );
1226    owner_context_id
1227}
1228
1229pub(crate) fn unregister_layout_node(
1230    owner_context_id: crate::render_state::AppContextId,
1231    id: NodeId,
1232) {
1233    let _ = crate::render_state::with_layout_node_registry_by_app_context(
1234        owner_context_id,
1235        |registry| {
1236            registry.unregister(id);
1237        },
1238    );
1239}
1240
1241#[cfg(test)]
1242fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1243    crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1244}
1245
1246pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1247    crate::render_state::with_layout_node_registry(|registry| registry.is_virtual_node(id))
1248}
1249
1250pub(crate) fn allocate_virtual_node_id() -> NodeId {
1251    crate::render_state::with_layout_node_registry(
1252        LayoutNodeRegistryState::allocate_virtual_node_id,
1253    )
1254}
1255
1256fn resolve_modifier_local_from_parent_chain(
1257    start: Option<NodeId>,
1258    token: &ModifierLocalToken,
1259) -> Option<ResolvedModifierLocal> {
1260    crate::render_state::with_layout_node_registry(|registry| {
1261        registry.resolve_modifier_local_from_parent_chain(start, token)
1262    })
1263}
1264
1265#[cfg(test)]
1266#[path = "tests/layout_node_tests.rs"]
1267mod tests;