1use std::{
4 sync::{
5 Arc, Weak,
6 atomic::{AtomicU32, AtomicUsize, Ordering},
7 },
8 time::Duration,
9};
10
11use crate::{
12 AnyVar, VARS, Var,
13 animation::{
14 AnimationHandle, Transitionable,
15 easing::{EasingStep, EasingTime},
16 },
17 contextual_var::{ContextInitFnImpl, any_contextual_var_impl},
18 shared_var::MutexHooks,
19};
20
21use super::*;
22
23#[macro_export]
81macro_rules! when_var {
82 ($($tt:tt)*) => {
83 $crate::__when_var! {
84 $crate
85 $($tt)*
86 }
87 }
88}
89
90use zng_clone_move::clmv;
91#[doc(hidden)]
92pub use zng_var_proc_macros::when_var as __when_var;
93
94#[derive(Clone)]
98pub struct AnyWhenVarBuilder {
99 conditions: Vec<(Var<bool>, AnyVar)>,
100 default: AnyVar,
101}
102impl AnyWhenVarBuilder {
103 pub fn new(default: AnyVar) -> Self {
105 AnyWhenVarBuilder {
106 conditions: Vec::with_capacity(2),
107 default,
108 }
109 }
110
111 pub fn push(&mut self, condition: Var<bool>, value: AnyVar) -> &mut Self {
116 self.conditions.push((condition, value));
117 self
118 }
119
120 pub fn replace_extend(&mut self, other: &Self) {
122 self.default = other.default.clone();
123 self.extend(other);
124 }
125
126 pub fn extend(&mut self, other: &Self) {
128 for (c, v) in other.conditions.iter() {
129 self.conditions.push((c.clone(), v.clone()));
130 }
131 }
132
133 pub fn build(self, value_type: TypeId) -> AnyVar {
137 when_var(self, value_type)
138 }
139
140 pub fn into_typed<O: VarValue>(self) -> WhenVarBuilder<O> {
144 WhenVarBuilder {
145 builder: self,
146 _t: PhantomData,
147 }
148 }
149
150 pub fn try_from_built(var: &AnyVar) -> Option<Self> {
154 match &var.0 {
155 DynAnyVar::When(built) => Some(Self {
156 conditions: built.0.conditions.to_vec(),
157 default: built.0.default.clone(),
158 }),
159 DynAnyVar::Contextual(built) => {
160 let init = built.0.init.lock();
161 let init: &dyn Any = &**init;
162 init.downcast_ref::<Self>().cloned()
163 }
164 _ => None,
165 }
166 }
167
168 fn is_contextual(&self) -> bool {
169 self.default.capabilities().is_contextual()
170 || self
171 .conditions
172 .iter()
173 .any(|(c, v)| c.capabilities().is_contextual() || v.capabilities().is_contextual())
174 }
175
176 fn current_context(&self) -> Self {
177 AnyWhenVarBuilder {
178 conditions: self
179 .conditions
180 .iter()
181 .map(|(c, v)| (c.current_context(), v.current_context()))
182 .collect(),
183 default: self.default.current_context(),
184 }
185 }
186}
187impl AnyWhenVarBuilder {
188 pub fn condition_count(&self) -> usize {
190 self.conditions.len()
191 }
192}
193
194#[derive(Clone)]
196pub struct WhenVarBuilder<O: VarValue> {
197 builder: AnyWhenVarBuilder,
198 _t: PhantomData<O>,
199}
200impl<O: VarValue> WhenVarBuilder<O> {
201 pub fn new(default: impl IntoVar<O>) -> Self {
203 Self {
204 builder: AnyWhenVarBuilder::new(default.into_var().into()),
205 _t: PhantomData,
206 }
207 }
208
209 pub fn push(&mut self, condition: impl IntoVar<bool>, value: impl IntoVar<O>) -> &mut Self {
214 self.builder.conditions.push((condition.into_var(), value.into_var().into()));
215 self
216 }
217
218 pub fn build(self) -> Var<O> {
220 Var::new_any(self.builder.build(TypeId::of::<O>()))
221 }
222
223 pub fn as_any(&mut self) -> &mut AnyWhenVarBuilder {
225 &mut self.builder
226 }
227
228 pub fn try_from_built(var: &Var<O>) -> Option<Self> {
232 let builder = AnyWhenVarBuilder::try_from_built(var)?;
235 Some(Self { builder, _t: PhantomData })
236 }
237}
238
239fn when_var(builder: AnyWhenVarBuilder, value_type: TypeId) -> AnyVar {
240 if builder.is_contextual() {
241 return any_contextual_var_impl(smallbox!(builder), value_type);
242 }
243 when_var_tail(builder)
244}
245impl ContextInitFnImpl for AnyWhenVarBuilder {
246 fn init(&mut self) -> AnyVar {
247 let builder = self.current_context();
248 when_var_tail(builder)
249 }
250}
251fn when_var_tail(mut builder: AnyWhenVarBuilder) -> AnyVar {
252 builder.conditions.retain(|(c, _)| !c.capabilities().is_const() || c.get());
253 if builder.conditions.is_empty() {
254 return builder.default;
255 }
256
257 let all_equal = builder.default.capabilities().is_const()
258 && builder.default.with(|default| {
259 builder
260 .conditions
261 .iter()
262 .all(|(_, v)| v.capabilities().is_const() && v.with(|v| v == default))
263 });
264 if all_equal {
265 return builder.default;
268 }
269
270 AnyVar(DynAnyVar::When(when_var_tail_impl(builder)))
271}
272fn when_var_tail_impl(builder: AnyWhenVarBuilder) -> WhenVar {
273 let data = Arc::new(WhenVarData {
274 active_condition: AtomicUsize::new(builder.conditions.iter().position(|(c, _)| c.get()).unwrap_or(usize::MAX)),
275 conditions: builder.conditions.into_boxed_slice(),
276 default: builder.default,
277 hooks: MutexHooks::default(),
278 last_active_change: AtomicU32::new(VarUpdateId::never().0),
279 });
280
281 for (i, (c, v)) in data.conditions.iter().enumerate() {
282 let weak = Arc::downgrade(&data);
283 c.hook(clmv!(weak, |args| {
284 if let Some(data) = weak.upgrade() {
285 let mut changed = false;
286 let mut active = data.active_condition.load(Ordering::Relaxed);
287 if active == i {
288 if !*args.value() {
289 active = data.conditions.iter().position(|(c, _)| c.get()).unwrap_or(usize::MAX);
291 changed = true;
292 }
293 } else if active > i && *args.value() {
294 changed = true;
296 active = i;
297 }
298
299 if changed {
300 data.active_condition.store(active, Ordering::Relaxed);
301 data.last_active_change.store(VARS.update_id().0, Ordering::Relaxed);
302
303 let active = if active < data.conditions.len() {
304 &data.conditions[active].1
305 } else {
306 &data.default
307 };
308
309 active.0.with(&mut |v| {
310 data.hooks.notify(&AnyVarHookArgs {
311 var_instance_tag: VarInstanceTag(Arc::as_ptr(&data) as _),
312 value: v,
313 update: args.update,
314 tags: args.tags,
315 });
316 });
317 }
318
319 true
320 } else {
321 false
322 }
323 }))
324 .perm();
325
326 v.hook(move |args| {
327 if let Some(data) = weak.upgrade() {
328 if data.active_condition.load(Ordering::Relaxed) == i {
329 data.hooks.notify(args);
330 }
331 true
332 } else {
333 false
334 }
335 })
336 .perm();
337 }
338 let weak = Arc::downgrade(&data);
339 data.default
340 .hook(move |args| {
341 if let Some(data) = weak.upgrade() {
342 if data.active_condition.load(Ordering::Relaxed) >= data.conditions.len() {
343 data.hooks.notify(args);
344 }
345 true
346 } else {
347 false
348 }
349 })
350 .perm();
351
352 WhenVar(data)
353}
354
355struct WhenVarData {
356 conditions: Box<[(Var<bool>, AnyVar)]>,
357 default: AnyVar,
358 active_condition: AtomicUsize,
359 hooks: MutexHooks,
360 last_active_change: AtomicU32,
362}
363pub(crate) struct WhenVar(Arc<WhenVarData>);
364impl WhenVar {
365 fn active(&self) -> &AnyVar {
366 let i = self.0.active_condition.load(Ordering::Relaxed);
367 if i < self.0.conditions.len() {
368 &self.0.conditions[i].1
369 } else {
370 &self.0.default
371 }
372 }
373}
374impl fmt::Debug for WhenVar {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 let mut b = f.debug_struct("MergeVar");
377 b.field("var_instance_tag()", &self.var_instance_tag());
378 b.field("inputs", &self.0.conditions);
379 b.field("default", &self.0.default);
380 let n = self.0.active_condition.load(Ordering::Relaxed);
381 b.field("active_condition", if n < self.0.conditions.len() { &n } else { &None::<usize> });
382 b.field(
383 "last_active_change",
384 &VarUpdateId(self.0.last_active_change.load(Ordering::Relaxed)),
385 );
386 b.field("hooks", &self.0.hooks);
387 b.finish()
388 }
389}
390impl VarImpl for WhenVar {
391 fn clone_dyn(&self) -> DynAnyVar {
392 DynAnyVar::When(Self(self.0.clone()))
393 }
394
395 fn value_type(&self) -> TypeId {
396 self.0.default.0.value_type()
397 }
398
399 #[cfg(feature = "type_names")]
400 fn value_type_name(&self) -> &'static str {
401 self.0.default.0.value_type_name()
402 }
403
404 fn strong_count(&self) -> usize {
405 Arc::strong_count(&self.0)
406 }
407
408 fn var_eq(&self, other: &DynAnyVar) -> bool {
409 match other {
410 DynAnyVar::When(o) => Arc::ptr_eq(&self.0, &o.0),
411 _ => false,
412 }
413 }
414
415 fn var_instance_tag(&self) -> VarInstanceTag {
416 VarInstanceTag(Arc::as_ptr(&self.0) as _)
417 }
418
419 fn downgrade(&self) -> DynWeakAnyVar {
420 DynWeakAnyVar::When(WeakWhenVar(Arc::downgrade(&self.0)))
421 }
422
423 fn capabilities(&self) -> VarCapability {
424 fn cap_changes(caps: VarCapability) -> VarCapability {
425 let mut out = VarCapability::NEW;
426 if caps.contains(VarCapability::MODIFY) || caps.contains(VarCapability::MODIFY_CHANGES) {
427 out |= VarCapability::MODIFY_CHANGES;
428 }
429 out
434 }
435 self.active().0.capabilities()
436 | cap_changes(self.0.default.capabilities())
437 | self
438 .0
439 .conditions
440 .iter()
441 .map(|(_, v)| cap_changes(v.capabilities()))
442 .fold(VarCapability::empty(), |a, b| a | b)
443 }
444
445 fn with(&self, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
446 self.active().0.with(visitor)
447 }
448
449 fn get(&self) -> BoxAnyVarValue {
450 self.active().0.get()
451 }
452
453 fn set(&self, new_value: BoxAnyVarValue) -> bool {
454 self.active().0.set(new_value)
455 }
456
457 fn update(&self) -> bool {
458 self.active().0.update()
459 }
460
461 fn modify(&self, modify: SmallBox<dyn FnMut(&mut AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool {
462 self.active().0.modify(modify)
463 }
464
465 fn hook(&self, on_new: SmallBox<dyn FnMut(&AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> VarHandle {
466 self.0.hooks.push(on_new)
467 }
468
469 fn last_update(&self) -> VarUpdateId {
470 VarUpdateId(self.0.last_active_change.load(Ordering::Relaxed)).max(self.active().0.last_update())
472 }
473
474 fn modify_info(&self) -> ModifyInfo {
475 self.active().0.modify_info()
476 }
477
478 fn modify_importance(&self) -> usize {
479 self.active().0.modify_importance()
480 }
481
482 fn is_animating(&self) -> bool {
485 self.active().0.is_animating()
486 }
487
488 fn hook_animation_stop(&self, handler: AnimationStopFn) -> VarHandle {
489 self.active().0.hook_animation_stop(handler)
490 }
491
492 fn current_context(&self) -> DynAnyVar {
493 self.clone_dyn()
494 }
495}
496
497pub(crate) struct WeakWhenVar(Weak<WhenVarData>);
498impl fmt::Debug for WeakWhenVar {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 f.debug_tuple("WeakWhenVar").field(&self.0.as_ptr()).finish()
501 }
502}
503impl WeakVarImpl for WeakWhenVar {
504 fn clone_dyn(&self) -> DynWeakAnyVar {
505 DynWeakAnyVar::When(Self(self.0.clone()))
506 }
507
508 fn strong_count(&self) -> usize {
509 self.0.strong_count()
510 }
511
512 fn upgrade(&self) -> Option<DynAnyVar> {
513 Some(DynAnyVar::When(WhenVar(self.0.upgrade()?)))
514 }
515}
516
517fn when_var_easing<O: VarValue + Transitionable>(builder: AnimatingWhenVarBuilder<O>) -> Var<O> {
518 if builder.builder.is_contextual() {
519 let any = any_contextual_var_impl(smallbox!(builder), TypeId::of::<O>());
520 return Var::new_any(any);
521 }
522 when_var_easing_tail(builder)
523}
524struct AnimatingWhenVarBuilder<O: VarValue + Transitionable> {
525 builder: AnyWhenVarBuilder,
526 condition_easing: Vec<Option<EasingData>>,
527 default_easing: EasingData,
528 _t: PhantomData<fn() -> O>,
529}
530impl<O: VarValue + Transitionable> ContextInitFnImpl for AnimatingWhenVarBuilder<O> {
531 fn init(&mut self) -> AnyVar {
532 let builder = AnimatingWhenVarBuilder {
533 builder: self.builder.current_context(),
534 condition_easing: self.condition_easing.clone(),
535 default_easing: self.default_easing.clone(),
536 _t: self._t,
537 };
538 when_var_easing_tail(builder).any
539 }
540}
541fn when_var_easing_tail<O: VarValue + Transitionable>(builder: AnimatingWhenVarBuilder<O>) -> Var<O> {
542 let source = when_var_tail_impl(builder.builder);
543 let weak_source = Arc::downgrade(&source.0);
544 let output = var(source.get().downcast::<O>().unwrap());
545 let weak_output = output.downgrade();
546 let condition_easing = builder.condition_easing.into_boxed_slice();
547 let default_easing = builder.default_easing;
548 let mut _animation_handle = AnimationHandle::dummy();
549 source
550 .hook(smallbox!(move |args: &AnyVarHookArgs| {
551 if let Some(output) = weak_output.upgrade() {
552 let source = weak_source.upgrade().unwrap();
553 for ((c, _), easing) in source.conditions.iter().zip(&condition_easing) {
554 if let Some((duration, func)) = easing
555 && c.get()
556 {
557 _animation_handle = output.ease(args.downcast_value::<O>().unwrap().clone(), *duration, clmv!(func, |t| func(t)));
558 return true;
559 }
560 }
561 let (duration, func) = &default_easing;
563 _animation_handle = output.ease(args.downcast_value::<O>().unwrap().clone(), *duration, clmv!(func, |t| func(t)));
564 true
565 } else {
566 false
567 }
568 }))
569 .perm();
570 output.hold(source).perm();
571 output
572}
573
574type EasingData = (Duration, Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>);
575
576impl<O: VarValue + Transitionable> WhenVarBuilder<O> {
577 pub fn build_easing(self, condition_easing: Vec<Option<EasingData>>, default_easing: EasingData) -> Var<O> {
582 when_var_easing(AnimatingWhenVarBuilder {
583 builder: self.builder,
584 condition_easing,
585 default_easing,
586 _t: PhantomData,
587 })
588 }
589}