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