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