1use std::{
2 cell::{Cell, Ref, RefCell, RefMut},
3 collections::HashMap,
4 rc::Rc,
5};
6
7use cranpose_core::{
8 Composer, NodeError, NodeId, Phase, SlotId, SlotTable, SlotsHost, SnapshotStateObserver,
9 SubcomposeState,
10};
11use cranpose_foundation::{
12 InvalidationKind, ModifierInvalidation, ModifierInvalidations, NodeCapabilities,
13 SemanticsConfiguration,
14};
15pub use cranpose_ui_layout::{Constraints, MeasureResult, Placement};
16use smallvec::SmallVec;
17use web_time::Instant;
18
19use crate::{
20 layout::MeasuredNode,
21 modifier::{Modifier, ModifierChainHandle, ModifierNodeSlices, Point, ResolvedModifiers, Size},
22 widgets::nodes::{
23 LayoutNode, LayoutNodeCacheHandles, LayoutState, allocate_virtual_node_id, is_virtual_node,
24 register_layout_node,
25 },
26};
27
28fn subcompose_telemetry_enabled() -> bool {
29 cranpose_core::env_flag!("CRANPOSE_SUBCOMPOSE_TELEMETRY")
30}
31
32#[derive(Clone, Copy, Debug)]
33pub struct SubcomposeChild {
34 node_id: NodeId,
35 measured_size: Option<Size>,
36}
37
38impl SubcomposeChild {
39 pub fn new(node_id: NodeId) -> Self {
40 Self {
41 node_id,
42 measured_size: None,
43 }
44 }
45
46 pub fn with_size(node_id: NodeId, size: Size) -> Self {
47 Self {
48 node_id,
49 measured_size: Some(size),
50 }
51 }
52
53 pub fn node_id(&self) -> NodeId {
54 self.node_id
55 }
56
57 pub fn size(&self) -> Size {
58 self.measured_size.unwrap_or(Size {
59 width: 0.0,
60 height: 0.0,
61 })
62 }
63
64 pub fn width(&self) -> f32 {
65 self.size().width
66 }
67
68 pub fn height(&self) -> f32 {
69 self.size().height
70 }
71
72 pub fn set_size(&mut self, size: Size) {
73 self.measured_size = Some(size);
74 }
75}
76
77impl PartialEq for SubcomposeChild {
78 fn eq(&self, other: &Self) -> bool {
79 self.node_id == other.node_id
80 }
81}
82
83pub type SubcomposePlaceable = cranpose_ui_layout::Placeable;
84
85type CachedMeasureBatchRegistrar<'a> =
86 Box<dyn FnMut(&[NodeId], Constraints, &mut Vec<Option<Size>>) + 'a>;
87type RetainedMeasureLookup<'a> = Box<dyn FnMut(NodeId) -> Option<Rc<MeasuredNode>> + 'a>;
88type RetainedMeasureRegistrar<'a> = Box<dyn FnMut(&[Rc<MeasuredNode>]) + 'a>;
89
90pub(crate) struct CachedBatchMeasureInputs<'a> {
91 pub(crate) measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
92 pub(crate) cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
93 pub(crate) retained_measure_lookup: RetainedMeasureLookup<'a>,
94 pub(crate) retained_measure_registrar: RetainedMeasureRegistrar<'a>,
95 pub(crate) error: &'a RefCell<Option<NodeError>>,
96}
97
98pub trait SubcomposeLayoutScope: cranpose_ui_layout::MeasureScope {
100 fn constraints(&self) -> Constraints;
101
102 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
103 where
104 I: IntoIterator<Item = Placement>,
105 {
106 MeasureResult::new(Size { width, height }, placements.into_iter().collect())
107 }
108}
109
110pub trait SubcomposeMeasureScope: SubcomposeLayoutScope {
112 fn subcompose<K, Content>(
127 &mut self,
128 slot_id: SlotId,
129 key: K,
130 content: Content,
131 ) -> Vec<SubcomposeChild>
132 where
133 K: PartialEq + 'static,
134 Content: FnMut() + 'static;
135
136 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable;
138
139 fn node_has_no_parent(&self, node_id: NodeId) -> bool;
142}
143
144pub struct SubcomposeMeasureScopeImpl<'a> {
146 composer: Composer,
147 density_scope: crate::density::DensityMeasureScope,
148 state: &'a mut SubcomposeState,
149 constraints: Constraints,
150 measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
151 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
152 retained_measure_lookup: RetainedMeasureLookup<'a>,
153 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
154 error: &'a RefCell<Option<NodeError>>,
155 parent_handle: SubcomposeLayoutNodeHandle,
156 root_id: NodeId,
157 placement_scratch: Vec<Placement>,
158 cached_measure_node_scratch: Vec<NodeId>,
159 cached_measure_size_scratch: Vec<Option<Size>>,
160 cached_measure_missing_scratch: Vec<NodeId>,
161 registered_measurement_node_ids: Vec<NodeId>,
162 pending_commands_applied: bool,
163 #[cfg(debug_assertions)]
164 shadow_stash: Option<(Vec<NodeId>, Vec<NodeId>)>,
165}
166
167thread_local! {
168 static CLEAN_SLOT_SKIPS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
169}
170
171fn record_clean_slot_skip() {
172 CLEAN_SLOT_SKIPS.with(|count| count.set(count.get() + 1));
173}
174
175pub fn clean_slot_skip_count() -> u64 {
179 CLEAN_SLOT_SKIPS.with(Cell::get)
180}
181
182struct SubcomposeMeasureScopeInit<'a> {
183 composer: Composer,
184 density: crate::density::Density,
185 state: &'a mut SubcomposeState,
186 constraints: Constraints,
187 measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
188 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
189 retained_measure_lookup: RetainedMeasureLookup<'a>,
190 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
191 error: &'a RefCell<Option<NodeError>>,
192 parent_handle: SubcomposeLayoutNodeHandle,
193 root_id: NodeId,
194 placement_scratch: Vec<Placement>,
195}
196
197impl<'a> SubcomposeMeasureScopeImpl<'a> {
198 pub(crate) fn root_id(&self) -> NodeId {
199 self.root_id
200 }
201
202 fn new(init: SubcomposeMeasureScopeInit<'a>) -> Self {
203 Self {
204 composer: init.composer,
205 density_scope: crate::density::DensityMeasureScope::new(init.density),
206 state: init.state,
207 constraints: init.constraints,
208 measurer: init.measurer,
209 cached_measure_batch_registrar: init.cached_measure_batch_registrar,
210 retained_measure_lookup: init.retained_measure_lookup,
211 retained_measure_registrar: init.retained_measure_registrar,
212 error: init.error,
213 parent_handle: init.parent_handle,
214 root_id: init.root_id,
215 placement_scratch: init.placement_scratch,
216 cached_measure_node_scratch: Vec::new(),
217 cached_measure_size_scratch: Vec::new(),
218 cached_measure_missing_scratch: Vec::new(),
219 registered_measurement_node_ids: Vec::new(),
220 pending_commands_applied: false,
221 #[cfg(debug_assertions)]
222 shadow_stash: None,
223 }
224 }
225
226 fn register_measurement_node_id(&mut self, node_id: NodeId) {
227 if !self.registered_measurement_node_ids.contains(&node_id) {
228 self.registered_measurement_node_ids.push(node_id);
229 }
230 }
231
232 fn into_placement_scratch(self) -> Vec<Placement> {
233 self.placement_scratch
234 }
235
236 pub(crate) fn layout_with_placement_builder(
237 &mut self,
238 width: f32,
239 height: f32,
240 build: impl FnOnce(&mut Vec<Placement>),
241 ) -> MeasureResult {
242 self.placement_scratch.clear();
243 build(&mut self.placement_scratch);
244 MeasureResult::new(
245 Size { width, height },
246 std::mem::take(&mut self.placement_scratch),
247 )
248 }
249
250 fn record_error(&self, err: NodeError) {
251 let mut slot = self.error.borrow_mut();
252 if slot.is_none() {
253 eprintln!("[SubcomposeLayout] Error suppressed: {err:?}");
254 *slot = Some(err);
255 }
256 }
257
258 fn owner_chain_deactivation_epoch(&self) -> u64 {
259 self.parent_handle
260 .inner
261 .borrow()
262 .captured_context
263 .as_ref()
264 .map_or(
265 0,
266 cranpose_core::CapturedCompositionContext::owner_chain_deactivation_epoch,
267 )
268 }
269
270 fn ensure_pending_commands_applied(&mut self) -> bool {
271 if self.pending_commands_applied {
272 return true;
273 }
274
275 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
276 if let Err(err) = self.composer.apply_pending_commands() {
277 self.record_error(err);
278 return false;
279 }
280 if let Some(start) = telemetry_start {
281 log::warn!(
282 "[subcompose-telemetry] apply_pending_commands_ms={:.2}",
283 start.elapsed().as_secs_f64() * 1000.0
284 );
285 }
286
287 self.pending_commands_applied = true;
288 true
289 }
290
291 fn perform_subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<NodeId>
292 where
293 Content: FnMut() + 'static,
294 {
295 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
296 let mut inner = self.parent_handle.inner.borrow_mut();
297
298 let (virtual_node_id, is_rebound) =
299 if let Some((node_id, rebound)) = self.state.take_node_from_reusables(slot_id) {
300 (node_id, rebound)
301 } else {
302 let id = allocate_virtual_node_id();
303 let node = LayoutNode::new_virtual();
304 if let Err(e) = self
305 .composer
306 .register_virtual_node(id, Box::new(node.clone()))
307 {
308 eprintln!("[Subcompose] Failed to register virtual node {id}: {e:?}");
309 }
310 register_layout_node(id, &node);
311
312 inner.virtual_nodes.insert(id, Rc::new(node));
313 inner.children.push(id);
314 (id, false)
315 };
316
317 self.composer.record_subcompose_child(virtual_node_id);
318
319 if let Some(v_node) = inner.virtual_nodes.get(&virtual_node_id) {
320 v_node.set_parent(self.root_id);
321 }
322
323 drop(inner);
324
325 let children = self.compose_into_slot(slot_id, virtual_node_id, content);
326 if is_rebound {
327 self.composer.record_rebound_slot_children(&children);
328 }
329 if let Some(start) = telemetry_start {
330 log::warn!(
331 "[subcompose-telemetry] slot={} reused={} children={} subcompose_ms={:.2}",
332 slot_id.raw(),
333 is_rebound,
334 children.len(),
335 start.elapsed().as_secs_f64() * 1000.0
336 );
337 }
338 children
339 }
340
341 fn compose_into_slot<Content>(
342 &mut self,
343 slot_id: SlotId,
344 virtual_node_id: NodeId,
345 content: Content,
346 ) -> Vec<NodeId>
347 where
348 Content: FnMut() + 'static,
349 {
350 let content_holder = self.state.callback_holder(slot_id);
351 content_holder.update(content);
352
353 let _ = self
354 .composer
355 .with_node_mut::<LayoutNode, _>(virtual_node_id, |node| {
356 node.set_parent(self.root_id);
357 });
358
359 let slot_host = self.state.get_or_create_slots(slot_id);
360 self.parent_handle.note_slot_host(&slot_host);
361 let scopes = self
362 .composer
363 .subcompose_slot(&slot_host, Some(virtual_node_id), move |_| {
364 compose_subcompose_slot_content(content_holder);
365 })
366 .map(|((), scopes)| scopes)
367 .unwrap_or_default();
368 self.pending_commands_applied = false;
369
370 let owner_epoch = self.owner_chain_deactivation_epoch();
371 self.state
372 .register_active(slot_id, &[virtual_node_id], &scopes);
373 self.state.mark_slot_composed_current(slot_id, owner_epoch);
374
375 self.composer.get_node_children(virtual_node_id).to_vec()
376 }
377
378 fn activate_clean_retained_slot(&mut self, slot_id: SlotId) -> Option<Vec<NodeId>> {
379 if self.state.has_pending_precompositions(slot_id) {
380 return None;
381 }
382 if !self
383 .state
384 .slot_content_generation_current(slot_id, self.owner_chain_deactivation_epoch())
385 {
386 return None;
387 }
388 if !self.ensure_pending_commands_applied() {
389 return None;
390 }
391 let virtual_node_ids = self.state.activate_current_active_slot(slot_id)?;
392
393 {
394 let inner = self.parent_handle.inner.borrow();
395 for virtual_node_id in &virtual_node_ids {
396 self.composer.record_subcompose_child(*virtual_node_id);
397 if let Some(v_node) = inner.virtual_nodes.get(virtual_node_id) {
398 v_node.set_parent(self.root_id);
399 }
400 }
401 }
402 for virtual_node_id in &virtual_node_ids {
403 let _ = self
404 .composer
405 .with_node_mut::<LayoutNode, _>(*virtual_node_id, |node| {
406 node.set_parent(self.root_id);
407 });
408 }
409
410 let mut children = Vec::new();
411 for virtual_node_id in &virtual_node_ids {
412 children.extend(self.composer.get_node_children(*virtual_node_id));
413 }
414 record_clean_slot_skip();
415
416 #[cfg(debug_assertions)]
417 {
418 self.shadow_stash = Some((virtual_node_ids, children.clone()));
419 }
420
421 Some(children)
422 }
423
424 #[cfg(debug_assertions)]
425 fn shadow_verify_clean_slot<Content>(&mut self, slot_id: SlotId, content: Content)
426 where
427 Content: FnMut() + 'static,
428 {
429 let Some((virtual_node_ids, skipped_children)) = self.shadow_stash.take() else {
430 return;
431 };
432 if virtual_node_ids.len() != 1 {
433 return;
434 }
435 let composed = self.compose_into_slot(slot_id, virtual_node_ids[0], content);
436 assert!(
437 composed == skipped_children,
438 "clean-slot skip diverged for slot {slot_id:?}: recomposing produced root \
439 children {composed:?} but the retained slot held {skipped_children:?}. The slot content \
440 read a value that changed between measure passes without any \
441 invalidation path — make that value reactive state, or part of \
442 the subcompose capture key",
443 );
444 }
445
446 pub(crate) fn activate_exact_retained_slot_with_known_children(
447 &mut self,
448 slot_id: SlotId,
449 known_children: &[u64],
450 ) -> Option<(Vec<SubcomposeChild>, bool)> {
451 for &node_id in known_children {
452 NodeId::try_from(node_id).ok()?;
453 }
454
455 let virtual_node_ids = match self.activate_current_active_slot_roots(slot_id) {
456 Some(virtual_node_ids) => {
457 for virtual_node_id in &virtual_node_ids {
458 self.composer.record_subcompose_child(*virtual_node_id);
459 }
460 virtual_node_ids
461 }
462 None => self.activate_recycled_exact_retained_slot_roots(slot_id)?,
463 };
464
465 if !self.ensure_pending_commands_applied() {
466 return None;
467 }
468
469 let mut activated_children = Vec::with_capacity(known_children.len());
470 for virtual_node_id in virtual_node_ids {
471 activated_children.extend(
472 self.composer
473 .get_node_children(virtual_node_id)
474 .iter()
475 .copied()
476 .map(SubcomposeChild::new),
477 );
478 }
479 let children_match = activated_children
480 .iter()
481 .map(|child| child.node_id() as u64)
482 .eq(known_children.iter().copied());
483 Some((activated_children, children_match))
484 }
485
486 fn activate_current_active_slot_roots(&mut self, slot_id: SlotId) -> Option<Vec<NodeId>> {
487 self.state.activate_current_active_slot(slot_id)
488 }
489
490 fn activate_recycled_exact_retained_slot_roots(
491 &mut self,
492 slot_id: SlotId,
493 ) -> Option<Vec<NodeId>> {
494 let activation = self.state.take_exact_slot_activation(slot_id)?;
495 let virtual_node_ids = activation.nodes;
496 let scopes = activation.scopes;
497 let reactivate_scopes = activation.reactivate_scopes;
498
499 if reactivate_scopes {
500 let inner = self.parent_handle.inner.borrow();
501 for virtual_node_id in &virtual_node_ids {
502 self.composer.record_subcompose_child(*virtual_node_id);
503 if let Some(v_node) = inner.virtual_nodes.get(virtual_node_id) {
504 v_node.set_parent(self.root_id);
505 }
506 }
507 for virtual_node_id in &virtual_node_ids {
508 let _ = self
509 .composer
510 .with_node_mut::<LayoutNode, _>(*virtual_node_id, |node| {
511 node.set_parent(self.root_id);
512 });
513 }
514 } else {
515 for virtual_node_id in &virtual_node_ids {
516 self.composer.record_subcompose_child(*virtual_node_id);
517 }
518 }
519
520 self.state.register_active_with_scope_reactivation(
521 slot_id,
522 &virtual_node_ids,
523 &scopes,
524 reactivate_scopes,
525 );
526 Some(virtual_node_ids)
527 }
528}
529
530impl SubcomposeLayoutScope for SubcomposeMeasureScopeImpl<'_> {
531 fn constraints(&self) -> Constraints {
532 self.constraints
533 }
534
535 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
536 where
537 I: IntoIterator<Item = Placement>,
538 {
539 self.layout_with_placement_builder(width, height, |scratch| {
540 scratch.extend(placements);
541 })
542 }
543}
544
545impl cranpose_ui_layout::MeasureScope for SubcomposeMeasureScopeImpl<'_> {
546 fn density(&self) -> f32 {
547 self.density_scope.density()
548 }
549
550 fn font_scale(&self) -> f32 {
551 self.density_scope.font_scale()
552 }
553}
554
555impl SubcomposeMeasureScope for SubcomposeMeasureScopeImpl<'_> {
556 fn subcompose<K, Content>(
557 &mut self,
558 slot_id: SlotId,
559 key: K,
560 content: Content,
561 ) -> Vec<SubcomposeChild>
562 where
563 K: PartialEq + 'static,
564 Content: FnMut() + 'static,
565 {
566 if self.state.retained_capture_key_matches(slot_id, &key)
567 && let Some(children) = self.activate_clean_retained_slot(slot_id)
568 {
569 #[cfg(debug_assertions)]
570 self.shadow_verify_clean_slot(slot_id, content);
571 return children.into_iter().map(SubcomposeChild::new).collect();
572 }
573 self.state.store_retained_capture_key(slot_id, key);
574 let nodes = self.perform_subcompose(slot_id, content);
575 nodes.into_iter().map(SubcomposeChild::new).collect()
576 }
577
578 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable {
579 if self.error.borrow().is_some() {
580 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
581 }
582
583 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
584 if !self.ensure_pending_commands_applied() {
585 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
586 }
587
588 let placeable = (self.measurer)(child.node_id, constraints);
589 self.register_measurement_node_id(child.node_id);
590 if let Some(start) = telemetry_start {
591 log::warn!(
592 "[subcompose-telemetry] child={} measure_ms={:.2} size=({:.2},{:.2})",
593 child.node_id,
594 start.elapsed().as_secs_f64() * 1000.0,
595 placeable.width(),
596 placeable.height()
597 );
598 }
599 placeable
600 }
601
602 fn node_has_no_parent(&self, node_id: NodeId) -> bool {
603 self.composer.node_has_no_parent(node_id)
604 }
605}
606
607impl SubcomposeMeasureScopeImpl<'_> {
608 pub fn active_slots_count(&self) -> usize {
612 self.state.active_slots_count()
613 }
614
615 pub fn reusable_slots_count(&self) -> usize {
619 self.state.reusable_slots_count()
620 }
621
622 pub(crate) fn slot_is_retained(&self, slot_id: SlotId) -> bool {
624 self.state.slot_is_retained(slot_id)
625 }
626
627 pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
633 self.state.register_content_type(slot_id, content_type);
634 }
635
636 pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
642 self.state.update_content_type(slot_id, content_type);
643 }
644
645 pub(crate) fn set_reusable_pool_limits(&mut self, per_type: usize, untyped: usize) {
646 self.state.set_reusable_pool_limits(per_type, untyped);
647 }
648
649 pub(crate) fn focused_slot(&self) -> Option<SlotId> {
650 let mut node_id = crate::active_focus_target()?;
651 while node_id != self.root_id {
652 if let Some(slot) = self.state.active_slot_for_node(node_id) {
653 return Some(slot);
654 }
655 node_id = self.composer.node_parent(node_id).ok()??;
656 }
657 None
658 }
659
660 pub(crate) fn recycle_active_slots_where(&mut self, predicate: impl FnMut(SlotId) -> bool) {
661 let disposed = self.state.recycle_active_slots_where(predicate);
662 debug_assert!(
663 disposed.is_empty(),
664 "lazy subcompose reusable pool limits must retain recycled active slots"
665 );
666 }
667
668 pub fn was_last_slot_reused(&self) -> Option<bool> {
676 self.state.was_last_slot_reused()
677 }
678
679 pub(crate) fn measure_retained(
680 &mut self,
681 child: SubcomposeChild,
682 constraints: Constraints,
683 ) -> (SubcomposePlaceable, Option<Rc<MeasuredNode>>) {
684 let placeable = self.measure(child, constraints);
685 let retained = (self.retained_measure_lookup)(child.node_id);
686 (placeable, retained)
687 }
688
689 pub(crate) fn register_retained_measurements(&mut self, measurements: &[Rc<MeasuredNode>]) {
690 if measurements.is_empty() {
691 return;
692 }
693
694 for measured in measurements {
695 self.register_measurement_node_id(measured.node_id());
696 }
697 (self.retained_measure_registrar)(measurements);
698 }
699
700 pub(crate) fn children_need_relayout(&mut self, children: &[SubcomposeChild]) -> bool {
701 if !self.ensure_pending_commands_applied() {
702 return true;
703 }
704
705 let mut root_ids = smallvec::SmallVec::<[NodeId; 8]>::new();
706 root_ids.extend(children.iter().map(SubcomposeChild::node_id));
707 self.composer.nodes_need_measure(&root_ids) || self.composer.nodes_need_layout(&root_ids)
708 }
709
710 pub(crate) fn ensure_cached_measurement_node_ids<I>(
711 &mut self,
712 node_ids: I,
713 constraints: Constraints,
714 ) -> usize
715 where
716 I: IntoIterator<Item = NodeId>,
717 {
718 if self.error.borrow().is_some() || !self.ensure_pending_commands_applied() {
719 return 0;
720 }
721
722 self.cached_measure_node_scratch.clear();
723 self.cached_measure_node_scratch.extend(
724 node_ids
725 .into_iter()
726 .filter(|node_id| !self.registered_measurement_node_ids.contains(node_id)),
727 );
728 if self.cached_measure_node_scratch.is_empty() {
729 return 0;
730 }
731
732 self.cached_measure_size_scratch.clear();
733 (self.cached_measure_batch_registrar)(
734 &self.cached_measure_node_scratch,
735 constraints,
736 &mut self.cached_measure_size_scratch,
737 );
738 self.cached_measure_size_scratch
739 .resize(self.cached_measure_node_scratch.len(), None);
740
741 let mut cached_count = 0;
742 self.cached_measure_missing_scratch.clear();
743 for index in 0..self.cached_measure_node_scratch.len() {
744 let node_id = self.cached_measure_node_scratch[index];
745 if self.cached_measure_size_scratch[index].is_some() {
746 cached_count += 1;
747 self.register_measurement_node_id(node_id);
748 } else {
749 self.cached_measure_missing_scratch.push(node_id);
750 }
751 }
752
753 let mut missing = std::mem::take(&mut self.cached_measure_missing_scratch);
754 for node_id in missing.drain(..) {
755 let _ = self.measure(SubcomposeChild::new(node_id), constraints);
756 }
757 self.cached_measure_missing_scratch = missing;
758
759 cached_count
760 }
761}
762
763fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
764 cranpose_core::with_current_composer(|composer| {
765 let holder_for_recompose = holder.clone();
766 composer.set_recompose_callback(move |_composer| {
767 compose_subcompose_slot_content(holder_for_recompose.clone());
768 });
769 });
770
771 let invoke = holder.clone_rc();
772 invoke();
773}
774
775pub type MeasurePolicy =
776 dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
777
778pub struct SubcomposeLayoutNode {
780 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
781 parent: Cell<Option<NodeId>>,
782 id: Cell<Option<NodeId>>,
783 needs_measure: Cell<bool>,
784 needs_layout: Cell<bool>,
785 needs_semantics: Cell<bool>,
786 needs_redraw: Cell<bool>,
787 needs_pointer_pass: Cell<bool>,
788 needs_focus_sync: Cell<bool>,
789 virtual_children_count: Cell<usize>,
790 layout_state: RefCell<LayoutState>,
791 cache_handles: LayoutNodeCacheHandles,
792 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
793 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
797 modifier_slices_dirty: Cell<bool>,
798}
799
800impl SubcomposeLayoutNode {
801 pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
802 let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
803 let node = Self {
804 inner,
805 parent: Cell::new(None),
806 id: Cell::new(None),
807 needs_measure: Cell::new(true),
808 needs_layout: Cell::new(true),
809 needs_semantics: Cell::new(true),
810 needs_redraw: Cell::new(true),
811 needs_pointer_pass: Cell::new(false),
812 needs_focus_sync: Cell::new(false),
813 virtual_children_count: Cell::new(0),
814 layout_state: RefCell::new(LayoutState::default()),
815 cache_handles: LayoutNodeCacheHandles::default(),
816 modifier_slices_snapshot: RefCell::new(Rc::default()),
817 coordinator_geometry: Rc::default(),
818 modifier_slices_dirty: Cell::new(true),
819 };
820 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
821 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
822 node.note_host_to_the_composition_that_made_it();
823 node
824 }
825
826 fn note_host_to_the_composition_that_made_it(&self) {
827 let host = Rc::clone(&self.inner.borrow().slots);
828 cranpose_core::note_nested_slots_host(&host);
829 }
830
831 pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
837 let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
838 inner_data
839 .state
840 .set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
841 let inner = Rc::new(RefCell::new(inner_data));
842 let node = Self {
843 inner,
844 parent: Cell::new(None),
845 id: Cell::new(None),
846 needs_measure: Cell::new(true),
847 needs_layout: Cell::new(true),
848 needs_semantics: Cell::new(true),
849 needs_redraw: Cell::new(true),
850 needs_pointer_pass: Cell::new(false),
851 needs_focus_sync: Cell::new(false),
852 virtual_children_count: Cell::new(0),
853 layout_state: RefCell::new(LayoutState::default()),
854 cache_handles: LayoutNodeCacheHandles::default(),
855 modifier_slices_snapshot: RefCell::new(Rc::default()),
856 coordinator_geometry: Rc::default(),
857 modifier_slices_dirty: Cell::new(true),
858 };
859 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
860 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
861 node.note_host_to_the_composition_that_made_it();
862 node
863 }
864
865 pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
866 SubcomposeLayoutNodeHandle {
867 inner: Rc::clone(&self.inner),
868 }
869 }
870
871 #[doc(hidden)]
872 pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
873 self.inner.borrow().state.debug_scope_ids_by_slot()
874 }
875
876 #[doc(hidden)]
877 pub fn debug_slot_table_for_slot(
878 &self,
879 slot_id: cranpose_core::SlotId,
880 ) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
881 self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
882 }
883
884 #[doc(hidden)]
885 pub fn debug_slot_table_groups_for_slot(
886 &self,
887 slot_id: cranpose_core::SlotId,
888 ) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
889 self.inner
890 .borrow()
891 .state
892 .debug_slot_table_groups_for_slot(slot_id)
893 }
894
895 pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
896 let mut inner = self.inner.borrow_mut();
897 if Rc::ptr_eq(&inner.measure_policy, &policy) {
898 return;
899 }
900 inner.set_measure_policy(policy);
901 drop(inner);
902 self.invalidate_subcomposition();
903 }
904
905 pub fn set_captured_context(&mut self, context: cranpose_core::CapturedCompositionContext) {
907 self.inner.borrow_mut().captured_context = Some(context);
908 }
909
910 pub fn set_density(&mut self, density: crate::density::Density) {
916 let mut inner = self.inner.borrow_mut();
917 if inner.density != density {
918 inner.density = density;
919 drop(inner);
920 self.modifier_slices_dirty.set(true);
921 self.mark_needs_measure();
922 }
923 }
924
925 pub(crate) fn density(&self) -> crate::density::Density {
927 self.inner.borrow().density
928 }
929
930 pub fn set_modifier(&mut self, modifier: Modifier) {
931 let prev_caps = self.modifier_capabilities();
932 let (invalidations, modifier_changed) = {
933 let mut inner = self.inner.borrow_mut();
934 inner.set_modifier_collect(modifier)
935 };
936 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
937 self.modifier_slices_dirty.set(true);
938 crate::modal_nodes::reach_changed(self.id.get());
939 if modifier_changed {
940 self.request_semantics_update();
941 }
942 }
943
944 fn update_modifier_slices_cache(&self) {
945 let inner = self.inner.borrow();
946 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
947 crate::modifier::collect_modifier_slices_into_shared(
948 inner.modifier_chain.chain(),
949 &mut snapshot,
950 &self.coordinator_geometry,
951 inner.density.density(),
952 );
953 self.modifier_slices_dirty.set(false);
954 }
955
956 pub(crate) fn mark_modifier_slices_dirty(&self) {
957 self.modifier_slices_dirty.set(true);
958 }
959
960 pub fn set_debug_modifiers(&mut self, enabled: bool) {
961 self.inner.borrow_mut().set_debug_modifiers(enabled);
962 }
963
964 pub fn modifier(&self) -> Modifier {
965 self.handle().modifier()
966 }
967
968 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
969 self.inner.borrow().resolved_modifiers
970 }
971
972 pub fn layout_state(&self) -> LayoutState {
974 self.layout_state.borrow().clone()
975 }
976
977 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
978 &self.cache_handles
979 }
980
981 pub fn set_position(&self, position: Point) {
984 self.layout_state.borrow_mut().place(position);
985 }
986
987 pub fn set_measured_size(&self, size: Size) {
991 self.layout_state.borrow_mut().set_size(size);
992 }
993
994 pub fn clear_placed(&self) {
996 self.layout_state.borrow_mut().clear_placed();
997 }
998
999 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
1001 crate::modifier::collect_semantics_from_chain(self.inner.borrow().modifier_chain.chain())
1002 }
1003
1004 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1006 crate::modifier::semantics_reach_of_chain(self.inner.borrow().modifier_chain.chain())
1007 }
1008
1009 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1011 if self.modifier_slices_dirty.get() {
1012 self.update_modifier_slices_cache();
1013 }
1014 self.modifier_slices_snapshot.borrow().clone()
1015 }
1016
1017 pub fn state(&self) -> Ref<'_, SubcomposeState> {
1018 Ref::map(self.inner.borrow(), |inner| &inner.state)
1019 }
1020
1021 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
1022 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
1023 }
1024
1025 pub fn invalidate_subcomposition(&self) {
1026 self.inner.borrow().state.invalidate_scopes();
1027 self.mark_needs_measure();
1028 if let Some(id) = self.id.get() {
1029 cranpose_core::bubble_measure_dirty_in_composer(id);
1030 }
1031 }
1032
1033 pub fn request_measure_recompose(&self) {
1034 self.mark_needs_measure();
1035 if let Some(id) = self.id.get() {
1036 cranpose_core::bubble_measure_dirty_in_composer(id);
1037 }
1038 }
1039
1040 pub fn with_active_children<R>(&self, f: impl FnOnce(&[NodeId]) -> R) -> R {
1045 let children = Rc::clone(&self.inner.borrow().last_placements);
1046 f(&children)
1047 }
1048
1049 pub fn mark_needs_measure(&self) {
1051 self.needs_measure.set(true);
1052 self.needs_layout.set(true);
1053 }
1054
1055 pub fn mark_needs_layout_flag(&self) {
1057 self.needs_layout.set(true);
1058 }
1059
1060 pub fn mark_needs_redraw(&self) {
1062 self.needs_redraw.set(true);
1063 if let Some(id) = self.id.get() {
1064 crate::schedule_draw_repass(id);
1065 }
1066 crate::request_render_invalidation();
1067 }
1068
1069 pub fn needs_measure(&self) -> bool {
1071 self.needs_measure.get()
1072 }
1073
1074 pub(crate) fn clear_needs_measure(&self) {
1075 self.needs_measure.set(false);
1076 }
1077
1078 pub(crate) fn clear_needs_layout(&self) {
1079 self.needs_layout.set(false);
1080 }
1081
1082 pub fn mark_needs_semantics(&self) {
1084 self.needs_semantics.set(true);
1085 crate::modal_nodes::reach_changed(self.id.get());
1086 }
1087
1088 pub(crate) fn id(&self) -> Option<NodeId> {
1090 self.id.get()
1091 }
1092
1093 pub(crate) fn clear_needs_semantics(&self) {
1094 self.needs_semantics.set(false);
1095 }
1096
1097 #[cfg(test)]
1098 pub(crate) fn clear_needs_semantics_for_tests(&self) {
1099 self.clear_needs_semantics();
1100 }
1101
1102 pub fn needs_redraw(&self) -> bool {
1104 self.needs_redraw.get()
1105 }
1106
1107 pub fn clear_needs_redraw(&self) {
1108 self.needs_redraw.set(false);
1109 }
1110
1111 pub fn mark_needs_pointer_pass(&self) {
1113 self.needs_pointer_pass.set(true);
1114 }
1115
1116 pub fn needs_pointer_pass(&self) -> bool {
1118 self.needs_pointer_pass.get()
1119 }
1120
1121 pub fn clear_needs_pointer_pass(&self) {
1123 self.needs_pointer_pass.set(false);
1124 }
1125
1126 pub fn mark_needs_focus_sync(&self) {
1128 self.needs_focus_sync.set(true);
1129 }
1130
1131 pub fn needs_focus_sync(&self) -> bool {
1133 self.needs_focus_sync.get()
1134 }
1135
1136 pub fn clear_needs_focus_sync(&self) {
1138 self.needs_focus_sync.set(false);
1139 }
1140
1141 fn request_semantics_update(&self) {
1142 let already_dirty = self.needs_semantics.replace(true);
1143 if already_dirty {
1144 return;
1145 }
1146
1147 if let Some(id) = self.id.get() {
1148 cranpose_core::queue_semantics_invalidation(id);
1149 }
1150 }
1151
1152 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1154 self.inner.borrow().modifier_capabilities
1155 }
1156
1157 pub fn has_layout_modifier_nodes(&self) -> bool {
1158 self.modifier_capabilities()
1159 .contains(NodeCapabilities::LAYOUT)
1160 }
1161
1162 pub fn has_draw_modifier_nodes(&self) -> bool {
1163 self.modifier_capabilities()
1164 .contains(NodeCapabilities::DRAW)
1165 }
1166
1167 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1168 self.modifier_capabilities()
1169 .contains(NodeCapabilities::POINTER_INPUT)
1170 }
1171
1172 pub fn has_semantics_modifier_nodes(&self) -> bool {
1173 self.modifier_capabilities()
1174 .contains(NodeCapabilities::SEMANTICS)
1175 }
1176
1177 pub fn has_focus_modifier_nodes(&self) -> bool {
1178 self.modifier_capabilities()
1179 .contains(NodeCapabilities::FOCUS)
1180 }
1181
1182 fn dispatch_modifier_invalidations(
1183 &self,
1184 invalidations: &[ModifierInvalidation],
1185 prev_caps: NodeCapabilities,
1186 ) {
1187 let curr_caps = self.modifier_capabilities();
1188 for invalidation in invalidations {
1189 self.modifier_slices_dirty.set(true);
1190 let invalidation_caps = invalidation.capabilities();
1191 let has_capability = |capability| {
1192 curr_caps.contains(capability)
1193 || prev_caps.contains(capability)
1194 || invalidation_caps.contains(capability)
1195 };
1196 match invalidation.kind() {
1197 InvalidationKind::Layout => {
1198 if has_capability(NodeCapabilities::LAYOUT) {
1199 self.mark_needs_measure();
1200 }
1201 }
1202 InvalidationKind::Draw => {
1203 if has_capability(NodeCapabilities::DRAW) {
1204 self.mark_needs_redraw();
1205 }
1206 }
1207 InvalidationKind::PointerInput => {
1208 if has_capability(NodeCapabilities::POINTER_INPUT) {
1209 self.mark_needs_pointer_pass();
1210 crate::request_pointer_invalidation();
1211 if let Some(id) = self.id.get() {
1212 crate::schedule_pointer_repass(id);
1213 }
1214 }
1215 }
1216 InvalidationKind::Semantics => {
1217 self.request_semantics_update();
1218 }
1219 InvalidationKind::Focus => {
1220 if has_capability(NodeCapabilities::FOCUS) {
1221 self.mark_needs_focus_sync();
1222 crate::request_focus_invalidation();
1223 if let Some(id) = self.id.get() {
1224 crate::schedule_focus_invalidation(id);
1225 }
1226 }
1227 }
1228 }
1229 }
1230 }
1231}
1232
1233impl cranpose_core::Node for SubcomposeLayoutNode {
1234 fn mount(&mut self) {
1235 let mut inner = self.inner.borrow_mut();
1236 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1237 chain.repair_chain();
1238 chain.attach_nodes(&mut *context);
1239 crate::modal_nodes::reach_changed(self.id.get());
1240 }
1241
1242 fn unmount(&mut self) {
1243 self.inner
1244 .borrow_mut()
1245 .modifier_chain
1246 .chain_mut()
1247 .detach_nodes();
1248 }
1249
1250 fn insert_child(&mut self, child: NodeId) -> bool {
1251 let mut inner = self.inner.borrow_mut();
1252 if inner.children.contains(&child) {
1253 return false;
1254 }
1255 if is_virtual_node(child) {
1256 let count = self.virtual_children_count.get();
1257 self.virtual_children_count.set(count + 1);
1258 }
1259 inner.children.push(child);
1260 true
1261 }
1262
1263 fn remove_child(&mut self, child: NodeId) -> bool {
1264 let mut inner = self.inner.borrow_mut();
1265 let before = inner.children.len();
1266 inner.children.retain(|&id| id != child);
1267 let removed = inner.children.len() < before;
1268 if removed && is_virtual_node(child) {
1269 let count = self.virtual_children_count.get();
1270 if count > 0 {
1271 self.virtual_children_count.set(count - 1);
1272 }
1273 }
1274 removed
1275 }
1276
1277 fn move_child(&mut self, from: usize, to: usize) {
1278 let mut inner = self.inner.borrow_mut();
1279 if from == to || from >= inner.children.len() {
1280 return;
1281 }
1282 let child = inner.children.remove(from);
1283 let target = to.min(inner.children.len());
1284 inner.children.insert(target, child);
1285 }
1286
1287 fn update_children(&mut self, children: &[NodeId]) {
1288 let mut inner = self.inner.borrow_mut();
1289 inner.children.clear();
1290 inner.children.extend_from_slice(children);
1291 }
1292
1293 fn collect_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1294 out.clear();
1295 self.with_active_children(|children| out.extend_from_slice(children));
1296 }
1297
1298 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1299 out.clear();
1300 out.extend_from_slice(&self.inner.borrow().children);
1301 }
1302
1303 fn set_node_id(&mut self, id: NodeId) {
1304 self.id.set(Some(id));
1305 self.layout_state.borrow_mut().set_node_id(id);
1306 {
1307 let mut inner = self.inner.borrow_mut();
1308 inner.node_id = Some(id);
1309 inner.modifier_chain.set_node_id(Some(id));
1310 }
1311 self.modifier_slices_dirty.set(true);
1313 }
1314
1315 fn on_attached_to_parent(&mut self, parent: NodeId) {
1316 self.parent.set(Some(parent));
1317 }
1318
1319 fn on_removed_from_parent(&mut self) {
1320 self.parent.set(None);
1321 self.inner.borrow().state.bump_content_generation();
1322 }
1323
1324 fn parent(&self) -> Option<NodeId> {
1325 self.parent.get()
1326 }
1327
1328 fn mark_needs_layout(&self) {
1329 self.needs_layout.set(true);
1330 }
1331
1332 fn needs_layout(&self) -> bool {
1333 self.needs_layout.get()
1334 }
1335
1336 fn mark_needs_measure(&self) {
1337 self.needs_measure.set(true);
1338 self.needs_layout.set(true);
1339 }
1340
1341 fn needs_measure(&self) -> bool {
1342 self.needs_measure.get()
1343 }
1344
1345 fn mark_needs_semantics(&self) {
1346 self.needs_semantics.set(true);
1347 crate::modal_nodes::reach_changed(self.id.get());
1348 }
1349
1350 fn mark_descendant_needs_semantics(&self) {
1353 self.needs_semantics.set(true);
1354 }
1355
1356 fn needs_semantics(&self) -> bool {
1357 self.needs_semantics.get()
1358 }
1359
1360 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1361 if self.parent.get().is_none() {
1362 self.parent.set(Some(parent));
1363 }
1364 }
1365}
1366
1367#[derive(Clone)]
1368pub struct SubcomposeLayoutNodeHandle {
1369 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1370}
1371
1372impl SubcomposeLayoutNodeHandle {
1373 pub(crate) fn observer_for_measure(&self) -> SnapshotStateObserver {
1374 let observer = self
1375 .inner
1376 .borrow_mut()
1377 .measure_observer
1378 .get_or_insert_with(|| SnapshotStateObserver::new(|callback| callback()))
1379 .clone();
1380 observer.prune_dead_scopes();
1381 observer
1382 }
1383
1384 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1385 let Ok(inner) = self.inner.try_borrow() else {
1386 return;
1387 };
1388 if Rc::ptr_eq(&inner.slots, slot_host) {
1389 return;
1390 }
1391 inner.slots.note_nested_host(slot_host);
1392 }
1393
1394 pub(crate) fn measured_children_scratch(
1395 &self,
1396 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1397 let scratch = {
1398 let inner = self.inner.borrow();
1399 Rc::clone(&inner.measured_children_scratch)
1400 };
1401 scratch.borrow_mut().clear();
1402 scratch
1403 }
1404
1405 pub fn modifier(&self) -> Modifier {
1406 self.inner.borrow().modifier.clone()
1407 }
1408
1409 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1410 self.resolved_modifiers().layout_properties()
1411 }
1412
1413 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1414 self.inner.borrow().resolved_modifiers
1415 }
1416
1417 pub fn total_offset(&self) -> Point {
1418 self.resolved_modifiers().offset()
1419 }
1420
1421 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1422 self.inner.borrow().modifier_capabilities
1423 }
1424
1425 pub fn has_layout_modifier_nodes(&self) -> bool {
1426 self.modifier_capabilities()
1427 .contains(NodeCapabilities::LAYOUT)
1428 }
1429
1430 pub fn has_draw_modifier_nodes(&self) -> bool {
1431 self.modifier_capabilities()
1432 .contains(NodeCapabilities::DRAW)
1433 }
1434
1435 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1436 self.modifier_capabilities()
1437 .contains(NodeCapabilities::POINTER_INPUT)
1438 }
1439
1440 pub fn has_semantics_modifier_nodes(&self) -> bool {
1441 self.modifier_capabilities()
1442 .contains(NodeCapabilities::SEMANTICS)
1443 }
1444
1445 pub fn has_focus_modifier_nodes(&self) -> bool {
1446 self.modifier_capabilities()
1447 .contains(NodeCapabilities::FOCUS)
1448 }
1449
1450 pub fn set_debug_modifiers(&self, enabled: bool) {
1451 self.inner.borrow_mut().set_debug_modifiers(enabled);
1452 }
1453
1454 pub fn measure<'a>(
1455 &self,
1456 composer: &Composer,
1457 node_id: NodeId,
1458 constraints: Constraints,
1459 measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
1460 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1461 error: &'a RefCell<Option<NodeError>>,
1462 ) -> Result<MeasureResult, NodeError> {
1463 self.measure_with_cached_batch(
1464 composer,
1465 node_id,
1466 constraints,
1467 CachedBatchMeasureInputs {
1468 measurer,
1469 cached_measure_batch_registrar: Box::new(
1470 move |node_ids, child_constraints, out| {
1471 out.clear();
1472 out.reserve(node_ids.len());
1473 for &child_id in node_ids {
1474 out.push(cached_measure_registrar(child_id, child_constraints));
1475 }
1476 },
1477 ),
1478 retained_measure_lookup: Box::new(|_| None),
1479 retained_measure_registrar: Box::new(|_| {}),
1480 error,
1481 },
1482 )
1483 }
1484
1485 pub(crate) fn measure_with_cached_batch(
1486 &self,
1487 composer: &Composer,
1488 node_id: NodeId,
1489 constraints: Constraints,
1490 callbacks: CachedBatchMeasureInputs<'_>,
1491 ) -> Result<MeasureResult, NodeError> {
1492 let CachedBatchMeasureInputs {
1493 measurer,
1494 cached_measure_batch_registrar,
1495 retained_measure_lookup,
1496 retained_measure_registrar,
1497 error,
1498 } = callbacks;
1499 let (policy, mut state, slots_host, placement_scratch, captured_context, density) = {
1500 let mut inner = self.inner.borrow_mut();
1501 let policy = Rc::clone(&inner.measure_policy);
1502 let state = std::mem::take(&mut inner.state);
1503 let slots_host = Rc::clone(&inner.slots);
1504 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1505 let captured_context = inner.captured_context.clone();
1506 let density = inner.density;
1507 (
1508 policy,
1509 state,
1510 slots_host,
1511 placement_scratch,
1512 captured_context,
1513 density,
1514 )
1515 };
1516 state.begin_pass();
1517
1518 let previous = composer.phase();
1519 if !matches!(previous, Phase::Measure | Phase::Layout) {
1520 composer.enter_phase(Phase::Measure);
1521 }
1522
1523 let constraints_copy = constraints;
1524 let fallback_context;
1525 let context = if let Some(context) = captured_context.as_ref() {
1526 context
1527 } else {
1528 fallback_context = composer.capture_composition_context();
1529 &fallback_context
1530 };
1531 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1532 &slots_host,
1533 Some(node_id),
1534 context,
1535 |inner_composer| {
1536 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1537 composer: inner_composer.clone(),
1538 density,
1539 state: &mut state,
1540 constraints: constraints_copy,
1541 measurer,
1542 cached_measure_batch_registrar,
1543 retained_measure_lookup,
1544 retained_measure_registrar,
1545 error,
1546 parent_handle: self.clone(),
1547 root_id: node_id,
1548 placement_scratch,
1549 });
1550 let result = (policy)(&mut scope, constraints_copy);
1551 (result, scope.into_placement_scratch())
1552 },
1553 )?;
1554
1555 state.finish_pass();
1556
1557 if previous != composer.phase() {
1558 composer.enter_phase(previous);
1559 }
1560
1561 {
1562 let mut inner = self.inner.borrow_mut();
1563 inner.state = state;
1564 inner.placement_scratch = placement_scratch;
1565
1566 inner.replace_placed_children(
1567 result.placements.iter().map(|placement| placement.node_id),
1568 );
1569 }
1570
1571 Ok(result)
1572 }
1573
1574 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1575 placements.clear();
1576 let mut inner = self.inner.borrow_mut();
1577 if placements.capacity() > inner.placement_scratch.capacity() {
1578 inner.placement_scratch = placements;
1579 }
1580 }
1581
1582 pub fn set_active_children<I>(&self, children: I)
1583 where
1584 I: IntoIterator<Item = NodeId>,
1585 {
1586 self.inner.borrow_mut().replace_placed_children(children);
1587 }
1588}
1589
1590struct SubcomposeLayoutNodeInner {
1591 modifier: Modifier,
1592 modifier_chain: ModifierChainHandle,
1593 resolved_modifiers: ResolvedModifiers,
1594 modifier_capabilities: NodeCapabilities,
1595 state: SubcomposeState,
1596 measure_policy: Rc<MeasurePolicy>,
1597 children: Vec<NodeId>,
1598 slots: Rc<SlotsHost>,
1599 debug_modifiers: bool,
1600 virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
1601 node_id: Option<NodeId>,
1602 last_placements: Rc<Vec<NodeId>>,
1603 placement_scratch: Vec<Placement>,
1604 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1605 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1606 measure_observer: Option<SnapshotStateObserver>,
1607 density: crate::density::Density,
1608}
1609
1610impl SubcomposeLayoutNodeInner {
1611 fn replace_placed_children<I>(&mut self, children: I)
1622 where
1623 I: IntoIterator<Item = NodeId>,
1624 {
1625 let mut changed = false;
1626 let mut count = 0usize;
1627 for child in children {
1628 match self.last_placements.get(count) {
1629 Some(&placed) if placed == child => {}
1630 Some(_) => {
1631 Rc::make_mut(&mut self.last_placements)[count] = child;
1632 changed = true;
1633 }
1634 None => {
1635 Rc::make_mut(&mut self.last_placements).push(child);
1636 changed = true;
1637 }
1638 }
1639 count += 1;
1640 }
1641 if self.last_placements.len() > count {
1642 Rc::make_mut(&mut self.last_placements).truncate(count);
1643 changed = true;
1644 }
1645 if changed && let Some(id) = self.node_id {
1646 crate::render_state::record_geometry_scene_node(id);
1647 }
1648 }
1649
1650 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1651 Self {
1652 modifier: Modifier::empty(),
1653 modifier_chain: ModifierChainHandle::new(),
1654 resolved_modifiers: ResolvedModifiers::default(),
1655 modifier_capabilities: NodeCapabilities::default(),
1656 state: SubcomposeState::default(),
1657 measure_policy,
1658 children: Vec::new(),
1659 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1660 debug_modifiers: false,
1661 virtual_nodes: HashMap::new(),
1662 node_id: None,
1663 last_placements: Rc::new(Vec::new()),
1664 placement_scratch: Vec::new(),
1665 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1666 captured_context: None,
1667 measure_observer: None,
1668 density: crate::density::Density::default(),
1669 }
1670 }
1671
1672 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1673 self.measure_policy = policy;
1674 if let Err(err) = self.slots.reset() {
1675 log::error!(
1676 "failed to reset root measurement slots after measure policy update: {err}"
1677 );
1678 }
1679 }
1680
1681 fn set_modifier_collect(&mut self, modifier: Modifier) -> (ModifierInvalidations, bool) {
1682 let modifier_changed = !self.modifier.structural_eq(&modifier);
1683 self.modifier = modifier;
1684 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1685 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1686 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1687 self.modifier_capabilities = self.modifier_chain.capabilities();
1688
1689 let mut invalidations = self.modifier_chain.take_invalidations();
1690 invalidations.extend(modifier_local_invalidations);
1691
1692 (invalidations, modifier_changed)
1693 }
1694
1695 fn set_debug_modifiers(&mut self, enabled: bool) {
1696 self.debug_modifiers = enabled;
1697 self.modifier_chain.set_debug_logging(enabled);
1698 }
1699}
1700
1701#[cfg(test)]
1702#[path = "tests/subcompose_layout_tests.rs"]
1703mod tests;