1use crate::{
2 layout::MeasuredNode,
3 modifier::{
4 collect_modifier_slices, Modifier, ModifierChainHandle, ModifierLocalSource,
5 ModifierLocalToken, ModifierLocalsHandle, ModifierNodeSlices, ResolvedModifierLocal,
6 ResolvedModifiers,
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)]
21struct MeasurementCacheEntry {
22 constraints: Constraints,
23 measured: Rc<MeasuredNode>,
24}
25
26#[derive(Clone, Copy, Debug)]
27pub enum IntrinsicKind {
28 MinWidth(f32),
29 MaxWidth(f32),
30 MinHeight(f32),
31 MaxHeight(f32),
32}
33
34impl IntrinsicKind {
35 fn discriminant(&self) -> u8 {
36 match self {
37 IntrinsicKind::MinWidth(_) => 0,
38 IntrinsicKind::MaxWidth(_) => 1,
39 IntrinsicKind::MinHeight(_) => 2,
40 IntrinsicKind::MaxHeight(_) => 3,
41 }
42 }
43
44 fn value_bits(&self) -> u32 {
45 match self {
46 IntrinsicKind::MinWidth(value)
47 | IntrinsicKind::MaxWidth(value)
48 | IntrinsicKind::MinHeight(value)
49 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
50 }
51 }
52}
53
54impl PartialEq for IntrinsicKind {
55 fn eq(&self, other: &Self) -> bool {
56 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
57 }
58}
59
60impl Eq for IntrinsicKind {}
61
62impl Hash for IntrinsicKind {
63 fn hash<H: Hasher>(&self, state: &mut H) {
64 self.discriminant().hash(state);
65 self.value_bits().hash(state);
66 }
67}
68
69#[derive(Default)]
70struct NodeCacheState {
71 epoch: u64,
72 measurements: Vec<MeasurementCacheEntry>,
73 intrinsics: Vec<(IntrinsicKind, f32)>,
74}
75
76#[derive(Clone, Default)]
77pub(crate) struct LayoutNodeCacheHandles {
78 state: Rc<RefCell<NodeCacheState>>,
79}
80
81impl LayoutNodeCacheHandles {
82 pub(crate) fn clear(&self) {
83 let mut state = self.state.borrow_mut();
84 state.measurements.clear();
85 state.intrinsics.clear();
86 state.epoch = 0;
87 }
88
89 pub(crate) fn activate(&self, epoch: u64) {
90 let mut state = self.state.borrow_mut();
91 if state.epoch != epoch {
92 state.measurements.clear();
93 state.intrinsics.clear();
94 state.epoch = epoch;
95 }
96 }
97
98 pub(crate) fn epoch(&self) -> u64 {
99 self.state.borrow().epoch
100 }
101
102 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
103 let state = self.state.borrow();
104 state
105 .measurements
106 .iter()
107 .find(|entry| entry.constraints == constraints)
108 .map(|entry| Rc::clone(&entry.measured))
109 }
110
111 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
112 let mut state = self.state.borrow_mut();
113 if let Some(entry) = state
114 .measurements
115 .iter_mut()
116 .find(|entry| entry.constraints == constraints)
117 {
118 entry.measured = measured;
119 } else {
120 state.measurements.push(MeasurementCacheEntry {
121 constraints,
122 measured,
123 });
124 }
125 }
126
127 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
128 let state = self.state.borrow();
129 state
130 .intrinsics
131 .iter()
132 .find(|(stored_kind, _)| stored_kind == kind)
133 .map(|(_, value)| *value)
134 }
135
136 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
137 let mut state = self.state.borrow_mut();
138 if let Some((_, existing)) = state
139 .intrinsics
140 .iter_mut()
141 .find(|(stored_kind, _)| stored_kind == &kind)
142 {
143 *existing = value;
144 } else {
145 state.intrinsics.push((kind, value));
146 }
147 }
148}
149
150pub struct LayoutNode {
151 pub modifier: Modifier,
152 modifier_chain: ModifierChainHandle,
153 resolved_modifiers: ResolvedModifiers,
154 modifier_capabilities: NodeCapabilities,
155 modifier_child_capabilities: NodeCapabilities,
156 pub measure_policy: Rc<dyn MeasurePolicy>,
157 pub children: IndexSet<NodeId>,
159 cache: LayoutNodeCacheHandles,
160 needs_measure: Cell<bool>,
162 needs_layout: Cell<bool>,
163 needs_semantics: Cell<bool>,
164 needs_redraw: Cell<bool>,
165 needs_pointer_pass: Cell<bool>,
166 needs_focus_sync: Cell<bool>,
167 parent: Cell<Option<NodeId>>,
169 folded_parent: Cell<Option<NodeId>>,
171 id: Cell<Option<NodeId>>,
173 debug_modifiers: Cell<bool>,
174 is_virtual: bool,
177 virtual_children_count: Cell<usize>,
179}
180
181impl LayoutNode {
182 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
183 Self::new_with_virtual(modifier, measure_policy, false)
184 }
185
186 pub fn new_virtual() -> Self {
189 Self::new_with_virtual(
190 Modifier::empty(),
191 Rc::new(crate::layout::policies::EmptyMeasurePolicy),
192 true,
193 )
194 }
195
196 fn new_with_virtual(
197 modifier: Modifier,
198 measure_policy: Rc<dyn MeasurePolicy>,
199 is_virtual: bool,
200 ) -> Self {
201 let mut node = Self {
202 modifier: Modifier::empty(),
203 modifier_chain: ModifierChainHandle::new(),
204 resolved_modifiers: ResolvedModifiers::default(),
205 modifier_capabilities: NodeCapabilities::default(),
206 modifier_child_capabilities: NodeCapabilities::default(),
207 measure_policy,
208 children: IndexSet::new(),
209 cache: LayoutNodeCacheHandles::default(),
210 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),
215 needs_focus_sync: Cell::new(false),
216 parent: Cell::new(None), folded_parent: Cell::new(None), id: Cell::new(None), debug_modifiers: Cell::new(false),
220 is_virtual,
221 virtual_children_count: Cell::new(0),
222 };
223 if !is_virtual {
224 node.set_modifier(modifier);
225 }
226 node
227 }
228
229 pub fn set_modifier(&mut self, modifier: Modifier) {
230 let modifier_changed = self.modifier != modifier;
236 self.modifier = modifier;
237 self.sync_modifier_chain();
238 if modifier_changed {
239 self.cache.clear();
240 self.mark_needs_measure();
241 self.request_semantics_update();
242 }
243 }
244
245 fn sync_modifier_chain(&mut self) {
246 let start_parent = self.parent();
247 let mut resolver = move |token: ModifierLocalToken| {
248 resolve_modifier_local_from_parent_chain(start_parent, token)
249 };
250 self.modifier_chain
251 .set_debug_logging(self.debug_modifiers.get());
252 self.modifier_chain.set_node_id(self.id.get());
253 let modifier_local_invalidations = self
254 .modifier_chain
255 .update_with_resolver(&self.modifier, &mut resolver);
256 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
257 self.modifier_capabilities = self.modifier_chain.capabilities();
258 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
259 let mut invalidations = self.modifier_chain.take_invalidations();
260 invalidations.extend(modifier_local_invalidations);
261 self.dispatch_modifier_invalidations(&invalidations);
262 self.refresh_registry_state();
263 }
264
265 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
266 for invalidation in invalidations {
267 match invalidation.kind() {
268 InvalidationKind::Layout => {
269 if self.has_layout_modifier_nodes() {
270 self.mark_needs_measure();
271 if let Some(id) = self.id.get() {
272 crate::schedule_layout_repass(id);
273 }
274 }
275 }
276 InvalidationKind::Draw => {
277 if self.has_draw_modifier_nodes() {
278 self.mark_needs_redraw();
279 }
280 }
281 InvalidationKind::PointerInput => {
282 if self.has_pointer_input_modifier_nodes() {
283 self.mark_needs_pointer_pass();
284 crate::request_pointer_invalidation();
285 if let Some(id) = self.id.get() {
287 crate::schedule_pointer_repass(id);
288 }
289 }
290 }
291 InvalidationKind::Semantics => {
292 self.request_semantics_update();
293 }
294 InvalidationKind::Focus => {
295 if self.has_focus_modifier_nodes() {
296 self.mark_needs_focus_sync();
297 crate::request_focus_invalidation();
298 if let Some(id) = self.id.get() {
300 crate::schedule_focus_invalidation(id);
301 }
302 }
303 }
304 }
305 }
306 }
307
308 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
309 if !Rc::ptr_eq(&self.measure_policy, &policy) {
311 self.measure_policy = policy;
312 self.cache.clear();
313 self.mark_needs_measure();
314 }
315 }
316
317 pub fn mark_needs_measure(&self) {
319 self.needs_measure.set(true);
320 self.needs_layout.set(true);
321 }
322
323 pub fn mark_needs_layout(&self) {
325 self.needs_layout.set(true);
326 }
327
328 pub fn mark_needs_redraw(&self) {
330 self.needs_redraw.set(true);
331 if let Some(id) = self.id.get() {
332 crate::schedule_draw_repass(id);
333 }
334 crate::request_render_invalidation();
335 }
336
337 pub fn needs_measure(&self) -> bool {
339 self.needs_measure.get()
340 }
341
342 pub fn needs_layout(&self) -> bool {
344 self.needs_layout.get()
345 }
346
347 pub fn mark_needs_semantics(&self) {
349 self.needs_semantics.set(true);
350 }
351
352 pub(crate) fn clear_needs_semantics(&self) {
354 self.needs_semantics.set(false);
355 }
356
357 pub fn needs_semantics(&self) -> bool {
359 self.needs_semantics.get()
360 }
361
362 pub fn needs_redraw(&self) -> bool {
364 self.needs_redraw.get()
365 }
366
367 pub fn clear_needs_redraw(&self) {
368 self.needs_redraw.set(false);
369 }
370
371 fn request_semantics_update(&self) {
372 let already_dirty = self.needs_semantics.replace(true);
373 if already_dirty {
374 return;
375 }
376
377 if let Some(id) = self.id.get() {
378 cranpose_core::queue_semantics_invalidation(id);
379 }
380 }
381
382 pub(crate) fn clear_needs_measure(&self) {
384 self.needs_measure.set(false);
385 }
386
387 pub(crate) fn clear_needs_layout(&self) {
389 self.needs_layout.set(false);
390 }
391
392 pub fn mark_needs_pointer_pass(&self) {
394 self.needs_pointer_pass.set(true);
395 }
396
397 pub fn needs_pointer_pass(&self) -> bool {
399 self.needs_pointer_pass.get()
400 }
401
402 pub fn clear_needs_pointer_pass(&self) {
404 self.needs_pointer_pass.set(false);
405 }
406
407 pub fn mark_needs_focus_sync(&self) {
409 self.needs_focus_sync.set(true);
410 }
411
412 pub fn needs_focus_sync(&self) -> bool {
414 self.needs_focus_sync.get()
415 }
416
417 pub fn clear_needs_focus_sync(&self) {
419 self.needs_focus_sync.set(false);
420 }
421
422 pub fn set_node_id(&mut self, id: NodeId) {
424 if let Some(existing) = self.id.replace(Some(id)) {
425 unregister_layout_node(existing);
426 }
427 register_layout_node(id, self);
428 self.refresh_registry_state();
429
430 self.modifier_chain.set_node_id(Some(id));
433 }
434
435 pub fn node_id(&self) -> Option<NodeId> {
437 self.id.get()
438 }
439
440 pub fn set_parent(&self, parent: NodeId) {
443 self.folded_parent.set(Some(parent));
444 self.parent.set(Some(parent));
447 self.refresh_registry_state();
448 }
449
450 pub fn clear_parent(&self) {
452 self.folded_parent.set(None);
453 self.parent.set(None);
454 self.refresh_registry_state();
455 }
456
457 pub fn parent(&self) -> Option<NodeId> {
459 self.parent.get()
460 }
461
462 pub fn folded_parent(&self) -> Option<NodeId> {
464 self.folded_parent.get()
465 }
466
467 pub fn is_virtual(&self) -> bool {
469 self.is_virtual
470 }
471
472 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
473 self.cache.clone()
474 }
475
476 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
477 self.resolved_modifiers
478 }
479
480 pub fn modifier_capabilities(&self) -> NodeCapabilities {
481 self.modifier_capabilities
482 }
483
484 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
485 self.modifier_child_capabilities
486 }
487
488 pub fn set_debug_modifiers(&mut self, enabled: bool) {
489 self.debug_modifiers.set(enabled);
490 self.modifier_chain.set_debug_logging(enabled);
491 }
492
493 pub fn debug_modifiers_enabled(&self) -> bool {
494 self.debug_modifiers.get()
495 }
496
497 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
498 self.modifier_chain.modifier_locals_handle()
499 }
500
501 pub fn has_layout_modifier_nodes(&self) -> bool {
502 self.modifier_capabilities
503 .contains(NodeCapabilities::LAYOUT)
504 }
505
506 pub fn has_draw_modifier_nodes(&self) -> bool {
507 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
508 }
509
510 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
511 self.modifier_capabilities
512 .contains(NodeCapabilities::POINTER_INPUT)
513 }
514
515 pub fn has_semantics_modifier_nodes(&self) -> bool {
516 self.modifier_capabilities
517 .contains(NodeCapabilities::SEMANTICS)
518 }
519
520 pub fn has_focus_modifier_nodes(&self) -> bool {
521 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
522 }
523
524 fn refresh_registry_state(&self) {
525 if let Some(id) = self.id.get() {
526 let parent = self.parent();
527 let capabilities = self.modifier_child_capabilities();
528 let modifier_locals = self.modifier_locals_handle();
529 LAYOUT_NODE_REGISTRY.with(|registry| {
530 if let Some(entry) = registry.borrow_mut().get_mut(&id) {
531 entry.parent = parent;
532 entry.modifier_child_capabilities = capabilities;
533 entry.modifier_locals = modifier_locals;
534 }
535 });
536 }
537 }
538
539 pub fn modifier_slices_snapshot(&self) -> ModifierNodeSlices {
540 collect_modifier_slices(self.modifier_chain.chain())
541 }
542
543 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
544 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
545 }
546
547 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
549 &self.modifier_chain
550 }
551
552 pub fn with_text_field_modifier_mut<R>(
557 &mut self,
558 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
559 ) -> Option<R> {
560 self.modifier_chain.with_text_field_modifier_mut(f)
561 }
562}
563impl Clone for LayoutNode {
564 fn clone(&self) -> Self {
565 let mut node = Self {
566 modifier: self.modifier.clone(),
567 modifier_chain: ModifierChainHandle::new(),
568 resolved_modifiers: ResolvedModifiers::default(),
569 modifier_capabilities: self.modifier_capabilities,
570 modifier_child_capabilities: self.modifier_child_capabilities,
571 measure_policy: self.measure_policy.clone(),
572 children: self.children.clone(),
573 cache: self.cache.clone(),
574 needs_measure: Cell::new(self.needs_measure.get()),
575 needs_layout: Cell::new(self.needs_layout.get()),
576 needs_semantics: Cell::new(self.needs_semantics.get()),
577 needs_redraw: Cell::new(self.needs_redraw.get()),
578 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
579 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
580 parent: Cell::new(self.parent.get()),
581 folded_parent: Cell::new(self.folded_parent.get()),
582 id: Cell::new(None),
583 debug_modifiers: Cell::new(self.debug_modifiers.get()),
584 is_virtual: self.is_virtual,
585 virtual_children_count: Cell::new(self.virtual_children_count.get()),
586 };
587 node.sync_modifier_chain();
588 node
589 }
590}
591
592impl Node for LayoutNode {
593 fn mount(&mut self) {
594 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
595 chain.repair_chain();
596 chain.attach_nodes(&mut *context);
597 }
598
599 fn unmount(&mut self) {
600 self.modifier_chain.chain_mut().detach_nodes();
601 }
602
603 fn set_node_id(&mut self, id: NodeId) {
604 LayoutNode::set_node_id(self, id);
606 }
607
608 fn insert_child(&mut self, child: NodeId) {
609 if is_virtual_node(child) {
610 let count = self.virtual_children_count.get();
611 self.virtual_children_count.set(count + 1);
612 }
613 self.children.insert(child);
614 self.cache.clear();
615 self.mark_needs_measure();
616 }
617
618 fn remove_child(&mut self, child: NodeId) {
619 if self.children.shift_remove(&child) {
620 if is_virtual_node(child) {
621 let count = self.virtual_children_count.get();
622 if count > 0 {
623 self.virtual_children_count.set(count - 1);
624 }
625 }
626 self.cache.clear();
627 self.mark_needs_measure();
628 }
629 }
630
631 fn move_child(&mut self, from: usize, to: usize) {
632 if from == to || from >= self.children.len() {
633 return;
634 }
635 let mut ordered: Vec<NodeId> = self.children.iter().copied().collect();
636 let child = ordered.remove(from);
637 let target = to.min(ordered.len());
638 ordered.insert(target, child);
639 self.children.clear();
640 for id in ordered {
641 self.children.insert(id);
642 }
643 self.cache.clear();
644 self.mark_needs_measure();
645 }
647
648 fn update_children(&mut self, children: &[NodeId]) {
649 self.children.clear();
650 for &child in children {
651 self.children.insert(child);
652 }
653 self.cache.clear();
654 self.mark_needs_measure();
655 }
656
657 fn children(&self) -> Vec<NodeId> {
658 self.children.iter().copied().collect()
659 }
660
661 fn on_attached_to_parent(&mut self, parent: NodeId) {
662 self.set_parent(parent);
663 }
664
665 fn on_removed_from_parent(&mut self) {
666 self.clear_parent();
667 }
668
669 fn parent(&self) -> Option<NodeId> {
670 self.parent.get()
671 }
672
673 fn mark_needs_layout(&self) {
674 self.needs_layout.set(true);
675 }
676
677 fn needs_layout(&self) -> bool {
678 self.needs_layout.get()
679 }
680
681 fn mark_needs_measure(&self) {
682 self.needs_measure.set(true);
683 self.needs_layout.set(true);
684 }
685
686 fn needs_measure(&self) -> bool {
687 self.needs_measure.get()
688 }
689
690 fn mark_needs_semantics(&self) {
691 self.needs_semantics.set(true);
692 }
693
694 fn needs_semantics(&self) -> bool {
695 self.needs_semantics.get()
696 }
697
698 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
703 self.parent.set(Some(parent));
704 }
705}
706
707impl Drop for LayoutNode {
708 fn drop(&mut self) {
709 if let Some(id) = self.id.get() {
710 unregister_layout_node(id);
711 }
712 }
713}
714
715thread_local! {
716 static LAYOUT_NODE_REGISTRY: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>> =
717 RefCell::new(HashMap::new());
718 static VIRTUAL_NODE_ID_COUNTER: std::sync::atomic::AtomicUsize = const { std::sync::atomic::AtomicUsize::new(0xC0000000) };
722}
723
724struct LayoutNodeRegistryEntry {
725 parent: Option<NodeId>,
726 modifier_child_capabilities: NodeCapabilities,
727 modifier_locals: ModifierLocalsHandle,
728 is_virtual: bool,
729}
730
731pub(crate) fn register_layout_node(id: NodeId, node: &LayoutNode) {
732 LAYOUT_NODE_REGISTRY.with(|registry| {
733 registry.borrow_mut().insert(
734 id,
735 LayoutNodeRegistryEntry {
736 parent: node.parent(),
737 modifier_child_capabilities: node.modifier_child_capabilities(),
738 modifier_locals: node.modifier_locals_handle(),
739 is_virtual: node.is_virtual(),
740 },
741 );
742 });
743}
744
745pub(crate) fn unregister_layout_node(id: NodeId) {
746 LAYOUT_NODE_REGISTRY.with(|registry| {
747 registry.borrow_mut().remove(&id);
748 });
749}
750
751pub(crate) fn is_virtual_node(id: NodeId) -> bool {
752 LAYOUT_NODE_REGISTRY.with(|registry| {
753 registry
754 .borrow()
755 .get(&id)
756 .map(|entry| entry.is_virtual)
757 .unwrap_or(false)
758 })
759}
760
761pub(crate) fn allocate_virtual_node_id() -> NodeId {
762 use std::sync::atomic::Ordering;
763 VIRTUAL_NODE_ID_COUNTER.with(|counter| counter.fetch_add(1, Ordering::Relaxed))
766}
767
768fn resolve_modifier_local_from_parent_chain(
769 start: Option<NodeId>,
770 token: ModifierLocalToken,
771) -> Option<ResolvedModifierLocal> {
772 let mut current = start;
773 while let Some(parent_id) = current {
774 let (next_parent, resolved) = LAYOUT_NODE_REGISTRY.with(|registry| {
775 let registry = registry.borrow();
776 if let Some(entry) = registry.get(&parent_id) {
777 let resolved = if entry
778 .modifier_child_capabilities
779 .contains(NodeCapabilities::MODIFIER_LOCALS)
780 {
781 entry
782 .modifier_locals
783 .borrow()
784 .resolve(token)
785 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
786 } else {
787 None
788 };
789 (entry.parent, resolved)
790 } else {
791 (None, None)
792 }
793 });
794 if let Some(value) = resolved {
795 return Some(value);
796 }
797 current = next_parent;
798 }
799 None
800}
801
802#[cfg(test)]
803mod tests {
804 use super::*;
805 use cranpose_ui_graphics::Size as GeometrySize;
806 use cranpose_ui_layout::{Measurable, MeasureResult};
807 use std::rc::Rc;
808
809 #[derive(Default)]
810 struct TestMeasurePolicy;
811
812 impl MeasurePolicy for TestMeasurePolicy {
813 fn measure(
814 &self,
815 _measurables: &[Box<dyn Measurable>],
816 _constraints: Constraints,
817 ) -> MeasureResult {
818 MeasureResult::new(
819 GeometrySize {
820 width: 0.0,
821 height: 0.0,
822 },
823 Vec::new(),
824 )
825 }
826
827 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
828 0.0
829 }
830
831 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
832 0.0
833 }
834
835 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
836 0.0
837 }
838
839 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
840 0.0
841 }
842 }
843
844 fn fresh_node() -> LayoutNode {
845 LayoutNode::new(Modifier::empty(), Rc::new(TestMeasurePolicy))
846 }
847
848 fn invalidation(kind: InvalidationKind) -> ModifierInvalidation {
849 ModifierInvalidation::new(kind, NodeCapabilities::for_invalidation(kind))
850 }
851
852 #[test]
853 fn layout_invalidation_requires_layout_capability() {
854 let mut node = fresh_node();
855 node.clear_needs_measure();
856 node.clear_needs_layout();
857 node.modifier_capabilities = NodeCapabilities::DRAW;
858 node.modifier_child_capabilities = node.modifier_capabilities;
859
860 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
861
862 assert!(!node.needs_measure());
863 assert!(!node.needs_layout());
864 }
865
866 #[test]
867 fn semantics_configuration_reflects_modifier_state() {
868 let mut node = fresh_node();
869 node.set_modifier(Modifier::empty().semantics(|config| {
870 config.content_description = Some("greeting".into());
871 config.is_clickable = true;
872 }));
873
874 let config = node
875 .semantics_configuration()
876 .expect("expected semantics configuration");
877 assert_eq!(config.content_description.as_deref(), Some("greeting"));
878 assert!(config.is_clickable);
879 }
880
881 #[test]
882 fn layout_invalidation_marks_flags_when_capability_present() {
883 let mut node = fresh_node();
884 node.clear_needs_measure();
885 node.clear_needs_layout();
886 node.modifier_capabilities = NodeCapabilities::LAYOUT;
887 node.modifier_child_capabilities = node.modifier_capabilities;
888
889 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
890
891 assert!(node.needs_measure());
892 assert!(node.needs_layout());
893 }
894
895 #[test]
896 fn draw_invalidation_marks_redraw_flag_when_capable() {
897 let mut node = fresh_node();
898 node.clear_needs_measure();
899 node.clear_needs_layout();
900 node.modifier_capabilities = NodeCapabilities::DRAW;
901 node.modifier_child_capabilities = node.modifier_capabilities;
902
903 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Draw)]);
904
905 assert!(node.needs_redraw());
906 assert!(!node.needs_layout());
907 }
908
909 #[test]
910 fn semantics_invalidation_sets_semantics_flag_only() {
911 let mut node = fresh_node();
912 node.clear_needs_measure();
913 node.clear_needs_layout();
914 node.clear_needs_semantics();
915 node.modifier_capabilities = NodeCapabilities::SEMANTICS;
916 node.modifier_child_capabilities = node.modifier_capabilities;
917
918 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Semantics)]);
919
920 assert!(node.needs_semantics());
921 assert!(!node.needs_measure());
922 assert!(!node.needs_layout());
923 }
924
925 #[test]
926 fn pointer_invalidation_requires_pointer_capability() {
927 let mut node = fresh_node();
928 node.clear_needs_pointer_pass();
929 node.modifier_capabilities = NodeCapabilities::DRAW;
930 node.modifier_child_capabilities = node.modifier_capabilities;
931 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
936
937 assert!(!node.needs_pointer_pass());
938 }
939
940 #[test]
941 fn pointer_invalidation_marks_flag_and_requests_queue() {
942 let mut node = fresh_node();
943 node.clear_needs_pointer_pass();
944 node.modifier_capabilities = NodeCapabilities::POINTER_INPUT;
945 node.modifier_child_capabilities = node.modifier_capabilities;
946 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
951
952 assert!(node.needs_pointer_pass());
953 }
954
955 #[test]
956 fn focus_invalidation_requires_focus_capability() {
957 let mut node = fresh_node();
958 node.clear_needs_focus_sync();
959 node.modifier_capabilities = NodeCapabilities::DRAW;
960 node.modifier_child_capabilities = node.modifier_capabilities;
961 crate::take_focus_invalidation();
962
963 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
964
965 assert!(!node.needs_focus_sync());
966 assert!(!crate::take_focus_invalidation());
967 }
968
969 #[test]
970 fn focus_invalidation_marks_flag_and_requests_queue() {
971 let mut node = fresh_node();
972 node.clear_needs_focus_sync();
973 node.modifier_capabilities = NodeCapabilities::FOCUS;
974 node.modifier_child_capabilities = node.modifier_capabilities;
975 crate::take_focus_invalidation();
976
977 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
978
979 assert!(node.needs_focus_sync());
980 assert!(crate::take_focus_invalidation());
981 }
982
983 #[test]
984 fn set_modifier_marks_semantics_dirty() {
985 let mut node = fresh_node();
986 node.clear_needs_semantics();
987 node.set_modifier(Modifier::empty().semantics(|config| {
988 config.is_clickable = true;
989 }));
990
991 assert!(node.needs_semantics());
992 }
993
994 #[test]
995 fn modifier_child_capabilities_reflect_chain_head() {
996 let mut node = fresh_node();
997 node.set_modifier(Modifier::empty().padding(4.0));
998 assert!(
999 node.modifier_child_capabilities()
1000 .contains(NodeCapabilities::LAYOUT),
1001 "padding should introduce layout capability"
1002 );
1003 }
1004}