1use crate::collections::map::HashMap;
2use crate::{
3 composer_context, empty_local_stack, hash_key, runtime, Applier, ApplierHost, ChildList,
4 Command, CommandQueue, CompositionLocal, DirtyBubble, Key, LocalKey, LocalStackSnapshot,
5 LocalStateEntry, MutableState, Node, NodeError, NodeId, Owned, ProvidedValue, RecomposeOptions,
6 RecomposeScope, RecycledNode, RuntimeHandle, SlotId, SlotPassOutcome, SlotTable, SlotsHost,
7 SnapshotStateList, SnapshotStateMap, SnapshotStateObserver, StartScopedGroup,
8 StaticCompositionLocal, StaticLocalEntry, SubcomposeState, COMMAND_FLUSH_THRESHOLD,
9};
10use smallvec::SmallVec;
11use std::any::Any;
12use std::cell::{Cell, RefCell, RefMut};
13use std::hash::Hash;
14use std::marker::PhantomData;
15use std::rc::Rc;
16
17#[derive(Clone, Copy)]
18pub struct ValueSlotHandle<'pass> {
19 index: usize,
20 _pass: PhantomData<&'pass Composer>,
21}
22
23impl ValueSlotHandle<'_> {
24 pub(crate) fn new(index: usize) -> Self {
25 Self {
26 index,
27 _pass: PhantomData,
28 }
29 }
30
31 pub(crate) fn index(self) -> usize {
32 self.index
33 }
34}
35
36pub(crate) struct ParentFrame {
37 pub(crate) id: NodeId,
38 pub(crate) previous: ChildList,
39 pub(crate) new_children: ChildList,
40 pub(crate) attach_mode: ParentAttachMode,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub(crate) enum ParentAttachMode {
45 ImmediateAppend,
46 DeferredSync,
47}
48
49#[derive(Default)]
50pub(crate) struct SubcomposeFrame {
51 pub(crate) nodes: Vec<NodeId>,
52 pub(crate) scopes: Vec<RecomposeScope>,
53}
54
55#[derive(Default, Clone)]
56pub(crate) struct LocalContext {
57 pub(crate) values: HashMap<LocalKey, Rc<dyn Any>>,
58}
59
60pub(crate) struct ComposerCore {
61 pub(crate) slots: Rc<SlotsHost>,
62 slot_hosts: RefCell<Vec<Rc<SlotsHost>>>,
63 pub(crate) applier: Rc<dyn ApplierHost>,
64 pub(crate) runtime: RuntimeHandle,
65 pub(crate) observer: SnapshotStateObserver,
66 pub(crate) parent_stack: RefCell<Vec<ParentFrame>>,
67 pub(crate) subcompose_stack: RefCell<Vec<SubcomposeFrame>>,
68 pub(crate) root: Cell<Option<NodeId>>,
69 pub(crate) commands: RefCell<CommandQueue>,
70 pub(crate) scope_stack: RefCell<Vec<RecomposeScope>>,
71 pub(crate) local_stack: RefCell<LocalStackSnapshot>,
72 pub(crate) side_effects: RefCell<Vec<Box<dyn FnOnce()>>>,
73 pub(crate) pending_scope_options: RefCell<Option<RecomposeOptions>>,
74 pub(crate) phase: Cell<crate::Phase>,
75 pub(crate) last_node_reused: Cell<Option<bool>>,
76 pub(crate) recranpose_parent_hint: Cell<Option<NodeId>>,
77 pub(crate) root_render_requested: Cell<bool>,
78 pub(crate) _not_send: PhantomData<*const ()>,
79}
80
81impl ComposerCore {
82 pub(crate) fn new(
83 slots: Rc<SlotsHost>,
84 applier: Rc<dyn ApplierHost>,
85 runtime: RuntimeHandle,
86 observer: SnapshotStateObserver,
87 root: Option<NodeId>,
88 ) -> Self {
89 let parent_stack = if let Some(root_id) = root {
90 vec![ParentFrame {
91 id: root_id,
92 previous: ChildList::new(),
93 new_children: ChildList::new(),
94 attach_mode: ParentAttachMode::DeferredSync,
95 }]
96 } else {
97 Vec::new()
98 };
99
100 Self {
101 slots,
102 slot_hosts: RefCell::new(Vec::new()),
103 applier,
104 runtime,
105 observer,
106 parent_stack: RefCell::new(parent_stack),
107 subcompose_stack: RefCell::new(Vec::new()),
108 root: Cell::new(root),
109 commands: RefCell::new(CommandQueue::default()),
110 scope_stack: RefCell::new(Vec::new()),
111 local_stack: RefCell::new(empty_local_stack()),
112 side_effects: RefCell::new(Vec::new()),
113 pending_scope_options: RefCell::new(None),
114 phase: Cell::new(crate::Phase::Compose),
115 last_node_reused: Cell::new(None),
116 recranpose_parent_hint: Cell::new(None),
117 root_render_requested: Cell::new(false),
118 _not_send: PhantomData,
119 }
120 }
121}
122
123#[derive(Clone)]
124pub struct Composer {
125 pub(crate) core: Rc<ComposerCore>,
126}
127
128pub(crate) enum EmittedNode {
129 Fresh(Box<dyn Node>),
130 Recycled(RecycledNode),
131}
132
133impl Composer {
134 pub fn new(
135 slots: Rc<SlotsHost>,
136 applier: Rc<dyn ApplierHost>,
137 runtime: RuntimeHandle,
138 observer: SnapshotStateObserver,
139 root: Option<NodeId>,
140 ) -> Self {
141 let core = Rc::new(ComposerCore::new(slots, applier, runtime, observer, root));
142 Self { core }
143 }
144
145 pub(crate) fn from_core(core: Rc<ComposerCore>) -> Self {
146 Self { core }
147 }
148
149 pub(crate) fn clone_core(&self) -> Rc<ComposerCore> {
150 Rc::clone(&self.core)
151 }
152
153 fn observer(&self) -> SnapshotStateObserver {
154 self.core.observer.clone()
155 }
156
157 pub(crate) fn request_root_render(&self) {
158 self.core.root_render_requested.set(true);
159 }
160
161 pub(crate) fn take_root_render_request(&self) -> bool {
162 self.core.root_render_requested.replace(false)
163 }
164
165 pub(crate) fn observe_scope<R>(&self, scope: &RecomposeScope, block: impl FnOnce() -> R) -> R {
166 let observer = self.observer();
167 let scope_clone = scope.clone();
168 observer.observe_reads(scope_clone, move |scope_ref| scope_ref.invalidate(), block)
169 }
170
171 fn active_slots_host(&self) -> Rc<SlotsHost> {
172 self.core
173 .slot_hosts
174 .borrow()
175 .last()
176 .cloned()
177 .unwrap_or_else(|| Rc::clone(&self.core.slots))
178 }
179
180 pub(crate) fn with_slots<R>(&self, f: impl FnOnce(&SlotTable) -> R) -> R {
181 let host = self.active_slots_host();
182 let slots = host.borrow();
183 f(&slots)
184 }
185
186 pub(crate) fn with_slots_mut<R>(&self, f: impl FnOnce(&mut SlotTable) -> R) -> R {
187 let host = self.active_slots_host();
188 let mut slots = host.borrow_mut();
189 f(&mut slots)
190 }
191
192 pub(crate) fn with_slot_session_mut<R>(
193 &self,
194 f: impl FnOnce(&mut crate::slot_table::SlotWriteSession<'_>) -> R,
195 ) -> R {
196 self.active_slots_host().with_write_session(f)
197 }
198
199 pub(crate) fn with_slot_host_pass<R>(
200 &self,
201 slots: Rc<SlotsHost>,
202 mode: crate::slot_table::SlotPassMode,
203 f: impl FnOnce(&Composer) -> R,
204 ) -> (R, SlotPassOutcome) {
205 slots.begin_pass(mode);
206 self.core.slot_hosts.borrow_mut().push(Rc::clone(&slots));
207
208 struct Guard {
209 core: Rc<ComposerCore>,
210 host: Rc<SlotsHost>,
211 outcome: Rc<RefCell<Option<SlotPassOutcome>>>,
212 }
213 impl Drop for Guard {
214 fn drop(&mut self) {
215 let host = self
216 .core
217 .slot_hosts
218 .borrow_mut()
219 .pop()
220 .expect("slot host underflow");
221 debug_assert!(Rc::ptr_eq(&host, &self.host));
222
223 let outcome = {
224 let mut applier = self.core.applier.borrow_dyn();
225 self.host.finish_pass(&mut *applier)
226 };
227
228 if outcome.compacted {
229 self.core.applier.compact();
230 self.core.applier.borrow_dyn().clear_recycled_nodes();
231 }
232
233 *self.outcome.borrow_mut() = Some(outcome);
234 }
235 }
236 let outcome = Rc::new(RefCell::new(None));
237 let guard = Guard {
238 core: self.clone_core(),
239 host: Rc::clone(&slots),
240 outcome: Rc::clone(&outcome),
241 };
242 let result = f(self);
243 drop(guard);
244 let outcome = outcome
245 .borrow_mut()
246 .take()
247 .expect("slot pass should produce an outcome");
248 (result, outcome)
249 }
250
251 pub(crate) fn with_slot_override<R>(
252 &self,
253 slots: Rc<SlotsHost>,
254 f: impl FnOnce(&Composer) -> R,
255 ) -> (R, SlotPassOutcome) {
256 self.with_slot_host_pass(slots, crate::slot_table::SlotPassMode::Compose, f)
257 }
258
259 pub(crate) fn parent_stack(&self) -> RefMut<'_, Vec<ParentFrame>> {
260 self.core.parent_stack.borrow_mut()
261 }
262
263 pub(crate) fn subcompose_stack(&self) -> RefMut<'_, Vec<SubcomposeFrame>> {
264 self.core.subcompose_stack.borrow_mut()
265 }
266
267 pub(crate) fn commands_mut(&self) -> RefMut<'_, CommandQueue> {
268 self.core.commands.borrow_mut()
269 }
270
271 pub(crate) fn enqueue_semantics_invalidation(&self, id: NodeId) {
272 self.commands_mut().push(Command::BubbleDirty {
273 node_id: id,
274 bubble: DirtyBubble::SEMANTICS,
275 });
276 }
277
278 pub(crate) fn scope_stack(&self) -> RefMut<'_, Vec<RecomposeScope>> {
279 self.core.scope_stack.borrow_mut()
280 }
281
282 pub(crate) fn local_stack(&self) -> RefMut<'_, LocalStackSnapshot> {
283 self.core.local_stack.borrow_mut()
284 }
285
286 pub(crate) fn current_local_stack(&self) -> LocalStackSnapshot {
287 self.core.local_stack.borrow().clone()
288 }
289
290 pub(crate) fn side_effects_mut(&self) -> RefMut<'_, Vec<Box<dyn FnOnce()>>> {
291 self.core.side_effects.borrow_mut()
292 }
293
294 fn pending_scope_options(&self) -> RefMut<'_, Option<RecomposeOptions>> {
295 self.core.pending_scope_options.borrow_mut()
296 }
297
298 pub(crate) fn borrow_applier(&self) -> RefMut<'_, dyn Applier> {
299 self.core.applier.borrow_dyn()
300 }
301
302 pub fn register_virtual_node(
309 &self,
310 node_id: NodeId,
311 node: Box<dyn Node>,
312 ) -> Result<(), NodeError> {
313 let mut applier = self.borrow_applier();
314 applier.insert_with_id(node_id, node)
315 }
316
317 pub fn node_has_no_parent(&self, node_id: NodeId) -> bool {
320 let mut applier = self.borrow_applier();
321 match applier.get_mut(node_id) {
322 Ok(node) => node.parent().is_none(),
323 Err(_) => true,
324 }
325 }
326
327 pub fn get_node_children(&self, node_id: NodeId) -> SmallVec<[NodeId; 8]> {
332 let mut applier = self.borrow_applier();
333 match applier.get_mut(node_id) {
334 Ok(node) => {
335 let mut children = SmallVec::<[NodeId; 8]>::new();
336 node.collect_children_into(&mut children);
337 children
338 }
339 Err(_) => SmallVec::<[NodeId; 8]>::new(),
340 }
341 }
342
343 pub fn record_subcompose_child(&self, child_id: NodeId) {
353 let mut parent_stack = self.parent_stack();
354 if let Some(frame) = parent_stack.last_mut() {
355 if matches!(frame.attach_mode, ParentAttachMode::DeferredSync)
356 && !frame.new_children.contains(&child_id)
357 {
358 frame.new_children.push(child_id);
359 }
360 }
361 }
362
363 pub fn clear_node_children(&self, node_id: NodeId) {
369 let mut applier = self.borrow_applier();
370 if let Ok(node) = applier.get_mut(node_id) {
371 node.update_children(&[]);
372 }
373 }
374
375 pub fn install<R>(&self, f: impl FnOnce(&Composer) -> R) -> R {
376 let _composer_guard = composer_context::enter(self);
377 runtime::push_active_runtime(&self.core.runtime);
378 struct Guard;
379 impl Drop for Guard {
380 fn drop(&mut self) {
381 runtime::pop_active_runtime();
382 }
383 }
384 let guard = Guard;
385 let result = f(self);
386 drop(guard);
387 result
388 }
389
390 pub(crate) fn flush_pending_commands_if_large(&self) {
391 let queued = self.core.commands.borrow().len();
392 if queued < COMMAND_FLUSH_THRESHOLD {
393 return;
394 }
395 self.apply_pending_commands()
396 .expect("mid-composition command flush failed");
397 }
398
399 fn with_group_in_active_pass<R>(&self, key: Key, f: impl FnOnce(&Composer) -> R) -> R {
400 struct GroupGuard {
401 composer: Composer,
402 scope: RecomposeScope,
403 }
404
405 impl Drop for GroupGuard {
406 fn drop(&mut self) {
407 let trimmed = self
408 .composer
409 .with_slot_session_mut(|slots| slots.finalize_current_group());
410 if trimmed {
411 self.scope.force_recompose();
412 }
413 self.composer.scope_stack().pop();
414 self.scope.mark_recomposed();
415 self.composer
416 .with_slot_session_mut(|slots| slots.end_group());
417 self.composer.flush_pending_commands_if_large();
418 }
419 }
420
421 let parent_scope = self.current_recranpose_scope();
422 let (group_anchor, scope_ref, requires_recompose) = self.with_slot_session_mut(|slots| {
423 let StartScopedGroup {
424 anchor,
425 scope,
426 requires_recompose,
427 ..
428 } = slots.begin_scoped_group(key, || RecomposeScope::new(self.runtime_handle()));
429 (anchor, scope, requires_recompose)
430 });
431
432 scope_ref.reactivate();
433 scope_ref.set_group_anchor(group_anchor);
434 scope_ref.set_parent_scope(parent_scope);
435
436 if let Some(options) = self.pending_scope_options().take() {
437 if options.force_recompose {
438 scope_ref.force_recompose();
439 } else if options.force_reuse {
440 scope_ref.force_reuse();
441 }
442 }
443 if requires_recompose {
444 scope_ref.force_recompose();
445 }
446
447 let slots_host = self.active_slots_host();
448 scope_ref.set_slots_host(Rc::downgrade(&slots_host));
449
450 {
451 let mut stack = self.scope_stack();
452 stack.push(scope_ref.clone());
453 }
454
455 {
456 let mut stack = self.subcompose_stack();
457 if let Some(frame) = stack.last_mut() {
458 frame.scopes.push(scope_ref.clone());
459 }
460 }
461
462 scope_ref.snapshot_locals(self.current_local_stack());
463 {
464 let parent_hint = self.parent_stack().last().map(|frame| frame.id);
465 scope_ref.set_parent_hint(parent_hint);
466 }
467
468 let guard = GroupGuard {
469 composer: self.clone(),
470 scope: scope_ref.clone(),
471 };
472 let result = self.observe_scope(&scope_ref, || f(self));
473 scope_ref.mark_composed_once();
474 drop(guard);
475 result
476 }
477
478 pub fn with_group<R>(&self, key: Key, f: impl FnOnce(&Composer) -> R) -> R {
479 let host = self.active_slots_host();
480 if host.has_active_pass() {
481 return self.with_group_in_active_pass(key, f);
482 }
483 let (result, _) =
484 self.with_slot_host_pass(host, crate::slot_table::SlotPassMode::Compose, |composer| {
485 composer.with_group_in_active_pass(key, f)
486 });
487 result
488 }
489
490 pub fn cranpose_with_reuse<R>(
491 &self,
492 key: Key,
493 options: RecomposeOptions,
494 f: impl FnOnce(&Composer) -> R,
495 ) -> R {
496 self.pending_scope_options().replace(options);
497 self.with_group(key, f)
498 }
499
500 pub fn with_key<K: Hash, R>(&self, key: &K, f: impl FnOnce(&Composer) -> R) -> R {
501 let hashed = hash_key(key);
502 self.with_group(hashed, f)
503 }
504
505 pub fn remember<T: 'static>(&self, init: impl FnOnce() -> T) -> Owned<T> {
506 self.with_slot_session_mut(|slots| slots.remember(init))
507 }
508
509 pub fn use_value_slot<'pass, T: 'static>(
510 &'pass self,
511 init: impl FnOnce() -> T,
512 ) -> ValueSlotHandle<'pass> {
513 let index = self.with_slot_session_mut(|slots| slots.use_value_slot(init));
514 ValueSlotHandle::new(index)
515 }
516
517 pub fn with_slot_value<'pass, T: 'static, R>(
518 &'pass self,
519 handle: ValueSlotHandle<'pass>,
520 f: impl FnOnce(&T) -> R,
521 ) -> R {
522 self.with_slots(|slots| f(slots.read_value(handle.index())))
523 }
524
525 pub fn with_slot_value_mut<'pass, T: 'static, R>(
526 &'pass self,
527 handle: ValueSlotHandle<'pass>,
528 f: impl FnOnce(&mut T) -> R,
529 ) -> R {
530 self.with_slots_mut(|slots| f(slots.read_value_mut(handle.index())))
531 }
532
533 pub fn write_slot_value<'pass, T: 'static>(
534 &'pass self,
535 handle: ValueSlotHandle<'pass>,
536 value: T,
537 ) {
538 self.with_slots_mut(|slots| slots.write_value(handle.index(), value));
539 }
540
541 pub fn mutable_state_of<T: Clone + 'static>(&self, initial: T) -> MutableState<T> {
542 MutableState::with_runtime(initial, self.runtime_handle())
543 }
544
545 pub fn mutable_state_list_of<T, I>(&self, values: I) -> SnapshotStateList<T>
546 where
547 T: Clone + 'static,
548 I: IntoIterator<Item = T>,
549 {
550 SnapshotStateList::with_runtime(values, self.runtime_handle())
551 }
552
553 pub fn mutable_state_map_of<K, V, I>(&self, pairs: I) -> SnapshotStateMap<K, V>
554 where
555 K: Clone + Eq + Hash + 'static,
556 V: Clone + 'static,
557 I: IntoIterator<Item = (K, V)>,
558 {
559 SnapshotStateMap::with_runtime(pairs, self.runtime_handle())
560 }
561
562 pub fn read_composition_local<T: Clone + 'static>(&self, local: &CompositionLocal<T>) -> T {
563 let stack = self.core.local_stack.borrow();
564 for context in stack.iter().rev() {
565 if let Some(entry) = context.values.get(&local.key) {
566 let typed = entry
567 .clone()
568 .downcast::<LocalStateEntry<T>>()
569 .expect("composition local type mismatch");
570 return typed.value();
571 }
572 }
573 local.default_value()
574 }
575
576 pub fn read_static_composition_local<T: Clone + 'static>(
577 &self,
578 local: &StaticCompositionLocal<T>,
579 ) -> T {
580 let stack = self.core.local_stack.borrow();
581 for context in stack.iter().rev() {
582 if let Some(entry) = context.values.get(&local.key) {
583 let typed = entry
584 .clone()
585 .downcast::<StaticLocalEntry<T>>()
586 .expect("static composition local type mismatch");
587 return typed.value();
588 }
589 }
590 local.default_value()
591 }
592
593 pub fn current_recranpose_scope(&self) -> Option<RecomposeScope> {
594 self.core.scope_stack.borrow().last().cloned()
595 }
596
597 pub fn phase(&self) -> crate::Phase {
598 self.core.phase.get()
599 }
600
601 pub(crate) fn set_phase(&self, phase: crate::Phase) {
602 self.core.phase.set(phase);
603 }
604
605 pub fn enter_phase(&self, phase: crate::Phase) {
606 self.set_phase(phase);
607 }
608
609 pub(crate) fn subcompose<R>(
610 &self,
611 state: &mut SubcomposeState,
612 slot_id: SlotId,
613 content: impl FnOnce(&Composer) -> R,
614 ) -> (R, Vec<NodeId>) {
615 match self.phase() {
616 crate::Phase::Measure | crate::Phase::Layout => {}
617 current => panic!(
618 "subcompose() may only be called during measure or layout; current phase: {:?}",
619 current
620 ),
621 }
622
623 self.subcompose_stack().push(SubcomposeFrame::default());
624 struct StackGuard {
625 core: Rc<ComposerCore>,
626 leaked: bool,
627 }
628 impl Drop for StackGuard {
629 fn drop(&mut self) {
630 if !self.leaked {
631 self.core.subcompose_stack.borrow_mut().pop();
632 }
633 }
634 }
635 let mut guard = StackGuard {
636 core: self.clone_core(),
637 leaked: false,
638 };
639
640 let slot_host = state.get_or_create_slots(slot_id);
641 let (result, _) = self.with_slot_override(slot_host.clone(), |composer| {
642 composer.with_group(slot_id.raw(), |composer| content(composer))
643 });
644
645 let frame = {
646 let mut stack = guard.core.subcompose_stack.borrow_mut();
647 let frame = stack.pop().expect("subcompose stack underflow");
648 guard.leaked = true;
649 frame
650 };
651 let nodes = frame.nodes;
652 let scopes = frame.scopes;
653 state.register_active(slot_id, &nodes, &scopes);
654 (result, nodes)
655 }
656
657 pub fn subcompose_measurement<R>(
658 &self,
659 state: &mut SubcomposeState,
660 slot_id: SlotId,
661 content: impl FnOnce(&Composer) -> R,
662 ) -> (R, Vec<NodeId>) {
663 let (result, nodes) = self.subcompose(state, slot_id, content);
664 let roots = nodes
665 .into_iter()
666 .filter(|&id| self.node_has_no_parent(id))
667 .collect();
668
669 (result, roots)
670 }
671
672 pub fn subcompose_in<R>(
673 &self,
674 slots: &Rc<SlotsHost>,
675 root: Option<NodeId>,
676 f: impl FnOnce(&Composer) -> R,
677 ) -> Result<R, NodeError> {
678 let runtime_handle = self.runtime_handle();
679 let phase = self.phase();
680 let locals = self.current_local_stack();
681 let core = Rc::new(ComposerCore::new(
682 Rc::clone(slots),
683 Rc::clone(&self.core.applier),
684 runtime_handle.clone(),
685 self.observer(),
686 root,
687 ));
688 core.phase.set(phase);
689 *core.local_stack.borrow_mut() = locals;
690 let composer = Composer::from_core(core);
691 let (result, commands, side_effects) = composer.install(|composer| {
692 let (output, _) = composer.with_slot_host_pass(
693 Rc::clone(slots),
694 crate::slot_table::SlotPassMode::Compose,
695 |composer| f(composer),
696 );
697 let commands = composer.take_commands();
698 let side_effects = composer.take_side_effects();
699 (output, commands, side_effects)
700 });
701 {
702 let mut applier = self.borrow_applier();
703 commands.apply(&mut *applier)?;
704 for update in runtime_handle.take_updates() {
705 update.apply(&mut *applier)?;
706 }
707 }
708 runtime_handle.drain_ui();
709 for effect in side_effects {
710 effect();
711 }
712 runtime_handle.drain_ui();
713 Ok(result)
714 }
715
716 pub fn subcompose_slot<R>(
721 &self,
722 slots: &Rc<SlotsHost>,
723 root: Option<NodeId>,
724 f: impl FnOnce(&Composer) -> R,
725 ) -> Result<(R, Vec<RecomposeScope>), NodeError> {
726 let runtime_handle = self.runtime_handle();
727 let phase = self.phase();
728 let locals = self.current_local_stack();
729 let core = Rc::new(ComposerCore::new(
730 Rc::clone(slots),
731 Rc::clone(&self.core.applier),
732 runtime_handle.clone(),
733 self.observer(),
734 root,
735 ));
736 core.phase.set(phase);
737 *core.local_stack.borrow_mut() = locals;
738 let composer = Composer::from_core(core);
739 composer.subcompose_stack().push(SubcomposeFrame::default());
740 struct StackGuard {
741 core: Rc<ComposerCore>,
742 leaked: bool,
743 }
744 impl Drop for StackGuard {
745 fn drop(&mut self) {
746 if !self.leaked {
747 self.core.subcompose_stack.borrow_mut().pop();
748 }
749 }
750 }
751 let mut guard = StackGuard {
752 core: composer.clone_core(),
753 leaked: false,
754 };
755 let root_group_key = crate::location_key(file!(), line!(), column!());
756 let (result, commands, side_effects) = composer.install(|composer| {
757 let (output, _) = composer.with_slot_host_pass(
758 Rc::clone(slots),
759 crate::slot_table::SlotPassMode::Compose,
760 |composer| {
761 let output = composer.with_group(root_group_key, |composer| f(composer));
762 if root.is_some() {
763 composer.pop_parent();
764 }
765 output
766 },
767 );
768 let commands = composer.take_commands();
769 let side_effects = composer.take_side_effects();
770 (output, commands, side_effects)
771 });
772 let frame = {
773 let mut stack = guard.core.subcompose_stack.borrow_mut();
774 let frame = stack.pop().expect("subcompose stack underflow");
775 guard.leaked = true;
776 frame
777 };
778
779 {
780 let mut applier = self.borrow_applier();
781 commands.apply(&mut *applier)?;
782 for update in runtime_handle.take_updates() {
783 update.apply(&mut *applier)?;
784 }
785 }
786 runtime_handle.drain_ui();
787 for effect in side_effects {
788 effect();
789 }
790 runtime_handle.drain_ui();
791 Ok((result, frame.scopes))
792 }
793
794 pub(crate) fn skipped_group_root_nodes(&self, nodes: &[NodeId]) -> Vec<NodeId> {
795 let node_set: std::collections::HashSet<NodeId> = nodes.iter().copied().collect();
796 let mut applier = self.borrow_applier();
797 nodes
798 .iter()
799 .copied()
800 .filter(|id| {
801 let parent = applier.get_mut(*id).ok().and_then(|node| node.parent());
802 parent.is_none_or(|parent_id| !node_set.contains(&parent_id))
803 })
804 .collect()
805 }
806
807 pub fn skip_current_group(&self) {
808 let nodes = self.with_slot_session_mut(|slots| slots.nodes_in_current_group());
809 let root_nodes = self.skipped_group_root_nodes(&nodes);
810 self.with_slot_session_mut(|slots| slots.skip_current_group());
811 for id in root_nodes {
812 self.attach_to_parent_with_mode(id, true);
813 }
814 }
815
816 pub fn runtime_handle(&self) -> RuntimeHandle {
817 self.core.runtime.clone()
818 }
819
820 pub fn set_recranpose_callback<F>(&self, callback: F)
821 where
822 F: FnMut(&Composer) + 'static,
823 {
824 if let Some(scope) = self.current_recranpose_scope() {
825 let observer = self.observer();
826 let scope_weak = scope.downgrade();
827 let mut callback = callback;
828 scope.set_recompose(Box::new(move |composer: &Composer| {
829 if let Some(inner) = scope_weak.upgrade() {
830 let scope_instance = RecomposeScope { inner };
831 observer.observe_reads(
832 scope_instance.clone(),
833 move |scope_ref| scope_ref.invalidate(),
834 || {
835 callback(composer);
836 },
837 );
838 }
839 }));
840 }
841 }
842
843 pub fn set_recranpose_fn(&self, callback: fn(&Composer)) {
844 if let Some(scope) = self.current_recranpose_scope() {
845 scope.set_recompose_fn(callback);
846 }
847 }
848
849 pub fn with_composition_locals<R>(
850 &self,
851 provided: Vec<ProvidedValue>,
852 f: impl FnOnce(&Composer) -> R,
853 ) -> R {
854 if provided.is_empty() {
855 return f(self);
856 }
857 let mut context = LocalContext::default();
858 for value in provided {
859 let (key, entry) = value.into_entry(self);
860 context.values.insert(key, entry);
861 }
862 {
863 let mut stack = self.local_stack();
864 Rc::make_mut(&mut *stack).push(context);
865 }
866 let result = f(self);
867 {
868 let mut stack = self.local_stack();
869 Rc::make_mut(&mut *stack).pop();
870 }
871 result
872 }
873}