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 state: &'a mut SubcomposeState,
144 constraints: Constraints,
145 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
146 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
147 retained_measure_lookup: RetainedMeasureLookup<'a>,
148 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
149 error: &'a RefCell<Option<NodeError>>,
150 parent_handle: SubcomposeLayoutNodeHandle,
151 root_id: NodeId,
152 placement_scratch: Vec<Placement>,
153 cached_measure_node_scratch: Vec<NodeId>,
154 cached_measure_size_scratch: Vec<Option<Size>>,
155 cached_measure_missing_scratch: Vec<NodeId>,
156 registered_measurement_node_ids: Vec<NodeId>,
157 pending_commands_applied: bool,
158}
159
160struct SubcomposeMeasureScopeInit<'a> {
161 composer: Composer,
162 state: &'a mut SubcomposeState,
163 constraints: Constraints,
164 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
165 cached_measure_batch_registrar: CachedMeasureBatchRegistrar<'a>,
166 retained_measure_lookup: RetainedMeasureLookup<'a>,
167 retained_measure_registrar: RetainedMeasureRegistrar<'a>,
168 error: &'a RefCell<Option<NodeError>>,
169 parent_handle: SubcomposeLayoutNodeHandle,
170 root_id: NodeId,
171 placement_scratch: Vec<Placement>,
172}
173
174impl<'a> SubcomposeMeasureScopeImpl<'a> {
175 fn new(init: SubcomposeMeasureScopeInit<'a>) -> Self {
176 Self {
177 composer: init.composer,
178 state: init.state,
179 constraints: init.constraints,
180 measurer: init.measurer,
181 cached_measure_batch_registrar: init.cached_measure_batch_registrar,
182 retained_measure_lookup: init.retained_measure_lookup,
183 retained_measure_registrar: init.retained_measure_registrar,
184 error: init.error,
185 parent_handle: init.parent_handle,
186 root_id: init.root_id,
187 placement_scratch: init.placement_scratch,
188 cached_measure_node_scratch: Vec::new(),
189 cached_measure_size_scratch: Vec::new(),
190 cached_measure_missing_scratch: Vec::new(),
191 registered_measurement_node_ids: Vec::new(),
192 pending_commands_applied: false,
193 }
194 }
195
196 fn register_measurement_node_id(&mut self, node_id: NodeId) {
197 if !self.registered_measurement_node_ids.contains(&node_id) {
198 self.registered_measurement_node_ids.push(node_id);
199 }
200 }
201
202 fn into_placement_scratch(self) -> Vec<Placement> {
203 self.placement_scratch
204 }
205
206 pub(crate) fn layout_with_placement_builder(
207 &mut self,
208 width: f32,
209 height: f32,
210 build: impl FnOnce(&mut Vec<Placement>),
211 ) -> MeasureResult {
212 self.placement_scratch.clear();
213 build(&mut self.placement_scratch);
214 MeasureResult::new(
215 Size { width, height },
216 std::mem::take(&mut self.placement_scratch),
217 )
218 }
219
220 fn record_error(&self, err: NodeError) {
221 let mut slot = self.error.borrow_mut();
222 if slot.is_none() {
223 eprintln!("[SubcomposeLayout] Error suppressed: {:?}", err);
224 *slot = Some(err);
225 }
226 }
227
228 fn ensure_pending_commands_applied(&mut self) -> bool {
229 if self.pending_commands_applied {
230 return true;
231 }
232
233 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
234 if let Err(err) = self.composer.apply_pending_commands() {
235 self.record_error(err);
236 return false;
237 }
238 if let Some(start) = telemetry_start {
239 log::warn!(
240 "[subcompose-telemetry] apply_pending_commands_ms={:.2}",
241 start.elapsed().as_secs_f64() * 1000.0
242 );
243 }
244
245 self.pending_commands_applied = true;
246 true
247 }
248
249 fn perform_subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<NodeId>
250 where
251 Content: FnMut() + 'static,
252 {
253 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
254 let mut inner = self.parent_handle.inner.borrow_mut();
255
256 let (virtual_node_id, is_reused) =
258 if let Some(node_id) = self.state.take_node_from_reusables(slot_id) {
259 (node_id, true)
260 } else {
261 let id = allocate_virtual_node_id();
262 let node = LayoutNode::new_virtual();
263 if let Err(e) = self
267 .composer
268 .register_virtual_node(id, Box::new(node.clone()))
269 {
270 eprintln!(
271 "[Subcompose] Failed to register virtual node {}: {:?}",
272 id, e
273 );
274 }
275 register_layout_node(id, &node);
276
277 inner.virtual_nodes.insert(id, Rc::new(node));
278 inner.children.push(id);
279 (id, false)
280 };
281
282 self.composer.record_subcompose_child(virtual_node_id);
285
286 if let Some(v_node) = inner.virtual_nodes.get(&virtual_node_id) {
289 v_node.set_parent(self.root_id);
290 }
291
292 drop(inner);
293
294 let content_holder = self.state.callback_holder(slot_id);
295 content_holder.update(content);
296
297 let _ = self
298 .composer
299 .with_node_mut::<LayoutNode, _>(virtual_node_id, |node| {
300 node.set_parent(self.root_id);
301 });
302
303 let slot_host = self.state.get_or_create_slots(slot_id);
304 self.parent_handle.note_slot_host(&slot_host);
305 let holder_for_slot = content_holder.clone();
306 let scopes = self
307 .composer
308 .subcompose_slot(&slot_host, Some(virtual_node_id), move |_| {
309 compose_subcompose_slot_content(holder_for_slot.clone());
310 })
311 .map(|(_, scopes)| scopes)
312 .unwrap_or_default();
313 self.pending_commands_applied = false;
314
315 self.state
316 .register_active(slot_id, &[virtual_node_id], &scopes);
317
318 let children = self.composer.get_node_children(virtual_node_id).to_vec();
322 if let Some(start) = telemetry_start {
323 log::warn!(
324 "[subcompose-telemetry] slot={} reused={} children={} subcompose_ms={:.2}",
325 slot_id.raw(),
326 is_reused,
327 children.len(),
328 start.elapsed().as_secs_f64() * 1000.0
329 );
330 }
331 children
332 }
333
334 pub(crate) fn activate_exact_retained_slot_with_known_children(
335 &mut self,
336 slot_id: SlotId,
337 known_children: &[u64],
338 ) -> Option<(Vec<SubcomposeChild>, bool)> {
339 let mut expected_children = Vec::with_capacity(known_children.len());
340 for &node_id in known_children {
341 expected_children.push(NodeId::try_from(node_id).ok()?);
342 }
343
344 let virtual_node_ids = match self.activate_current_active_slot_roots(slot_id) {
345 Some(virtual_node_ids) => {
346 for virtual_node_id in &virtual_node_ids {
347 self.composer.record_subcompose_child(*virtual_node_id);
348 }
349 virtual_node_ids
350 }
351 None => self.activate_recycled_exact_retained_slot_roots(slot_id)?,
352 };
353
354 if !self.ensure_pending_commands_applied() {
366 return None;
367 }
368
369 let mut activated_children = Vec::with_capacity(expected_children.len());
370 for virtual_node_id in virtual_node_ids {
371 activated_children.extend(
372 self.composer
373 .get_node_children(virtual_node_id)
374 .iter()
375 .copied(),
376 );
377 }
378 let children_match = activated_children == expected_children;
379 Some((
380 activated_children
381 .into_iter()
382 .map(SubcomposeChild::new)
383 .collect(),
384 children_match,
385 ))
386 }
387
388 fn activate_current_active_slot_roots(&mut self, slot_id: SlotId) -> Option<Vec<NodeId>> {
389 self.state.activate_current_active_slot(slot_id)
390 }
391
392 fn activate_recycled_exact_retained_slot_roots(
393 &mut self,
394 slot_id: SlotId,
395 ) -> Option<Vec<NodeId>> {
396 let activation = self.state.take_exact_slot_activation(slot_id)?;
397 let virtual_node_ids = activation.nodes;
398 let scopes = activation.scopes;
399 let reactivate_scopes = activation.reactivate_scopes;
400
401 if reactivate_scopes {
402 let inner = self.parent_handle.inner.borrow();
403 for virtual_node_id in &virtual_node_ids {
404 self.composer.record_subcompose_child(*virtual_node_id);
405 if let Some(v_node) = inner.virtual_nodes.get(virtual_node_id) {
406 v_node.set_parent(self.root_id);
407 }
408 }
409 for virtual_node_id in &virtual_node_ids {
410 let _ = self
411 .composer
412 .with_node_mut::<LayoutNode, _>(*virtual_node_id, |node| {
413 node.set_parent(self.root_id);
414 });
415 }
416 } else {
417 for virtual_node_id in &virtual_node_ids {
418 self.composer.record_subcompose_child(*virtual_node_id);
419 }
420 }
421
422 self.state.register_active_with_scope_reactivation(
423 slot_id,
424 &virtual_node_ids,
425 &scopes,
426 reactivate_scopes,
427 );
428 Some(virtual_node_ids)
429 }
430}
431
432impl<'a> SubcomposeLayoutScope for SubcomposeMeasureScopeImpl<'a> {
433 fn constraints(&self) -> Constraints {
434 self.constraints
435 }
436
437 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
438 where
439 I: IntoIterator<Item = Placement>,
440 {
441 self.layout_with_placement_builder(width, height, |scratch| {
442 scratch.extend(placements);
443 })
444 }
445}
446
447impl cranpose_ui_layout::MeasureScope for SubcomposeMeasureScopeImpl<'_> {
448 fn density(&self) -> f32 {
449 crate::current_density()
450 }
451
452 fn font_scale(&self) -> f32 {
453 crate::current_font_scale()
454 }
455}
456
457impl<'a> SubcomposeMeasureScope for SubcomposeMeasureScopeImpl<'a> {
458 fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
459 where
460 Content: FnMut() + 'static,
461 {
462 let nodes = self.perform_subcompose(slot_id, content);
463 nodes.into_iter().map(SubcomposeChild::new).collect()
464 }
465
466 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable {
467 if self.error.borrow().is_some() {
468 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
470 }
471
472 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
473 if !self.ensure_pending_commands_applied() {
474 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
475 }
476
477 let size = (self.measurer)(child.node_id, constraints);
478 self.register_measurement_node_id(child.node_id);
479 if let Some(start) = telemetry_start {
480 log::warn!(
481 "[subcompose-telemetry] child={} measure_ms={:.2} size=({:.2},{:.2})",
482 child.node_id,
483 start.elapsed().as_secs_f64() * 1000.0,
484 size.width,
485 size.height
486 );
487 }
488 SubcomposePlaceable::value(size.width, size.height, child.node_id)
489 }
490
491 fn node_has_no_parent(&self, node_id: NodeId) -> bool {
492 self.composer.node_has_no_parent(node_id)
493 }
494}
495
496impl<'a> SubcomposeMeasureScopeImpl<'a> {
497 pub fn subcompose_with_size<Content, F>(
502 &mut self,
503 slot_id: SlotId,
504 content: Content,
505 estimate_size: F,
506 ) -> Vec<SubcomposeChild>
507 where
508 Content: FnMut() + 'static,
509 F: Fn(usize) -> Size,
510 {
511 let nodes = self.perform_subcompose(slot_id, content);
512 nodes
513 .into_iter()
514 .enumerate()
515 .map(|(i, node_id)| SubcomposeChild::with_size(node_id, estimate_size(i)))
516 .collect()
517 }
518
519 pub fn active_slots_count(&self) -> usize {
523 self.state.active_slots_count()
524 }
525
526 pub fn reusable_slots_count(&self) -> usize {
530 self.state.reusable_slots_count()
531 }
532
533 pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
539 self.state.register_content_type(slot_id, content_type);
540 }
541
542 pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
548 self.state.update_content_type(slot_id, content_type);
549 }
550
551 pub(crate) fn set_reusable_pool_limits(&mut self, per_type: usize, untyped: usize) {
552 self.state.set_reusable_pool_limits(per_type, untyped);
553 }
554
555 pub(crate) fn recycle_active_slots_where(&mut self, predicate: impl FnMut(SlotId) -> bool) {
556 let disposed = self.state.recycle_active_slots_where(predicate);
557 debug_assert!(
558 disposed.is_empty(),
559 "lazy subcompose reusable pool limits must retain recycled active slots"
560 );
561 }
562
563 pub fn was_last_slot_reused(&self) -> Option<bool> {
571 self.state.was_last_slot_reused()
572 }
573
574 pub(crate) fn measure_retained(
575 &mut self,
576 child: SubcomposeChild,
577 constraints: Constraints,
578 ) -> (SubcomposePlaceable, Option<Rc<MeasuredNode>>) {
579 let placeable = self.measure(child, constraints);
580 let retained = (self.retained_measure_lookup)(child.node_id);
581 (placeable, retained)
582 }
583
584 pub(crate) fn register_retained_measurements(&mut self, measurements: &[Rc<MeasuredNode>]) {
585 if measurements.is_empty() {
586 return;
587 }
588
589 for measured in measurements {
590 self.register_measurement_node_id(measured.node_id());
591 }
592 (self.retained_measure_registrar)(measurements);
593 }
594
595 pub(crate) fn children_need_relayout(&mut self, children: &[SubcomposeChild]) -> bool {
608 if !self.ensure_pending_commands_applied() {
609 return true;
610 }
611
612 let mut root_ids = smallvec::SmallVec::<[NodeId; 8]>::new();
613 root_ids.extend(children.iter().map(SubcomposeChild::node_id));
614 self.composer.nodes_need_measure(&root_ids) || self.composer.nodes_need_layout(&root_ids)
615 }
616
617 pub(crate) fn ensure_cached_measurement_node_ids<I>(
618 &mut self,
619 node_ids: I,
620 constraints: Constraints,
621 ) -> usize
622 where
623 I: IntoIterator<Item = NodeId>,
624 {
625 if self.error.borrow().is_some() || !self.ensure_pending_commands_applied() {
626 return 0;
627 }
628
629 self.cached_measure_node_scratch.clear();
630 self.cached_measure_node_scratch.extend(
631 node_ids
632 .into_iter()
633 .filter(|node_id| !self.registered_measurement_node_ids.contains(node_id)),
634 );
635 if self.cached_measure_node_scratch.is_empty() {
636 return 0;
637 }
638
639 self.cached_measure_size_scratch.clear();
640 (self.cached_measure_batch_registrar)(
641 &self.cached_measure_node_scratch,
642 constraints,
643 &mut self.cached_measure_size_scratch,
644 );
645 self.cached_measure_size_scratch
646 .resize(self.cached_measure_node_scratch.len(), None);
647
648 let mut cached_count = 0;
649 self.cached_measure_missing_scratch.clear();
650 for index in 0..self.cached_measure_node_scratch.len() {
651 let node_id = self.cached_measure_node_scratch[index];
652 if self.cached_measure_size_scratch[index].is_some() {
653 cached_count += 1;
654 self.register_measurement_node_id(node_id);
655 } else {
656 self.cached_measure_missing_scratch.push(node_id);
657 }
658 }
659
660 let mut missing = std::mem::take(&mut self.cached_measure_missing_scratch);
661 for node_id in missing.drain(..) {
662 let _ = self.measure(SubcomposeChild::new(node_id), constraints);
663 }
664 self.cached_measure_missing_scratch = missing;
665
666 cached_count
667 }
668}
669
670fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
671 cranpose_core::with_current_composer(|composer| {
672 let holder_for_recompose = holder.clone();
673 composer.set_recranpose_callback(move |_composer| {
674 compose_subcompose_slot_content(holder_for_recompose.clone());
675 });
676 });
677
678 let invoke = holder.clone_rc();
679 invoke();
680}
681
682pub type MeasurePolicy =
684 dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
685
686pub struct SubcomposeLayoutNode {
688 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
689 parent: Cell<Option<NodeId>>,
691 id: Cell<Option<NodeId>>,
693 needs_measure: Cell<bool>,
695 needs_layout: Cell<bool>,
696 needs_semantics: Cell<bool>,
697 needs_redraw: Cell<bool>,
698 needs_pointer_pass: Cell<bool>,
699 needs_focus_sync: Cell<bool>,
700 virtual_children_count: Cell<usize>,
701 layout_state: RefCell<LayoutState>,
703 cache_handles: LayoutNodeCacheHandles,
704 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
705 modifier_slices_dirty: Cell<bool>,
706}
707
708impl SubcomposeLayoutNode {
709 pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
710 let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
711 let node = Self {
712 inner,
713 parent: Cell::new(None),
714 id: Cell::new(None),
715 needs_measure: Cell::new(true),
716 needs_layout: Cell::new(true),
717 needs_semantics: Cell::new(true),
718 needs_redraw: Cell::new(true),
719 needs_pointer_pass: Cell::new(false),
720 needs_focus_sync: Cell::new(false),
721 virtual_children_count: Cell::new(0),
722 layout_state: RefCell::new(LayoutState::default()),
723 cache_handles: LayoutNodeCacheHandles::default(),
724 modifier_slices_snapshot: RefCell::new(Rc::default()),
725 modifier_slices_dirty: Cell::new(true),
726 };
727 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
730 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
731 node.update_modifier_slices_cache();
732 node.note_host_to_the_composition_that_made_it();
733 node
734 }
735
736 fn note_host_to_the_composition_that_made_it(&self) {
737 let host = Rc::clone(&self.inner.borrow().slots);
738 cranpose_core::note_nested_slots_host(&host);
739 }
740
741 pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
747 let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
748 inner_data
749 .state
750 .set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
751 let inner = Rc::new(RefCell::new(inner_data));
752 let node = Self {
753 inner,
754 parent: Cell::new(None),
755 id: Cell::new(None),
756 needs_measure: Cell::new(true),
757 needs_layout: Cell::new(true),
758 needs_semantics: Cell::new(true),
759 needs_redraw: Cell::new(true),
760 needs_pointer_pass: Cell::new(false),
761 needs_focus_sync: Cell::new(false),
762 virtual_children_count: Cell::new(0),
763 layout_state: RefCell::new(LayoutState::default()),
764 cache_handles: LayoutNodeCacheHandles::default(),
765 modifier_slices_snapshot: RefCell::new(Rc::default()),
766 modifier_slices_dirty: Cell::new(true),
767 };
768 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
771 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
772 node.update_modifier_slices_cache();
773 node.note_host_to_the_composition_that_made_it();
774 node
775 }
776
777 pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
778 SubcomposeLayoutNodeHandle {
779 inner: Rc::clone(&self.inner),
780 }
781 }
782
783 #[doc(hidden)]
784 pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
785 self.inner.borrow().state.debug_scope_ids_by_slot()
786 }
787
788 #[doc(hidden)]
789 pub fn debug_slot_table_for_slot(
790 &self,
791 slot_id: cranpose_core::SlotId,
792 ) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
793 self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
794 }
795
796 #[doc(hidden)]
797 pub fn debug_slot_table_groups_for_slot(
798 &self,
799 slot_id: cranpose_core::SlotId,
800 ) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
801 self.inner
802 .borrow()
803 .state
804 .debug_slot_table_groups_for_slot(slot_id)
805 }
806
807 pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
808 let mut inner = self.inner.borrow_mut();
809 if Rc::ptr_eq(&inner.measure_policy, &policy) {
810 return;
811 }
812 inner.set_measure_policy(policy);
813 drop(inner);
814 self.invalidate_subcomposition();
815 }
816
817 pub fn set_captured_context(&mut self, context: cranpose_core::CapturedCompositionContext) {
819 self.inner.borrow_mut().captured_context = Some(context);
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 fn needs_semantics_flag(&self) -> bool {
965 self.needs_semantics.get()
966 }
967
968 pub(crate) fn clear_needs_semantics(&self) {
969 self.needs_semantics.set(false);
970 }
971
972 #[cfg(test)]
973 pub(crate) fn clear_needs_semantics_for_tests(&self) {
974 self.clear_needs_semantics();
975 }
976
977 pub fn needs_redraw(&self) -> bool {
979 self.needs_redraw.get()
980 }
981
982 pub fn clear_needs_redraw(&self) {
983 self.needs_redraw.set(false);
984 }
985
986 pub fn mark_needs_pointer_pass(&self) {
988 self.needs_pointer_pass.set(true);
989 }
990
991 pub fn needs_pointer_pass(&self) -> bool {
993 self.needs_pointer_pass.get()
994 }
995
996 pub fn clear_needs_pointer_pass(&self) {
998 self.needs_pointer_pass.set(false);
999 }
1000
1001 pub fn mark_needs_focus_sync(&self) {
1003 self.needs_focus_sync.set(true);
1004 }
1005
1006 pub fn needs_focus_sync(&self) -> bool {
1008 self.needs_focus_sync.get()
1009 }
1010
1011 pub fn clear_needs_focus_sync(&self) {
1013 self.needs_focus_sync.set(false);
1014 }
1015
1016 fn request_semantics_update(&self) {
1017 let already_dirty = self.needs_semantics.replace(true);
1018 if already_dirty {
1019 return;
1020 }
1021
1022 if let Some(id) = self.id.get() {
1023 cranpose_core::queue_semantics_invalidation(id);
1024 }
1025 }
1026
1027 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1029 self.inner.borrow().modifier_capabilities
1030 }
1031
1032 pub fn has_layout_modifier_nodes(&self) -> bool {
1033 self.modifier_capabilities()
1034 .contains(NodeCapabilities::LAYOUT)
1035 }
1036
1037 pub fn has_draw_modifier_nodes(&self) -> bool {
1038 self.modifier_capabilities()
1039 .contains(NodeCapabilities::DRAW)
1040 }
1041
1042 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1043 self.modifier_capabilities()
1044 .contains(NodeCapabilities::POINTER_INPUT)
1045 }
1046
1047 pub fn has_semantics_modifier_nodes(&self) -> bool {
1048 self.modifier_capabilities()
1049 .contains(NodeCapabilities::SEMANTICS)
1050 }
1051
1052 pub fn has_focus_modifier_nodes(&self) -> bool {
1053 self.modifier_capabilities()
1054 .contains(NodeCapabilities::FOCUS)
1055 }
1056
1057 fn dispatch_modifier_invalidations(
1064 &self,
1065 invalidations: &[ModifierInvalidation],
1066 prev_caps: NodeCapabilities,
1067 ) {
1068 let curr_caps = self.modifier_capabilities();
1069 for invalidation in invalidations {
1070 self.modifier_slices_dirty.set(true);
1071 let invalidation_caps = invalidation.capabilities();
1072 let has_capability = |capability| {
1073 curr_caps.contains(capability)
1074 || prev_caps.contains(capability)
1075 || invalidation_caps.contains(capability)
1076 };
1077 match invalidation.kind() {
1078 InvalidationKind::Layout => {
1079 if has_capability(NodeCapabilities::LAYOUT) {
1080 self.mark_needs_measure();
1081 }
1082 }
1083 InvalidationKind::Draw => {
1084 if has_capability(NodeCapabilities::DRAW) {
1085 self.mark_needs_redraw();
1086 }
1087 }
1088 InvalidationKind::PointerInput => {
1089 if has_capability(NodeCapabilities::POINTER_INPUT) {
1090 self.mark_needs_pointer_pass();
1091 crate::request_pointer_invalidation();
1092 if let Some(id) = self.id.get() {
1094 crate::schedule_pointer_repass(id);
1095 }
1096 }
1097 }
1098 InvalidationKind::Semantics => {
1099 self.request_semantics_update();
1100 }
1101 InvalidationKind::Focus => {
1102 if has_capability(NodeCapabilities::FOCUS) {
1103 self.mark_needs_focus_sync();
1104 crate::request_focus_invalidation();
1105 if let Some(id) = self.id.get() {
1107 crate::schedule_focus_invalidation(id);
1108 }
1109 }
1110 }
1111 }
1112 }
1113 }
1114}
1115
1116impl cranpose_core::Node for SubcomposeLayoutNode {
1117 fn mount(&mut self) {
1118 let mut inner = self.inner.borrow_mut();
1119 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1120 chain.repair_chain();
1121 chain.attach_nodes(&mut *context);
1122 }
1123
1124 fn unmount(&mut self) {
1125 self.inner
1126 .borrow_mut()
1127 .modifier_chain
1128 .chain_mut()
1129 .detach_nodes();
1130 }
1131
1132 fn insert_child(&mut self, child: NodeId) {
1133 let mut inner = self.inner.borrow_mut();
1134 if inner.children.contains(&child) {
1135 return;
1136 }
1137 if is_virtual_node(child) {
1138 let count = self.virtual_children_count.get();
1139 self.virtual_children_count.set(count + 1);
1140 }
1141 inner.children.push(child);
1142 }
1143
1144 fn remove_child(&mut self, child: NodeId) {
1145 let mut inner = self.inner.borrow_mut();
1146 let before = inner.children.len();
1147 inner.children.retain(|&id| id != child);
1148 if inner.children.len() < before && is_virtual_node(child) {
1149 let count = self.virtual_children_count.get();
1150 if count > 0 {
1151 self.virtual_children_count.set(count - 1);
1152 }
1153 }
1154 }
1155
1156 fn move_child(&mut self, from: usize, to: usize) {
1157 let mut inner = self.inner.borrow_mut();
1158 if from == to || from >= inner.children.len() {
1159 return;
1160 }
1161 let child = inner.children.remove(from);
1162 let target = to.min(inner.children.len());
1163 inner.children.insert(target, child);
1164 }
1165
1166 fn update_children(&mut self, children: &[NodeId]) {
1167 let mut inner = self.inner.borrow_mut();
1168 inner.children.clear();
1169 inner.children.extend_from_slice(children);
1170 }
1171
1172 fn children(&self) -> Vec<NodeId> {
1173 current_subcompose_children(&self.inner.borrow())
1174 }
1175
1176 fn collect_owned_children_into(&self, out: &mut SmallVec<[NodeId; 8]>) {
1177 out.clear();
1178 out.extend(self.inner.borrow().children.iter().copied());
1179 }
1180
1181 fn set_node_id(&mut self, id: NodeId) {
1182 self.id.set(Some(id));
1183 self.inner.borrow_mut().modifier_chain.set_node_id(Some(id));
1184 self.update_modifier_slices_cache();
1185 }
1186
1187 fn on_attached_to_parent(&mut self, parent: NodeId) {
1188 self.parent.set(Some(parent));
1189 }
1190
1191 fn on_removed_from_parent(&mut self) {
1192 self.parent.set(None);
1193 }
1194
1195 fn parent(&self) -> Option<NodeId> {
1196 self.parent.get()
1197 }
1198
1199 fn mark_needs_layout(&self) {
1200 self.needs_layout.set(true);
1201 }
1202
1203 fn needs_layout(&self) -> bool {
1204 self.needs_layout.get()
1205 }
1206
1207 fn mark_needs_measure(&self) {
1208 self.needs_measure.set(true);
1209 self.needs_layout.set(true); }
1211
1212 fn needs_measure(&self) -> bool {
1213 self.needs_measure.get()
1214 }
1215
1216 fn mark_needs_semantics(&self) {
1217 self.needs_semantics.set(true);
1218 }
1219
1220 fn needs_semantics(&self) -> bool {
1221 self.needs_semantics.get()
1222 }
1223
1224 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1226 self.parent.set(Some(parent));
1227 }
1228}
1229
1230#[derive(Clone)]
1231pub struct SubcomposeLayoutNodeHandle {
1232 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1233}
1234
1235impl SubcomposeLayoutNodeHandle {
1236 pub(crate) fn note_slot_host(&self, slot_host: &Rc<cranpose_core::SlotsHost>) {
1237 let Ok(inner) = self.inner.try_borrow() else {
1238 return;
1239 };
1240 if Rc::ptr_eq(&inner.slots, slot_host) {
1241 return;
1242 }
1243 inner.slots.note_nested_host(slot_host);
1244 }
1245
1246 pub(crate) fn measured_children_scratch(
1247 &self,
1248 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1249 let scratch = {
1250 let inner = self.inner.borrow();
1251 Rc::clone(&inner.measured_children_scratch)
1252 };
1253 scratch.borrow_mut().clear();
1254 scratch
1255 }
1256
1257 pub fn modifier(&self) -> Modifier {
1258 self.inner.borrow().modifier.clone()
1259 }
1260
1261 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1262 self.resolved_modifiers().layout_properties()
1263 }
1264
1265 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1266 self.inner.borrow().resolved_modifiers
1267 }
1268
1269 pub fn total_offset(&self) -> Point {
1270 self.resolved_modifiers().offset()
1271 }
1272
1273 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1274 self.inner.borrow().modifier_capabilities
1275 }
1276
1277 pub fn has_layout_modifier_nodes(&self) -> bool {
1278 self.modifier_capabilities()
1279 .contains(NodeCapabilities::LAYOUT)
1280 }
1281
1282 pub fn has_draw_modifier_nodes(&self) -> bool {
1283 self.modifier_capabilities()
1284 .contains(NodeCapabilities::DRAW)
1285 }
1286
1287 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1288 self.modifier_capabilities()
1289 .contains(NodeCapabilities::POINTER_INPUT)
1290 }
1291
1292 pub fn has_semantics_modifier_nodes(&self) -> bool {
1293 self.modifier_capabilities()
1294 .contains(NodeCapabilities::SEMANTICS)
1295 }
1296
1297 pub fn has_focus_modifier_nodes(&self) -> bool {
1298 self.modifier_capabilities()
1299 .contains(NodeCapabilities::FOCUS)
1300 }
1301
1302 pub fn set_debug_modifiers(&self, enabled: bool) {
1303 self.inner.borrow_mut().set_debug_modifiers(enabled);
1304 }
1305
1306 pub fn measure<'a>(
1307 &self,
1308 composer: &Composer,
1309 node_id: NodeId,
1310 constraints: Constraints,
1311 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
1312 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1313 error: &'a RefCell<Option<NodeError>>,
1314 ) -> Result<MeasureResult, NodeError> {
1315 self.measure_with_cached_batch(
1316 composer,
1317 node_id,
1318 constraints,
1319 CachedBatchMeasureInputs {
1320 measurer,
1321 cached_measure_batch_registrar: Box::new(
1322 move |node_ids, child_constraints, out| {
1323 out.clear();
1324 out.reserve(node_ids.len());
1325 for &child_id in node_ids {
1326 out.push(cached_measure_registrar(child_id, child_constraints));
1327 }
1328 },
1329 ),
1330 retained_measure_lookup: Box::new(|_| None),
1331 retained_measure_registrar: Box::new(|_| {}),
1332 error,
1333 },
1334 )
1335 }
1336
1337 pub(crate) fn measure_with_cached_batch<'a>(
1338 &self,
1339 composer: &Composer,
1340 node_id: NodeId,
1341 constraints: Constraints,
1342 callbacks: CachedBatchMeasureInputs<'a>,
1343 ) -> Result<MeasureResult, NodeError> {
1344 let CachedBatchMeasureInputs {
1345 measurer,
1346 cached_measure_batch_registrar,
1347 retained_measure_lookup,
1348 retained_measure_registrar,
1349 error,
1350 } = callbacks;
1351 let (policy, mut state, slots_host, placement_scratch, captured_context) = {
1352 let mut inner = self.inner.borrow_mut();
1353 let policy = Rc::clone(&inner.measure_policy);
1354 let state = std::mem::take(&mut inner.state);
1355 let slots_host = Rc::clone(&inner.slots);
1356 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1357 let captured_context = inner.captured_context.clone();
1358 (
1359 policy,
1360 state,
1361 slots_host,
1362 placement_scratch,
1363 captured_context,
1364 )
1365 };
1366 state.begin_pass();
1367
1368 let previous = composer.phase();
1369 if !matches!(previous, Phase::Measure | Phase::Layout) {
1370 composer.enter_phase(Phase::Measure);
1371 }
1372
1373 let constraints_copy = constraints;
1374 let fallback_context;
1383 let context = if let Some(context) = captured_context.as_ref() {
1384 context
1385 } else {
1386 fallback_context = composer.capture_composition_context();
1387 &fallback_context
1388 };
1389 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1390 &slots_host,
1391 Some(node_id),
1392 context,
1393 |inner_composer| {
1394 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1395 composer: inner_composer.clone(),
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}
1475
1476impl SubcomposeLayoutNodeInner {
1477 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1478 Self {
1479 modifier: Modifier::empty(),
1480 modifier_chain: ModifierChainHandle::new(),
1481 resolved_modifiers: ResolvedModifiers::default(),
1482 modifier_capabilities: NodeCapabilities::default(),
1483 state: SubcomposeState::default(),
1484 measure_policy,
1485 children: Vec::new(),
1486 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1487 debug_modifiers: false,
1488 virtual_nodes: HashMap::new(),
1489 last_placements: Vec::new(),
1490 placement_scratch: Vec::new(),
1491 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1492 captured_context: None,
1493 }
1494 }
1495
1496 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1497 self.measure_policy = policy;
1498 if let Err(err) = self.slots.reset() {
1503 log::error!(
1504 "failed to reset root measurement slots after measure policy update: {err}"
1505 );
1506 }
1507 }
1508
1509 fn set_modifier_collect(&mut self, modifier: Modifier) -> (Vec<ModifierInvalidation>, bool) {
1512 let modifier_changed = !self.modifier.structural_eq(&modifier);
1513 self.modifier = modifier;
1514 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1515 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1516 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1517 self.modifier_capabilities = self.modifier_chain.capabilities();
1518
1519 let mut invalidations = self.modifier_chain.take_invalidations();
1521 invalidations.extend(modifier_local_invalidations);
1522
1523 (invalidations, modifier_changed)
1524 }
1525
1526 fn set_debug_modifiers(&mut self, enabled: bool) {
1527 self.debug_modifiers = enabled;
1528 self.modifier_chain.set_debug_logging(enabled);
1529 }
1530}
1531
1532#[cfg(test)]
1533#[path = "tests/subcompose_layout_tests.rs"]
1534mod tests;