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