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