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 drop(inner);
908 self.modifier_slices_dirty.set(true);
909 self.mark_needs_measure();
910 }
911 }
912
913 pub(crate) fn density(&self) -> crate::density::Density {
915 self.inner.borrow().density
916 }
917
918 pub fn set_modifier(&mut self, modifier: Modifier) {
921 let prev_caps = self.modifier_capabilities();
922 let (invalidations, modifier_changed) = {
923 let mut inner = self.inner.borrow_mut();
924 inner.set_modifier_collect(modifier)
925 };
926 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
927 self.modifier_slices_dirty.set(true);
928 crate::modal_nodes::reach_changed(self.id.get());
929 if modifier_changed {
930 self.request_semantics_update();
931 }
932 }
933
934 fn update_modifier_slices_cache(&self) {
935 let inner = self.inner.borrow();
936 self.modifier_slices_snapshot.collect(
937 inner.modifier_chain.chain(),
938 &self.coordinator_geometry,
939 inner.density.density(),
940 );
941 self.modifier_slices_dirty.set(false);
942 }
943
944 pub(crate) fn mark_modifier_slices_dirty(&self) {
945 self.modifier_slices_dirty.set(true);
946 }
947
948 pub fn set_debug_modifiers(&mut self, enabled: bool) {
950 self.inner.borrow_mut().set_debug_modifiers(enabled);
951 }
952
953 pub fn modifier(&self) -> Modifier {
955 self.handle().modifier()
956 }
957
958 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
960 self.inner.borrow().modifier_chain.resolved_modifiers()
961 }
962
963 pub(crate) fn parent_data(&self) -> cranpose_ui_layout::ParentData {
965 self.inner.borrow().modifier_chain.parent_data()
966 }
967
968 pub fn layout_state(&self) -> LayoutState {
970 self.layout_state.borrow().clone()
971 }
972
973 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
974 &self.cache_handles
975 }
976
977 pub fn set_position(&self, position: Point) {
980 self.layout_state.borrow_mut().place(position);
981 }
982
983 pub fn set_measured_size(&self, size: Size) {
987 self.layout_state.borrow_mut().set_size(size);
988 }
989
990 pub fn clear_placed(&self) {
992 self.layout_state.borrow_mut().clear_placed();
993 }
994
995 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
997 crate::modifier::collect_semantics_from_chain(self.inner.borrow().modifier_chain.chain())
998 }
999
1000 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1002 crate::modifier::semantics_reach_of_chain(self.inner.borrow().modifier_chain.chain())
1003 }
1004
1005 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1007 if self.modifier_slices_dirty.get() {
1008 self.update_modifier_slices_cache();
1009 }
1010 self.modifier_slices_snapshot.get()
1011 }
1012
1013 pub fn state(&self) -> Ref<'_, SubcomposeState> {
1015 Ref::map(self.inner.borrow(), |inner| &inner.state)
1016 }
1017
1018 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
1020 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
1021 }
1022
1023 pub fn invalidate_subcomposition(&self) {
1026 self.inner.borrow().state.invalidate_scopes();
1027 self.mark_needs_measure();
1028 if let Some(id) = self.id.get() {
1029 cranpose_core::bubble_measure_dirty_in_composer(id);
1030 }
1031 }
1032
1033 pub fn request_measure_recompose(&self) {
1036 self.mark_needs_measure();
1037 if let Some(id) = self.id.get() {
1038 cranpose_core::bubble_measure_dirty_in_composer(id);
1039 }
1040 }
1041
1042 pub fn with_active_children<R>(&self, f: impl FnOnce(&[NodeId]) -> R) -> R {
1047 let children = Rc::clone(&self.inner.borrow().last_placements);
1048 f(&children)
1049 }
1050
1051 pub fn mark_needs_measure(&self) {
1053 self.needs_measure.set(true);
1054 self.needs_layout.set(true);
1055 }
1056
1057 pub fn mark_needs_layout_flag(&self) {
1059 self.needs_layout.set(true);
1060 }
1061
1062 pub fn mark_needs_redraw(&self) {
1064 self.needs_redraw.set(true);
1065 if let Some(id) = self.id.get() {
1066 crate::schedule_draw_repass(id);
1067 }
1068 crate::request_render_invalidation();
1069 }
1070
1071 pub fn needs_measure(&self) -> bool {
1073 self.needs_measure.get()
1074 }
1075
1076 pub(crate) fn clear_needs_measure(&self) {
1077 self.needs_measure.set(false);
1078 }
1079
1080 pub(crate) fn clear_needs_layout(&self) {
1083 self.needs_layout.set(false);
1084 self.descendant_dirt.set(DescendantDirt::default());
1085 }
1086
1087 pub fn mark_needs_semantics(&self) {
1089 self.needs_semantics.set(true);
1090 crate::modal_nodes::reach_changed(self.id.get());
1091 }
1092
1093 pub(crate) fn id(&self) -> Option<NodeId> {
1095 self.id.get()
1096 }
1097
1098 pub(crate) fn clear_needs_semantics(&self) {
1099 self.needs_semantics.set(false);
1100 }
1101
1102 #[cfg(test)]
1103 pub(crate) fn clear_needs_semantics_for_tests(&self) {
1104 self.clear_needs_semantics();
1105 }
1106
1107 pub fn needs_redraw(&self) -> bool {
1109 self.needs_redraw.get()
1110 }
1111
1112 pub fn clear_needs_redraw(&self) {
1114 self.needs_redraw.set(false);
1115 }
1116
1117 pub fn mark_needs_pointer_pass(&self) {
1119 self.needs_pointer_pass.set(true);
1120 }
1121
1122 pub fn needs_pointer_pass(&self) -> bool {
1124 self.needs_pointer_pass.get()
1125 }
1126
1127 pub fn clear_needs_pointer_pass(&self) {
1129 self.needs_pointer_pass.set(false);
1130 }
1131
1132 pub fn mark_needs_focus_sync(&self) {
1134 self.needs_focus_sync.set(true);
1135 }
1136
1137 pub fn needs_focus_sync(&self) -> bool {
1139 self.needs_focus_sync.get()
1140 }
1141
1142 pub fn clear_needs_focus_sync(&self) {
1144 self.needs_focus_sync.set(false);
1145 }
1146
1147 fn request_semantics_update(&self) {
1148 let already_dirty = self.needs_semantics.replace(true);
1149 if already_dirty {
1150 return;
1151 }
1152
1153 if let Some(id) = self.id.get() {
1154 cranpose_core::queue_semantics_invalidation(id);
1155 }
1156 }
1157
1158 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1160 self.inner.borrow().modifier_capabilities
1161 }
1162
1163 pub fn has_layout_modifier_nodes(&self) -> bool {
1165 self.modifier_capabilities()
1166 .contains(NodeCapabilities::LAYOUT)
1167 }
1168
1169 pub fn has_draw_modifier_nodes(&self) -> bool {
1171 self.modifier_capabilities()
1172 .contains(NodeCapabilities::DRAW)
1173 }
1174
1175 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1177 self.modifier_capabilities()
1178 .contains(NodeCapabilities::POINTER_INPUT)
1179 }
1180
1181 pub fn has_semantics_modifier_nodes(&self) -> bool {
1183 self.modifier_capabilities()
1184 .contains(NodeCapabilities::SEMANTICS)
1185 }
1186
1187 pub fn has_focus_modifier_nodes(&self) -> bool {
1189 self.modifier_capabilities()
1190 .contains(NodeCapabilities::FOCUS)
1191 }
1192
1193 fn dispatch_modifier_invalidations(
1194 &self,
1195 invalidations: &[ModifierInvalidation],
1196 prev_caps: NodeCapabilities,
1197 ) {
1198 let curr_caps = self.modifier_capabilities();
1199 for invalidation in invalidations {
1200 self.modifier_slices_dirty.set(true);
1201 let invalidation_caps = invalidation.capabilities();
1202 let has_capability = |capability| {
1203 curr_caps.contains(capability)
1204 || prev_caps.contains(capability)
1205 || invalidation_caps.contains(capability)
1206 };
1207 match invalidation.kind() {
1208 InvalidationKind::Layout => {
1209 if has_capability(NodeCapabilities::LAYOUT) {
1210 self.mark_needs_measure();
1211 }
1212 }
1213 InvalidationKind::Draw => {
1214 if has_capability(NodeCapabilities::DRAW) {
1215 self.mark_needs_redraw();
1216 }
1217 }
1218 InvalidationKind::PointerInput => {
1219 if has_capability(NodeCapabilities::POINTER_INPUT) {
1220 self.mark_needs_pointer_pass();
1221 crate::request_pointer_invalidation();
1222 if let Some(id) = self.id.get() {
1223 crate::schedule_pointer_repass(id);
1224 }
1225 }
1226 }
1227 InvalidationKind::Semantics => {
1228 self.request_semantics_update();
1229 }
1230 InvalidationKind::Focus => {
1231 if has_capability(NodeCapabilities::FOCUS) {
1232 self.mark_needs_focus_sync();
1233 crate::request_focus_invalidation();
1234 if let Some(id) = self.id.get() {
1235 crate::schedule_focus_invalidation(id);
1236 }
1237 }
1238 }
1239 }
1240 }
1241 }
1242}
1243
1244impl cranpose_core::Node for SubcomposeLayoutNode {
1245 fn mount(&mut self) {
1246 let mut inner = self.inner.borrow_mut();
1247 let node_id = inner.node_id;
1248 inner.modifier_chain.remount(node_id);
1249 crate::modal_nodes::reach_changed(self.id.get());
1250 }
1251
1252 fn unmount(&mut self) {
1253 self.inner
1254 .borrow_mut()
1255 .modifier_chain
1256 .chain_mut()
1257 .detach_nodes();
1258 }
1259
1260 fn insert_child(&mut self, child: NodeId) -> bool {
1261 let mut inner = self.inner.borrow_mut();
1262 if inner.children.contains(&child) {
1263 return false;
1264 }
1265 inner.children.push(child);
1266 true
1267 }
1268
1269 fn remove_child(&mut self, child: NodeId) -> bool {
1270 let mut inner = self.inner.borrow_mut();
1271 let before = inner.children.len();
1272 inner.children.retain(|&id| id != child);
1273 inner.children.len() < before
1274 }
1275
1276 fn move_child(&mut self, from: usize, to: usize) {
1277 let mut inner = self.inner.borrow_mut();
1278 if from == to || from >= inner.children.len() {
1279 return;
1280 }
1281 let child = inner.children.remove(from);
1282 let target = to.min(inner.children.len());
1283 inner.children.insert(target, child);
1284 }
1285
1286 fn update_children(&mut self, children: &[NodeId]) {
1287 let mut inner = self.inner.borrow_mut();
1288 inner.children.clear();
1289 inner.children.extend_from_slice(children);
1290 }
1291
1292 fn collect_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1293 out.clear();
1294 self.with_active_children(|children| out.extend_from_slice(children));
1295 }
1296
1297 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1298 out.clear();
1299 out.extend_from_slice(&self.inner.borrow().children);
1300 }
1301
1302 fn owned_child_index(&self, child: NodeId) -> Option<usize> {
1303 self.inner
1304 .borrow()
1305 .children
1306 .iter()
1307 .position(|&id| id == child)
1308 }
1309
1310 fn set_node_id(&mut self, id: NodeId) {
1311 self.id.set(Some(id));
1312 self.layout_state.borrow_mut().set_node_id(id);
1313 {
1314 let mut inner = self.inner.borrow_mut();
1315 let previous = inner.node_id.replace(id);
1316 inner.modifier_chain.move_to_node_later(previous, Some(id));
1317 }
1318 self.modifier_slices_dirty.set(true);
1320 }
1321
1322 fn on_attached_to_parent(&mut self, parent: NodeId) {
1323 self.parent.set(Some(parent));
1324 }
1325
1326 fn on_removed_from_parent(&mut self) {
1327 self.parent.set(None);
1328 self.inner.borrow().state.bump_content_generation();
1329 }
1330
1331 fn parent(&self) -> Option<NodeId> {
1332 self.parent.get()
1333 }
1334
1335 fn mark_needs_layout(&self) {
1336 self.needs_layout.set(true);
1337 }
1338
1339 fn needs_layout(&self) -> bool {
1340 self.needs_layout.get()
1341 }
1342
1343 fn mark_needs_measure(&self) {
1344 self.needs_measure.set(true);
1345 self.needs_layout.set(true);
1346 }
1347
1348 fn needs_measure(&self) -> bool {
1349 self.needs_measure.get()
1350 }
1351
1352 fn mark_descendant_needs_layout(&self, measure: bool) {
1353 self.descendant_dirt
1354 .set(self.descendant_dirt.get().marked(measure));
1355 if measure {
1356 self.cache_handles.forget_intrinsics();
1357 }
1358 }
1359
1360 fn descendant_needs_layout(&self) -> bool {
1361 self.descendant_dirt.get().layout
1362 }
1363
1364 fn descendant_needs_measure(&self) -> bool {
1365 self.descendant_dirt.get().measure
1366 }
1367
1368 fn mark_needs_semantics(&self) {
1369 self.needs_semantics.set(true);
1370 crate::modal_nodes::reach_changed(self.id.get());
1371 }
1372
1373 fn mark_descendant_needs_semantics(&self) {
1376 self.needs_semantics.set(true);
1377 }
1378
1379 fn needs_semantics(&self) -> bool {
1380 self.needs_semantics.get()
1381 }
1382
1383 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1384 if self.parent.get().is_none() {
1385 self.parent.set(Some(parent));
1386 }
1387 }
1388}
1389
1390#[derive(Clone)]
1391pub struct SubcomposeLayoutNodeHandle {
1392 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1393}
1394
1395impl SubcomposeLayoutNodeHandle {
1396 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1397 let Ok(inner) = self.inner.try_borrow() else {
1398 return;
1399 };
1400 if Rc::ptr_eq(&inner.slots, slot_host) {
1401 return;
1402 }
1403 inner.slots.note_nested_host(slot_host);
1404 }
1405
1406 pub(crate) fn measured_children_scratch(
1407 &self,
1408 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1409 let scratch = {
1410 let inner = self.inner.borrow();
1411 Rc::clone(&inner.measured_children_scratch)
1412 };
1413 scratch.borrow_mut().clear();
1414 scratch
1415 }
1416
1417 pub fn modifier(&self) -> Modifier {
1418 self.inner.borrow().modifier.clone()
1419 }
1420
1421 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1422 self.resolved_modifiers().layout_properties()
1423 }
1424
1425 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1426 self.inner.borrow().modifier_chain.resolved_modifiers()
1427 }
1428
1429 pub fn total_offset(&self) -> Point {
1430 self.resolved_modifiers().offset()
1431 }
1432
1433 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1434 self.inner.borrow().modifier_capabilities
1435 }
1436
1437 pub fn has_layout_modifier_nodes(&self) -> bool {
1438 self.modifier_capabilities()
1439 .contains(NodeCapabilities::LAYOUT)
1440 }
1441
1442 pub fn has_draw_modifier_nodes(&self) -> bool {
1443 self.modifier_capabilities()
1444 .contains(NodeCapabilities::DRAW)
1445 }
1446
1447 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1448 self.modifier_capabilities()
1449 .contains(NodeCapabilities::POINTER_INPUT)
1450 }
1451
1452 pub fn has_semantics_modifier_nodes(&self) -> bool {
1453 self.modifier_capabilities()
1454 .contains(NodeCapabilities::SEMANTICS)
1455 }
1456
1457 pub fn has_focus_modifier_nodes(&self) -> bool {
1458 self.modifier_capabilities()
1459 .contains(NodeCapabilities::FOCUS)
1460 }
1461
1462 pub fn set_debug_modifiers(&self, enabled: bool) {
1463 self.inner.borrow_mut().set_debug_modifiers(enabled);
1464 }
1465
1466 pub fn measure<'a>(
1467 &self,
1468 composer: &Composer,
1469 node_id: NodeId,
1470 constraints: Constraints,
1471 measurer: Box<dyn FnMut(NodeId, Constraints) -> SubcomposePlaceable + 'a>,
1472 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1473 error: &'a RefCell<Option<NodeError>>,
1474 ) -> Result<MeasureResult, NodeError> {
1475 self.measure_with_cached_batch(
1476 composer,
1477 node_id,
1478 constraints,
1479 CachedBatchMeasureInputs {
1480 measurer,
1481 cached_measure_batch_registrar: Box::new(
1482 move |node_ids, child_constraints, out| {
1483 out.clear();
1484 out.reserve(node_ids.len());
1485 for &child_id in node_ids {
1486 out.push(cached_measure_registrar(child_id, child_constraints));
1487 }
1488 },
1489 ),
1490 retained_measure_lookup: Box::new(|_| None),
1491 retained_measure_registrar: Box::new(|_| {}),
1492 error,
1493 },
1494 )
1495 }
1496
1497 pub(crate) fn measure_with_cached_batch(
1498 &self,
1499 composer: &Composer,
1500 node_id: NodeId,
1501 constraints: Constraints,
1502 callbacks: CachedBatchMeasureInputs<'_>,
1503 ) -> Result<MeasureResult, NodeError> {
1504 let CachedBatchMeasureInputs {
1505 measurer,
1506 cached_measure_batch_registrar,
1507 retained_measure_lookup,
1508 retained_measure_registrar,
1509 error,
1510 } = callbacks;
1511 let (policy, mut state, slots_host, placement_scratch, captured_context, density) = {
1512 let mut inner = self.inner.borrow_mut();
1513 let policy = Rc::clone(&inner.measure_policy);
1514 let state = std::mem::take(&mut inner.state);
1515 let slots_host = Rc::clone(&inner.slots);
1516 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1517 let captured_context = inner.captured_context.clone();
1518 let density = inner.density;
1519 (
1520 policy,
1521 state,
1522 slots_host,
1523 placement_scratch,
1524 captured_context,
1525 density,
1526 )
1527 };
1528 state.begin_pass();
1529
1530 let previous = composer.phase();
1531 if !matches!(previous, Phase::Measure | Phase::Layout) {
1532 composer.enter_phase(Phase::Measure);
1533 }
1534
1535 let constraints_copy = constraints;
1536 let fallback_context;
1537 let context = if let Some(context) = captured_context.as_ref() {
1538 context
1539 } else {
1540 fallback_context = composer.capture_composition_context();
1541 &fallback_context
1542 };
1543 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1544 &slots_host,
1545 Some(node_id),
1546 context,
1547 |inner_composer| {
1548 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1549 composer: inner_composer.clone(),
1550 density,
1551 state: &mut state,
1552 constraints: constraints_copy,
1553 measurer,
1554 cached_measure_batch_registrar,
1555 retained_measure_lookup,
1556 retained_measure_registrar,
1557 error,
1558 parent_handle: self.clone(),
1559 root_id: node_id,
1560 placement_scratch,
1561 });
1562 let result = (policy)(&mut scope, constraints_copy);
1563 (result, scope.into_placement_scratch())
1564 },
1565 )?;
1566
1567 let disposed = state.finish_pass();
1568 let disposal = composer.dispose_subcomposed_nodes(disposed);
1569
1570 if previous != composer.phase() {
1571 composer.enter_phase(previous);
1572 }
1573
1574 {
1575 let mut inner = self.inner.borrow_mut();
1576 inner.state = state;
1577 inner.placement_scratch = placement_scratch;
1578
1579 inner.replace_placed_children(
1580 result.placements.iter().map(|placement| placement.node_id),
1581 );
1582 }
1583
1584 disposal?;
1585 Ok(result)
1586 }
1587
1588 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1589 placements.clear();
1590 let mut inner = self.inner.borrow_mut();
1591 if placements.capacity() > inner.placement_scratch.capacity() {
1592 inner.placement_scratch = placements;
1593 }
1594 }
1595
1596 pub fn set_active_children<I>(&self, children: I)
1597 where
1598 I: IntoIterator<Item = NodeId>,
1599 {
1600 self.inner.borrow_mut().replace_placed_children(children);
1601 }
1602}
1603
1604struct SubcomposeLayoutNodeInner {
1605 modifier: Modifier,
1606 modifier_chain: ModifierChainHandle,
1607 modifier_capabilities: NodeCapabilities,
1608 state: SubcomposeState,
1609 measure_policy: Rc<MeasurePolicy>,
1610 children: Vec<NodeId>,
1611 slots: Rc<SlotsHost>,
1612 debug_modifiers: bool,
1613 node_id: Option<NodeId>,
1614 last_placements: Rc<Vec<NodeId>>,
1615 placement_scratch: Vec<Placement>,
1616 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1617 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1618 density: crate::density::Density,
1619}
1620
1621impl SubcomposeLayoutNodeInner {
1622 fn replace_placed_children<I>(&mut self, children: I)
1633 where
1634 I: IntoIterator<Item = NodeId>,
1635 {
1636 let mut changed = false;
1637 let mut count = 0usize;
1638 for child in children {
1639 match self.last_placements.get(count) {
1640 Some(&placed) if placed == child => {}
1641 Some(_) => {
1642 Rc::make_mut(&mut self.last_placements)[count] = child;
1643 changed = true;
1644 }
1645 None => {
1646 Rc::make_mut(&mut self.last_placements).push(child);
1647 changed = true;
1648 }
1649 }
1650 count += 1;
1651 }
1652 if self.last_placements.len() > count {
1653 Rc::make_mut(&mut self.last_placements).truncate(count);
1654 changed = true;
1655 }
1656 if changed && let Some(id) = self.node_id {
1657 crate::render_state::record_geometry_scene_node(id);
1658 }
1659 }
1660
1661 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1662 Self {
1663 modifier: Modifier::empty(),
1664 modifier_chain: ModifierChainHandle::detached(),
1665 modifier_capabilities: NodeCapabilities::default(),
1666 state: SubcomposeState::default(),
1667 measure_policy,
1668 children: Vec::new(),
1669 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1670 debug_modifiers: false,
1671 node_id: None,
1672 last_placements: Rc::new(Vec::new()),
1673 placement_scratch: Vec::new(),
1674 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1675 captured_context: None,
1676 density: crate::density::Density::default(),
1677 }
1678 }
1679
1680 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1681 self.measure_policy = policy;
1682 if let Err(err) = self.slots.reset() {
1683 log::error!(
1684 "failed to reset root measurement slots after measure policy update: {err}"
1685 );
1686 }
1687 }
1688
1689 fn set_modifier_collect(&mut self, modifier: Modifier) -> (ModifierInvalidations, bool) {
1690 let modifier_changed = !self.modifier.structural_eq(&modifier);
1691 self.modifier = modifier;
1692 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1693 let invalidations = self
1694 .modifier_chain
1695 .update_on_node(self.node_id, &self.modifier);
1696 self.modifier_capabilities = self.modifier_chain.capabilities();
1697
1698 (invalidations, modifier_changed)
1699 }
1700
1701 fn set_debug_modifiers(&mut self, enabled: bool) {
1702 self.debug_modifiers = enabled;
1703 self.modifier_chain.set_debug_logging(enabled);
1704 }
1705}
1706
1707#[cfg(test)]
1708#[path = "tests/subcompose_layout_tests.rs"]
1709mod tests;