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