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 if modifier_changed {
938 self.request_semantics_update();
939 }
940 }
941
942 fn update_modifier_slices_cache(&self) {
943 let inner = self.inner.borrow();
944 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
945 crate::modifier::collect_modifier_slices_into_shared(
946 inner.modifier_chain.chain(),
947 &mut snapshot,
948 &self.coordinator_geometry,
949 inner.density.density(),
950 );
951 self.modifier_slices_dirty.set(false);
952 }
953
954 pub(crate) fn mark_modifier_slices_dirty(&self) {
955 self.modifier_slices_dirty.set(true);
956 }
957
958 pub fn set_debug_modifiers(&mut self, enabled: bool) {
959 self.inner.borrow_mut().set_debug_modifiers(enabled);
960 }
961
962 pub fn modifier(&self) -> Modifier {
963 self.handle().modifier()
964 }
965
966 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
967 self.inner.borrow().resolved_modifiers
968 }
969
970 pub fn layout_state(&self) -> LayoutState {
972 self.layout_state.borrow().clone()
973 }
974
975 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
976 self.cache_handles.clone()
977 }
978
979 pub fn set_position(&self, position: Point) {
982 self.layout_state.borrow_mut().place(position);
983 }
984
985 pub fn set_measured_size(&self, size: Size) {
989 self.layout_state.borrow_mut().set_size(size);
990 }
991
992 pub fn clear_placed(&self) {
994 self.layout_state.borrow_mut().clear_placed();
995 }
996
997 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
999 crate::modifier::collect_semantics_from_chain(self.inner.borrow().modifier_chain.chain())
1000 }
1001
1002 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1004 crate::modifier::semantics_reach_of_chain(self.inner.borrow().modifier_chain.chain())
1005 }
1006
1007 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1009 if self.modifier_slices_dirty.get() {
1010 self.update_modifier_slices_cache();
1011 }
1012 self.modifier_slices_snapshot.borrow().clone()
1013 }
1014
1015 pub fn state(&self) -> Ref<'_, SubcomposeState> {
1016 Ref::map(self.inner.borrow(), |inner| &inner.state)
1017 }
1018
1019 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
1020 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
1021 }
1022
1023 pub fn invalidate_subcomposition(&self) {
1024 self.inner.borrow().state.invalidate_scopes();
1025 self.mark_needs_measure();
1026 if let Some(id) = self.id.get() {
1027 cranpose_core::bubble_measure_dirty_in_composer(id);
1028 }
1029 }
1030
1031 pub fn request_measure_recompose(&self) {
1032 self.mark_needs_measure();
1033 if let Some(id) = self.id.get() {
1034 cranpose_core::bubble_measure_dirty_in_composer(id);
1035 }
1036 }
1037
1038 pub fn with_active_children<R>(&self, f: impl FnOnce(&[NodeId]) -> R) -> R {
1041 f(&self.inner.borrow().last_placements)
1042 }
1043
1044 pub fn mark_needs_measure(&self) {
1046 self.needs_measure.set(true);
1047 self.needs_layout.set(true);
1048 }
1049
1050 pub fn mark_needs_layout_flag(&self) {
1052 self.needs_layout.set(true);
1053 }
1054
1055 pub fn mark_needs_redraw(&self) {
1057 self.needs_redraw.set(true);
1058 if let Some(id) = self.id.get() {
1059 crate::schedule_draw_repass(id);
1060 }
1061 crate::request_render_invalidation();
1062 }
1063
1064 pub fn needs_measure(&self) -> bool {
1066 self.needs_measure.get()
1067 }
1068
1069 pub(crate) fn clear_needs_measure(&self) {
1070 self.needs_measure.set(false);
1071 }
1072
1073 pub(crate) fn clear_needs_layout(&self) {
1074 self.needs_layout.set(false);
1075 }
1076
1077 pub fn mark_needs_semantics(&self) {
1079 self.needs_semantics.set(true);
1080 }
1081
1082 pub(crate) fn clear_needs_semantics(&self) {
1083 self.needs_semantics.set(false);
1084 }
1085
1086 #[cfg(test)]
1087 pub(crate) fn clear_needs_semantics_for_tests(&self) {
1088 self.clear_needs_semantics();
1089 }
1090
1091 pub fn needs_redraw(&self) -> bool {
1093 self.needs_redraw.get()
1094 }
1095
1096 pub fn clear_needs_redraw(&self) {
1097 self.needs_redraw.set(false);
1098 }
1099
1100 pub fn mark_needs_pointer_pass(&self) {
1102 self.needs_pointer_pass.set(true);
1103 }
1104
1105 pub fn needs_pointer_pass(&self) -> bool {
1107 self.needs_pointer_pass.get()
1108 }
1109
1110 pub fn clear_needs_pointer_pass(&self) {
1112 self.needs_pointer_pass.set(false);
1113 }
1114
1115 pub fn mark_needs_focus_sync(&self) {
1117 self.needs_focus_sync.set(true);
1118 }
1119
1120 pub fn needs_focus_sync(&self) -> bool {
1122 self.needs_focus_sync.get()
1123 }
1124
1125 pub fn clear_needs_focus_sync(&self) {
1127 self.needs_focus_sync.set(false);
1128 }
1129
1130 fn request_semantics_update(&self) {
1131 let already_dirty = self.needs_semantics.replace(true);
1132 if already_dirty {
1133 return;
1134 }
1135
1136 if let Some(id) = self.id.get() {
1137 cranpose_core::queue_semantics_invalidation(id);
1138 }
1139 }
1140
1141 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1143 self.inner.borrow().modifier_capabilities
1144 }
1145
1146 pub fn has_layout_modifier_nodes(&self) -> bool {
1147 self.modifier_capabilities()
1148 .contains(NodeCapabilities::LAYOUT)
1149 }
1150
1151 pub fn has_draw_modifier_nodes(&self) -> bool {
1152 self.modifier_capabilities()
1153 .contains(NodeCapabilities::DRAW)
1154 }
1155
1156 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1157 self.modifier_capabilities()
1158 .contains(NodeCapabilities::POINTER_INPUT)
1159 }
1160
1161 pub fn has_semantics_modifier_nodes(&self) -> bool {
1162 self.modifier_capabilities()
1163 .contains(NodeCapabilities::SEMANTICS)
1164 }
1165
1166 pub fn has_focus_modifier_nodes(&self) -> bool {
1167 self.modifier_capabilities()
1168 .contains(NodeCapabilities::FOCUS)
1169 }
1170
1171 fn dispatch_modifier_invalidations(
1172 &self,
1173 invalidations: &[ModifierInvalidation],
1174 prev_caps: NodeCapabilities,
1175 ) {
1176 let curr_caps = self.modifier_capabilities();
1177 for invalidation in invalidations {
1178 self.modifier_slices_dirty.set(true);
1179 let invalidation_caps = invalidation.capabilities();
1180 let has_capability = |capability| {
1181 curr_caps.contains(capability)
1182 || prev_caps.contains(capability)
1183 || invalidation_caps.contains(capability)
1184 };
1185 match invalidation.kind() {
1186 InvalidationKind::Layout => {
1187 if has_capability(NodeCapabilities::LAYOUT) {
1188 self.mark_needs_measure();
1189 }
1190 }
1191 InvalidationKind::Draw => {
1192 if has_capability(NodeCapabilities::DRAW) {
1193 self.mark_needs_redraw();
1194 }
1195 }
1196 InvalidationKind::PointerInput => {
1197 if has_capability(NodeCapabilities::POINTER_INPUT) {
1198 self.mark_needs_pointer_pass();
1199 crate::request_pointer_invalidation();
1200 if let Some(id) = self.id.get() {
1201 crate::schedule_pointer_repass(id);
1202 }
1203 }
1204 }
1205 InvalidationKind::Semantics => {
1206 self.request_semantics_update();
1207 }
1208 InvalidationKind::Focus => {
1209 if has_capability(NodeCapabilities::FOCUS) {
1210 self.mark_needs_focus_sync();
1211 crate::request_focus_invalidation();
1212 if let Some(id) = self.id.get() {
1213 crate::schedule_focus_invalidation(id);
1214 }
1215 }
1216 }
1217 }
1218 }
1219 }
1220}
1221
1222impl cranpose_core::Node for SubcomposeLayoutNode {
1223 fn mount(&mut self) {
1224 let mut inner = self.inner.borrow_mut();
1225 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1226 chain.repair_chain();
1227 chain.attach_nodes(&mut *context);
1228 }
1229
1230 fn unmount(&mut self) {
1231 self.inner
1232 .borrow_mut()
1233 .modifier_chain
1234 .chain_mut()
1235 .detach_nodes();
1236 }
1237
1238 fn insert_child(&mut self, child: NodeId) -> bool {
1239 let mut inner = self.inner.borrow_mut();
1240 if inner.children.contains(&child) {
1241 return false;
1242 }
1243 if is_virtual_node(child) {
1244 let count = self.virtual_children_count.get();
1245 self.virtual_children_count.set(count + 1);
1246 }
1247 inner.children.push(child);
1248 true
1249 }
1250
1251 fn remove_child(&mut self, child: NodeId) -> bool {
1252 let mut inner = self.inner.borrow_mut();
1253 let before = inner.children.len();
1254 inner.children.retain(|&id| id != child);
1255 let removed = inner.children.len() < before;
1256 if removed && is_virtual_node(child) {
1257 let count = self.virtual_children_count.get();
1258 if count > 0 {
1259 self.virtual_children_count.set(count - 1);
1260 }
1261 }
1262 removed
1263 }
1264
1265 fn move_child(&mut self, from: usize, to: usize) {
1266 let mut inner = self.inner.borrow_mut();
1267 if from == to || from >= inner.children.len() {
1268 return;
1269 }
1270 let child = inner.children.remove(from);
1271 let target = to.min(inner.children.len());
1272 inner.children.insert(target, child);
1273 }
1274
1275 fn update_children(&mut self, children: &[NodeId]) {
1276 let mut inner = self.inner.borrow_mut();
1277 inner.children.clear();
1278 inner.children.extend_from_slice(children);
1279 }
1280
1281 fn collect_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1282 out.clear();
1283 self.with_active_children(|children| out.extend_from_slice(children));
1284 }
1285
1286 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1287 out.clear();
1288 out.extend_from_slice(&self.inner.borrow().children);
1289 }
1290
1291 fn set_node_id(&mut self, id: NodeId) {
1292 self.id.set(Some(id));
1293 self.layout_state.borrow_mut().set_node_id(id);
1294 {
1295 let mut inner = self.inner.borrow_mut();
1296 inner.node_id = Some(id);
1297 inner.modifier_chain.set_node_id(Some(id));
1298 }
1299 self.modifier_slices_dirty.set(true);
1301 }
1302
1303 fn on_attached_to_parent(&mut self, parent: NodeId) {
1304 self.parent.set(Some(parent));
1305 }
1306
1307 fn on_removed_from_parent(&mut self) {
1308 self.parent.set(None);
1309 self.inner.borrow().state.bump_content_generation();
1310 }
1311
1312 fn parent(&self) -> Option<NodeId> {
1313 self.parent.get()
1314 }
1315
1316 fn mark_needs_layout(&self) {
1317 self.needs_layout.set(true);
1318 }
1319
1320 fn needs_layout(&self) -> bool {
1321 self.needs_layout.get()
1322 }
1323
1324 fn mark_needs_measure(&self) {
1325 self.needs_measure.set(true);
1326 self.needs_layout.set(true);
1327 }
1328
1329 fn needs_measure(&self) -> bool {
1330 self.needs_measure.get()
1331 }
1332
1333 fn mark_needs_semantics(&self) {
1334 self.needs_semantics.set(true);
1335 }
1336
1337 fn needs_semantics(&self) -> bool {
1338 self.needs_semantics.get()
1339 }
1340
1341 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1342 if self.parent.get().is_none() {
1343 self.parent.set(Some(parent));
1344 }
1345 }
1346}
1347
1348#[derive(Clone)]
1349pub struct SubcomposeLayoutNodeHandle {
1350 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1351}
1352
1353impl SubcomposeLayoutNodeHandle {
1354 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1355 let Ok(inner) = self.inner.try_borrow() else {
1356 return;
1357 };
1358 if Rc::ptr_eq(&inner.slots, slot_host) {
1359 return;
1360 }
1361 inner.slots.note_nested_host(slot_host);
1362 }
1363
1364 pub(crate) fn measured_children_scratch(
1365 &self,
1366 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1367 let scratch = {
1368 let inner = self.inner.borrow();
1369 Rc::clone(&inner.measured_children_scratch)
1370 };
1371 scratch.borrow_mut().clear();
1372 scratch
1373 }
1374
1375 pub fn modifier(&self) -> Modifier {
1376 self.inner.borrow().modifier.clone()
1377 }
1378
1379 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1380 self.resolved_modifiers().layout_properties()
1381 }
1382
1383 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1384 self.inner.borrow().resolved_modifiers
1385 }
1386
1387 pub fn total_offset(&self) -> Point {
1388 self.resolved_modifiers().offset()
1389 }
1390
1391 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1392 self.inner.borrow().modifier_capabilities
1393 }
1394
1395 pub fn has_layout_modifier_nodes(&self) -> bool {
1396 self.modifier_capabilities()
1397 .contains(NodeCapabilities::LAYOUT)
1398 }
1399
1400 pub fn has_draw_modifier_nodes(&self) -> bool {
1401 self.modifier_capabilities()
1402 .contains(NodeCapabilities::DRAW)
1403 }
1404
1405 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1406 self.modifier_capabilities()
1407 .contains(NodeCapabilities::POINTER_INPUT)
1408 }
1409
1410 pub fn has_semantics_modifier_nodes(&self) -> bool {
1411 self.modifier_capabilities()
1412 .contains(NodeCapabilities::SEMANTICS)
1413 }
1414
1415 pub fn has_focus_modifier_nodes(&self) -> bool {
1416 self.modifier_capabilities()
1417 .contains(NodeCapabilities::FOCUS)
1418 }
1419
1420 pub fn set_debug_modifiers(&self, enabled: bool) {
1421 self.inner.borrow_mut().set_debug_modifiers(enabled);
1422 }
1423
1424 pub fn measure<'a>(
1425 &self,
1426 composer: &Composer,
1427 node_id: NodeId,
1428 constraints: Constraints,
1429 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
1430 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1431 error: &'a RefCell<Option<NodeError>>,
1432 ) -> Result<MeasureResult, NodeError> {
1433 self.measure_with_cached_batch(
1434 composer,
1435 node_id,
1436 constraints,
1437 CachedBatchMeasureInputs {
1438 measurer,
1439 cached_measure_batch_registrar: Box::new(
1440 move |node_ids, child_constraints, out| {
1441 out.clear();
1442 out.reserve(node_ids.len());
1443 for &child_id in node_ids {
1444 out.push(cached_measure_registrar(child_id, child_constraints));
1445 }
1446 },
1447 ),
1448 retained_measure_lookup: Box::new(|_| None),
1449 retained_measure_registrar: Box::new(|_| {}),
1450 error,
1451 },
1452 )
1453 }
1454
1455 pub(crate) fn measure_with_cached_batch(
1456 &self,
1457 composer: &Composer,
1458 node_id: NodeId,
1459 constraints: Constraints,
1460 callbacks: CachedBatchMeasureInputs<'_>,
1461 ) -> Result<MeasureResult, NodeError> {
1462 let CachedBatchMeasureInputs {
1463 measurer,
1464 cached_measure_batch_registrar,
1465 retained_measure_lookup,
1466 retained_measure_registrar,
1467 error,
1468 } = callbacks;
1469 let (policy, mut state, slots_host, placement_scratch, captured_context, density) = {
1470 let mut inner = self.inner.borrow_mut();
1471 let policy = Rc::clone(&inner.measure_policy);
1472 let state = std::mem::take(&mut inner.state);
1473 let slots_host = Rc::clone(&inner.slots);
1474 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1475 let captured_context = inner.captured_context.clone();
1476 let density = inner.density;
1477 (
1478 policy,
1479 state,
1480 slots_host,
1481 placement_scratch,
1482 captured_context,
1483 density,
1484 )
1485 };
1486 state.begin_pass();
1487
1488 let previous = composer.phase();
1489 if !matches!(previous, Phase::Measure | Phase::Layout) {
1490 composer.enter_phase(Phase::Measure);
1491 }
1492
1493 let constraints_copy = constraints;
1494 let fallback_context;
1495 let context = if let Some(context) = captured_context.as_ref() {
1496 context
1497 } else {
1498 fallback_context = composer.capture_composition_context();
1499 &fallback_context
1500 };
1501 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1502 &slots_host,
1503 Some(node_id),
1504 context,
1505 |inner_composer| {
1506 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1507 composer: inner_composer.clone(),
1508 density,
1509 state: &mut state,
1510 constraints: constraints_copy,
1511 measurer,
1512 cached_measure_batch_registrar,
1513 retained_measure_lookup,
1514 retained_measure_registrar,
1515 error,
1516 parent_handle: self.clone(),
1517 root_id: node_id,
1518 placement_scratch,
1519 });
1520 let result = (policy)(&mut scope, constraints_copy);
1521 (result, scope.into_placement_scratch())
1522 },
1523 )?;
1524
1525 state.finish_pass();
1526
1527 if previous != composer.phase() {
1528 composer.enter_phase(previous);
1529 }
1530
1531 {
1532 let mut inner = self.inner.borrow_mut();
1533 inner.state = state;
1534 inner.placement_scratch = placement_scratch;
1535
1536 inner.replace_placed_children(
1537 result.placements.iter().map(|placement| placement.node_id),
1538 );
1539 }
1540
1541 Ok(result)
1542 }
1543
1544 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1545 placements.clear();
1546 let mut inner = self.inner.borrow_mut();
1547 if placements.capacity() > inner.placement_scratch.capacity() {
1548 inner.placement_scratch = placements;
1549 }
1550 }
1551
1552 pub fn set_active_children<I>(&self, children: I)
1553 where
1554 I: IntoIterator<Item = NodeId>,
1555 {
1556 self.inner.borrow_mut().replace_placed_children(children);
1557 }
1558}
1559
1560struct SubcomposeLayoutNodeInner {
1561 modifier: Modifier,
1562 modifier_chain: ModifierChainHandle,
1563 resolved_modifiers: ResolvedModifiers,
1564 modifier_capabilities: NodeCapabilities,
1565 state: SubcomposeState,
1566 measure_policy: Rc<MeasurePolicy>,
1567 children: Vec<NodeId>,
1568 slots: Rc<SlotsHost>,
1569 debug_modifiers: bool,
1570 virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
1571 node_id: Option<NodeId>,
1572 last_placements: Vec<NodeId>,
1573 placement_scratch: Vec<Placement>,
1574 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1575 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1576 density: crate::density::Density,
1577}
1578
1579impl SubcomposeLayoutNodeInner {
1580 fn replace_placed_children<I>(&mut self, children: I)
1591 where
1592 I: IntoIterator<Item = NodeId>,
1593 {
1594 let mut changed = false;
1595 let mut count = 0usize;
1596 for child in children {
1597 match self.last_placements.get(count) {
1598 Some(&placed) if placed == child => {}
1599 Some(_) => {
1600 self.last_placements[count] = child;
1601 changed = true;
1602 }
1603 None => {
1604 self.last_placements.push(child);
1605 changed = true;
1606 }
1607 }
1608 count += 1;
1609 }
1610 if self.last_placements.len() > count {
1611 self.last_placements.truncate(count);
1612 changed = true;
1613 }
1614 if changed && let Some(id) = self.node_id {
1615 crate::render_state::record_geometry_scene_node(id);
1616 }
1617 }
1618
1619 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1620 Self {
1621 modifier: Modifier::empty(),
1622 modifier_chain: ModifierChainHandle::new(),
1623 resolved_modifiers: ResolvedModifiers::default(),
1624 modifier_capabilities: NodeCapabilities::default(),
1625 state: SubcomposeState::default(),
1626 measure_policy,
1627 children: Vec::new(),
1628 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1629 debug_modifiers: false,
1630 virtual_nodes: HashMap::new(),
1631 node_id: None,
1632 last_placements: Vec::new(),
1633 placement_scratch: Vec::new(),
1634 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1635 captured_context: None,
1636 density: crate::density::Density::default(),
1637 }
1638 }
1639
1640 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1641 self.measure_policy = policy;
1642 if let Err(err) = self.slots.reset() {
1643 log::error!(
1644 "failed to reset root measurement slots after measure policy update: {err}"
1645 );
1646 }
1647 }
1648
1649 fn set_modifier_collect(&mut self, modifier: Modifier) -> (ModifierInvalidations, bool) {
1650 let modifier_changed = !self.modifier.structural_eq(&modifier);
1651 self.modifier = modifier;
1652 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1653 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1654 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1655 self.modifier_capabilities = self.modifier_chain.capabilities();
1656
1657 let mut invalidations = self.modifier_chain.take_invalidations();
1658 invalidations.extend(modifier_local_invalidations);
1659
1660 (invalidations, modifier_changed)
1661 }
1662
1663 fn set_debug_modifiers(&mut self, enabled: bool) {
1664 self.debug_modifiers = enabled;
1665 self.modifier_chain.set_debug_logging(enabled);
1666 }
1667}
1668
1669#[cfg(test)]
1670#[path = "tests/subcompose_layout_tests.rs"]
1671mod tests;