1use zng_app_context::{AppLocalId, ContextLocal, ContextLocalKeyProvider, context_local};
2
3use super::*;
4
5#[macro_export]
49macro_rules! context_var {
50 ($(
51 $(#[$attr:meta])*
52 $vis:vis static $NAME:ident: $Type:ty = $default:expr;
53 )+) => {$(
54 $(#[$attr])*
55 $vis static $NAME: $crate::ContextVar<$Type> = {
56 $crate::types::context_local! {
57 static VAR: $crate::BoxedVar<$Type> = $crate::types::context_var_init::<$Type>($default);
58 }
59 $crate::ContextVar::new(&VAR)
60 };
61 )+}
62}
63
64#[doc(hidden)]
65pub fn context_var_init<T: VarValue>(init: impl IntoVar<T>) -> BoxedVar<T> {
66 init.into_var().boxed()
67}
68
69impl<T: VarValue> ContextLocalKeyProvider for ContextVar<T> {
70 fn context_local_key(&'static self) -> AppLocalId {
71 self.0.context_local_key()
72 }
73}
74
75#[derive(Clone)]
82pub struct ContextVar<T: VarValue>(&'static ContextLocal<BoxedVar<T>>);
83impl<T: VarValue> ContextVar<T> {
84 #[doc(hidden)]
85 pub const fn new(var: &'static ContextLocal<BoxedVar<T>>) -> Self {
86 Self(var)
87 }
88
89 pub fn with_context<R>(self, id: ContextInitHandle, var: &mut Option<Arc<BoxedVar<T>>>, action: impl FnOnce() -> R) -> R {
98 self.0.with_context_var(var, move || id.with_context(action))
99 }
100
101 pub fn with_context_var<R>(self, id: ContextInitHandle, var: impl IntoVar<T>, action: impl FnOnce() -> R) -> R {
112 let mut var = Some(Arc::new(var.into_var().actual_var().boxed()));
113 self.with_context(id, &mut var, action)
114 }
115}
116impl<T: VarValue> Copy for ContextVar<T> {}
117
118impl<T: VarValue> crate::private::Sealed for ContextVar<T> {}
119
120impl<T: VarValue> AnyVar for ContextVar<T> {
121 fn clone_any(&self) -> BoxedAnyVar {
122 Box::new(*self)
123 }
124
125 fn as_any(&self) -> &dyn Any {
126 self
127 }
128
129 fn as_unboxed_any(&self) -> &dyn Any {
130 self
131 }
132
133 fn double_boxed_any(self: Box<Self>) -> Box<dyn Any> {
134 let me: BoxedVar<T> = self;
135 Box::new(me)
136 }
137
138 fn var_type_id(&self) -> TypeId {
139 TypeId::of::<T>()
140 }
141
142 fn get_any(&self) -> Box<dyn AnyVarValue> {
143 Box::new(self.get())
144 }
145
146 fn with_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) {
147 self.with(|v| read(v))
148 }
149
150 fn with_new_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) -> bool {
151 self.with_new(|v| read(v)).is_some()
152 }
153
154 fn set_any(&self, value: Box<dyn AnyVarValue>) -> Result<(), VarIsReadOnlyError> {
155 self.modify(var_set_any(value))
156 }
157
158 fn last_update(&self) -> VarUpdateId {
159 self.0.get().last_update()
160 }
161
162 fn is_contextual(&self) -> bool {
163 true
164 }
165
166 fn capabilities(&self) -> VarCapability {
167 self.0.get().capabilities() | VarCapability::CAPS_CHANGE
168 }
169
170 fn hook_any(&self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
171 self.0.get().hook_any(pos_modify_action)
172 }
173
174 fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
175 self.0.get().hook_animation_stop(handler)
176 }
177
178 fn strong_count(&self) -> usize {
179 self.0.get().strong_count()
180 }
181
182 fn weak_count(&self) -> usize {
183 self.0.get().weak_count()
184 }
185
186 fn actual_var_any(&self) -> BoxedAnyVar {
187 self.0.get().actual_var_any()
188 }
189
190 fn downgrade_any(&self) -> BoxedAnyWeakVar {
191 self.0.get().downgrade_any()
192 }
193
194 fn is_animating(&self) -> bool {
195 self.0.get().is_animating()
196 }
197
198 fn modify_importance(&self) -> usize {
199 self.0.get().modify_importance()
200 }
201
202 fn var_ptr(&self) -> VarPtr {
203 VarPtr::new_ctx_local(self.0)
204 }
205
206 fn get_debug(&self) -> Txt {
207 self.with(var_debug)
208 }
209
210 fn update(&self) -> Result<(), VarIsReadOnlyError> {
211 Var::modify(self, var_update)
212 }
213
214 fn map_debug(&self) -> BoxedVar<Txt> {
215 Var::map(self, var_debug).boxed()
216 }
217}
218
219impl<T: VarValue> IntoVar<T> for ContextVar<T> {
220 type Var = Self;
221
222 fn into_var(self) -> Self::Var {
223 self
224 }
225}
226
227impl<T: VarValue> Var<T> for ContextVar<T> {
228 type ReadOnly = types::ReadOnlyVar<T, Self>;
229
230 type ActualVar = BoxedVar<T>;
231
232 type Downgrade = BoxedWeakVar<T>;
233
234 type Map<O: VarValue> = contextualized::ContextualizedVar<O>;
235 type MapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
236
237 type FlatMap<O: VarValue, V: Var<O>> = contextualized::ContextualizedVar<O>;
238
239 type FilterMap<O: VarValue> = contextualized::ContextualizedVar<O>;
240 type FilterMapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
241
242 type MapRef<O: VarValue> = types::MapRef<T, O, Self>;
243 type MapRefBidi<O: VarValue> = types::MapRefBidi<T, O, Self>;
244
245 type Easing = types::ContextualizedVar<T>;
246
247 fn with<R, F>(&self, read: F) -> R
248 where
249 F: FnOnce(&T) -> R,
250 {
251 self.0.get().with(read)
252 }
253
254 fn modify<F>(&self, modify: F) -> Result<(), VarIsReadOnlyError>
255 where
256 F: FnOnce(&mut VarModify<T>) + Send + 'static,
257 {
258 self.0.get().modify(modify)
259 }
260
261 fn actual_var(self) -> BoxedVar<T> {
262 self.0.get_clone().actual_var()
263 }
264
265 fn downgrade(&self) -> BoxedWeakVar<T> {
266 self.0.get().downgrade()
267 }
268
269 fn into_value(self) -> T {
270 self.get()
271 }
272
273 fn read_only(&self) -> Self::ReadOnly {
274 types::ReadOnlyVar::new(*self)
275 }
276
277 fn map<O, M>(&self, map: M) -> Self::Map<O>
278 where
279 O: VarValue,
280 M: FnMut(&T) -> O + Send + 'static,
281 {
282 var_map_ctx(self, map)
283 }
284
285 fn map_bidi<O, M, B>(&self, map: M, map_back: B) -> Self::MapBidi<O>
286 where
287 O: VarValue,
288 M: FnMut(&T) -> O + Send + 'static,
289 B: FnMut(&O) -> T + Send + 'static,
290 {
291 var_map_bidi_ctx(self, map, map_back)
292 }
293
294 fn flat_map<O, V, M>(&self, map: M) -> Self::FlatMap<O, V>
295 where
296 O: VarValue,
297 V: Var<O>,
298 M: FnMut(&T) -> V + Send + 'static,
299 {
300 var_flat_map_ctx(self, map)
301 }
302
303 fn filter_map<O, M, I>(&self, map: M, fallback: I) -> Self::FilterMap<O>
304 where
305 O: VarValue,
306 M: FnMut(&T) -> Option<O> + Send + 'static,
307 I: Fn() -> O + Send + Sync + 'static,
308 {
309 var_filter_map_ctx(self, map, fallback)
310 }
311
312 fn filter_map_bidi<O, M, B, I>(&self, map: M, map_back: B, fallback: I) -> Self::FilterMapBidi<O>
313 where
314 O: VarValue,
315 M: FnMut(&T) -> Option<O> + Send + 'static,
316 B: FnMut(&O) -> Option<T> + Send + 'static,
317 I: Fn() -> O + Send + Sync + 'static,
318 {
319 var_filter_map_bidi_ctx(self, map, map_back, fallback)
320 }
321
322 fn map_ref<O, M>(&self, map: M) -> Self::MapRef<O>
323 where
324 O: VarValue,
325 M: Fn(&T) -> &O + Send + Sync + 'static,
326 {
327 var_map_ref(self, map)
328 }
329
330 fn map_ref_bidi<O, M, B>(&self, map: M, map_mut: B) -> Self::MapRefBidi<O>
331 where
332 O: VarValue,
333 M: Fn(&T) -> &O + Send + Sync + 'static,
334 B: Fn(&mut T) -> &mut O + Send + Sync + 'static,
335 {
336 var_map_ref_bidi(self, map, map_mut)
337 }
338
339 fn easing<F>(&self, duration: Duration, easing: F) -> Self::Easing
340 where
341 T: Transitionable,
342 F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
343 {
344 var_easing_ctx(self, duration, easing)
345 }
346
347 fn easing_with<F, S>(&self, duration: Duration, easing: F, sampler: S) -> Self::Easing
348 where
349 T: Transitionable,
350 F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
351 S: Fn(&animation::Transition<T>, EasingStep) -> T + Send + Sync + 'static,
352 {
353 var_easing_with_ctx(self, duration, easing, sampler)
354 }
355}
356
357pub type ReadOnlyContextVar<T> = types::ReadOnlyVar<T, ContextVar<T>>;
359
360#[derive(Default)]
361struct ContextInitHandleMarker;
362
363#[derive(Clone, Default)]
371pub struct ContextInitHandle(Arc<ContextInitHandleMarker>);
372context_local! {
373 static CONTEXT_INIT_ID: ContextInitHandleMarker = ContextInitHandleMarker;
374}
375impl ContextInitHandle {
376 pub fn new() -> Self {
378 Self::default()
379 }
380
381 pub fn current() -> Self {
383 Self(CONTEXT_INIT_ID.get())
384 }
385
386 pub fn with_context<R>(&self, action: impl FnOnce() -> R) -> R {
390 let mut opt = Some(self.0.clone());
391 CONTEXT_INIT_ID.with_context(&mut opt, action)
392 }
393
394 pub fn downgrade(&self) -> WeakContextInitHandle {
396 WeakContextInitHandle(Arc::downgrade(&self.0))
397 }
398}
399impl fmt::Debug for ContextInitHandle {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.debug_tuple("ContextInitHandle").field(&Arc::as_ptr(&self.0)).finish()
402 }
403}
404impl PartialEq for ContextInitHandle {
405 fn eq(&self, other: &Self) -> bool {
406 Arc::ptr_eq(&self.0, &other.0)
407 }
408}
409impl Eq for ContextInitHandle {}
410impl std::hash::Hash for ContextInitHandle {
411 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
412 let i = Arc::as_ptr(&self.0) as usize;
413 std::hash::Hash::hash(&i, state)
414 }
415}
416
417#[derive(Clone, Default)]
419pub struct WeakContextInitHandle(std::sync::Weak<ContextInitHandleMarker>);
420impl WeakContextInitHandle {
421 pub fn is_alive(&self) -> bool {
423 self.0.strong_count() > 0
424 }
425}
426impl fmt::Debug for WeakContextInitHandle {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 f.debug_tuple("WeakContextInitHandle")
429 .field(&std::sync::Weak::as_ptr(&self.0))
430 .finish()
431 }
432}
433impl PartialEq for WeakContextInitHandle {
434 fn eq(&self, other: &Self) -> bool {
435 std::sync::Weak::ptr_eq(&self.0, &other.0)
436 }
437}
438impl Eq for WeakContextInitHandle {}
439impl std::hash::Hash for WeakContextInitHandle {
440 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
441 let i = std::sync::Weak::as_ptr(&self.0) as usize;
442 std::hash::Hash::hash(&i, state)
443 }
444}