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 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_captured_locals(&mut self, locals: cranpose_core::CapturedCompositionLocals) {
778 self.inner.borrow_mut().captured_locals = Some(locals);
779 }
780
781 pub fn set_modifier(&mut self, modifier: Modifier) {
782 let prev_caps = self.modifier_capabilities();
784 let (invalidations, modifier_changed) = {
786 let mut inner = self.inner.borrow_mut();
787 inner.set_modifier_collect(modifier)
788 };
789 self.dispatch_modifier_invalidations(&invalidations, prev_caps);
792 self.update_modifier_slices_cache();
793 if modifier_changed {
794 self.request_semantics_update();
795 }
796 }
797
798 fn update_modifier_slices_cache(&self) {
800 let inner = self.inner.borrow();
801 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
802 collect_modifier_slices_into(inner.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
803 self.modifier_slices_dirty.set(false);
804 }
805
806 pub(crate) fn mark_modifier_slices_dirty(&self) {
807 self.modifier_slices_dirty.set(true);
808 }
809
810 pub fn set_debug_modifiers(&mut self, enabled: bool) {
811 self.inner.borrow_mut().set_debug_modifiers(enabled);
812 }
813
814 pub fn modifier(&self) -> Modifier {
815 self.handle().modifier()
816 }
817
818 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
819 self.inner.borrow().resolved_modifiers
820 }
821
822 pub fn layout_state(&self) -> LayoutState {
824 self.layout_state.borrow().clone()
825 }
826
827 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
828 self.cache_handles.clone()
829 }
830
831 pub fn set_position(&self, position: Point) {
833 let mut state = self.layout_state.borrow_mut();
834 state.position = position;
835 state.is_placed = true;
836 }
837
838 pub fn set_measured_size(&self, size: Size) {
840 let mut state = self.layout_state.borrow_mut();
841 state.size = size;
842 }
843
844 pub fn clear_placed(&self) {
846 self.layout_state.borrow_mut().is_placed = false;
847 }
848
849 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
851 if self.modifier_slices_dirty.get() {
852 self.update_modifier_slices_cache();
853 }
854 self.modifier_slices_snapshot.borrow().clone()
855 }
856
857 pub fn state(&self) -> Ref<'_, SubcomposeState> {
858 Ref::map(self.inner.borrow(), |inner| &inner.state)
859 }
860
861 pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
862 RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
863 }
864
865 pub fn invalidate_subcomposition(&self) {
866 self.inner.borrow().state.invalidate_scopes();
867 self.mark_needs_measure();
868 if let Some(id) = self.id.get() {
869 cranpose_core::bubble_measure_dirty_in_composer(id);
870 }
871 }
872
873 pub fn request_measure_recompose(&self) {
874 self.mark_needs_measure();
875 if let Some(id) = self.id.get() {
876 cranpose_core::bubble_measure_dirty_in_composer(id);
877 }
878 }
879
880 pub fn active_children(&self) -> Vec<NodeId> {
881 current_subcompose_children(&self.inner.borrow())
882 }
883
884 pub fn mark_needs_measure(&self) {
886 self.needs_measure.set(true);
887 self.needs_layout.set(true);
888 }
889
890 pub fn mark_needs_layout_flag(&self) {
892 self.needs_layout.set(true);
893 }
894
895 pub fn mark_needs_redraw(&self) {
897 self.needs_redraw.set(true);
898 if let Some(id) = self.id.get() {
899 crate::schedule_draw_repass(id);
900 }
901 crate::request_render_invalidation();
902 }
903
904 pub fn needs_measure(&self) -> bool {
906 self.needs_measure.get()
907 }
908
909 pub(crate) fn clear_needs_measure(&self) {
910 self.needs_measure.set(false);
911 }
912
913 pub(crate) fn clear_needs_layout(&self) {
914 self.needs_layout.set(false);
915 }
916
917 pub fn mark_needs_semantics(&self) {
919 self.needs_semantics.set(true);
920 }
921
922 pub fn needs_semantics_flag(&self) -> bool {
924 self.needs_semantics.get()
925 }
926
927 pub(crate) fn clear_needs_semantics(&self) {
928 self.needs_semantics.set(false);
929 }
930
931 #[cfg(test)]
932 pub(crate) fn clear_needs_semantics_for_tests(&self) {
933 self.clear_needs_semantics();
934 }
935
936 pub fn needs_redraw(&self) -> bool {
938 self.needs_redraw.get()
939 }
940
941 pub fn clear_needs_redraw(&self) {
942 self.needs_redraw.set(false);
943 }
944
945 pub fn mark_needs_pointer_pass(&self) {
947 self.needs_pointer_pass.set(true);
948 }
949
950 pub fn needs_pointer_pass(&self) -> bool {
952 self.needs_pointer_pass.get()
953 }
954
955 pub fn clear_needs_pointer_pass(&self) {
957 self.needs_pointer_pass.set(false);
958 }
959
960 pub fn mark_needs_focus_sync(&self) {
962 self.needs_focus_sync.set(true);
963 }
964
965 pub fn needs_focus_sync(&self) -> bool {
967 self.needs_focus_sync.get()
968 }
969
970 pub fn clear_needs_focus_sync(&self) {
972 self.needs_focus_sync.set(false);
973 }
974
975 fn request_semantics_update(&self) {
976 let already_dirty = self.needs_semantics.replace(true);
977 if already_dirty {
978 return;
979 }
980
981 if let Some(id) = self.id.get() {
982 cranpose_core::queue_semantics_invalidation(id);
983 }
984 }
985
986 pub fn modifier_capabilities(&self) -> NodeCapabilities {
988 self.inner.borrow().modifier_capabilities
989 }
990
991 pub fn has_layout_modifier_nodes(&self) -> bool {
992 self.modifier_capabilities()
993 .contains(NodeCapabilities::LAYOUT)
994 }
995
996 pub fn has_draw_modifier_nodes(&self) -> bool {
997 self.modifier_capabilities()
998 .contains(NodeCapabilities::DRAW)
999 }
1000
1001 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1002 self.modifier_capabilities()
1003 .contains(NodeCapabilities::POINTER_INPUT)
1004 }
1005
1006 pub fn has_semantics_modifier_nodes(&self) -> bool {
1007 self.modifier_capabilities()
1008 .contains(NodeCapabilities::SEMANTICS)
1009 }
1010
1011 pub fn has_focus_modifier_nodes(&self) -> bool {
1012 self.modifier_capabilities()
1013 .contains(NodeCapabilities::FOCUS)
1014 }
1015
1016 fn dispatch_modifier_invalidations(
1023 &self,
1024 invalidations: &[ModifierInvalidation],
1025 prev_caps: NodeCapabilities,
1026 ) {
1027 let curr_caps = self.modifier_capabilities();
1028 for invalidation in invalidations {
1029 self.modifier_slices_dirty.set(true);
1030 let invalidation_caps = invalidation.capabilities();
1031 let has_capability = |capability| {
1032 curr_caps.contains(capability)
1033 || prev_caps.contains(capability)
1034 || invalidation_caps.contains(capability)
1035 };
1036 match invalidation.kind() {
1037 InvalidationKind::Layout => {
1038 if has_capability(NodeCapabilities::LAYOUT) {
1039 self.mark_needs_measure();
1040 }
1041 }
1042 InvalidationKind::Draw => {
1043 if has_capability(NodeCapabilities::DRAW) {
1044 self.mark_needs_redraw();
1045 }
1046 }
1047 InvalidationKind::PointerInput => {
1048 if has_capability(NodeCapabilities::POINTER_INPUT) {
1049 self.mark_needs_pointer_pass();
1050 crate::request_pointer_invalidation();
1051 if let Some(id) = self.id.get() {
1053 crate::schedule_pointer_repass(id);
1054 }
1055 }
1056 }
1057 InvalidationKind::Semantics => {
1058 self.request_semantics_update();
1059 }
1060 InvalidationKind::Focus => {
1061 if has_capability(NodeCapabilities::FOCUS) {
1062 self.mark_needs_focus_sync();
1063 crate::request_focus_invalidation();
1064 if let Some(id) = self.id.get() {
1066 crate::schedule_focus_invalidation(id);
1067 }
1068 }
1069 }
1070 }
1071 }
1072 }
1073}
1074
1075impl cranpose_core::Node for SubcomposeLayoutNode {
1076 fn mount(&mut self) {
1077 let mut inner = self.inner.borrow_mut();
1078 let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
1079 chain.repair_chain();
1080 chain.attach_nodes(&mut *context);
1081 }
1082
1083 fn unmount(&mut self) {
1084 self.inner
1085 .borrow_mut()
1086 .modifier_chain
1087 .chain_mut()
1088 .detach_nodes();
1089 }
1090
1091 fn insert_child(&mut self, child: NodeId) {
1092 let mut inner = self.inner.borrow_mut();
1093 if inner.children.contains(&child) {
1094 return;
1095 }
1096 if is_virtual_node(child) {
1097 let count = self.virtual_children_count.get();
1098 self.virtual_children_count.set(count + 1);
1099 }
1100 inner.children.push(child);
1101 }
1102
1103 fn remove_child(&mut self, child: NodeId) {
1104 let mut inner = self.inner.borrow_mut();
1105 let before = inner.children.len();
1106 inner.children.retain(|&id| id != child);
1107 if inner.children.len() < before && is_virtual_node(child) {
1108 let count = self.virtual_children_count.get();
1109 if count > 0 {
1110 self.virtual_children_count.set(count - 1);
1111 }
1112 }
1113 }
1114
1115 fn move_child(&mut self, from: usize, to: usize) {
1116 let mut inner = self.inner.borrow_mut();
1117 if from == to || from >= inner.children.len() {
1118 return;
1119 }
1120 let child = inner.children.remove(from);
1121 let target = to.min(inner.children.len());
1122 inner.children.insert(target, child);
1123 }
1124
1125 fn update_children(&mut self, children: &[NodeId]) {
1126 let mut inner = self.inner.borrow_mut();
1127 inner.children.clear();
1128 inner.children.extend_from_slice(children);
1129 }
1130
1131 fn children(&self) -> Vec<NodeId> {
1132 current_subcompose_children(&self.inner.borrow())
1133 }
1134
1135 fn set_node_id(&mut self, id: NodeId) {
1136 self.id.set(Some(id));
1137 self.inner.borrow_mut().modifier_chain.set_node_id(Some(id));
1138 self.update_modifier_slices_cache();
1139 }
1140
1141 fn on_attached_to_parent(&mut self, parent: NodeId) {
1142 self.parent.set(Some(parent));
1143 }
1144
1145 fn on_removed_from_parent(&mut self) {
1146 self.parent.set(None);
1147 }
1148
1149 fn parent(&self) -> Option<NodeId> {
1150 self.parent.get()
1151 }
1152
1153 fn mark_needs_layout(&self) {
1154 self.needs_layout.set(true);
1155 }
1156
1157 fn needs_layout(&self) -> bool {
1158 self.needs_layout.get()
1159 }
1160
1161 fn mark_needs_measure(&self) {
1162 self.needs_measure.set(true);
1163 self.needs_layout.set(true); }
1165
1166 fn needs_measure(&self) -> bool {
1167 self.needs_measure.get()
1168 }
1169
1170 fn mark_needs_semantics(&self) {
1171 self.needs_semantics.set(true);
1172 }
1173
1174 fn needs_semantics(&self) -> bool {
1175 self.needs_semantics.get()
1176 }
1177
1178 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1180 self.parent.set(Some(parent));
1181 }
1182}
1183
1184#[derive(Clone)]
1185pub struct SubcomposeLayoutNodeHandle {
1186 inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
1187}
1188
1189impl SubcomposeLayoutNodeHandle {
1190 pub(crate) fn measured_children_scratch(
1191 &self,
1192 ) -> Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>> {
1193 let scratch = {
1194 let inner = self.inner.borrow();
1195 Rc::clone(&inner.measured_children_scratch)
1196 };
1197 scratch.borrow_mut().clear();
1198 scratch
1199 }
1200
1201 pub fn modifier(&self) -> Modifier {
1202 self.inner.borrow().modifier.clone()
1203 }
1204
1205 pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
1206 self.resolved_modifiers().layout_properties()
1207 }
1208
1209 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1210 self.inner.borrow().resolved_modifiers
1211 }
1212
1213 pub fn total_offset(&self) -> Point {
1214 self.resolved_modifiers().offset()
1215 }
1216
1217 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1218 self.inner.borrow().modifier_capabilities
1219 }
1220
1221 pub fn has_layout_modifier_nodes(&self) -> bool {
1222 self.modifier_capabilities()
1223 .contains(NodeCapabilities::LAYOUT)
1224 }
1225
1226 pub fn has_draw_modifier_nodes(&self) -> bool {
1227 self.modifier_capabilities()
1228 .contains(NodeCapabilities::DRAW)
1229 }
1230
1231 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1232 self.modifier_capabilities()
1233 .contains(NodeCapabilities::POINTER_INPUT)
1234 }
1235
1236 pub fn has_semantics_modifier_nodes(&self) -> bool {
1237 self.modifier_capabilities()
1238 .contains(NodeCapabilities::SEMANTICS)
1239 }
1240
1241 pub fn has_focus_modifier_nodes(&self) -> bool {
1242 self.modifier_capabilities()
1243 .contains(NodeCapabilities::FOCUS)
1244 }
1245
1246 pub fn set_debug_modifiers(&self, enabled: bool) {
1247 self.inner.borrow_mut().set_debug_modifiers(enabled);
1248 }
1249
1250 pub fn measure<'a>(
1251 &self,
1252 composer: &Composer,
1253 node_id: NodeId,
1254 constraints: Constraints,
1255 measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
1256 mut cached_measure_registrar: Box<dyn FnMut(NodeId, Constraints) -> Option<Size> + 'a>,
1257 error: &'a RefCell<Option<NodeError>>,
1258 ) -> Result<MeasureResult, NodeError> {
1259 self.measure_with_cached_batch(
1260 composer,
1261 node_id,
1262 constraints,
1263 CachedBatchMeasureInputs {
1264 measurer,
1265 cached_measure_batch_registrar: Box::new(
1266 move |node_ids, child_constraints, out| {
1267 out.clear();
1268 out.reserve(node_ids.len());
1269 for &child_id in node_ids {
1270 out.push(cached_measure_registrar(child_id, child_constraints));
1271 }
1272 },
1273 ),
1274 retained_measure_lookup: Box::new(|_| None),
1275 retained_measure_registrar: Box::new(|_| {}),
1276 error,
1277 },
1278 )
1279 }
1280
1281 pub(crate) fn measure_with_cached_batch<'a>(
1282 &self,
1283 composer: &Composer,
1284 node_id: NodeId,
1285 constraints: Constraints,
1286 callbacks: CachedBatchMeasureInputs<'a>,
1287 ) -> Result<MeasureResult, NodeError> {
1288 let CachedBatchMeasureInputs {
1289 measurer,
1290 cached_measure_batch_registrar,
1291 retained_measure_lookup,
1292 retained_measure_registrar,
1293 error,
1294 } = callbacks;
1295 let (policy, mut state, slots_host, placement_scratch, captured_locals) = {
1296 let mut inner = self.inner.borrow_mut();
1297 let policy = Rc::clone(&inner.measure_policy);
1298 let state = std::mem::take(&mut inner.state);
1299 let slots_host = Rc::clone(&inner.slots);
1300 let placement_scratch = std::mem::take(&mut inner.placement_scratch);
1301 let captured_locals = inner.captured_locals.clone();
1302 (
1303 policy,
1304 state,
1305 slots_host,
1306 placement_scratch,
1307 captured_locals,
1308 )
1309 };
1310 state.begin_pass();
1311
1312 let previous = composer.phase();
1313 if !matches!(previous, Phase::Measure | Phase::Layout) {
1314 composer.enter_phase(Phase::Measure);
1315 }
1316
1317 let constraints_copy = constraints;
1318 let ((result, placement_scratch), _) = composer.subcompose_slot_with_locals(
1327 &slots_host,
1328 Some(node_id),
1329 captured_locals.as_ref(),
1330 |inner_composer| {
1331 let mut scope = SubcomposeMeasureScopeImpl::new(SubcomposeMeasureScopeInit {
1332 composer: inner_composer.clone(),
1333 state: &mut state,
1334 constraints: constraints_copy,
1335 measurer,
1336 cached_measure_batch_registrar,
1337 retained_measure_lookup,
1338 retained_measure_registrar,
1339 error,
1340 parent_handle: self.clone(),
1341 root_id: node_id,
1342 placement_scratch,
1343 });
1344 let result = (policy)(&mut scope, constraints_copy);
1345 (result, scope.into_placement_scratch())
1346 },
1347 )?;
1348
1349 state.finish_pass();
1350
1351 if previous != composer.phase() {
1352 composer.enter_phase(previous);
1353 }
1354
1355 {
1356 let mut inner = self.inner.borrow_mut();
1357 inner.state = state;
1358 inner.placement_scratch = placement_scratch;
1359
1360 inner.last_placements = result.placements.iter().map(|p| p.node_id).collect();
1365 }
1366
1367 Ok(result)
1368 }
1369
1370 pub(crate) fn recycle_placement_scratch(&self, mut placements: Vec<Placement>) {
1371 placements.clear();
1372 let mut inner = self.inner.borrow_mut();
1373 if placements.capacity() > inner.placement_scratch.capacity() {
1374 inner.placement_scratch = placements;
1375 }
1376 }
1377
1378 pub fn set_active_children<I>(&self, children: I)
1379 where
1380 I: IntoIterator<Item = NodeId>,
1381 {
1382 let mut inner = self.inner.borrow_mut();
1383 inner.children.clear();
1384 inner.children.extend(children);
1385 }
1386}
1387
1388fn current_subcompose_children(inner: &SubcomposeLayoutNodeInner) -> Vec<NodeId> {
1389 if !inner.last_placements.is_empty() {
1390 inner.last_placements.clone()
1391 } else {
1392 inner.children.clone()
1393 }
1394}
1395
1396struct SubcomposeLayoutNodeInner {
1397 modifier: Modifier,
1398 modifier_chain: ModifierChainHandle,
1399 resolved_modifiers: ResolvedModifiers,
1400 modifier_capabilities: NodeCapabilities,
1401 state: SubcomposeState,
1402 measure_policy: Rc<MeasurePolicy>,
1403 children: Vec<NodeId>,
1404 slots: Rc<SlotsHost>,
1405 debug_modifiers: bool,
1406 virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
1408 last_placements: Vec<NodeId>,
1411 placement_scratch: Vec<Placement>,
1412 measured_children_scratch: Rc<RefCell<HashMap<NodeId, Rc<MeasuredNode>>>>,
1413 captured_locals: Option<cranpose_core::CapturedCompositionLocals>,
1418}
1419
1420impl SubcomposeLayoutNodeInner {
1421 fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
1422 Self {
1423 modifier: Modifier::empty(),
1424 modifier_chain: ModifierChainHandle::new(),
1425 resolved_modifiers: ResolvedModifiers::default(),
1426 modifier_capabilities: NodeCapabilities::default(),
1427 state: SubcomposeState::default(),
1428 measure_policy,
1429 children: Vec::new(),
1430 slots: Rc::new(SlotsHost::new(SlotTable::default())),
1431 debug_modifiers: false,
1432 virtual_nodes: HashMap::new(),
1433 last_placements: Vec::new(),
1434 placement_scratch: Vec::new(),
1435 measured_children_scratch: Rc::new(RefCell::new(HashMap::default())),
1436 captured_locals: None,
1437 }
1438 }
1439
1440 fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
1441 self.measure_policy = policy;
1442 if let Err(err) = self.slots.reset() {
1447 log::error!(
1448 "failed to reset root measurement slots after measure policy update: {err}"
1449 );
1450 }
1451 }
1452
1453 fn set_modifier_collect(&mut self, modifier: Modifier) -> (Vec<ModifierInvalidation>, bool) {
1456 let modifier_changed = !self.modifier.structural_eq(&modifier);
1457 self.modifier = modifier;
1458 self.modifier_chain.set_debug_logging(self.debug_modifiers);
1459 let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
1460 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
1461 self.modifier_capabilities = self.modifier_chain.capabilities();
1462
1463 let mut invalidations = self.modifier_chain.take_invalidations();
1465 invalidations.extend(modifier_local_invalidations);
1466
1467 (invalidations, modifier_changed)
1468 }
1469
1470 fn set_debug_modifiers(&mut self, enabled: bool) {
1471 self.debug_modifiers = enabled;
1472 self.modifier_chain.set_debug_logging(enabled);
1473 }
1474}
1475
1476#[cfg(test)]
1477#[path = "tests/subcompose_layout_tests.rs"]
1478mod tests;