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