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