Browse Source

Make sure there's only one instance of Animator spawned per property target key. Add unit test for this.

pull/2661/head
Jumar Macato 7 years ago
parent
commit
c180191ef1
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 2
      samples/RenderDemo/Pages/AnimationsPage.xaml
  2. 15
      src/Avalonia.Animation/Animation.cs
  3. 31
      src/Avalonia.Animation/AnimationTarget.cs
  4. 13
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  5. 94
      tests/Avalonia.Animation.UnitTests/AnimationGeneralTests.cs

2
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -105,7 +105,7 @@
</Animation>
</Style.Animations>
</Style>
<Style Selector="Border.Rect6:pointerover">
<Style Selector="Border.Rect6">
<Style.Animations>
<Animation Duration="0:0:3"
IterationCount="Infinite"

15
src/Avalonia.Animation/Animation.cs

@ -309,7 +309,7 @@ namespace Avalonia.Animation
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>();
var animatorInstances = new Dictionary<AnimationTarget, IAnimator>(new AnimationTarget.EqualityComparer());
foreach (var keyframe in Children)
{
@ -320,6 +320,7 @@ namespace Avalonia.Animation
if (haltProcessing) return (null, null);
var animatorType = GetAnimatorType(target.TargetProperty);
target.AnimatorHandlerType = animatorType;
if (animatorType == null)
{
@ -331,15 +332,19 @@ namespace Avalonia.Animation
}
IAnimator animator;
if (!animatorInstances.Keys.Contains((animatorType, target)))
var k = animatorInstances.Keys.Where(p => target.Equals(p)).Any();
if (k)
{
animator = animatorInstances[target];
}
else
{
var newAnimator = (IAnimator)Activator.CreateInstance(animatorType);
newAnimator.Target = target;
animatorInstances[(animatorType, target)] = newAnimator;
animatorInstances.Add(target, newAnimator);
animator = newAnimator;
}
animator = animatorInstances[(animatorType, target)];
var cue = keyframe.Cue;

31
src/Avalonia.Animation/AnimationTarget.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Data.Core;
namespace Avalonia.Animation
@ -18,6 +19,8 @@ namespace Avalonia.Animation
TargetProperty = property;
}
public Type AnimatorHandlerType { get; internal set; }
public Animatable RootAnimatable { get; internal set; }
/// <summary>
@ -38,17 +41,31 @@ namespace Avalonia.Animation
/// </summary>
public object TargetObject { get; internal set; }
public bool Equals(AnimationTarget other)
public class EqualityComparer : IEqualityComparer<AnimationTarget>
{
if (TargetProperty == null || TargetAnimatable == null ||
other.TargetProperty == null || other.TargetAnimatable == null)
public bool Equals(AnimationTarget x, AnimationTarget y)
{
return x.Equals(y);
}
public int GetHashCode(AnimationTarget obj)
{
return false;
return obj.AnimatorHandlerType.GetHashCode() ^
obj.RootAnimatable.GetHashCode() ^
((IPropertyInfo)obj.TargetProperty).GetHashCode();
}
}
public bool Equals(AnimationTarget other)
{
if (other == null) return false;
var EQ = ((IPropertyInfo)this.TargetProperty).Equals((IPropertyInfo)other.TargetProperty) &&
this.RootAnimatable.Equals(other.RootAnimatable) &&
this.AnimatorHandlerType.Equals(other.AnimatorHandlerType);
return ((IPropertyInfo)this.TargetProperty).Equals((IPropertyInfo)other.TargetProperty) &&
this.TargetAnimatable.Equals(other.TargetAnimatable);
return EQ;
}
}
}

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

@ -29,14 +29,21 @@ namespace Avalonia.Animation.Animators
{
foreach (var keyframe in this)
{
if (keyframe.Value as ISolidColorBrush == null)
return Disposable.Empty;
var k = keyframe.Value.GetType();
// Preprocess keyframe values to Color if the xaml parser converts them to ISCB.
if (keyframe.Value.GetType() == typeof(ImmutableSolidColorBrush))
if (k == typeof(ImmutableSolidColorBrush))
{
keyframe.Value = ((ImmutableSolidColorBrush)keyframe.Value).Color;
}
else if (k == typeof(Color))
{
continue;
}
else
{
return Disposable.Empty;
}
}
var targetObj = Target.TargetObject;

94
tests/Avalonia.Animation.UnitTests/AnimationGeneralTests.cs

@ -0,0 +1,94 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.Data;
using Xunit;
using Avalonia.Animation.Easings;
using Avalonia.Data.Core;
using Avalonia.Media;
namespace Avalonia.Animation.UnitTests
{
public class AnimationGeneralTests
{
[Fact]
public void Test_Color_Animations_Single()
{
// initialize SCB handler
var initSCB = new SolidColorBrush();
var kf1 = new KeyFrame()
{
Setters =
{
new Setter(null, Colors.White)
{ PropertyPath = new PropertyPathBuilder().Property(Border.BackgroundProperty).Build() }
},
Cue = new Cue(0d)
};
var kf2 = new KeyFrame()
{
Setters =
{
new Setter(null, Colors.Black)
{ PropertyPath = new PropertyPathBuilder().Property(Border.BackgroundProperty).Build() }
},
Cue = new Cue(1d)
};
var animation = new Animation()
{
FillMode = FillMode.Both,
Duration = TimeSpan.FromSeconds(3),
Children =
{
kf1,
kf2
}
};
var border = new Border()
{
};
var clock = new TestClock();
var testTSO = new TestSelectorObservable();
animation.Apply(border, clock, testTSO, () => { });
testTSO.Toggle(true);
clock.Step(TimeSpan.Zero);
// Initial Delay.
clock.Step(TimeSpan.FromSeconds(3));
Assert.Equal(Colors.Black, ((SolidColorBrush)border.Background).Color);
}
public class TestSelectorObservable : IObservable<bool>, IDisposable
{
IObserver<bool> testObserver;
public void Toggle(bool val)
{
testObserver?.OnNext(val);
}
public void Dispose()
{
testObserver?.OnCompleted();
}
public IDisposable Subscribe(IObserver<bool> observer)
{
testObserver = observer;
return this;
}
}
}
}
Loading…
Cancel
Save