Browse Source

Simplify InterpretKeyFrames;

Pass the root animatable to AnimationTarget instead.
pull/2661/head
Jumar Macato 7 years ago
parent
commit
3768824611
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 60
      src/Avalonia.Animation/Animation.cs
  2. 7
      src/Avalonia.Animation/AnimationTarget.cs
  3. 9
      src/Avalonia.Animation/Animators/Animator`1.cs
  4. 8
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  5. 2
      src/Avalonia.Animation/IAnimator.cs
  6. 5
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

60
src/Avalonia.Animation/Animation.cs

@ -12,6 +12,7 @@ using Avalonia.Animation.Easings;
using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Logging;
using Avalonia.Metadata;
namespace Avalonia.Animation
@ -213,11 +214,6 @@ namespace Avalonia.Animation
( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
};
private readonly static List<(Func<AvaloniaProperty, Type, bool> Condition, Type Animator)> SecondLevelAnimators
= new List<(Func<AvaloniaProperty, Type, bool> Condition, Type Animator)>();
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
where TAnimator : IAnimator
{
@ -236,7 +232,7 @@ namespace Avalonia.Animation
return null;
}
AnimationTarget GetTargetFromSetter(IAnimationSetter setter, Animatable root)
AnimationTarget GetTargetFromSetter(IAnimationSetter setter, Animatable root, ref bool haltProcessing)
{
var target = new AnimationTarget(root, null);
bool traverse = false, start = true;
@ -269,15 +265,33 @@ namespace Avalonia.Animation
}
break;
case CastTypePropertyPathElement ct:
if (tempTarget != null && tempTarget?.GetType() != ct.Type)
var tmpTargetType = tempTarget?.GetType();
var castType = ct.Type;
if (!castType.IsAssignableFrom(tmpTargetType))
{
tempTarget = Convert.ChangeType(target.TargetAnimatable, ct.Type);
Logger.Error(LogArea.Animations, this,
$"Type cast mismatch: `{tempTarget?.GetType()}` is not assignable to `{ct.Type}`");
haltProcessing = true;
return null;
}
break;
case EnsureTypePropertyPathElement et:
if (target.TargetAnimatable?.GetType() != et.Type)
var one = tempTarget?.GetType();
var two = et.Type;
if (!one.IsAssignableFrom(two))
{
if (et.Type.IsAssignableFrom(target.TargetProperty.PropertyType))
{
tempTarget = Activator.CreateInstance(et.Type);
}
else
{
Logger.Error(LogArea.Animations, this,
$"Type enforcement mismatch: `{tempTarget?.GetType()}` is not assignable to `{et.Type}`");
haltProcessing = true;
return null;
}
target.TargetAnimatable.SetValue(target.TargetProperty, tempTarget);
}
break;
case ChildTraversalPropertyPathElement tr:
@ -292,7 +306,7 @@ namespace Avalonia.Animation
return target;
}
private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable root)
private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable root, ref bool haltProcessing)
{
var subscriptions = new List<IDisposable>();
var animatorInstances = new Dictionary<(Type type, AnimationTarget animTarget), IAnimator>();
@ -301,12 +315,19 @@ namespace Avalonia.Animation
{
foreach (var setter in keyframe.Setters)
{
var target = GetTargetFromSetter(setter, root);
var target = GetTargetFromSetter(setter, root, ref haltProcessing);
if (haltProcessing) return (null, null);
var animatorType = GetAnimatorType(target.TargetProperty);
if (animatorType == null)
{
throw new InvalidOperationException($"No animator registered for the property {target.TargetProperty}. Add an animator to the Animation.Animators collection that matches this property to animate it.");
Logger.Error(LogArea.Control, this, $"No animator registered for the property {target.TargetProperty}. " +
"Add an animator to the Animation.Animators collection that matches " +
"this property to animate it.");
haltProcessing = true;
return (null, null);
}
IAnimator animator;
@ -328,9 +349,8 @@ namespace Avalonia.Animation
}
var newKF = new AnimatorKeyFrame(animatorType, cue, target);
newKF.Value = setter.Value;
subscriptions.Add(newKF.BindSetter(setter, target.TargetAnimatable));
subscriptions.Add(newKF.BindSetter(setter, target.RootAnimatable));
animator.Add(newKF);
}
@ -342,10 +362,14 @@ namespace Avalonia.Animation
/// <inheritdocs/>
public IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{
var (animators, subscriptions) = InterpretKeyframes(control);
bool haltProcessing = false;
var (animators, subscriptions) = InterpretKeyframes(control, ref haltProcessing);
if (haltProcessing)
return Disposable.Empty;
if (animators.Count == 1)
{
subscriptions.Add(animators[0].Apply(this, control, clock, match, onComplete));
subscriptions.Add(animators[0].Apply(this, clock, match, onComplete));
}
else
{
@ -359,7 +383,7 @@ namespace Avalonia.Animation
animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task);
}
subscriptions.Add(animator.Apply(this, control, clock, match, animatorOnComplete));
subscriptions.Add(animator.Apply(this, clock, match, animatorOnComplete));
}
if (onComplete != null)

7
src/Avalonia.Animation/AnimationTarget.cs

@ -11,12 +11,15 @@ namespace Avalonia.Animation
/// </summary>
public class AnimationTarget : IEquatable<AnimationTarget>
{
public AnimationTarget(Animatable control, AvaloniaProperty property)
public AnimationTarget(Animatable root, AvaloniaProperty property)
{
TargetAnimatable = control;
RootAnimatable = root;
TargetAnimatable = root;
TargetProperty = property;
}
public Animatable RootAnimatable { get; internal set; }
/// <summary>
/// The target property of the animation.
/// </summary>

9
src/Avalonia.Animation/Animators/Animator`1.cs

@ -37,12 +37,12 @@ namespace Avalonia.Animation.Animators
}
/// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
public virtual IDisposable Apply(Animation animation, IClock clock, IObservable<bool> match, Action onComplete)
{
if (!_isVerifiedAndConverted)
VerifyConvertKeyFrames();
var subject = new DisposeAnimationInstanceSubject<T>(this, animation, control, clock, onComplete);
var subject = new DisposeAnimationInstanceSubject<T>(this, animation, clock, onComplete);
return match.Subscribe(subject);
}
@ -108,17 +108,16 @@ namespace Avalonia.Animation.Animators
/// <summary>
/// Runs the KeyFrames Animation.
/// </summary>
internal IDisposable Run(Animation animation, Animatable control, IClock clock, Action onComplete)
internal IDisposable Run(Animation animation, IClock clock, Action onComplete)
{
var instance = new AnimationInstance<T>(
animation,
Target.TargetAnimatable,
this,
clock ?? Target.TargetAnimatable.Clock ?? Clock.GlobalClock,
clock ?? Target.RootAnimatable.Clock ?? Clock.GlobalClock,
onComplete,
InterpolationHandler);
return Target.TargetAnimatable.Bind<T>((AvaloniaProperty<T>)Target.TargetProperty, instance, BindingPriority.Animation);
}

8
src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs

@ -22,15 +22,13 @@ namespace Avalonia.Animation
private bool _lastMatch;
private Animator<T> _animator;
private Animation _animation;
private Animatable _control;
private Action _onComplete;
private IClock _clock;
public DisposeAnimationInstanceSubject(Animator<T> animator, Animation animation, Animatable control, IClock clock, Action onComplete)
public DisposeAnimationInstanceSubject(Animator<T> animator, Animation animation, IClock clock, Action onComplete)
{
this._animator = animator;
this._animation = animation;
this._control = control;
this._onComplete = onComplete;
this._clock = clock;
}
@ -55,10 +53,10 @@ namespace Avalonia.Animation
_lastInstance?.Dispose();
if (matchVal)
{
_lastInstance = _animator.Run(_animation, _control, _clock, _onComplete);
_lastInstance = _animator.Run(_animation, _clock, _onComplete);
}
_lastMatch = matchVal;
}
}
}
}
}

2
src/Avalonia.Animation/IAnimator.cs

@ -19,6 +19,6 @@ namespace Avalonia.Animation
/// <summary>
/// Applies the current KeyFrame group to the specified control.
/// </summary>
IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete);
IDisposable Apply(Animation animation, IClock clock, IObservable<bool> match, Action onComplete);
}
}

5
src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

@ -25,7 +25,7 @@ namespace Avalonia.Animation.Animators
_colorAnimator.Target = new AnimationTarget(target, SolidColorBrush.ColorProperty);
}
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
public override IDisposable Apply(Animation animation, IClock clock, IObservable<bool> match, Action onComplete)
{
foreach (var keyframe in this)
{
@ -52,7 +52,6 @@ namespace Avalonia.Animation.Animators
// Continue if target prop is not empty & is a SolidColorBrush derivative.
if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType()))
{
SolidColorBrush finalTarget;
// If it's ISCB, change it back to SCB.
@ -68,7 +67,7 @@ namespace Avalonia.Animation.Animators
if (_colorAnimator == null)
InitializeColorAnimator(finalTarget);
return _colorAnimator.Apply(animation, finalTarget, clock ?? targetAnim.Clock, match, onComplete);
return _colorAnimator.Apply(animation, clock, match, onComplete);
}
return Disposable.Empty;

Loading…
Cancel
Save