Skip to main content

cranpose_ui/widgets/nodes/
layout_node.rs

1use std::{
2    any::TypeId,
3    cell::{Cell, RefCell},
4    rc::Rc,
5};
6
7use cranpose_core::{Node, NodeId, NodeIdCell};
8use cranpose_foundation::{
9    InvalidationKind, ModifierInvalidation, ModifierInvalidations, NodeCapabilities,
10    SemanticsConfiguration,
11};
12use cranpose_ui_layout::MeasurePolicy;
13
14use super::layout_invalidation_diag::log_layout_invalidation_dispatch;
15#[cfg(test)]
16use crate::layout::LayoutRuntimeDebugStats;
17use crate::{
18    layout::LayoutRuntimeState,
19    modifier::{
20        InPlaceUpdate, Modifier, ModifierChainHandle, ModifierLocalToken, ModifierLocalsHandle,
21        ModifierNodeSlices, Point, ResolvedModifiers, Size,
22    },
23    render_state::AppContext,
24    widgets::nodes::{
25        ChildPlacement, LayoutDirt, LayoutParts, LayoutState, NodeCache,
26        layout_node_registry::{
27            register_layout_node, resolve_modifier_local_from_parent_chain, unregister_layout_node,
28        },
29    },
30};
31
32/// A node of the layout tree.
33///
34/// Its fields keep the order they are declared in: what a layout pass reads
35/// for every child it binds comes first and fits one cache line, then what
36/// measuring the node reads, then what composition reads. Layout reads
37/// thousands of nodes a frame, and on a phone its cost is the lines it loads.
38#[repr(C)]
39pub struct LayoutNode {
40    parts: Rc<LayoutParts>,
41    // The actual children of this node (folded view - includes virtual nodes as-is)
42    pub(crate) children: Vec<NodeId>,
43
44    // Policy that measures and places the children of this node. Change it through
45    // `set_measure_policy` so that a measure is scheduled.
46    pub(crate) measure_policy: Rc<dyn MeasurePolicy>,
47    /// Where the chain's layout modifiers put their content, written by
48    /// layout and read by the draws and text the slices collect.
49    density: crate::density::Density,
50    is_virtual: bool,
51    needs_semantics: Cell<bool>,
52    /// The semantics tree has to read this node again, though its own
53    /// semantics are unchanged: a node below it changed, or its placement
54    /// or children did.
55    descendant_needs_semantics: Cell<bool>,
56    needs_redraw: Cell<bool>,
57    needs_pointer_pass: Cell<bool>,
58    needs_focus_sync: Cell<bool>,
59    modifier_slices_dirty: Cell<bool>,
60    debug_modifiers: Cell<bool>,
61
62    id: NodeIdCell,
63    parent: NodeIdCell,
64    folded_parent: NodeIdCell,
65    modifier_slices_snapshot: crate::modifier::SlicesSnapshot,
66
67    /// The chain's modal and hidden flags, read by the modal count and the
68    /// modal walk: dropped whenever the chain syncs or semantics are
69    /// invalidated, the two ways its semantics change.
70    semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
71    owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
72    modifier_chain: ModifierChainHandle,
73    // The modifier of this node. Change it through `set_modifier` so that the modifier chain
74    // updates.
75    pub(crate) modifier: Modifier,
76    /// Composable definitions responsible for this node, captured only for inspection builds.
77    #[cfg(feature = "inspection")]
78    pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
79}
80
81pub(crate) fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
82    crate::thread_state::with(|state| {
83        Rc::clone(
84            state
85                .empty_measure_policy
86                .get_or_init(|| Rc::new(crate::layout::policies::EmptyMeasurePolicy)),
87        )
88    })
89}
90
91impl LayoutNode {
92    /// Creates a node with `modifier` whose children `measure_policy` measures and places.
93    pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
94        Self::new_with_virtual(modifier, measure_policy, false)
95    }
96
97    /// Create a virtual LayoutNode for subcomposition slot containers.
98    /// Virtual nodes are transparent - their children are flattened into parent's children list.
99    pub fn new_virtual() -> Self {
100        Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
101    }
102
103    fn new_recycled_shell(is_virtual: bool) -> Self {
104        let mut shell =
105            Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
106        shell.parts.cache().set_dirt(LayoutDirt::CLEAN);
107        shell.needs_semantics.set(false);
108        shell.descendant_needs_semantics.set(false);
109        shell.needs_redraw.set(false);
110        shell.needs_pointer_pass.set(false);
111        shell.needs_focus_sync.set(false);
112        shell.parent.set(None);
113        shell.folded_parent.set(None);
114        shell.id.set(None);
115        shell.owner_context_id.set(None);
116        shell.debug_modifiers.set(false);
117        shell.modifier_slices_dirty = Cell::new(true);
118        shell
119    }
120
121    fn new_with_virtual(
122        modifier: Modifier,
123        measure_policy: Rc<dyn MeasurePolicy>,
124        is_virtual: bool,
125    ) -> Self {
126        let parts = Rc::new(LayoutParts::new(LayoutRuntimeState::new(Rc::clone(
127            &measure_policy,
128        ))));
129        let mut node = Self {
130            #[cfg(feature = "inspection")]
131            source_trace: cranpose_core::source_trace::current_source_trace(),
132            modifier,
133            modifier_chain: ModifierChainHandle::detached(),
134            measure_policy,
135            density: crate::density::Density::default(),
136            children: Vec::new(),
137            needs_semantics: Cell::new(true),
138            descendant_needs_semantics: Cell::new(false),
139            semantics_reach: Cell::new(None),
140            needs_redraw: Cell::new(true),
141            needs_pointer_pass: Cell::new(false),
142            needs_focus_sync: Cell::new(false),
143            parent: NodeIdCell::new(None),
144            folded_parent: NodeIdCell::new(None),
145            id: NodeIdCell::new(None),
146            owner_context_id: Cell::new(None),
147            debug_modifiers: Cell::new(false),
148            is_virtual,
149            modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
150            modifier_slices_dirty: Cell::new(true),
151            parts,
152        };
153        node.sync_modifier_chain(None);
154        node
155    }
156
157    /// The node's children, in the order they were inserted, virtual nodes as they are.
158    pub fn children(&self) -> &[NodeId] {
159        &self.children
160    }
161
162    /// Returns the modifier that was last set on this node.
163    pub fn modifier(&self) -> Modifier {
164        self.modifier.clone()
165    }
166
167    /// Replaces the node's modifier and updates its modifier chain, invalidating the work that the
168    /// change affects.
169    pub fn set_modifier(&mut self, modifier: Modifier) {
170        #[cfg(feature = "inspection")]
171        {
172            let trace = cranpose_core::source_trace::current_source_trace();
173            if !trace.is_empty() {
174                self.source_trace = trace;
175            }
176        }
177        // An equal modifier leaves every element's node as it was. Only a
178        // chain reading modifier locals resyncs, since its parent may now
179        // provide others.
180        if self.modifier == modifier
181            && !self
182                .modifier_capabilities()
183                .contains(NodeCapabilities::MODIFIER_LOCALS)
184        {
185            return;
186        }
187        // A change only in elements nothing else reads, as a ticker's text
188        // or an animated size, keeps the slices the chain gave.
189        let update = self.modifier.in_place_update(&modifier);
190        let modifier_changed = update.is_some() || !self.modifier.structural_eq(&modifier);
191        self.modifier = modifier;
192        self.sync_modifier_chain(update);
193        if modifier_changed {
194            self.parts.cache().clear();
195            self.request_semantics_update();
196        }
197    }
198
199    /// Reconciles the chain with the node's modifier. `in_place` says how
200    /// far the change reaches when the modifier changed only in elements the
201    /// chain can update in place: when it does, the current slices are kept.
202    fn sync_modifier_chain(&mut self, in_place: Option<InPlaceUpdate>) {
203        let prev_caps = self.modifier_capabilities();
204        let start_parent = self.parent();
205        let mut resolver = move |token: &ModifierLocalToken| {
206            resolve_modifier_local_from_parent_chain(start_parent, token)
207        };
208        self.modifier_chain
209            .set_debug_logging(self.debug_modifiers.get());
210        let mut context = self.modifier_chain.context(self.id.get());
211        let in_place = in_place.is_some_and(|update| {
212            self.modifier_chain
213                .update_elements_in_place(&self.modifier, update, &mut context)
214        });
215        let modifier_local_invalidations = if in_place {
216            ModifierInvalidations::new()
217        } else {
218            self.modifier_chain
219                .update_with_resolver(&self.modifier, &mut resolver, &mut context)
220        };
221        if prev_caps.contains(NodeCapabilities::WINDOW_ROOT)
222            != self
223                .modifier_capabilities()
224                .contains(NodeCapabilities::WINDOW_ROOT)
225        {
226            self.note_semantics_layout_change();
227        }
228        // An element updated in place reaches nothing, so the update leaves
229        // the chain's reach as it was, and the modal count does not read it
230        // again.
231        if !in_place {
232            self.forget_semantics_reach();
233        }
234        let keep_slices = in_place && !self.modifier_slices_dirty.get();
235        if !keep_slices {
236            self.modifier_slices_dirty.set(true);
237        }
238
239        let mut invalidations = context.take_invalidations();
240        invalidations.extend(modifier_local_invalidations);
241        self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps, keep_slices);
242        // An update in place leaves the node's parent, capabilities and
243        // modifier locals as the registry holds them.
244        if !in_place {
245            self.refresh_registry_state();
246        }
247    }
248
249    fn update_modifier_slices_cache(&self) {
250        self.modifier_slices_snapshot.collect(
251            self.modifier_chain.chain(),
252            Some(&self.parts),
253            self.density.density(),
254        );
255        self.modifier_slices_dirty.set(false);
256    }
257
258    /// Whether the modifier slices are built and current.
259    #[cfg(test)]
260    pub(crate) fn modifier_slices_ready(&self) -> bool {
261        !self.modifier_slices_dirty.get()
262    }
263
264    pub(crate) fn mark_modifier_slices_dirty(&self) {
265        self.modifier_slices_dirty.set(true);
266    }
267
268    #[cfg(test)]
269    fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
270        self.dispatch_modifier_invalidations_with_prev(
271            invalidations,
272            NodeCapabilities::empty(),
273            false,
274        );
275    }
276
277    /// Applies `invalidations` to the node. Each marks the slices for a
278    /// rebuild unless `keep_slices`.
279    fn dispatch_modifier_invalidations_with_prev(
280        &self,
281        invalidations: &[ModifierInvalidation],
282        prev_caps: NodeCapabilities,
283        keep_slices: bool,
284    ) {
285        let curr_caps = self.modifier_capabilities();
286        for invalidation in invalidations {
287            if !keep_slices {
288                self.modifier_slices_dirty.set(true);
289            }
290            let has_capability =
291                |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
292            match invalidation.kind() {
293                InvalidationKind::Layout => {
294                    if has_capability(NodeCapabilities::LAYOUT) {
295                        self.mark_needs_measure();
296                        if let Some(id) = self.id.get() {
297                            log_layout_invalidation_dispatch(
298                                id,
299                                invalidation,
300                                curr_caps,
301                                prev_caps,
302                                &self.modifier,
303                            );
304                            let inside_composition =
305                                cranpose_core::composer_context::try_with_composer(|_| ())
306                                    .is_some();
307                            if inside_composition {
308                                cranpose_core::bubble_measure_dirty_in_composer(id);
309                            } else {
310                                crate::schedule_layout_repass(id);
311                            }
312                        }
313                    }
314                }
315                InvalidationKind::Draw => {
316                    if has_capability(NodeCapabilities::DRAW)
317                        || invalidation.capabilities().contains(NodeCapabilities::DRAW)
318                    {
319                        self.mark_needs_redraw();
320                    }
321                }
322                InvalidationKind::PointerInput => {
323                    if has_capability(NodeCapabilities::POINTER_INPUT) {
324                        self.mark_needs_pointer_pass();
325                        crate::request_pointer_invalidation();
326                        if let Some(id) = self.id.get() {
327                            crate::schedule_pointer_repass(id);
328                        }
329                    }
330                }
331                InvalidationKind::Semantics => {
332                    self.request_semantics_update();
333                }
334                InvalidationKind::Focus => {
335                    if has_capability(NodeCapabilities::FOCUS) {
336                        self.mark_needs_focus_sync();
337                        crate::request_focus_invalidation();
338                        if let Some(id) = self.id.get() {
339                            crate::schedule_focus_invalidation(id);
340                        }
341                    }
342                }
343            }
344        }
345    }
346
347    /// The grid this node was composed against.
348    pub fn density(&self) -> crate::density::Density {
349        self.density
350    }
351
352    /// Records the grid the composition provided, re-measuring if it moved.
353    pub fn set_density(&mut self, density: crate::density::Density) {
354        if self.density != density {
355            self.density = density;
356            self.parts.cache().clear();
357            self.modifier_slices_dirty.set(true);
358            self.mark_needs_measure();
359        }
360    }
361
362    /// Updates relative modifier placement for this node's composition scope.
363    pub fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
364        if self.modifier_chain.set_layout_direction(direction) {
365            self.parts.cache().clear();
366            self.modifier_slices_dirty.set(true);
367            self.mark_needs_measure();
368        }
369    }
370
371    /// Replaces the measure policy and schedules a measure. Does nothing if `policy` is the same
372    /// `Rc` as the current one.
373    pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
374        if !Rc::ptr_eq(&self.measure_policy, &policy) {
375            self.measure_policy = policy;
376            self.parts.cache().clear();
377            self.mark_needs_measure();
378            if let Some(id) = self.id.get() {
379                cranpose_core::bubble_measure_dirty_in_composer(id);
380            }
381        }
382    }
383
384    /// Mark this node as needing measure. Also marks it as needing layout.
385    pub fn mark_needs_measure(&self) {
386        self.parts.cache().mark_needs_measure();
387    }
388
389    /// Mark this node as needing layout (but not necessarily measure).
390    pub fn mark_needs_layout(&self) {
391        self.parts.cache().mark_needs_layout();
392    }
393
394    /// Mark this node as needing redraw without forcing measure/layout.
395    pub fn mark_needs_redraw(&self) {
396        self.needs_redraw.set(true);
397        if let Some(id) = self.id.get() {
398            crate::schedule_draw_repass(id);
399        }
400        crate::request_render_invalidation();
401    }
402
403    /// Check if this node needs measure.
404    pub fn needs_measure(&self) -> bool {
405        self.parts.cache().dirt().measure
406    }
407
408    /// Check if this node needs layout.
409    pub fn needs_layout(&self) -> bool {
410        self.parts.cache().dirt().layout
411    }
412
413    /// Mark this node as needing semantics recomputation.
414    pub fn mark_needs_semantics(&self) {
415        self.needs_semantics.set(true);
416        self.forget_semantics_reach();
417    }
418
419    pub(crate) fn clear_needs_semantics(&self) {
420        self.needs_semantics.set(false);
421        self.descendant_needs_semantics.set(false);
422    }
423
424    /// Returns true when this node's semantics or a descendant's need to be
425    /// recomputed.
426    pub fn needs_semantics(&self) -> bool {
427        self.needs_semantics.get() || self.descendant_needs_semantics.get()
428    }
429
430    /// Whether this node's own semantics changed since the tree last read
431    /// them, as opposed to only a descendant's.
432    pub(crate) fn semantics_changed(&self) -> bool {
433        self.needs_semantics.get()
434    }
435
436    /// Returns true when this node requested a redraw since the last render pass.
437    pub fn needs_redraw(&self) -> bool {
438        self.needs_redraw.get()
439    }
440
441    /// Clears the pending redraw flag once the node has been drawn.
442    pub fn clear_needs_redraw(&self) {
443        self.needs_redraw.set(false);
444    }
445
446    fn request_semantics_update(&self) {
447        let already_dirty = self.needs_semantics.replace(true);
448        if already_dirty {
449            return;
450        }
451
452        if let Some(id) = self.id.get() {
453            cranpose_core::queue_semantics_invalidation(id);
454        }
455    }
456
457    pub(crate) fn clear_needs_measure(&self) {
458        self.parts.cache().clear_needs_measure();
459    }
460
461    /// Clears the node's layout flag and its descendants': a layout pass
462    /// visited them.
463    pub(crate) fn clear_needs_layout(&self) {
464        self.parts.cache().clear_layout_dirt();
465    }
466
467    /// Marks this node as needing a fresh pointer-input pass.
468    pub fn mark_needs_pointer_pass(&self) {
469        self.needs_pointer_pass.set(true);
470    }
471
472    /// Returns true when pointer-input state needs to be recomputed.
473    pub fn needs_pointer_pass(&self) -> bool {
474        self.needs_pointer_pass.get()
475    }
476
477    /// Clears the pointer-input dirty flag after hosts service it.
478    pub fn clear_needs_pointer_pass(&self) {
479        self.needs_pointer_pass.set(false);
480    }
481
482    /// Marks this node as needing a focus synchronization.
483    pub fn mark_needs_focus_sync(&self) {
484        self.needs_focus_sync.set(true);
485    }
486
487    /// Returns true when focus state needs to be synchronized.
488    pub fn needs_focus_sync(&self) -> bool {
489        self.needs_focus_sync.get()
490    }
491
492    /// Clears the focus dirty flag after the focus manager processes it.
493    pub fn clear_needs_focus_sync(&self) {
494        self.needs_focus_sync.set(false);
495    }
496
497    /// Set this node's ID (called by applier after creation).
498    pub fn set_node_id(&mut self, id: NodeId) {
499        // A redraw asked for before the node had an id went to no draw
500        // repass.
501        if self.needs_redraw.get() {
502            crate::render_state::note_retained_redraw(id);
503        }
504        let previous = self.id.replace(Some(id));
505        if let Some(existing) = previous
506            && let Some(owner_context_id) = self.owner_context_id.take()
507        {
508            unregister_layout_node(owner_context_id, existing);
509        }
510        self.parts.layout_state().borrow_mut().set_node_id(id);
511        // The new entry holds the node's parent, capabilities and modifier
512        // locals as they are now.
513        let owner_context_id = register_layout_node(id, self);
514        self.owner_context_id.set(Some(owner_context_id));
515
516        let mut context = self.modifier_chain.context(Some(id));
517        self.modifier_chain.move_to_node(previous, &mut context);
518        let invalidations = context.take_invalidations();
519        self.dispatch_modifier_invalidations_with_prev(
520            &invalidations,
521            NodeCapabilities::empty(),
522            false,
523        );
524        // The slices carry the node's id; they are collected again when next read.
525        self.modifier_slices_dirty.set(true);
526    }
527
528    /// Get this node's ID.
529    pub fn node_id(&self) -> Option<NodeId> {
530        self.id.get()
531    }
532
533    /// Set this node's parent (called when node is added as child).
534    /// Sets both folded_parent (direct) and parent (first non-virtual ancestor for bubbling).
535    pub fn set_parent(&self, parent: NodeId) {
536        // The registry holds the parent the node has; the same one again
537        // changes nothing.
538        if self.parent.get() == Some(parent) && self.folded_parent.get() == Some(parent) {
539            return;
540        }
541        self.folded_parent.set(Some(parent));
542        self.parent.set(Some(parent));
543        self.refresh_registry_state();
544    }
545
546    /// Clear this node's parent (called when node is removed from parent).
547    pub fn clear_parent(&self) {
548        self.folded_parent.set(None);
549        self.parent.set(None);
550        self.refresh_registry_state();
551    }
552
553    /// Get this node's parent for dirty flag bubbling (may skip virtual nodes).
554    pub fn parent(&self) -> Option<NodeId> {
555        self.parent.get()
556    }
557
558    /// Get this node's direct parent (may be a virtual node).
559    pub fn folded_parent(&self) -> Option<NodeId> {
560        self.folded_parent.get()
561    }
562
563    pub(crate) fn cache(&self) -> &NodeCache {
564        self.parts.cache()
565    }
566
567    /// Returns the padding, layout properties and offset that the modifier chain resolved to.
568    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
569        self.modifier_chain.resolved_modifiers()
570    }
571
572    /// What the chain tells the parent's measure policy about this node.
573    pub(crate) fn parent_data(&self) -> cranpose_ui_layout::ParentData {
574        self.modifier_chain.parent_data()
575    }
576
577    /// Returns the union of the capabilities of all nodes in the modifier chain.
578    pub fn modifier_capabilities(&self) -> NodeCapabilities {
579        self.modifier_chain.capabilities()
580    }
581
582    /// Whether this node's modifier chain makes it the root of a separate
583    /// window. Its parent lays out as if it had no size and its parent's
584    /// scene skips it; its own scene starts here.
585    pub fn is_window_root(&self) -> bool {
586        self.modifier_capabilities()
587            .contains(NodeCapabilities::WINDOW_ROOT)
588    }
589
590    /// Returns the capabilities of the chain's nodes counted from the head of the chain. Compose's
591    /// `aggregateChildKindSet`.
592    pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
593        self.modifier_chain.aggregate_child_capabilities()
594    }
595
596    /// Turns debug logging of the modifier chain on or off.
597    pub fn set_debug_modifiers(&mut self, enabled: bool) {
598        self.debug_modifiers.set(enabled);
599        self.modifier_chain.set_debug_logging(enabled);
600    }
601
602    /// Returns whether debug logging of the modifier chain is on.
603    pub fn debug_modifiers_enabled(&self) -> bool {
604        self.debug_modifiers.get()
605    }
606
607    /// The node's modifier locals, or `None` while its modifiers have never
608    /// provided or read one.
609    pub fn modifier_locals_handle(&self) -> Option<ModifierLocalsHandle> {
610        self.modifier_chain.modifier_locals_handle()
611    }
612
613    /// Returns whether the modifier chain contains layout modifier nodes.
614    pub fn has_layout_modifier_nodes(&self) -> bool {
615        self.modifier_capabilities()
616            .contains(NodeCapabilities::LAYOUT)
617    }
618
619    /// Returns whether the modifier chain contains draw modifier nodes.
620    pub fn has_draw_modifier_nodes(&self) -> bool {
621        self.modifier_capabilities()
622            .contains(NodeCapabilities::DRAW)
623    }
624
625    /// Returns whether the modifier chain contains pointer input modifier nodes.
626    pub fn has_pointer_input_modifier_nodes(&self) -> bool {
627        self.modifier_capabilities()
628            .contains(NodeCapabilities::POINTER_INPUT)
629    }
630
631    /// Returns whether the modifier chain contains semantics modifier nodes.
632    pub fn has_semantics_modifier_nodes(&self) -> bool {
633        self.modifier_capabilities()
634            .contains(NodeCapabilities::SEMANTICS)
635    }
636
637    /// Returns whether the modifier chain contains focus modifier nodes.
638    pub fn has_focus_modifier_nodes(&self) -> bool {
639        self.modifier_capabilities()
640            .contains(NodeCapabilities::FOCUS)
641    }
642
643    fn refresh_registry_state(&self) {
644        if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
645            let parent = self.parent();
646            let capabilities = self.modifier_child_capabilities();
647            let modifier_locals = self.modifier_locals_handle();
648            let _ = crate::render_state::with_layout_node_registry_by_app_context(
649                owner_context_id,
650                |registry| {
651                    registry.update_entry(id, parent, capabilities, modifier_locals);
652                },
653            );
654        }
655    }
656
657    /// Returns the draw commands, pointer input handlers, text and graphics layer data collected
658    /// from the modifier nodes. The snapshot is rebuilt after the chain changed.
659    pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
660        if self.modifier_slices_dirty.get() {
661            self.update_modifier_slices_cache();
662        }
663        self.modifier_slices_snapshot.get()
664    }
665
666    /// Returns a clone of the current layout state.
667    pub fn layout_state(&self) -> LayoutState {
668        *self.parts.layout_state().borrow()
669    }
670
671    /// Returns the measured size of this node.
672    pub fn measured_size(&self) -> Size {
673        self.parts.layout_state().borrow().size()
674    }
675
676    /// Returns the position of this node relative to its parent.
677    pub fn position(&self) -> Point {
678        self.parts.layout_state().borrow().position()
679    }
680
681    /// Returns true if this node has been placed in the current layout pass.
682    pub fn is_placed(&self) -> bool {
683        self.parts.layout_state().borrow().is_placed()
684    }
685
686    /// Updates the measured size of this node. Called during measurement.
687    /// [`LayoutState::set_size`] self-reports actual changes to the scene
688    /// phase.
689    pub fn set_measured_size(&self, size: Size, context: &AppContext) {
690        self.parts
691            .layout_state()
692            .borrow_mut()
693            .set_size(size, context);
694    }
695
696    // Updates the position of this node, which `placer` placed. Called
697    // during placement. `LayoutState::place` self-reports actual moves to
698    // the scene phase.
699    pub(crate) fn place(&self, at: ChildPlacement, placer: Option<NodeId>, context: &AppContext) {
700        self.parts
701            .layout_state()
702            .borrow_mut()
703            .place_child(at, placer, context);
704    }
705
706    /// Records the content offset (e.g. from padding).
707    pub fn set_content_offset(&self, offset: Point) {
708        self.parts
709            .layout_state()
710            .borrow_mut()
711            .set_content_offset(offset);
712    }
713
714    /// Clears the is_placed flag. Called at the start of a layout pass.
715    pub fn clear_placed(&self, context: &AppContext) {
716        self.parts.layout_state().borrow_mut().clear_placed(context);
717    }
718
719    fn note_semantics_layout_change(&self) {
720        if let Ok(mut state) = self.parts.layout_state().try_borrow_mut() {
721            state.note_semantics_change();
722        }
723    }
724
725    /// Returns the semantics that the modifier chain reports, or `None` if it reports none.
726    pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
727        crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
728    }
729
730    /// Drops the cached reach and queues the node for the modal count.
731    fn forget_semantics_reach(&self) {
732        self.semantics_reach.set(None);
733        crate::modal_nodes::reach_changed(self.id.get());
734    }
735
736    /// Whether this node's modifiers make it modal or hidden.
737    pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
738        // A chain without semantics reaches nothing, known without touching it.
739        if !self
740            .modifier_capabilities()
741            .contains(NodeCapabilities::SEMANTICS)
742        {
743            return cranpose_foundation::SemanticsReach::default();
744        }
745        if let Some(reach) = self.semantics_reach.get() {
746            return reach;
747        }
748        let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
749        self.semantics_reach.set(Some(reach));
750        reach
751    }
752
753    pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
754        &self.modifier_chain
755    }
756
757    /// Runs `read` on the node's layout state, without a copy of it.
758    pub fn with_layout_state<R>(&self, read: impl FnOnce(&LayoutState) -> R) -> R {
759        read(&self.parts.layout_state().borrow())
760    }
761
762    pub(crate) fn layout_parts(&self) -> &Rc<LayoutParts> {
763        &self.parts
764    }
765
766    pub(crate) fn layout_runtime_state(&self) -> &RefCell<LayoutRuntimeState> {
767        self.parts.runtime_state()
768    }
769
770    #[cfg(test)]
771    pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
772        self.parts.runtime_state().borrow().debug_stats()
773    }
774}
775impl Clone for LayoutNode {
776    fn clone(&self) -> Self {
777        let mut node = Self {
778            #[cfg(feature = "inspection")]
779            source_trace: Rc::clone(&self.source_trace),
780            modifier: self.modifier.clone(),
781            modifier_chain: ModifierChainHandle::for_layout_direction(
782                self.modifier_chain.layout_direction(),
783            ),
784            measure_policy: Rc::clone(&self.measure_policy),
785            density: self.density,
786            children: self.children.clone(),
787            needs_semantics: Cell::new(self.needs_semantics.get()),
788            descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
789            semantics_reach: Cell::new(None),
790            needs_redraw: Cell::new(self.needs_redraw.get()),
791            needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
792            needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
793            parent: NodeIdCell::new(self.parent.get()),
794            folded_parent: NodeIdCell::new(self.folded_parent.get()),
795            id: NodeIdCell::new(None),
796            owner_context_id: Cell::new(None),
797            debug_modifiers: Cell::new(self.debug_modifiers.get()),
798            is_virtual: self.is_virtual,
799            modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
800            modifier_slices_dirty: Cell::new(true),
801            parts: Rc::clone(&self.parts),
802        };
803        node.sync_modifier_chain(None);
804        node
805    }
806}
807
808impl Node for LayoutNode {
809    fn mount(&mut self) {
810        self.modifier_chain.remount(self.id.get());
811        crate::modal_nodes::reach_changed(self.id.get());
812    }
813
814    fn unmount(&mut self) {
815        self.modifier_chain.chain_mut().detach_nodes();
816    }
817
818    fn set_node_id(&mut self, id: NodeId) {
819        Self::set_node_id(self, id);
820    }
821
822    fn insert_child(&mut self, child: NodeId) -> bool {
823        if self.children.contains(&child) {
824            return false;
825        }
826        self.children.push(child);
827        self.parts.cache().clear();
828        self.mark_needs_measure();
829        self.note_semantics_layout_change();
830        true
831    }
832
833    fn remove_child(&mut self, child: NodeId) -> bool {
834        let before = self.children.len();
835        self.children.retain(|&id| id != child);
836        let removed = self.children.len() < before;
837        if removed {
838            self.parts.cache().clear();
839            self.mark_needs_measure();
840            self.note_semantics_layout_change();
841        }
842        removed
843    }
844
845    fn move_child(&mut self, from: usize, to: usize) {
846        if from == to || from >= self.children.len() {
847            return;
848        }
849        let child = self.children.remove(from);
850        let target = to.min(self.children.len());
851        self.children.insert(target, child);
852        self.parts.cache().clear();
853        self.mark_needs_measure();
854        self.note_semantics_layout_change();
855    }
856
857    fn update_children(&mut self, children: &[NodeId]) {
858        self.children.clear();
859        self.children.extend_from_slice(children);
860        self.parts.cache().clear();
861        self.mark_needs_measure();
862        self.note_semantics_layout_change();
863    }
864
865    fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
866        out.clear();
867        out.extend_from_slice(&self.children);
868    }
869
870    fn for_each_child(&self, f: &mut dyn FnMut(NodeId)) {
871        for &child in &self.children {
872            f(child);
873        }
874    }
875
876    fn owned_child_index(&self, child: NodeId) -> Option<usize> {
877        self.children.iter().position(|&id| id == child)
878    }
879
880    fn on_attached_to_parent(&mut self, parent: NodeId) {
881        self.set_parent(parent);
882    }
883
884    fn on_removed_from_parent(&mut self) {
885        self.clear_parent();
886    }
887
888    fn parent(&self) -> Option<NodeId> {
889        self.parent.get()
890    }
891
892    fn mark_needs_layout(&self) {
893        Self::mark_needs_layout(self);
894    }
895
896    fn needs_layout(&self) -> bool {
897        Self::needs_layout(self)
898    }
899
900    fn mark_needs_measure(&self) {
901        Self::mark_needs_measure(self);
902    }
903
904    fn needs_measure(&self) -> bool {
905        Self::needs_measure(self)
906    }
907
908    fn mark_descendant_needs_layout(&self, measure: bool) {
909        self.parts.cache().mark_descendant_needs_layout(measure);
910    }
911
912    fn descendant_needs_layout(&self) -> bool {
913        self.parts.cache().dirt().descendant.layout
914    }
915
916    fn is_virtual(&self) -> bool {
917        self.is_virtual
918    }
919
920    fn descendant_needs_measure(&self) -> bool {
921        self.parts.cache().dirt().descendant.measure
922    }
923
924    fn mark_needs_semantics(&self) {
925        self.needs_semantics.set(true);
926        self.forget_semantics_reach();
927    }
928
929    fn mark_descendant_needs_semantics(&self) {
930        self.descendant_needs_semantics.set(true);
931    }
932
933    fn needs_semantics(&self) -> bool {
934        Self::needs_semantics(self)
935    }
936
937    fn set_parent_for_bubbling(&mut self, parent: NodeId) {
938        if self.parent.get().is_none() {
939            self.parent.set(Some(parent));
940        }
941    }
942
943    fn recycle_key(&self) -> Option<TypeId> {
944        Some(TypeId::of::<Self>())
945    }
946
947    fn recycle_pool_limit(&self) -> Option<usize> {
948        Some(super::node_pool_limit::node_pool_limit())
949    }
950
951    fn prepare_for_recycle(&mut self) {
952        *self = Self::new_recycled_shell(self.is_virtual);
953    }
954
955    fn rehouse_for_recycle(&self) -> Option<Box<dyn Node>> {
956        Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
957    }
958
959    fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn Node>> {
960        // The whole node moves into the new box. Its modifier nodes keep
961        // what the user is doing with it, such as a focus, a held press or an
962        // IME composition, and its caches and registry entry stay true for
963        // it. Only its child list, which a spike of children may have grown,
964        // is cut to size. The shell left behind has no id to unregister.
965        let mut node = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
966        node.children.shrink_to_fit();
967        Some(Box::new(node))
968    }
969}
970
971impl Drop for LayoutNode {
972    fn drop(&mut self) {
973        self.parts.cache().advance_revision();
974        if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
975            unregister_layout_node(owner_context_id, id);
976        }
977    }
978}
979
980#[cfg(test)]
981#[path = "tests/layout_node_tests.rs"]
982mod tests;