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, PartialEq, Eq)]
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 runtime
56 .children
57 .borrow_mut()
58 .entry(self.0)
59 .or_default()
60 .insert(child);
61 runtime.parents.borrow_mut().insert(child, self.0);
62 });
63 Scope(child, PhantomData)
64 }
65
66 pub fn set_parent(&self, new_parent: Scope) {
82 RUNTIME.with(|runtime| {
83 if let Some(old_parent) = runtime.parents.borrow_mut().remove(&self.0) {
85 if let Some(children) = runtime.children.borrow_mut().get_mut(&old_parent) {
86 children.remove(&self.0);
87 }
88 }
89
90 runtime
92 .children
93 .borrow_mut()
94 .entry(new_parent.0)
95 .or_default()
96 .insert(self.0);
97
98 runtime.parents.borrow_mut().insert(self.0, new_parent.0);
100 });
101 }
102
103 pub fn parent(&self) -> Option<Scope> {
105 RUNTIME.with(|runtime| {
106 runtime
107 .parents
108 .borrow()
109 .get(&self.0)
110 .map(|id| Scope(*id, PhantomData))
111 })
112 }
113
114 #[cfg_attr(debug_assertions, track_caller)]
116 pub fn create_signal<T>(self, value: T) -> (ReadSignal<T>, WriteSignal<T>)
117 where
118 T: Any + 'static,
119 {
120 self.enter(|| RwSignal::new_split(value))
121 }
122
123 #[cfg_attr(debug_assertions, track_caller)]
125 pub fn create_rw_signal<T>(self, value: T) -> RwSignal<T>
126 where
127 T: Any + 'static,
128 {
129 self.enter(|| RwSignal::new(value))
130 }
131
132 #[cfg_attr(debug_assertions, track_caller)]
134 pub fn create_sync_signal<T>(
135 self,
136 value: T,
137 ) -> (ReadSignal<T, SyncStorage>, WriteSignal<T, SyncStorage>)
138 where
139 T: Any + Send + Sync + 'static,
140 {
141 self.enter(|| RwSignal::<T, SyncStorage>::new_sync_split(value))
142 }
143
144 #[cfg_attr(debug_assertions, track_caller)]
146 pub fn create_sync_rw_signal<T>(self, value: T) -> RwSignal<T, SyncStorage>
147 where
148 T: Any + Send + Sync + 'static,
149 {
150 self.enter(|| RwSignal::<T, SyncStorage>::new_sync(value))
151 }
152
153 #[cfg_attr(debug_assertions, track_caller)]
155 pub fn create_local_signal<T>(
156 self,
157 value: T,
158 ) -> (ReadSignal<T, UnsyncStorage>, WriteSignal<T, UnsyncStorage>)
159 where
160 T: Any + 'static,
161 {
162 self.enter(|| RwSignal::new_split(value))
163 }
164
165 #[cfg_attr(debug_assertions, track_caller)]
167 pub fn create_local_rw_signal<T>(self, value: T) -> RwSignal<T, UnsyncStorage>
168 where
169 T: Any + 'static,
170 {
171 self.enter(|| RwSignal::new(value))
172 }
173
174 #[cfg_attr(debug_assertions, track_caller)]
176 pub fn create_memo<T>(self, f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
177 where
178 T: PartialEq + 'static,
179 {
180 self.enter(|| create_memo(f))
181 }
182
183 #[cfg_attr(debug_assertions, track_caller)]
185 pub fn create_trigger(self) -> Trigger {
186 self.enter(create_trigger)
187 }
188
189 #[cfg_attr(debug_assertions, track_caller)]
191 pub fn create_effect<T>(self, f: impl Fn(Option<T>) -> T + 'static)
192 where
193 T: Any + 'static,
194 {
195 self.enter(|| create_effect(f))
196 }
197
198 #[cfg_attr(debug_assertions, track_caller)]
200 pub fn create_updater<R>(
201 self,
202 compute: impl Fn() -> R + 'static,
203 on_change: impl Fn(R) + 'static,
204 ) -> R
205 where
206 R: 'static,
207 {
208 self.enter(|| create_updater(compute, on_change))
209 }
210
211 pub fn provide_context<T>(&self, value: T)
230 where
231 T: Clone + 'static,
232 {
233 self.enter(|| crate::context::Context::provide(value))
234 }
235
236 pub fn get_context<T>(&self) -> Option<T>
257 where
258 T: Clone + 'static,
259 {
260 self.enter(crate::context::Context::get::<T>)
261 }
262
263 #[cfg_attr(debug_assertions, track_caller)]
265 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T
266 where
267 T: 'static,
268 {
269 Runtime::assert_ui_thread();
270 let prev_scope = RUNTIME.with(|runtime| {
271 let mut current_scope = runtime.current_scope.borrow_mut();
272 let prev_scope = *current_scope;
273 *current_scope = self.0;
274 prev_scope
275 });
276
277 let result = f();
278
279 RUNTIME.with(|runtime| {
280 *runtime.current_scope.borrow_mut() = prev_scope;
281 });
282
283 result
284 }
285
286 #[cfg_attr(debug_assertions, track_caller)]
288 pub fn enter_child<T, U>(&self, f: impl Fn(T) -> U + 'static) -> impl Fn(T) -> (U, Scope)
289 where
290 T: 'static,
291 {
292 Runtime::assert_ui_thread();
293 let parent = *self;
294 move |t| {
295 let scope = parent.create_child();
296 let prev_scope = RUNTIME.with(|runtime| {
297 let mut current_scope = runtime.current_scope.borrow_mut();
298 let prev_scope = *current_scope;
299 *current_scope = scope.0;
300 prev_scope
301 });
302
303 let result = f(t);
304
305 RUNTIME.with(|runtime| {
306 *runtime.current_scope.borrow_mut() = prev_scope;
307 });
308
309 (result, scope)
310 }
311 }
312
313 #[cfg_attr(debug_assertions, track_caller)]
316 pub fn track(&self) {
317 Runtime::assert_ui_thread();
318 let signal = if let Some(signal) = self.0.signal() {
319 signal
320 } else {
321 let signal = SignalState {
322 id: self.0,
323 subscribers: Arc::new(Mutex::new(HashSet::new())),
324 value: SignalValue::Local(Rc::new(RefCell::new(()))),
325 };
326 self.0.add_signal(signal.clone());
327 signal
328 };
329 signal.subscribe();
330 }
331
332 pub fn dispose(&self) {
335 self.0.dispose();
336 }
337}
338
339#[deprecated(
340 since = "0.2.0",
341 note = "Use Scope::enter instead; this will be removed in a future release"
342)]
343pub fn with_scope<T>(scope: Scope, f: impl FnOnce() -> T) -> T
345where
346 T: 'static,
347{
348 scope.enter(f)
349}
350
351#[deprecated(
354 since = "0.2.0",
355 note = "Use Scope::current().enter_child instead; this will be removed in a future release"
356)]
357pub fn as_child_of_current_scope<T, U>(f: impl Fn(T) -> U + 'static) -> impl Fn(T) -> (U, Scope)
358where
359 T: 'static,
360{
361 Scope::current().enter_child(f)
362}