zng_var/
context.rs

1use zng_app_context::{AppLocalId, ContextLocal, ContextLocalKeyProvider, context_local};
2
3use super::*;
4
5///<span data-del-macro-root></span> Declares new [`ContextVar`] static items.
6///
7/// # Examples
8///
9/// ```
10/// # use zng_var::context_var;
11/// # #[derive(Debug, Clone, PartialEq)]
12/// # struct NotConst(u8);
13/// # fn init_val() -> NotConst { NotConst(10) }
14/// #
15/// context_var! {
16///     /// A public documented context var.
17///     pub static FOO_VAR: u8 = 10;
18///
19///     // A private context var.
20///     static BAR_VAR: NotConst = init_val();
21///
22///     // A var that *inherits* from another.
23///     pub static DERIVED_VAR: u8 = FOO_VAR;
24/// }
25/// ```
26///
27/// # Default Value
28///
29/// All context variable have a default fallback value that is used when the variable is not set in the context.
30///
31/// The default value is instantiated once per app and is the value of the variable when it is not set in the context,
32/// any value [`IntoVar<T>`] is allowed, including other variables.
33///
34/// The default value can also be a [`Var::map`] to another context var, but note that mapping vars are contextualized,
35/// meaning that they evaluate the mapping in each different context read, so a context var with mapping value
36/// read in a thousand widgets will generate a thousand different mapping vars, but if the same var mapping is set
37/// in the root widget, the thousand widgets will all use the same mapping var.
38///
39/// # Naming Convention
40///
41/// It is recommended that the type name ends with the `_VAR` suffix.
42///
43/// # Context Local
44///
45/// If you are only interested in sharing a contextual value you can use the [`context_local!`] macro instead.
46///
47/// [`context_local!`]: crate::context::context_local
48#[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/// Represents another variable in a context.
76///
77/// Context variables are [`Var<T>`] implementers that represent a contextual value, unlike other variables it does not own
78/// the value it represents.
79///
80/// See [`context_var!`] for more details.
81#[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    /// Runs `action` with this context var representing the other `var` in the current thread.
90    ///
91    /// The `var` must be `Some` and must be the `actual_var`, it is moved to the context storage during the call.
92    ///
93    /// Note that the `var` must be the same for subsequent calls in the same *context*, otherwise [contextualized]
94    /// variables may not update their binding, in widgets you must re-init the descendants if you replace the `var`.
95    ///
96    /// [contextualized]: types::ContextualizedVar
97    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    /// Runs `action` with this context var representing the other `var` in the current thread.
102    ///
103    /// Note that the `var` must be the same for subsequent calls in the same *context*, otherwise [contextualized]
104    /// variables may not update their binding, in widgets you must re-init the descendants if you replace the `var`.
105    ///
106    /// The `var` is converted into var, the actual var, boxed and placed in a new `Arc`, you can use the [`with_context`]
107    /// method to avoid doing this in a hot path.
108    ///
109    /// [contextualized]: types::ContextualizedVar
110    /// [`with_context`]: Self::with_context
111    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
357/// Context var that is always read-only, even if it is representing a read-write var.
358pub type ReadOnlyContextVar<T> = types::ReadOnlyVar<T, ContextVar<T>>;
359
360#[derive(Default)]
361struct ContextInitHandleMarker;
362
363/// Identifies the unique context a [`ContextualizedVar`] is in.
364///
365/// Each node that sets context-vars have an unique ID, it is different after each (re)init. The [`ContextualizedVar`]
366/// records this ID, and rebuilds when it has changed. The contextualized inner vars are retained when the ID has at least one
367/// clone.
368///
369/// [`ContextualizedVar`]: crate::types::ContextualizedVar
370#[derive(Clone, Default)]
371pub struct ContextInitHandle(Arc<ContextInitHandleMarker>);
372context_local! {
373    static CONTEXT_INIT_ID: ContextInitHandleMarker = ContextInitHandleMarker;
374}
375impl ContextInitHandle {
376    /// Generates a new unique handle.
377    pub fn new() -> Self {
378        Self::default()
379    }
380
381    /// Gets the current context handle.
382    pub fn current() -> Self {
383        Self(CONTEXT_INIT_ID.get())
384    }
385
386    /// Runs `action` with `self` as the current context ID.
387    ///
388    /// Note that [`ContextVar::with_context`] already calls this method.
389    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    /// Create a weak handle that can be used to monitor `self`, but does not hold it.
395    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/// Weak [`ContextInitHandle`].
418#[derive(Clone, Default)]
419pub struct WeakContextInitHandle(std::sync::Weak<ContextInitHandleMarker>);
420impl WeakContextInitHandle {
421    /// Returns `true` if the strong handle still exists.
422    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}