1use crate::{
2 layout::MeasuredNode,
3 modifier::{
4 Modifier, ModifierChainHandle, ModifierLocalSource, ModifierLocalToken,
5 ModifierLocalsHandle, ModifierNodeSlices, Point, ResolvedModifierLocal, ResolvedModifiers,
6 Size,
7 },
8};
9use cranpose_core::{Node, NodeId};
10use cranpose_foundation::{
11 InvalidationKind, ModifierInvalidation, NodeCapabilities, SemanticsConfiguration,
12};
13use cranpose_ui_layout::{Constraints, MeasurePolicy};
14use std::any::TypeId;
15use std::cell::{Cell, RefCell};
16use std::collections::HashMap;
17use std::hash::{Hash, Hasher};
18use std::rc::Rc;
19
20#[derive(Clone, Debug)]
25pub struct LayoutState {
26 pub size: Size,
28 pub position: Point,
30 pub is_placed: bool,
32 pub measurement_constraints: Constraints,
34 pub content_offset: Point,
36}
37
38impl Default for LayoutState {
39 fn default() -> Self {
40 Self {
41 size: Size::default(),
42 position: Point::default(),
43 is_placed: false,
44 measurement_constraints: Constraints {
45 min_width: 0.0,
46 max_width: f32::INFINITY,
47 min_height: 0.0,
48 max_height: f32::INFINITY,
49 },
50 content_offset: Point::default(),
51 }
52 }
53}
54
55#[derive(Clone)]
56struct MeasurementCacheEntry {
57 constraints: Constraints,
58 measured: Rc<MeasuredNode>,
59}
60
61#[derive(Clone, Copy, Debug)]
62pub enum IntrinsicKind {
63 MinWidth(f32),
64 MaxWidth(f32),
65 MinHeight(f32),
66 MaxHeight(f32),
67}
68
69impl IntrinsicKind {
70 fn discriminant(&self) -> u8 {
71 match self {
72 IntrinsicKind::MinWidth(_) => 0,
73 IntrinsicKind::MaxWidth(_) => 1,
74 IntrinsicKind::MinHeight(_) => 2,
75 IntrinsicKind::MaxHeight(_) => 3,
76 }
77 }
78
79 fn value_bits(&self) -> u32 {
80 match self {
81 IntrinsicKind::MinWidth(value)
82 | IntrinsicKind::MaxWidth(value)
83 | IntrinsicKind::MinHeight(value)
84 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
85 }
86 }
87}
88
89impl PartialEq for IntrinsicKind {
90 fn eq(&self, other: &Self) -> bool {
91 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
92 }
93}
94
95impl Eq for IntrinsicKind {}
96
97impl Hash for IntrinsicKind {
98 fn hash<H: Hasher>(&self, state: &mut H) {
99 self.discriminant().hash(state);
100 self.value_bits().hash(state);
101 }
102}
103
104#[derive(Default)]
105struct NodeCacheState {
106 epoch: u64,
107 measurements: Vec<MeasurementCacheEntry>,
108 intrinsics: Vec<(IntrinsicKind, f32)>,
109}
110
111#[derive(Clone, Default)]
112pub(crate) struct LayoutNodeCacheHandles {
113 state: Rc<RefCell<NodeCacheState>>,
114}
115
116impl LayoutNodeCacheHandles {
117 pub(crate) fn clear(&self) {
118 let mut state = self.state.borrow_mut();
119 state.measurements.clear();
120 state.intrinsics.clear();
121 state.epoch = 0;
122 }
123
124 pub(crate) fn activate(&self, epoch: u64) {
125 let mut state = self.state.borrow_mut();
126 if state.epoch != epoch {
127 state.measurements.clear();
128 state.intrinsics.clear();
129 state.epoch = epoch;
130 }
131 }
132
133 pub(crate) fn epoch(&self) -> u64 {
134 self.state.borrow().epoch
135 }
136
137 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
138 let state = self.state.borrow();
139 state
140 .measurements
141 .iter()
142 .find(|entry| entry.constraints == constraints)
143 .map(|entry| Rc::clone(&entry.measured))
144 }
145
146 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
147 let mut state = self.state.borrow_mut();
148 if let Some(entry) = state
149 .measurements
150 .iter_mut()
151 .find(|entry| entry.constraints == constraints)
152 {
153 entry.measured = measured;
154 } else {
155 state.measurements.push(MeasurementCacheEntry {
156 constraints,
157 measured,
158 });
159 }
160 }
161
162 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
163 let state = self.state.borrow();
164 state
165 .intrinsics
166 .iter()
167 .find(|(stored_kind, _)| stored_kind == kind)
168 .map(|(_, value)| *value)
169 }
170
171 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
172 let mut state = self.state.borrow_mut();
173 if let Some((_, existing)) = state
174 .intrinsics
175 .iter_mut()
176 .find(|(stored_kind, _)| stored_kind == &kind)
177 {
178 *existing = value;
179 } else {
180 state.intrinsics.push((kind, value));
181 }
182 }
183}
184
185pub struct LayoutNode {
186 pub modifier: Modifier,
187 modifier_chain: ModifierChainHandle,
188 resolved_modifiers: ResolvedModifiers,
189 modifier_capabilities: NodeCapabilities,
190 modifier_child_capabilities: NodeCapabilities,
191 pub measure_policy: Rc<dyn MeasurePolicy>,
192 pub children: Vec<NodeId>,
194 cache: LayoutNodeCacheHandles,
195 needs_measure: Cell<bool>,
197 needs_layout: Cell<bool>,
198 needs_semantics: Cell<bool>,
199 needs_redraw: Cell<bool>,
200 needs_pointer_pass: Cell<bool>,
201 needs_focus_sync: Cell<bool>,
202 parent: Cell<Option<NodeId>>,
204 folded_parent: Cell<Option<NodeId>>,
206 id: Cell<Option<NodeId>>,
208 debug_modifiers: Cell<bool>,
209 is_virtual: bool,
212 virtual_children_count: Cell<usize>,
214
215 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
216 modifier_slices_dirty: Cell<bool>,
217
218 layout_state: Rc<RefCell<LayoutState>>,
222}
223
224pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
225
226thread_local! {
227 static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
228 Rc::new(crate::layout::policies::EmptyMeasurePolicy);
229}
230
231fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
232 EMPTY_MEASURE_POLICY.with(Rc::clone)
233}
234
235impl LayoutNode {
236 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
237 Self::new_with_virtual(modifier, measure_policy, false)
238 }
239
240 pub fn new_virtual() -> Self {
243 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
244 }
245
246 fn new_recycled_shell(is_virtual: bool) -> Self {
247 let mut shell =
248 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
249 shell.needs_measure.set(false);
250 shell.needs_layout.set(false);
251 shell.needs_semantics.set(false);
252 shell.needs_redraw.set(false);
253 shell.needs_pointer_pass.set(false);
254 shell.needs_focus_sync.set(false);
255 shell.parent.set(None);
256 shell.folded_parent.set(None);
257 shell.id.set(None);
258 shell.debug_modifiers.set(false);
259 shell.virtual_children_count.set(0);
260 shell.cache = LayoutNodeCacheHandles::default();
261 shell.modifier_slices_snapshot = RefCell::new(Rc::default());
262 shell.modifier_slices_dirty = Cell::new(true);
263 shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
264 shell
265 }
266
267 fn new_with_virtual(
268 modifier: Modifier,
269 measure_policy: Rc<dyn MeasurePolicy>,
270 is_virtual: bool,
271 ) -> Self {
272 let mut node = Self {
273 modifier,
274 modifier_chain: ModifierChainHandle::new(),
275 resolved_modifiers: ResolvedModifiers::default(),
276 modifier_capabilities: NodeCapabilities::default(),
277 modifier_child_capabilities: NodeCapabilities::default(),
278 measure_policy,
279 children: Vec::new(),
280 cache: LayoutNodeCacheHandles::default(),
281 needs_measure: Cell::new(true), needs_layout: Cell::new(true), needs_semantics: Cell::new(true), needs_redraw: Cell::new(true), needs_pointer_pass: Cell::new(false),
286 needs_focus_sync: Cell::new(false),
287 parent: Cell::new(None), folded_parent: Cell::new(None), id: Cell::new(None), debug_modifiers: Cell::new(false),
291 is_virtual,
292 virtual_children_count: Cell::new(0),
293 modifier_slices_snapshot: RefCell::new(Rc::default()),
294 modifier_slices_dirty: Cell::new(true),
295 layout_state: Rc::new(RefCell::new(LayoutState::default())),
296 };
297 node.sync_modifier_chain();
298 node
299 }
300
301 pub fn set_modifier(&mut self, modifier: Modifier) {
302 let modifier_changed = !self.modifier.structural_eq(&modifier);
308 self.modifier = modifier;
309 self.sync_modifier_chain();
310 if modifier_changed {
311 self.cache.clear();
312 self.request_semantics_update();
313 }
314 }
315
316 fn sync_modifier_chain(&mut self) {
317 let prev_caps = self.modifier_capabilities;
318 let start_parent = self.parent();
319 let mut resolver = move |token: ModifierLocalToken| {
320 resolve_modifier_local_from_parent_chain(start_parent, token)
321 };
322 self.modifier_chain
323 .set_debug_logging(self.debug_modifiers.get());
324 self.modifier_chain.set_node_id(self.id.get());
325 let modifier_local_invalidations = self
326 .modifier_chain
327 .update_with_resolver(&self.modifier, &mut resolver);
328 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
329 self.modifier_capabilities = self.modifier_chain.capabilities();
330 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
331
332 self.update_modifier_slices_cache();
333
334 let mut invalidations = self.modifier_chain.take_invalidations();
335 invalidations.extend(modifier_local_invalidations);
336 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps);
337 self.refresh_registry_state();
338 }
339
340 fn update_modifier_slices_cache(&self) {
341 use crate::modifier::collect_modifier_slices_into;
342
343 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
344 collect_modifier_slices_into(self.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
345 self.modifier_slices_dirty.set(false);
346 }
347
348 pub(crate) fn mark_modifier_slices_dirty(&self) {
349 self.modifier_slices_dirty.set(true);
350 }
351
352 #[cfg(test)]
353 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
354 self.dispatch_modifier_invalidations_with_prev(invalidations, NodeCapabilities::empty());
355 }
356
357 fn dispatch_modifier_invalidations_with_prev(
358 &self,
359 invalidations: &[ModifierInvalidation],
360 prev_caps: NodeCapabilities,
361 ) {
362 let curr_caps = self.modifier_capabilities;
363 for invalidation in invalidations {
364 self.modifier_slices_dirty.set(true);
365 match invalidation.kind() {
366 InvalidationKind::Layout => {
367 if curr_caps.contains(NodeCapabilities::LAYOUT)
368 || prev_caps.contains(NodeCapabilities::LAYOUT)
369 {
370 self.mark_needs_measure();
371 if let Some(id) = self.id.get() {
372 let inside_composition =
373 cranpose_core::composer_context::try_with_composer(|_| ())
374 .is_some();
375 if !inside_composition {
376 crate::schedule_layout_repass(id);
377 }
378 }
379 }
380 }
381 InvalidationKind::Draw => {
382 if curr_caps.contains(NodeCapabilities::DRAW)
383 || prev_caps.contains(NodeCapabilities::DRAW)
384 {
385 self.mark_needs_redraw();
386 }
387 }
388 InvalidationKind::PointerInput => {
389 if curr_caps.contains(NodeCapabilities::POINTER_INPUT)
390 || prev_caps.contains(NodeCapabilities::POINTER_INPUT)
391 {
392 self.mark_needs_pointer_pass();
393 crate::request_pointer_invalidation();
394 if let Some(id) = self.id.get() {
396 crate::schedule_pointer_repass(id);
397 }
398 }
399 }
400 InvalidationKind::Semantics => {
401 self.request_semantics_update();
402 }
403 InvalidationKind::Focus => {
404 if curr_caps.contains(NodeCapabilities::FOCUS)
405 || prev_caps.contains(NodeCapabilities::FOCUS)
406 {
407 self.mark_needs_focus_sync();
408 crate::request_focus_invalidation();
409 if let Some(id) = self.id.get() {
411 crate::schedule_focus_invalidation(id);
412 }
413 }
414 }
415 }
416 }
417 }
418
419 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
420 if !Rc::ptr_eq(&self.measure_policy, &policy) {
422 self.measure_policy = policy;
423 self.cache.clear();
424 self.mark_needs_measure();
425 if let Some(id) = self.id.get() {
426 cranpose_core::bubble_measure_dirty_in_composer(id);
427 }
428 }
429 }
430
431 pub fn mark_needs_measure(&self) {
433 self.needs_measure.set(true);
434 self.needs_layout.set(true);
435 }
436
437 pub fn mark_needs_layout(&self) {
439 self.needs_layout.set(true);
440 }
441
442 pub fn mark_needs_redraw(&self) {
444 self.needs_redraw.set(true);
445 if let Some(id) = self.id.get() {
446 crate::schedule_draw_repass(id);
447 }
448 crate::request_render_invalidation();
449 }
450
451 pub fn needs_measure(&self) -> bool {
453 self.needs_measure.get()
454 }
455
456 pub fn needs_layout(&self) -> bool {
458 self.needs_layout.get()
459 }
460
461 pub fn mark_needs_semantics(&self) {
463 self.needs_semantics.set(true);
464 }
465
466 pub(crate) fn clear_needs_semantics(&self) {
468 self.needs_semantics.set(false);
469 }
470
471 pub fn needs_semantics(&self) -> bool {
473 self.needs_semantics.get()
474 }
475
476 pub fn needs_redraw(&self) -> bool {
478 self.needs_redraw.get()
479 }
480
481 pub fn clear_needs_redraw(&self) {
482 self.needs_redraw.set(false);
483 }
484
485 fn request_semantics_update(&self) {
486 let already_dirty = self.needs_semantics.replace(true);
487 if already_dirty {
488 return;
489 }
490
491 if let Some(id) = self.id.get() {
492 cranpose_core::queue_semantics_invalidation(id);
493 }
494 }
495
496 pub(crate) fn clear_needs_measure(&self) {
498 self.needs_measure.set(false);
499 }
500
501 pub(crate) fn clear_needs_layout(&self) {
503 self.needs_layout.set(false);
504 }
505
506 pub fn mark_needs_pointer_pass(&self) {
508 self.needs_pointer_pass.set(true);
509 }
510
511 pub fn needs_pointer_pass(&self) -> bool {
513 self.needs_pointer_pass.get()
514 }
515
516 pub fn clear_needs_pointer_pass(&self) {
518 self.needs_pointer_pass.set(false);
519 }
520
521 pub fn mark_needs_focus_sync(&self) {
523 self.needs_focus_sync.set(true);
524 }
525
526 pub fn needs_focus_sync(&self) -> bool {
528 self.needs_focus_sync.get()
529 }
530
531 pub fn clear_needs_focus_sync(&self) {
533 self.needs_focus_sync.set(false);
534 }
535
536 pub fn set_node_id(&mut self, id: NodeId) {
538 if let Some(existing) = self.id.replace(Some(id)) {
539 unregister_layout_node(existing);
540 }
541 register_layout_node(id, self);
542 self.refresh_registry_state();
543
544 self.modifier_chain.set_node_id(Some(id));
547 self.update_modifier_slices_cache();
548 }
549
550 pub fn node_id(&self) -> Option<NodeId> {
552 self.id.get()
553 }
554
555 pub fn set_parent(&self, parent: NodeId) {
558 self.folded_parent.set(Some(parent));
559 self.parent.set(Some(parent));
562 self.refresh_registry_state();
563 }
564
565 pub fn clear_parent(&self) {
567 self.folded_parent.set(None);
568 self.parent.set(None);
569 self.refresh_registry_state();
570 }
571
572 pub fn parent(&self) -> Option<NodeId> {
574 self.parent.get()
575 }
576
577 pub fn folded_parent(&self) -> Option<NodeId> {
579 self.folded_parent.get()
580 }
581
582 pub fn is_virtual(&self) -> bool {
584 self.is_virtual
585 }
586
587 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
588 self.cache.clone()
589 }
590
591 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
592 self.resolved_modifiers
593 }
594
595 pub fn modifier_capabilities(&self) -> NodeCapabilities {
596 self.modifier_capabilities
597 }
598
599 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
600 self.modifier_child_capabilities
601 }
602
603 pub fn set_debug_modifiers(&mut self, enabled: bool) {
604 self.debug_modifiers.set(enabled);
605 self.modifier_chain.set_debug_logging(enabled);
606 }
607
608 pub fn debug_modifiers_enabled(&self) -> bool {
609 self.debug_modifiers.get()
610 }
611
612 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
613 self.modifier_chain.modifier_locals_handle()
614 }
615
616 pub fn has_layout_modifier_nodes(&self) -> bool {
617 self.modifier_capabilities
618 .contains(NodeCapabilities::LAYOUT)
619 }
620
621 pub fn has_draw_modifier_nodes(&self) -> bool {
622 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
623 }
624
625 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
626 self.modifier_capabilities
627 .contains(NodeCapabilities::POINTER_INPUT)
628 }
629
630 pub fn has_semantics_modifier_nodes(&self) -> bool {
631 self.modifier_capabilities
632 .contains(NodeCapabilities::SEMANTICS)
633 }
634
635 pub fn has_focus_modifier_nodes(&self) -> bool {
636 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
637 }
638
639 fn refresh_registry_state(&self) {
640 if let Some(id) = self.id.get() {
641 let parent = self.parent();
642 let capabilities = self.modifier_child_capabilities();
643 let modifier_locals = self.modifier_locals_handle();
644 LAYOUT_NODE_REGISTRY.with(|registry| {
645 if let Some(entry) = registry.borrow_mut().get_mut(&id) {
646 entry.parent = parent;
647 entry.modifier_child_capabilities = capabilities;
648 entry.modifier_locals = modifier_locals;
649 }
650 });
651 }
652 }
653
654 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
655 if self.modifier_slices_dirty.get() {
656 self.update_modifier_slices_cache();
657 }
658 self.modifier_slices_snapshot.borrow().clone()
659 }
660
661 pub fn layout_state(&self) -> LayoutState {
667 self.layout_state.borrow().clone()
668 }
669
670 pub fn measured_size(&self) -> Size {
672 self.layout_state.borrow().size
673 }
674
675 pub fn position(&self) -> Point {
677 self.layout_state.borrow().position
678 }
679
680 pub fn is_placed(&self) -> bool {
682 self.layout_state.borrow().is_placed
683 }
684
685 pub fn set_measured_size(&self, size: Size) {
687 let mut state = self.layout_state.borrow_mut();
688 state.size = size;
689 }
690
691 pub fn set_position(&self, position: Point) {
693 let mut state = self.layout_state.borrow_mut();
694 state.position = position;
695 state.is_placed = true;
696 }
697
698 pub fn set_measurement_constraints(&self, constraints: Constraints) {
700 self.layout_state.borrow_mut().measurement_constraints = constraints;
701 }
702
703 pub fn set_content_offset(&self, offset: Point) {
705 self.layout_state.borrow_mut().content_offset = offset;
706 }
707
708 pub fn clear_placed(&self) {
710 self.layout_state.borrow_mut().is_placed = false;
711 }
712
713 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
714 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
715 }
716
717 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
719 &self.modifier_chain
720 }
721
722 pub fn with_text_field_modifier_mut<R>(
727 &mut self,
728 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
729 ) -> Option<R> {
730 self.modifier_chain.with_text_field_modifier_mut(f)
731 }
732
733 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
736 self.layout_state.clone()
737 }
738}
739impl Clone for LayoutNode {
740 fn clone(&self) -> Self {
741 let mut node = Self {
742 modifier: self.modifier.clone(),
743 modifier_chain: ModifierChainHandle::new(),
744 resolved_modifiers: ResolvedModifiers::default(),
745 modifier_capabilities: self.modifier_capabilities,
746 modifier_child_capabilities: self.modifier_child_capabilities,
747 measure_policy: self.measure_policy.clone(),
748 children: self.children.clone(),
749 cache: self.cache.clone(),
750 needs_measure: Cell::new(self.needs_measure.get()),
751 needs_layout: Cell::new(self.needs_layout.get()),
752 needs_semantics: Cell::new(self.needs_semantics.get()),
753 needs_redraw: Cell::new(self.needs_redraw.get()),
754 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
755 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
756 parent: Cell::new(self.parent.get()),
757 folded_parent: Cell::new(self.folded_parent.get()),
758 id: Cell::new(None),
759 debug_modifiers: Cell::new(self.debug_modifiers.get()),
760 is_virtual: self.is_virtual,
761 virtual_children_count: Cell::new(self.virtual_children_count.get()),
762 modifier_slices_snapshot: RefCell::new(Rc::default()),
763 modifier_slices_dirty: Cell::new(true),
764 layout_state: self.layout_state.clone(),
766 };
767 node.sync_modifier_chain();
768 node
769 }
770}
771
772impl Node for LayoutNode {
773 fn mount(&mut self) {
774 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
775 chain.repair_chain();
776 chain.attach_nodes(&mut *context);
777 }
778
779 fn unmount(&mut self) {
780 self.modifier_chain.chain_mut().detach_nodes();
781 }
782
783 fn set_node_id(&mut self, id: NodeId) {
784 LayoutNode::set_node_id(self, id);
786 }
787
788 fn insert_child(&mut self, child: NodeId) {
789 if self.children.contains(&child) {
790 return;
791 }
792 if is_virtual_node(child) {
793 let count = self.virtual_children_count.get();
794 self.virtual_children_count.set(count + 1);
795 }
796 self.children.push(child);
797 self.cache.clear();
798 self.mark_needs_measure();
799 }
800
801 fn remove_child(&mut self, child: NodeId) {
802 let before = self.children.len();
803 self.children.retain(|&id| id != child);
804 if self.children.len() < before {
805 if is_virtual_node(child) {
806 let count = self.virtual_children_count.get();
807 if count > 0 {
808 self.virtual_children_count.set(count - 1);
809 }
810 }
811 self.cache.clear();
812 self.mark_needs_measure();
813 }
814 }
815
816 fn move_child(&mut self, from: usize, to: usize) {
817 if from == to || from >= self.children.len() {
818 return;
819 }
820 let child = self.children.remove(from);
821 let target = to.min(self.children.len());
822 self.children.insert(target, child);
823 self.cache.clear();
824 self.mark_needs_measure();
825 }
826
827 fn update_children(&mut self, children: &[NodeId]) {
828 self.children.clear();
829 self.children.extend_from_slice(children);
830 self.cache.clear();
831 self.mark_needs_measure();
832 }
833
834 fn children(&self) -> Vec<NodeId> {
835 self.children.clone()
836 }
837
838 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
839 out.clear();
840 out.extend(self.children.iter().copied());
841 }
842
843 fn on_attached_to_parent(&mut self, parent: NodeId) {
844 self.set_parent(parent);
845 }
846
847 fn on_removed_from_parent(&mut self) {
848 self.clear_parent();
849 }
850
851 fn parent(&self) -> Option<NodeId> {
852 self.parent.get()
853 }
854
855 fn mark_needs_layout(&self) {
856 self.needs_layout.set(true);
857 }
858
859 fn needs_layout(&self) -> bool {
860 self.needs_layout.get()
861 }
862
863 fn mark_needs_measure(&self) {
864 self.needs_measure.set(true);
865 self.needs_layout.set(true);
866 }
867
868 fn needs_measure(&self) -> bool {
869 self.needs_measure.get()
870 }
871
872 fn mark_needs_semantics(&self) {
873 self.needs_semantics.set(true);
874 }
875
876 fn needs_semantics(&self) -> bool {
877 self.needs_semantics.get()
878 }
879
880 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
885 self.parent.set(Some(parent));
886 }
887
888 fn recycle_key(&self) -> Option<TypeId> {
889 Some(TypeId::of::<Self>())
890 }
891
892 fn recycle_pool_limit(&self) -> Option<usize> {
893 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
894 }
895
896 fn prepare_for_recycle(&mut self) {
897 *self = Self::new_recycled_shell(self.is_virtual);
898 }
899
900 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
901 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
902 }
903
904 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
905 let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
906 let node_id = previous.id.replace(None);
907 let parent = previous.parent.get();
908 let folded_parent = previous.folded_parent.get();
909 let debug_modifiers = previous.debug_modifiers.get();
910 let needs_measure = previous.needs_measure.get();
911 let needs_layout = previous.needs_layout.get();
912 let needs_semantics = previous.needs_semantics.get();
913 let needs_redraw = previous.needs_redraw.get();
914 let needs_pointer_pass = previous.needs_pointer_pass.get();
915 let needs_focus_sync = previous.needs_focus_sync.get();
916 let virtual_children_count = previous.virtual_children_count.get();
917 let children = previous.children.to_vec();
918 let modifier = previous.modifier.rehouse_for_live_compaction();
919 let measure_policy = previous.measure_policy.clone();
920 let layout_state = previous.layout_state.clone();
921
922 previous.modifier_chain.chain_mut().detach_nodes();
923
924 let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
925 compact.children = children;
926 compact.parent.set(parent);
927 compact.folded_parent.set(folded_parent);
928 compact.id.set(node_id);
929 compact.debug_modifiers.set(debug_modifiers);
930 compact.needs_measure.set(needs_measure);
931 compact.needs_layout.set(needs_layout);
932 compact.needs_semantics.set(needs_semantics);
933 compact.needs_redraw.set(needs_redraw);
934 compact.needs_pointer_pass.set(needs_pointer_pass);
935 compact.needs_focus_sync.set(needs_focus_sync);
936 compact.virtual_children_count.set(virtual_children_count);
937 compact.layout_state = layout_state;
938 compact.sync_modifier_chain();
939 if let Some(id) = node_id {
940 register_layout_node(id, &compact);
941 }
942
943 Some(Box::new(compact))
944 }
945}
946
947impl Drop for LayoutNode {
948 fn drop(&mut self) {
949 if let Some(id) = self.id.get() {
950 unregister_layout_node(id);
951 }
952 }
953}
954
955thread_local! {
956 static LAYOUT_NODE_REGISTRY: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>> =
957 RefCell::new(HashMap::new());
958 static VIRTUAL_NODE_ID_COUNTER: std::sync::atomic::AtomicUsize = const { std::sync::atomic::AtomicUsize::new(0xC0000000) };
962}
963
964const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
965
966#[cfg(test)]
967#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
968struct LayoutNodeRegistryDebugStats {
969 len: usize,
970 capacity: usize,
971}
972
973struct LayoutNodeRegistryEntry {
974 parent: Option<NodeId>,
975 modifier_child_capabilities: NodeCapabilities,
976 modifier_locals: ModifierLocalsHandle,
977 is_virtual: bool,
978}
979
980pub(crate) fn register_layout_node(id: NodeId, node: &LayoutNode) {
981 LAYOUT_NODE_REGISTRY.with(|registry| {
982 registry.borrow_mut().insert(
983 id,
984 LayoutNodeRegistryEntry {
985 parent: node.parent(),
986 modifier_child_capabilities: node.modifier_child_capabilities(),
987 modifier_locals: node.modifier_locals_handle(),
988 is_virtual: node.is_virtual(),
989 },
990 );
991 });
992}
993
994pub(crate) fn unregister_layout_node(id: NodeId) {
995 LAYOUT_NODE_REGISTRY.with(|registry| {
996 let mut registry = registry.borrow_mut();
997 registry.remove(&id);
998 let should_shrink = (registry.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
999 && registry.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1000 || registry.capacity()
1001 > registry
1002 .len()
1003 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1004 .saturating_mul(4);
1005 if should_shrink {
1006 let retained = registry
1007 .len()
1008 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1009 let mut rebuilt = HashMap::new();
1010 rebuilt.reserve(retained);
1011 rebuilt.extend(registry.drain());
1012 *registry = rebuilt;
1013 }
1014 });
1015}
1016
1017#[cfg(test)]
1018fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1019 LAYOUT_NODE_REGISTRY.with(|registry| {
1020 let registry = registry.borrow();
1021 LayoutNodeRegistryDebugStats {
1022 len: registry.len(),
1023 capacity: registry.capacity(),
1024 }
1025 })
1026}
1027
1028pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1029 LAYOUT_NODE_REGISTRY.with(|registry| {
1030 registry
1031 .borrow()
1032 .get(&id)
1033 .map(|entry| entry.is_virtual)
1034 .unwrap_or(false)
1035 })
1036}
1037
1038pub(crate) fn allocate_virtual_node_id() -> NodeId {
1039 use std::sync::atomic::Ordering;
1040 VIRTUAL_NODE_ID_COUNTER.with(|counter| counter.fetch_add(1, Ordering::Relaxed))
1043}
1044
1045fn resolve_modifier_local_from_parent_chain(
1046 start: Option<NodeId>,
1047 token: ModifierLocalToken,
1048) -> Option<ResolvedModifierLocal> {
1049 let mut current = start;
1050 while let Some(parent_id) = current {
1051 let (next_parent, resolved) = LAYOUT_NODE_REGISTRY.with(|registry| {
1052 let registry = registry.borrow();
1053 if let Some(entry) = registry.get(&parent_id) {
1054 let resolved = if entry
1055 .modifier_child_capabilities
1056 .contains(NodeCapabilities::MODIFIER_LOCALS)
1057 {
1058 entry
1059 .modifier_locals
1060 .borrow()
1061 .resolve(token)
1062 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
1063 } else {
1064 None
1065 };
1066 (entry.parent, resolved)
1067 } else {
1068 (None, None)
1069 }
1070 });
1071 if let Some(value) = resolved {
1072 return Some(value);
1073 }
1074 current = next_parent;
1075 }
1076 None
1077}
1078
1079#[cfg(test)]
1080mod tests {
1081 use super::*;
1082 use cranpose_ui_graphics::Size as GeometrySize;
1083 use cranpose_ui_layout::{Measurable, MeasureResult};
1084 use std::rc::Rc;
1085
1086 #[derive(Default)]
1087 struct TestMeasurePolicy;
1088
1089 impl MeasurePolicy for TestMeasurePolicy {
1090 fn measure(
1091 &self,
1092 _measurables: &[Box<dyn Measurable>],
1093 _constraints: Constraints,
1094 ) -> MeasureResult {
1095 MeasureResult::new(
1096 GeometrySize {
1097 width: 0.0,
1098 height: 0.0,
1099 },
1100 Vec::new(),
1101 )
1102 }
1103
1104 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1105 0.0
1106 }
1107
1108 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1109 0.0
1110 }
1111
1112 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1113 0.0
1114 }
1115
1116 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1117 0.0
1118 }
1119 }
1120
1121 fn fresh_node() -> LayoutNode {
1122 LayoutNode::new(Modifier::empty(), Rc::new(TestMeasurePolicy))
1123 }
1124
1125 #[test]
1126 fn modifier_slices_cache_reuses_unique_snapshot_allocation() {
1127 let mut node = fresh_node();
1128 let snapshot = node.modifier_slices_snapshot();
1129 let snapshot_ptr = Rc::as_ptr(&snapshot);
1130 drop(snapshot);
1131
1132 node.set_modifier(Modifier::empty().padding(4.0));
1133
1134 let updated = node.modifier_slices_snapshot();
1135 assert_eq!(Rc::as_ptr(&updated), snapshot_ptr);
1136 }
1137
1138 #[test]
1139 fn modifier_slices_cache_preserves_live_snapshot_isolation() {
1140 let mut node = fresh_node();
1141 let old_snapshot = node.modifier_slices_snapshot();
1142 let old_snapshot_ptr = Rc::as_ptr(&old_snapshot);
1143
1144 node.set_modifier(Modifier::empty().padding(4.0));
1145
1146 let updated = node.modifier_slices_snapshot();
1147 assert_ne!(Rc::as_ptr(&updated), old_snapshot_ptr);
1148 assert_eq!(old_snapshot.draw_commands().len(), 0);
1149 }
1150
1151 #[test]
1152 fn layout_node_registry_retains_warm_capacity_after_large_cleanup() {
1153 let nodes: Vec<_> = (0..2048)
1154 .map(|_| {
1155 let id = allocate_virtual_node_id();
1156 let node = fresh_node();
1157 register_layout_node(id, &node);
1158 (id, node)
1159 })
1160 .collect();
1161
1162 for (id, _) in &nodes {
1163 unregister_layout_node(*id);
1164 }
1165
1166 let stats = layout_node_registry_stats();
1167 assert_eq!(stats.len, 0);
1168 assert!(
1169 (MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1170 ..=MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY.saturating_mul(2))
1171 .contains(&stats.capacity),
1172 "registry warm capacity {} fell outside expected retained range {}..={}",
1173 stats.capacity,
1174 MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY,
1175 MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY.saturating_mul(2),
1176 );
1177 }
1178
1179 fn invalidation(kind: InvalidationKind) -> ModifierInvalidation {
1180 ModifierInvalidation::new(kind, NodeCapabilities::for_invalidation(kind))
1181 }
1182
1183 #[test]
1184 fn layout_invalidation_requires_layout_capability() {
1185 let mut node = fresh_node();
1186 node.clear_needs_measure();
1187 node.clear_needs_layout();
1188 node.modifier_capabilities = NodeCapabilities::DRAW;
1189 node.modifier_child_capabilities = node.modifier_capabilities;
1190
1191 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1192
1193 assert!(!node.needs_measure());
1194 assert!(!node.needs_layout());
1195 }
1196
1197 #[test]
1198 fn semantics_configuration_reflects_modifier_state() {
1199 let mut node = fresh_node();
1200 node.set_modifier(Modifier::empty().semantics(|config| {
1201 config.content_description = Some("greeting".into());
1202 config.is_clickable = true;
1203 }));
1204
1205 let config = node
1206 .semantics_configuration()
1207 .expect("expected semantics configuration");
1208 assert_eq!(config.content_description.as_deref(), Some("greeting"));
1209 assert!(config.is_clickable);
1210 }
1211
1212 #[test]
1213 fn layout_invalidation_marks_flags_when_capability_present() {
1214 let _guard = crate::render_state::render_state_test_guard();
1215 crate::reset_render_state_for_tests();
1216 let mut node = fresh_node();
1217 node.id.set(Some(11));
1218 node.clear_needs_measure();
1219 node.clear_needs_layout();
1220 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1221 node.modifier_child_capabilities = node.modifier_capabilities;
1222
1223 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1224
1225 assert!(node.needs_measure());
1226 assert!(node.needs_layout());
1227 assert_eq!(crate::take_layout_repass_nodes(), vec![11]);
1228 assert!(crate::take_layout_invalidation());
1229 }
1230
1231 #[test]
1232 fn layout_invalidation_skips_repass_while_composing() {
1233 let _guard = crate::render_state::render_state_test_guard();
1234 crate::reset_render_state_for_tests();
1235
1236 let node = Rc::new(RefCell::new(fresh_node()));
1237 {
1238 let mut node = node.borrow_mut();
1239 node.id.set(Some(17));
1240 node.clear_needs_measure();
1241 node.clear_needs_layout();
1242 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1243 node.modifier_child_capabilities = node.modifier_capabilities;
1244 }
1245
1246 let node_for_composition = Rc::clone(&node);
1247 let _composition = crate::run_test_composition(move || {
1248 node_for_composition
1249 .borrow()
1250 .dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1251 });
1252
1253 let node = node.borrow();
1254 assert!(node.needs_measure());
1255 assert!(node.needs_layout());
1256 assert!(crate::take_layout_repass_nodes().is_empty());
1257 assert!(!crate::take_layout_invalidation());
1258 }
1259
1260 #[test]
1261 fn draw_invalidation_marks_redraw_flag_when_capable() {
1262 let mut node = fresh_node();
1263 node.clear_needs_measure();
1264 node.clear_needs_layout();
1265 node.modifier_capabilities = NodeCapabilities::DRAW;
1266 node.modifier_child_capabilities = node.modifier_capabilities;
1267
1268 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Draw)]);
1269
1270 assert!(node.needs_redraw());
1271 assert!(!node.needs_layout());
1272 }
1273
1274 #[test]
1275 fn semantics_invalidation_sets_semantics_flag_only() {
1276 let mut node = fresh_node();
1277 node.clear_needs_measure();
1278 node.clear_needs_layout();
1279 node.clear_needs_semantics();
1280 node.modifier_capabilities = NodeCapabilities::SEMANTICS;
1281 node.modifier_child_capabilities = node.modifier_capabilities;
1282
1283 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Semantics)]);
1284
1285 assert!(node.needs_semantics());
1286 assert!(!node.needs_measure());
1287 assert!(!node.needs_layout());
1288 }
1289
1290 #[test]
1291 fn pointer_invalidation_requires_pointer_capability() {
1292 let mut node = fresh_node();
1293 node.clear_needs_pointer_pass();
1294 node.modifier_capabilities = NodeCapabilities::DRAW;
1295 node.modifier_child_capabilities = node.modifier_capabilities;
1296 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1301
1302 assert!(!node.needs_pointer_pass());
1303 }
1304
1305 #[test]
1306 fn pointer_invalidation_marks_flag_and_requests_queue() {
1307 let mut node = fresh_node();
1308 node.clear_needs_pointer_pass();
1309 node.modifier_capabilities = NodeCapabilities::POINTER_INPUT;
1310 node.modifier_child_capabilities = node.modifier_capabilities;
1311 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1316
1317 assert!(node.needs_pointer_pass());
1318 }
1319
1320 #[test]
1321 fn focus_invalidation_requires_focus_capability() {
1322 let mut node = fresh_node();
1323 node.clear_needs_focus_sync();
1324 node.modifier_capabilities = NodeCapabilities::DRAW;
1325 node.modifier_child_capabilities = node.modifier_capabilities;
1326 crate::take_focus_invalidation();
1327
1328 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1329
1330 assert!(!node.needs_focus_sync());
1331 assert!(!crate::take_focus_invalidation());
1332 }
1333
1334 #[test]
1335 fn focus_invalidation_marks_flag_and_requests_queue() {
1336 let mut node = fresh_node();
1337 node.clear_needs_focus_sync();
1338 node.modifier_capabilities = NodeCapabilities::FOCUS;
1339 node.modifier_child_capabilities = node.modifier_capabilities;
1340 crate::take_focus_invalidation();
1341
1342 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1343
1344 assert!(node.needs_focus_sync());
1345 assert!(crate::take_focus_invalidation());
1346 }
1347
1348 #[test]
1349 fn set_modifier_marks_semantics_dirty() {
1350 let mut node = fresh_node();
1351 node.clear_needs_semantics();
1352 node.set_modifier(Modifier::empty().semantics(|config| {
1353 config.is_clickable = true;
1354 }));
1355
1356 assert!(node.needs_semantics());
1357 }
1358
1359 #[test]
1360 fn modifier_child_capabilities_reflect_chain_head() {
1361 let mut node = fresh_node();
1362 node.set_modifier(Modifier::empty().padding(4.0));
1363 assert!(
1364 node.modifier_child_capabilities()
1365 .contains(NodeCapabilities::LAYOUT),
1366 "padding should introduce layout capability"
1367 );
1368 }
1369}