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