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#[derive(Clone, Debug, Default)]
73pub struct LayoutState {
74 size: Size,
75 position: Point,
76 is_placed: bool,
77 node_id: Option<NodeId>,
78 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 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 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 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 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 #[cfg(feature = "inspection")]
269 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
270 pub modifier: Modifier,
271 modifier_chain: ModifierChainHandle,
272 resolved_modifiers: ResolvedModifiers,
273 modifier_capabilities: NodeCapabilities,
274 modifier_child_capabilities: NodeCapabilities,
275 pub measure_policy: Rc<dyn MeasurePolicy>,
276 density: crate::density::Density,
277 pub children: Vec<NodeId>,
279 cache: LayoutNodeCacheHandles,
280 needs_measure: Cell<bool>,
281 needs_layout: Cell<bool>,
282 needs_semantics: Cell<bool>,
283 descendant_needs_semantics: Cell<bool>,
286 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
290 needs_redraw: Cell<bool>,
291 needs_pointer_pass: Cell<bool>,
292 needs_focus_sync: Cell<bool>,
293 parent: Cell<Option<NodeId>>,
294 folded_parent: Cell<Option<NodeId>>,
295 id: Cell<Option<NodeId>>,
296 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
297 debug_modifiers: Cell<bool>,
298 is_virtual: bool,
299 virtual_children_count: Cell<usize>,
300
301 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
302 modifier_slices_dirty: Cell<bool>,
303
304 layout_state: Rc<RefCell<LayoutState>>,
305 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
306 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
309}
310
311pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
312
313thread_local! {
314 static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
315 Rc::new(crate::layout::policies::EmptyMeasurePolicy);
316}
317
318fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
319 EMPTY_MEASURE_POLICY.with(Rc::clone)
320}
321
322impl LayoutNode {
323 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
324 Self::new_with_virtual(modifier, measure_policy, false)
325 }
326
327 pub fn new_virtual() -> Self {
330 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
331 }
332
333 fn new_recycled_shell(is_virtual: bool) -> Self {
334 let mut shell =
335 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
336 shell.needs_measure.set(false);
337 shell.needs_layout.set(false);
338 shell.needs_semantics.set(false);
339 shell.descendant_needs_semantics.set(false);
340 shell.needs_redraw.set(false);
341 shell.needs_pointer_pass.set(false);
342 shell.needs_focus_sync.set(false);
343 shell.parent.set(None);
344 shell.folded_parent.set(None);
345 shell.id.set(None);
346 shell.owner_context_id.set(None);
347 shell.debug_modifiers.set(false);
348 shell.virtual_children_count.set(0);
349 shell.cache = LayoutNodeCacheHandles::default();
350 shell.modifier_slices_snapshot = RefCell::new(Rc::default());
351 shell.modifier_slices_dirty = Cell::new(true);
352 shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
353 shell.layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::default()));
354 shell.coordinator_geometry = Rc::default();
355 shell
356 }
357
358 fn new_with_virtual(
359 modifier: Modifier,
360 measure_policy: Rc<dyn MeasurePolicy>,
361 is_virtual: bool,
362 ) -> Self {
363 let mut node = Self {
364 #[cfg(feature = "inspection")]
365 source_trace: cranpose_core::source_trace::current_source_trace(),
366 modifier,
367 modifier_chain: ModifierChainHandle::new(),
368 resolved_modifiers: ResolvedModifiers::default(),
369 modifier_capabilities: NodeCapabilities::default(),
370 modifier_child_capabilities: NodeCapabilities::default(),
371 measure_policy,
372 density: crate::density::Density::default(),
373 children: Vec::new(),
374 cache: LayoutNodeCacheHandles::default(),
375 needs_measure: Cell::new(true),
376 needs_layout: Cell::new(true),
377 needs_semantics: Cell::new(true),
378 descendant_needs_semantics: Cell::new(false),
379 semantics_reach: Cell::new(None),
380 needs_redraw: Cell::new(true),
381 needs_pointer_pass: Cell::new(false),
382 needs_focus_sync: Cell::new(false),
383 parent: Cell::new(None),
384 folded_parent: Cell::new(None),
385 id: Cell::new(None),
386 owner_context_id: Cell::new(None),
387 debug_modifiers: Cell::new(false),
388 is_virtual,
389 virtual_children_count: Cell::new(0),
390 modifier_slices_snapshot: RefCell::new(Rc::default()),
391 modifier_slices_dirty: Cell::new(true),
392 layout_state: Rc::new(RefCell::new(LayoutState::default())),
393 layout_runtime_state: Rc::new(RefCell::new(LayoutRuntimeState::default())),
394 coordinator_geometry: Rc::default(),
395 };
396 node.sync_modifier_chain();
397 node
398 }
399
400 pub fn set_modifier(&mut self, modifier: Modifier) {
401 #[cfg(feature = "inspection")]
402 {
403 let trace = cranpose_core::source_trace::current_source_trace();
404 if !trace.is_empty() {
405 self.source_trace = trace;
406 }
407 }
408 if self.modifier == modifier
412 && !self
413 .modifier_capabilities
414 .contains(NodeCapabilities::MODIFIER_LOCALS)
415 {
416 return;
417 }
418 let modifier_changed = !self.modifier.structural_eq(&modifier);
419 self.modifier = modifier;
420 self.sync_modifier_chain();
421 if modifier_changed {
422 self.cache.clear();
423 self.request_semantics_update();
424 }
425 }
426
427 fn sync_modifier_chain(&mut self) {
428 let prev_caps = self.modifier_capabilities;
429 let start_parent = self.parent();
430 let mut resolver = move |token: &ModifierLocalToken| {
431 resolve_modifier_local_from_parent_chain(start_parent, token)
432 };
433 self.modifier_chain
434 .set_debug_logging(self.debug_modifiers.get());
435 self.modifier_chain.set_node_id(self.id.get());
436 let modifier_local_invalidations = self
437 .modifier_chain
438 .update_with_resolver(&self.modifier, &mut resolver);
439 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
440 self.modifier_capabilities = self.modifier_chain.capabilities();
441 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
442 self.forget_semantics_reach();
443 self.modifier_slices_dirty.set(true);
444
445 let mut invalidations = self.modifier_chain.take_invalidations();
446 invalidations.extend(modifier_local_invalidations);
447 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps);
448 self.refresh_registry_state();
449 }
450
451 fn update_modifier_slices_cache(&self) {
452 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
453 crate::modifier::collect_modifier_slices_into_shared(
454 self.modifier_chain.chain(),
455 &mut snapshot,
456 &self.coordinator_geometry,
457 self.density.density(),
458 );
459 self.modifier_slices_dirty.set(false);
460 }
461
462 pub(crate) fn mark_modifier_slices_dirty(&self) {
463 self.modifier_slices_dirty.set(true);
464 }
465
466 #[cfg(test)]
467 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
468 self.dispatch_modifier_invalidations_with_prev(invalidations, NodeCapabilities::empty());
469 }
470
471 fn dispatch_modifier_invalidations_with_prev(
472 &self,
473 invalidations: &[ModifierInvalidation],
474 prev_caps: NodeCapabilities,
475 ) {
476 let curr_caps = self.modifier_capabilities;
477 for invalidation in invalidations {
478 self.modifier_slices_dirty.set(true);
479 let has_capability =
480 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
481 match invalidation.kind() {
482 InvalidationKind::Layout => {
483 if has_capability(NodeCapabilities::LAYOUT) {
484 self.mark_needs_measure();
485 if let Some(id) = self.id.get() {
486 log_layout_invalidation_dispatch(
487 id,
488 invalidation,
489 curr_caps,
490 prev_caps,
491 &self.modifier,
492 );
493 let inside_composition =
494 cranpose_core::composer_context::try_with_composer(|_| ())
495 .is_some();
496 if inside_composition {
497 cranpose_core::bubble_measure_dirty_in_composer(id);
498 } else {
499 crate::schedule_layout_repass(id);
500 }
501 }
502 }
503 }
504 InvalidationKind::Draw => {
505 if has_capability(NodeCapabilities::DRAW)
506 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
507 {
508 self.mark_needs_redraw();
509 }
510 }
511 InvalidationKind::PointerInput => {
512 if has_capability(NodeCapabilities::POINTER_INPUT) {
513 self.mark_needs_pointer_pass();
514 crate::request_pointer_invalidation();
515 if let Some(id) = self.id.get() {
516 crate::schedule_pointer_repass(id);
517 }
518 }
519 }
520 InvalidationKind::Semantics => {
521 self.request_semantics_update();
522 }
523 InvalidationKind::Focus => {
524 if has_capability(NodeCapabilities::FOCUS) {
525 self.mark_needs_focus_sync();
526 crate::request_focus_invalidation();
527 if let Some(id) = self.id.get() {
528 crate::schedule_focus_invalidation(id);
529 }
530 }
531 }
532 }
533 }
534 }
535
536 pub fn density(&self) -> crate::density::Density {
538 self.density
539 }
540
541 pub fn set_density(&mut self, density: crate::density::Density) {
543 if self.density != density {
544 self.density = density;
545 self.cache.clear();
546 self.modifier_slices_dirty.set(true);
547 self.mark_needs_measure();
548 }
549 }
550
551 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
552 if !Rc::ptr_eq(&self.measure_policy, &policy) {
553 self.measure_policy = policy;
554 self.cache.clear();
555 self.mark_needs_measure();
556 if let Some(id) = self.id.get() {
557 cranpose_core::bubble_measure_dirty_in_composer(id);
558 }
559 }
560 }
561
562 pub fn mark_needs_measure(&self) {
564 self.needs_measure.set(true);
565 self.needs_layout.set(true);
566 }
567
568 pub fn mark_needs_layout(&self) {
570 self.needs_layout.set(true);
571 }
572
573 pub fn mark_needs_redraw(&self) {
575 self.needs_redraw.set(true);
576 if let Some(id) = self.id.get() {
577 crate::schedule_draw_repass(id);
578 }
579 crate::request_render_invalidation();
580 }
581
582 pub fn needs_measure(&self) -> bool {
584 self.needs_measure.get()
585 }
586
587 pub fn needs_layout(&self) -> bool {
589 self.needs_layout.get()
590 }
591
592 pub fn mark_needs_semantics(&self) {
594 self.needs_semantics.set(true);
595 self.forget_semantics_reach();
596 }
597
598 pub(crate) fn clear_needs_semantics(&self) {
599 self.needs_semantics.set(false);
600 self.descendant_needs_semantics.set(false);
601 }
602
603 pub fn needs_semantics(&self) -> bool {
606 self.needs_semantics.get() || self.descendant_needs_semantics.get()
607 }
608
609 pub(crate) fn semantics_changed(&self) -> bool {
612 self.needs_semantics.get()
613 }
614
615 pub fn needs_redraw(&self) -> bool {
617 self.needs_redraw.get()
618 }
619
620 pub fn clear_needs_redraw(&self) {
621 self.needs_redraw.set(false);
622 }
623
624 fn request_semantics_update(&self) {
625 let already_dirty = self.needs_semantics.replace(true);
626 if already_dirty {
627 return;
628 }
629
630 if let Some(id) = self.id.get() {
631 cranpose_core::queue_semantics_invalidation(id);
632 }
633 }
634
635 pub(crate) fn clear_needs_measure(&self) {
636 self.needs_measure.set(false);
637 }
638
639 pub(crate) fn clear_needs_layout(&self) {
640 self.needs_layout.set(false);
641 }
642
643 pub fn mark_needs_pointer_pass(&self) {
645 self.needs_pointer_pass.set(true);
646 }
647
648 pub fn needs_pointer_pass(&self) -> bool {
650 self.needs_pointer_pass.get()
651 }
652
653 pub fn clear_needs_pointer_pass(&self) {
655 self.needs_pointer_pass.set(false);
656 }
657
658 pub fn mark_needs_focus_sync(&self) {
660 self.needs_focus_sync.set(true);
661 }
662
663 pub fn needs_focus_sync(&self) -> bool {
665 self.needs_focus_sync.get()
666 }
667
668 pub fn clear_needs_focus_sync(&self) {
670 self.needs_focus_sync.set(false);
671 }
672
673 pub fn set_node_id(&mut self, id: NodeId) {
675 if let Some(existing) = self.id.replace(Some(id))
676 && let Some(owner_context_id) = self.owner_context_id.take()
677 {
678 unregister_layout_node(owner_context_id, existing);
679 }
680 self.layout_state.borrow_mut().set_node_id(id);
681 let owner_context_id = register_layout_node(id, self);
682 self.owner_context_id.set(Some(owner_context_id));
683 self.refresh_registry_state();
684
685 self.modifier_chain.set_node_id(Some(id));
686 let invalidations = self.modifier_chain.take_invalidations();
687 self.dispatch_modifier_invalidations_with_prev(&invalidations, NodeCapabilities::empty());
688 self.modifier_slices_dirty.set(true);
690 }
691
692 pub fn node_id(&self) -> Option<NodeId> {
694 self.id.get()
695 }
696
697 pub fn set_parent(&self, parent: NodeId) {
700 self.folded_parent.set(Some(parent));
701 self.parent.set(Some(parent));
702 self.refresh_registry_state();
703 }
704
705 pub fn clear_parent(&self) {
707 self.folded_parent.set(None);
708 self.parent.set(None);
709 self.refresh_registry_state();
710 }
711
712 pub fn parent(&self) -> Option<NodeId> {
714 self.parent.get()
715 }
716
717 pub fn folded_parent(&self) -> Option<NodeId> {
719 self.folded_parent.get()
720 }
721
722 pub fn is_virtual(&self) -> bool {
724 self.is_virtual
725 }
726
727 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
728 self.cache.clone()
729 }
730
731 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
732 self.resolved_modifiers
733 }
734
735 pub fn modifier_capabilities(&self) -> NodeCapabilities {
736 self.modifier_capabilities
737 }
738
739 pub fn is_window_root(&self) -> bool {
743 self.modifier_capabilities
744 .contains(NodeCapabilities::WINDOW_ROOT)
745 }
746
747 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
748 self.modifier_child_capabilities
749 }
750
751 pub fn set_debug_modifiers(&mut self, enabled: bool) {
752 self.debug_modifiers.set(enabled);
753 self.modifier_chain.set_debug_logging(enabled);
754 }
755
756 pub fn debug_modifiers_enabled(&self) -> bool {
757 self.debug_modifiers.get()
758 }
759
760 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
761 self.modifier_chain.modifier_locals_handle()
762 }
763
764 pub fn has_layout_modifier_nodes(&self) -> bool {
765 self.modifier_capabilities
766 .contains(NodeCapabilities::LAYOUT)
767 }
768
769 pub fn has_draw_modifier_nodes(&self) -> bool {
770 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
771 }
772
773 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
774 self.modifier_capabilities
775 .contains(NodeCapabilities::POINTER_INPUT)
776 }
777
778 pub fn has_semantics_modifier_nodes(&self) -> bool {
779 self.modifier_capabilities
780 .contains(NodeCapabilities::SEMANTICS)
781 }
782
783 pub fn has_focus_modifier_nodes(&self) -> bool {
784 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
785 }
786
787 fn refresh_registry_state(&self) {
788 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
789 let parent = self.parent();
790 let capabilities = self.modifier_child_capabilities();
791 let modifier_locals = self.modifier_locals_handle();
792 let _ = crate::render_state::with_layout_node_registry_by_app_context(
793 owner_context_id,
794 |registry| {
795 registry.update_entry(id, parent, capabilities, modifier_locals);
796 },
797 );
798 }
799 }
800
801 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
802 if self.modifier_slices_dirty.get() {
803 self.update_modifier_slices_cache();
804 }
805 self.modifier_slices_snapshot.borrow().clone()
806 }
807
808 pub fn layout_state(&self) -> LayoutState {
810 self.layout_state.borrow().clone()
811 }
812
813 pub fn measured_size(&self) -> Size {
815 self.layout_state.borrow().size
816 }
817
818 pub fn position(&self) -> Point {
820 self.layout_state.borrow().position
821 }
822
823 pub fn is_placed(&self) -> bool {
825 self.layout_state.borrow().is_placed
826 }
827
828 pub fn set_measured_size(&self, size: Size) {
832 self.layout_state.borrow_mut().set_size(size);
833 }
834
835 pub fn set_position(&self, position: Point) {
838 self.layout_state.borrow_mut().place(position);
839 }
840
841 pub fn set_content_offset(&self, offset: Point) {
843 self.layout_state.borrow_mut().content_offset = offset;
844 }
845
846 pub fn clear_placed(&self) {
848 self.layout_state.borrow_mut().is_placed = false;
849 }
850
851 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
852 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
853 }
854
855 fn forget_semantics_reach(&self) {
857 self.semantics_reach.set(None);
858 crate::modal_nodes::reach_changed(self.id.get());
859 }
860
861 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
863 if !self
865 .modifier_capabilities
866 .contains(NodeCapabilities::SEMANTICS)
867 {
868 return cranpose_foundation::SemanticsReach::default();
869 }
870 if let Some(reach) = self.semantics_reach.get() {
871 return reach;
872 }
873 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
874 self.semantics_reach.set(Some(reach));
875 reach
876 }
877
878 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
879 &self.modifier_chain
880 }
881
882 pub fn with_text_field_modifier_mut<R>(
887 &mut self,
888 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
889 ) -> Option<R> {
890 self.modifier_chain.with_text_field_modifier_mut(f)
891 }
892
893 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
896 self.layout_state.clone()
897 }
898
899 pub(crate) fn coordinator_geometry(&self) -> Rc<crate::modifier::CoordinatorGeometry> {
900 Rc::clone(&self.coordinator_geometry)
901 }
902
903 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
904 self.layout_runtime_state.clone()
905 }
906
907 #[cfg(test)]
908 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
909 self.layout_runtime_state.borrow().debug_stats()
910 }
911}
912impl Clone for LayoutNode {
913 fn clone(&self) -> Self {
914 let mut node = Self {
915 #[cfg(feature = "inspection")]
916 source_trace: self.source_trace.clone(),
917 modifier: self.modifier.clone(),
918 modifier_chain: ModifierChainHandle::new(),
919 resolved_modifiers: ResolvedModifiers::default(),
920 modifier_capabilities: self.modifier_capabilities,
921 modifier_child_capabilities: self.modifier_child_capabilities,
922 measure_policy: self.measure_policy.clone(),
923 density: self.density,
924 children: self.children.clone(),
925 cache: self.cache.clone(),
926 needs_measure: Cell::new(self.needs_measure.get()),
927 needs_layout: Cell::new(self.needs_layout.get()),
928 needs_semantics: Cell::new(self.needs_semantics.get()),
929 descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
930 semantics_reach: Cell::new(None),
931 needs_redraw: Cell::new(self.needs_redraw.get()),
932 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
933 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
934 parent: Cell::new(self.parent.get()),
935 folded_parent: Cell::new(self.folded_parent.get()),
936 id: Cell::new(None),
937 owner_context_id: Cell::new(None),
938 debug_modifiers: Cell::new(self.debug_modifiers.get()),
939 is_virtual: self.is_virtual,
940 virtual_children_count: Cell::new(self.virtual_children_count.get()),
941 modifier_slices_snapshot: RefCell::new(Rc::default()),
942 modifier_slices_dirty: Cell::new(true),
943 layout_state: self.layout_state.clone(),
944 layout_runtime_state: self.layout_runtime_state.clone(),
945 coordinator_geometry: Rc::clone(&self.coordinator_geometry),
946 };
947 node.sync_modifier_chain();
948 node
949 }
950}
951
952impl Node for LayoutNode {
953 fn mount(&mut self) {
954 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
955 chain.repair_chain();
956 chain.attach_nodes(&mut *context);
957 crate::modal_nodes::reach_changed(self.id.get());
958 }
959
960 fn unmount(&mut self) {
961 self.modifier_chain.chain_mut().detach_nodes();
962 }
963
964 fn set_node_id(&mut self, id: NodeId) {
965 LayoutNode::set_node_id(self, id);
966 }
967
968 fn insert_child(&mut self, child: NodeId) -> bool {
969 if self.children.contains(&child) {
970 return false;
971 }
972 if is_virtual_node(child) {
973 let count = self.virtual_children_count.get();
974 self.virtual_children_count.set(count + 1);
975 }
976 self.children.push(child);
977 self.cache.clear();
978 self.mark_needs_measure();
979 true
980 }
981
982 fn remove_child(&mut self, child: NodeId) -> bool {
983 let before = self.children.len();
984 self.children.retain(|&id| id != child);
985 let removed = self.children.len() < before;
986 if removed {
987 if is_virtual_node(child) {
988 let count = self.virtual_children_count.get();
989 if count > 0 {
990 self.virtual_children_count.set(count - 1);
991 }
992 }
993 self.cache.clear();
994 self.mark_needs_measure();
995 }
996 removed
997 }
998
999 fn move_child(&mut self, from: usize, to: usize) {
1000 if from == to || from >= self.children.len() {
1001 return;
1002 }
1003 let child = self.children.remove(from);
1004 let target = to.min(self.children.len());
1005 self.children.insert(target, child);
1006 self.cache.clear();
1007 self.mark_needs_measure();
1008 }
1009
1010 fn update_children(&mut self, children: &[NodeId]) {
1011 self.children.clear();
1012 self.children.extend_from_slice(children);
1013 self.cache.clear();
1014 self.mark_needs_measure();
1015 }
1016
1017 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
1018 out.clear();
1019 out.extend_from_slice(&self.children);
1020 }
1021
1022 fn on_attached_to_parent(&mut self, parent: NodeId) {
1023 self.set_parent(parent);
1024 }
1025
1026 fn on_removed_from_parent(&mut self) {
1027 self.clear_parent();
1028 }
1029
1030 fn parent(&self) -> Option<NodeId> {
1031 self.parent.get()
1032 }
1033
1034 fn mark_needs_layout(&self) {
1035 self.needs_layout.set(true);
1036 }
1037
1038 fn needs_layout(&self) -> bool {
1039 self.needs_layout.get()
1040 }
1041
1042 fn mark_needs_measure(&self) {
1043 self.needs_measure.set(true);
1044 self.needs_layout.set(true);
1045 }
1046
1047 fn needs_measure(&self) -> bool {
1048 self.needs_measure.get()
1049 }
1050
1051 fn mark_needs_semantics(&self) {
1052 self.needs_semantics.set(true);
1053 self.forget_semantics_reach();
1054 }
1055
1056 fn mark_descendant_needs_semantics(&self) {
1057 self.descendant_needs_semantics.set(true);
1058 }
1059
1060 fn needs_semantics(&self) -> bool {
1061 LayoutNode::needs_semantics(self)
1062 }
1063
1064 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1065 if self.parent.get().is_none() {
1066 self.parent.set(Some(parent));
1067 }
1068 }
1069
1070 fn recycle_key(&self) -> Option<TypeId> {
1071 Some(TypeId::of::<Self>())
1072 }
1073
1074 fn recycle_pool_limit(&self) -> Option<usize> {
1075 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1076 }
1077
1078 fn prepare_for_recycle(&mut self) {
1079 *self = Self::new_recycled_shell(self.is_virtual);
1080 }
1081
1082 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1083 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1084 }
1085
1086 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1087 let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1088 let node_id = previous.id.replace(None);
1089 let parent = previous.parent.get();
1090 let folded_parent = previous.folded_parent.get();
1091 let debug_modifiers = previous.debug_modifiers.get();
1092 let needs_measure = previous.needs_measure.get();
1093 let needs_layout = previous.needs_layout.get();
1094 let needs_semantics = previous.needs_semantics.get();
1095 let descendant_needs_semantics = previous.descendant_needs_semantics.get();
1096 let needs_redraw = previous.needs_redraw.get();
1097 let needs_pointer_pass = previous.needs_pointer_pass.get();
1098 let needs_focus_sync = previous.needs_focus_sync.get();
1099 let virtual_children_count = previous.virtual_children_count.get();
1100 let children = previous.children.to_vec();
1101 let modifier = previous.modifier.rehouse_for_live_compaction();
1102 let measure_policy = previous.measure_policy.clone();
1103 let layout_state = previous.layout_state.clone();
1104 let layout_runtime_state = previous.layout_runtime_state.clone();
1105 let coordinator_geometry = Rc::clone(&previous.coordinator_geometry);
1106
1107 previous.modifier_chain.chain_mut().detach_nodes();
1108
1109 let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
1110 compact.children = children;
1111 #[cfg(feature = "inspection")]
1112 {
1113 compact.source_trace = previous.source_trace.clone();
1114 }
1115 compact.parent.set(parent);
1116 compact.folded_parent.set(folded_parent);
1117 compact.id.set(node_id);
1118 compact.debug_modifiers.set(debug_modifiers);
1119 compact.needs_measure.set(needs_measure);
1120 compact.needs_layout.set(needs_layout);
1121 compact.needs_semantics.set(needs_semantics);
1122 compact
1123 .descendant_needs_semantics
1124 .set(descendant_needs_semantics);
1125 compact.needs_redraw.set(needs_redraw);
1126 compact.needs_pointer_pass.set(needs_pointer_pass);
1127 compact.needs_focus_sync.set(needs_focus_sync);
1128 compact.virtual_children_count.set(virtual_children_count);
1129 compact.layout_state = layout_state;
1130 compact.layout_runtime_state = layout_runtime_state;
1131 compact.coordinator_geometry = coordinator_geometry;
1132 compact.sync_modifier_chain();
1133 if let Some(id) = node_id {
1134 let owner_context_id = register_layout_node(id, &compact);
1135 compact.owner_context_id.set(Some(owner_context_id));
1136 }
1137
1138 Some(Box::new(compact))
1139 }
1140}
1141
1142impl Drop for LayoutNode {
1143 fn drop(&mut self) {
1144 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1145 unregister_layout_node(owner_context_id, id);
1146 }
1147 }
1148}
1149
1150const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1151const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1152
1153#[cfg(test)]
1154#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1155struct LayoutNodeRegistryDebugStats {
1156 len: usize,
1157 capacity: usize,
1158}
1159
1160struct LayoutNodeRegistryEntry {
1161 parent: Option<NodeId>,
1162 modifier_child_capabilities: NodeCapabilities,
1163 modifier_locals: ModifierLocalsHandle,
1164 is_virtual: bool,
1165}
1166
1167pub(crate) struct LayoutNodeRegistryState {
1168 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1169 virtual_node_id_counter: Cell<NodeId>,
1170}
1171
1172impl LayoutNodeRegistryState {
1173 pub(crate) fn new() -> Self {
1174 Self {
1175 entries: RefCell::new(HashMap::new()),
1176 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1177 }
1178 }
1179
1180 fn register(&self, id: NodeId, node: &LayoutNode) {
1181 self.entries.borrow_mut().insert(
1182 id,
1183 LayoutNodeRegistryEntry {
1184 parent: node.parent(),
1185 modifier_child_capabilities: node.modifier_child_capabilities(),
1186 modifier_locals: node.modifier_locals_handle(),
1187 is_virtual: node.is_virtual(),
1188 },
1189 );
1190 }
1191
1192 fn unregister(&self, id: NodeId) {
1193 let mut entries = self.entries.borrow_mut();
1194 entries.remove(&id);
1195 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1196 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1197 || entries.capacity()
1198 > entries
1199 .len()
1200 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1201 .saturating_mul(4);
1202 if should_shrink {
1203 let retained = entries
1204 .len()
1205 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1206 let mut rebuilt = HashMap::new();
1207 rebuilt.reserve(retained);
1208 rebuilt.extend(entries.drain());
1209 *entries = rebuilt;
1210 }
1211 }
1212
1213 fn update_entry(
1214 &self,
1215 id: NodeId,
1216 parent: Option<NodeId>,
1217 modifier_child_capabilities: NodeCapabilities,
1218 modifier_locals: ModifierLocalsHandle,
1219 ) {
1220 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1221 entry.parent = parent;
1222 entry.modifier_child_capabilities = modifier_child_capabilities;
1223 entry.modifier_locals = modifier_locals;
1224 }
1225 }
1226
1227 #[cfg(test)]
1228 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1229 let entries = self.entries.borrow();
1230 LayoutNodeRegistryDebugStats {
1231 len: entries.len(),
1232 capacity: entries.capacity(),
1233 }
1234 }
1235
1236 fn is_virtual_node(&self, id: NodeId) -> bool {
1237 self.entries
1238 .borrow()
1239 .get(&id)
1240 .is_some_and(|entry| entry.is_virtual)
1241 }
1242
1243 fn allocate_virtual_node_id(&self) -> NodeId {
1244 let id = self.virtual_node_id_counter.get();
1245 self.virtual_node_id_counter.set(id.wrapping_add(1));
1246 id
1247 }
1248
1249 fn resolve_modifier_local_from_parent_chain(
1250 &self,
1251 start: Option<NodeId>,
1252 token: &ModifierLocalToken,
1253 ) -> Option<ResolvedModifierLocal> {
1254 let mut current = start;
1255 while let Some(parent_id) = current {
1256 let (next_parent, resolved) = {
1257 let entries = self.entries.borrow();
1258 if let Some(entry) = entries.get(&parent_id) {
1259 let resolved = if entry
1260 .modifier_child_capabilities
1261 .contains(NodeCapabilities::MODIFIER_LOCALS)
1262 {
1263 entry
1264 .modifier_locals
1265 .borrow()
1266 .resolve(token)
1267 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
1268 } else {
1269 None
1270 };
1271 (entry.parent, resolved)
1272 } else {
1273 (None, None)
1274 }
1275 };
1276 if let Some(value) = resolved {
1277 return Some(value);
1278 }
1279 current = next_parent;
1280 }
1281 None
1282 }
1283}
1284
1285pub(crate) fn register_layout_node(
1286 id: NodeId,
1287 node: &LayoutNode,
1288) -> crate::render_state::AppContextId {
1289 let owner_context_id = crate::render_state::current_app_context_id();
1290 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1291 owner_context_id,
1292 |registry| {
1293 registry.register(id, node);
1294 },
1295 );
1296 owner_context_id
1297}
1298
1299pub(crate) fn unregister_layout_node(
1300 owner_context_id: crate::render_state::AppContextId,
1301 id: NodeId,
1302) {
1303 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1304 owner_context_id,
1305 |registry| {
1306 registry.unregister(id);
1307 },
1308 );
1309}
1310
1311#[cfg(test)]
1312fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1313 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1314}
1315
1316pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1317 crate::render_state::with_layout_node_registry(|registry| registry.is_virtual_node(id))
1318}
1319
1320pub(crate) fn allocate_virtual_node_id() -> NodeId {
1321 crate::render_state::with_layout_node_registry(
1322 LayoutNodeRegistryState::allocate_virtual_node_id,
1323 )
1324}
1325
1326fn resolve_modifier_local_from_parent_chain(
1327 start: Option<NodeId>,
1328 token: &ModifierLocalToken,
1329) -> Option<ResolvedModifierLocal> {
1330 crate::render_state::with_layout_node_registry(|registry| {
1331 registry.resolve_modifier_local_from_parent_chain(start, token)
1332 })
1333}
1334
1335#[cfg(test)]
1336#[path = "tests/layout_node_tests.rs"]
1337mod tests;