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