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