1use std::{hash::Hash, rc::Rc, sync::Arc};
2
3use crate::{
4 composer_context,
5 owned::Owned,
6 runtime,
7 state::{
8 DerivedState, MutableState, OwnedMutableState, SnapshotStateList, SnapshotStateMap, State,
9 StructuralEqual,
10 },
11};
12
13#[track_caller]
14pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Owned<T> {
15 let source = crate::caller_location_key();
16 composer_context::with_composer(|composer| composer.remember_at(source, init))
17}
18
19#[allow(non_snake_case)]
27#[track_caller]
28pub fn rememberKeyed<K, T>(key: K, init: impl FnOnce(&K) -> T) -> T
29where
30 K: PartialEq + 'static,
31 T: Clone + 'static,
32{
33 let slot = remember(|| std::cell::RefCell::new(None::<(K, T)>));
34 slot.with(|cell| {
35 let mut stored = cell.borrow_mut();
36 match &*stored {
37 Some((stored_key, value)) if *stored_key == key => value.clone(),
38 _ => {
39 let value = init(&key);
40 *stored = Some((key, value.clone()));
41 value
42 }
43 }
44 })
45}
46
47#[allow(non_snake_case)]
75#[track_caller]
76pub fn rememberUpdatedState<T: Clone + 'static>(value: T) -> MutableState<T> {
77 let source = crate::caller_location_key();
78 composer_context::with_composer(|composer| {
79 let runtime = composer.runtime_handle();
80 let state = composer.remember_at(source, || {
81 OwnedMutableState::with_runtime(value.clone(), runtime)
82 });
83 state.with(|s| {
84 s.set(value);
85 s.handle()
86 })
87 })
88}
89
90#[cfg(feature = "internal")]
91#[allow(non_snake_case)]
92pub fn withFrameNanos(
93 callback: impl FnOnce(u64) + 'static,
94) -> crate::internal::FrameCallbackRegistration {
95 composer_context::with_composer(|composer| {
96 composer
97 .runtime_handle()
98 .frame_clock()
99 .with_frame_nanos(callback)
100 })
101}
102
103#[cfg(feature = "internal")]
104#[allow(non_snake_case)]
105pub fn withFrameMillis(
106 callback: impl FnOnce(u64) + 'static,
107) -> crate::internal::FrameCallbackRegistration {
108 composer_context::with_composer(|composer| {
109 composer
110 .runtime_handle()
111 .frame_clock()
112 .with_frame_millis(callback)
113 })
114}
115
116#[allow(non_snake_case)]
164pub fn mutableStateOf<T: Clone + PartialEq + 'static>(initial: T) -> MutableState<T> {
165 current_runtime("mutableStateOf")
166 .alloc_persistent_state_with_policy(initial, Arc::new(StructuralEqual))
167}
168
169#[allow(non_snake_case)]
175pub fn mutableStateOfNeverEqual<T: Clone + 'static>(initial: T) -> MutableState<T> {
176 current_runtime("mutableStateOfNeverEqual").alloc_persistent_state(initial)
177}
178
179#[allow(non_snake_case)]
180pub fn ownedMutableStateOf<T: Clone + PartialEq + 'static>(initial: T) -> OwnedMutableState<T> {
181 OwnedMutableState::with_runtime_structural_eq(initial, current_runtime("ownedMutableStateOf"))
182}
183
184#[allow(non_snake_case)]
186pub fn ownedMutableStateOfNeverEqual<T: Clone + 'static>(initial: T) -> OwnedMutableState<T> {
187 OwnedMutableState::with_runtime(initial, current_runtime("ownedMutableStateOfNeverEqual"))
188}
189
190fn current_runtime(what: &str) -> runtime::RuntimeHandle {
191 composer_context::try_with_composer(|composer| composer.runtime_handle())
192 .or_else(runtime::current_runtime_handle)
193 .unwrap_or_else(|| {
194 panic!(
195 "{what} requires an active runtime. Create state inside a composition or after a Runtime is created."
196 )
197 })
198}
199
200#[allow(non_snake_case)]
205pub fn try_mutableStateOf<T: Clone + PartialEq + 'static>(initial: T) -> Option<MutableState<T>> {
206 let runtime = composer_context::try_with_composer(|composer| composer.runtime_handle())
207 .or_else(runtime::current_runtime_handle)?;
208 Some(runtime.alloc_persistent_state_with_policy(initial, Arc::new(StructuralEqual)))
209}
210
211#[allow(non_snake_case)]
212pub fn mutableStateListOf<T, I>(values: I) -> SnapshotStateList<T>
213where
214 T: Clone + 'static,
215 I: IntoIterator<Item = T>,
216{
217 composer_context::with_composer(move |composer| composer.mutable_state_list_of(values))
218}
219
220#[allow(non_snake_case)]
221pub fn mutableStateList<T: Clone + 'static>() -> SnapshotStateList<T> {
222 mutableStateListOf(std::iter::empty::<T>())
223}
224
225#[allow(non_snake_case)]
226pub fn mutableStateMapOf<K, V, I>(pairs: I) -> SnapshotStateMap<K, V>
227where
228 K: Clone + Eq + Hash + 'static,
229 V: Clone + 'static,
230 I: IntoIterator<Item = (K, V)>,
231{
232 composer_context::with_composer(move |composer| composer.mutable_state_map_of(pairs))
233}
234
235#[allow(non_snake_case)]
236pub fn mutableStateMap<K, V>() -> SnapshotStateMap<K, V>
237where
238 K: Clone + Eq + Hash + 'static,
239 V: Clone + 'static,
240{
241 mutableStateMapOf(std::iter::empty::<(K, V)>())
242}
243
244#[allow(non_snake_case)]
272#[track_caller]
273pub fn rememberMutableStateOf<T: Clone + PartialEq + 'static>(
274 init: impl FnOnce() -> T,
275) -> MutableState<T> {
276 let source = crate::caller_location_key();
277 composer_context::with_composer(|composer| {
278 let runtime = composer.runtime_handle();
279 composer
280 .remember_at(source, || {
281 OwnedMutableState::with_runtime_structural_eq(init(), runtime)
282 })
283 .with(|state| state.handle())
284 })
285}
286
287#[allow(non_snake_case)]
288#[track_caller]
289pub fn rememberMutableStateOfNeverEqual<T: Clone + 'static>(
290 init: impl FnOnce() -> T,
291) -> MutableState<T> {
292 let source = crate::caller_location_key();
293 composer_context::with_composer(|composer| {
294 let runtime = composer.runtime_handle();
295 composer
296 .remember_at(source, || OwnedMutableState::with_runtime(init(), runtime))
297 .with(|state| state.handle())
298 })
299}
300
301#[allow(non_snake_case)]
302#[track_caller]
303pub fn derivedStateOf<T: 'static + Clone>(compute: impl Fn() -> T + 'static) -> State<T> {
304 let source = crate::caller_location_key();
305 composer_context::with_composer(|composer| {
306 composer.with_group(source, |composer| {
307 let should_recompute = composer
308 .current_recompose_scope()
309 .map(|scope| scope.should_recompose())
310 .unwrap_or(true);
311 let runtime = composer.runtime_handle();
312 let compute_rc: Rc<dyn Fn() -> T> = Rc::new(compute);
313 let derived = composer.remember_at(source, || {
314 DerivedState::new(runtime.clone(), compute_rc.clone())
315 });
316 derived.update(|derived| {
317 derived.set_compute(compute_rc.clone());
318 if should_recompute {
319 derived.recompute();
320 }
321 });
322 derived.with(|derived| derived.state.as_state())
323 })
324 })
325}