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