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