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