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