1#![doc = r"Core runtime pieces for the Cranpose experiment."]
2
3pub extern crate self as cranpose_core;
4
5pub mod composer_context;
6mod frame_clock;
7mod launched_effect;
8pub mod owned;
9pub mod platform;
10pub mod runtime;
11pub mod snapshot_double_index_heap;
12pub mod snapshot_id_set;
13pub mod snapshot_pinning;
14pub mod snapshot_state_observer;
15pub mod snapshot_v2;
16mod snapshot_weak_set;
17mod state;
18pub mod subcompose;
19
20#[cfg(feature = "internal")]
21#[doc(hidden)]
22pub mod internal {
23 pub use crate::frame_clock::{FrameCallbackRegistration, FrameClock};
24}
25pub use launched_effect::{
26 CancelToken, LaunchedEffectScope, __launched_effect_async_impl, __launched_effect_impl,
27};
28pub use owned::Owned;
29pub use platform::{Clock, RuntimeScheduler};
30pub use runtime::{
31 current_runtime_handle, schedule_frame, schedule_node_update, DefaultScheduler, Runtime,
32 RuntimeHandle, StateId, TaskHandle,
33};
34pub use snapshot_state_observer::SnapshotStateObserver;
35
36pub fn run_in_mutable_snapshot<T>(block: impl FnOnce() -> T) -> Result<T, &'static str> {
61 let snapshot = snapshot_v2::take_mutable_snapshot(None, None);
62
63 IN_APPLIED_SNAPSHOT.with(|c| c.set(true));
65 let value = snapshot.enter(block);
66 IN_APPLIED_SNAPSHOT.with(|c| c.set(false));
67
68 match snapshot.apply() {
69 snapshot_v2::SnapshotApplyResult::Success => Ok(value),
70 snapshot_v2::SnapshotApplyResult::Failure => Err("Snapshot apply failed"),
71 }
72}
73
74pub fn dispatch_ui_event<T>(block: impl FnOnce() -> T) -> Option<T> {
89 run_in_mutable_snapshot(block).ok()
90}
91
92thread_local! {
99 pub(crate) static IN_EVENT_HANDLER: Cell<bool> = const { Cell::new(false) };
101 pub(crate) static IN_APPLIED_SNAPSHOT: Cell<bool> = const { Cell::new(false) };
103}
104
105pub fn enter_event_handler() {
109 IN_EVENT_HANDLER.with(|c| c.set(true));
110}
111
112pub fn exit_event_handler() {
114 IN_EVENT_HANDLER.with(|c| c.set(false));
115}
116
117pub fn in_event_handler() -> bool {
119 IN_EVENT_HANDLER.with(|c| c.get())
120}
121
122pub fn in_applied_snapshot() -> bool {
124 IN_APPLIED_SNAPSHOT.with(|c| c.get())
125}
126
127#[cfg(not(target_arch = "wasm32"))]
133fn compose_debug_enabled() -> bool {
134 use std::sync::OnceLock;
135 static COMPOSE_DEBUG: OnceLock<bool> = OnceLock::new();
136 *COMPOSE_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG").is_some())
137}
138
139#[cfg(not(target_arch = "wasm32"))]
140fn input_debug_enabled() -> bool {
141 use std::sync::OnceLock;
142 static INPUT_DEBUG: OnceLock<bool> = OnceLock::new();
143 *INPUT_DEBUG.get_or_init(|| std::env::var_os("CRANPOSE_INPUT_DEBUG").is_some())
144}
145
146#[cfg(target_arch = "wasm32")]
147fn input_debug_enabled() -> bool {
148 false
149}
150
151#[cfg(test)]
152pub use runtime::{TestRuntime, TestScheduler};
153
154use crate::collections::map::HashMap;
155use crate::collections::map::HashSet;
156use crate::runtime::{runtime_handle_for, RuntimeId};
157use crate::state::{NeverEqual, SnapshotMutableState, UpdateScope};
158use std::any::Any;
159use std::cell::{Cell, Ref, RefCell, RefMut};
160use std::fmt;
161use std::hash::{Hash, Hasher};
162use std::marker::PhantomData;
163use std::ops::{Deref, DerefMut};
164use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicUsize, Ordering};
166use std::sync::Arc;
167
168pub type Key = u64;
169pub type NodeId = usize;
170
171#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
177pub struct AnchorId(usize);
178
179impl AnchorId {
180 pub(crate) const INVALID: AnchorId = AnchorId(0);
182
183 pub(crate) fn new(id: usize) -> Self {
185 Self(id)
186 }
187
188 pub fn is_valid(&self) -> bool {
190 self.0 != 0
191 }
192}
193
194pub(crate) type ScopeId = usize;
195type LocalKey = usize;
196pub(crate) type FrameCallbackId = u64;
197
198static NEXT_SCOPE_ID: AtomicUsize = AtomicUsize::new(1);
199static NEXT_LOCAL_KEY: AtomicUsize = AtomicUsize::new(1);
200
201fn next_scope_id() -> ScopeId {
202 NEXT_SCOPE_ID.fetch_add(1, Ordering::Relaxed)
203}
204
205fn next_local_key() -> LocalKey {
206 NEXT_LOCAL_KEY.fetch_add(1, Ordering::Relaxed)
207}
208
209pub(crate) struct RecomposeScopeInner {
210 id: ScopeId,
211 runtime: RuntimeHandle,
212 invalid: Cell<bool>,
213 enqueued: Cell<bool>,
214 active: Cell<bool>,
215 pending_recompose: Cell<bool>,
216 force_reuse: Cell<bool>,
217 force_recompose: Cell<bool>,
218 parent_hint: Cell<Option<NodeId>>,
219 recompose: RefCell<Option<RecomposeCallback>>,
220 local_stack: RefCell<Vec<LocalContext>>,
221 slots_host: RefCell<Option<Weak<SlotsHost>>>,
222}
223
224impl RecomposeScopeInner {
225 fn new(runtime: RuntimeHandle) -> Self {
226 Self {
227 id: next_scope_id(),
228 runtime,
229 invalid: Cell::new(false),
230 enqueued: Cell::new(false),
231 active: Cell::new(true),
232 pending_recompose: Cell::new(false),
233 force_reuse: Cell::new(false),
234 force_recompose: Cell::new(false),
235 parent_hint: Cell::new(None),
236 recompose: RefCell::new(None),
237 local_stack: RefCell::new(Vec::new()),
238 slots_host: RefCell::new(None),
239 }
240 }
241}
242
243type RecomposeCallback = Box<dyn FnMut(&Composer) + 'static>;
244
245#[derive(Clone)]
246pub struct RecomposeScope {
247 inner: Rc<RecomposeScopeInner>, }
249
250impl PartialEq for RecomposeScope {
251 fn eq(&self, other: &Self) -> bool {
252 Rc::ptr_eq(&self.inner, &other.inner)
253 }
254}
255
256impl Eq for RecomposeScope {}
257
258impl RecomposeScope {
259 fn new(runtime: RuntimeHandle) -> Self {
260 Self {
261 inner: Rc::new(RecomposeScopeInner::new(runtime)),
262 }
263 }
264
265 pub fn id(&self) -> ScopeId {
266 self.inner.id
267 }
268
269 pub fn is_invalid(&self) -> bool {
270 self.inner.invalid.get()
271 }
272
273 pub fn is_active(&self) -> bool {
274 self.inner.active.get()
275 }
276
277 fn invalidate(&self) {
278 if input_debug_enabled() {
279 eprintln!(
280 "[CRANPOSE_INPUT_DEBUG] scope invalidate id={} active={} enqueued={}",
281 self.inner.id,
282 self.inner.active.get(),
283 self.inner.enqueued.get()
284 );
285 }
286 self.inner.invalid.set(true);
287 if !self.inner.active.get() {
288 return;
289 }
290 if !self.inner.enqueued.replace(true) {
291 self.inner
292 .runtime
293 .register_invalid_scope(self.inner.id, Rc::downgrade(&self.inner));
294 }
295 }
296
297 fn mark_recomposed(&self) {
298 self.inner.invalid.set(false);
299 self.inner.force_reuse.set(false);
300 self.inner.force_recompose.set(false);
301 if self.inner.enqueued.replace(false) {
302 self.inner.runtime.mark_scope_recomposed(self.inner.id);
303 }
304 let pending = self.inner.pending_recompose.replace(false);
305 if pending {
306 if self.inner.active.get() {
307 self.invalidate();
308 } else {
309 self.inner.invalid.set(true);
310 }
311 }
312 }
313
314 fn downgrade(&self) -> Weak<RecomposeScopeInner> {
315 Rc::downgrade(&self.inner)
316 }
317
318 fn set_recompose(&self, callback: RecomposeCallback) {
319 *self.inner.recompose.borrow_mut() = Some(callback);
320 }
321
322 fn run_recompose(&self, composer: &Composer) {
323 let mut callback_cell = self.inner.recompose.borrow_mut();
324 if let Some(mut callback) = callback_cell.take() {
325 drop(callback_cell);
326 callback(composer);
327 }
328 }
329
330 fn snapshot_locals(&self, stack: &[LocalContext]) {
331 *self.inner.local_stack.borrow_mut() = stack.to_vec();
332 }
333
334 fn local_stack(&self) -> Vec<LocalContext> {
335 self.inner.local_stack.borrow().clone()
336 }
337
338 fn set_parent_hint(&self, parent: Option<NodeId>) {
339 self.inner.parent_hint.set(parent);
340 }
341
342 fn parent_hint(&self) -> Option<NodeId> {
343 self.inner.parent_hint.get()
344 }
345
346 fn set_slots_host(&self, host: Weak<SlotsHost>) {
347 *self.inner.slots_host.borrow_mut() = Some(host);
348 }
349
350 fn slots_host(&self) -> Option<Rc<SlotsHost>> {
351 self.inner
352 .slots_host
353 .borrow()
354 .as_ref()
355 .and_then(|weak| weak.upgrade())
356 }
357
358 pub fn deactivate(&self) {
359 if !self.inner.active.replace(false) {
360 return;
361 }
362 if self.inner.enqueued.replace(false) {
363 self.inner.runtime.mark_scope_recomposed(self.inner.id);
364 }
365 }
366
367 pub fn reactivate(&self) {
368 if self.inner.active.replace(true) {
369 return;
370 }
371 if self.inner.invalid.get() && !self.inner.enqueued.replace(true) {
372 self.inner
373 .runtime
374 .register_invalid_scope(self.inner.id, Rc::downgrade(&self.inner));
375 }
376 }
377
378 pub fn force_reuse(&self) {
379 self.inner.force_reuse.set(true);
380 self.inner.force_recompose.set(false);
381 self.inner.pending_recompose.set(true);
382 }
383
384 pub fn force_recompose(&self) {
385 self.inner.force_recompose.set(true);
386 self.inner.force_reuse.set(false);
387 self.inner.pending_recompose.set(false);
388 }
389
390 pub fn should_recompose(&self) -> bool {
391 if self.inner.force_recompose.replace(false) {
392 self.inner.force_reuse.set(false);
393 return true;
394 }
395 if self.inner.force_reuse.replace(false) {
396 return false;
397 }
398 self.is_invalid()
399 }
400}
401
402#[cfg(test)]
403impl RecomposeScope {
404 pub(crate) fn new_for_test(runtime: RuntimeHandle) -> Self {
405 Self::new(runtime)
406 }
407}
408
409#[derive(Debug, Clone, Copy, Default)]
410pub struct RecomposeOptions {
411 pub force_reuse: bool,
412 pub force_recompose: bool,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
416pub enum NodeError {
417 Missing { id: NodeId },
418 TypeMismatch { id: NodeId, expected: &'static str },
419 MissingContext { id: NodeId, reason: &'static str },
420 AlreadyExists { id: NodeId },
421}
422
423impl std::fmt::Display for NodeError {
424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425 match self {
426 NodeError::Missing { id } => write!(f, "node {id} missing"),
427 NodeError::TypeMismatch { id, expected } => {
428 write!(f, "node {id} type mismatch; expected {expected}")
429 }
430 NodeError::MissingContext { id, reason } => {
431 write!(f, "missing context for node {id}: {reason}")
432 }
433 NodeError::AlreadyExists { id } => {
434 write!(f, "node {id} already exists")
435 }
436 }
437 }
438}
439
440impl std::error::Error for NodeError {}
441
442pub use subcompose::{
443 ContentTypeReusePolicy, DefaultSlotReusePolicy, SlotId, SlotReusePolicy, SubcomposeState,
444};
445
446#[derive(Copy, Clone, Debug, PartialEq, Eq)]
447pub enum Phase {
448 Compose,
449 Measure,
450 Layout,
451}
452
453pub use composer_context::with_composer as with_current_composer;
454
455#[allow(non_snake_case)]
456pub fn withCurrentComposer<R>(f: impl FnOnce(&Composer) -> R) -> R {
457 composer_context::with_composer(f)
458}
459
460fn with_current_composer_opt<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
461 composer_context::try_with_composer(f)
462}
463
464pub fn with_key<K: Hash>(key: &K, content: impl FnOnce()) {
465 with_current_composer(|composer| composer.with_key(key, |_| content()));
466}
467
468#[allow(non_snake_case)]
469pub fn withKey<K: Hash>(key: &K, content: impl FnOnce()) {
470 with_key(key, content)
471}
472
473pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Owned<T> {
474 with_current_composer(|composer| composer.remember(init))
475}
476
477#[allow(non_snake_case)]
505pub fn rememberUpdatedState<T: Clone + 'static>(value: T) -> MutableState<T> {
506 let state = remember(|| mutableStateOf(value.clone()));
507 state.with(|s| {
508 s.set(value);
509 *s
510 })
511}
512
513#[cfg(feature = "internal")]
514#[allow(non_snake_case)]
515pub fn withFrameNanos(callback: impl FnOnce(u64) + 'static) -> internal::FrameCallbackRegistration {
516 with_current_composer(|composer| {
517 composer
518 .runtime_handle()
519 .frame_clock()
520 .with_frame_nanos(callback)
521 })
522}
523
524#[cfg(feature = "internal")]
525#[allow(non_snake_case)]
526pub fn withFrameMillis(
527 callback: impl FnOnce(u64) + 'static,
528) -> internal::FrameCallbackRegistration {
529 with_current_composer(|composer| {
530 composer
531 .runtime_handle()
532 .frame_clock()
533 .with_frame_millis(callback)
534 })
535}
536
537#[allow(non_snake_case)]
568pub fn mutableStateOf<T: Clone + 'static>(initial: T) -> MutableState<T> {
569 let runtime = with_current_composer_opt(|composer| composer.runtime_handle())
574 .or_else(runtime::current_runtime_handle)
575 .expect("mutableStateOf requires an active runtime. Create state inside a composition or after a Runtime is created.");
576 MutableState::with_runtime(initial, runtime)
577}
578
579#[allow(non_snake_case)]
584pub fn try_mutableStateOf<T: Clone + 'static>(initial: T) -> Option<MutableState<T>> {
585 let runtime = with_current_composer_opt(|composer| composer.runtime_handle())
586 .or_else(runtime::current_runtime_handle)?;
587 Some(MutableState::with_runtime(initial, runtime))
588}
589
590#[allow(non_snake_case)]
591pub fn mutableStateListOf<T, I>(values: I) -> SnapshotStateList<T>
592where
593 T: Clone + 'static,
594 I: IntoIterator<Item = T>,
595{
596 with_current_composer(move |composer| composer.mutable_state_list_of(values))
597}
598
599#[allow(non_snake_case)]
600pub fn mutableStateList<T: Clone + 'static>() -> SnapshotStateList<T> {
601 mutableStateListOf(std::iter::empty::<T>())
602}
603
604#[allow(non_snake_case)]
605pub fn mutableStateMapOf<K, V, I>(pairs: I) -> SnapshotStateMap<K, V>
606where
607 K: Clone + Eq + Hash + 'static,
608 V: Clone + 'static,
609 I: IntoIterator<Item = (K, V)>,
610{
611 with_current_composer(move |composer| composer.mutable_state_map_of(pairs))
612}
613
614#[allow(non_snake_case)]
615pub fn mutableStateMap<K, V>() -> SnapshotStateMap<K, V>
616where
617 K: Clone + Eq + Hash + 'static,
618 V: Clone + 'static,
619{
620 mutableStateMapOf(std::iter::empty::<(K, V)>())
621}
622
623#[allow(non_snake_case)]
649pub fn useState<T: Clone + 'static>(init: impl FnOnce() -> T) -> MutableState<T> {
650 remember(|| mutableStateOf(init())).with(|state| *state)
651}
652
653#[allow(deprecated)]
654#[deprecated(
655 since = "0.1.0",
656 note = "use useState(|| value) instead of use_state(|| value)"
657)]
658pub fn use_state<T: Clone + 'static>(init: impl FnOnce() -> T) -> MutableState<T> {
659 useState(init)
660}
661
662#[allow(non_snake_case)]
663pub fn derivedStateOf<T: 'static + Clone>(compute: impl Fn() -> T + 'static) -> State<T> {
664 with_current_composer(|composer| {
665 let key = location_key(file!(), line!(), column!());
666 composer.with_group(key, |composer| {
667 let should_recompute = composer
668 .current_recranpose_scope()
669 .map(|scope| scope.should_recompose())
670 .unwrap_or(true);
671 let runtime = composer.runtime_handle();
672 let compute_rc: Rc<dyn Fn() -> T> = Rc::new(compute); let derived =
674 composer.remember(|| DerivedState::new(runtime.clone(), compute_rc.clone()));
675 derived.update(|derived| {
676 derived.set_compute(compute_rc.clone());
677 if should_recompute {
678 derived.recompute();
679 }
680 });
681 derived.with(|derived| derived.state.as_state())
682 })
683 })
684}
685
686pub struct ProvidedValue {
687 key: LocalKey,
688 #[allow(clippy::type_complexity)] apply: Box<dyn Fn(&Composer) -> Rc<dyn Any>>, }
691
692impl ProvidedValue {
693 fn into_entry(self, composer: &Composer) -> (LocalKey, Rc<dyn Any>) {
694 let ProvidedValue { key, apply } = self;
696 let entry = apply(composer);
697 (key, entry)
698 }
699}
700
701#[allow(non_snake_case)]
702pub fn CompositionLocalProvider(
703 values: impl IntoIterator<Item = ProvidedValue>,
704 content: impl FnOnce(),
705) {
706 with_current_composer(|composer| {
707 let provided: Vec<ProvidedValue> = values.into_iter().collect(); composer.with_composition_locals(provided, |_composer| content());
709 })
710}
711
712struct LocalStateEntry<T: Clone + 'static> {
713 state: MutableState<T>,
714}
715
716impl<T: Clone + 'static> LocalStateEntry<T> {
717 fn new(initial: T, runtime: RuntimeHandle) -> Self {
718 Self {
719 state: MutableState::with_runtime(initial, runtime),
720 }
721 }
722
723 fn set(&self, value: T) {
724 self.state.replace(value);
725 }
726
727 fn value(&self) -> T {
728 self.state.value()
729 }
730}
731
732struct StaticLocalEntry<T: Clone + 'static> {
733 value: RefCell<T>,
734}
735
736impl<T: Clone + 'static> StaticLocalEntry<T> {
737 fn new(value: T) -> Self {
738 Self {
739 value: RefCell::new(value),
740 }
741 }
742
743 fn set(&self, value: T) {
744 *self.value.borrow_mut() = value;
745 }
746
747 fn value(&self) -> T {
748 self.value.borrow().clone()
749 }
750}
751
752#[derive(Clone)]
753pub struct CompositionLocal<T: Clone + 'static> {
754 key: LocalKey,
755 default: Rc<dyn Fn() -> T>, }
757
758impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
759 fn eq(&self, other: &Self) -> bool {
760 self.key == other.key
761 }
762}
763
764impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
765
766impl<T: Clone + 'static> CompositionLocal<T> {
767 pub fn provides(&self, value: T) -> ProvidedValue {
768 let key = self.key;
769 ProvidedValue {
770 key,
771 apply: Box::new(move |composer: &Composer| {
772 let runtime = composer.runtime_handle();
773 let entry_ref = composer
774 .remember(|| Rc::new(LocalStateEntry::new(value.clone(), runtime.clone())));
775 entry_ref.update(|entry| entry.set(value.clone()));
776 entry_ref.with(|entry| entry.clone() as Rc<dyn Any>) }),
778 }
779 }
780
781 pub fn current(&self) -> T {
782 with_current_composer(|composer| composer.read_composition_local(self))
783 }
784
785 pub fn default_value(&self) -> T {
786 (self.default)()
787 }
788}
789
790#[allow(non_snake_case)]
791pub fn compositionLocalOf<T: Clone + 'static>(
792 default: impl Fn() -> T + 'static,
793) -> CompositionLocal<T> {
794 CompositionLocal {
795 key: next_local_key(),
796 default: Rc::new(default), }
798}
799
800#[derive(Clone)]
811pub struct StaticCompositionLocal<T: Clone + 'static> {
812 key: LocalKey,
813 default: Rc<dyn Fn() -> T>, }
815
816impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
817 fn eq(&self, other: &Self) -> bool {
818 self.key == other.key
819 }
820}
821
822impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
823
824impl<T: Clone + 'static> StaticCompositionLocal<T> {
825 pub fn provides(&self, value: T) -> ProvidedValue {
826 let key = self.key;
827 ProvidedValue {
828 key,
829 apply: Box::new(move |composer: &Composer| {
830 let entry_ref = composer.remember(|| Rc::new(StaticLocalEntry::new(value.clone())));
833 entry_ref.update(|entry| entry.set(value.clone()));
834 entry_ref.with(|entry| entry.clone() as Rc<dyn Any>) }),
836 }
837 }
838
839 pub fn current(&self) -> T {
840 with_current_composer(|composer| composer.read_static_composition_local(self))
841 }
842
843 pub fn default_value(&self) -> T {
844 (self.default)()
845 }
846}
847
848#[allow(non_snake_case)]
849pub fn staticCompositionLocalOf<T: Clone + 'static>(
850 default: impl Fn() -> T + 'static,
851) -> StaticCompositionLocal<T> {
852 StaticCompositionLocal {
853 key: next_local_key(),
854 default: Rc::new(default), }
856}
857
858#[derive(Default)]
859struct DisposableEffectState {
860 key: Option<Key>,
861 cleanup: Option<Box<dyn FnOnce()>>,
862}
863
864impl DisposableEffectState {
865 fn should_run(&self, key: Key) -> bool {
866 match self.key {
867 Some(current) => current != key,
868 None => true,
869 }
870 }
871
872 fn set_key(&mut self, key: Key) {
873 self.key = Some(key);
874 }
875
876 fn set_cleanup(&mut self, cleanup: Option<Box<dyn FnOnce()>>) {
877 self.cleanup = cleanup;
878 }
879
880 fn run_cleanup(&mut self) {
881 if let Some(cleanup) = self.cleanup.take() {
882 cleanup();
883 }
884 }
885}
886
887impl Drop for DisposableEffectState {
888 fn drop(&mut self) {
889 self.run_cleanup();
890 }
891}
892
893#[derive(Clone, Copy, Debug, Default)]
894pub struct DisposableEffectScope;
895
896#[derive(Default)]
897pub struct DisposableEffectResult {
898 cleanup: Option<Box<dyn FnOnce()>>,
899}
900
901impl DisposableEffectScope {
902 pub fn on_dispose(&self, cleanup: impl FnOnce() + 'static) -> DisposableEffectResult {
903 DisposableEffectResult::new(cleanup)
904 }
905}
906
907impl DisposableEffectResult {
908 pub fn new(cleanup: impl FnOnce() + 'static) -> Self {
909 Self {
910 cleanup: Some(Box::new(cleanup)),
911 }
912 }
913
914 fn into_cleanup(self) -> Option<Box<dyn FnOnce()>> {
915 self.cleanup
916 }
917}
918
919#[allow(non_snake_case)]
920pub fn SideEffect(effect: impl FnOnce() + 'static) {
921 with_current_composer(|composer| composer.register_side_effect(effect));
922}
923
924pub fn __disposable_effect_impl<K, F>(group_key: Key, keys: K, effect: F)
925where
926 K: Hash,
927 F: FnOnce(DisposableEffectScope) -> DisposableEffectResult + 'static,
928{
929 with_current_composer(|composer| {
932 composer.with_group(group_key, |composer| {
933 let key_hash = hash_key(&keys);
934 let state = composer.remember(DisposableEffectState::default);
935 if state.with(|state| state.should_run(key_hash)) {
936 state.update(|state| {
937 state.run_cleanup();
938 state.set_key(key_hash);
939 });
940 let state_for_effect = state.clone();
941 let mut effect_opt = Some(effect);
942 composer.register_side_effect(move || {
943 if let Some(effect) = effect_opt.take() {
944 let result = effect(DisposableEffectScope);
945 state_for_effect.update(|state| state.set_cleanup(result.into_cleanup()));
946 }
947 });
948 }
949 });
950 });
951}
952
953#[macro_export]
954macro_rules! DisposableEffect {
955 ($keys:expr, $effect:expr) => {
956 $crate::__disposable_effect_impl(
957 $crate::location_key(file!(), line!(), column!()),
958 $keys,
959 $effect,
960 )
961 };
962}
963
964pub fn with_node_mut<N: Node + 'static, R>(
965 id: NodeId,
966 f: impl FnOnce(&mut N) -> R,
967) -> Result<R, NodeError> {
968 with_current_composer(|composer| composer.with_node_mut(id, f))
969}
970
971pub fn push_parent(id: NodeId) {
972 with_current_composer(|composer| composer.push_parent(id));
973}
974
975pub fn pop_parent() {
976 with_current_composer(|composer| composer.pop_parent());
977}
978
979mod slot_storage;
984pub use slot_storage::{GroupId, SlotStorage, StartGroup, ValueSlotId};
985
986pub mod chunked_slot_storage;
987pub mod hierarchical_slot_storage;
988pub mod slot_backend;
989pub mod split_slot_storage;
990pub use slot_backend::{make_backend, SlotBackend, SlotBackendKind};
991
992pub mod slot_table;
997pub use slot_table::SlotTable;
998
999pub trait Node: Any {
1000 fn mount(&mut self) {}
1001 fn update(&mut self) {}
1002 fn unmount(&mut self) {}
1003 fn insert_child(&mut self, _child: NodeId) {}
1004 fn remove_child(&mut self, _child: NodeId) {}
1005 fn move_child(&mut self, _from: usize, _to: usize) {}
1006 fn update_children(&mut self, _children: &[NodeId]) {}
1007 fn children(&self) -> Vec<NodeId> {
1008 Vec::new()
1009 }
1010 fn set_node_id(&mut self, _id: NodeId) {}
1013 fn on_attached_to_parent(&mut self, _parent: NodeId) {}
1016 fn on_removed_from_parent(&mut self) {}
1019 fn parent(&self) -> Option<NodeId> {
1022 None
1023 }
1024 fn mark_needs_layout(&self) {}
1027 fn needs_layout(&self) -> bool {
1029 false
1030 }
1031 fn mark_needs_measure(&self) {}
1034 fn needs_measure(&self) -> bool {
1036 false
1037 }
1038 fn mark_needs_semantics(&self) {}
1040 fn needs_semantics(&self) -> bool {
1042 false
1043 }
1044 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1052 self.on_attached_to_parent(parent);
1053 }
1054}
1055
1056pub fn bubble_layout_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1075 bubble_layout_dirty_applier(applier, node_id);
1076}
1077
1078pub fn bubble_measure_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1089 bubble_measure_dirty_applier(applier, node_id);
1090}
1091
1092pub fn bubble_semantics_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1098 bubble_semantics_dirty_applier(applier, node_id);
1099}
1100
1101pub fn queue_semantics_invalidation(node_id: NodeId) {
1106 let _ = composer_context::try_with_composer(|composer| {
1107 composer.enqueue_semantics_invalidation(node_id);
1108 });
1109}
1110
1111pub fn bubble_layout_dirty_in_composer<N: Node + 'static>(node_id: NodeId) {
1134 bubble_layout_dirty_composer::<N>(node_id);
1135}
1136
1137pub fn bubble_semantics_dirty_in_composer<N: Node + 'static>(node_id: NodeId) {
1144 bubble_semantics_dirty_composer::<N>(node_id);
1145}
1146
1147fn bubble_layout_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1149 if let Ok(node) = applier.get_mut(node_id) {
1152 node.mark_needs_layout();
1153 }
1154
1155 loop {
1157 let parent_id = match applier.get_mut(node_id) {
1159 Ok(node) => node.parent(),
1160 Err(_) => None,
1161 };
1162
1163 match parent_id {
1164 Some(pid) => {
1165 if let Ok(parent) = applier.get_mut(pid) {
1167 if !parent.needs_layout() {
1168 parent.mark_needs_layout();
1169 node_id = pid; } else {
1171 break; }
1173 } else {
1174 break;
1175 }
1176 }
1177 None => break, }
1179 }
1180}
1181
1182fn bubble_measure_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1184 if let Ok(node) = applier.get_mut(node_id) {
1186 node.mark_needs_measure();
1187 }
1188
1189 loop {
1191 let parent_id = match applier.get_mut(node_id) {
1193 Ok(node) => node.parent(),
1194 Err(_) => None,
1195 };
1196
1197 match parent_id {
1198 Some(pid) => {
1199 if let Ok(parent) = applier.get_mut(pid) {
1201 if !parent.needs_measure() {
1202 parent.mark_needs_measure();
1203 node_id = pid; } else {
1205 break; }
1207 } else {
1208 break;
1209 }
1210 }
1211 None => {
1212 break; }
1214 }
1215 }
1216}
1217
1218fn bubble_semantics_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1220 if let Ok(node) = applier.get_mut(node_id) {
1221 node.mark_needs_semantics();
1222 }
1223
1224 loop {
1225 let parent_id = match applier.get_mut(node_id) {
1226 Ok(node) => node.parent(),
1227 Err(_) => None,
1228 };
1229
1230 match parent_id {
1231 Some(pid) => {
1232 if let Ok(parent) = applier.get_mut(pid) {
1233 if !parent.needs_semantics() {
1234 parent.mark_needs_semantics();
1235 node_id = pid;
1236 } else {
1237 break;
1238 }
1239 } else {
1240 break;
1241 }
1242 }
1243 None => break,
1244 }
1245 }
1246}
1247
1248fn bubble_layout_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1252 let _ = with_node_mut(node_id, |node: &mut N| {
1254 node.mark_needs_layout();
1255 });
1256
1257 while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1259 let parent_id = pid;
1260
1261 let should_continue = with_node_mut(parent_id, |node: &mut N| {
1263 if !node.needs_layout() {
1264 node.mark_needs_layout();
1265 true } else {
1267 false }
1269 })
1270 .unwrap_or(false);
1271
1272 if should_continue {
1273 node_id = parent_id;
1274 } else {
1275 break;
1276 }
1277 }
1278}
1279
1280fn bubble_semantics_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1282 let _ = with_node_mut(node_id, |node: &mut N| {
1284 node.mark_needs_semantics();
1285 });
1286
1287 while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1288 let parent_id = pid;
1289
1290 let should_continue = with_node_mut(parent_id, |node: &mut N| {
1291 if !node.needs_semantics() {
1292 node.mark_needs_semantics();
1293 true
1294 } else {
1295 false
1296 }
1297 })
1298 .unwrap_or(false);
1299
1300 if should_continue {
1301 node_id = parent_id;
1302 } else {
1303 break;
1304 }
1305 }
1306}
1307
1308impl dyn Node {
1309 pub fn as_any_mut(&mut self) -> &mut dyn Any {
1310 self
1311 }
1312}
1313
1314pub trait Applier: Any {
1315 fn create(&mut self, node: Box<dyn Node>) -> NodeId;
1316 fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError>;
1317 fn remove(&mut self, id: NodeId) -> Result<(), NodeError>;
1318
1319 fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError>;
1327
1328 fn as_any(&self) -> &dyn Any
1329 where
1330 Self: Sized,
1331 {
1332 self
1333 }
1334
1335 fn as_any_mut(&mut self) -> &mut dyn Any
1336 where
1337 Self: Sized,
1338 {
1339 self
1340 }
1341}
1342
1343pub(crate) type Command = Box<dyn FnMut(&mut dyn Applier) -> Result<(), NodeError> + 'static>;
1344
1345#[derive(Default)]
1346pub struct MemoryApplier {
1347 nodes: Vec<Option<Box<dyn Node>>>, high_id_nodes: std::collections::HashMap<NodeId, Box<dyn Node>>,
1351 layout_runtime: Option<RuntimeHandle>,
1352 slots: SlotBackend,
1353}
1354
1355impl MemoryApplier {
1356 pub fn new() -> Self {
1357 Self {
1358 nodes: Vec::new(),
1359 high_id_nodes: std::collections::HashMap::new(),
1360 layout_runtime: None,
1361 slots: SlotBackend::default(),
1362 }
1363 }
1364
1365 pub fn slots(&mut self) -> &mut SlotBackend {
1366 &mut self.slots
1367 }
1368
1369 pub fn with_node<N: Node + 'static, R>(
1370 &mut self,
1371 id: NodeId,
1372 f: impl FnOnce(&mut N) -> R,
1373 ) -> Result<R, NodeError> {
1374 let slot = self
1375 .nodes
1376 .get_mut(id)
1377 .ok_or(NodeError::Missing { id })?
1378 .as_deref_mut()
1379 .ok_or(NodeError::Missing { id })?;
1380 let typed = slot
1381 .as_any_mut()
1382 .downcast_mut::<N>()
1383 .ok_or(NodeError::TypeMismatch {
1384 id,
1385 expected: std::any::type_name::<N>(),
1386 })?;
1387 Ok(f(typed))
1388 }
1389
1390 pub fn len(&self) -> usize {
1391 self.nodes.iter().filter(|n| n.is_some()).count()
1392 }
1393
1394 pub fn is_empty(&self) -> bool {
1395 self.len() == 0
1396 }
1397
1398 pub fn set_runtime_handle(&mut self, handle: RuntimeHandle) {
1399 self.layout_runtime = Some(handle);
1400 }
1401
1402 pub fn clear_runtime_handle(&mut self) {
1403 self.layout_runtime = None;
1404 }
1405
1406 pub fn runtime_handle(&self) -> Option<RuntimeHandle> {
1407 self.layout_runtime.clone()
1408 }
1409
1410 pub fn dump_tree(&self, root: Option<NodeId>) -> String {
1411 let mut output = String::new();
1412 if let Some(root_id) = root {
1413 self.dump_node(&mut output, root_id, 0);
1414 } else {
1415 output.push_str("(no root)\n");
1416 }
1417 output
1418 }
1419
1420 fn dump_node(&self, output: &mut String, id: NodeId, depth: usize) {
1421 let indent = " ".repeat(depth);
1422 if let Some(Some(node)) = self.nodes.get(id) {
1423 let type_name = std::any::type_name_of_val(&**node);
1424 output.push_str(&format!("{}[{}] {}\n", indent, id, type_name));
1425
1426 let children = node.children();
1427 for child_id in children {
1428 self.dump_node(output, child_id, depth + 1);
1429 }
1430 } else {
1431 output.push_str(&format!("{}[{}] (missing)\n", indent, id));
1432 }
1433 }
1434}
1435
1436impl Applier for MemoryApplier {
1437 fn create(&mut self, node: Box<dyn Node>) -> NodeId {
1438 let id = self.nodes.len();
1439 self.nodes.push(Some(node));
1440 id
1441 }
1442
1443 fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError> {
1444 if let Some(node) = self.high_id_nodes.get_mut(&id) {
1446 return Ok(node.as_mut());
1447 }
1448 let slot = self
1450 .nodes
1451 .get_mut(id)
1452 .ok_or(NodeError::Missing { id })?
1453 .as_deref_mut()
1454 .ok_or(NodeError::Missing { id })?;
1455 Ok(slot)
1456 }
1457
1458 fn remove(&mut self, id: NodeId) -> Result<(), NodeError> {
1459 if self.high_id_nodes.contains_key(&id) {
1461 let children = self
1463 .high_id_nodes
1464 .get(&id)
1465 .map(|n| n.children())
1466 .unwrap_or_default();
1467
1468 for child_id in children {
1470 let is_owned = self
1471 .get_mut(child_id)
1472 .map(|child| child.parent() == Some(id))
1473 .unwrap_or(false);
1474 if is_owned {
1475 let _ = self.remove(child_id);
1476 }
1477 }
1478
1479 self.high_id_nodes.remove(&id);
1480 return Ok(());
1481 }
1482
1483 let children = {
1485 let slot = self.nodes.get(id).ok_or(NodeError::Missing { id })?;
1486 if let Some(node) = slot {
1487 node.children()
1488 } else {
1489 return Err(NodeError::Missing { id });
1490 }
1491 };
1492
1493 for child_id in children {
1495 let is_owned = self
1496 .get_mut(child_id)
1497 .map(|child| child.parent() == Some(id))
1498 .unwrap_or(false);
1499
1500 if is_owned {
1501 let _ = self.remove(child_id);
1502 }
1503 }
1504
1505 let slot = self.nodes.get_mut(id).ok_or(NodeError::Missing { id })?;
1506 slot.take();
1507 Ok(())
1508 }
1509
1510 fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError> {
1511 const HIGH_ID_THRESHOLD: NodeId = 1_000_000_000; if id >= HIGH_ID_THRESHOLD {
1516 if self.high_id_nodes.contains_key(&id) {
1517 return Err(NodeError::AlreadyExists { id });
1518 }
1519 self.high_id_nodes.insert(id, node);
1520 Ok(())
1521 } else {
1522 if id >= self.nodes.len() {
1524 self.nodes.resize_with(id + 1, || None);
1525 }
1526
1527 if self.nodes[id].is_some() {
1528 return Err(NodeError::AlreadyExists { id });
1529 }
1530
1531 self.nodes[id] = Some(node);
1532 Ok(())
1533 }
1534 }
1535}
1536
1537pub trait ApplierHost {
1538 fn borrow_dyn(&self) -> RefMut<'_, dyn Applier>;
1539}
1540
1541pub struct ConcreteApplierHost<A: Applier + 'static> {
1542 inner: RefCell<A>,
1543}
1544
1545impl<A: Applier + 'static> ConcreteApplierHost<A> {
1546 pub fn new(applier: A) -> Self {
1547 Self {
1548 inner: RefCell::new(applier),
1549 }
1550 }
1551
1552 pub fn borrow_typed(&self) -> RefMut<'_, A> {
1553 self.inner.borrow_mut()
1554 }
1555
1556 pub fn try_borrow_typed(&self) -> Result<RefMut<'_, A>, std::cell::BorrowMutError> {
1557 self.inner.try_borrow_mut()
1558 }
1559
1560 pub fn into_inner(self) -> A {
1561 self.inner.into_inner()
1562 }
1563}
1564
1565impl<A: Applier + 'static> ApplierHost for ConcreteApplierHost<A> {
1566 fn borrow_dyn(&self) -> RefMut<'_, dyn Applier> {
1567 RefMut::map(self.inner.borrow_mut(), |applier| {
1568 applier as &mut dyn Applier
1569 })
1570 }
1571}
1572
1573pub struct ApplierGuard<'a, A: Applier + 'static> {
1574 inner: RefMut<'a, A>,
1575}
1576
1577impl<'a, A: Applier + 'static> ApplierGuard<'a, A> {
1578 fn new(inner: RefMut<'a, A>) -> Self {
1579 Self { inner }
1580 }
1581}
1582
1583impl<'a, A: Applier + 'static> Deref for ApplierGuard<'a, A> {
1584 type Target = A;
1585
1586 fn deref(&self) -> &Self::Target {
1587 &self.inner
1588 }
1589}
1590
1591impl<'a, A: Applier + 'static> DerefMut for ApplierGuard<'a, A> {
1592 fn deref_mut(&mut self) -> &mut Self::Target {
1593 &mut self.inner
1594 }
1595}
1596
1597pub struct SlotsHost {
1598 inner: RefCell<SlotBackend>,
1599}
1600
1601impl SlotsHost {
1602 pub fn new(storage: SlotBackend) -> Self {
1603 Self {
1604 inner: RefCell::new(storage),
1605 }
1606 }
1607
1608 pub fn borrow(&self) -> Ref<'_, SlotBackend> {
1609 self.inner.borrow()
1610 }
1611
1612 pub fn borrow_mut(&self) -> RefMut<'_, SlotBackend> {
1613 self.inner.borrow_mut()
1614 }
1615
1616 pub fn take(&self) -> SlotBackend {
1617 std::mem::take(&mut *self.inner.borrow_mut())
1618 }
1619}
1620
1621pub(crate) struct ComposerCore {
1622 slots: Rc<SlotsHost>,
1623 slots_override: RefCell<Vec<Rc<SlotsHost>>>,
1624 applier: Rc<dyn ApplierHost>,
1625 runtime: RuntimeHandle,
1626 observer: SnapshotStateObserver,
1627 parent_stack: RefCell<Vec<ParentFrame>>,
1628 subcompose_stack: RefCell<Vec<SubcomposeFrame>>,
1629 root: Cell<Option<NodeId>>,
1630 commands: RefCell<Vec<Command>>,
1631 scope_stack: RefCell<Vec<RecomposeScope>>,
1632 local_stack: RefCell<Vec<LocalContext>>,
1633 side_effects: RefCell<Vec<Box<dyn FnOnce()>>>,
1634 pending_scope_options: RefCell<Option<RecomposeOptions>>,
1635 phase: Cell<Phase>,
1636 last_node_reused: Cell<Option<bool>>,
1637 recranpose_parent_hint: Cell<Option<NodeId>>,
1638 _not_send: PhantomData<*const ()>,
1639}
1640
1641impl ComposerCore {
1642 pub fn new(
1643 slots: Rc<SlotsHost>,
1644 applier: Rc<dyn ApplierHost>,
1645 runtime: RuntimeHandle,
1646 observer: SnapshotStateObserver,
1647 root: Option<NodeId>,
1648 ) -> Self {
1649 let parent_stack = if let Some(root_id) = root {
1655 vec![ParentFrame {
1656 id: root_id,
1657 remembered: Owned::new(ParentChildren::default()),
1658 previous: Vec::new(),
1659 new_children: Vec::new(),
1660 }]
1661 } else {
1662 Vec::new()
1663 };
1664
1665 Self {
1666 slots,
1667 slots_override: RefCell::new(Vec::new()),
1668 applier,
1669 runtime,
1670 observer,
1671 parent_stack: RefCell::new(parent_stack),
1672 subcompose_stack: RefCell::new(Vec::new()),
1673 root: Cell::new(root),
1674 commands: RefCell::new(Vec::new()),
1675 scope_stack: RefCell::new(Vec::new()),
1676 local_stack: RefCell::new(Vec::new()),
1677 side_effects: RefCell::new(Vec::new()),
1678 pending_scope_options: RefCell::new(None),
1679 phase: Cell::new(Phase::Compose),
1680 last_node_reused: Cell::new(None),
1681 recranpose_parent_hint: Cell::new(None),
1682 _not_send: PhantomData,
1683 }
1684 }
1685}
1686
1687#[derive(Clone)]
1688pub struct Composer {
1689 core: Rc<ComposerCore>,
1690}
1691
1692impl Composer {
1693 pub fn new(
1694 slots: Rc<SlotsHost>,
1695 applier: Rc<dyn ApplierHost>,
1696 runtime: RuntimeHandle,
1697 observer: SnapshotStateObserver,
1698 root: Option<NodeId>,
1699 ) -> Self {
1700 let core = Rc::new(ComposerCore::new(slots, applier, runtime, observer, root));
1701 Self { core }
1702 }
1703
1704 pub(crate) fn from_core(core: Rc<ComposerCore>) -> Self {
1705 Self { core }
1706 }
1707
1708 pub(crate) fn clone_core(&self) -> Rc<ComposerCore> {
1709 Rc::clone(&self.core)
1710 }
1711
1712 fn observer(&self) -> SnapshotStateObserver {
1713 self.core.observer.clone()
1714 }
1715
1716 fn observe_scope<R>(&self, scope: &RecomposeScope, block: impl FnOnce() -> R) -> R {
1717 let observer = self.observer();
1718 let scope_clone = scope.clone();
1719 observer.observe_reads(scope_clone, move |scope_ref| scope_ref.invalidate(), block)
1720 }
1721
1722 fn active_slots_host(&self) -> Rc<SlotsHost> {
1723 self.core
1724 .slots_override
1725 .borrow()
1726 .last()
1727 .cloned()
1728 .unwrap_or_else(|| Rc::clone(&self.core.slots))
1729 }
1730
1731 fn with_slots<R>(&self, f: impl FnOnce(&SlotBackend) -> R) -> R {
1732 let host = self.active_slots_host();
1733 let slots = host.borrow();
1734 f(&slots)
1735 }
1736
1737 fn with_slots_mut<R>(&self, f: impl FnOnce(&mut SlotBackend) -> R) -> R {
1738 let host = self.active_slots_host();
1739 let mut slots = host.borrow_mut();
1740 f(&mut slots)
1741 }
1742
1743 fn with_slot_override<R>(&self, slots: Rc<SlotsHost>, f: impl FnOnce(&Composer) -> R) -> R {
1744 self.core.slots_override.borrow_mut().push(slots);
1745 struct Guard {
1746 core: Rc<ComposerCore>,
1747 }
1748 impl Drop for Guard {
1749 fn drop(&mut self) {
1750 self.core.slots_override.borrow_mut().pop();
1751 }
1752 }
1753 let guard = Guard {
1754 core: self.clone_core(),
1755 };
1756 let result = f(self);
1757 drop(guard);
1758 result
1759 }
1760
1761 fn parent_stack(&self) -> RefMut<'_, Vec<ParentFrame>> {
1762 self.core.parent_stack.borrow_mut()
1763 }
1764
1765 fn subcompose_stack(&self) -> RefMut<'_, Vec<SubcomposeFrame>> {
1766 self.core.subcompose_stack.borrow_mut()
1767 }
1768
1769 fn commands_mut(&self) -> RefMut<'_, Vec<Command>> {
1770 self.core.commands.borrow_mut()
1771 }
1772
1773 pub(crate) fn enqueue_semantics_invalidation(&self, id: NodeId) {
1774 self.commands_mut()
1775 .push(Box::new(move |applier: &mut dyn Applier| {
1776 bubble_semantics_dirty(applier, id);
1777 Ok(())
1778 }));
1779 }
1780
1781 fn scope_stack(&self) -> RefMut<'_, Vec<RecomposeScope>> {
1782 self.core.scope_stack.borrow_mut()
1783 }
1784
1785 fn local_stack(&self) -> RefMut<'_, Vec<LocalContext>> {
1786 self.core.local_stack.borrow_mut()
1787 }
1788
1789 fn side_effects_mut(&self) -> RefMut<'_, Vec<Box<dyn FnOnce()>>> {
1790 self.core.side_effects.borrow_mut()
1791 }
1792
1793 fn pending_scope_options(&self) -> RefMut<'_, Option<RecomposeOptions>> {
1794 self.core.pending_scope_options.borrow_mut()
1795 }
1796
1797 fn borrow_applier(&self) -> RefMut<'_, dyn Applier> {
1798 self.core.applier.borrow_dyn()
1799 }
1800
1801 pub fn register_virtual_node(
1808 &self,
1809 node_id: NodeId,
1810 node: Box<dyn Node>,
1811 ) -> Result<(), NodeError> {
1812 let mut applier = self.borrow_applier();
1813 applier.insert_with_id(node_id, node)
1814 }
1815
1816 pub fn node_has_no_parent(&self, node_id: NodeId) -> bool {
1819 let mut applier = self.borrow_applier();
1820 match applier.get_mut(node_id) {
1821 Ok(node) => node.parent().is_none(),
1822 Err(_) => true, }
1824 }
1825
1826 pub fn get_node_children(&self, node_id: NodeId) -> Vec<NodeId> {
1831 let mut applier = self.borrow_applier();
1832 match applier.get_mut(node_id) {
1833 Ok(node) => node.children(),
1834 Err(_) => Vec::new(),
1835 }
1836 }
1837
1838 pub fn clear_node_children(&self, node_id: NodeId) {
1844 let mut applier = self.borrow_applier();
1845 if let Ok(node) = applier.get_mut(node_id) {
1846 node.update_children(&[]);
1848 }
1849 }
1850
1851 pub fn install<R>(&self, f: impl FnOnce(&Composer) -> R) -> R {
1852 let _composer_guard = composer_context::enter(self);
1853 runtime::push_active_runtime(&self.core.runtime);
1854 struct Guard;
1855 impl Drop for Guard {
1856 fn drop(&mut self) {
1857 runtime::pop_active_runtime();
1858 }
1859 }
1860 let guard = Guard;
1861 let result = f(self);
1862 drop(guard);
1863 result
1864 }
1865
1866 pub fn with_group<R>(&self, key: Key, f: impl FnOnce(&Composer) -> R) -> R {
1867 let (group, scope_ref, restored_from_gap) = self.with_slots_mut(|slots| {
1868 let StartGroup {
1869 group,
1870 restored_from_gap,
1871 } = slots.begin_group(key);
1872 let scope_ref = slots
1873 .remember(|| RecomposeScope::new(self.runtime_handle()))
1874 .with(|scope| scope.clone());
1875 (group, scope_ref, restored_from_gap)
1876 });
1877
1878 if restored_from_gap {
1879 scope_ref.force_recompose();
1880 }
1881
1882 if let Some(options) = self.pending_scope_options().take() {
1883 if options.force_recompose {
1884 scope_ref.force_recompose();
1885 } else if options.force_reuse {
1886 scope_ref.force_reuse();
1887 }
1888 }
1889
1890 self.with_slots_mut(|slots| {
1891 SlotStorage::set_group_scope(slots, group, scope_ref.id());
1892 });
1893
1894 let slots_host = self.active_slots_host();
1895 scope_ref.set_slots_host(Rc::downgrade(&slots_host));
1896
1897 {
1898 let mut stack = self.scope_stack();
1899 stack.push(scope_ref.clone());
1900 }
1901
1902 {
1903 let mut stack = self.subcompose_stack();
1904 if let Some(frame) = stack.last_mut() {
1905 frame.scopes.push(scope_ref.clone());
1906 }
1907 }
1908
1909 {
1910 let locals = self.core.local_stack.borrow();
1911 scope_ref.snapshot_locals(&locals);
1912 }
1913 {
1914 let parent_hint = self.parent_stack().last().map(|frame| frame.id);
1915 scope_ref.set_parent_hint(parent_hint);
1916 }
1917
1918 let result = self.observe_scope(&scope_ref, || f(self));
1919
1920 let trimmed = self.with_slots_mut(|slots| slots.finalize_current_group());
1921 if trimmed {
1922 scope_ref.force_recompose();
1923 }
1924
1925 {
1926 let mut stack = self.scope_stack();
1927 stack.pop();
1928 }
1929 scope_ref.mark_recomposed();
1930 self.with_slots_mut(|slots| slots.end_group());
1931 result
1932 }
1933
1934 pub fn cranpose_with_reuse<R>(
1935 &self,
1936 key: Key,
1937 options: RecomposeOptions,
1938 f: impl FnOnce(&Composer) -> R,
1939 ) -> R {
1940 self.pending_scope_options().replace(options);
1941 self.with_group(key, f)
1942 }
1943
1944 pub fn with_key<K: Hash, R>(&self, key: &K, f: impl FnOnce(&Composer) -> R) -> R {
1945 let hashed = hash_key(key);
1946 self.with_group(hashed, f)
1947 }
1948
1949 pub fn remember<T: 'static>(&self, init: impl FnOnce() -> T) -> Owned<T> {
1950 self.with_slots_mut(|slots| slots.remember(init))
1951 }
1952
1953 pub fn use_value_slot<T: 'static>(&self, init: impl FnOnce() -> T) -> usize {
1954 self.with_slots_mut(|slots| slots.alloc_value_slot(init).index())
1955 }
1956
1957 pub fn with_slot_value<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&T) -> R) -> R {
1958 self.with_slots(|slots| {
1959 let value = SlotStorage::read_value(slots, ValueSlotId::new(idx));
1960 f(value)
1961 })
1962 }
1963
1964 pub fn with_slot_value_mut<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&mut T) -> R) -> R {
1965 self.with_slots_mut(|slots| {
1966 let value = SlotStorage::read_value_mut(slots, ValueSlotId::new(idx));
1967 f(value)
1968 })
1969 }
1970
1971 pub fn write_slot_value<T: 'static>(&self, idx: usize, value: T) {
1972 self.with_slots_mut(|slots| slots.write_value(ValueSlotId::new(idx), value));
1973 }
1974
1975 pub fn mutable_state_of<T: Clone + 'static>(&self, initial: T) -> MutableState<T> {
1976 MutableState::with_runtime(initial, self.runtime_handle())
1977 }
1978
1979 pub fn mutable_state_list_of<T, I>(&self, values: I) -> SnapshotStateList<T>
1980 where
1981 T: Clone + 'static,
1982 I: IntoIterator<Item = T>,
1983 {
1984 SnapshotStateList::with_runtime(values, self.runtime_handle())
1985 }
1986
1987 pub fn mutable_state_map_of<K, V, I>(&self, pairs: I) -> SnapshotStateMap<K, V>
1988 where
1989 K: Clone + Eq + Hash + 'static,
1990 V: Clone + 'static,
1991 I: IntoIterator<Item = (K, V)>,
1992 {
1993 SnapshotStateMap::with_runtime(pairs, self.runtime_handle())
1994 }
1995
1996 pub fn read_composition_local<T: Clone + 'static>(&self, local: &CompositionLocal<T>) -> T {
1997 let stack = self.core.local_stack.borrow();
1998 for context in stack.iter().rev() {
1999 if let Some(entry) = context.values.get(&local.key) {
2000 let typed = entry
2001 .clone()
2002 .downcast::<LocalStateEntry<T>>()
2003 .expect("composition local type mismatch");
2004 return typed.value();
2005 }
2006 }
2007 local.default_value()
2008 }
2009
2010 pub fn read_static_composition_local<T: Clone + 'static>(
2011 &self,
2012 local: &StaticCompositionLocal<T>,
2013 ) -> T {
2014 let stack = self.core.local_stack.borrow();
2015 for context in stack.iter().rev() {
2016 if let Some(entry) = context.values.get(&local.key) {
2017 let typed = entry
2018 .clone()
2019 .downcast::<StaticLocalEntry<T>>()
2020 .expect("static composition local type mismatch");
2021 return typed.value();
2022 }
2023 }
2024 local.default_value()
2025 }
2026
2027 pub fn current_recranpose_scope(&self) -> Option<RecomposeScope> {
2028 self.core.scope_stack.borrow().last().cloned()
2029 }
2030
2031 pub fn phase(&self) -> Phase {
2032 self.core.phase.get()
2033 }
2034
2035 pub(crate) fn set_phase(&self, phase: Phase) {
2036 self.core.phase.set(phase);
2037 }
2038
2039 pub fn enter_phase(&self, phase: Phase) {
2040 self.set_phase(phase);
2041 }
2042
2043 pub(crate) fn subcompose<R>(
2044 &self,
2045 state: &mut SubcomposeState,
2046 slot_id: SlotId,
2047 content: impl FnOnce(&Composer) -> R,
2048 ) -> (R, Vec<NodeId>) {
2049 match self.phase() {
2050 Phase::Measure | Phase::Layout => {}
2051 current => panic!(
2052 "subcompose() may only be called during measure or layout; current phase: {:?}",
2053 current
2054 ),
2055 }
2056
2057 self.subcompose_stack().push(SubcomposeFrame::default());
2058 struct StackGuard {
2059 core: Rc<ComposerCore>,
2060 leaked: bool,
2061 }
2062 impl Drop for StackGuard {
2063 fn drop(&mut self) {
2064 if !self.leaked {
2065 self.core.subcompose_stack.borrow_mut().pop();
2066 }
2067 }
2068 }
2069 let mut guard = StackGuard {
2070 core: self.clone_core(),
2071 leaked: false,
2072 };
2073
2074 let slot_host = state.get_or_create_slots(slot_id);
2075 {
2076 let mut slots = slot_host.borrow_mut();
2077 slots.reset();
2078 }
2079 let result = self.with_slot_override(slot_host.clone(), |composer| {
2080 composer.with_group(slot_id.raw(), |composer| content(composer))
2082 });
2083 {
2084 let mut slots = slot_host.borrow_mut();
2085 slots.finalize_current_group();
2086 slots.flush();
2087 }
2088
2089 let frame = {
2090 let mut stack = guard.core.subcompose_stack.borrow_mut();
2091 let frame = stack.pop().expect("subcompose stack underflow");
2092 guard.leaked = true;
2093 frame
2094 };
2095 let nodes = frame.nodes;
2096 let scopes = frame.scopes;
2097 state.register_active(slot_id, &nodes, &scopes);
2098 (result, nodes)
2099 }
2100
2101 pub fn subcompose_measurement<R>(
2102 &self,
2103 state: &mut SubcomposeState,
2104 slot_id: SlotId,
2105 content: impl FnOnce(&Composer) -> R,
2106 ) -> (R, Vec<NodeId>) {
2107 let (result, nodes) = self.subcompose(state, slot_id, content);
2108
2109 let roots = nodes
2113 .into_iter()
2114 .filter(|&id| self.node_has_no_parent(id))
2115 .collect();
2116
2117 (result, roots)
2118 }
2119
2120 pub fn subcompose_in<R>(
2121 &self,
2122 slots: &Rc<SlotsHost>,
2123 root: Option<NodeId>,
2124 f: impl FnOnce(&Composer) -> R,
2125 ) -> Result<R, NodeError> {
2126 let runtime_handle = self.runtime_handle();
2127 slots.borrow_mut().reset();
2128 let phase = self.phase();
2129 let locals = self.core.local_stack.borrow().clone();
2130 let core = Rc::new(ComposerCore::new(
2131 Rc::clone(slots),
2132 Rc::clone(&self.core.applier),
2133 runtime_handle.clone(),
2134 self.observer(),
2135 root,
2136 ));
2137 core.phase.set(phase);
2138 *core.local_stack.borrow_mut() = locals;
2139 let composer = Composer::from_core(core);
2140 let (result, mut commands, side_effects) = composer.install(|composer| {
2141 let output = f(composer);
2142 let commands = composer.take_commands();
2143 let side_effects = composer.take_side_effects();
2144 (output, commands, side_effects)
2145 });
2146
2147 {
2148 let mut applier = self.borrow_applier();
2149 for mut command in commands.drain(..) {
2150 command(&mut *applier)?;
2151 }
2152 for mut update in runtime_handle.take_updates() {
2153 update(&mut *applier)?;
2154 }
2155 }
2156 runtime_handle.drain_ui();
2157 for effect in side_effects {
2158 effect();
2159 }
2160 runtime_handle.drain_ui();
2161 {
2162 let mut slots_mut = slots.borrow_mut();
2163 slots_mut.finalize_current_group();
2164 slots_mut.flush();
2165 }
2166 Ok(result)
2167 }
2168
2169 pub fn subcompose_slot<R>(
2174 &self,
2175 slots: &Rc<SlotsHost>,
2176 root: Option<NodeId>,
2177 f: impl FnOnce(&Composer) -> R,
2178 ) -> Result<R, NodeError> {
2179 let runtime_handle = self.runtime_handle();
2180 slots.borrow_mut().reset();
2183 let phase = self.phase();
2184 let locals = self.core.local_stack.borrow().clone();
2185 let core = Rc::new(ComposerCore::new(
2186 Rc::clone(slots),
2187 Rc::clone(&self.core.applier),
2188 runtime_handle.clone(),
2189 self.observer(),
2190 root, ));
2192 core.phase.set(phase);
2193 *core.local_stack.borrow_mut() = locals;
2194 let composer = Composer::from_core(core);
2195 let (result, mut commands, side_effects) = composer.install(|composer| {
2196 let output = f(composer);
2197 if root.is_some() {
2201 composer.pop_parent();
2202 }
2203 let commands = composer.take_commands();
2204 let side_effects = composer.take_side_effects();
2205 (output, commands, side_effects)
2206 });
2207
2208 {
2209 let mut applier = self.borrow_applier();
2210 for mut command in commands.drain(..) {
2211 command(&mut *applier)?;
2212 }
2213 for mut update in runtime_handle.take_updates() {
2214 update(&mut *applier)?;
2215 }
2216 }
2217 runtime_handle.drain_ui();
2218 for effect in side_effects {
2219 effect();
2220 }
2221 runtime_handle.drain_ui();
2222 Ok(result)
2227 }
2228
2229 pub fn skip_current_group(&self) {
2230 let nodes = self.with_slots(|slots| slots.nodes_in_current_group());
2231 self.with_slots_mut(|slots| slots.skip_current_group());
2232 let current_parent = {
2234 let stack = self.parent_stack();
2235 stack.last().map(|frame| frame.id)
2236 };
2237
2238 let mut applier = self.borrow_applier();
2242 for id in nodes {
2243 if let Ok(node) = applier.get_mut(id) {
2244 let node_parent = node.parent();
2245 if node_parent.is_none() || node_parent == current_parent {
2246 drop(applier);
2247 self.attach_to_parent(id);
2248 applier = self.borrow_applier();
2249 }
2250 }
2251 }
2252 }
2253
2254 pub fn runtime_handle(&self) -> RuntimeHandle {
2255 self.core.runtime.clone()
2256 }
2257
2258 pub fn set_recranpose_callback<F>(&self, callback: F)
2259 where
2260 F: FnMut(&Composer) + 'static,
2261 {
2262 if let Some(scope) = self.current_recranpose_scope() {
2263 let observer = self.observer();
2264 let scope_weak = scope.downgrade();
2265 let mut callback = callback;
2266 scope.set_recompose(Box::new(move |composer: &Composer| {
2267 if let Some(inner) = scope_weak.upgrade() {
2268 let scope_instance = RecomposeScope { inner };
2269 observer.observe_reads(
2270 scope_instance.clone(),
2271 move |scope_ref| scope_ref.invalidate(),
2272 || {
2273 callback(composer);
2274 },
2275 );
2276 }
2277 }));
2278 }
2279 }
2280
2281 pub fn with_composition_locals<R>(
2282 &self,
2283 provided: Vec<ProvidedValue>,
2284 f: impl FnOnce(&Composer) -> R,
2285 ) -> R {
2286 if provided.is_empty() {
2287 return f(self);
2288 }
2289 let mut context = LocalContext::default();
2290 for value in provided {
2291 let (key, entry) = value.into_entry(self);
2292 context.values.insert(key, entry);
2293 }
2294 {
2295 let mut stack = self.local_stack();
2296 stack.push(context);
2297 }
2298 let result = f(self);
2299 {
2300 let mut stack = self.local_stack();
2301 stack.pop();
2302 }
2303 result
2304 }
2305
2306 fn recranpose_group(&self, scope: &RecomposeScope) {
2307 if !scope.is_invalid() {
2313 scope.mark_recomposed();
2314 return;
2315 }
2316 let started = self.with_slots_mut(|slots| slots.begin_recranpose_at_scope(scope.id()));
2317 if started.is_some() {
2318 let previous_hint = self
2319 .core
2320 .recranpose_parent_hint
2321 .replace(scope.parent_hint());
2322 struct HintGuard {
2323 core: Rc<ComposerCore>,
2324 previous: Option<NodeId>,
2325 }
2326 impl Drop for HintGuard {
2327 fn drop(&mut self) {
2328 self.core.recranpose_parent_hint.set(self.previous);
2329 }
2330 }
2331 let _hint_guard = HintGuard {
2332 core: self.clone_core(),
2333 previous: previous_hint,
2334 };
2335 {
2336 let mut stack = self.scope_stack();
2337 stack.push(scope.clone());
2338 }
2339 let saved_locals = {
2340 let mut locals = self.local_stack();
2341 std::mem::take(&mut *locals)
2342 };
2343 {
2344 let mut locals = self.local_stack();
2345 *locals = scope.local_stack();
2346 }
2347 self.observe_scope(scope, || {
2348 scope.run_recompose(self);
2349 });
2350 {
2351 let mut locals = self.local_stack();
2352 *locals = saved_locals;
2353 }
2354 {
2355 let mut stack = self.scope_stack();
2356 stack.pop();
2357 }
2358 self.with_slots_mut(SlotStorage::end_recompose);
2359 scope.mark_recomposed();
2360 } else {
2361 scope.mark_recomposed();
2362 }
2363 }
2364
2365 pub fn use_state<T: Clone + 'static>(&self, init: impl FnOnce() -> T) -> MutableState<T> {
2366 let runtime = self.runtime_handle();
2367 let state = self.with_slots_mut(|slots| {
2368 slots.remember(|| MutableState::with_runtime(init(), runtime.clone()))
2369 });
2370 state.with(|state| *state)
2371 }
2372
2373 pub fn emit_node<N: Node + 'static>(&self, init: impl FnOnce() -> N) -> NodeId {
2374 let (existing_id, type_matches) = {
2376 if let Some(id) = self.with_slots_mut(|slots| slots.peek_node()) {
2377 let mut applier = self.borrow_applier();
2379 let matches = match applier.get_mut(id) {
2380 Ok(node) => node.as_any_mut().downcast_ref::<N>().is_some(),
2381 Err(_) => false,
2382 };
2383 (Some(id), matches)
2384 } else {
2385 (None, false)
2386 }
2387 };
2388
2389 if let Some(id) = existing_id {
2391 if type_matches {
2392 let reuse_allowed = true;
2396
2397 #[cfg(not(target_arch = "wasm32"))]
2398 if compose_debug_enabled() {
2399 eprintln!("emit_node: candidate #{id} reuse_allowed={reuse_allowed}");
2400 }
2401
2402 if reuse_allowed {
2403 self.core.last_node_reused.set(Some(true));
2404 #[cfg(not(target_arch = "wasm32"))]
2405 if compose_debug_enabled() {
2406 eprintln!(
2407 "emit_node: reusing node #{id} as {}",
2408 std::any::type_name::<N>()
2409 );
2410 }
2411 self.with_slots_mut(|slots| slots.advance_after_node_read());
2412
2413 self.commands_mut()
2414 .push(Box::new(move |applier: &mut dyn Applier| {
2415 let node = match applier.get_mut(id) {
2416 Ok(node) => node,
2417 Err(NodeError::Missing { .. }) => return Ok(()),
2418 Err(err) => return Err(err),
2419 };
2420 let typed = node.as_any_mut().downcast_mut::<N>().ok_or(
2421 NodeError::TypeMismatch {
2422 id,
2423 expected: std::any::type_name::<N>(),
2424 },
2425 )?;
2426 typed.update();
2427 Ok(())
2428 }));
2429 self.attach_to_parent(id);
2430 return id;
2431 }
2432 }
2433 }
2434
2435 if let Some(old_id) = existing_id {
2437 if !type_matches {
2438 #[cfg(not(target_arch = "wasm32"))]
2439 if compose_debug_enabled() {
2440 eprintln!(
2441 "emit_node: replacing node #{old_id} with new {}",
2442 std::any::type_name::<N>()
2443 );
2444 }
2445 self.commands_mut()
2446 .push(Box::new(move |applier: &mut dyn Applier| {
2447 if let Ok(node) = applier.get_mut(old_id) {
2448 node.unmount();
2449 }
2450 match applier.remove(old_id) {
2451 Ok(()) | Err(NodeError::Missing { .. }) => Ok(()),
2452 Err(err) => Err(err),
2453 }
2454 }));
2455 }
2456 }
2457
2458 let id = {
2461 let mut applier = self.borrow_applier();
2462 applier.create(Box::new(init()))
2463 };
2464 self.core.last_node_reused.set(Some(false));
2465 #[cfg(not(target_arch = "wasm32"))]
2466 if compose_debug_enabled() {
2467 eprintln!(
2468 "emit_node: creating node #{} as {}",
2469 id,
2470 std::any::type_name::<N>()
2471 );
2472 }
2473 {
2474 self.with_slots_mut(|slots| slots.record_node(id));
2475 }
2476 self.commands_mut()
2477 .push(Box::new(move |applier: &mut dyn Applier| {
2478 let node = match applier.get_mut(id) {
2479 Ok(node) => node,
2480 Err(NodeError::Missing { .. }) => return Ok(()),
2481 Err(err) => return Err(err),
2482 };
2483 node.set_node_id(id);
2484 node.mount();
2485 Ok(())
2486 }));
2487 self.attach_to_parent(id);
2488 id
2489 }
2490
2491 fn attach_to_parent(&self, id: NodeId) {
2492 let mut parent_stack = self.parent_stack();
2498 if let Some(frame) = parent_stack.last_mut() {
2499 let parent_id = frame.id;
2500 if parent_id == id {
2501 return;
2502 }
2503 frame.new_children.push(id);
2504 drop(parent_stack);
2505
2506 {
2517 let mut applier = self.borrow_applier();
2518 if let Ok(child_node) = applier.get_mut(id) {
2519 let existing_parent = child_node.parent();
2520 let should_set = match existing_parent {
2525 None => true,
2526 Some(existing) => {
2527 let root_id = self.core.root.get();
2529 parent_id != root_id.unwrap_or(0) || existing == root_id.unwrap_or(0)
2530 }
2531 };
2532 if should_set {
2533 child_node.set_parent_for_bubbling(parent_id);
2534 }
2535 }
2536 }
2537 return;
2538 }
2539 drop(parent_stack);
2540
2541 let in_subcompose = !self.subcompose_stack().is_empty();
2543 if in_subcompose {
2544 let has_parent = {
2548 let mut applier = self.borrow_applier();
2549 applier
2550 .get_mut(id)
2551 .map(|node| node.parent().is_some())
2552 .unwrap_or(false)
2553 };
2554
2555 if !has_parent {
2556 let mut subcompose_stack = self.subcompose_stack();
2557 if let Some(frame) = subcompose_stack.last_mut() {
2558 frame.nodes.push(id);
2559 }
2560 }
2561 return;
2562 }
2563
2564 if let Some(parent_hint) = self.core.recranpose_parent_hint.get() {
2566 let parent_status = {
2567 let mut applier = self.borrow_applier();
2568 applier
2569 .get_mut(id)
2570 .map(|node| node.parent())
2571 .unwrap_or(None)
2572 };
2573 match parent_status {
2574 Some(existing) if existing == parent_hint => {}
2575 None => {
2576 self.commands_mut()
2577 .push(Box::new(move |applier: &mut dyn Applier| {
2578 if let Ok(parent_node) = applier.get_mut(parent_hint) {
2579 parent_node.insert_child(id);
2580 }
2581 if let Ok(child_node) = applier.get_mut(id) {
2582 child_node.on_attached_to_parent(parent_hint);
2583 }
2584 bubble_layout_dirty(applier, parent_hint);
2585 bubble_measure_dirty(applier, parent_hint);
2586 Ok(())
2587 }));
2588 }
2589 Some(_) => {}
2590 }
2591 return;
2592 }
2593
2594 let has_parent = {
2599 let mut applier = self.borrow_applier();
2600 applier
2601 .get_mut(id)
2602 .map(|node| node.parent().is_some())
2603 .unwrap_or(false)
2604 };
2605 if has_parent {
2606 return;
2608 }
2609
2610 self.set_root(Some(id));
2612 }
2613
2614 pub fn with_node_mut<N: Node + 'static, R>(
2615 &self,
2616 id: NodeId,
2617 f: impl FnOnce(&mut N) -> R,
2618 ) -> Result<R, NodeError> {
2619 let mut applier = self.borrow_applier();
2620 let node = applier.get_mut(id)?;
2621 let typed = node
2622 .as_any_mut()
2623 .downcast_mut::<N>()
2624 .ok_or(NodeError::TypeMismatch {
2625 id,
2626 expected: std::any::type_name::<N>(),
2627 })?;
2628 Ok(f(typed))
2629 }
2630
2631 pub fn push_parent(&self, id: NodeId) {
2632 let remembered = self.remember(ParentChildren::default);
2633 let reused = self.core.last_node_reused.take().unwrap_or(true);
2634 let in_subcompose = !self.core.subcompose_stack.borrow().is_empty();
2635
2636 let previous = if reused || in_subcompose {
2639 remembered.with(|entry| entry.children.clone())
2640 } else {
2641 Vec::new()
2642 };
2643
2644 self.parent_stack().push(ParentFrame {
2645 id,
2646 remembered,
2647 previous,
2648 new_children: Vec::new(),
2649 });
2650 }
2651
2652 pub fn pop_parent(&self) {
2653 let frame_opt = {
2654 let mut stack = self.parent_stack();
2655 stack.pop()
2656 };
2657 if let Some(frame) = frame_opt {
2658 let ParentFrame {
2659 id,
2660 remembered,
2661 previous,
2662 new_children,
2663 } = frame;
2664
2665 #[cfg(not(target_arch = "wasm32"))]
2666 if compose_debug_enabled() {
2667 eprintln!("pop_parent: node #{}", id);
2668 eprintln!(" previous children: {:?}", previous);
2669 eprintln!(" new children: {:?}", new_children);
2670 }
2671 let children_changed = previous != new_children;
2672
2673 if children_changed {
2674 let mut current = previous.clone();
2675 let target = new_children.clone();
2676 let desired: HashSet<NodeId> = target.iter().copied().collect();
2677
2678 for index in (0..current.len()).rev() {
2679 let child = current[index];
2680 if !desired.contains(&child) {
2681 current.remove(index);
2682 self.commands_mut()
2683 .push(Box::new(move |applier: &mut dyn Applier| {
2684 if let Ok(parent_node) = applier.get_mut(id) {
2686 parent_node.remove_child(child);
2687 }
2688 bubble_layout_dirty(applier, id);
2690 bubble_measure_dirty(applier, id);
2691 let should_remove = if let Ok(node) = applier.get_mut(child) {
2695 match node.parent() {
2696 Some(parent_id) if parent_id == id => {
2697 node.on_removed_from_parent();
2698 node.unmount();
2699 true
2700 }
2701 None => {
2702 node.unmount();
2704 true
2705 }
2706 Some(_) => false,
2707 }
2708 } else {
2709 true
2710 };
2711
2712 if should_remove {
2713 let _ = applier.remove(child);
2714 }
2715 Ok(())
2716 }));
2717 }
2718 }
2719
2720 for (target_index, &child) in target.iter().enumerate() {
2721 if let Some(current_index) = current.iter().position(|&c| c == child) {
2722 if current_index != target_index {
2723 let from_index = current_index;
2724 current.remove(from_index);
2725 let to_index = target_index.min(current.len());
2726 current.insert(to_index, child);
2727 self.commands_mut()
2728 .push(Box::new(move |applier: &mut dyn Applier| {
2729 if let Ok(parent_node) = applier.get_mut(id) {
2730 parent_node.move_child(from_index, to_index);
2731 }
2732 Ok(())
2733 }));
2734 self.commands_mut()
2735 .push(Box::new(move |applier: &mut dyn Applier| {
2736 bubble_layout_dirty(applier, id);
2739 bubble_measure_dirty(applier, id);
2740 Ok(())
2741 }));
2742 }
2743 } else {
2744 let insert_index = target_index.min(current.len());
2745 let appended_index = current.len();
2746 current.insert(insert_index, child);
2747 self.commands_mut()
2748 .push(Box::new(move |applier: &mut dyn Applier| {
2749 let old_parent =
2752 applier.get_mut(child).ok().and_then(|node| node.parent());
2753 if let Some(old_parent_id) = old_parent {
2754 if old_parent_id != id {
2755 if let Ok(old_parent_node) = applier.get_mut(old_parent_id)
2756 {
2757 old_parent_node.remove_child(child);
2758 }
2759 if let Ok(child_node) = applier.get_mut(child) {
2760 child_node.on_removed_from_parent();
2761 }
2762 bubble_layout_dirty(applier, old_parent_id);
2763 bubble_measure_dirty(applier, old_parent_id);
2764 }
2765 }
2766 if let Ok(parent_node) = applier.get_mut(id) {
2768 parent_node.insert_child(child);
2769 }
2770 if let Ok(child_node) = applier.get_mut(child) {
2772 child_node.on_attached_to_parent(id);
2773 }
2774 bubble_layout_dirty(applier, id);
2776 bubble_measure_dirty(applier, id);
2777 Ok(())
2778 }));
2779 if insert_index != appended_index {
2780 self.commands_mut()
2781 .push(Box::new(move |applier: &mut dyn Applier| {
2782 if let Ok(parent_node) = applier.get_mut(id) {
2783 parent_node.move_child(appended_index, insert_index);
2784 }
2785 Ok(())
2786 }));
2787 }
2788 }
2789 }
2790 }
2791
2792 let expected_children = new_children.clone();
2793 let needs_dirty_check = !children_changed;
2794 self.commands_mut()
2795 .push(Box::new(move |applier: &mut dyn Applier| {
2796 let mut repaired = false;
2797 for &child in &expected_children {
2798 let needs_attach = if let Ok(node) = applier.get_mut(child) {
2799 node.parent() != Some(id)
2800 } else {
2801 false
2802 };
2803 if needs_attach {
2804 let old_parent =
2805 applier.get_mut(child).ok().and_then(|node| node.parent());
2806 if let Some(old_parent_id) = old_parent {
2807 if old_parent_id != id {
2808 if let Ok(old_parent_node) = applier.get_mut(old_parent_id) {
2809 old_parent_node.remove_child(child);
2810 }
2811 if let Ok(child_node) = applier.get_mut(child) {
2812 child_node.on_removed_from_parent();
2813 }
2814 bubble_layout_dirty(applier, old_parent_id);
2815 bubble_measure_dirty(applier, old_parent_id);
2816 }
2817 }
2818 if let Ok(parent_node) = applier.get_mut(id) {
2819 parent_node.insert_child(child);
2820 }
2821 if let Ok(child_node) = applier.get_mut(child) {
2822 child_node.on_attached_to_parent(id);
2823 }
2824 repaired = true;
2825 }
2826 }
2827 let is_dirty = if needs_dirty_check {
2828 if let Ok(node) = applier.get_mut(id) {
2829 node.needs_layout()
2830 } else {
2831 false
2832 }
2833 } else {
2834 false
2835 };
2836 if repaired {
2837 bubble_layout_dirty(applier, id);
2838 bubble_measure_dirty(applier, id);
2839 } else if is_dirty {
2840 bubble_layout_dirty(applier, id);
2841 }
2842 Ok(())
2843 }));
2844
2845 remembered.update(|entry| entry.children = new_children);
2846 }
2847 }
2848
2849 pub fn take_commands(&self) -> Vec<Command> {
2850 std::mem::take(&mut *self.commands_mut())
2851 }
2852
2853 pub fn apply_pending_commands(&self) -> Result<(), NodeError> {
2858 let mut commands = self.take_commands();
2859 let runtime_handle = self.runtime_handle();
2860 {
2861 let mut applier = self.borrow_applier();
2862 for mut command in commands.drain(..) {
2863 command(&mut *applier)?;
2864 }
2865 for mut update in runtime_handle.take_updates() {
2866 update(&mut *applier)?;
2867 }
2868 }
2869 runtime_handle.drain_ui();
2870 Ok(())
2871 }
2872
2873 pub fn register_side_effect(&self, effect: impl FnOnce() + 'static) {
2874 self.side_effects_mut().push(Box::new(effect));
2875 }
2876
2877 pub fn take_side_effects(&self) -> Vec<Box<dyn FnOnce()>> {
2878 std::mem::take(&mut *self.side_effects_mut())
2879 }
2880
2881 pub(crate) fn root(&self) -> Option<NodeId> {
2882 self.core.root.get()
2883 }
2884
2885 pub(crate) fn set_root(&self, node: Option<NodeId>) {
2886 self.core.root.set(node);
2887 }
2888}
2889
2890#[derive(Default, Clone)]
2891struct ParentChildren {
2892 children: Vec<NodeId>,
2893}
2894
2895struct ParentFrame {
2896 id: NodeId,
2897 remembered: Owned<ParentChildren>,
2898 previous: Vec<NodeId>,
2899 new_children: Vec<NodeId>,
2900}
2901
2902#[derive(Default)]
2903struct SubcomposeFrame {
2904 nodes: Vec<NodeId>,
2905 scopes: Vec<RecomposeScope>,
2906}
2907
2908#[derive(Default, Clone)]
2909struct LocalContext {
2910 values: HashMap<LocalKey, Rc<dyn Any>>,
2911}
2912
2913pub(crate) struct MutableStateInner<T: Clone + 'static> {
2914 state: Arc<SnapshotMutableState<T>>,
2915 watchers: RefCell<Vec<Weak<RecomposeScopeInner>>>, runtime: RuntimeHandle,
2917}
2918
2919impl<T: Clone + 'static> MutableStateInner<T> {
2920 fn new(value: T, runtime: RuntimeHandle) -> Self {
2921 Self {
2922 state: SnapshotMutableState::new_in_arc(value, Arc::new(NeverEqual)),
2923 watchers: RefCell::new(Vec::new()),
2924 runtime,
2925 }
2926 }
2927
2928 fn install_snapshot_observer(&self, state_id: StateId) {
2929 let runtime_handle = self.runtime.clone();
2930 self.state.add_apply_observer(Box::new(move || {
2931 let runtime = runtime_handle.clone();
2932 runtime_handle.enqueue_ui_task(Box::new(move || {
2933 runtime.with_state_arena(|arena| {
2934 if let Some(inner) = arena.get_typed_opt::<T>(state_id) {
2935 inner.invalidate_watchers();
2936 }
2937 });
2938 }));
2939 }));
2940 }
2941
2942 fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
2943 let value = self.state.get();
2944 f(&value)
2945 }
2946
2947 fn invalidate_watchers(&self) {
2948 let watchers: Vec<RecomposeScope> = {
2949 let mut watchers = self.watchers.borrow_mut();
2950 watchers.retain(|w| w.strong_count() > 0);
2951 watchers
2952 .iter()
2953 .filter_map(|w| w.upgrade())
2954 .map(|inner| RecomposeScope { inner })
2955 .collect()
2956 };
2957
2958 if input_debug_enabled() {
2959 let scope_ids: Vec<_> = watchers.iter().map(|scope| scope.id()).collect();
2960 eprintln!(
2961 "[CRANPOSE_INPUT_DEBUG] state {:?} invalidate_watchers count={} scopes={:?}",
2962 self.state.id(),
2963 scope_ids.len(),
2964 scope_ids
2965 );
2966 }
2967
2968 for watcher in watchers {
2969 watcher.invalidate();
2970 }
2971 }
2972}
2973
2974#[derive(Clone)]
2975pub struct State<T: Clone + 'static> {
2976 id: StateId,
2977 runtime_id: RuntimeId,
2978 _marker: PhantomData<fn() -> T>,
2979}
2980
2981impl<T: Clone + 'static> Copy for State<T> {}
2982
2983#[derive(Clone)]
2984pub struct MutableState<T: Clone + 'static> {
2985 id: StateId,
2986 runtime_id: RuntimeId,
2987 _marker: PhantomData<fn() -> T>,
2988}
2989
2990impl<T: Clone + 'static> Copy for MutableState<T> {}
2991
2992impl<T: Clone + 'static> PartialEq for State<T> {
2993 fn eq(&self, other: &Self) -> bool {
2994 self.id == other.id && self.runtime_id == other.runtime_id
2995 }
2996}
2997
2998impl<T: Clone + 'static> Eq for State<T> {}
2999
3000impl<T: Clone + 'static> PartialEq for MutableState<T> {
3001 fn eq(&self, other: &Self) -> bool {
3002 self.id == other.id && self.runtime_id == other.runtime_id
3003 }
3004}
3005
3006impl<T: Clone + 'static> Eq for MutableState<T> {}
3007
3008impl<T: Clone + 'static> State<T> {
3009 fn runtime_handle(&self) -> RuntimeHandle {
3010 runtime_handle_for(self.runtime_id).expect("runtime handle missing")
3011 }
3012
3013 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3014 self.runtime_handle().with_state_arena(|arena| {
3015 let inner = arena.get_typed::<T>(self.id);
3016 f(&inner)
3017 })
3018 }
3019
3020 fn subscribe_current_scope(&self) {
3021 if let Some(Some(scope)) =
3022 with_current_composer_opt(|composer| composer.current_recranpose_scope())
3023 {
3024 self.with_inner(|inner| {
3025 let mut watchers = inner.watchers.borrow_mut();
3026 watchers.retain(|w| w.strong_count() > 0);
3027 let id = scope.id();
3028 let already_registered = watchers
3029 .iter()
3030 .any(|w| w.upgrade().map(|inner| inner.id == id).unwrap_or(false));
3031 if !already_registered {
3032 watchers.push(scope.downgrade());
3033 if input_debug_enabled() {
3034 eprintln!(
3035 "[CRANPOSE_INPUT_DEBUG] state {:?} subscribe scope={} watchers={}",
3036 inner.state.id(),
3037 id,
3038 watchers.len()
3039 );
3040 }
3041 }
3042 });
3043 }
3044 }
3045
3046 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3047 self.subscribe_current_scope();
3048 self.with_inner(|inner| inner.with_value(f))
3049 }
3050
3051 pub fn value(&self) -> T {
3052 self.subscribe_current_scope();
3053 self.with(|value| value.clone())
3054 }
3055
3056 pub fn get(&self) -> T {
3057 self.value()
3058 }
3059}
3060
3061impl<T: Clone + 'static> MutableState<T> {
3062 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
3063 let id = runtime.alloc_state(value);
3064 Self {
3065 id,
3066 runtime_id: runtime.id(),
3067 _marker: PhantomData,
3068 }
3069 }
3070
3071 fn runtime_handle(&self) -> RuntimeHandle {
3072 runtime_handle_for(self.runtime_id).expect("runtime handle missing")
3073 }
3074
3075 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3076 self.runtime_handle().with_state_arena(|arena| {
3077 let inner = arena.get_typed::<T>(self.id);
3078 f(&inner)
3079 })
3080 }
3081
3082 pub fn as_state(&self) -> State<T> {
3083 State {
3084 id: self.id,
3085 runtime_id: self.runtime_id,
3086 _marker: PhantomData,
3087 }
3088 }
3089
3090 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3091 self.as_state().with(f)
3092 }
3093
3094 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
3095 let runtime = self.runtime_handle();
3096 runtime.assert_ui_thread();
3097 runtime.with_state_arena(|arena| {
3098 let inner = arena.get_typed::<T>(self.id);
3099 let mut value = inner.state.get();
3100 let tracker = UpdateScope::new(inner.state.id());
3101 let result = f(&mut value);
3102 let wrote_elsewhere = tracker.finish();
3103 if !wrote_elsewhere {
3104 inner.state.set(value);
3105 }
3106 inner.invalidate_watchers();
3107 result
3108 })
3109 }
3110
3111 pub fn replace(&self, value: T) {
3112 let runtime = self.runtime_handle();
3113 runtime.assert_ui_thread();
3114 runtime.with_state_arena(|arena| {
3115 let inner = arena.get_typed::<T>(self.id);
3116 inner.state.set(value);
3117 inner.invalidate_watchers();
3118 });
3119 }
3120
3121 pub fn set_value(&self, value: T) {
3122 self.replace(value);
3123 }
3124
3125 pub fn set(&self, value: T) {
3126 self.replace(value);
3127 }
3128
3129 pub fn value(&self) -> T {
3130 self.as_state().value()
3131 }
3132
3133 pub fn get(&self) -> T {
3134 self.value()
3135 }
3136
3137 pub fn get_non_reactive(&self) -> T {
3150 self.with_inner(|inner| inner.state.get())
3152 }
3153
3154 #[cfg(test)]
3155 pub(crate) fn watcher_count(&self) -> usize {
3156 self.with_inner(|inner| inner.watchers.borrow().len())
3157 }
3158}
3159
3160impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
3161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3162 self.with_inner(|inner| {
3163 inner.with_value(|value| {
3164 f.debug_struct("MutableState")
3165 .field("value", value)
3166 .finish()
3167 })
3168 })
3169 }
3170}
3171
3172#[derive(Clone)]
3173pub struct SnapshotStateList<T: Clone + 'static> {
3174 state: MutableState<Vec<T>>,
3175}
3176
3177impl<T: Clone + 'static> SnapshotStateList<T> {
3178 pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
3179 where
3180 I: IntoIterator<Item = T>,
3181 {
3182 let initial: Vec<T> = values.into_iter().collect();
3183 Self {
3184 state: MutableState::with_runtime(initial, runtime),
3185 }
3186 }
3187
3188 pub fn as_state(&self) -> State<Vec<T>> {
3189 self.state.as_state()
3190 }
3191
3192 pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
3193 self.state
3194 }
3195
3196 pub fn len(&self) -> usize {
3197 self.state.with(|values| values.len())
3198 }
3199
3200 pub fn is_empty(&self) -> bool {
3201 self.len() == 0
3202 }
3203
3204 pub fn to_vec(&self) -> Vec<T> {
3205 self.state.with(|values| values.clone())
3206 }
3207
3208 pub fn iter(&self) -> Vec<T> {
3209 self.to_vec()
3210 }
3211
3212 pub fn get(&self, index: usize) -> T {
3213 self.state.with(|values| values[index].clone())
3214 }
3215
3216 pub fn get_opt(&self, index: usize) -> Option<T> {
3217 self.state.with(|values| values.get(index).cloned())
3218 }
3219
3220 pub fn first(&self) -> Option<T> {
3221 self.get_opt(0)
3222 }
3223
3224 pub fn last(&self) -> Option<T> {
3225 self.state.with(|values| values.last().cloned())
3226 }
3227
3228 pub fn push(&self, value: T) {
3229 self.state.update(|values| values.push(value));
3230 }
3231
3232 pub fn extend<I>(&self, iter: I)
3233 where
3234 I: IntoIterator<Item = T>,
3235 {
3236 self.state.update(|values| values.extend(iter));
3237 }
3238
3239 pub fn insert(&self, index: usize, value: T) {
3240 self.state.update(|values| values.insert(index, value));
3241 }
3242
3243 pub fn set(&self, index: usize, value: T) -> T {
3244 self.state
3245 .update(|values| std::mem::replace(&mut values[index], value))
3246 }
3247
3248 pub fn remove(&self, index: usize) -> T {
3249 self.state.update(|values| values.remove(index))
3250 }
3251
3252 pub fn pop(&self) -> Option<T> {
3253 self.state.update(|values| values.pop())
3254 }
3255
3256 pub fn clear(&self) {
3257 self.state.replace(Vec::new());
3258 }
3259
3260 pub fn retain<F>(&self, mut predicate: F)
3261 where
3262 F: FnMut(&T) -> bool,
3263 {
3264 self.state
3265 .update(|values| values.retain(|value| predicate(value)));
3266 }
3267
3268 pub fn replace_with<I>(&self, iter: I)
3269 where
3270 I: IntoIterator<Item = T>,
3271 {
3272 self.state.replace(iter.into_iter().collect());
3273 }
3274}
3275
3276impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
3277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3278 let contents = self.to_vec();
3279 f.debug_struct("SnapshotStateList")
3280 .field("values", &contents)
3281 .finish()
3282 }
3283}
3284
3285#[derive(Clone)]
3286pub struct SnapshotStateMap<K, V>
3287where
3288 K: Clone + Eq + Hash + 'static,
3289 V: Clone + 'static,
3290{
3291 state: MutableState<HashMap<K, V>>,
3292}
3293
3294impl<K, V> SnapshotStateMap<K, V>
3295where
3296 K: Clone + Eq + Hash + 'static,
3297 V: Clone + 'static,
3298{
3299 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
3300 where
3301 I: IntoIterator<Item = (K, V)>,
3302 {
3303 let map: HashMap<K, V> = pairs.into_iter().collect();
3304 Self {
3305 state: MutableState::with_runtime(map, runtime),
3306 }
3307 }
3308
3309 pub fn as_state(&self) -> State<HashMap<K, V>> {
3310 self.state.as_state()
3311 }
3312
3313 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
3314 self.state
3315 }
3316
3317 pub fn len(&self) -> usize {
3318 self.state.with(|map| map.len())
3319 }
3320
3321 pub fn is_empty(&self) -> bool {
3322 self.state.with(|map| map.is_empty())
3323 }
3324
3325 pub fn contains_key(&self, key: &K) -> bool {
3326 self.state.with(|map| map.contains_key(key))
3327 }
3328
3329 pub fn get(&self, key: &K) -> Option<V> {
3330 self.state.with(|map| map.get(key).cloned())
3331 }
3332
3333 pub fn to_hash_map(&self) -> HashMap<K, V> {
3334 self.state.with(|map| map.clone())
3335 }
3336
3337 pub fn insert(&self, key: K, value: V) -> Option<V> {
3338 self.state.update(|map| map.insert(key, value))
3339 }
3340
3341 pub fn extend<I>(&self, iter: I)
3342 where
3343 I: IntoIterator<Item = (K, V)>,
3344 {
3345 self.state.update(|map| map.extend(iter));
3346 }
3348
3349 pub fn remove(&self, key: &K) -> Option<V> {
3350 self.state.update(|map| map.remove(key))
3351 }
3352
3353 pub fn clear(&self) {
3354 self.state.replace(HashMap::default());
3355 }
3356
3357 pub fn retain<F>(&self, mut predicate: F)
3358 where
3359 F: FnMut(&K, &mut V) -> bool,
3360 {
3361 self.state.update(|map| map.retain(|k, v| predicate(k, v)));
3362 }
3363}
3364
3365impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
3366where
3367 K: Clone + Eq + Hash + fmt::Debug + 'static,
3368 V: Clone + fmt::Debug + 'static,
3369{
3370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3371 let contents = self.to_hash_map();
3372 f.debug_struct("SnapshotStateMap")
3373 .field("entries", &contents)
3374 .finish()
3375 }
3376}
3377
3378struct DerivedState<T: Clone + 'static> {
3379 compute: Rc<dyn Fn() -> T>, state: MutableState<T>,
3381}
3382
3383impl<T: Clone + 'static> DerivedState<T> {
3384 fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
3385 let initial = compute();
3387 Self {
3388 compute,
3389 state: MutableState::with_runtime(initial, runtime),
3390 }
3391 }
3392
3393 fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
3394 self.compute = compute;
3396 }
3397
3398 fn recompute(&self) {
3399 let value = (self.compute)();
3400 self.state.set_value(value);
3401 }
3402}
3403
3404impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
3405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3406 self.with_inner(|inner| {
3407 inner.with_value(|value| f.debug_struct("State").field("value", value).finish())
3408 })
3409 }
3410}
3411
3412pub struct ParamState<T> {
3413 value: Option<T>,
3414}
3415
3416impl<T> ParamState<T> {
3417 pub fn update(&mut self, new_value: &T) -> bool
3418 where
3419 T: PartialEq + Clone,
3420 {
3421 match &self.value {
3422 Some(old) if old == new_value => false,
3423 _ => {
3424 self.value = Some(new_value.clone());
3425 true
3426 }
3427 }
3428 }
3429
3430 pub fn value(&self) -> Option<T>
3431 where
3432 T: Clone,
3433 {
3434 self.value.clone()
3435 }
3436}
3437
3438pub struct ParamSlot<T> {
3441 val: RefCell<Option<T>>,
3442}
3443
3444impl<T> Default for ParamSlot<T> {
3445 fn default() -> Self {
3446 Self {
3447 val: RefCell::new(None),
3448 }
3449 }
3450}
3451
3452impl<T> ParamSlot<T> {
3453 pub fn set(&self, v: T) {
3454 *self.val.borrow_mut() = Some(v);
3455 }
3456
3457 pub fn take(&self) -> T {
3459 self.val
3460 .borrow_mut()
3461 .take()
3462 .expect("ParamSlot take() called before set")
3463 }
3464}
3465
3466#[derive(Clone)]
3470pub struct CallbackHolder {
3471 rc: Rc<RefCell<Box<dyn FnMut()>>>,
3472}
3473
3474impl CallbackHolder {
3475 pub fn new() -> Self {
3477 Self::default()
3478 }
3479
3480 pub fn update<F>(&self, f: F)
3482 where
3483 F: FnMut() + 'static,
3484 {
3485 *self.rc.borrow_mut() = Box::new(f);
3486 }
3487
3488 pub fn clone_rc(&self) -> impl Fn() + 'static {
3490 let rc = self.rc.clone();
3491 move || {
3492 (rc.borrow_mut())();
3493 }
3494 }
3495}
3496
3497impl Default for CallbackHolder {
3498 fn default() -> Self {
3499 Self {
3500 rc: Rc::new(RefCell::new(Box::new(|| {}) as Box<dyn FnMut()>)),
3501 }
3502 }
3503}
3504
3505#[derive(Clone)]
3508pub struct CallbackHolder1<A: 'static> {
3509 #[allow(clippy::type_complexity)]
3510 rc: Rc<RefCell<Box<dyn FnMut(A)>>>,
3511}
3512
3513impl<A: 'static> CallbackHolder1<A> {
3514 pub fn new() -> Self {
3516 Self::default()
3517 }
3518
3519 pub fn update<F>(&self, f: F)
3521 where
3522 F: FnMut(A) + 'static,
3523 {
3524 *self.rc.borrow_mut() = Box::new(f);
3525 }
3526
3527 pub fn clone_rc(&self) -> impl Fn(A) + 'static {
3529 let rc = self.rc.clone();
3530 move |arg| {
3531 (rc.borrow_mut())(arg);
3532 }
3533 }
3534}
3535
3536impl<A: 'static> Default for CallbackHolder1<A> {
3537 fn default() -> Self {
3538 Self {
3539 rc: Rc::new(RefCell::new(Box::new(|_| {}) as Box<dyn FnMut(A)>)),
3540 }
3541 }
3542}
3543
3544pub struct ReturnSlot<T> {
3545 value: Option<T>,
3546}
3547
3548impl<T: Clone> ReturnSlot<T> {
3549 pub fn store(&mut self, value: T) {
3550 self.value = Some(value);
3551 }
3552
3553 pub fn get(&self) -> Option<T> {
3554 self.value.clone()
3555 }
3556}
3557
3558impl<T> Default for ParamState<T> {
3559 fn default() -> Self {
3560 Self { value: None }
3561 }
3562}
3563
3564impl<T> Default for ReturnSlot<T> {
3565 fn default() -> Self {
3566 Self { value: None }
3567 }
3568}
3569
3570pub struct Composition<A: Applier + 'static> {
3571 slots: Rc<SlotsHost>,
3572 applier: Rc<ConcreteApplierHost<A>>,
3573 runtime: Runtime,
3574 observer: SnapshotStateObserver,
3575 root: Option<NodeId>,
3576}
3577
3578impl<A: Applier + 'static> Composition<A> {
3579 pub fn new(applier: A) -> Self {
3580 Self::with_runtime(applier, Runtime::new(Arc::new(DefaultScheduler)))
3581 }
3582
3583 pub fn with_runtime(applier: A, runtime: Runtime) -> Self {
3584 Self::with_backend(applier, runtime, SlotBackendKind::default())
3585 }
3586
3587 pub fn with_backend(applier: A, runtime: Runtime, backend_kind: SlotBackendKind) -> Self {
3588 let storage = make_backend(backend_kind);
3589 let slots = Rc::new(SlotsHost::new(storage));
3590 let applier = Rc::new(ConcreteApplierHost::new(applier));
3591 let observer_handle = runtime.handle();
3592 let observer = SnapshotStateObserver::new(move |callback| {
3593 observer_handle.enqueue_ui_task(callback);
3594 });
3595 observer.start();
3596 Self {
3597 slots,
3598 applier,
3599 runtime,
3600 observer,
3601 root: None,
3602 }
3603 }
3604
3605 fn slots_host(&self) -> Rc<SlotsHost> {
3606 Rc::clone(&self.slots)
3607 }
3608
3609 fn applier_host(&self) -> Rc<dyn ApplierHost> {
3610 self.applier.clone()
3611 }
3612
3613 pub fn render(&mut self, key: Key, mut content: impl FnMut()) -> Result<(), NodeError> {
3614 self.slots.borrow_mut().reset();
3615 let runtime_handle = self.runtime_handle();
3616 runtime_handle.drain_ui();
3617 let composer = Composer::new(
3618 Rc::clone(&self.slots),
3619 self.applier.clone(),
3620 runtime_handle.clone(),
3621 self.observer.clone(),
3622 self.root,
3623 );
3624 self.observer.begin_frame();
3625 let (root, mut commands, side_effects) = composer.install(|composer| {
3626 composer.with_group(key, |_| content());
3627 let root = composer.root();
3628 let commands = composer.take_commands();
3629 let side_effects = composer.take_side_effects();
3630 (root, commands, side_effects)
3631 });
3632
3633 {
3634 let mut applier = self.applier.borrow_dyn();
3635 for mut command in commands.drain(..) {
3636 command(&mut *applier)?;
3637 }
3638 for mut update in runtime_handle.take_updates() {
3639 update(&mut *applier)?;
3640 }
3641 }
3642
3643 runtime_handle.drain_ui();
3644 for effect in side_effects {
3645 effect();
3646 }
3647 runtime_handle.drain_ui();
3648 self.root = root;
3649 {
3650 let mut slots = self.slots.borrow_mut();
3651 let _ = slots.finalize_current_group();
3652 slots.flush();
3653 }
3654 let _ = self.process_invalid_scopes()?;
3655 if !self.runtime.has_updates()
3656 && !runtime_handle.has_invalid_scopes()
3657 && !runtime_handle.has_frame_callbacks()
3658 && !runtime_handle.has_pending_ui()
3659 {
3660 self.runtime.set_needs_frame(false);
3661 }
3662 Ok(())
3663 }
3664
3665 pub fn should_render(&self) -> bool {
3674 self.runtime.needs_frame() || self.runtime.has_updates()
3675 }
3676
3677 pub fn runtime_handle(&self) -> RuntimeHandle {
3678 self.runtime.handle()
3679 }
3680
3681 pub fn applier_mut(&mut self) -> ApplierGuard<'_, A> {
3682 ApplierGuard::new(self.applier.borrow_typed())
3683 }
3684
3685 pub fn root(&self) -> Option<NodeId> {
3686 self.root
3687 }
3688
3689 pub fn debug_dump_slot_table_groups(&self) -> Vec<(usize, Key, Option<ScopeId>, usize)> {
3690 self.slots.borrow().debug_dump_groups()
3691 }
3692
3693 pub fn debug_dump_all_slots(&self) -> Vec<(usize, String)> {
3694 self.slots.borrow().debug_dump_all_slots()
3695 }
3696
3697 pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
3698 let runtime_handle = self.runtime_handle();
3699 let mut did_recompose = false;
3700 let mut loop_count = 0;
3701 loop {
3702 loop_count += 1;
3703 if loop_count > 100 {
3704 log::error!("process_invalid_scopes looped too many times! Breaking loop to prevent freeze.");
3705 break;
3706 }
3707 runtime_handle.drain_ui();
3708 let pending = runtime_handle.take_invalidated_scopes();
3709 if pending.is_empty() {
3710 break;
3711 }
3712 if input_debug_enabled() {
3713 let pending_ids: Vec<_> = pending.iter().map(|(id, _)| *id).collect();
3714 eprintln!(
3715 "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes pending_ids={:?}",
3716 pending_ids
3717 );
3718 }
3719 let mut scopes = Vec::new();
3720 for (id, weak) in pending {
3721 if let Some(inner) = weak.upgrade() {
3722 scopes.push(RecomposeScope { inner });
3723 } else {
3724 runtime_handle.mark_scope_recomposed(id);
3725 }
3726 }
3727 if scopes.is_empty() {
3728 continue;
3729 }
3730 if input_debug_enabled() {
3731 let scope_ids: Vec<_> = scopes.iter().map(|scope| scope.id()).collect();
3732 eprintln!(
3733 "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes live_scope_ids={:?}",
3734 scope_ids
3735 );
3736 }
3737 did_recompose = true;
3738 let runtime_clone = runtime_handle.clone();
3739 let root_host = self.slots_host();
3740 let mut scope_groups: Vec<(Rc<SlotsHost>, Vec<RecomposeScope>)> = Vec::new();
3741 for scope in scopes {
3742 let host = scope.slots_host().unwrap_or_else(|| Rc::clone(&root_host));
3743 if let Some((_, group)) = scope_groups
3744 .iter_mut()
3745 .find(|(existing, _)| Rc::ptr_eq(existing, &host))
3746 {
3747 group.push(scope);
3748 } else {
3749 scope_groups.push((host, vec![scope]));
3750 }
3751 }
3752 let (mut commands, side_effects) = {
3753 let composer = Composer::new(
3754 Rc::clone(&root_host),
3755 self.applier_host(),
3756 runtime_clone,
3757 self.observer.clone(),
3758 self.root,
3759 );
3760 self.observer.begin_frame();
3761 composer.install(|composer| {
3762 for (host, scopes) in scope_groups.into_iter() {
3763 if Rc::ptr_eq(&host, &root_host) {
3764 for scope in scopes.iter() {
3765 composer.recranpose_group(scope);
3766 }
3767 } else {
3768 composer.with_slot_override(host, |composer| {
3769 for scope in scopes.iter() {
3770 composer.recranpose_group(scope);
3771 }
3772 });
3773 }
3774 }
3775 let commands = composer.take_commands();
3776 let side_effects = composer.take_side_effects();
3777 (commands, side_effects)
3778 })
3779 };
3780 {
3781 let mut applier = self.applier.borrow_dyn();
3782 for mut command in commands.drain(..) {
3783 command(&mut *applier)?;
3784 }
3785 for mut update in runtime_handle.take_updates() {
3786 update(&mut *applier)?;
3787 }
3788 }
3789 for effect in side_effects {
3790 effect();
3791 }
3792 runtime_handle.drain_ui();
3793 }
3794 if !self.runtime.has_updates()
3795 && !runtime_handle.has_invalid_scopes()
3796 && !runtime_handle.has_frame_callbacks()
3797 && !runtime_handle.has_pending_ui()
3798 {
3799 self.runtime.set_needs_frame(false);
3800 }
3801 Ok(did_recompose)
3802 }
3803
3804 pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
3805 let updates = self.runtime_handle().take_updates();
3806 let mut applier = self.applier.borrow_dyn();
3807 for mut update in updates {
3808 update(&mut *applier)?;
3809 }
3810 Ok(())
3811 }
3812}
3813
3814impl<A: Applier + 'static> Drop for Composition<A> {
3815 fn drop(&mut self) {
3816 self.observer.stop();
3817 }
3818}
3819pub fn location_key(file: &str, line: u32, column: u32) -> Key {
3820 let base = file.as_ptr() as u64;
3821 base
3822 .wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ ((line as u64) << 32)
3824 ^ (column as u64)
3825}
3826
3827fn hash_key<K: Hash>(key: &K) -> Key {
3828 let mut hasher = hash::default::new();
3829 key.hash(&mut hasher);
3830 hasher.finish()
3831}
3832
3833#[cfg(test)]
3834#[path = "tests/lib_tests.rs"]
3835mod tests;
3836
3837#[cfg(test)]
3838#[path = "tests/recursive_decrease_increase_test.rs"]
3839mod recursive_decrease_increase_test;
3840
3841#[cfg(test)]
3842#[path = "tests/slot_backend_tests.rs"]
3843mod slot_backend_tests;
3844
3845pub mod collections;
3846pub mod hash;