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 std::env::var_os("CRANPOSE_SUBCOMPOSE_TELEMETRY").is_some()
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 {
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 let holder_for_slot = content_holder.clone();
304 let scopes = self
305 .composer
306 .subcompose_slot(&slot_host, Some(virtual_node_id), move |_| {
307 compose_subcompose_slot_content(holder_for_slot.clone());
308 })
309 .map(|(_, scopes)| scopes)
310 .unwrap_or_default();
311 self.pending_commands_applied = false;
312
313 self.state
314 .register_active(slot_id, &[virtual_node_id], &scopes);
315
316 let children = self.composer.get_node_children(virtual_node_id).to_vec();
320 if let Some(start) = telemetry_start {
321 log::warn!(
322 "[subcompose-telemetry] slot={} reused={} children={} subcompose_ms={:.2}",
323 slot_id.raw(),
324 is_reused,
325 children.len(),
326 start.elapsed().as_secs_f64() * 1000.0
327 );
328 }
329 children
330 }
331
332 pub(crate) fn activate_exact_retained_slot_with_known_children(
333 &mut self,
334 slot_id: SlotId,
335 known_children: &[u64],
336 ) -> Option<(Vec<SubcomposeChild>, bool)> {
337 let mut expected_children = Vec::with_capacity(known_children.len());
338 for &node_id in known_children {
339 expected_children.push(NodeId::try_from(node_id).ok()?);
340 }
341
342 let virtual_node_ids = match self.activate_current_active_slot_roots(slot_id) {
343 Some(virtual_node_ids) => {
344 for virtual_node_id in &virtual_node_ids {
345 self.composer.record_subcompose_child(*virtual_node_id);
346 }
347 virtual_node_ids
348 }
349 None => self.activate_recycled_exact_retained_slot_roots(slot_id)?,
350 };
351
352 if !self.ensure_pending_commands_applied() {
364 return None;
365 }
366
367 let mut activated_children = Vec::with_capacity(expected_children.len());
368 for virtual_node_id in virtual_node_ids {
369 activated_children.extend(
370 self.composer
371 .get_node_children(virtual_node_id)
372 .iter()
373 .copied(),
374 );
375 }
376 let children_match = activated_children == expected_children;
377 Some((
378 activated_children
379 .into_iter()
380 .map(SubcomposeChild::new)
381 .collect(),
382 children_match,
383 ))
384 }
385
386 fn activate_current_active_slot_roots(&mut self, slot_id: SlotId) -> Option<Vec<NodeId>> {
387 self.state.activate_current_active_slot(slot_id)
388 }
389
390 fn activate_recycled_exact_retained_slot_roots(
391 &mut self,
392 slot_id: SlotId,
393 ) -> Option<Vec<NodeId>> {
394 let activation = self.state.take_exact_slot_activation(slot_id)?;
395 let virtual_node_ids = activation.nodes;
396 let scopes = activation.scopes;
397 let reactivate_scopes = activation.reactivate_scopes;
398
399 if reactivate_scopes {
400 let inner = self.parent_handle.inner.borrow();
401 for virtual_node_id in &virtual_node_ids {
402 self.composer.record_subcompose_child(*virtual_node_id);
403 if let Some(v_node) = inner.virtual_nodes.get(virtual_node_id) {
404 v_node.set_parent(self.root_id);
405 }
406 }
407 for virtual_node_id in &virtual_node_ids {
408 let _ = self
409 .composer
410 .with_node_mut::<LayoutNode, _>(*virtual_node_id, |node| {
411 node.set_parent(self.root_id);
412 });
413 }
414 } else {
415 for virtual_node_id in &virtual_node_ids {
416 self.composer.record_subcompose_child(*virtual_node_id);
417 }
418 }
419
420 self.state.register_active_with_scope_reactivation(
421 slot_id,
422 &virtual_node_ids,
423 &scopes,
424 reactivate_scopes,
425 );
426 Some(virtual_node_ids)
427 }
428}
429
430impl<'a> SubcomposeLayoutScope for SubcomposeMeasureScopeImpl<'a> {
431 fn constraints(&self) -> Constraints {
432 self.constraints
433 }
434
435 fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
436 where
437 I: IntoIterator<Item = Placement>,
438 {
439 self.layout_with_placement_builder(width, height, |scratch| {
440 scratch.extend(placements);
441 })
442 }
443}
444
445impl<'a> SubcomposeMeasureScope for SubcomposeMeasureScopeImpl<'a> {
446 fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
447 where
448 Content: FnMut() + 'static,
449 {
450 let nodes = self.perform_subcompose(slot_id, content);
451 nodes.into_iter().map(SubcomposeChild::new).collect()
452 }
453
454 fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable {
455 if self.error.borrow().is_some() {
456 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
458 }
459
460 let telemetry_start = subcompose_telemetry_enabled().then(Instant::now);
461 if !self.ensure_pending_commands_applied() {
462 return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
463 }
464
465 let size = (self.measurer)(child.node_id, constraints);
466 self.register_measurement_node_id(child.node_id);
467 if let Some(start) = telemetry_start {
468 log::warn!(
469 "[subcompose-telemetry] child={} measure_ms={:.2} size=({:.2},{:.2})",
470 child.node_id,
471 start.elapsed().as_secs_f64() * 1000.0,
472 size.width,
473 size.height
474 );
475 }
476 SubcomposePlaceable::value(size.width, size.height, child.node_id)
477 }
478
479 fn node_has_no_parent(&self, node_id: NodeId) -> bool {
480 self.composer.node_has_no_parent(node_id)
481 }
482}
483
484impl<'a> SubcomposeMeasureScopeImpl<'a> {
485 pub fn subcompose_with_size<Content, F>(
490 &mut self,
491 slot_id: SlotId,
492 content: Content,
493 estimate_size: F,
494 ) -> Vec<SubcomposeChild>
495 where
496 Content: FnMut() + 'static,
497 F: Fn(usize) -> Size,
498 {
499 let nodes = self.perform_subcompose(slot_id, content);
500 nodes
501 .into_iter()
502 .enumerate()
503 .map(|(i, node_id)| SubcomposeChild::with_size(node_id, estimate_size(i)))
504 .collect()
505 }
506
507 pub fn active_slots_count(&self) -> usize {
511 self.state.active_slots_count()
512 }
513
514 pub fn reusable_slots_count(&self) -> usize {
518 self.state.reusable_slots_count()
519 }
520
521 pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
527 self.state.register_content_type(slot_id, content_type);
528 }
529
530 pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
536 self.state.update_content_type(slot_id, content_type);
537 }
538
539 pub(crate) fn set_reusable_pool_limits(&mut self, per_type: usize, untyped: usize) {
540 self.state.set_reusable_pool_limits(per_type, untyped);
541 }
542
543 pub(crate) fn recycle_active_slots_where(&mut self, predicate: impl FnMut(SlotId) -> bool) {
544 let disposed = self.state.recycle_active_slots_where(predicate);
545 debug_assert!(
546 disposed.is_empty(),
547 "lazy subcompose reusable pool limits must retain recycled active slots"
548 );
549 }
550
551 pub fn was_last_slot_reused(&self) -> Option<bool> {
559 self.state.was_last_slot_reused()
560 }
561
562 pub(crate) fn measure_retained(
563 &mut self,
564 child: SubcomposeChild,
565 constraints: Constraints,
566 ) -> (SubcomposePlaceable, Option<Rc<MeasuredNode>>) {
567 let placeable = self.measure(child, constraints);
568 let retained = (self.retained_measure_lookup)(child.node_id);
569 (placeable, retained)
570 }
571
572 pub(crate) fn register_retained_measurements(&mut self, measurements: &[Rc<MeasuredNode>]) {
573 if measurements.is_empty() {
574 return;
575 }
576
577 for measured in measurements {
578 self.register_measurement_node_id(measured.node_id());
579 }
580 (self.retained_measure_registrar)(measurements);
581 }
582
583 pub(crate) fn children_need_relayout(&mut self, children: &[SubcomposeChild]) -> bool {
596 if !self.ensure_pending_commands_applied() {
597 return true;
598 }
599
600 let mut root_ids = smallvec::SmallVec::<[NodeId; 8]>::new();
601 root_ids.extend(children.iter().map(SubcomposeChild::node_id));
602 self.composer.nodes_need_measure(&root_ids) || self.composer.nodes_need_layout(&root_ids)
603 }
604
605 pub(crate) fn ensure_cached_measurement_node_ids<I>(
606 &mut self,
607 node_ids: I,
608 constraints: Constraints,
609 ) -> usize
610 where
611 I: IntoIterator<Item = NodeId>,
612 {
613 if self.error.borrow().is_some() || !self.ensure_pending_commands_applied() {
614 return 0;
615 }
616
617 self.cached_measure_node_scratch.clear();
618 self.cached_measure_node_scratch.extend(
619 node_ids
620 .into_iter()
621 .filter(|node_id| !self.registered_measurement_node_ids.contains(node_id)),
622 );
623 if self.cached_measure_node_scratch.is_empty() {
624 return 0;
625 }
626
627 self.cached_measure_size_scratch.clear();
628 (self.cached_measure_batch_registrar)(
629 &self.cached_measure_node_scratch,
630 constraints,
631 &mut self.cached_measure_size_scratch,
632 );
633 self.cached_measure_size_scratch
634 .resize(self.cached_measure_node_scratch.len(), None);
635
636 let mut cached_count = 0;
637 self.cached_measure_missing_scratch.clear();
638 for index in 0..self.cached_measure_node_scratch.len() {
639 let node_id = self.cached_measure_node_scratch[index];
640 if self.cached_measure_size_scratch[index].is_some() {
641 cached_count += 1;
642 self.register_measurement_node_id(node_id);
643 } else {
644 self.cached_measure_missing_scratch.push(node_id);
645 }
646 }
647
648 let mut missing = std::mem::take(&mut self.cached_measure_missing_scratch);
649 for node_id in missing.drain(..) {
650 let _ = self.measure(SubcomposeChild::new(node_id), constraints);
651 }
652 self.cached_measure_missing_scratch = missing;
653
654 cached_count
655 }
656}
657
658fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
659 cranpose_core::with_current_composer(|composer| {
660 let holder_for_recompose = holder.clone();
661 composer.set_recranpose_callback(move |_composer| {
662 compose_subcompose_slot_content(holder_for_recompose.clone());
663 });
664 });
665
666 let invoke = holder.clone_rc();
667 invoke();
668}
669
670pub type MeasurePolicy =
672 dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
673
674pub struct SubcomposeLayoutNode {
676 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
677 parent: Cell<Option<NodeId>>,
679 id: Cell<Option<NodeId>>,
681 needs_measure: Cell<bool>,
683 needs_layout: Cell<bool>,
684 needs_semantics: Cell<bool>,
685 needs_redraw: Cell<bool>,
686 needs_pointer_pass: Cell<bool>,
687 needs_focus_sync: Cell<bool>,
688 virtual_children_count: Cell<usize>,
689 layout_state: RefCell<LayoutState>,
691 cache_handles: LayoutNodeCacheHandles,
692 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
693 modifier_slices_dirty: Cell<bool>,
694}
695
696impl SubcomposeLayoutNode {
697 pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
698 let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
699 let node = Self {
700 inner,
701 parent: Cell::new(None),
702 id: Cell::new(None),
703 needs_measure: Cell::new(true),
704 needs_layout: Cell::new(true),
705 needs_semantics: Cell::new(true),
706 needs_redraw: Cell::new(true),
707 needs_pointer_pass: Cell::new(false),
708 needs_focus_sync: Cell::new(false),
709 virtual_children_count: Cell::new(0),
710 layout_state: RefCell::new(LayoutState::default()),
711 cache_handles: LayoutNodeCacheHandles::default(),
712 modifier_slices_snapshot: RefCell::new(Rc::default()),
713 modifier_slices_dirty: Cell::new(true),
714 };
715 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
718 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
719 node.update_modifier_slices_cache();
720 node
721 }
722
723 pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
729 let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
730 inner_data
731 .state
732 .set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
733 let inner = Rc::new(RefCell::new(inner_data));
734 let node = Self {
735 inner,
736 parent: Cell::new(None),
737 id: Cell::new(None),
738 needs_measure: Cell::new(true),
739 needs_layout: Cell::new(true),
740 needs_semantics: Cell::new(true),
741 needs_redraw: Cell::new(true),
742 needs_pointer_pass: Cell::new(false),
743 needs_focus_sync: Cell::new(false),
744 virtual_children_count: Cell::new(0),
745 layout_state: RefCell::new(LayoutState::default()),
746 cache_handles: LayoutNodeCacheHandles::default(),
747 modifier_slices_snapshot: RefCell::new(Rc::default()),
748 modifier_slices_dirty: Cell::new(true),
749 };
750 let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
753 node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
754 node.update_modifier_slices_cache();
755 node
756 }
757
758 pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
759 SubcomposeLayoutNodeHandle {
760 inner: Rc::clone(&self.inner),
761 }
762 }
763
764 #[doc(hidden)]
765 pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
766 self.inner.borrow().state.debug_scope_ids_by_slot()
767 }
768
769 #[doc(hidden)]
770 pub fn debug_slot_table_for_slot(
771 &self,
772 slot_id: cranpose_core::SlotId,
773 ) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
774 self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
775 }
776
777 #[doc(hidden)]
778 pub fn debug_slot_table_groups_for_slot(
779 &self,
780 slot_id: cranpose_core::SlotId,
781 ) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
782 self.inner
783 .borrow()
784 .state
785 .debug_slot_table_groups_for_slot(slot_id)
786 }
787
788 pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
789 let mut inner = self.inner.borrow_mut();
790 if Rc::ptr_eq(&inner.measure_policy, &policy) {
791 return;
792 }
793 inner.set_measure_policy(policy);
794 drop(inner);
795 self.invalidate_subcomposition();
796 }
797
798 pub fn set_captured_context(&mut self, context: cranpose_core::CapturedCompositionContext) {
800 self.inner.borrow_mut().captured_context = Some(context);
801 }
802
803 pub fn set_modifier(&mut self, modifier: Modifier) {
804 let prev_caps = self.modifier_capabilities();
806 let (invalidations, modifier_changed) = {
808 let mut inner = self.inner.borrow_mut();
809 inner.set_modifier_collect(modifier)
810 };
811 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
814 self.update_modifier_slices_cache();
815 if modifier_changed {
816 self.request_semantics_update();
817 }
818 }
819
820 fn update_modifier_slices_cache(&self) {
822 let inner = self.inner.borrow();
823 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
824 collect_modifier_slices_into(inner.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
825 self.modifier_slices_dirty.set(false);
826 }
827
828 pub(crate) fn mark_modifier_slices_dirty(&self) {
829 self.modifier_slices_dirty.set(true);
830 }
831
832 pub fn set_debug_modifiers(&mut self, enabled: bool) {
833 self.inner.borrow_mut().set_debug_modifiers(enabled);
834 }
835
836 pub fn modifier(&self) -> Modifier {
837 self.handle().modifier()
838 }
839
840 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
841 self.inner.borrow().resolved_modifiers
842 }
843
844 pub fn layout_state(&self) -> LayoutState {
846 self.layout_state.borrow().clone()
847 }
848
849 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
850 self.cache_handles.clone()
851 }
852
853 pub fn set_position(&self, position: Point) {
855 let mut state = self.layout_state.borrow_mut();
856 state.position = position;
857 state.is_placed = true;
858 }
859
860 pub fn set_measured_size(&self, size: Size) {
862 let mut state = self.layout_state.borrow_mut();
863 state.size = size;
864 }
865
866 pub fn clear_placed(&self) {
868 self.layout_state.borrow_mut().is_placed = false;
869 }
870
871 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
873 if self.modifier_slices_dirty.get() {
874 self.update_modifier_slices_cache();
875 }
876 self.modifier_slices_snapshot.borrow().clone()
877 }
878
879 pub fn state(&self) -> Ref<'_, SubcomposeState> {
880 Ref::map(self.inner.borrow(), |inner| &inner.state)
881 }
882
883 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
884 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
885 }
886
887 pub fn invalidate_subcomposition(&self) {
888 self.inner.borrow().state.invalidate_scopes();
889 self.mark_needs_measure();
890 if let Some(id) = self.id.get() {
891 cranpose_core::bubble_measure_dirty_in_composer(id);
892 }
893 }
894
895 pub fn request_measure_recompose(&self) {
896 self.mark_needs_measure();
897 if let Some(id) = self.id.get() {
898 cranpose_core::bubble_measure_dirty_in_composer(id);
899 }
900 }
901
902 pub fn active_children(&self) -> Vec<NodeId> {
903 current_subcompose_children(&self.inner.borrow())
904 }
905
906 pub fn mark_needs_measure(&self) {
908 self.needs_measure.set(true);
909 self.needs_layout.set(true);
910 }
911
912 pub fn mark_needs_layout_flag(&self) {
914 self.needs_layout.set(true);
915 }
916
917 pub fn mark_needs_redraw(&self) {
919 self.needs_redraw.set(true);
920 if let Some(id) = self.id.get() {
921 crate::schedule_draw_repass(id);
922 }
923 crate::request_render_invalidation();
924 }
925
926 pub fn needs_measure(&self) -> bool {
928 self.needs_measure.get()
929 }
930
931 pub(crate) fn clear_needs_measure(&self) {
932 self.needs_measure.set(false);
933 }
934
935 pub(crate) fn clear_needs_layout(&self) {
936 self.needs_layout.set(false);
937 }
938
939 pub fn mark_needs_semantics(&self) {
941 self.needs_semantics.set(true);
942 }
943
944 pub fn needs_semantics_flag(&self) -> bool {
946 self.needs_semantics.get()
947 }
948
949 pub(crate) fn clear_needs_semantics(&self) {
950 self.needs_semantics.set(false);
951 }
952
953 #[cfg(test)]
954 pub(crate) fn clear_needs_semantics_for_tests(&self) {
955 self.clear_needs_semantics();
956 }
957
958 pub fn needs_redraw(&self) -> bool {
960 self.needs_redraw.get()
961 }
962
963 pub fn clear_needs_redraw(&self) {
964 self.needs_redraw.set(false);
965 }
966
967 pub fn mark_needs_pointer_pass(&self) {
969 self.needs_pointer_pass.set(true);
970 }
971
972 pub fn needs_pointer_pass(&self) -> bool {
974 self.needs_pointer_pass.get()
975 }
976
977 pub fn clear_needs_pointer_pass(&self) {
979 self.needs_pointer_pass.set(false);
980 }
981
982 pub fn mark_needs_focus_sync(&self) {
984 self.needs_focus_sync.set(true);
985 }
986
987 pub fn needs_focus_sync(&self) -> bool {
989 self.needs_focus_sync.get()
990 }
991
992 pub fn clear_needs_focus_sync(&self) {
994 self.needs_focus_sync.set(false);
995 }
996
997 fn request_semantics_update(&self) {
998 let already_dirty = self.needs_semantics.replace(true);
999 if already_dirty {
1000 return;
1001 }
1002
1003 if let Some(id) = self.id.get() {
1004 cranpose_core::queue_semantics_invalidation(id);
1005 }
1006 }
1007
1008 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1010 self.inner.borrow().modifier_capabilities
1011 }
1012
1013 pub fn has_layout_modifier_nodes(&self) -> bool {
1014 self.modifier_capabilities()
1015 .contains(NodeCapabilities::LAYOUT)
1016 }
1017
1018 pub fn has_draw_modifier_nodes(&self) -> bool {
1019 self.modifier_capabilities()
1020 .contains(NodeCapabilities::DRAW)
1021 }
1022
1023 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1024 self.modifier_capabilities()
1025 .contains(NodeCapabilities::POINTER_INPUT)
1026 }
1027
1028 pub fn has_semantics_modifier_nodes(&self) -> bool {
1029 self.modifier_capabilities()
1030 .contains(NodeCapabilities::SEMANTICS)
1031 }
1032
1033 pub fn has_focus_modifier_nodes(&self) -> bool {
1034 self.modifier_capabilities()
1035 .contains(NodeCapabilities::FOCUS)
1036 }
1037
1038 fn dispatch_modifier_invalidations(
1045 &self,
1046 invalidations: &[ModifierInvalidation],
1047 prev_caps: NodeCapabilities,
1048 ) {
1049 let curr_caps = self.modifier_capabilities();
1050 for invalidation in invalidations {
1051 self.modifier_slices_dirty.set(true);
1052 let invalidation_caps = invalidation.capabilities();
1053 let has_capability = |capability| {
1054 curr_caps.contains(capability)
1055 || prev_caps.contains(capability)
1056 || invalidation_caps.contains(capability)
1057 };
1058 match invalidation.kind() {
1059 InvalidationKind::Layout => {
1060 if has_capability(NodeCapabilities::LAYOUT) {
1061 self.mark_needs_measure();
1062 }
1063 }
1064 InvalidationKind::Draw => {
1065 if has_capability(NodeCapabilities::DRAW) {
1066 self.mark_needs_redraw();
1067 }
1068 }
1069 InvalidationKind::PointerInput => {
1070 if has_capability(NodeCapabilities::POINTER_INPUT) {
1071 self.mark_needs_pointer_pass();
1072 crate::request_pointer_invalidation();
1073 if let Some(id) = self.id.get() {
1075 crate::schedule_pointer_repass(id);
1076 }
1077 }
1078 }
1079 InvalidationKind::Semantics => {
1080 self.request_semantics_update();
1081 }
1082 InvalidationKind::Focus => {
1083 if has_capability(NodeCapabilities::FOCUS) {
1084 self.mark_needs_focus_sync();
1085 crate::request_focus_invalidation();
1086 if let Some(id) = self.id.get() {
1088 crate::schedule_focus_invalidation(id);
1089 }
1090 }
1091 }
1092 }
1093 }
1094 }
1095}
1096
1097impl cranpose_core::Node for SubcomposeLayoutNode {
1098 fn mount(&mut self) {
1099 let mut inner = self.inner.borrow_mut();
1100 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1101 chain.repair_chain();
1102 chain.attach_nodes(&mut *context);
1103 }
1104
1105 fn unmount(&mut self) {
1106 self.inner
1107 .borrow_mut()
1108 .modifier_chain
1109 .chain_mut()
1110 .detach_nodes();
1111 }
1112
1113 fn insert_child(&mut self, child: NodeId) {
1114 let mut inner = self.inner.borrow_mut();
1115 if inner.children.contains(&child) {
1116 return;
1117 }
1118 if is_virtual_node(child) {
1119 let count = self.virtual_children_count.get();
1120 self.virtual_children_count.set(count + 1);
1121 }
1122 inner.children.push(child);
1123 }
1124
1125 fn remove_child(&mut self, child: NodeId) {
1126 let mut inner = self.inner.borrow_mut();
1127 let before = inner.children.len();
1128 inner.children.retain(|&id| id != child);
1129 if inner.children.len() < before && is_virtual_node(child) {
1130 let count = self.virtual_children_count.get();
1131 if count > 0 {
1132 self.virtual_children_count.set(count - 1);
1133 }
1134 }
1135 }
1136
1137 fn move_child(&mut self, from: usize, to: usize) {
1138 let mut inner = self.inner.borrow_mut();
1139 if from == to || from >= inner.children.len() {
1140 return;
1141 }
1142 let child = inner.children.remove(from);
1143 let target = to.min(inner.children.len());
1144 inner.children.insert(target, child);
1145 }
1146
1147 fn update_children(&mut self, children: &[NodeId]) {
1148 let mut inner = self.inner.borrow_mut();
1149 inner.children.clear();
1150 inner.children.extend_from_slice(children);
1151 }
1152
1153 fn children(&self) -> Vec<NodeId> {
1154 current_subcompose_children(&self.inner.borrow())
1155 }
1156
1157 fn set_node_id(&mut self, id: NodeId) {
1158 self.id.set(Some(id));
1159 self.inner.borrow_mut().modifier_chain.set_node_id(Some(id));
1160 self.update_modifier_slices_cache();
1161 }
1162
1163 fn on_attached_to_parent(&mut self, parent: NodeId) {
1164 self.parent.set(Some(parent));
1165 }
1166
1167 fn on_removed_from_parent(&mut self) {
1168 self.parent.set(None);
1169 }
1170
1171 fn parent(&self) -> Option<NodeId> {
1172 self.parent.get()
1173 }
1174
1175 fn mark_needs_layout(&self) {
1176 self.needs_layout.set(true);
1177 }
1178
1179 fn needs_layout(&self) -> bool {
1180 self.needs_layout.get()
1181 }
1182
1183 fn mark_needs_measure(&self) {
1184 self.needs_measure.set(true);
1185 self.needs_layout.set(true); }
1187
1188 fn needs_measure(&self) -> bool {
1189 self.needs_measure.get()
1190 }
1191
1192 fn mark_needs_semantics(&self) {
1193 self.needs_semantics.set(true);
1194 }
1195
1196 fn needs_semantics(&self) -> bool {
1197 self.needs_semantics.get()
1198 }
1199
1200 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1202 self.parent.set(Some(parent));
1203 }
1204}
1205
1206#[derive(Clone)]
1207pub struct SubcomposeLayoutNodeHandle {
1208 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1209}
1210
1211impl SubcomposeLayoutNodeHandle {
1212 pub(crate) fn measured_children_scratch(
1213 &self,
1214 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1215 let scratch = {
1216 let inner = self.inner.borrow();
1217 Rc::clone(&inner.measured_children_scratch)
1218 };
1219 scratch.borrow_mut().clear();
1220 scratch
1221 }
1222
1223 pub fn modifier(&self) -> Modifier {
1224 self.inner.borrow().modifier.clone()
1225 }
1226
1227 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1228 self.resolved_modifiers().layout_properties()
1229 }
1230
1231 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1232 self.inner.borrow().resolved_modifiers
1233 }
1234
1235 pub fn total_offset(&self) -> Point {
1236 self.resolved_modifiers().offset()
1237 }
1238
1239 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1240 self.inner.borrow().modifier_capabilities
1241 }
1242
1243 pub fn has_layout_modifier_nodes(&self) -> bool {
1244 self.modifier_capabilities()
1245 .contains(NodeCapabilities::LAYOUT)
1246 }
1247
1248 pub fn has_draw_modifier_nodes(&self) -> bool {
1249 self.modifier_capabilities()
1250 .contains(NodeCapabilities::DRAW)
1251 }
1252
1253 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1254 self.modifier_capabilities()
1255 .contains(NodeCapabilities::POINTER_INPUT)
1256 }
1257
1258 pub fn has_semantics_modifier_nodes(&self) -> bool {
1259 self.modifier_capabilities()
1260 .contains(NodeCapabilities::SEMANTICS)
1261 }
1262
1263 pub fn has_focus_modifier_nodes(&self) -> bool {
1264 self.modifier_capabilities()
1265 .contains(NodeCapabilities::FOCUS)
1266 }
1267
1268 pub fn set_debug_modifiers(&self, enabled: bool) {
1269 self.inner.borrow_mut().set_debug_modifiers(enabled);
1270 }
1271
1272 pub fn measure<'a>(
1273 &self,
1274 composer: &Composer,
1275 node_id: NodeId,
1276 constraints: Constraints,
1277 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
1278 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1279 error: &'a RefCell<Option<NodeError>>,
1280 ) -> Result<MeasureResult, NodeError> {
1281 self.measure_with_cached_batch(
1282 composer,
1283 node_id,
1284 constraints,
1285 CachedBatchMeasureInputs {
1286 measurer,
1287 cached_measure_batch_registrar: Box::new(
1288 move |node_ids, child_constraints, out| {
1289 out.clear();
1290 out.reserve(node_ids.len());
1291 for &child_id in node_ids {
1292 out.push(cached_measure_registrar(child_id, child_constraints));
1293 }
1294 },
1295 ),
1296 retained_measure_lookup: Box::new(|_| None),
1297 retained_measure_registrar: Box::new(|_| {}),
1298 error,
1299 },
1300 )
1301 }
1302
1303 pub(crate) fn measure_with_cached_batch<'a>(
1304 &self,
1305 composer: &Composer,
1306 node_id: NodeId,
1307 constraints: Constraints,
1308 callbacks: CachedBatchMeasureInputs<'a>,
1309 ) -> Result<MeasureResult, NodeError> {
1310 let CachedBatchMeasureInputs {
1311 measurer,
1312 cached_measure_batch_registrar,
1313 retained_measure_lookup,
1314 retained_measure_registrar,
1315 error,
1316 } = callbacks;
1317 let (policy, mut state, slots_host, placement_scratch, captured_context) = {
1318 let mut inner = self.inner.borrow_mut();
1319 let policy = Rc::clone(&inner.measure_policy);
1320 let state = std::mem::take(&mut inner.state);
1321 let slots_host = Rc::clone(&inner.slots);
1322 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1323 let captured_context = inner.captured_context.clone();
1324 (
1325 policy,
1326 state,
1327 slots_host,
1328 placement_scratch,
1329 captured_context,
1330 )
1331 };
1332 state.begin_pass();
1333
1334 let previous = composer.phase();
1335 if !matches!(previous, Phase::Measure | Phase::Layout) {
1336 composer.enter_phase(Phase::Measure);
1337 }
1338
1339 let constraints_copy = constraints;
1340 let fallback_context;
1349 let context = if let Some(context) = captured_context.as_ref() {
1350 context
1351 } else {
1352 fallback_context = composer.capture_composition_context();
1353 &fallback_context
1354 };
1355 let ((result, placement_scratch), _) = composer.subcompose_slot_with_context(
1356 &slots_host,
1357 Some(node_id),
1358 context,
1359 |inner_composer| {
1360 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1361 composer: inner_composer.clone(),
1362 state: &mut state,
1363 constraints: constraints_copy,
1364 measurer,
1365 cached_measure_batch_registrar,
1366 retained_measure_lookup,
1367 retained_measure_registrar,
1368 error,
1369 parent_handle: self.clone(),
1370 root_id: node_id,
1371 placement_scratch,
1372 });
1373 let result = (policy)(&mut scope, constraints_copy);
1374 (result, scope.into_placement_scratch())
1375 },
1376 )?;
1377
1378 state.finish_pass();
1379
1380 if previous != composer.phase() {
1381 composer.enter_phase(previous);
1382 }
1383
1384 {
1385 let mut inner = self.inner.borrow_mut();
1386 inner.state = state;
1387 inner.placement_scratch = placement_scratch;
1388
1389 inner.last_placements = result.placements.iter().map(|p| p.node_id).collect();
1394 }
1395
1396 Ok(result)
1397 }
1398
1399 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1400 placements.clear();
1401 let mut inner = self.inner.borrow_mut();
1402 if placements.capacity() > inner.placement_scratch.capacity() {
1403 inner.placement_scratch = placements;
1404 }
1405 }
1406
1407 pub fn set_active_children<I>(&self, children: I)
1408 where
1409 I: IntoIterator<Item = NodeId>,
1410 {
1411 let mut inner = self.inner.borrow_mut();
1412 inner.children.clear();
1413 inner.children.extend(children);
1414 }
1415}
1416
1417fn current_subcompose_children(inner: &SubcomposeLayoutNodeInner) -> Vec<NodeId> {
1418 if !inner.last_placements.is_empty() {
1419 inner.last_placements.clone()
1420 } else {
1421 inner.children.clone()
1422 }
1423}
1424
1425struct SubcomposeLayoutNodeInner {
1426 modifier: Modifier,
1427 modifier_chain: ModifierChainHandle,
1428 resolved_modifiers: ResolvedModifiers,
1429 modifier_capabilities: NodeCapabilities,
1430 state: SubcomposeState,
1431 measure_policy: Rc<MeasurePolicy>,
1432 children: Vec<NodeId>,
1433 slots: Rc<SlotsHost>,
1434 debug_modifiers: bool,
1435 virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
1437 last_placements: Vec<NodeId>,
1440 placement_scratch: Vec<Placement>,
1441 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1442 captured_context: Option<cranpose_core::CapturedCompositionContext>,
1444}
1445
1446impl SubcomposeLayoutNodeInner {
1447 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1448 Self {
1449 modifier: Modifier::empty(),
1450 modifier_chain: ModifierChainHandle::new(),
1451 resolved_modifiers: ResolvedModifiers::default(),
1452 modifier_capabilities: NodeCapabilities::default(),
1453 state: SubcomposeState::default(),
1454 measure_policy,
1455 children: Vec::new(),
1456 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1457 debug_modifiers: false,
1458 virtual_nodes: HashMap::new(),
1459 last_placements: Vec::new(),
1460 placement_scratch: Vec::new(),
1461 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1462 captured_context: None,
1463 }
1464 }
1465
1466 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1467 self.measure_policy = policy;
1468 if let Err(err) = self.slots.reset() {
1473 log::error!(
1474 "failed to reset root measurement slots after measure policy update: {err}"
1475 );
1476 }
1477 }
1478
1479 fn set_modifier_collect(&mut self, modifier: Modifier) -> (Vec<ModifierInvalidation>, bool) {
1482 let modifier_changed = !self.modifier.structural_eq(&modifier);
1483 self.modifier = modifier;
1484 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1485 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1486 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1487 self.modifier_capabilities = self.modifier_chain.capabilities();
1488
1489 let mut invalidations = self.modifier_chain.take_invalidations();
1491 invalidations.extend(modifier_local_invalidations);
1492
1493 (invalidations, modifier_changed)
1494 }
1495
1496 fn set_debug_modifiers(&mut self, enabled: bool) {
1497 self.debug_modifiers = enabled;
1498 self.modifier_chain.set_debug_logging(enabled);
1499 }
1500}
1501
1502#[cfg(test)]
1503#[path = "tests/subcompose_layout_tests.rs"]
1504mod tests;