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 indexmap::IndexSet;
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: IndexSet<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_buffer: RefCell<ModifierNodeSlices>,
217 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
218
219 layout_state: Rc<RefCell<LayoutState>>,
223}
224
225impl LayoutNode {
226 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
227 Self::new_with_virtual(modifier, measure_policy, false)
228 }
229
230 pub fn new_virtual() -> Self {
233 Self::new_with_virtual(
234 Modifier::empty(),
235 Rc::new(crate::layout::policies::EmptyMeasurePolicy),
236 true,
237 )
238 }
239
240 fn new_with_virtual(
241 modifier: Modifier,
242 measure_policy: Rc<dyn MeasurePolicy>,
243 is_virtual: bool,
244 ) -> Self {
245 let mut node = Self {
246 modifier,
247 modifier_chain: ModifierChainHandle::new(),
248 resolved_modifiers: ResolvedModifiers::default(),
249 modifier_capabilities: NodeCapabilities::default(),
250 modifier_child_capabilities: NodeCapabilities::default(),
251 measure_policy,
252 children: IndexSet::new(),
253 cache: LayoutNodeCacheHandles::default(),
254 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),
259 needs_focus_sync: Cell::new(false),
260 parent: Cell::new(None), folded_parent: Cell::new(None), id: Cell::new(None), debug_modifiers: Cell::new(false),
264 is_virtual,
265 virtual_children_count: Cell::new(0),
266 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
267 modifier_slices_snapshot: RefCell::new(Rc::default()),
268 layout_state: Rc::new(RefCell::new(LayoutState::default())),
269 };
270 node.sync_modifier_chain();
271 node
272 }
273
274 pub fn set_modifier(&mut self, modifier: Modifier) {
275 let modifier_changed = !self.modifier.structural_eq(&modifier);
281 self.modifier = modifier;
282 self.sync_modifier_chain();
283 if modifier_changed {
284 self.cache.clear();
285 self.mark_needs_measure();
286 self.request_semantics_update();
287 }
288 }
289
290 fn sync_modifier_chain(&mut self) {
291 let start_parent = self.parent();
292 let mut resolver = move |token: ModifierLocalToken| {
293 resolve_modifier_local_from_parent_chain(start_parent, token)
294 };
295 self.modifier_chain
296 .set_debug_logging(self.debug_modifiers.get());
297 self.modifier_chain.set_node_id(self.id.get());
298 let modifier_local_invalidations = self
299 .modifier_chain
300 .update_with_resolver(&self.modifier, &mut resolver);
301 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
302 self.modifier_capabilities = self.modifier_chain.capabilities();
303 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
304
305 use crate::modifier::collect_modifier_slices_into;
308 let mut buffer = self.modifier_slices_buffer.borrow_mut();
309 collect_modifier_slices_into(self.modifier_chain.chain(), &mut buffer);
310 *self.modifier_slices_snapshot.borrow_mut() = Rc::new(buffer.clone());
311
312 let mut invalidations = self.modifier_chain.take_invalidations();
313 invalidations.extend(modifier_local_invalidations);
314 self.dispatch_modifier_invalidations(&invalidations);
315 self.refresh_registry_state();
316 }
317
318 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
319 for invalidation in invalidations {
320 match invalidation.kind() {
321 InvalidationKind::Layout => {
322 if self.has_layout_modifier_nodes() {
323 self.mark_needs_measure();
324 if let Some(id) = self.id.get() {
325 crate::schedule_layout_repass(id);
326 }
327 }
328 }
329 InvalidationKind::Draw => {
330 if self.has_draw_modifier_nodes() {
331 self.mark_needs_redraw();
332 }
333 }
334 InvalidationKind::PointerInput => {
335 if self.has_pointer_input_modifier_nodes() {
336 self.mark_needs_pointer_pass();
337 crate::request_pointer_invalidation();
338 if let Some(id) = self.id.get() {
340 crate::schedule_pointer_repass(id);
341 }
342 }
343 }
344 InvalidationKind::Semantics => {
345 self.request_semantics_update();
346 }
347 InvalidationKind::Focus => {
348 if self.has_focus_modifier_nodes() {
349 self.mark_needs_focus_sync();
350 crate::request_focus_invalidation();
351 if let Some(id) = self.id.get() {
353 crate::schedule_focus_invalidation(id);
354 }
355 }
356 }
357 }
358 }
359 }
360
361 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
362 if !Rc::ptr_eq(&self.measure_policy, &policy) {
364 self.measure_policy = policy;
365 self.cache.clear();
366 self.mark_needs_measure();
367 }
368 }
369
370 pub fn mark_needs_measure(&self) {
372 self.needs_measure.set(true);
373 self.needs_layout.set(true);
374 }
375
376 pub fn mark_needs_layout(&self) {
378 self.needs_layout.set(true);
379 }
380
381 pub fn mark_needs_redraw(&self) {
383 self.needs_redraw.set(true);
384 if let Some(id) = self.id.get() {
385 crate::schedule_draw_repass(id);
386 }
387 crate::request_render_invalidation();
388 }
389
390 pub fn needs_measure(&self) -> bool {
392 self.needs_measure.get()
393 }
394
395 pub fn needs_layout(&self) -> bool {
397 self.needs_layout.get()
398 }
399
400 pub fn mark_needs_semantics(&self) {
402 self.needs_semantics.set(true);
403 }
404
405 pub(crate) fn clear_needs_semantics(&self) {
407 self.needs_semantics.set(false);
408 }
409
410 pub fn needs_semantics(&self) -> bool {
412 self.needs_semantics.get()
413 }
414
415 pub fn needs_redraw(&self) -> bool {
417 self.needs_redraw.get()
418 }
419
420 pub fn clear_needs_redraw(&self) {
421 self.needs_redraw.set(false);
422 }
423
424 fn request_semantics_update(&self) {
425 let already_dirty = self.needs_semantics.replace(true);
426 if already_dirty {
427 return;
428 }
429
430 if let Some(id) = self.id.get() {
431 cranpose_core::queue_semantics_invalidation(id);
432 }
433 }
434
435 pub(crate) fn clear_needs_measure(&self) {
437 self.needs_measure.set(false);
438 }
439
440 pub(crate) fn clear_needs_layout(&self) {
442 self.needs_layout.set(false);
443 }
444
445 pub fn mark_needs_pointer_pass(&self) {
447 self.needs_pointer_pass.set(true);
448 }
449
450 pub fn needs_pointer_pass(&self) -> bool {
452 self.needs_pointer_pass.get()
453 }
454
455 pub fn clear_needs_pointer_pass(&self) {
457 self.needs_pointer_pass.set(false);
458 }
459
460 pub fn mark_needs_focus_sync(&self) {
462 self.needs_focus_sync.set(true);
463 }
464
465 pub fn needs_focus_sync(&self) -> bool {
467 self.needs_focus_sync.get()
468 }
469
470 pub fn clear_needs_focus_sync(&self) {
472 self.needs_focus_sync.set(false);
473 }
474
475 pub fn set_node_id(&mut self, id: NodeId) {
477 if let Some(existing) = self.id.replace(Some(id)) {
478 unregister_layout_node(existing);
479 }
480 register_layout_node(id, self);
481 self.refresh_registry_state();
482
483 self.modifier_chain.set_node_id(Some(id));
486 }
487
488 pub fn node_id(&self) -> Option<NodeId> {
490 self.id.get()
491 }
492
493 pub fn set_parent(&self, parent: NodeId) {
496 self.folded_parent.set(Some(parent));
497 self.parent.set(Some(parent));
500 self.refresh_registry_state();
501 }
502
503 pub fn clear_parent(&self) {
505 self.folded_parent.set(None);
506 self.parent.set(None);
507 self.refresh_registry_state();
508 }
509
510 pub fn parent(&self) -> Option<NodeId> {
512 self.parent.get()
513 }
514
515 pub fn folded_parent(&self) -> Option<NodeId> {
517 self.folded_parent.get()
518 }
519
520 pub fn is_virtual(&self) -> bool {
522 self.is_virtual
523 }
524
525 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
526 self.cache.clone()
527 }
528
529 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
530 self.resolved_modifiers
531 }
532
533 pub fn modifier_capabilities(&self) -> NodeCapabilities {
534 self.modifier_capabilities
535 }
536
537 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
538 self.modifier_child_capabilities
539 }
540
541 pub fn set_debug_modifiers(&mut self, enabled: bool) {
542 self.debug_modifiers.set(enabled);
543 self.modifier_chain.set_debug_logging(enabled);
544 }
545
546 pub fn debug_modifiers_enabled(&self) -> bool {
547 self.debug_modifiers.get()
548 }
549
550 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
551 self.modifier_chain.modifier_locals_handle()
552 }
553
554 pub fn has_layout_modifier_nodes(&self) -> bool {
555 self.modifier_capabilities
556 .contains(NodeCapabilities::LAYOUT)
557 }
558
559 pub fn has_draw_modifier_nodes(&self) -> bool {
560 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
561 }
562
563 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
564 self.modifier_capabilities
565 .contains(NodeCapabilities::POINTER_INPUT)
566 }
567
568 pub fn has_semantics_modifier_nodes(&self) -> bool {
569 self.modifier_capabilities
570 .contains(NodeCapabilities::SEMANTICS)
571 }
572
573 pub fn has_focus_modifier_nodes(&self) -> bool {
574 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
575 }
576
577 fn refresh_registry_state(&self) {
578 if let Some(id) = self.id.get() {
579 let parent = self.parent();
580 let capabilities = self.modifier_child_capabilities();
581 let modifier_locals = self.modifier_locals_handle();
582 LAYOUT_NODE_REGISTRY.with(|registry| {
583 if let Some(entry) = registry.borrow_mut().get_mut(&id) {
584 entry.parent = parent;
585 entry.modifier_child_capabilities = capabilities;
586 entry.modifier_locals = modifier_locals;
587 }
588 });
589 }
590 }
591
592 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
593 self.modifier_slices_snapshot.borrow().clone()
594 }
595
596 pub fn layout_state(&self) -> LayoutState {
602 self.layout_state.borrow().clone()
603 }
604
605 pub fn measured_size(&self) -> Size {
607 self.layout_state.borrow().size
608 }
609
610 pub fn position(&self) -> Point {
612 self.layout_state.borrow().position
613 }
614
615 pub fn is_placed(&self) -> bool {
617 self.layout_state.borrow().is_placed
618 }
619
620 pub fn set_measured_size(&self, size: Size) {
622 let mut state = self.layout_state.borrow_mut();
623 state.size = size;
624 }
625
626 pub fn set_position(&self, position: Point) {
628 let mut state = self.layout_state.borrow_mut();
629 state.position = position;
630 state.is_placed = true;
631 }
632
633 pub fn set_measurement_constraints(&self, constraints: Constraints) {
635 self.layout_state.borrow_mut().measurement_constraints = constraints;
636 }
637
638 pub fn set_content_offset(&self, offset: Point) {
640 self.layout_state.borrow_mut().content_offset = offset;
641 }
642
643 pub fn clear_placed(&self) {
645 self.layout_state.borrow_mut().is_placed = false;
646 }
647
648 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
649 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
650 }
651
652 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
654 &self.modifier_chain
655 }
656
657 pub fn with_text_field_modifier_mut<R>(
662 &mut self,
663 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
664 ) -> Option<R> {
665 self.modifier_chain.with_text_field_modifier_mut(f)
666 }
667
668 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
671 self.layout_state.clone()
672 }
673}
674impl Clone for LayoutNode {
675 fn clone(&self) -> Self {
676 let mut node = Self {
677 modifier: self.modifier.clone(),
678 modifier_chain: ModifierChainHandle::new(),
679 resolved_modifiers: ResolvedModifiers::default(),
680 modifier_capabilities: self.modifier_capabilities,
681 modifier_child_capabilities: self.modifier_child_capabilities,
682 measure_policy: self.measure_policy.clone(),
683 children: self.children.clone(),
684 cache: self.cache.clone(),
685 needs_measure: Cell::new(self.needs_measure.get()),
686 needs_layout: Cell::new(self.needs_layout.get()),
687 needs_semantics: Cell::new(self.needs_semantics.get()),
688 needs_redraw: Cell::new(self.needs_redraw.get()),
689 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
690 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
691 parent: Cell::new(self.parent.get()),
692 folded_parent: Cell::new(self.folded_parent.get()),
693 id: Cell::new(None),
694 debug_modifiers: Cell::new(self.debug_modifiers.get()),
695 is_virtual: self.is_virtual,
696 virtual_children_count: Cell::new(self.virtual_children_count.get()),
697 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
698 modifier_slices_snapshot: RefCell::new(Rc::default()),
699 layout_state: self.layout_state.clone(),
701 };
702 node.sync_modifier_chain();
703 node
704 }
705}
706
707impl Node for LayoutNode {
708 fn mount(&mut self) {
709 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
710 chain.repair_chain();
711 chain.attach_nodes(&mut *context);
712 }
713
714 fn unmount(&mut self) {
715 self.modifier_chain.chain_mut().detach_nodes();
716 }
717
718 fn set_node_id(&mut self, id: NodeId) {
719 LayoutNode::set_node_id(self, id);
721 }
722
723 fn insert_child(&mut self, child: NodeId) {
724 if is_virtual_node(child) {
725 let count = self.virtual_children_count.get();
726 self.virtual_children_count.set(count + 1);
727 }
728 self.children.insert(child);
729 self.cache.clear();
730 self.mark_needs_measure();
731 }
732
733 fn remove_child(&mut self, child: NodeId) {
734 if self.children.shift_remove(&child) {
735 if is_virtual_node(child) {
736 let count = self.virtual_children_count.get();
737 if count > 0 {
738 self.virtual_children_count.set(count - 1);
739 }
740 }
741 self.cache.clear();
742 self.mark_needs_measure();
743 }
744 }
745
746 fn move_child(&mut self, from: usize, to: usize) {
747 if from == to || from >= self.children.len() {
748 return;
749 }
750 let mut ordered: Vec<NodeId> = self.children.iter().copied().collect();
751 let child = ordered.remove(from);
752 let target = to.min(ordered.len());
753 ordered.insert(target, child);
754 self.children.clear();
755 for id in ordered {
756 self.children.insert(id);
757 }
758 self.cache.clear();
759 self.mark_needs_measure();
760 }
762
763 fn update_children(&mut self, children: &[NodeId]) {
764 self.children.clear();
765 for &child in children {
766 self.children.insert(child);
767 }
768 self.cache.clear();
769 self.mark_needs_measure();
770 }
771
772 fn children(&self) -> Vec<NodeId> {
773 self.children.iter().copied().collect()
774 }
775
776 fn on_attached_to_parent(&mut self, parent: NodeId) {
777 self.set_parent(parent);
778 }
779
780 fn on_removed_from_parent(&mut self) {
781 self.clear_parent();
782 }
783
784 fn parent(&self) -> Option<NodeId> {
785 self.parent.get()
786 }
787
788 fn mark_needs_layout(&self) {
789 self.needs_layout.set(true);
790 }
791
792 fn needs_layout(&self) -> bool {
793 self.needs_layout.get()
794 }
795
796 fn mark_needs_measure(&self) {
797 self.needs_measure.set(true);
798 self.needs_layout.set(true);
799 }
800
801 fn needs_measure(&self) -> bool {
802 self.needs_measure.get()
803 }
804
805 fn mark_needs_semantics(&self) {
806 self.needs_semantics.set(true);
807 }
808
809 fn needs_semantics(&self) -> bool {
810 self.needs_semantics.get()
811 }
812
813 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
818 self.parent.set(Some(parent));
819 }
820}
821
822impl Drop for LayoutNode {
823 fn drop(&mut self) {
824 if let Some(id) = self.id.get() {
825 unregister_layout_node(id);
826 }
827 }
828}
829
830thread_local! {
831 static LAYOUT_NODE_REGISTRY: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>> =
832 RefCell::new(HashMap::new());
833 static VIRTUAL_NODE_ID_COUNTER: std::sync::atomic::AtomicUsize = const { std::sync::atomic::AtomicUsize::new(0xC0000000) };
837}
838
839struct LayoutNodeRegistryEntry {
840 parent: Option<NodeId>,
841 modifier_child_capabilities: NodeCapabilities,
842 modifier_locals: ModifierLocalsHandle,
843 is_virtual: bool,
844}
845
846pub(crate) fn register_layout_node(id: NodeId, node: &LayoutNode) {
847 LAYOUT_NODE_REGISTRY.with(|registry| {
848 registry.borrow_mut().insert(
849 id,
850 LayoutNodeRegistryEntry {
851 parent: node.parent(),
852 modifier_child_capabilities: node.modifier_child_capabilities(),
853 modifier_locals: node.modifier_locals_handle(),
854 is_virtual: node.is_virtual(),
855 },
856 );
857 });
858}
859
860pub(crate) fn unregister_layout_node(id: NodeId) {
861 LAYOUT_NODE_REGISTRY.with(|registry| {
862 registry.borrow_mut().remove(&id);
863 });
864}
865
866pub(crate) fn is_virtual_node(id: NodeId) -> bool {
867 LAYOUT_NODE_REGISTRY.with(|registry| {
868 registry
869 .borrow()
870 .get(&id)
871 .map(|entry| entry.is_virtual)
872 .unwrap_or(false)
873 })
874}
875
876pub(crate) fn allocate_virtual_node_id() -> NodeId {
877 use std::sync::atomic::Ordering;
878 VIRTUAL_NODE_ID_COUNTER.with(|counter| counter.fetch_add(1, Ordering::Relaxed))
881}
882
883fn resolve_modifier_local_from_parent_chain(
884 start: Option<NodeId>,
885 token: ModifierLocalToken,
886) -> Option<ResolvedModifierLocal> {
887 let mut current = start;
888 while let Some(parent_id) = current {
889 let (next_parent, resolved) = LAYOUT_NODE_REGISTRY.with(|registry| {
890 let registry = registry.borrow();
891 if let Some(entry) = registry.get(&parent_id) {
892 let resolved = if entry
893 .modifier_child_capabilities
894 .contains(NodeCapabilities::MODIFIER_LOCALS)
895 {
896 entry
897 .modifier_locals
898 .borrow()
899 .resolve(token)
900 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
901 } else {
902 None
903 };
904 (entry.parent, resolved)
905 } else {
906 (None, None)
907 }
908 });
909 if let Some(value) = resolved {
910 return Some(value);
911 }
912 current = next_parent;
913 }
914 None
915}
916
917#[cfg(test)]
918mod tests {
919 use super::*;
920 use cranpose_ui_graphics::Size as GeometrySize;
921 use cranpose_ui_layout::{Measurable, MeasureResult};
922 use std::rc::Rc;
923
924 #[derive(Default)]
925 struct TestMeasurePolicy;
926
927 impl MeasurePolicy for TestMeasurePolicy {
928 fn measure(
929 &self,
930 _measurables: &[Box<dyn Measurable>],
931 _constraints: Constraints,
932 ) -> MeasureResult {
933 MeasureResult::new(
934 GeometrySize {
935 width: 0.0,
936 height: 0.0,
937 },
938 Vec::new(),
939 )
940 }
941
942 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
943 0.0
944 }
945
946 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
947 0.0
948 }
949
950 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
951 0.0
952 }
953
954 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
955 0.0
956 }
957 }
958
959 fn fresh_node() -> LayoutNode {
960 LayoutNode::new(Modifier::empty(), Rc::new(TestMeasurePolicy))
961 }
962
963 fn invalidation(kind: InvalidationKind) -> ModifierInvalidation {
964 ModifierInvalidation::new(kind, NodeCapabilities::for_invalidation(kind))
965 }
966
967 #[test]
968 fn layout_invalidation_requires_layout_capability() {
969 let mut node = fresh_node();
970 node.clear_needs_measure();
971 node.clear_needs_layout();
972 node.modifier_capabilities = NodeCapabilities::DRAW;
973 node.modifier_child_capabilities = node.modifier_capabilities;
974
975 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
976
977 assert!(!node.needs_measure());
978 assert!(!node.needs_layout());
979 }
980
981 #[test]
982 fn semantics_configuration_reflects_modifier_state() {
983 let mut node = fresh_node();
984 node.set_modifier(Modifier::empty().semantics(|config| {
985 config.content_description = Some("greeting".into());
986 config.is_clickable = true;
987 }));
988
989 let config = node
990 .semantics_configuration()
991 .expect("expected semantics configuration");
992 assert_eq!(config.content_description.as_deref(), Some("greeting"));
993 assert!(config.is_clickable);
994 }
995
996 #[test]
997 fn layout_invalidation_marks_flags_when_capability_present() {
998 let mut node = fresh_node();
999 node.clear_needs_measure();
1000 node.clear_needs_layout();
1001 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1002 node.modifier_child_capabilities = node.modifier_capabilities;
1003
1004 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1005
1006 assert!(node.needs_measure());
1007 assert!(node.needs_layout());
1008 }
1009
1010 #[test]
1011 fn draw_invalidation_marks_redraw_flag_when_capable() {
1012 let mut node = fresh_node();
1013 node.clear_needs_measure();
1014 node.clear_needs_layout();
1015 node.modifier_capabilities = NodeCapabilities::DRAW;
1016 node.modifier_child_capabilities = node.modifier_capabilities;
1017
1018 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Draw)]);
1019
1020 assert!(node.needs_redraw());
1021 assert!(!node.needs_layout());
1022 }
1023
1024 #[test]
1025 fn semantics_invalidation_sets_semantics_flag_only() {
1026 let mut node = fresh_node();
1027 node.clear_needs_measure();
1028 node.clear_needs_layout();
1029 node.clear_needs_semantics();
1030 node.modifier_capabilities = NodeCapabilities::SEMANTICS;
1031 node.modifier_child_capabilities = node.modifier_capabilities;
1032
1033 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Semantics)]);
1034
1035 assert!(node.needs_semantics());
1036 assert!(!node.needs_measure());
1037 assert!(!node.needs_layout());
1038 }
1039
1040 #[test]
1041 fn pointer_invalidation_requires_pointer_capability() {
1042 let mut node = fresh_node();
1043 node.clear_needs_pointer_pass();
1044 node.modifier_capabilities = NodeCapabilities::DRAW;
1045 node.modifier_child_capabilities = node.modifier_capabilities;
1046 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1051
1052 assert!(!node.needs_pointer_pass());
1053 }
1054
1055 #[test]
1056 fn pointer_invalidation_marks_flag_and_requests_queue() {
1057 let mut node = fresh_node();
1058 node.clear_needs_pointer_pass();
1059 node.modifier_capabilities = NodeCapabilities::POINTER_INPUT;
1060 node.modifier_child_capabilities = node.modifier_capabilities;
1061 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1066
1067 assert!(node.needs_pointer_pass());
1068 }
1069
1070 #[test]
1071 fn focus_invalidation_requires_focus_capability() {
1072 let mut node = fresh_node();
1073 node.clear_needs_focus_sync();
1074 node.modifier_capabilities = NodeCapabilities::DRAW;
1075 node.modifier_child_capabilities = node.modifier_capabilities;
1076 crate::take_focus_invalidation();
1077
1078 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1079
1080 assert!(!node.needs_focus_sync());
1081 assert!(!crate::take_focus_invalidation());
1082 }
1083
1084 #[test]
1085 fn focus_invalidation_marks_flag_and_requests_queue() {
1086 let mut node = fresh_node();
1087 node.clear_needs_focus_sync();
1088 node.modifier_capabilities = NodeCapabilities::FOCUS;
1089 node.modifier_child_capabilities = node.modifier_capabilities;
1090 crate::take_focus_invalidation();
1091
1092 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1093
1094 assert!(node.needs_focus_sync());
1095 assert!(crate::take_focus_invalidation());
1096 }
1097
1098 #[test]
1099 fn set_modifier_marks_semantics_dirty() {
1100 let mut node = fresh_node();
1101 node.clear_needs_semantics();
1102 node.set_modifier(Modifier::empty().semantics(|config| {
1103 config.is_clickable = true;
1104 }));
1105
1106 assert!(node.needs_semantics());
1107 }
1108
1109 #[test]
1110 fn modifier_child_capabilities_reflect_chain_head() {
1111 let mut node = fresh_node();
1112 node.set_modifier(Modifier::empty().padding(4.0));
1113 assert!(
1114 node.modifier_child_capabilities()
1115 .contains(NodeCapabilities::LAYOUT),
1116 "padding should introduce layout capability"
1117 );
1118 }
1119}