1use std::{
2 any::TypeId,
3 cell::{Cell, RefCell},
4 collections::HashMap,
5 hash::{Hash, Hasher},
6 rc::Rc,
7};
8
9use cranpose_core::{Node, NodeId};
10use cranpose_foundation::{
11 InvalidationKind, ModifierInvalidation, ModifierInvalidations, NodeCapabilities,
12 SemanticsConfiguration,
13};
14use cranpose_ui_layout::{Constraints, MeasurePolicy};
15
16#[cfg(test)]
17use crate::layout::LayoutRuntimeDebugStats;
18use crate::{
19 layout::{LayoutRuntimeState, MeasuredNode},
20 modifier::{
21 Modifier, ModifierChainHandle, ModifierLocalSource, ModifierLocalToken,
22 ModifierLocalsHandle, ModifierNodeSlices, Point, ResolvedModifierLocal, ResolvedModifiers,
23 Size,
24 },
25};
26
27#[derive(Clone, Copy)]
28enum LayoutInvalidationDispatchDiag {
29 Disabled,
30 All,
31 Node(NodeId),
32}
33
34fn layout_invalidation_dispatch_diag() -> LayoutInvalidationDispatchDiag {
35 static MODE: std::sync::OnceLock<LayoutInvalidationDispatchDiag> = std::sync::OnceLock::new();
36 *MODE.get_or_init(|| {
37 let Some(value) = std::env::var_os("CRANPOSE_LAYOUT_INVALIDATION_DISPATCH_DIAG") else {
38 return LayoutInvalidationDispatchDiag::Disabled;
39 };
40 if value == "all" {
41 return LayoutInvalidationDispatchDiag::All;
42 }
43 value.to_string_lossy().parse::<NodeId>().map_or(
44 LayoutInvalidationDispatchDiag::Disabled,
45 LayoutInvalidationDispatchDiag::Node,
46 )
47 })
48}
49
50fn log_layout_invalidation_dispatch(
51 id: NodeId,
52 invalidation: &ModifierInvalidation,
53 curr_caps: NodeCapabilities,
54 prev_caps: NodeCapabilities,
55 modifier: &Modifier,
56) {
57 let enabled = match layout_invalidation_dispatch_diag() {
58 LayoutInvalidationDispatchDiag::Disabled => false,
59 LayoutInvalidationDispatchDiag::All => true,
60 LayoutInvalidationDispatchDiag::Node(target) => target == id,
61 };
62 if enabled {
63 log::warn!(
64 "[layout-invalidation-dispatch] node={id} invalidation={invalidation:?} curr_caps={curr_caps:?} prev_caps={prev_caps:?} modifier={modifier}"
65 );
66 }
67}
68
69pub(crate) struct PlacementPass {
73 id: Cell<u64>,
74 unplaced: Cell<usize>,
75}
76
77impl PlacementPass {
78 pub(crate) const fn new() -> Self {
79 Self {
80 id: Cell::new(1),
81 unplaced: Cell::new(0),
82 }
83 }
84}
85
86pub(crate) fn begin_placement_pass() {
88 crate::thread_state::cells(|cells| {
89 let pass = &cells.placement_pass;
90 pass.id.set(pass.id.get() + 1);
91 pass.unplaced.set(0);
92 });
93}
94
95pub(crate) fn placement_pass_with_unplaced_nodes() -> Option<u64> {
97 crate::thread_state::cells(|cells| {
98 let pass = &cells.placement_pass;
99 (pass.unplaced.get() > 0).then(|| pass.id.get())
100 })
101}
102
103#[derive(Clone, Copy, Default)]
109pub(crate) struct DescendantDirt {
110 pub(crate) layout: bool,
111 pub(crate) measure: bool,
112}
113
114impl DescendantDirt {
115 pub(crate) fn marked(self, measure: bool) -> Self {
116 Self {
117 layout: true,
118 measure: self.measure || measure,
119 }
120 }
121}
122
123#[derive(Clone, Debug, Default)]
124pub struct LayoutState {
125 size: Size,
126 position: Point,
127 is_placed: bool,
128 node_id: Option<NodeId>,
129 cleared_in_pass: u64,
131 content_offset: Point,
132 semantics_stamp: u64,
133}
134
135impl LayoutState {
136 pub fn size(&self) -> Size {
137 self.size
138 }
139
140 pub fn position(&self) -> Point {
141 self.position
142 }
143
144 pub fn content_offset(&self) -> Point {
147 self.content_offset
148 }
149
150 pub fn set_content_offset(&mut self, offset: Point) {
153 if self.content_offset != offset {
154 self.content_offset = offset;
155 self.note_semantics_change();
156 }
157 }
158
159 pub fn at_origin(mut self) -> Self {
163 self.position = Point::default();
164 self
165 }
166
167 pub fn is_placed(&self) -> bool {
168 self.is_placed
169 }
170
171 pub(crate) fn set_node_id(&mut self, node_id: NodeId) {
172 self.node_id = Some(node_id);
173 }
174
175 pub fn set_size(&mut self, size: Size) {
178 if self.size != size {
179 if let Some(id) = self.node_id {
180 crate::render_state::record_geometry_scene_node(id);
181 }
182 self.size = size;
183 self.note_semantics_change();
184 }
185 }
186
187 pub fn place(&mut self, position: Point) {
192 let newly_placed = !self.is_placed && !self.restore_placement();
193 let moved = self.position != position;
194 if let Some(id) = self.node_id {
195 if newly_placed {
196 crate::render_state::record_geometry_scene_node(id);
197 } else if moved {
198 crate::render_state::record_moved_scene_node(id);
199 }
200 }
201 self.position = position;
202 if moved || !self.is_placed {
203 self.is_placed = true;
204 self.note_semantics_change();
205 }
206 }
207
208 fn restore_placement(&self) -> bool {
211 crate::thread_state::cells(|cells| {
212 let pass = &cells.placement_pass;
213 let restored = self.cleared_in_pass == pass.id.get();
214 if restored {
215 pass.unplaced.set(pass.unplaced.get().saturating_sub(1));
216 }
217 restored
218 })
219 }
220
221 pub fn clear_placed(&mut self) {
225 if !self.is_placed {
226 return;
227 }
228 self.is_placed = false;
229 self.cleared_in_pass = crate::thread_state::cells(|cells| {
230 let pass = &cells.placement_pass;
231 pass.unplaced.set(pass.unplaced.get() + 1);
232 pass.id.get()
233 });
234 self.note_semantics_change();
235 }
236
237 pub(crate) fn unplaced_in(&self, pass: u64) -> bool {
239 !self.is_placed && self.cleared_in_pass == pass
240 }
241
242 pub(crate) fn note_semantics_change(&mut self) {
243 if let Some(id) = self.node_id {
244 crate::semantics_layout_log::record_layout_change(id, &mut self.semantics_stamp);
245 }
246 }
247}
248
249#[derive(Clone)]
250struct MeasurementCacheEntry {
251 constraints: Constraints,
252 measured: Rc<MeasuredNode>,
253 hold: Option<cranpose_ui_layout::ConstraintsHold>,
255}
256
257#[derive(Clone, Copy, Debug)]
258pub enum IntrinsicKind {
259 MinWidth(f32),
260 MaxWidth(f32),
261 MinHeight(f32),
262 MaxHeight(f32),
263}
264
265impl IntrinsicKind {
266 fn discriminant(&self) -> u8 {
267 match self {
268 IntrinsicKind::MinWidth(_) => 0,
269 IntrinsicKind::MaxWidth(_) => 1,
270 IntrinsicKind::MinHeight(_) => 2,
271 IntrinsicKind::MaxHeight(_) => 3,
272 }
273 }
274
275 fn value_bits(&self) -> u32 {
276 match self {
277 IntrinsicKind::MinWidth(value)
278 | IntrinsicKind::MaxWidth(value)
279 | IntrinsicKind::MinHeight(value)
280 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
281 }
282 }
283}
284
285impl PartialEq for IntrinsicKind {
286 fn eq(&self, other: &Self) -> bool {
287 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
288 }
289}
290
291impl Eq for IntrinsicKind {}
292
293impl Hash for IntrinsicKind {
294 fn hash<H: Hasher>(&self, state: &mut H) {
295 self.discriminant().hash(state);
296 self.value_bits().hash(state);
297 }
298}
299
300#[derive(Default)]
301struct NodeCacheState {
302 epoch: u64,
303 measurement: Option<MeasurementCacheEntry>,
304 intrinsics: Vec<(IntrinsicKind, f32)>,
305 intrinsics_read: bool,
307}
308
309#[derive(Default)]
310pub(crate) struct LayoutNodeCacheHandles {
311 state: Rc<RefCell<NodeCacheState>>,
312}
313
314impl Clone for LayoutNodeCacheHandles {
315 fn clone(&self) -> Self {
316 Self {
317 state: Rc::clone(&self.state),
318 }
319 }
320
321 fn clone_from(&mut self, source: &Self) {
322 if !Rc::ptr_eq(&self.state, &source.state) {
323 self.state = Rc::clone(&source.state);
324 }
325 }
326}
327
328impl LayoutNodeCacheHandles {
329 pub(crate) fn clear(&self) {
330 let mut state = self.state.borrow_mut();
331 state.measurement = None;
332 state.intrinsics.clear();
333 state.intrinsics_read = false;
334 state.epoch = 0;
335 }
336
337 pub(crate) fn activate(&self, epoch: u64) {
338 let mut state = self.state.borrow_mut();
339 if state.epoch != epoch {
340 state.measurement = None;
341 state.intrinsics.clear();
342 state.intrinsics_read = false;
343 state.epoch = epoch;
344 }
345 }
346
347 pub(crate) fn forget_intrinsics(&self) {
351 self.state.borrow_mut().intrinsics.clear();
352 }
353
354 pub(crate) fn epoch(&self) -> u64 {
355 self.state.borrow().epoch
356 }
357
358 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
362 let state = self.state.borrow();
363 state
364 .measurement
365 .as_ref()
366 .filter(|entry| {
367 entry.constraints == constraints
368 || entry.hold.is_some_and(|hold| hold.contains(constraints))
369 })
370 .map(|entry| Rc::clone(&entry.measured))
371 }
372
373 pub(crate) fn measured_constraints(&self) -> Option<Constraints> {
375 self.state
376 .borrow()
377 .measurement
378 .as_ref()
379 .map(|entry| entry.constraints)
380 }
381
382 pub(crate) fn has_intrinsics(&self) -> bool {
384 self.state.borrow().intrinsics_read
385 }
386
387 pub(crate) fn hold_of(
389 &self,
390 measured: &Rc<MeasuredNode>,
391 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
392 self.state
393 .borrow()
394 .measurement
395 .as_ref()
396 .filter(|entry| Rc::ptr_eq(&entry.measured, measured))
397 .and_then(|entry| entry.hold)
398 }
399
400 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
403 let mut state = self.state.borrow_mut();
404 let hold = state
405 .measurement
406 .as_ref()
407 .filter(|entry| Rc::ptr_eq(&entry.measured, &measured))
408 .and_then(|entry| entry.hold);
409 state.measurement = Some(MeasurementCacheEntry {
410 constraints,
411 measured,
412 hold,
413 });
414 }
415
416 pub(crate) fn store_held_measurement(
419 &self,
420 constraints: Constraints,
421 measured: Rc<MeasuredNode>,
422 hold: Option<cranpose_ui_layout::ConstraintsHold>,
423 ) {
424 self.state.borrow_mut().measurement = Some(MeasurementCacheEntry {
425 constraints,
426 measured,
427 hold,
428 });
429 }
430
431 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
432 let state = self.state.borrow();
433 state
434 .intrinsics
435 .iter()
436 .find(|(stored_kind, _)| stored_kind == kind)
437 .map(|(_, value)| *value)
438 }
439
440 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
441 let mut state = self.state.borrow_mut();
442 state.intrinsics_read = true;
443 if let Some((_, existing)) = state
444 .intrinsics
445 .iter_mut()
446 .find(|(stored_kind, _)| stored_kind == &kind)
447 {
448 *existing = value;
449 } else {
450 state.intrinsics.push((kind, value));
451 }
452 }
453}
454
455struct NodeIdCell(Cell<NodeId>);
459
460const NO_NODE_ID: NodeId = NodeId::MAX;
463
464impl NodeIdCell {
465 fn new(id: Option<NodeId>) -> Self {
466 Self(Cell::new(id.unwrap_or(NO_NODE_ID)))
467 }
468
469 fn get(&self) -> Option<NodeId> {
470 let id = self.0.get();
471 (id != NO_NODE_ID).then_some(id)
472 }
473
474 fn set(&self, id: Option<NodeId>) {
475 debug_assert_ne!(id, Some(NO_NODE_ID), "no node takes the none id");
476 self.0.set(id.unwrap_or(NO_NODE_ID));
477 }
478
479 fn replace(&self, id: Option<NodeId>) -> Option<NodeId> {
480 let previous = self.get();
481 self.set(id);
482 previous
483 }
484}
485
486#[repr(C)]
493pub struct LayoutNode {
494 cache: LayoutNodeCacheHandles,
495 layout_state: Rc<RefCell<LayoutState>>,
496 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
497 pub children: Vec<NodeId>,
499 parent_data: Cell<cranpose_ui_layout::ParentData>,
502 needs_measure: Cell<bool>,
503 needs_layout: Cell<bool>,
504 descendant_dirt: Cell<DescendantDirt>,
506
507 pub measure_policy: Rc<dyn MeasurePolicy>,
508 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
511 density: crate::density::Density,
512 is_virtual: bool,
513 needs_semantics: Cell<bool>,
514 descendant_needs_semantics: Cell<bool>,
518 needs_redraw: Cell<bool>,
519 needs_pointer_pass: Cell<bool>,
520 needs_focus_sync: Cell<bool>,
521 modifier_slices_dirty: Cell<bool>,
522 debug_modifiers: Cell<bool>,
523
524 id: NodeIdCell,
525 parent: NodeIdCell,
526 folded_parent: NodeIdCell,
527 modifier_slices_snapshot: crate::modifier::SlicesSnapshot,
528
529 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
533 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
534 modifier_chain: ModifierChainHandle,
535 pub modifier: Modifier,
536 #[cfg(feature = "inspection")]
538 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
539}
540
541pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
542
543pub(crate) fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
544 crate::thread_state::with(|state| {
545 Rc::clone(
546 state
547 .empty_measure_policy
548 .get_or_init(|| Rc::new(crate::layout::policies::EmptyMeasurePolicy)),
549 )
550 })
551}
552
553impl LayoutNode {
554 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
555 Self::new_with_virtual(modifier, measure_policy, false)
556 }
557
558 pub fn new_virtual() -> Self {
561 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
562 }
563
564 fn new_recycled_shell(is_virtual: bool) -> Self {
565 let mut shell =
566 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
567 shell.needs_measure.set(false);
568 shell.needs_layout.set(false);
569 shell.needs_semantics.set(false);
570 shell.descendant_needs_semantics.set(false);
571 shell.needs_redraw.set(false);
572 shell.needs_pointer_pass.set(false);
573 shell.needs_focus_sync.set(false);
574 shell.parent.set(None);
575 shell.folded_parent.set(None);
576 shell.id.set(None);
577 shell.owner_context_id.set(None);
578 shell.debug_modifiers.set(false);
579 shell.modifier_slices_dirty = Cell::new(true);
580 shell
581 }
582
583 fn new_with_virtual(
584 modifier: Modifier,
585 measure_policy: Rc<dyn MeasurePolicy>,
586 is_virtual: bool,
587 ) -> Self {
588 let layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::new(Rc::clone(
589 &measure_policy,
590 ))));
591 let mut node = Self {
592 #[cfg(feature = "inspection")]
593 source_trace: cranpose_core::source_trace::current_source_trace(),
594 modifier,
595 modifier_chain: ModifierChainHandle::new(),
596 measure_policy,
597 density: crate::density::Density::default(),
598 children: Vec::new(),
599 cache: LayoutNodeCacheHandles::default(),
600 needs_measure: Cell::new(true),
601 needs_layout: Cell::new(true),
602 descendant_dirt: Cell::default(),
603 parent_data: Cell::default(),
604 needs_semantics: Cell::new(true),
605 descendant_needs_semantics: Cell::new(false),
606 semantics_reach: Cell::new(None),
607 needs_redraw: Cell::new(true),
608 needs_pointer_pass: Cell::new(false),
609 needs_focus_sync: Cell::new(false),
610 parent: NodeIdCell::new(None),
611 folded_parent: NodeIdCell::new(None),
612 id: NodeIdCell::new(None),
613 owner_context_id: Cell::new(None),
614 debug_modifiers: Cell::new(false),
615 is_virtual,
616 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
617 modifier_slices_dirty: Cell::new(true),
618 layout_state: Rc::new(RefCell::new(LayoutState::default())),
619 layout_runtime_state,
620 coordinator_geometry: Rc::default(),
621 };
622 node.sync_modifier_chain(false);
623 node
624 }
625
626 pub fn set_modifier(&mut self, modifier: Modifier) {
627 #[cfg(feature = "inspection")]
628 {
629 let trace = cranpose_core::source_trace::current_source_trace();
630 if !trace.is_empty() {
631 self.source_trace = trace;
632 }
633 }
634 if self.modifier == modifier
638 && !self
639 .modifier_capabilities()
640 .contains(NodeCapabilities::MODIFIER_LOCALS)
641 {
642 return;
643 }
644 let text_only = self
648 .modifier
649 .differs_only_in::<crate::text_modifier_node::TextModifierElement>(&modifier);
650 let modifier_changed = text_only || !self.modifier.structural_eq(&modifier);
651 self.modifier = modifier;
652 self.sync_modifier_chain(text_only);
653 if modifier_changed {
654 self.cache.clear();
655 self.request_semantics_update();
656 }
657 }
658
659 fn sync_modifier_chain(&mut self, text_only: bool) {
663 let prev_caps = self.modifier_capabilities();
664 let start_parent = self.parent();
665 let mut resolver = move |token: &ModifierLocalToken| {
666 resolve_modifier_local_from_parent_chain(start_parent, token)
667 };
668 self.modifier_chain
669 .set_debug_logging(self.debug_modifiers.get());
670 let mut context = self.modifier_chain.context(self.id.get());
671 let in_place = text_only
672 && self
673 .modifier_chain
674 .update_elements_in_place::<crate::text_modifier_node::TextModifierElement>(
675 &self.modifier,
676 &mut context,
677 );
678 let modifier_local_invalidations = if in_place {
679 ModifierInvalidations::new()
680 } else {
681 let invalidations = self.modifier_chain.update_with_resolver(
682 &self.modifier,
683 &mut resolver,
684 &mut context,
685 );
686 self.refresh_parent_data();
687 invalidations
688 };
689 if prev_caps.contains(NodeCapabilities::WINDOW_ROOT)
690 != self
691 .modifier_capabilities()
692 .contains(NodeCapabilities::WINDOW_ROOT)
693 {
694 self.note_semantics_layout_change();
695 }
696 if !in_place {
699 self.forget_semantics_reach();
700 }
701 let keep_slices = in_place && !self.modifier_slices_dirty.get();
702 if !keep_slices {
703 self.modifier_slices_dirty.set(true);
704 }
705
706 let mut invalidations = context.take_invalidations();
707 invalidations.extend(modifier_local_invalidations);
708 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps, keep_slices);
709 if !in_place {
712 self.refresh_registry_state();
713 }
714 }
715
716 fn update_modifier_slices_cache(&self) {
717 self.modifier_slices_snapshot.collect(
718 self.modifier_chain.chain(),
719 &self.coordinator_geometry,
720 self.density.density(),
721 );
722 self.modifier_slices_dirty.set(false);
723 }
724
725 #[cfg(test)]
727 pub(crate) fn modifier_slices_ready(&self) -> bool {
728 !self.modifier_slices_dirty.get()
729 }
730
731 pub(crate) fn mark_modifier_slices_dirty(&self) {
732 self.modifier_slices_dirty.set(true);
733 }
734
735 #[cfg(test)]
736 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
737 self.dispatch_modifier_invalidations_with_prev(
738 invalidations,
739 NodeCapabilities::empty(),
740 false,
741 );
742 }
743
744 fn dispatch_modifier_invalidations_with_prev(
747 &self,
748 invalidations: &[ModifierInvalidation],
749 prev_caps: NodeCapabilities,
750 keep_slices: bool,
751 ) {
752 let curr_caps = self.modifier_capabilities();
753 for invalidation in invalidations {
754 if !keep_slices {
755 self.modifier_slices_dirty.set(true);
756 }
757 let has_capability =
758 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
759 match invalidation.kind() {
760 InvalidationKind::Layout => {
761 if has_capability(NodeCapabilities::LAYOUT) {
762 self.mark_needs_measure();
763 if let Some(id) = self.id.get() {
764 log_layout_invalidation_dispatch(
765 id,
766 invalidation,
767 curr_caps,
768 prev_caps,
769 &self.modifier,
770 );
771 let inside_composition =
772 cranpose_core::composer_context::try_with_composer(|_| ())
773 .is_some();
774 if inside_composition {
775 cranpose_core::bubble_measure_dirty_in_composer(id);
776 } else {
777 crate::schedule_layout_repass(id);
778 }
779 }
780 }
781 }
782 InvalidationKind::Draw => {
783 if has_capability(NodeCapabilities::DRAW)
784 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
785 {
786 self.mark_needs_redraw();
787 }
788 }
789 InvalidationKind::PointerInput => {
790 if has_capability(NodeCapabilities::POINTER_INPUT) {
791 self.mark_needs_pointer_pass();
792 crate::request_pointer_invalidation();
793 if let Some(id) = self.id.get() {
794 crate::schedule_pointer_repass(id);
795 }
796 }
797 }
798 InvalidationKind::Semantics => {
799 self.request_semantics_update();
800 }
801 InvalidationKind::Focus => {
802 if has_capability(NodeCapabilities::FOCUS) {
803 self.mark_needs_focus_sync();
804 crate::request_focus_invalidation();
805 if let Some(id) = self.id.get() {
806 crate::schedule_focus_invalidation(id);
807 }
808 }
809 }
810 }
811 }
812 }
813
814 pub fn density(&self) -> crate::density::Density {
816 self.density
817 }
818
819 pub fn set_density(&mut self, density: crate::density::Density) {
821 if self.density != density {
822 self.density = density;
823 self.cache.clear();
824 self.modifier_slices_dirty.set(true);
825 self.mark_needs_measure();
826 }
827 }
828
829 pub fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
831 if self.modifier_chain.set_layout_direction(direction) {
832 self.refresh_parent_data();
833 self.cache.clear();
834 self.modifier_slices_dirty.set(true);
835 self.mark_needs_measure();
836 }
837 }
838
839 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
840 if !Rc::ptr_eq(&self.measure_policy, &policy) {
841 self.measure_policy = policy;
842 self.cache.clear();
843 self.mark_needs_measure();
844 if let Some(id) = self.id.get() {
845 cranpose_core::bubble_measure_dirty_in_composer(id);
846 }
847 }
848 }
849
850 pub fn mark_needs_measure(&self) {
852 self.needs_measure.set(true);
853 self.needs_layout.set(true);
854 }
855
856 pub fn mark_needs_layout(&self) {
858 self.needs_layout.set(true);
859 }
860
861 pub fn mark_needs_redraw(&self) {
863 self.needs_redraw.set(true);
864 if let Some(id) = self.id.get() {
865 crate::schedule_draw_repass(id);
866 }
867 crate::request_render_invalidation();
868 }
869
870 pub fn needs_measure(&self) -> bool {
872 self.needs_measure.get()
873 }
874
875 pub fn needs_layout(&self) -> bool {
877 self.needs_layout.get()
878 }
879
880 pub fn mark_needs_semantics(&self) {
882 self.needs_semantics.set(true);
883 self.forget_semantics_reach();
884 }
885
886 pub(crate) fn clear_needs_semantics(&self) {
887 self.needs_semantics.set(false);
888 self.descendant_needs_semantics.set(false);
889 }
890
891 pub fn needs_semantics(&self) -> bool {
894 self.needs_semantics.get() || self.descendant_needs_semantics.get()
895 }
896
897 pub(crate) fn semantics_changed(&self) -> bool {
900 self.needs_semantics.get()
901 }
902
903 pub fn needs_redraw(&self) -> bool {
905 self.needs_redraw.get()
906 }
907
908 pub fn clear_needs_redraw(&self) {
909 self.needs_redraw.set(false);
910 }
911
912 fn request_semantics_update(&self) {
913 let already_dirty = self.needs_semantics.replace(true);
914 if already_dirty {
915 return;
916 }
917
918 if let Some(id) = self.id.get() {
919 cranpose_core::queue_semantics_invalidation(id);
920 }
921 }
922
923 pub(crate) fn clear_needs_measure(&self) {
924 self.needs_measure.set(false);
925 }
926
927 pub(crate) fn clear_needs_layout(&self) {
930 self.needs_layout.set(false);
931 self.descendant_dirt.set(DescendantDirt::default());
932 }
933
934 pub fn mark_needs_pointer_pass(&self) {
936 self.needs_pointer_pass.set(true);
937 }
938
939 pub fn needs_pointer_pass(&self) -> bool {
941 self.needs_pointer_pass.get()
942 }
943
944 pub fn clear_needs_pointer_pass(&self) {
946 self.needs_pointer_pass.set(false);
947 }
948
949 pub fn mark_needs_focus_sync(&self) {
951 self.needs_focus_sync.set(true);
952 }
953
954 pub fn needs_focus_sync(&self) -> bool {
956 self.needs_focus_sync.get()
957 }
958
959 pub fn clear_needs_focus_sync(&self) {
961 self.needs_focus_sync.set(false);
962 }
963
964 pub fn set_node_id(&mut self, id: NodeId) {
966 let previous = self.id.replace(Some(id));
967 if let Some(existing) = previous
968 && let Some(owner_context_id) = self.owner_context_id.take()
969 {
970 unregister_layout_node(owner_context_id, existing);
971 }
972 self.layout_state.borrow_mut().set_node_id(id);
973 let owner_context_id = register_layout_node(id, self);
974 self.owner_context_id.set(Some(owner_context_id));
975 self.refresh_registry_state();
976
977 let mut context = self.modifier_chain.context(Some(id));
978 self.modifier_chain.move_to_node(previous, &mut context);
979 let invalidations = context.take_invalidations();
980 self.dispatch_modifier_invalidations_with_prev(
981 &invalidations,
982 NodeCapabilities::empty(),
983 false,
984 );
985 self.modifier_slices_dirty.set(true);
987 }
988
989 pub fn node_id(&self) -> Option<NodeId> {
991 self.id.get()
992 }
993
994 pub fn set_parent(&self, parent: NodeId) {
997 self.folded_parent.set(Some(parent));
998 self.parent.set(Some(parent));
999 self.refresh_registry_state();
1000 }
1001
1002 pub fn clear_parent(&self) {
1004 self.folded_parent.set(None);
1005 self.parent.set(None);
1006 self.refresh_registry_state();
1007 }
1008
1009 pub fn parent(&self) -> Option<NodeId> {
1011 self.parent.get()
1012 }
1013
1014 pub fn folded_parent(&self) -> Option<NodeId> {
1016 self.folded_parent.get()
1017 }
1018
1019 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
1020 &self.cache
1021 }
1022
1023 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1024 self.modifier_chain.resolved_modifiers()
1025 }
1026
1027 pub(crate) fn parent_data(&self) -> cranpose_ui_layout::ParentData {
1029 self.parent_data.get()
1030 }
1031
1032 fn refresh_parent_data(&self) {
1035 self.parent_data.set(crate::layout::parent_data_of(
1036 self.modifier_chain.resolved_modifiers().layout_properties(),
1037 ));
1038 }
1039
1040 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1041 self.modifier_chain.capabilities()
1042 }
1043
1044 pub fn is_window_root(&self) -> bool {
1048 self.modifier_capabilities()
1049 .contains(NodeCapabilities::WINDOW_ROOT)
1050 }
1051
1052 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
1053 self.modifier_chain.aggregate_child_capabilities()
1054 }
1055
1056 pub fn set_debug_modifiers(&mut self, enabled: bool) {
1057 self.debug_modifiers.set(enabled);
1058 self.modifier_chain.set_debug_logging(enabled);
1059 }
1060
1061 pub fn debug_modifiers_enabled(&self) -> bool {
1062 self.debug_modifiers.get()
1063 }
1064
1065 pub fn modifier_locals_handle(&self) -> Option<ModifierLocalsHandle> {
1068 self.modifier_chain.modifier_locals_handle()
1069 }
1070
1071 pub fn has_layout_modifier_nodes(&self) -> bool {
1072 self.modifier_capabilities()
1073 .contains(NodeCapabilities::LAYOUT)
1074 }
1075
1076 pub fn has_draw_modifier_nodes(&self) -> bool {
1077 self.modifier_capabilities()
1078 .contains(NodeCapabilities::DRAW)
1079 }
1080
1081 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1082 self.modifier_capabilities()
1083 .contains(NodeCapabilities::POINTER_INPUT)
1084 }
1085
1086 pub fn has_semantics_modifier_nodes(&self) -> bool {
1087 self.modifier_capabilities()
1088 .contains(NodeCapabilities::SEMANTICS)
1089 }
1090
1091 pub fn has_focus_modifier_nodes(&self) -> bool {
1092 self.modifier_capabilities()
1093 .contains(NodeCapabilities::FOCUS)
1094 }
1095
1096 fn refresh_registry_state(&self) {
1097 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1098 let parent = self.parent();
1099 let capabilities = self.modifier_child_capabilities();
1100 let modifier_locals = self.modifier_locals_handle();
1101 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1102 owner_context_id,
1103 |registry| {
1104 registry.update_entry(id, parent, capabilities, modifier_locals);
1105 },
1106 );
1107 }
1108 }
1109
1110 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1111 if self.modifier_slices_dirty.get() {
1112 self.update_modifier_slices_cache();
1113 }
1114 self.modifier_slices_snapshot.get()
1115 }
1116
1117 pub fn layout_state(&self) -> LayoutState {
1119 self.layout_state.borrow().clone()
1120 }
1121
1122 pub fn measured_size(&self) -> Size {
1124 self.layout_state.borrow().size
1125 }
1126
1127 pub fn position(&self) -> Point {
1129 self.layout_state.borrow().position
1130 }
1131
1132 pub fn is_placed(&self) -> bool {
1134 self.layout_state.borrow().is_placed
1135 }
1136
1137 pub fn set_measured_size(&self, size: Size) {
1141 self.layout_state.borrow_mut().set_size(size);
1142 }
1143
1144 pub fn set_position(&self, position: Point) {
1147 self.layout_state.borrow_mut().place(position);
1148 }
1149
1150 pub fn set_content_offset(&self, offset: Point) {
1152 self.layout_state.borrow_mut().set_content_offset(offset);
1153 }
1154
1155 pub fn clear_placed(&self) {
1157 self.layout_state.borrow_mut().clear_placed();
1158 }
1159
1160 fn note_semantics_layout_change(&self) {
1161 if let Ok(mut state) = self.layout_state.try_borrow_mut() {
1162 state.note_semantics_change();
1163 }
1164 }
1165
1166 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
1167 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
1168 }
1169
1170 fn forget_semantics_reach(&self) {
1172 self.semantics_reach.set(None);
1173 crate::modal_nodes::reach_changed(self.id.get());
1174 }
1175
1176 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1178 if !self
1180 .modifier_capabilities()
1181 .contains(NodeCapabilities::SEMANTICS)
1182 {
1183 return cranpose_foundation::SemanticsReach::default();
1184 }
1185 if let Some(reach) = self.semantics_reach.get() {
1186 return reach;
1187 }
1188 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
1189 self.semantics_reach.set(Some(reach));
1190 reach
1191 }
1192
1193 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
1194 &self.modifier_chain
1195 }
1196
1197 pub fn with_text_field_modifier_mut<R>(
1202 &mut self,
1203 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
1204 ) -> Option<R> {
1205 self.modifier_chain.with_text_field_modifier_mut(f)
1206 }
1207
1208 pub fn layout_state_handle(&self) -> &Rc<RefCell<LayoutState>> {
1211 &self.layout_state
1212 }
1213
1214 pub(crate) fn coordinator_geometry(&self) -> &crate::modifier::CoordinatorGeometry {
1215 &self.coordinator_geometry
1216 }
1217
1218 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
1219 self.layout_runtime_state.clone()
1220 }
1221
1222 #[cfg(test)]
1223 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
1224 self.layout_runtime_state.borrow().debug_stats()
1225 }
1226}
1227impl Clone for LayoutNode {
1228 fn clone(&self) -> Self {
1229 let mut node = Self {
1230 #[cfg(feature = "inspection")]
1231 source_trace: self.source_trace.clone(),
1232 modifier: self.modifier.clone(),
1233 modifier_chain: ModifierChainHandle::for_layout_direction(
1234 self.modifier_chain.layout_direction(),
1235 ),
1236 measure_policy: self.measure_policy.clone(),
1237 density: self.density,
1238 children: self.children.clone(),
1239 cache: self.cache.clone(),
1240 needs_measure: Cell::new(self.needs_measure.get()),
1241 needs_layout: Cell::new(self.needs_layout.get()),
1242 descendant_dirt: Cell::new(self.descendant_dirt.get()),
1243 parent_data: Cell::new(self.parent_data.get()),
1244 needs_semantics: Cell::new(self.needs_semantics.get()),
1245 descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
1246 semantics_reach: Cell::new(None),
1247 needs_redraw: Cell::new(self.needs_redraw.get()),
1248 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
1249 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
1250 parent: NodeIdCell::new(self.parent.get()),
1251 folded_parent: NodeIdCell::new(self.folded_parent.get()),
1252 id: NodeIdCell::new(None),
1253 owner_context_id: Cell::new(None),
1254 debug_modifiers: Cell::new(self.debug_modifiers.get()),
1255 is_virtual: self.is_virtual,
1256 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
1257 modifier_slices_dirty: Cell::new(true),
1258 layout_state: self.layout_state.clone(),
1259 layout_runtime_state: self.layout_runtime_state.clone(),
1260 coordinator_geometry: Rc::clone(&self.coordinator_geometry),
1261 };
1262 node.sync_modifier_chain(false);
1263 node
1264 }
1265}
1266
1267impl Node for LayoutNode {
1268 fn mount(&mut self) {
1269 self.modifier_chain.remount(self.id.get());
1270 crate::modal_nodes::reach_changed(self.id.get());
1271 }
1272
1273 fn unmount(&mut self) {
1274 self.modifier_chain.chain_mut().detach_nodes();
1275 }
1276
1277 fn set_node_id(&mut self, id: NodeId) {
1278 LayoutNode::set_node_id(self, id);
1279 }
1280
1281 fn insert_child(&mut self, child: NodeId) -> bool {
1282 if self.children.contains(&child) {
1283 return false;
1284 }
1285 self.children.push(child);
1286 self.cache.clear();
1287 self.mark_needs_measure();
1288 self.note_semantics_layout_change();
1289 true
1290 }
1291
1292 fn remove_child(&mut self, child: NodeId) -> bool {
1293 let before = self.children.len();
1294 self.children.retain(|&id| id != child);
1295 let removed = self.children.len() < before;
1296 if removed {
1297 self.cache.clear();
1298 self.mark_needs_measure();
1299 self.note_semantics_layout_change();
1300 }
1301 removed
1302 }
1303
1304 fn move_child(&mut self, from: usize, to: usize) {
1305 if from == to || from >= self.children.len() {
1306 return;
1307 }
1308 let child = self.children.remove(from);
1309 let target = to.min(self.children.len());
1310 self.children.insert(target, child);
1311 self.cache.clear();
1312 self.mark_needs_measure();
1313 self.note_semantics_layout_change();
1314 }
1315
1316 fn update_children(&mut self, children: &[NodeId]) {
1317 self.children.clear();
1318 self.children.extend_from_slice(children);
1319 self.cache.clear();
1320 self.mark_needs_measure();
1321 self.note_semantics_layout_change();
1322 }
1323
1324 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
1325 out.clear();
1326 out.extend_from_slice(&self.children);
1327 }
1328
1329 fn owned_child_index(&self, child: NodeId) -> Option<usize> {
1330 self.children.iter().position(|&id| id == child)
1331 }
1332
1333 fn on_attached_to_parent(&mut self, parent: NodeId) {
1334 self.set_parent(parent);
1335 }
1336
1337 fn on_removed_from_parent(&mut self) {
1338 self.clear_parent();
1339 }
1340
1341 fn parent(&self) -> Option<NodeId> {
1342 self.parent.get()
1343 }
1344
1345 fn mark_needs_layout(&self) {
1346 self.needs_layout.set(true);
1347 }
1348
1349 fn needs_layout(&self) -> bool {
1350 self.needs_layout.get()
1351 }
1352
1353 fn mark_needs_measure(&self) {
1354 self.needs_measure.set(true);
1355 self.needs_layout.set(true);
1356 }
1357
1358 fn needs_measure(&self) -> bool {
1359 self.needs_measure.get()
1360 }
1361
1362 fn mark_descendant_needs_layout(&self, measure: bool) {
1363 self.descendant_dirt
1364 .set(self.descendant_dirt.get().marked(measure));
1365 if measure {
1366 self.cache.forget_intrinsics();
1367 }
1368 }
1369
1370 fn descendant_needs_layout(&self) -> bool {
1371 self.descendant_dirt.get().layout
1372 }
1373
1374 fn is_virtual(&self) -> bool {
1375 self.is_virtual
1376 }
1377
1378 fn descendant_needs_measure(&self) -> bool {
1379 self.descendant_dirt.get().measure
1380 }
1381
1382 fn mark_needs_semantics(&self) {
1383 self.needs_semantics.set(true);
1384 self.forget_semantics_reach();
1385 }
1386
1387 fn mark_descendant_needs_semantics(&self) {
1388 self.descendant_needs_semantics.set(true);
1389 }
1390
1391 fn needs_semantics(&self) -> bool {
1392 LayoutNode::needs_semantics(self)
1393 }
1394
1395 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1396 if self.parent.get().is_none() {
1397 self.parent.set(Some(parent));
1398 }
1399 }
1400
1401 fn recycle_key(&self) -> Option<TypeId> {
1402 Some(TypeId::of::<Self>())
1403 }
1404
1405 fn recycle_pool_limit(&self) -> Option<usize> {
1406 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1407 }
1408
1409 fn prepare_for_recycle(&mut self) {
1410 *self = Self::new_recycled_shell(self.is_virtual);
1411 }
1412
1413 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1414 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1415 }
1416
1417 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1418 let mut node = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1424 node.children.shrink_to_fit();
1425 Some(Box::new(node))
1426 }
1427}
1428
1429impl Drop for LayoutNode {
1430 fn drop(&mut self) {
1431 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1432 unregister_layout_node(owner_context_id, id);
1433 }
1434 }
1435}
1436
1437const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1438const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1439
1440#[cfg(test)]
1441#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1442struct LayoutNodeRegistryDebugStats {
1443 len: usize,
1444 capacity: usize,
1445}
1446
1447struct LayoutNodeRegistryEntry {
1448 parent: Option<NodeId>,
1449 modifier_child_capabilities: NodeCapabilities,
1450 modifier_locals: Option<ModifierLocalsHandle>,
1451}
1452
1453pub(crate) struct LayoutNodeRegistryState {
1454 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1455 virtual_node_id_counter: Cell<NodeId>,
1456}
1457
1458impl LayoutNodeRegistryState {
1459 pub(crate) fn new() -> Self {
1460 Self {
1461 entries: RefCell::new(HashMap::new()),
1462 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1463 }
1464 }
1465
1466 fn register(&self, id: NodeId, node: &LayoutNode) {
1467 self.entries.borrow_mut().insert(
1468 id,
1469 LayoutNodeRegistryEntry {
1470 parent: node.parent(),
1471 modifier_child_capabilities: node.modifier_child_capabilities(),
1472 modifier_locals: node.modifier_locals_handle(),
1473 },
1474 );
1475 }
1476
1477 fn unregister(&self, id: NodeId) {
1478 let mut entries = self.entries.borrow_mut();
1479 entries.remove(&id);
1480 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1481 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1482 || entries.capacity()
1483 > entries
1484 .len()
1485 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1486 .saturating_mul(4);
1487 if should_shrink {
1488 let retained = entries
1489 .len()
1490 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1491 let mut rebuilt = HashMap::new();
1492 rebuilt.reserve(retained);
1493 rebuilt.extend(entries.drain());
1494 *entries = rebuilt;
1495 }
1496 }
1497
1498 fn update_entry(
1499 &self,
1500 id: NodeId,
1501 parent: Option<NodeId>,
1502 modifier_child_capabilities: NodeCapabilities,
1503 modifier_locals: Option<ModifierLocalsHandle>,
1504 ) {
1505 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1506 entry.parent = parent;
1507 entry.modifier_child_capabilities = modifier_child_capabilities;
1508 entry.modifier_locals = modifier_locals;
1509 }
1510 }
1511
1512 #[cfg(test)]
1513 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1514 let entries = self.entries.borrow();
1515 LayoutNodeRegistryDebugStats {
1516 len: entries.len(),
1517 capacity: entries.capacity(),
1518 }
1519 }
1520
1521 fn allocate_virtual_node_id(&self) -> NodeId {
1522 let id = self.virtual_node_id_counter.get();
1523 self.virtual_node_id_counter.set(id.wrapping_add(1));
1524 id
1525 }
1526
1527 fn resolve_modifier_local_from_parent_chain(
1528 &self,
1529 start: Option<NodeId>,
1530 token: &ModifierLocalToken,
1531 ) -> Option<ResolvedModifierLocal> {
1532 let mut current = start;
1533 while let Some(parent_id) = current {
1534 let (next_parent, resolved) = {
1535 let entries = self.entries.borrow();
1536 if let Some(entry) = entries.get(&parent_id) {
1537 let resolved = entry
1538 .modifier_child_capabilities
1539 .contains(NodeCapabilities::MODIFIER_LOCALS)
1540 .then_some(entry.modifier_locals.as_ref())
1541 .flatten()
1542 .and_then(|locals| locals.borrow().resolve(token))
1543 .map(|value| value.with_source(ModifierLocalSource::Ancestor));
1544 (entry.parent, resolved)
1545 } else {
1546 (None, None)
1547 }
1548 };
1549 if let Some(value) = resolved {
1550 return Some(value);
1551 }
1552 current = next_parent;
1553 }
1554 None
1555 }
1556}
1557
1558pub(crate) fn register_layout_node(
1559 id: NodeId,
1560 node: &LayoutNode,
1561) -> crate::render_state::AppContextId {
1562 let owner_context_id = crate::render_state::current_app_context_id();
1563 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1564 owner_context_id,
1565 |registry| {
1566 registry.register(id, node);
1567 },
1568 );
1569 owner_context_id
1570}
1571
1572pub(crate) fn unregister_layout_node(
1573 owner_context_id: crate::render_state::AppContextId,
1574 id: NodeId,
1575) {
1576 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1577 owner_context_id,
1578 |registry| {
1579 registry.unregister(id);
1580 },
1581 );
1582}
1583
1584#[cfg(test)]
1585fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1586 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1587}
1588
1589pub(crate) fn allocate_virtual_node_id() -> NodeId {
1590 crate::render_state::with_layout_node_registry(
1591 LayoutNodeRegistryState::allocate_virtual_node_id,
1592 )
1593}
1594
1595fn resolve_modifier_local_from_parent_chain(
1596 start: Option<NodeId>,
1597 token: &ModifierLocalToken,
1598) -> Option<ResolvedModifierLocal> {
1599 crate::render_state::with_layout_node_registry(|registry| {
1600 registry.resolve_modifier_local_from_parent_chain(start, token)
1601 })
1602}
1603
1604#[cfg(test)]
1605#[path = "tests/layout_node_tests.rs"]
1606mod tests;