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