zng_var/
contextualized.rs

1use std::{
2    marker::PhantomData,
3    sync::{Arc, Weak},
4};
5
6use parking_lot::{RwLock, RwLockReadGuard};
7
8use super::{types::WeakContextInitHandle, *};
9
10#[cfg(feature = "dyn_closure")]
11macro_rules! ActualLock {
12    ($T:ident) => {
13        parking_lot::RwLock<Vec<(WeakContextInitHandle, Box<dyn Any + Send + Sync>)>>
14    }
15}
16#[cfg(not(feature = "dyn_closure"))]
17macro_rules! ActualLock {
18    ($T:ident) => {
19        parking_lot::RwLock<Vec<(WeakContextInitHandle, BoxedVar<$T>)>>
20    }
21}
22
23#[cfg(feature = "dyn_closure")]
24macro_rules! ActualInit {
25    ($T:ident) => {
26        Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>
27    }
28}
29#[cfg(not(feature = "dyn_closure"))]
30macro_rules! ActualInit {
31    ($T:ident) => {
32        Arc<dyn Fn() -> BoxedVar<$T> + Send + Sync>
33    }
34}
35
36#[cfg(feature = "dyn_closure")]
37macro_rules! ActualReadGuard {
38    ($a:tt, $T:ident) => {
39        parking_lot::MappedRwLockReadGuard<$a, Box<dyn Any + Send + Sync>>
40    }
41}
42#[cfg(not(feature = "dyn_closure"))]
43macro_rules! ActualReadGuard {
44    ($a:tt, $T:ident) => {
45        parking_lot::MappedRwLockReadGuard<$a, BoxedVar<$T>>
46    }
47}
48
49/// Represents a variable that delays initialization until the first usage.
50///
51/// Usage that initializes the variable are all [`AnyVar`] and [`Var<T>`] methods except `read_only`, `downgrade` and `boxed`.
52/// The variable re-initializes when the [`ContextInitHandle::current`] is different on usage.
53///
54/// This variable is used in the [`Var::map`] and other mapping methods to support mapping from [`ContextVar<T>`].
55///
56/// ```
57/// # macro_rules! fake{($($tt:tt)*) => {}}
58/// # fake! {
59/// let wgt = MyWgt! {
60///     my_property = MY_CTX_VAR.map(|&b| !b);
61/// };
62/// # }
63/// ```
64///
65/// In the example above the mapping var will bind with the `MY_CTX_VAR` context inside the property node, not
66/// the context at the moment the widget is instantiated.
67///
68/// # Clone
69///
70/// Note that a clone of this variable may call the init closure again for the same context, the inited actual var
71/// is only reused if it is already inited when clone is called and clone is called on the same context.
72pub struct ContextualizedVar<T> {
73    _type: PhantomData<T>,
74
75    init: ActualInit![T],
76    actual: ActualLock![T],
77}
78
79#[expect(clippy::extra_unused_type_parameters)]
80fn borrow_init_impl<'a, T>(
81    actual: &'a ActualLock![T],
82    init: &ActualInit![T],
83    #[cfg(debug_assertions)] type_name: &'static str,
84) -> ActualReadGuard!['a, T] {
85    let current_ctx = ContextInitHandle::current();
86    let current_ctx = current_ctx.downgrade();
87
88    let act = actual.read_recursive();
89
90    if let Some(i) = act.iter().position(|(h, _)| h == &current_ctx) {
91        return RwLockReadGuard::map(act, move |m| &m[i].1);
92    }
93    drop(act);
94
95    let mut actual_mut = actual.write();
96    actual_mut.retain(|(h, _)| h.is_alive());
97    let i = actual_mut.len();
98
99    #[cfg(debug_assertions)]
100    if i == 200 {
101        tracing::debug!("variable of type `{type_name}` actualized >200 times");
102    }
103
104    if !actual_mut.iter().any(|(c, _)| c == &current_ctx) {
105        actual_mut.push((current_ctx.clone(), init()));
106    }
107    drop(actual_mut);
108
109    let actual = actual.read_recursive();
110    RwLockReadGuard::map(actual, move |m| {
111        if i < m.len() && m[i].0 == current_ctx {
112            &m[i].1
113        } else if let Some(i) = m.iter().position(|(h, _)| h == &current_ctx) {
114            &m[i].1
115        } else {
116            unreachable!()
117        }
118    })
119}
120
121impl<T: VarValue> ContextualizedVar<T> {
122    /// New with initialization function.
123    ///
124    /// The `init` closure will be called on the first usage of the var, once after the var is cloned and any time
125    /// a parent contextualized var is initializing.
126    pub fn new<V: Var<T>>(init: impl Fn() -> V + Send + Sync + 'static) -> Self {
127        Self {
128            _type: PhantomData,
129
130            #[cfg(feature = "dyn_closure")]
131            init: Arc::new(move || Box::new(init().boxed())),
132            #[cfg(not(feature = "dyn_closure"))]
133            init: Arc::new(move || init().boxed()),
134
135            actual: RwLock::new(Vec::with_capacity(1)),
136        }
137    }
138
139    /// New with initialization function that produces a value.
140    ///
141    /// The `init` closure will be called on the first usage of the var, once after the var is cloned and any time
142    /// a parent contextualized var is initializing.
143    pub fn new_value(init: impl Fn() -> T + Send + Sync + 'static) -> Self {
144        Self::new(move || init().into_var())
145    }
146
147    /// Borrow/initialize the actual var.
148    pub fn borrow_init(&self) -> parking_lot::MappedRwLockReadGuard<BoxedVar<T>> {
149        #[cfg(feature = "dyn_closure")]
150        {
151            parking_lot::MappedRwLockReadGuard::map(
152                borrow_init_impl::<()>(
153                    &self.actual,
154                    &self.init,
155                    #[cfg(debug_assertions)]
156                    std::any::type_name::<T>(),
157                ),
158                |v| v.downcast_ref().unwrap(),
159            )
160        }
161        #[cfg(not(feature = "dyn_closure"))]
162        {
163            borrow_init_impl(
164                &self.actual,
165                &self.init,
166                #[cfg(debug_assertions)]
167                std::any::type_name::<T>(),
168            )
169        }
170    }
171
172    /// Unwraps the initialized actual var or initializes it now.
173    pub fn into_init(self) -> BoxedVar<T> {
174        let mut act = self.actual.into_inner();
175        let current_ctx = ContextInitHandle::current().downgrade();
176
177        if let Some(i) = act.iter().position(|(h, _)| h == &current_ctx) {
178            #[cfg(feature = "dyn_closure")]
179            {
180                *act.swap_remove(i).1.downcast().unwrap()
181            }
182            #[cfg(not(feature = "dyn_closure"))]
183            {
184                act.swap_remove(i).1
185            }
186        } else {
187            #[cfg(feature = "dyn_closure")]
188            {
189                *(self.init)().downcast().unwrap()
190            }
191            #[cfg(not(feature = "dyn_closure"))]
192            {
193                (self.init)()
194            }
195        }
196    }
197}
198
199/// Weak var that upgrades to an uninitialized [`ContextualizedVar<T, S>`].
200pub struct WeakContextualizedVar<T> {
201    _type: PhantomData<T>,
202
203    #[cfg(feature = "dyn_closure")]
204    init: Weak<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
205
206    #[cfg(not(feature = "dyn_closure"))]
207    init: Weak<dyn Fn() -> BoxedVar<T> + Send + Sync>,
208}
209
210impl<T: VarValue> Clone for ContextualizedVar<T> {
211    fn clone(&self) -> Self {
212        let current_ctx_id = ContextInitHandle::current().downgrade();
213        let act = self.actual.read_recursive();
214        if let Some(i) = act.iter().position(|(id, _)| *id == current_ctx_id) {
215            return Self {
216                _type: PhantomData,
217                init: self.init.clone(),
218                #[cfg(feature = "dyn_closure")]
219                actual: RwLock::new(vec![(
220                    act[i].0.clone(),
221                    Box::new(act[i].1.downcast_ref::<BoxedVar<T>>().unwrap().clone()),
222                )]),
223                #[cfg(not(feature = "dyn_closure"))]
224                actual: RwLock::new(vec![act[i].clone()]),
225            };
226        }
227        Self {
228            _type: PhantomData,
229            init: self.init.clone(),
230            actual: RwLock::default(),
231        }
232    }
233}
234impl<T: VarValue> Clone for WeakContextualizedVar<T> {
235    fn clone(&self) -> Self {
236        Self {
237            _type: PhantomData,
238            init: self.init.clone(),
239        }
240    }
241}
242
243impl<T: VarValue> crate::private::Sealed for ContextualizedVar<T> {}
244impl<T: VarValue> crate::private::Sealed for WeakContextualizedVar<T> {}
245
246impl<T: VarValue> AnyVar for ContextualizedVar<T> {
247    fn clone_any(&self) -> BoxedAnyVar {
248        Box::new(self.clone())
249    }
250
251    fn as_any(&self) -> &dyn Any {
252        self
253    }
254
255    fn as_unboxed_any(&self) -> &dyn Any {
256        self
257    }
258
259    fn double_boxed_any(self: Box<Self>) -> Box<dyn Any> {
260        let me: BoxedVar<T> = self;
261        Box::new(me)
262    }
263
264    fn var_type_id(&self) -> TypeId {
265        TypeId::of::<T>()
266    }
267
268    fn get_any(&self) -> Box<dyn AnyVarValue> {
269        Box::new(self.get())
270    }
271
272    fn with_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) {
273        self.borrow_init().with_any(read)
274    }
275
276    fn with_new_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) -> bool {
277        self.borrow_init().with_new_any(read)
278    }
279
280    fn set_any(&self, value: Box<dyn AnyVarValue>) -> Result<(), VarIsReadOnlyError> {
281        self.modify(var_set_any(value))
282    }
283
284    fn last_update(&self) -> VarUpdateId {
285        self.borrow_init().last_update()
286    }
287
288    fn is_contextual(&self) -> bool {
289        true
290    }
291
292    fn capabilities(&self) -> VarCapability {
293        self.borrow_init().capabilities()
294    }
295
296    fn hook_any(&self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
297        self.borrow_init().hook_any(pos_modify_action)
298    }
299
300    fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
301        self.borrow_init().hook_animation_stop(handler)
302    }
303
304    fn strong_count(&self) -> usize {
305        Arc::strong_count(&self.init)
306    }
307
308    fn weak_count(&self) -> usize {
309        Arc::weak_count(&self.init)
310    }
311
312    fn actual_var_any(&self) -> BoxedAnyVar {
313        self.borrow_init().actual_var_any()
314    }
315
316    fn downgrade_any(&self) -> BoxedAnyWeakVar {
317        Box::new(self.downgrade())
318    }
319
320    fn is_animating(&self) -> bool {
321        self.borrow_init().is_animating()
322    }
323
324    fn modify_importance(&self) -> usize {
325        self.borrow_init().modify_importance()
326    }
327
328    fn var_ptr(&self) -> VarPtr {
329        VarPtr::new_arc(&self.init)
330    }
331
332    fn get_debug(&self) -> Txt {
333        self.with(var_debug)
334    }
335
336    fn update(&self) -> Result<(), VarIsReadOnlyError> {
337        Var::modify(self, var_update)
338    }
339
340    fn map_debug(&self) -> BoxedVar<Txt> {
341        Var::map(self, var_debug).boxed()
342    }
343}
344impl<T: VarValue> AnyWeakVar for WeakContextualizedVar<T> {
345    fn clone_any(&self) -> BoxedAnyWeakVar {
346        Box::new(self.clone())
347    }
348
349    fn strong_count(&self) -> usize {
350        self.init.strong_count()
351    }
352
353    fn weak_count(&self) -> usize {
354        self.init.weak_count()
355    }
356
357    fn upgrade_any(&self) -> Option<BoxedAnyVar> {
358        self.upgrade().map(|c| Box::new(c) as _)
359    }
360
361    fn as_any(&self) -> &dyn Any {
362        self
363    }
364}
365
366impl<T: VarValue> IntoVar<T> for ContextualizedVar<T> {
367    type Var = Self;
368
369    fn into_var(self) -> Self::Var {
370        self
371    }
372}
373
374impl<T: VarValue> Var<T> for ContextualizedVar<T> {
375    type ReadOnly = types::ReadOnlyVar<T, Self>;
376
377    type ActualVar = BoxedVar<T>;
378
379    type Downgrade = WeakContextualizedVar<T>;
380
381    type Map<O: VarValue> = contextualized::ContextualizedVar<O>;
382    type MapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
383
384    type FlatMap<O: VarValue, V: Var<O>> = contextualized::ContextualizedVar<O>;
385
386    type FilterMap<O: VarValue> = contextualized::ContextualizedVar<O>;
387    type FilterMapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
388
389    type MapRef<O: VarValue> = types::MapRef<T, O, Self>;
390    type MapRefBidi<O: VarValue> = types::MapRefBidi<T, O, Self>;
391
392    type Easing = types::ContextualizedVar<T>;
393
394    fn with<R, F>(&self, read: F) -> R
395    where
396        F: FnOnce(&T) -> R,
397    {
398        self.borrow_init().with(read)
399    }
400
401    fn modify<F>(&self, modify: F) -> Result<(), VarIsReadOnlyError>
402    where
403        F: FnOnce(&mut VarModify<T>) + Send + 'static,
404    {
405        self.borrow_init().modify(modify)
406    }
407
408    fn actual_var(self) -> Self::ActualVar {
409        self.into_init().actual_var()
410    }
411
412    fn downgrade(&self) -> Self::Downgrade {
413        WeakContextualizedVar {
414            _type: PhantomData,
415            init: Arc::downgrade(&self.init),
416        }
417    }
418
419    fn into_value(self) -> T {
420        self.into_init().into_value()
421    }
422
423    fn read_only(&self) -> Self::ReadOnly {
424        types::ReadOnlyVar::new(self.clone())
425    }
426
427    fn map<O, M>(&self, map: M) -> Self::Map<O>
428    where
429        O: VarValue,
430        M: FnMut(&T) -> O + Send + 'static,
431    {
432        var_map_ctx(self, map)
433    }
434
435    fn map_bidi<O, M, B>(&self, map: M, map_back: B) -> Self::MapBidi<O>
436    where
437        O: VarValue,
438        M: FnMut(&T) -> O + Send + 'static,
439        B: FnMut(&O) -> T + Send + 'static,
440    {
441        var_map_bidi_ctx(self, map, map_back)
442    }
443
444    fn flat_map<O, V, M>(&self, map: M) -> Self::FlatMap<O, V>
445    where
446        O: VarValue,
447        V: Var<O>,
448        M: FnMut(&T) -> V + Send + 'static,
449    {
450        var_flat_map_ctx(self, map)
451    }
452
453    fn filter_map<O, M, I>(&self, map: M, fallback: I) -> Self::FilterMap<O>
454    where
455        O: VarValue,
456        M: FnMut(&T) -> Option<O> + Send + 'static,
457        I: Fn() -> O + Send + Sync + 'static,
458    {
459        var_filter_map_ctx(self, map, fallback)
460    }
461
462    fn filter_map_bidi<O, M, B, I>(&self, map: M, map_back: B, fallback: I) -> Self::FilterMapBidi<O>
463    where
464        O: VarValue,
465        M: FnMut(&T) -> Option<O> + Send + 'static,
466        B: FnMut(&O) -> Option<T> + Send + 'static,
467        I: Fn() -> O + Send + Sync + 'static,
468    {
469        var_filter_map_bidi_ctx(self, map, map_back, fallback)
470    }
471
472    fn map_ref<O, M>(&self, map: M) -> Self::MapRef<O>
473    where
474        O: VarValue,
475        M: Fn(&T) -> &O + Send + Sync + 'static,
476    {
477        var_map_ref(self, map)
478    }
479
480    fn map_ref_bidi<O, M, B>(&self, map: M, map_mut: B) -> Self::MapRefBidi<O>
481    where
482        O: VarValue,
483        M: Fn(&T) -> &O + Send + Sync + 'static,
484        B: Fn(&mut T) -> &mut O + Send + Sync + 'static,
485    {
486        var_map_ref_bidi(self, map, map_mut)
487    }
488
489    fn easing<F>(&self, duration: Duration, easing: F) -> Self::Easing
490    where
491        T: Transitionable,
492        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
493    {
494        var_easing_ctx(self, duration, easing)
495    }
496
497    fn easing_with<F, SE>(&self, duration: Duration, easing: F, sampler: SE) -> Self::Easing
498    where
499        T: Transitionable,
500        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
501        SE: Fn(&animation::Transition<T>, EasingStep) -> T + Send + Sync + 'static,
502    {
503        var_easing_with_ctx(self, duration, easing, sampler)
504    }
505}
506impl<T: VarValue> WeakVar<T> for WeakContextualizedVar<T> {
507    type Upgrade = ContextualizedVar<T>;
508
509    fn upgrade(&self) -> Option<Self::Upgrade> {
510        Some(ContextualizedVar {
511            _type: PhantomData,
512            init: self.init.upgrade()?,
513            actual: RwLock::default(),
514        })
515    }
516}