1use std::{
2 any::Any, cell::RefCell, collections::HashSet, fmt, marker::PhantomData, rc::Rc, sync::Arc,
3};
4
5use parking_lot::Mutex;
6
7use crate::{
8 create_effect, create_updater,
9 id::Id,
10 memo::{create_memo, Memo},
11 runtime::{Runtime, RUNTIME},
12 signal::{ReadSignal, RwSignal, SignalState, SignalValue, WriteSignal},
13 storage::{SyncStorage, UnsyncStorage},
14 trigger::{create_trigger, Trigger},
15};
16
17#[derive(Clone, Copy)]
23pub struct Scope(pub(crate) Id, pub(crate) PhantomData<()>);
24
25impl Default for Scope {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl fmt::Debug for Scope {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 let mut s = f.debug_struct("Scope");
34 s.field("id", &self.0);
35 s.finish()
36 }
37}
38
39impl Scope {
40 pub fn new() -> Self {
42 Self(Id::next(), PhantomData)
43 }
44
45 pub fn current() -> Scope {
48 RUNTIME.with(|runtime| Scope(*runtime.current_scope.borrow(), PhantomData))
49 }
50
51 pub fn create_child(&self) -> Scope {
53 let child = Id::next();
54 RUNTIME.with(|runtime| {
55 let mut children = runtime.children.borrow_mut();
56 let children = children.entry(self.0).or_default();
57 children.insert(child);
58 });
59 Scope(child, PhantomData)
60 }
61
62 #[cfg_attr(debug_assertions, track_caller)]
64 pub fn create_signal<T>(self, value: T) -> (ReadSignal<T>, WriteSignal<T>)
65 where
66 T: Any + 'static,
67 {
68 self.enter(|| RwSignal::new_split(value))
69 }
70
71 #[cfg_attr(debug_assertions, track_caller)]
73 pub fn create_rw_signal<T>(self, value: T) -> RwSignal<T>
74 where
75 T: Any + 'static,
76 {
77 self.enter(|| RwSignal::new(value))
78 }
79
80 #[cfg_attr(debug_assertions, track_caller)]
82 pub fn create_sync_signal<T>(
83 self,
84 value: T,
85 ) -> (ReadSignal<T, SyncStorage>, WriteSignal<T, SyncStorage>)
86 where
87 T: Any + Send + Sync + 'static,
88 {
89 self.enter(|| RwSignal::<T, SyncStorage>::new_sync_split(value))
90 }
91
92 #[cfg_attr(debug_assertions, track_caller)]
94 pub fn create_sync_rw_signal<T>(self, value: T) -> RwSignal<T, SyncStorage>
95 where
96 T: Any + Send + Sync + 'static,
97 {
98 self.enter(|| RwSignal::<T, SyncStorage>::new_sync(value))
99 }
100
101 #[cfg_attr(debug_assertions, track_caller)]
103 pub fn create_local_signal<T>(
104 self,
105 value: T,
106 ) -> (ReadSignal<T, UnsyncStorage>, WriteSignal<T, UnsyncStorage>)
107 where
108 T: Any + 'static,
109 {
110 self.enter(|| RwSignal::new_split(value))
111 }
112
113 #[cfg_attr(debug_assertions, track_caller)]
115 pub fn create_local_rw_signal<T>(self, value: T) -> RwSignal<T, UnsyncStorage>
116 where
117 T: Any + 'static,
118 {
119 self.enter(|| RwSignal::new(value))
120 }
121
122 #[cfg_attr(debug_assertions, track_caller)]
124 pub fn create_memo<T>(self, f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
125 where
126 T: PartialEq + 'static,
127 {
128 self.enter(|| create_memo(f))
129 }
130
131 #[cfg_attr(debug_assertions, track_caller)]
133 pub fn create_trigger(self) -> Trigger {
134 self.enter(create_trigger)
135 }
136
137 #[cfg_attr(debug_assertions, track_caller)]
139 pub fn create_effect<T>(self, f: impl Fn(Option<T>) -> T + 'static)
140 where
141 T: Any + 'static,
142 {
143 self.enter(|| create_effect(f))
144 }
145
146 #[cfg_attr(debug_assertions, track_caller)]
148 pub fn create_updater<R>(
149 self,
150 compute: impl Fn() -> R + 'static,
151 on_change: impl Fn(R) + 'static,
152 ) -> R
153 where
154 R: 'static,
155 {
156 self.enter(|| create_updater(compute, on_change))
157 }
158
159 #[cfg_attr(debug_assertions, track_caller)]
161 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T
162 where
163 T: 'static,
164 {
165 Runtime::assert_ui_thread();
166 let prev_scope = RUNTIME.with(|runtime| {
167 let mut current_scope = runtime.current_scope.borrow_mut();
168 let prev_scope = *current_scope;
169 *current_scope = self.0;
170 prev_scope
171 });
172
173 let result = f();
174
175 RUNTIME.with(|runtime| {
176 *runtime.current_scope.borrow_mut() = prev_scope;
177 });
178
179 result
180 }
181
182 #[cfg_attr(debug_assertions, track_caller)]
184 pub fn enter_child<T, U>(&self, f: impl Fn(T) -> U + 'static) -> impl Fn(T) -> (U, Scope)
185 where
186 T: 'static,
187 {
188 Runtime::assert_ui_thread();
189 let parent = *self;
190 move |t| {
191 let scope = parent.create_child();
192 let prev_scope = RUNTIME.with(|runtime| {
193 let mut current_scope = runtime.current_scope.borrow_mut();
194 let prev_scope = *current_scope;
195 *current_scope = scope.0;
196 prev_scope
197 });
198
199 let result = f(t);
200
201 RUNTIME.with(|runtime| {
202 *runtime.current_scope.borrow_mut() = prev_scope;
203 });
204
205 (result, scope)
206 }
207 }
208
209 #[cfg_attr(debug_assertions, track_caller)]
212 pub fn track(&self) {
213 Runtime::assert_ui_thread();
214 let signal = if let Some(signal) = self.0.signal() {
215 signal
216 } else {
217 let signal = SignalState {
218 id: self.0,
219 subscribers: Arc::new(Mutex::new(HashSet::new())),
220 value: SignalValue::Local(Rc::new(RefCell::new(()))),
221 };
222 self.0.add_signal(signal.clone());
223 signal
224 };
225 signal.subscribe();
226 }
227
228 pub fn dispose(&self) {
231 self.0.dispose();
232 }
233}
234
235#[deprecated(
236 since = "0.2.0",
237 note = "Use Scope::enter instead; this will be removed in a future release"
238)]
239pub fn with_scope<T>(scope: Scope, f: impl FnOnce() -> T) -> T
241where
242 T: 'static,
243{
244 scope.enter(f)
245}
246
247#[deprecated(
250 since = "0.2.0",
251 note = "Use Scope::current().enter_child instead; this will be removed in a future release"
252)]
253pub fn as_child_of_current_scope<T, U>(f: impl Fn(T) -> U + 'static) -> impl Fn(T) -> (U, Scope)
254where
255 T: 'static,
256{
257 Scope::current().enter_child(f)
258}