This commit is contained in:
Samuel Guerra 2023-12-01 10:59:28 -03:00
parent faf8fb546f
commit 4eb58acf66
4 changed files with 51 additions and 229 deletions

View File

@ -1,3 +1,6 @@
* `easing` attribute in when conditions not working.
- They don't apply.
# TextInput
* Touch selection.
@ -28,6 +31,8 @@ TextInput! {
# Accessibility
* panicked at 'assertion failed: self.nodes.contains_key(&self.focus)'
- Run icon example, search z, click a button.
* All examples must be fully useable with a screen reader.
- Test OS defaults and NVDA.

View File

@ -2,11 +2,10 @@
use std::{any::Any, sync::Arc, time::Duration};
use parking_lot::Mutex;
use zero_ui_var::{
animation::{
easing::{EasingStep, EasingTime},
AnimationHandle, Transitionable,
Transitionable,
},
*,
};
@ -319,99 +318,6 @@ impl_easing_property_inputs! {
I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15,
}
/// Easing when extension for ArcWhenVar.
pub trait VarEasingWhen<T: VarValue>: Var<T> {
/// Create a variable similar to [`Var::easing`], but with different duration and easing functions for each condition.
///
/// The `condition_easing` must contain one entry for each when condition, entries can be `None`, the easing used
/// is the first entry that corresponds to a `true` condition, or falls-back to the `default_easing`.
fn easing_when(
&self,
condition_easing: Vec<Option<(Duration, EasingFn)>>,
default_easing: (Duration, EasingFn),
) -> types::ContextualizedVar<T, ReadOnlyArcVar<T>>
where
T: Transitionable;
}
impl<T: VarValue> VarEasingWhen<T> for super::types::ArcWhenVar<T> {
fn easing_when(
&self,
condition_easing: Vec<Option<(Duration, EasingFn)>>,
default_easing: (Duration, EasingFn),
) -> types::ContextualizedVar<T, ReadOnlyArcVar<T>>
where
T: Transitionable,
{
let source = self.clone();
types::ContextualizedVar::new(Arc::new(move || {
debug_assert_eq!(source.conditions().len(), condition_easing.len());
let source_wk = source.downgrade();
let easing_var = super::var(source.get());
let condition_easing = condition_easing.clone();
let default_easing = default_easing.clone();
let mut _anim_handle = AnimationHandle::dummy();
var_bind(&source, &easing_var, move |value, _, easing_var| {
let source = source_wk.upgrade().unwrap();
for ((c, _), easing) in source.conditions().iter().zip(&condition_easing) {
if let Some((duration, func)) = easing {
if c.get() {
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
return;
}
}
}
let (duration, func) = &default_easing;
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
})
.perm();
easing_var.read_only()
}))
}
}
fn var_bind<I, O, V>(
input: &impl Var<I>,
output: &V,
update_output: impl FnMut(&I, &VarHookArgs, <V::Downgrade as WeakVar<O>>::Upgrade) + Send + 'static,
) -> VarHandle
where
I: VarValue,
O: VarValue,
V: Var<O>,
{
var_bind_ok(input, output.downgrade(), update_output)
}
fn var_bind_ok<I, O, W>(
input: &impl Var<I>,
wk_output: W,
update_output: impl FnMut(&I, &VarHookArgs, W::Upgrade) + Send + 'static,
) -> VarHandle
where
I: VarValue,
O: VarValue,
W: WeakVar<O>,
{
let update_output = Mutex::new(update_output);
input.hook(Box::new(move |args| {
if let Some(output) = wk_output.upgrade() {
if output.capabilities().contains(VarCapabilities::MODIFY) {
if let Some(value) = args.downcast_value::<I>() {
update_output.lock()(value, args, output);
}
}
true
} else {
false
}
}))
}
/// Variable for state properties (`is_*`, `has_*`).
///
/// State variables are `bool` probes that are set by the property, they are created automatically

View File

@ -1,134 +0,0 @@
use std::{any::Any, sync::Arc, time::Duration};
use crate::{
units::*,
var::{animation::AnimationHandle, WeakVar},
widget_builder::{AnyPropertyBuildAction, PropertyBuildAction, PropertyInputTypes, WhenBuildAction},
};
use super::{animation::Transitionable, types, BoxedVar, ReadOnlyArcVar, Var, VarValue};
type EasingFn = Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>;
#[doc(hidden)]
#[allow(non_camel_case_types)]
pub trait easing_property: Send + Sync + Clone + Copy {
fn easing_property_unset(self);
fn easing_property(self, duration: Duration, easing: EasingFn) -> Vec<Box<dyn AnyPropertyBuildAction>>;
fn easing_when_data(self, duration: Duration, easing: EasingFn) -> WhenBuildAction;
}
#[doc(hidden)]
#[allow(non_camel_case_types)]
pub trait easing_property_input_Transitionable: Any + Send {
fn easing(self, duration: Duration, easing: EasingFn, when_conditions_data: &[Option<Arc<dyn Any + Send + Sync>>]) -> Self;
}
impl<T: VarValue + Transitionable> easing_property_input_Transitionable for BoxedVar<T> {
fn easing(self, duration: Duration, easing: EasingFn, when_conditions_data: &[Option<Arc<dyn Any + Send + Sync>>]) -> Self {
if let Some(when) = self.as_any().downcast_ref::<types::ContextualizedVar<T, types::ArcWhenVar<T>>>() {
let conditions: Vec<_> = when_conditions_data
.iter()
.map(|d| d.as_ref().and_then(|d| d.downcast_ref::<(Duration, EasingFn)>().cloned()))
.collect();
if conditions.iter().any(|c| c.is_some()) {
let when = when.clone();
return types::ContextualizedVar::new(Arc::new(move || {
when.borrow_init().easing_when(conditions.clone(), (duration, easing.clone()))
}))
.boxed();
}
}
Var::easing(&self, duration, move |t| easing(t)).boxed()
}
}
macro_rules! impl_easing_property_inputs {
($T0:ident, $($T:ident,)*) => {
impl_easing_property_inputs! {
$($T,)*
}
impl<
$T0: easing_property_input_Transitionable,
$($T: easing_property_input_Transitionable),*
> easing_property for PropertyInputTypes<($T0, $($T,)*)> {
fn easing_property_unset(self) { }
fn easing_property(self, duration: Duration, easing: EasingFn) -> Vec<Box<dyn AnyPropertyBuildAction>> {
if duration == Duration::ZERO {
vec![]
} else {
vec![
Box::new(PropertyBuildAction::<$T0>::new(clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, duration, easing.clone(), &a.when_conditions_data)))),
$(Box::new(PropertyBuildAction::<$T>::new(clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, duration, easing.clone(), &a.when_conditions_data)))),)*
]
}
}
fn easing_when_data(self, duration: Duration, easing: EasingFn) -> WhenBuildAction {
if duration == Duration::ZERO {
WhenBuildAction::new_no_default((duration, easing))
} else {
WhenBuildAction::new(
(duration, easing),
|| {
let easing = Arc::new($crate::var::animation::easing::linear) as EasingFn;
vec![
Box::new(PropertyBuildAction::<$T0>::new(clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, 0.ms(), easing.clone(), &a.when_conditions_data)))),
$(Box::new(PropertyBuildAction::<$T>::new(clmv!(easing, |a| easing_property_input_Transitionable::easing(a.input, 0.ms(), easing.clone(), &a.when_conditions_data)))),)*
]
}
)
}
}
}
};
() => { };
}
impl_easing_property_inputs! {
I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15,
}
impl<T: VarValue> super::types::ArcWhenVar<T> {
/// Create a variable similar to [`Var::easing`], but with different duration and easing functions for each condition.
///
/// The `condition_easing` must contain one entry for each when condition, entries can be `None`, the easing used
/// is the first entry that corresponds to a `true` condition, or falls-back to the `default_easing`.
pub fn easing_when(
&self,
condition_easing: Vec<Option<(Duration, EasingFn)>>,
default_easing: (Duration, EasingFn),
) -> types::ContextualizedVar<T, ReadOnlyArcVar<T>>
where
T: Transitionable,
{
let source = self.clone();
types::ContextualizedVar::new(Arc::new(move || {
debug_assert_eq!(source.conditions().len(), condition_easing.len());
let source_wk = source.downgrade();
let easing_var = super::var(source.get());
let condition_easing = condition_easing.clone();
let default_easing = default_easing.clone();
let mut _anim_handle = AnimationHandle::dummy();
crate::var::var_bind(&source, &easing_var, move |value, _, easing_var| {
let source = source_wk.upgrade().unwrap();
for ((c, _), easing) in source.conditions().iter().zip(&condition_easing) {
if let Some((duration, func)) = easing {
if c.get() {
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
return;
}
}
}
let (duration, func) = &default_easing;
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
})
.perm();
easing_var.read_only()
}))
}
}

View File

@ -1,5 +1,7 @@
use std::sync::{Arc, Weak};
use crate::animation::AnimationHandle;
use super::*;
///<span data-del-macro-root></span> Initializes a new conditional var.
@ -385,6 +387,49 @@ impl<T: VarValue> ArcWhenVar<T> {
pub fn default(&self) -> &BoxedVar<T> {
&self.0.default
}
/// Create a variable similar to [`Var::easing`], but with different duration and easing functions for each condition.
///
/// The `condition_easing` must contain one entry for each when condition, entries can be `None`, the easing used
/// is the first entry that corresponds to a `true` condition, or falls-back to the `default_easing`.
pub fn easing_when(
&self,
condition_easing: Vec<Option<(Duration, Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>)>>,
default_easing: (Duration, Arc<dyn Fn(EasingTime) -> EasingStep + Send + Sync>),
) -> types::ContextualizedVar<T, ReadOnlyArcVar<T>>
where
T: Transitionable,
{
let source = self.clone();
types::ContextualizedVar::new(Arc::new(move || {
debug_assert_eq!(source.conditions().len(), condition_easing.len());
let source_wk = source.downgrade();
let easing_var = super::var(source.get());
let condition_easing = condition_easing.clone();
let default_easing = default_easing.clone();
let mut _anim_handle = AnimationHandle::dummy();
var_bind(&source, &easing_var, move |value, _, easing_var| {
let source = source_wk.upgrade().unwrap();
for ((c, _), easing) in source.conditions().iter().zip(&condition_easing) {
if let Some((duration, func)) = easing {
if c.get() {
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
return;
}
}
}
let (duration, func) = &default_easing;
let func = func.clone();
_anim_handle = easing_var.ease(value.clone(), *duration, move |t| func(t));
})
.perm();
easing_var.read_only()
}))
}
}
impl<T> Clone for ArcWhenVar<T> {