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