Browse Source

Dispose subscriptions on completed code-behind run of animation.

pull/1774/head
Jeremy Koritzinsky 8 years ago
parent
commit
7d1b7593a5
  1. 39
      src/Avalonia.Animation/Animation.cs

39
src/Avalonia.Animation/Animation.cs

@ -12,13 +12,14 @@ using System.Reflection;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Disposables;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Tracks the progress of an animation. /// Tracks the progress of an animation.
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IDisposable, IAnimation public class Animation : AvaloniaList<KeyFrame>, IAnimation
{ {
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)> private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{ {
@ -43,7 +44,6 @@ namespace Avalonia.Animation
return null; return null;
} }
private List<IDisposable> _subscription = new List<IDisposable>();
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>(); public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
/// <summary> /// <summary>
@ -76,10 +76,11 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public Easing Easing { get; set; } = new LinearEasing(); public Easing Easing { get; set; } = new LinearEasing();
private IList<IAnimator> InterpretKeyframes(Animatable control) private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control)
{ {
var handlerList = new List<(Type type, AvaloniaProperty property)>(); var handlerList = new List<(Type type, AvaloniaProperty property)>();
var animatorKeyFrames = new List<AnimatorKeyFrame>(); var animatorKeyFrames = new List<AnimatorKeyFrame>();
var subscriptions = new List<IDisposable>();
foreach (var keyframe in this) foreach (var keyframe in this)
{ {
@ -104,7 +105,7 @@ namespace Avalonia.Animation
var newKF = new AnimatorKeyFrame(handler, cue); var newKF = new AnimatorKeyFrame(handler, cue);
_subscription.Add(newKF.BindSetter(setter, control)); subscriptions.Add(newKF.BindSetter(setter, control));
animatorKeyFrames.Add(newKF); animatorKeyFrames.Add(newKF);
} }
@ -126,32 +127,21 @@ namespace Avalonia.Animation
animator.Add(keyframe); animator.Add(keyframe);
} }
return newAnimatorInstances; return (newAnimatorInstances, subscriptions);
}
/// <summary>
/// Cancels the animation.
/// </summary>
public void Dispose()
{
foreach (var sub in _subscription)
{
sub.Dispose();
}
} }
/// <inheritdocs/> /// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete) public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete)
{ {
var animators = InterpretKeyframes(control); var (animators, subscriptions) = InterpretKeyframes(control);
if (animators.Count == 1) if (animators.Count == 1)
{ {
_subscription.Add(animators[0].Apply(this, control, match, onComplete)); subscriptions.Add(animators[0].Apply(this, control, match, onComplete));
} }
else else
{ {
var completionTasks = onComplete != null ? new List<Task>() : null; var completionTasks = onComplete != null ? new List<Task>() : null;
foreach (IAnimator animator in InterpretKeyframes(control)) foreach (IAnimator animator in animators)
{ {
Action animatorOnComplete = null; Action animatorOnComplete = null;
if (onComplete != null) if (onComplete != null)
@ -160,7 +150,7 @@ namespace Avalonia.Animation
animatorOnComplete = () => tcs.SetResult(null); animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task); completionTasks.Add(tcs.Task);
} }
_subscription.Add(animator.Apply(this, control, match, animatorOnComplete)); subscriptions.Add(animator.Apply(this, control, match, animatorOnComplete));
} }
if (onComplete != null) if (onComplete != null)
@ -168,7 +158,7 @@ namespace Avalonia.Animation
Task.WhenAll(completionTasks).ContinueWith(_ => onComplete()); Task.WhenAll(completionTasks).ContinueWith(_ => onComplete());
} }
} }
return this; return new CompositeDisposable(subscriptions);
} }
/// <inheritdocs/> /// <inheritdocs/>
@ -179,7 +169,12 @@ namespace Avalonia.Animation
if (this.RepeatCount == RepeatCount.Loop) if (this.RepeatCount == RepeatCount.Loop)
run.SetException(new InvalidOperationException("Looping animations must not use the Run method.")); run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
this.Apply(control, Observable.Return(true), () => run.SetResult(null)); IDisposable subscriptions = null;
subscriptions = this.Apply(control, Observable.Return(true), () =>
{
run.SetResult(null);
subscriptions.Dispose();
});
return run.Task; return run.Task;
} }

Loading…
Cancel
Save