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