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::{InvalidationKind, ModifierInvalidation, NodeCapabilities};
11pub use cranpose_ui_layout::{Constraints, MeasureResult, Placement};
12use smallvec::SmallVec;
13use web_time::Instant;
14
15use crate::{
16 layout::MeasuredNode,
17 modifier::{
18 collect_modifier_slices_into, Modifier, ModifierChainHandle, ModifierNodeSlices, Point,
19 ResolvedModifiers, Size,
20 },
21 widgets::nodes::{
22 allocate_virtual_node_id, is_virtual_node, register_layout_node, LayoutNode,
23 LayoutNodeCacheHandles, LayoutState,
24 },
25};
26
27fn subcompose_telemetry_enabled() -> bool {
28 cranpose_core::env_flag!("CRANPOSE_SUBCOMPOSE_TELEMETRY")
29}
30
31#[derive(Clone, Copy, Debug)]
36pub struct SubcomposeChild {
37 node_id: NodeId,
38 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 {
53 Self {
54 node_id,
55 measured_size: Some(size),
56 }
57 }
58
59 pub fn node_id(&self) -> NodeId {
60 self.node_id
61 }
62
63 pub fn size(&self) -> Size {
68 self.measured_size.unwrap_or(Size {
69 width: 0.0,
70 height: 0.0,
71 })
72 }
73
74 pub fn width(&self) -> f32 {
76 self.size().width
77 }
78
79 pub fn height(&self) -> f32 {
81 self.size().height
82 }
83
84 pub fn set_size(&mut self, size: Size) {
86 self.measured_size = Some(size);
87 }
88}
89
90impl PartialEq for SubcomposeChild {
91 fn eq(&self, other: &Self) -> bool {
92 self.node_id == other.node_id
93 }
94}
95
96pub type SubcomposePlaceable = cranpose_ui_layout::Placeable;
102
103type CachedMeasureBatchRegistrar<'a> =
104 Box<dyn FnMut(&[NodeId], Constraints, &mut Vec<Option<Size>>) + 'a>;
105type RetainedMeasureLookup<'a> = Box<dyn FnMut(NodeId) -> Option<Rc<MeasuredNode>> + 'a>;
106type RetainedMeasureRegistrar<'a> = Box<dyn FnMut(&[Rc<MeasuredNode>]) + 'a>;
107
108pub(crate) struct CachedBatchMeasureInputs<'a> {
109 pub(crate) measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
110 pub(crate) cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
111 pub(crate) retained_measure_lookup: RetainedMeasureLookup<'a>,
112 pub(crate) retained_measure_registrar: RetainedMeasureRegistrar<'a>,
113 pub(crate) error: &'a RefCell<Option<NodeError>>,
114}
115
116pub trait SubcomposeLayoutScope: cranpose_ui_layout::MeasureScope {
118 fn constraints(&self) -> Constraints;
119
120 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
121 where
122 I: IntoIterator<Item = Placement>,
123 {
124 MeasureResult::new(Size { width, height }, placements.into_iter().collect())
125 }
126}
127
128pub trait SubcomposeMeasureScope: SubcomposeLayoutScope {
130 fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
131 where
132 Content: FnMut() + 'static;
133
134 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable;
136
137 fn node_has_no_parent(&self, node_id: NodeId) -> bool;
140}
141
142pub struct SubcomposeMeasureScopeImpl<'a> {
144 composer: Composer,
145 density_scope: crate::density::DensityMeasureScope,
146 state: &'a mut SubcomposeState,
147 constraints: Constraints,
148 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
149 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
150 retained_measure_lookup: RetainedMeasureLookup<'a>,
151 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
152 error: &'a RefCell<Option<NodeError>>,
153 parent_handle: SubcomposeLayoutNodeHandle,
154 root_id: NodeId,
155 placement_scratch: Vec<Placement>,
156 cached_measure_node_scratch: Vec<NodeId>,
157 cached_measure_size_scratch: Vec<Option<Size>>,
158 cached_measure_missing_scratch: Vec<NodeId>,
159 registered_measurement_node_ids: Vec<NodeId>,
160 pending_commands_applied: bool,
161}
162
163struct SubcomposeMeasureScopeInit<'a> {
164 composer: Composer,
165 density: crate::density::Density,
166 state: &'a mut SubcomposeState,
167 constraints: Constraints,
168 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
169 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
170 retained_measure_lookup: RetainedMeasureLookup<'a>,
171 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
172 error: &'a RefCell<Option<NodeError>>,
173 parent_handle: SubcomposeLayoutNodeHandle,
174 root_id: NodeId,
175 placement_scratch: Vec<Placement>,
176}
177
178impl<'a> SubcomposeMeasureScopeImpl<'a> {
179 fn new(init: SubcomposeMeasureScopeInit<'a>) -> Self {
180 Self {
181 composer: init.composer,
182 density_scope: crate::density::DensityMeasureScope::new(init.density),
183 state: init.state,
184 constraints: init.constraints,
185 measurer: init.measurer,
186 cached_measure_batch_registrar: init.cached_measure_batch_registrar,
187 retained_measure_lookup: init.retained_measure_lookup,
188 retained_measure_registrar: init.retained_measure_registrar,
189 error: init.error,
190 parent_handle: init.parent_handle,
191 root_id: init.root_id,
192 placement_scratch: init.placement_scratch,
193 cached_measure_node_scratch: Vec::new(),
194 cached_measure_size_scratch: Vec::new(),
195 cached_measure_missing_scratch: Vec::new(),
196 registered_measurement_node_ids: Vec::new(),
197 pending_commands_applied: false,
198 }
199 }
200
201 fn register_measurement_node_id(&mut self, node_id: NodeId) {
202 if !self.registered_measurement_node_ids.contains(&node_id) {
203 self.registered_measurement_node_ids.push(node_id);
204 }
205 }
206
207 fn into_placement_scratch(self) -> Vec<Placement> {
208 self.placement_scratch
209 }
210
211 pub(crate) fn layout_with_placement_builder(
212 &mut self,
213 width: f32,
214 height: f32,
215 build: impl FnOnce(&mut Vec<Placement>),
216 ) -> MeasureResult {
217 self.placement_scratch.clear();
218 build(&mut self.placement_scratch);
219 MeasureResult::new(
220 Size { width, height },
221 std::mem::take(&mut self.placement_scratch),
222 )
223 }
224
225 fn record_error(&self, err: NodeError) {
226 let mut slot = self.error.borrow_mut();
227 if slot.is_none() {
228 eprintln!("[SubcomposeLayout] Error suppressed: {:?}", err);
229 *slot = Some(err);
230 }
231 }
232
233 fn ensure_pending_commands_applied(&mut self) -> bool {
234 if self.pending_commands_applied {
235 return true;
236 }
237
238 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
239 if let Err(err) = self.composer.apply_pending_commands() {
240 self.record_error(err);
241 return false;
242 }
243 if let Some(start) = telemetry_start {
244 log::warn!(
245 "[subcompose-telemetry] apply_pending_commands_ms={:.2}",
246 start.elapsed().as_secs_f64() * 1000.0
247 );
248 }
249
250 self.pending_commands_applied = true;
251 true
252 }
253
254 fn perform_subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<NodeId>
255 where
256 Content: FnMut() + 'static,
257 {
258 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
259 let mut inner = self.parent_handle.inner.borrow_mut();
260
261 let (virtual_node_id, is_reused) =
263 if let Some(node_id) = self.state.take_node_from_reusables(slot_id) {
264 (node_id, true)
265 } else {
266 let id = allocate_virtual_node_id();
267 let node = LayoutNode::new_virtual();
268 if let Err(e) = self
272 .composer
273 .register_virtual_node(id, Box::new(node.clone()))
274 {
275 eprintln!(
276 "[Subcompose] Failed to register virtual node {}: {:?}",
277 id, e
278 );
279 }
280 register_layout_node(id, &node);
281
282 inner.virtual_nodes.insert(id, Rc::new(node));
283 inner.children.push(id);
284 (id, false)
285 };
286
287 self.composer.record_subcompose_child(virtual_node_id);
290
291 if let Some(v_node) = inner.virtual_nodes.get(&virtual_node_id) {
294 v_node.set_parent(self.root_id);
295 }
296
297 drop(inner);
298
299 let content_holder = self.state.callback_holder(slot_id);
300 content_holder.update(content);
301
302 let _ = self
303 .composer
304 .with_node_mut::<LayoutNode, _>(virtual_node_id, |node| {
305 node.set_parent(self.root_id);
306 });
307
308 let slot_host = self.state.get_or_create_slots(slot_id);
309 self.parent_handle.note_slot_host(&slot_host);
310 let holder_for_slot = content_holder.clone();
311 let scopes = self
312 .composer
313 .subcompose_slot(&slot_host, Some(virtual_node_id), move |_| {
314 compose_subcompose_slot_content(holder_for_slot.clone());
315 })
316 .map(|(_, scopes)| scopes)
317 .unwrap_or_default();
318 self.pending_commands_applied = false;
319
320 self.state
321 .register_active(slot_id, &[virtual_node_id], &scopes);
322
323 let children = self.composer.get_node_children(virtual_node_id).to_vec();
327 if let Some(start) = telemetry_start {
328 log::warn!(
329 "[subcompose-telemetry] slot={} reused={} children={} subcompose_ms={:.2}",
330 slot_id.raw(),
331 is_reused,
332 children.len(),
333 start.elapsed().as_secs_f64() * 1000.0
334 );
335 }
336 children
337 }
338
339 pub(crate) fn activate_exact_retained_slot_with_known_children(
340 &mut self,
341 slot_id: SlotId,
342 known_children: &[u64],
343 ) -> Option<(Vec<SubcomposeChild>, bool)> {
344 let mut expected_children = Vec::with_capacity(known_children.len());
345 for &node_id in known_children {
346 expected_children.push(NodeId::try_from(node_id).ok()?);
347 }
348
349 let virtual_node_ids = match self.activate_current_active_slot_roots(slot_id) {
350 Some(virtual_node_ids) => {
351 for virtual_node_id in &virtual_node_ids {
352 self.composer.record_subcompose_child(*virtual_node_id);
353 }
354 virtual_node_ids
355 }
356 None => self.activate_recycled_exact_retained_slot_roots(slot_id)?,
357 };
358
359 if !self.ensure_pending_commands_applied() {
371 return None;
372 }
373
374 let mut activated_children = Vec::with_capacity(expected_children.len());
375 for virtual_node_id in virtual_node_ids {
376 activated_children.extend(
377 self.composer
378 .get_node_children(virtual_node_id)
379 .iter()
380 .copied(),
381 );
382 }
383 let children_match = activated_children == expected_children;
384 Some((
385 activated_children
386 .into_iter()
387 .map(SubcomposeChild::new)
388 .collect(),
389 children_match,
390 ))
391 }
392
393 fn activate_current_active_slot_roots(&mut self, slot_id: SlotId) -> Option<Vec<NodeId>> {
394 self.state.activate_current_active_slot(slot_id)
395 }
396
397 fn activate_recycled_exact_retained_slot_roots(
398 &mut self,
399 slot_id: SlotId,
400 ) -> Option<Vec<NodeId>> {
401 let activation = self.state.take_exact_slot_activation(slot_id)?;
402 let virtual_node_ids = activation.nodes;
403 let scopes = activation.scopes;
404 let reactivate_scopes = activation.reactivate_scopes;
405
406 if reactivate_scopes {
407 let inner = self.parent_handle.inner.borrow();
408 for virtual_node_id in &virtual_node_ids {
409 self.composer.record_subcompose_child(*virtual_node_id);
410 if let Some(v_node) = inner.virtual_nodes.get(virtual_node_id) {
411 v_node.set_parent(self.root_id);
412 }
413 }
414 for virtual_node_id in &virtual_node_ids {
415 let _ = self
416 .composer
417 .with_node_mut::<LayoutNode, _>(*virtual_node_id, |node| {
418 node.set_parent(self.root_id);
419 });
420 }
421 } else {
422 for virtual_node_id in &virtual_node_ids {
423 self.composer.record_subcompose_child(*virtual_node_id);
424 }
425 }
426
427 self.state.register_active_with_scope_reactivation(
428 slot_id,
429 &virtual_node_ids,
430 &scopes,
431 reactivate_scopes,
432 );
433 Some(virtual_node_ids)
434 }
435}
436
437impl<'a> SubcomposeLayoutScope for SubcomposeMeasureScopeImpl<'a> {
438 fn constraints(&self) -> Constraints {
439 self.constraints
440 }
441
442 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
443 where
444 I: IntoIterator<Item = Placement>,
445 {
446 self.layout_with_placement_builder(width, height, |scratch| {
447 scratch.extend(placements);
448 })
449 }
450}
451
452impl cranpose_ui_layout::MeasureScope for SubcomposeMeasureScopeImpl<'_> {
453 fn density(&self) -> f32 {
459 self.density_scope.density()
460 }
461
462 fn font_scale(&self) -> f32 {
463 self.density_scope.font_scale()
464 }
465}
466
467impl<'a> SubcomposeMeasureScope for SubcomposeMeasureScopeImpl<'a> {
468 fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
469 where
470 Content: FnMut() + 'static,
471 {
472 let nodes = self.perform_subcompose(slot_id, content);
473 nodes.into_iter().map(SubcomposeChild::new).collect()
474 }
475
476 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable {
477 if self.error.borrow().is_some() {
478 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
480 }
481
482 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
483 if !self.ensure_pending_commands_applied() {
484 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
485 }
486
487 let size = (self.measurer)(child.node_id, constraints);
488 self.register_measurement_node_id(child.node_id);
489 if let Some(start) = telemetry_start {
490 log::warn!(
491 "[subcompose-telemetry] child={} measure_ms={:.2} size=({:.2},{:.2})",
492 child.node_id,
493 start.elapsed().as_secs_f64() * 1000.0,
494 size.width,
495 size.height
496 );
497 }
498 SubcomposePlaceable::value(size.width, size.height, child.node_id)
499 }
500
501 fn node_has_no_parent(&self, node_id: NodeId) -> bool {
502 self.composer.node_has_no_parent(node_id)
503 }
504}
505
506impl<'a> SubcomposeMeasureScopeImpl<'a> {
507 pub fn active_slots_count(&self) -> usize {
511 self.state.active_slots_count()
512 }
513
514 pub fn reusable_slots_count(&self) -> usize {
518 self.state.reusable_slots_count()
519 }
520
521 pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
527 self.state.register_content_type(slot_id, content_type);
528 }
529
530 pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
536 self.state.update_content_type(slot_id, content_type);
537 }
538
539 pub(crate) fn set_reusable_pool_limits(&mut self, per_type: usize, untyped: usize) {
540 self.state.set_reusable_pool_limits(per_type, untyped);
541 }
542
543 pub(crate) fn recycle_active_slots_where(&mut self, predicate: impl FnMut(SlotId) -> bool) {
544 let disposed = self.state.recycle_active_slots_where(predicate);
545 debug_assert!(
546 disposed.is_empty(),
547 "lazy subcompose reusable pool limits must retain recycled active slots"
548 );
549 }
550
551 pub fn was_last_slot_reused(&self) -> Option<bool> {
559 self.state.was_last_slot_reused()
560 }
561
562 pub(crate) fn measure_retained(
563 &mut self,
564 child: SubcomposeChild,
565 constraints: Constraints,
566 ) -> (SubcomposePlaceable, Option<Rc<MeasuredNode>>) {
567 let placeable = self.measure(child, constraints);
568 let retained = (self.retained_measure_lookup)(child.node_id);
569 (placeable, retained)
570 }
571
572 pub(crate) fn register_retained_measurements(&mut self, measurements: &[Rc<MeasuredNode>]) {
573 if measurements.is_empty() {
574 return;
575 }
576
577 for measured in measurements {
578 self.register_measurement_node_id(measured.node_id());
579 }
580 (self.retained_measure_registrar)(measurements);
581 }
582
583 pub(crate) fn children_need_relayout(&mut self, children: &[SubcomposeChild]) -> bool {
596 if !self.ensure_pending_commands_applied() {
597 return true;
598 }
599
600 let mut root_ids = smallvec::SmallVec::<[NodeId; 8]>::new();
601 root_ids.extend(children.iter().map(SubcomposeChild::node_id));
602 self.composer.nodes_need_measure(&root_ids) || self.composer.nodes_need_layout(&root_ids)
603 }
604
605 pub(crate) fn ensure_cached_measurement_node_ids<I>(
606 &mut self,
607 node_ids: I,
608 constraints: Constraints,
609 ) -> usize
610 where
611 I: IntoIterator<Item = NodeId>,
612 {
613 if self.error.borrow().is_some() || !self.ensure_pending_commands_applied() {
614 return 0;
615 }
616
617 self.cached_measure_node_scratch.clear();
618 self.cached_measure_node_scratch.extend(
619 node_ids
620 .into_iter()
621 .filter(|node_id| !self.registered_measurement_node_ids.contains(node_id)),
622 );
623 if self.cached_measure_node_scratch.is_empty() {
624 return 0;
625 }
626
627 self.cached_measure_size_scratch.clear();
628 (self.cached_measure_batch_registrar)(
629 &self.cached_measure_node_scratch,
630 constraints,
631 &mut self.cached_measure_size_scratch,
632 );
633 self.cached_measure_size_scratch
634 .resize(self.cached_measure_node_scratch.len(), None);
635
636 let mut cached_count = 0;
637 self.cached_measure_missing_scratch.clear();
638 for index in 0..self.cached_measure_node_scratch.len() {
639 let node_id = self.cached_measure_node_scratch[index];
640 if self.cached_measure_size_scratch[index].is_some() {
641 cached_count += 1;
642 self.register_measurement_node_id(node_id);
643 } else {
644 self.cached_measure_missing_scratch.push(node_id);
645 }
646 }
647
648 let mut missing = std::mem::take(&mut self.cached_measure_missing_scratch);
649 for node_id in missing.drain(..) {
650 let _ = self.measure(SubcomposeChild::new(node_id), constraints);
651 }
652 self.cached_measure_missing_scratch = missing;
653
654 cached_count
655 }
656}
657
658fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
659 cranpose_core::with_current_composer(|composer| {
660 let holder_for_recompose = holder.clone();
661 composer.set_recompose_callback(move |_composer| {
662 compose_subcompose_slot_content(holder_for_recompose.clone());
663 });
664 });
665
666 let invoke = holder.clone_rc();
667 invoke();
668}
669
670pub type MeasurePolicy =
672 dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
673
674pub struct SubcomposeLayoutNode {
676 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
677 parent: Cell<Option<NodeId>>,
679 id: Cell<Option<NodeId>>,
681 needs_measure: Cell<bool>,
683 needs_layout: Cell<bool>,
684 needs_semantics: Cell<bool>,
685 needs_redraw: Cell<bool>,
686 needs_pointer_pass: Cell<bool>,
687 needs_focus_sync: Cell<bool>,
688 virtual_children_count: Cell<usize>,
689 layout_state: RefCell<LayoutState>,
691 cache_handles: LayoutNodeCacheHandles,
692 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
693 modifier_slices_dirty: Cell<bool>,
694}
695
696impl SubcomposeLayoutNode {
697 pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
698 let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
699 let node = Self {
700 inner,
701 parent: Cell::new(None),
702 id: Cell::new(None),
703 needs_measure: Cell::new(true),
704 needs_layout: Cell::new(true),
705 needs_semantics: Cell::new(true),
706 needs_redraw: Cell::new(true),
707 needs_pointer_pass: Cell::new(false),
708 needs_focus_sync: Cell::new(false),
709 virtual_children_count: Cell::new(0),
710 layout_state: RefCell::new(LayoutState::default()),
711 cache_handles: LayoutNodeCacheHandles::default(),
712 modifier_slices_snapshot: RefCell::new(Rc::default()),
713 modifier_slices_dirty: Cell::new(true),
714 };
715 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
718 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
719 node.update_modifier_slices_cache();
720 node.note_host_to_the_composition_that_made_it();
721 node
722 }
723
724 fn note_host_to_the_composition_that_made_it(&self) {
725 let host = Rc::clone(&self.inner.borrow().slots);
726 cranpose_core::note_nested_slots_host(&host);
727 }
728
729 pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
735 let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
736 inner_data
737 .state
738 .set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
739 let inner = Rc::new(RefCell::new(inner_data));
740 let node = Self {
741 inner,
742 parent: Cell::new(None),
743 id: Cell::new(None),
744 needs_measure: Cell::new(true),
745 needs_layout: Cell::new(true),
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 virtual_children_count: Cell::new(0),
751 layout_state: RefCell::new(LayoutState::default()),
752 cache_handles: LayoutNodeCacheHandles::default(),
753 modifier_slices_snapshot: RefCell::new(Rc::default()),
754 modifier_slices_dirty: Cell::new(true),
755 };
756 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
759 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
760 node.update_modifier_slices_cache();
761 node.note_host_to_the_composition_that_made_it();
762 node
763 }
764
765 pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
766 SubcomposeLayoutNodeHandle {
767 inner: Rc::clone(&self.inner),
768 }
769 }
770
771 #[doc(hidden)]
772 pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
773 self.inner.borrow().state.debug_scope_ids_by_slot()
774 }
775
776 #[doc(hidden)]
777 pub fn debug_slot_table_for_slot(
778 &self,
779 slot_id: cranpose_core::SlotId,
780 ) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
781 self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
782 }
783
784 #[doc(hidden)]
785 pub fn debug_slot_table_groups_for_slot(
786 &self,
787 slot_id: cranpose_core::SlotId,
788 ) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
789 self.inner
790 .borrow()
791 .state
792 .debug_slot_table_groups_for_slot(slot_id)
793 }
794
795 pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
796 let mut inner = self.inner.borrow_mut();
797 if Rc::ptr_eq(&inner.measure_policy, &policy) {
798 return;
799 }
800 inner.set_measure_policy(policy);
801 drop(inner);
802 self.invalidate_subcomposition();
803 }
804
805 pub fn set_captured_context(&mut self, context: cranpose_core::CapturedCompositionContext) {
807 self.inner.borrow_mut().captured_context = Some(context);
808 }
809
810 pub fn set_density(&mut self, density: crate::density::Density) {
816 let mut inner = self.inner.borrow_mut();
817 if inner.density != density {
818 inner.density = density;
819 drop(inner);
820 self.mark_needs_measure();
821 }
822 }
823
824 pub fn set_modifier(&mut self, modifier: Modifier) {
825 let prev_caps = self.modifier_capabilities();
827 let (invalidations, modifier_changed) = {
829 let mut inner = self.inner.borrow_mut();
830 inner.set_modifier_collect(modifier)
831 };
832 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
835 self.update_modifier_slices_cache();
836 if modifier_changed {
837 self.request_semantics_update();
838 }
839 }
840
841 fn update_modifier_slices_cache(&self) {
843 let inner = self.inner.borrow();
844 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
845 collect_modifier_slices_into(inner.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
846 self.modifier_slices_dirty.set(false);
847 }
848
849 pub(crate) fn mark_modifier_slices_dirty(&self) {
850 self.modifier_slices_dirty.set(true);
851 }
852
853 pub fn set_debug_modifiers(&mut self, enabled: bool) {
854 self.inner.borrow_mut().set_debug_modifiers(enabled);
855 }
856
857 pub fn modifier(&self) -> Modifier {
858 self.handle().modifier()
859 }
860
861 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
862 self.inner.borrow().resolved_modifiers
863 }
864
865 pub fn layout_state(&self) -> LayoutState {
867 self.layout_state.borrow().clone()
868 }
869
870 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
871 self.cache_handles.clone()
872 }
873
874 pub fn set_position(&self, position: Point) {
876 let mut state = self.layout_state.borrow_mut();
877 state.position = position;
878 state.is_placed = true;
879 }
880
881 pub fn set_measured_size(&self, size: Size) {
883 let mut state = self.layout_state.borrow_mut();
884 state.size = size;
885 }
886
887 pub fn clear_placed(&self) {
889 self.layout_state.borrow_mut().is_placed = false;
890 }
891
892 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
894 if self.modifier_slices_dirty.get() {
895 self.update_modifier_slices_cache();
896 }
897 self.modifier_slices_snapshot.borrow().clone()
898 }
899
900 pub fn state(&self) -> Ref<'_, SubcomposeState> {
901 Ref::map(self.inner.borrow(), |inner| &inner.state)
902 }
903
904 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
905 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
906 }
907
908 pub fn invalidate_subcomposition(&self) {
909 self.inner.borrow().state.invalidate_scopes();
910 self.mark_needs_measure();
911 if let Some(id) = self.id.get() {
912 cranpose_core::bubble_measure_dirty_in_composer(id);
913 }
914 }
915
916 pub fn request_measure_recompose(&self) {
917 self.mark_needs_measure();
918 if let Some(id) = self.id.get() {
919 cranpose_core::bubble_measure_dirty_in_composer(id);
920 }
921 }
922
923 pub fn active_children(&self) -> Vec<NodeId> {
924 current_subcompose_children(&self.inner.borrow())
925 }
926
927 pub fn mark_needs_measure(&self) {
929 self.needs_measure.set(true);
930 self.needs_layout.set(true);
931 }
932
933 pub fn mark_needs_layout_flag(&self) {
935 self.needs_layout.set(true);
936 }
937
938 pub fn mark_needs_redraw(&self) {
940 self.needs_redraw.set(true);
941 if let Some(id) = self.id.get() {
942 crate::schedule_draw_repass(id);
943 }
944 crate::request_render_invalidation();
945 }
946
947 pub fn needs_measure(&self) -> bool {
949 self.needs_measure.get()
950 }
951
952 pub(crate) fn clear_needs_measure(&self) {
953 self.needs_measure.set(false);
954 }
955
956 pub(crate) fn clear_needs_layout(&self) {
957 self.needs_layout.set(false);
958 }
959
960 pub fn mark_needs_semantics(&self) {
962 self.needs_semantics.set(true);
963 }
964
965 pub(crate) fn clear_needs_semantics(&self) {
966 self.needs_semantics.set(false);
967 }
968
969 #[cfg(test)]
970 pub(crate) fn clear_needs_semantics_for_tests(&self) {
971 self.clear_needs_semantics();
972 }
973
974 pub fn needs_redraw(&self) -> bool {
976 self.needs_redraw.get()
977 }
978
979 pub fn clear_needs_redraw(&self) {
980 self.needs_redraw.set(false);
981 }
982
983 pub fn mark_needs_pointer_pass(&self) {
985 self.needs_pointer_pass.set(true);
986 }
987
988 pub fn needs_pointer_pass(&self) -> bool {
990 self.needs_pointer_pass.get()
991 }
992
993 pub fn clear_needs_pointer_pass(&self) {
995 self.needs_pointer_pass.set(false);
996 }
997
998 pub fn mark_needs_focus_sync(&self) {
1000 self.needs_focus_sync.set(true);
1001 }
1002
1003 pub fn needs_focus_sync(&self) -> bool {
1005 self.needs_focus_sync.get()
1006 }
1007
1008 pub fn clear_needs_focus_sync(&self) {
1010 self.needs_focus_sync.set(false);
1011 }
1012
1013 fn request_semantics_update(&self) {
1014 let already_dirty = self.needs_semantics.replace(true);
1015 if already_dirty {
1016 return;
1017 }
1018
1019 if let Some(id) = self.id.get() {
1020 cranpose_core::queue_semantics_invalidation(id);
1021 }
1022 }
1023
1024 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1026 self.inner.borrow().modifier_capabilities
1027 }
1028
1029 pub fn has_layout_modifier_nodes(&self) -> bool {
1030 self.modifier_capabilities()
1031 .contains(NodeCapabilities::LAYOUT)
1032 }
1033
1034 pub fn has_draw_modifier_nodes(&self) -> bool {
1035 self.modifier_capabilities()
1036 .contains(NodeCapabilities::DRAW)
1037 }
1038
1039 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1040 self.modifier_capabilities()
1041 .contains(NodeCapabilities::POINTER_INPUT)
1042 }
1043
1044 pub fn has_semantics_modifier_nodes(&self) -> bool {
1045 self.modifier_capabilities()
1046 .contains(NodeCapabilities::SEMANTICS)
1047 }
1048
1049 pub fn has_focus_modifier_nodes(&self) -> bool {
1050 self.modifier_capabilities()
1051 .contains(NodeCapabilities::FOCUS)
1052 }
1053
1054 fn dispatch_modifier_invalidations(
1061 &self,
1062 invalidations: &[ModifierInvalidation],
1063 prev_caps: NodeCapabilities,
1064 ) {
1065 let curr_caps = self.modifier_capabilities();
1066 for invalidation in invalidations {
1067 self.modifier_slices_dirty.set(true);
1068 let invalidation_caps = invalidation.capabilities();
1069 let has_capability = |capability| {
1070 curr_caps.contains(capability)
1071 || prev_caps.contains(capability)
1072 || invalidation_caps.contains(capability)
1073 };
1074 match invalidation.kind() {
1075 InvalidationKind::Layout => {
1076 if has_capability(NodeCapabilities::LAYOUT) {
1077 self.mark_needs_measure();
1078 }
1079 }
1080 InvalidationKind::Draw => {
1081 if has_capability(NodeCapabilities::DRAW) {
1082 self.mark_needs_redraw();
1083 }
1084 }
1085 InvalidationKind::PointerInput => {
1086 if has_capability(NodeCapabilities::POINTER_INPUT) {
1087 self.mark_needs_pointer_pass();
1088 crate::request_pointer_invalidation();
1089 if let Some(id) = self.id.get() {
1091 crate::schedule_pointer_repass(id);
1092 }
1093 }
1094 }
1095 InvalidationKind::Semantics => {
1096 self.request_semantics_update();
1097 }
1098 InvalidationKind::Focus => {
1099 if has_capability(NodeCapabilities::FOCUS) {
1100 self.mark_needs_focus_sync();
1101 crate::request_focus_invalidation();
1102 if let Some(id) = self.id.get() {
1104 crate::schedule_focus_invalidation(id);
1105 }
1106 }
1107 }
1108 }
1109 }
1110 }
1111}
1112
1113impl cranpose_core::Node for SubcomposeLayoutNode {
1114 fn mount(&mut self) {
1115 let mut inner = self.inner.borrow_mut();
1116 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1117 chain.repair_chain();
1118 chain.attach_nodes(&mut *context);
1119 }
1120
1121 fn unmount(&mut self) {
1122 self.inner
1123 .borrow_mut()
1124 .modifier_chain
1125 .chain_mut()
1126 .detach_nodes();
1127 }
1128
1129 fn insert_child(&mut self, child: NodeId) {
1130 let mut inner = self.inner.borrow_mut();
1131 if inner.children.contains(&child) {
1132 return;
1133 }
1134 if is_virtual_node(child) {
1135 let count = self.virtual_children_count.get();
1136 self.virtual_children_count.set(count + 1);
1137 }
1138 inner.children.push(child);
1139 }
1140
1141 fn remove_child(&mut self, child: NodeId) {
1142 let mut inner = self.inner.borrow_mut();
1143 let before = inner.children.len();
1144 inner.children.retain(|&id| id != child);
1145 if inner.children.len() < before && is_virtual_node(child) {
1146 let count = self.virtual_children_count.get();
1147 if count > 0 {
1148 self.virtual_children_count.set(count - 1);
1149 }
1150 }
1151 }
1152
1153 fn move_child(&mut self, from: usize, to: usize) {
1154 let mut inner = self.inner.borrow_mut();
1155 if from == to || from >= inner.children.len() {
1156 return;
1157 }
1158 let child = inner.children.remove(from);
1159 let target = to.min(inner.children.len());
1160 inner.children.insert(target, child);
1161 }
1162
1163 fn update_children(&mut self, children: &[NodeId]) {
1164 let mut inner = self.inner.borrow_mut();
1165 inner.children.clear();
1166 inner.children.extend_from_slice(children);
1167 }
1168
1169 fn children(&self) -> Vec<NodeId> {
1170 current_subcompose_children(&self.inner.borrow())
1171 }
1172
1173 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1174 out.clear();
1175 out.extend(self.inner.borrow().children.iter().copied());
1176 }
1177
1178 fn set_node_id(&mut self, id: NodeId) {
1179 self.id.set(Some(id));
1180 self.inner.borrow_mut().modifier_chain.set_node_id(Some(id));
1181 self.update_modifier_slices_cache();
1182 }
1183
1184 fn on_attached_to_parent(&mut self, parent: NodeId) {
1185 self.parent.set(Some(parent));
1186 }
1187
1188 fn on_removed_from_parent(&mut self) {
1189 self.parent.set(None);
1190 }
1191
1192 fn parent(&self) -> Option<NodeId> {
1193 self.parent.get()
1194 }
1195
1196 fn mark_needs_layout(&self) {
1197 self.needs_layout.set(true);
1198 }
1199
1200 fn needs_layout(&self) -> bool {
1201 self.needs_layout.get()
1202 }
1203
1204 fn mark_needs_measure(&self) {
1205 self.needs_measure.set(true);
1206 self.needs_layout.set(true); }
1208
1209 fn needs_measure(&self) -> bool {
1210 self.needs_measure.get()
1211 }
1212
1213 fn mark_needs_semantics(&self) {
1214 self.needs_semantics.set(true);
1215 }
1216
1217 fn needs_semantics(&self) -> bool {
1218 self.needs_semantics.get()
1219 }
1220
1221 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1223 self.parent.set(Some(parent));
1224 }
1225}
1226
1227#[derive(Clone)]
1228pub struct SubcomposeLayoutNodeHandle {
1229 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1230}
1231
1232impl SubcomposeLayoutNodeHandle {
1233 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1234 let Ok(inner) = self.inner.try_borrow() else {
1235 return;
1236 };
1237 if Rc::ptr_eq(&inner.slots, slot_host) {
1238 return;
1239 }
1240 inner.slots.note_nested_host(slot_host);
1241 }
1242
1243 pub(crate) fn measured_children_scratch(
1244 &self,
1245 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1246 let scratch = {
1247 let inner = self.inner.borrow();
1248 Rc::clone(&inner.measured_children_scratch)
1249 };
1250 scratch.borrow_mut().clear();
1251 scratch
1252 }
1253
1254 pub fn modifier(&self) -> Modifier {
1255 self.inner.borrow().modifier.clone()
1256 }
1257
1258 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1259 self.resolved_modifiers().layout_properties()
1260 }
1261
1262 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1263 self.inner.borrow().resolved_modifiers
1264 }
1265
1266 pub fn total_offset(&self) -> Point {
1267 self.resolved_modifiers().offset()
1268 }
1269
1270 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1271 self.inner.borrow().modifier_capabilities
1272 }
1273
1274 pub fn has_layout_modifier_nodes(&self) -> bool {
1275 self.modifier_capabilities()
1276 .contains(NodeCapabilities::LAYOUT)
1277 }
1278
1279 pub fn has_draw_modifier_nodes(&self) -> bool {
1280 self.modifier_capabilities()
1281 .contains(NodeCapabilities::DRAW)
1282 }
1283
1284 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1285 self.modifier_capabilities()
1286 .contains(NodeCapabilities::POINTER_INPUT)
1287 }
1288
1289 pub fn has_semantics_modifier_nodes(&self) -> bool {
1290 self.modifier_capabilities()
1291 .contains(NodeCapabilities::SEMANTICS)
1292 }
1293
1294 pub fn has_focus_modifier_nodes(&self) -> bool {
1295 self.modifier_capabilities()
1296 .contains(NodeCapabilities::FOCUS)
1297 }
1298
1299 pub fn set_debug_modifiers(&self, enabled: bool) {
1300 self.inner.borrow_mut().set_debug_modifiers(enabled);
1301 }
1302
1303 pub fn measure<'a>(
1304 &self,
1305 composer: &Composer,
1306 node_id: NodeId,
1307 constraints: Constraints,
1308 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
1309 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1310 error: &'a RefCell<Option<NodeError>>,
1311 ) -> Result<MeasureResult, NodeError> {
1312 self.measure_with_cached_batch(
1313 composer,
1314 node_id,
1315 constraints,
1316 CachedBatchMeasureInputs {
1317 measurer,
1318 cached_measure_batch_registrar: Box::new(
1319 move |node_ids, child_constraints, out| {
1320 out.clear();
1321 out.reserve(node_ids.len());
1322 for &child_id in node_ids {
1323 out.push(cached_measure_registrar(child_id, child_constraints));
1324 }
1325 },
1326 ),
1327 retained_measure_lookup: Box::new(|_| None),
1328 retained_measure_registrar: Box::new(|_| {}),
1329 error,
1330 },
1331 )
1332 }
1333
1334 pub(crate) fn measure_with_cached_batch<'a>(
1335 &self,
1336 composer: &Composer,
1337 node_id: NodeId,
1338 constraints: Constraints,
1339 callbacks: CachedBatchMeasureInputs<'a>,
1340 ) -> Result<MeasureResult, NodeError> {
1341 let CachedBatchMeasureInputs {
1342 measurer,
1343 cached_measure_batch_registrar,
1344 retained_measure_lookup,
1345 retained_measure_registrar,
1346 error,
1347 } = callbacks;
1348 let (policy, mut state, slots_host, placement_scratch, captured_context, density) = {
1349 let mut inner = self.inner.borrow_mut();
1350 let policy = Rc::clone(&inner.measure_policy);
1351 let state = std::mem::take(&mut inner.state);
1352 let slots_host = Rc::clone(&inner.slots);
1353 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1354 let captured_context = inner.captured_context.clone();
1355 let density = inner.density;
1356 (
1357 policy,
1358 state,
1359 slots_host,
1360 placement_scratch,
1361 captured_context,
1362 density,
1363 )
1364 };
1365 state.begin_pass();
1366
1367 let previous = composer.phase();
1368 if !matches!(previous, Phase::Measure | Phase::Layout) {
1369 composer.enter_phase(Phase::Measure);
1370 }
1371
1372 let constraints_copy = constraints;
1373 let fallback_context;
1382 let context = if let Some(context) = captured_context.as_ref() {
1383 context
1384 } else {
1385 fallback_context = composer.capture_composition_context();
1386 &fallback_context
1387 };
1388 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1389 &slots_host,
1390 Some(node_id),
1391 context,
1392 |inner_composer| {
1393 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1394 composer: inner_composer.clone(),
1395 density,
1396 state: &mut state,
1397 constraints: constraints_copy,
1398 measurer,
1399 cached_measure_batch_registrar,
1400 retained_measure_lookup,
1401 retained_measure_registrar,
1402 error,
1403 parent_handle: self.clone(),
1404 root_id: node_id,
1405 placement_scratch,
1406 });
1407 let result = (policy)(&mut scope, constraints_copy);
1408 (result, scope.into_placement_scratch())
1409 },
1410 )?;
1411
1412 state.finish_pass();
1413
1414 if previous != composer.phase() {
1415 composer.enter_phase(previous);
1416 }
1417
1418 {
1419 let mut inner = self.inner.borrow_mut();
1420 inner.state = state;
1421 inner.placement_scratch = placement_scratch;
1422
1423 inner.last_placements = result.placements.iter().map(|p| p.node_id).collect();
1428 }
1429
1430 Ok(result)
1431 }
1432
1433 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1434 placements.clear();
1435 let mut inner = self.inner.borrow_mut();
1436 if placements.capacity() > inner.placement_scratch.capacity() {
1437 inner.placement_scratch = placements;
1438 }
1439 }
1440
1441 pub fn set_active_children<I>(&self, children: I)
1442 where
1443 I: IntoIterator<Item = NodeId>,
1444 {
1445 let mut inner = self.inner.borrow_mut();
1446 inner.last_placements.clear();
1447 inner.last_placements.extend(children);
1448 }
1449}
1450
1451fn current_subcompose_children(inner: &SubcomposeLayoutNodeInner) -> Vec<NodeId> {
1452 inner.last_placements.clone()
1453}
1454
1455struct SubcomposeLayoutNodeInner {
1456 modifier: Modifier,
1457 modifier_chain: ModifierChainHandle,
1458 resolved_modifiers: ResolvedModifiers,
1459 modifier_capabilities: NodeCapabilities,
1460 state: SubcomposeState,
1461 measure_policy: Rc<MeasurePolicy>,
1462 children: Vec<NodeId>,
1463 slots: Rc<SlotsHost>,
1464 debug_modifiers: bool,
1465 virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
1467 last_placements: Vec<NodeId>,
1470 placement_scratch: Vec<Placement>,
1471 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1472 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1474 density: crate::density::Density,
1478}
1479
1480impl SubcomposeLayoutNodeInner {
1481 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1482 Self {
1483 modifier: Modifier::empty(),
1484 modifier_chain: ModifierChainHandle::new(),
1485 resolved_modifiers: ResolvedModifiers::default(),
1486 modifier_capabilities: NodeCapabilities::default(),
1487 state: SubcomposeState::default(),
1488 measure_policy,
1489 children: Vec::new(),
1490 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1491 debug_modifiers: false,
1492 virtual_nodes: HashMap::new(),
1493 last_placements: Vec::new(),
1494 placement_scratch: Vec::new(),
1495 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1496 captured_context: None,
1497 density: crate::density::Density::default(),
1498 }
1499 }
1500
1501 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1502 self.measure_policy = policy;
1503 if let Err(err) = self.slots.reset() {
1508 log::error!(
1509 "failed to reset root measurement slots after measure policy update: {err}"
1510 );
1511 }
1512 }
1513
1514 fn set_modifier_collect(&mut self, modifier: Modifier) -> (Vec<ModifierInvalidation>, bool) {
1517 let modifier_changed = !self.modifier.structural_eq(&modifier);
1518 self.modifier = modifier;
1519 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1520 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1521 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1522 self.modifier_capabilities = self.modifier_chain.capabilities();
1523
1524 let mut invalidations = self.modifier_chain.take_invalidations();
1526 invalidations.extend(modifier_local_invalidations);
1527
1528 (invalidations, modifier_changed)
1529 }
1530
1531 fn set_debug_modifiers(&mut self, enabled: bool) {
1532 self.debug_modifiers = enabled;
1533 self.modifier_chain.set_debug_logging(enabled);
1534 }
1535}
1536
1537#[cfg(test)]
1538#[path = "tests/subcompose_layout_tests.rs"]
1539mod tests;