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