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(crate) fn slot_is_active(&self, slot_id: SlotId) -> bool {
590 self.state.slot_is_active(slot_id)
591 }
592
593 pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
599 self.state.register_content_type(slot_id, content_type);
600 }
601
602 pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
608 self.state.update_content_type(slot_id, content_type);
609 }
610
611 pub(crate) fn set_reusable_pool_limits(&mut self, per_type: usize, untyped: usize) {
612 self.state.set_reusable_pool_limits(per_type, untyped);
613 }
614
615 pub(crate) fn focused_slot(&self) -> Option<SlotId> {
616 let mut node_id = crate::active_focus_target()?;
617 while node_id != self.root_id {
618 if let Some(slot) = self.state.active_slot_for_node(node_id) {
619 return Some(slot);
620 }
621 node_id = self.composer.node_parent(node_id).ok()??;
622 }
623 None
624 }
625
626 pub(crate) fn recycle_active_slots_where(&mut self, predicate: impl FnMut(SlotId) -> bool) {
627 let disposed = self.state.recycle_active_slots_where(predicate);
628 if let Err(error) = self.composer.dispose_subcomposed_nodes(disposed) {
629 self.record_error(error);
630 }
631 }
632
633 pub fn was_last_slot_reused(&self) -> Option<bool> {
641 self.state.was_last_slot_reused()
642 }
643
644 pub(crate) fn measure_retained(
645 &mut self,
646 child: SubcomposeChild,
647 constraints: Constraints,
648 ) -> (SubcomposePlaceable, Option<Rc<MeasuredNode>>) {
649 let placeable = self.measure(child, constraints);
650 let retained = (self.retained_measure_lookup)(child.node_id);
651 (placeable, retained)
652 }
653
654 pub(crate) fn register_retained_measurements(&mut self, measurements: &[Rc<MeasuredNode>]) {
655 if measurements.is_empty() {
656 return;
657 }
658
659 for measured in measurements {
660 self.register_measurement_node_id(measured.node_id());
661 }
662 (self.retained_measure_registrar)(measurements);
663 }
664
665 pub(crate) fn children_need_relayout(&mut self, children: &[SubcomposeChild]) -> bool {
666 if !self.ensure_pending_commands_applied() {
667 return true;
668 }
669
670 self.composer
671 .nodes_need_relayout(children.iter().map(SubcomposeChild::node_id))
672 }
673
674 pub(crate) fn ensure_cached_measurement_node_ids<I>(
675 &mut self,
676 node_ids: I,
677 constraints: Constraints,
678 ) -> usize
679 where
680 I: IntoIterator<Item = NodeId>,
681 {
682 if self.error.borrow().is_some() || !self.ensure_pending_commands_applied() {
683 return 0;
684 }
685
686 self.cached_measure_node_scratch.clear();
687 self.cached_measure_node_scratch.extend(
688 node_ids
689 .into_iter()
690 .filter(|node_id| !self.registered_measurement_node_ids.contains(node_id)),
691 );
692 if self.cached_measure_node_scratch.is_empty() {
693 return 0;
694 }
695
696 self.cached_measure_size_scratch.clear();
697 (self.cached_measure_batch_registrar)(
698 &self.cached_measure_node_scratch,
699 constraints,
700 &mut self.cached_measure_size_scratch,
701 );
702 self.cached_measure_size_scratch
703 .resize(self.cached_measure_node_scratch.len(), None);
704
705 let mut cached_count = 0;
706 self.cached_measure_missing_scratch.clear();
707 for index in 0..self.cached_measure_node_scratch.len() {
708 let node_id = self.cached_measure_node_scratch[index];
709 if self.cached_measure_size_scratch[index].is_some() {
710 cached_count += 1;
711 self.register_measurement_node_id(node_id);
712 } else {
713 self.cached_measure_missing_scratch.push(node_id);
714 }
715 }
716
717 let mut missing = std::mem::take(&mut self.cached_measure_missing_scratch);
718 for node_id in missing.drain(..) {
719 let _ = self.measure(SubcomposeChild::new(node_id), constraints);
720 }
721 self.cached_measure_missing_scratch = missing;
722
723 cached_count
724 }
725}
726
727fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
728 cranpose_core::with_current_composer(|composer| {
729 let holder_for_recompose = holder.clone();
730 composer.set_recompose_callback(move |_composer| {
731 compose_subcompose_slot_content(holder_for_recompose.clone());
732 });
733 });
734
735 let invoke = holder.clone_rc();
736 invoke();
737}
738
739pub type MeasurePolicy =
740 dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
741
742pub struct SubcomposeLayoutNode {
744 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
745 parent: Cell<Option<NodeId>>,
746 id: Cell<Option<NodeId>>,
747 needs_measure: Cell<bool>,
748 needs_layout: Cell<bool>,
749 descendant_dirt: Cell<DescendantDirt>,
751 needs_semantics: Cell<bool>,
752 needs_redraw: Cell<bool>,
753 needs_pointer_pass: Cell<bool>,
754 needs_focus_sync: Cell<bool>,
755 layout_state: RefCell<LayoutState>,
756 cache_handles: LayoutNodeCacheHandles,
757 modifier_slices_snapshot: crate::modifier::SlicesSnapshot,
758 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
762 modifier_slices_dirty: Cell<bool>,
763}
764
765impl SubcomposeLayoutNode {
766 pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
767 let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
768 let node = Self {
769 inner,
770 parent: Cell::new(None),
771 id: Cell::new(None),
772 needs_measure: Cell::new(true),
773 needs_layout: Cell::new(true),
774 descendant_dirt: Cell::default(),
775 needs_semantics: Cell::new(true),
776 needs_redraw: Cell::new(true),
777 needs_pointer_pass: Cell::new(false),
778 needs_focus_sync: Cell::new(false),
779 layout_state: RefCell::new(LayoutState::default()),
780 cache_handles: LayoutNodeCacheHandles::default(),
781 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
782 coordinator_geometry: Rc::default(),
783 modifier_slices_dirty: Cell::new(true),
784 };
785 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
786 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
787 node.note_host_to_the_composition_that_made_it();
788 node
789 }
790
791 fn note_host_to_the_composition_that_made_it(&self) {
792 let host = Rc::clone(&self.inner.borrow().slots);
793 cranpose_core::note_nested_slots_host(&host);
794 }
795
796 pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
802 let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
803 inner_data
804 .state
805 .set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
806 let inner = Rc::new(RefCell::new(inner_data));
807 let node = Self {
808 inner,
809 parent: Cell::new(None),
810 id: Cell::new(None),
811 needs_measure: Cell::new(true),
812 needs_layout: Cell::new(true),
813 descendant_dirt: Cell::default(),
814 needs_semantics: Cell::new(true),
815 needs_redraw: Cell::new(true),
816 needs_pointer_pass: Cell::new(false),
817 needs_focus_sync: Cell::new(false),
818 layout_state: RefCell::new(LayoutState::default()),
819 cache_handles: LayoutNodeCacheHandles::default(),
820 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
821 coordinator_geometry: Rc::default(),
822 modifier_slices_dirty: Cell::new(true),
823 };
824 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
825 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
826 node.note_host_to_the_composition_that_made_it();
827 node
828 }
829
830 pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
831 SubcomposeLayoutNodeHandle {
832 inner: Rc::clone(&self.inner),
833 }
834 }
835
836 #[doc(hidden)]
837 pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
838 self.inner.borrow().state.debug_scope_ids_by_slot()
839 }
840
841 #[doc(hidden)]
842 pub fn debug_slot_table_for_slot(
843 &self,
844 slot_id: cranpose_core::SlotId,
845 ) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
846 self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
847 }
848
849 #[doc(hidden)]
850 pub fn debug_slot_table_groups_for_slot(
851 &self,
852 slot_id: cranpose_core::SlotId,
853 ) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
854 self.inner
855 .borrow()
856 .state
857 .debug_slot_table_groups_for_slot(slot_id)
858 }
859
860 pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
861 let mut inner = self.inner.borrow_mut();
862 if Rc::ptr_eq(&inner.measure_policy, &policy) {
863 return;
864 }
865 inner.set_measure_policy(policy);
866 drop(inner);
867 self.invalidate_subcomposition();
868 }
869
870 pub fn set_captured_context(&mut self, context: cranpose_core::CapturedCompositionContext) {
875 let locals_changed = context.locals_changed();
876 self.inner.borrow_mut().captured_context = Some(context);
877 if locals_changed {
878 self.inner.borrow().state.invalidate_locals();
879 self.request_measure_recompose();
880 }
881 }
882
883 pub fn set_density(&mut self, density: crate::density::Density) {
889 let mut inner = self.inner.borrow_mut();
890 if inner.density != density {
891 inner.density = density;
892 drop(inner);
893 self.modifier_slices_dirty.set(true);
894 self.mark_needs_measure();
895 }
896 }
897
898 pub fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
900 let mut inner = self.inner.borrow_mut();
901 if inner.modifier_chain.set_layout_direction(direction) {
902 inner.resolved_modifiers = inner.modifier_chain.resolved_modifiers();
903 drop(inner);
904 self.modifier_slices_dirty.set(true);
905 self.mark_needs_measure();
906 }
907 }
908
909 pub(crate) fn density(&self) -> crate::density::Density {
911 self.inner.borrow().density
912 }
913
914 pub fn set_modifier(&mut self, modifier: Modifier) {
915 let prev_caps = self.modifier_capabilities();
916 let (invalidations, modifier_changed) = {
917 let mut inner = self.inner.borrow_mut();
918 inner.set_modifier_collect(modifier)
919 };
920 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
921 self.modifier_slices_dirty.set(true);
922 crate::modal_nodes::reach_changed(self.id.get());
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 self.modifier_slices_snapshot.collect(
931 inner.modifier_chain.chain(),
932 &self.coordinator_geometry,
933 inner.density.density(),
934 );
935 self.modifier_slices_dirty.set(false);
936 }
937
938 pub(crate) fn mark_modifier_slices_dirty(&self) {
939 self.modifier_slices_dirty.set(true);
940 }
941
942 pub fn set_debug_modifiers(&mut self, enabled: bool) {
943 self.inner.borrow_mut().set_debug_modifiers(enabled);
944 }
945
946 pub fn modifier(&self) -> Modifier {
947 self.handle().modifier()
948 }
949
950 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
951 self.inner.borrow().resolved_modifiers
952 }
953
954 pub fn layout_state(&self) -> LayoutState {
956 self.layout_state.borrow().clone()
957 }
958
959 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
960 &self.cache_handles
961 }
962
963 pub fn set_position(&self, position: Point) {
966 self.layout_state.borrow_mut().place(position);
967 }
968
969 pub fn set_measured_size(&self, size: Size) {
973 self.layout_state.borrow_mut().set_size(size);
974 }
975
976 pub fn clear_placed(&self) {
978 self.layout_state.borrow_mut().clear_placed();
979 }
980
981 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
983 crate::modifier::collect_semantics_from_chain(self.inner.borrow().modifier_chain.chain())
984 }
985
986 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
988 crate::modifier::semantics_reach_of_chain(self.inner.borrow().modifier_chain.chain())
989 }
990
991 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
993 if self.modifier_slices_dirty.get() {
994 self.update_modifier_slices_cache();
995 }
996 self.modifier_slices_snapshot.get()
997 }
998
999 pub fn state(&self) -> Ref<'_, SubcomposeState> {
1000 Ref::map(self.inner.borrow(), |inner| &inner.state)
1001 }
1002
1003 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
1004 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
1005 }
1006
1007 pub fn invalidate_subcomposition(&self) {
1008 self.inner.borrow().state.invalidate_scopes();
1009 self.mark_needs_measure();
1010 if let Some(id) = self.id.get() {
1011 cranpose_core::bubble_measure_dirty_in_composer(id);
1012 }
1013 }
1014
1015 pub fn request_measure_recompose(&self) {
1016 self.mark_needs_measure();
1017 if let Some(id) = self.id.get() {
1018 cranpose_core::bubble_measure_dirty_in_composer(id);
1019 }
1020 }
1021
1022 pub fn with_active_children<R>(&self, f: impl FnOnce(&[NodeId]) -> R) -> R {
1027 let children = Rc::clone(&self.inner.borrow().last_placements);
1028 f(&children)
1029 }
1030
1031 pub fn mark_needs_measure(&self) {
1033 self.needs_measure.set(true);
1034 self.needs_layout.set(true);
1035 }
1036
1037 pub fn mark_needs_layout_flag(&self) {
1039 self.needs_layout.set(true);
1040 }
1041
1042 pub fn mark_needs_redraw(&self) {
1044 self.needs_redraw.set(true);
1045 if let Some(id) = self.id.get() {
1046 crate::schedule_draw_repass(id);
1047 }
1048 crate::request_render_invalidation();
1049 }
1050
1051 pub fn needs_measure(&self) -> bool {
1053 self.needs_measure.get()
1054 }
1055
1056 pub(crate) fn clear_needs_measure(&self) {
1057 self.needs_measure.set(false);
1058 }
1059
1060 pub(crate) fn clear_needs_layout(&self) {
1063 self.needs_layout.set(false);
1064 self.descendant_dirt.set(DescendantDirt::default());
1065 }
1066
1067 pub fn mark_needs_semantics(&self) {
1069 self.needs_semantics.set(true);
1070 crate::modal_nodes::reach_changed(self.id.get());
1071 }
1072
1073 pub(crate) fn id(&self) -> Option<NodeId> {
1075 self.id.get()
1076 }
1077
1078 pub(crate) fn clear_needs_semantics(&self) {
1079 self.needs_semantics.set(false);
1080 }
1081
1082 #[cfg(test)]
1083 pub(crate) fn clear_needs_semantics_for_tests(&self) {
1084 self.clear_needs_semantics();
1085 }
1086
1087 pub fn needs_redraw(&self) -> bool {
1089 self.needs_redraw.get()
1090 }
1091
1092 pub fn clear_needs_redraw(&self) {
1093 self.needs_redraw.set(false);
1094 }
1095
1096 pub fn mark_needs_pointer_pass(&self) {
1098 self.needs_pointer_pass.set(true);
1099 }
1100
1101 pub fn needs_pointer_pass(&self) -> bool {
1103 self.needs_pointer_pass.get()
1104 }
1105
1106 pub fn clear_needs_pointer_pass(&self) {
1108 self.needs_pointer_pass.set(false);
1109 }
1110
1111 pub fn mark_needs_focus_sync(&self) {
1113 self.needs_focus_sync.set(true);
1114 }
1115
1116 pub fn needs_focus_sync(&self) -> bool {
1118 self.needs_focus_sync.get()
1119 }
1120
1121 pub fn clear_needs_focus_sync(&self) {
1123 self.needs_focus_sync.set(false);
1124 }
1125
1126 fn request_semantics_update(&self) {
1127 let already_dirty = self.needs_semantics.replace(true);
1128 if already_dirty {
1129 return;
1130 }
1131
1132 if let Some(id) = self.id.get() {
1133 cranpose_core::queue_semantics_invalidation(id);
1134 }
1135 }
1136
1137 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1139 self.inner.borrow().modifier_capabilities
1140 }
1141
1142 pub fn has_layout_modifier_nodes(&self) -> bool {
1143 self.modifier_capabilities()
1144 .contains(NodeCapabilities::LAYOUT)
1145 }
1146
1147 pub fn has_draw_modifier_nodes(&self) -> bool {
1148 self.modifier_capabilities()
1149 .contains(NodeCapabilities::DRAW)
1150 }
1151
1152 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1153 self.modifier_capabilities()
1154 .contains(NodeCapabilities::POINTER_INPUT)
1155 }
1156
1157 pub fn has_semantics_modifier_nodes(&self) -> bool {
1158 self.modifier_capabilities()
1159 .contains(NodeCapabilities::SEMANTICS)
1160 }
1161
1162 pub fn has_focus_modifier_nodes(&self) -> bool {
1163 self.modifier_capabilities()
1164 .contains(NodeCapabilities::FOCUS)
1165 }
1166
1167 fn dispatch_modifier_invalidations(
1168 &self,
1169 invalidations: &[ModifierInvalidation],
1170 prev_caps: NodeCapabilities,
1171 ) {
1172 let curr_caps = self.modifier_capabilities();
1173 for invalidation in invalidations {
1174 self.modifier_slices_dirty.set(true);
1175 let invalidation_caps = invalidation.capabilities();
1176 let has_capability = |capability| {
1177 curr_caps.contains(capability)
1178 || prev_caps.contains(capability)
1179 || invalidation_caps.contains(capability)
1180 };
1181 match invalidation.kind() {
1182 InvalidationKind::Layout => {
1183 if has_capability(NodeCapabilities::LAYOUT) {
1184 self.mark_needs_measure();
1185 }
1186 }
1187 InvalidationKind::Draw => {
1188 if has_capability(NodeCapabilities::DRAW) {
1189 self.mark_needs_redraw();
1190 }
1191 }
1192 InvalidationKind::PointerInput => {
1193 if has_capability(NodeCapabilities::POINTER_INPUT) {
1194 self.mark_needs_pointer_pass();
1195 crate::request_pointer_invalidation();
1196 if let Some(id) = self.id.get() {
1197 crate::schedule_pointer_repass(id);
1198 }
1199 }
1200 }
1201 InvalidationKind::Semantics => {
1202 self.request_semantics_update();
1203 }
1204 InvalidationKind::Focus => {
1205 if has_capability(NodeCapabilities::FOCUS) {
1206 self.mark_needs_focus_sync();
1207 crate::request_focus_invalidation();
1208 if let Some(id) = self.id.get() {
1209 crate::schedule_focus_invalidation(id);
1210 }
1211 }
1212 }
1213 }
1214 }
1215 }
1216}
1217
1218impl cranpose_core::Node for SubcomposeLayoutNode {
1219 fn mount(&mut self) {
1220 let mut inner = self.inner.borrow_mut();
1221 let node_id = inner.node_id;
1222 inner.modifier_chain.remount(node_id);
1223 crate::modal_nodes::reach_changed(self.id.get());
1224 }
1225
1226 fn unmount(&mut self) {
1227 self.inner
1228 .borrow_mut()
1229 .modifier_chain
1230 .chain_mut()
1231 .detach_nodes();
1232 }
1233
1234 fn insert_child(&mut self, child: NodeId) -> bool {
1235 let mut inner = self.inner.borrow_mut();
1236 if inner.children.contains(&child) {
1237 return false;
1238 }
1239 inner.children.push(child);
1240 true
1241 }
1242
1243 fn remove_child(&mut self, child: NodeId) -> bool {
1244 let mut inner = self.inner.borrow_mut();
1245 let before = inner.children.len();
1246 inner.children.retain(|&id| id != child);
1247 inner.children.len() < before
1248 }
1249
1250 fn move_child(&mut self, from: usize, to: usize) {
1251 let mut inner = self.inner.borrow_mut();
1252 if from == to || from >= inner.children.len() {
1253 return;
1254 }
1255 let child = inner.children.remove(from);
1256 let target = to.min(inner.children.len());
1257 inner.children.insert(target, child);
1258 }
1259
1260 fn update_children(&mut self, children: &[NodeId]) {
1261 let mut inner = self.inner.borrow_mut();
1262 inner.children.clear();
1263 inner.children.extend_from_slice(children);
1264 }
1265
1266 fn collect_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1267 out.clear();
1268 self.with_active_children(|children| out.extend_from_slice(children));
1269 }
1270
1271 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1272 out.clear();
1273 out.extend_from_slice(&self.inner.borrow().children);
1274 }
1275
1276 fn owned_child_index(&self, child: NodeId) -> Option<usize> {
1277 self.inner
1278 .borrow()
1279 .children
1280 .iter()
1281 .position(|&id| id == child)
1282 }
1283
1284 fn set_node_id(&mut self, id: NodeId) {
1285 self.id.set(Some(id));
1286 self.layout_state.borrow_mut().set_node_id(id);
1287 {
1288 let mut inner = self.inner.borrow_mut();
1289 let previous = inner.node_id.replace(id);
1290 inner.modifier_chain.move_to_node_later(previous, Some(id));
1291 }
1292 self.modifier_slices_dirty.set(true);
1294 }
1295
1296 fn on_attached_to_parent(&mut self, parent: NodeId) {
1297 self.parent.set(Some(parent));
1298 }
1299
1300 fn on_removed_from_parent(&mut self) {
1301 self.parent.set(None);
1302 self.inner.borrow().state.bump_content_generation();
1303 }
1304
1305 fn parent(&self) -> Option<NodeId> {
1306 self.parent.get()
1307 }
1308
1309 fn mark_needs_layout(&self) {
1310 self.needs_layout.set(true);
1311 }
1312
1313 fn needs_layout(&self) -> bool {
1314 self.needs_layout.get()
1315 }
1316
1317 fn mark_needs_measure(&self) {
1318 self.needs_measure.set(true);
1319 self.needs_layout.set(true);
1320 }
1321
1322 fn needs_measure(&self) -> bool {
1323 self.needs_measure.get()
1324 }
1325
1326 fn mark_descendant_needs_layout(&self, measure: bool) {
1327 self.descendant_dirt
1328 .set(self.descendant_dirt.get().marked(measure));
1329 if measure {
1330 self.cache_handles.forget_intrinsics();
1331 }
1332 }
1333
1334 fn descendant_needs_layout(&self) -> bool {
1335 self.descendant_dirt.get().layout
1336 }
1337
1338 fn descendant_needs_measure(&self) -> bool {
1339 self.descendant_dirt.get().measure
1340 }
1341
1342 fn mark_needs_semantics(&self) {
1343 self.needs_semantics.set(true);
1344 crate::modal_nodes::reach_changed(self.id.get());
1345 }
1346
1347 fn mark_descendant_needs_semantics(&self) {
1350 self.needs_semantics.set(true);
1351 }
1352
1353 fn needs_semantics(&self) -> bool {
1354 self.needs_semantics.get()
1355 }
1356
1357 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1358 if self.parent.get().is_none() {
1359 self.parent.set(Some(parent));
1360 }
1361 }
1362}
1363
1364#[derive(Clone)]
1365pub struct SubcomposeLayoutNodeHandle {
1366 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1367}
1368
1369impl SubcomposeLayoutNodeHandle {
1370 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1371 let Ok(inner) = self.inner.try_borrow() else {
1372 return;
1373 };
1374 if Rc::ptr_eq(&inner.slots, slot_host) {
1375 return;
1376 }
1377 inner.slots.note_nested_host(slot_host);
1378 }
1379
1380 pub(crate) fn measured_children_scratch(
1381 &self,
1382 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1383 let scratch = {
1384 let inner = self.inner.borrow();
1385 Rc::clone(&inner.measured_children_scratch)
1386 };
1387 scratch.borrow_mut().clear();
1388 scratch
1389 }
1390
1391 pub fn modifier(&self) -> Modifier {
1392 self.inner.borrow().modifier.clone()
1393 }
1394
1395 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1396 self.resolved_modifiers().layout_properties()
1397 }
1398
1399 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1400 self.inner.borrow().resolved_modifiers
1401 }
1402
1403 pub fn total_offset(&self) -> Point {
1404 self.resolved_modifiers().offset()
1405 }
1406
1407 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1408 self.inner.borrow().modifier_capabilities
1409 }
1410
1411 pub fn has_layout_modifier_nodes(&self) -> bool {
1412 self.modifier_capabilities()
1413 .contains(NodeCapabilities::LAYOUT)
1414 }
1415
1416 pub fn has_draw_modifier_nodes(&self) -> bool {
1417 self.modifier_capabilities()
1418 .contains(NodeCapabilities::DRAW)
1419 }
1420
1421 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1422 self.modifier_capabilities()
1423 .contains(NodeCapabilities::POINTER_INPUT)
1424 }
1425
1426 pub fn has_semantics_modifier_nodes(&self) -> bool {
1427 self.modifier_capabilities()
1428 .contains(NodeCapabilities::SEMANTICS)
1429 }
1430
1431 pub fn has_focus_modifier_nodes(&self) -> bool {
1432 self.modifier_capabilities()
1433 .contains(NodeCapabilities::FOCUS)
1434 }
1435
1436 pub fn set_debug_modifiers(&self, enabled: bool) {
1437 self.inner.borrow_mut().set_debug_modifiers(enabled);
1438 }
1439
1440 pub fn measure<'a>(
1441 &self,
1442 composer: &Composer,
1443 node_id: NodeId,
1444 constraints: Constraints,
1445 measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
1446 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1447 error: &'a RefCell<Option<NodeError>>,
1448 ) -> Result<MeasureResult, NodeError> {
1449 self.measure_with_cached_batch(
1450 composer,
1451 node_id,
1452 constraints,
1453 CachedBatchMeasureInputs {
1454 measurer,
1455 cached_measure_batch_registrar: Box::new(
1456 move |node_ids, child_constraints, out| {
1457 out.clear();
1458 out.reserve(node_ids.len());
1459 for &child_id in node_ids {
1460 out.push(cached_measure_registrar(child_id, child_constraints));
1461 }
1462 },
1463 ),
1464 retained_measure_lookup: Box::new(|_| None),
1465 retained_measure_registrar: Box::new(|_| {}),
1466 error,
1467 },
1468 )
1469 }
1470
1471 pub(crate) fn measure_with_cached_batch(
1472 &self,
1473 composer: &Composer,
1474 node_id: NodeId,
1475 constraints: Constraints,
1476 callbacks: CachedBatchMeasureInputs<'_>,
1477 ) -> Result<MeasureResult, NodeError> {
1478 let CachedBatchMeasureInputs {
1479 measurer,
1480 cached_measure_batch_registrar,
1481 retained_measure_lookup,
1482 retained_measure_registrar,
1483 error,
1484 } = callbacks;
1485 let (policy, mut state, slots_host, placement_scratch, captured_context, density) = {
1486 let mut inner = self.inner.borrow_mut();
1487 let policy = Rc::clone(&inner.measure_policy);
1488 let state = std::mem::take(&mut inner.state);
1489 let slots_host = Rc::clone(&inner.slots);
1490 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1491 let captured_context = inner.captured_context.clone();
1492 let density = inner.density;
1493 (
1494 policy,
1495 state,
1496 slots_host,
1497 placement_scratch,
1498 captured_context,
1499 density,
1500 )
1501 };
1502 state.begin_pass();
1503
1504 let previous = composer.phase();
1505 if !matches!(previous, Phase::Measure | Phase::Layout) {
1506 composer.enter_phase(Phase::Measure);
1507 }
1508
1509 let constraints_copy = constraints;
1510 let fallback_context;
1511 let context = if let Some(context) = captured_context.as_ref() {
1512 context
1513 } else {
1514 fallback_context = composer.capture_composition_context();
1515 &fallback_context
1516 };
1517 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1518 &slots_host,
1519 Some(node_id),
1520 context,
1521 |inner_composer| {
1522 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1523 composer: inner_composer.clone(),
1524 density,
1525 state: &mut state,
1526 constraints: constraints_copy,
1527 measurer,
1528 cached_measure_batch_registrar,
1529 retained_measure_lookup,
1530 retained_measure_registrar,
1531 error,
1532 parent_handle: self.clone(),
1533 root_id: node_id,
1534 placement_scratch,
1535 });
1536 let result = (policy)(&mut scope, constraints_copy);
1537 (result, scope.into_placement_scratch())
1538 },
1539 )?;
1540
1541 let disposed = state.finish_pass();
1542 let disposal = composer.dispose_subcomposed_nodes(disposed);
1543
1544 if previous != composer.phase() {
1545 composer.enter_phase(previous);
1546 }
1547
1548 {
1549 let mut inner = self.inner.borrow_mut();
1550 inner.state = state;
1551 inner.placement_scratch = placement_scratch;
1552
1553 inner.replace_placed_children(
1554 result.placements.iter().map(|placement| placement.node_id),
1555 );
1556 }
1557
1558 disposal?;
1559 Ok(result)
1560 }
1561
1562 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1563 placements.clear();
1564 let mut inner = self.inner.borrow_mut();
1565 if placements.capacity() > inner.placement_scratch.capacity() {
1566 inner.placement_scratch = placements;
1567 }
1568 }
1569
1570 pub fn set_active_children<I>(&self, children: I)
1571 where
1572 I: IntoIterator<Item = NodeId>,
1573 {
1574 self.inner.borrow_mut().replace_placed_children(children);
1575 }
1576}
1577
1578struct SubcomposeLayoutNodeInner {
1579 modifier: Modifier,
1580 modifier_chain: ModifierChainHandle,
1581 resolved_modifiers: ResolvedModifiers,
1582 modifier_capabilities: NodeCapabilities,
1583 state: SubcomposeState,
1584 measure_policy: Rc<MeasurePolicy>,
1585 children: Vec<NodeId>,
1586 slots: Rc<SlotsHost>,
1587 debug_modifiers: bool,
1588 node_id: Option<NodeId>,
1589 last_placements: Rc<Vec<NodeId>>,
1590 placement_scratch: Vec<Placement>,
1591 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1592 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1593 density: crate::density::Density,
1594}
1595
1596impl SubcomposeLayoutNodeInner {
1597 fn replace_placed_children<I>(&mut self, children: I)
1608 where
1609 I: IntoIterator<Item = NodeId>,
1610 {
1611 let mut changed = false;
1612 let mut count = 0usize;
1613 for child in children {
1614 match self.last_placements.get(count) {
1615 Some(&placed) if placed == child => {}
1616 Some(_) => {
1617 Rc::make_mut(&mut self.last_placements)[count] = child;
1618 changed = true;
1619 }
1620 None => {
1621 Rc::make_mut(&mut self.last_placements).push(child);
1622 changed = true;
1623 }
1624 }
1625 count += 1;
1626 }
1627 if self.last_placements.len() > count {
1628 Rc::make_mut(&mut self.last_placements).truncate(count);
1629 changed = true;
1630 }
1631 if changed && let Some(id) = self.node_id {
1632 crate::render_state::record_geometry_scene_node(id);
1633 }
1634 }
1635
1636 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1637 Self {
1638 modifier: Modifier::empty(),
1639 modifier_chain: ModifierChainHandle::new(),
1640 resolved_modifiers: ResolvedModifiers::default(),
1641 modifier_capabilities: NodeCapabilities::default(),
1642 state: SubcomposeState::default(),
1643 measure_policy,
1644 children: Vec::new(),
1645 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1646 debug_modifiers: false,
1647 node_id: None,
1648 last_placements: Rc::new(Vec::new()),
1649 placement_scratch: Vec::new(),
1650 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1651 captured_context: None,
1652 density: crate::density::Density::default(),
1653 }
1654 }
1655
1656 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1657 self.measure_policy = policy;
1658 if let Err(err) = self.slots.reset() {
1659 log::error!(
1660 "failed to reset root measurement slots after measure policy update: {err}"
1661 );
1662 }
1663 }
1664
1665 fn set_modifier_collect(&mut self, modifier: Modifier) -> (ModifierInvalidations, bool) {
1666 let modifier_changed = !self.modifier.structural_eq(&modifier);
1667 self.modifier = modifier;
1668 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1669 let invalidations = self
1670 .modifier_chain
1671 .update_on_node(self.node_id, &self.modifier);
1672 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1673 self.modifier_capabilities = self.modifier_chain.capabilities();
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;