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