diff --git a/build/Base.props b/build/Base.props index 9265d555c2..6689465338 100644 --- a/build/Base.props +++ b/build/Base.props @@ -1,6 +1,5 @@  - diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index d3a97c5b4f..a27d996301 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -23,7 +23,7 @@ namespace Avalonia.Animation /// public Animatable() { - Transitions = new Transitions.Transitions(); + Transitions = new Transitions(); AnimatableTimer = Timing.AnimationStateTimer .Select(p => { diff --git a/src/Avalonia.Animation/IKeyFrames.cs b/src/Avalonia.Animation/IKeyFrames.cs index d570f95b8b..ce1e096687 100644 --- a/src/Avalonia.Animation/IKeyFrames.cs +++ b/src/Avalonia.Animation/IKeyFrames.cs @@ -7,7 +7,7 @@ namespace Avalonia.Animation /// /// Interface for Keyframe group object /// - public interface IKeyFrames + internal interface IKeyFrames { /// /// Applies the current KeyFrame group to the specified control. diff --git a/src/Avalonia.Animation/KeyFrame.cs b/src/Avalonia.Animation/KeyFrame.cs index 93d858a8b5..d38d533715 100644 --- a/src/Avalonia.Animation/KeyFrame.cs +++ b/src/Avalonia.Animation/KeyFrame.cs @@ -64,7 +64,4 @@ namespace Avalonia.Animation public object Value { get; set; } } - - - } diff --git a/src/Avalonia.Animation/KeyFramesStateMachine`1.cs b/src/Avalonia.Animation/KeyFramesStateMachine`1.cs index 2e0785d66d..66971c534c 100644 --- a/src/Avalonia.Animation/KeyFramesStateMachine`1.cs +++ b/src/Avalonia.Animation/KeyFramesStateMachine`1.cs @@ -11,7 +11,7 @@ namespace Avalonia.Animation { object _lastInterpValue; object _firstKFValue; - + private ulong _delayTotalFrameCount, _durationTotalFrameCount, _delayFrameCount, @@ -33,18 +33,19 @@ namespace Avalonia.Animation internal bool _unsubscribe = false; private IObserver _targetObserver; + [Flags] private enum KeyFramesStates { - INITIALIZE, - DO_DELAY, - DO_RUN, - RUN_FORWARDS, - RUN_BACKWARDS, - RUN_APPLYVALUE, - RUN_COMPLETE, - PAUSE, - STOP, - DISPOSED + Initialize, + DoDelay, + DoRun, + RunForwards, + RunBackwards, + RunApplyValue, + RunComplete, + Pause, + Stop, + Disposed } public void Initialize(Animation animation, Animatable control, KeyFrames keyframes) @@ -82,24 +83,14 @@ namespace Avalonia.Animation break; } - switch (animation.PlaybackDirection) - { - case PlaybackDirection.Reverse: - case PlaybackDirection.AlternateReverse: - _isReversed = true; - break; - default: - _isReversed = false; - break; - } - + _isReversed = (animation.PlaybackDirection & PlaybackDirection.Reverse) != 0; _animationDirection = _targetAnimation.PlaybackDirection; _fillMode = _targetAnimation.FillMode; if (_durationTotalFrameCount > 0) - _currentState = KeyFramesStates.DO_DELAY; + _currentState = KeyFramesStates.DoDelay; else - _currentState = KeyFramesStates.DO_RUN; + _currentState = KeyFramesStates.DoRun; } @@ -124,21 +115,21 @@ namespace Avalonia.Animation _gotFirstKFValue = true; } - if (_currentState == KeyFramesStates.DISPOSED) + if (_currentState == KeyFramesStates.Disposed) throw new InvalidProgramException("This KeyFrames Animation is already disposed."); if (_playState == PlayState.Stop) - _currentState = KeyFramesStates.STOP; + _currentState = KeyFramesStates.Stop; // Save state and pause the machine - if (_playState == PlayState.Pause && _currentState != KeyFramesStates.PAUSE) + if (_playState == PlayState.Pause && _currentState != KeyFramesStates.Pause) { _savedState = _currentState; - _currentState = KeyFramesStates.PAUSE; + _currentState = KeyFramesStates.Pause; } // Resume the previous state - if (_playState != PlayState.Pause && _currentState == KeyFramesStates.PAUSE) + if (_playState != PlayState.Pause && _currentState == KeyFramesStates.Pause) _currentState = _savedState; double _tempDuration = 0d, _easedTime; @@ -146,7 +137,7 @@ namespace Avalonia.Animation checkstate: switch (_currentState) { - case KeyFramesStates.DO_DELAY: + case KeyFramesStates.DoDelay: if (_fillMode == FillMode.Backward || _fillMode == FillMode.Both) @@ -163,60 +154,60 @@ namespace Avalonia.Animation if (_delayFrameCount > _delayTotalFrameCount) { - _currentState = KeyFramesStates.DO_RUN; + _currentState = KeyFramesStates.DoRun; goto checkstate; } _delayFrameCount++; break; - case KeyFramesStates.DO_RUN: + case KeyFramesStates.DoRun: if (_isReversed) - _currentState = KeyFramesStates.RUN_BACKWARDS; + _currentState = KeyFramesStates.RunBackwards; else - _currentState = KeyFramesStates.RUN_FORWARDS; + _currentState = KeyFramesStates.RunForwards; goto checkstate; - case KeyFramesStates.RUN_FORWARDS: + case KeyFramesStates.RunForwards: if (_durationFrameCount > _durationTotalFrameCount) { - _currentState = KeyFramesStates.RUN_COMPLETE; + _currentState = KeyFramesStates.RunComplete; goto checkstate; } _tempDuration = (double)_durationFrameCount / _durationTotalFrameCount; - _currentState = KeyFramesStates.RUN_APPLYVALUE; + _currentState = KeyFramesStates.RunApplyValue; goto checkstate; - case KeyFramesStates.RUN_BACKWARDS: + case KeyFramesStates.RunBackwards: if (_durationFrameCount > _durationTotalFrameCount) { - _currentState = KeyFramesStates.RUN_COMPLETE; + _currentState = KeyFramesStates.RunComplete; goto checkstate; } _tempDuration = (double)(_durationTotalFrameCount - _durationFrameCount) / _durationTotalFrameCount; - _currentState = KeyFramesStates.RUN_APPLYVALUE; + _currentState = KeyFramesStates.RunApplyValue; goto checkstate; - case KeyFramesStates.RUN_APPLYVALUE: + case KeyFramesStates.RunApplyValue: _easedTime = _targetAnimation.Easing.Ease(_tempDuration); _durationFrameCount++; _lastInterpValue = Interpolator(_easedTime); _targetObserver.OnNext(_lastInterpValue); - _currentState = KeyFramesStates.DO_RUN; + _currentState = KeyFramesStates.DoRun; break; - case KeyFramesStates.RUN_COMPLETE: + case KeyFramesStates.RunComplete: if (_checkLoopAndRepeat) { @@ -225,17 +216,17 @@ namespace Avalonia.Animation if (_isLooping) { - _currentState = KeyFramesStates.DO_RUN; + _currentState = KeyFramesStates.DoRun; } else if (_isRepeating) { if (_currentIteration >= _repeatCount) { - _currentState = KeyFramesStates.STOP; + _currentState = KeyFramesStates.Stop; } else { - _currentState = KeyFramesStates.DO_RUN; + _currentState = KeyFramesStates.DoRun; } _currentIteration++; } @@ -247,14 +238,15 @@ namespace Avalonia.Animation break; } - _currentState = KeyFramesStates.STOP; + _currentState = KeyFramesStates.Stop; goto checkstate; - case KeyFramesStates.STOP: + case KeyFramesStates.Stop: + if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both) { - _targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.Animation); + _targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue); } _targetObserver.OnCompleted(); break; @@ -267,10 +259,12 @@ namespace Avalonia.Animation _targetObserver = observer; return this; } + public void Dispose() { _unsubscribe = true; - _currentState = KeyFramesStates.DISPOSED; + _currentState = KeyFramesStates.Disposed; } + } } \ No newline at end of file diff --git a/src/Avalonia.Animation/KeyFrames`1.cs b/src/Avalonia.Animation/KeyFrames`1.cs index c374bb5466..633d4f6fff 100644 --- a/src/Avalonia.Animation/KeyFrames`1.cs +++ b/src/Avalonia.Animation/KeyFrames`1.cs @@ -17,17 +17,17 @@ namespace Avalonia.Animation public abstract class KeyFrames : AvaloniaList, IKeyFrames { - private bool _isVerfifiedAndConverted; - /// - /// Target property. + /// List of type-converted keyframes. /// - public AvaloniaProperty Property { get; set; } + private Dictionary _convertedKeyframes = new Dictionary(); + + private bool _isVerfifiedAndConverted; /// - /// List of type-converted keyframes. + /// Gets or sets the target property for the keyframe. /// - public Dictionary ConvertedKeyframes = new Dictionary(); + public AvaloniaProperty Property { get; set; } /// public virtual IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch) @@ -47,38 +47,38 @@ namespace Avalonia.Animation /// /// Get the nearest pair of cue-time ordered keyframes - /// according to the given time parameter that is relative to + /// according to the given time parameter that is relative to the /// total animation time and the normalized intra-keyframe pair time /// (i.e., the normalized time between the selected keyframes, relative to the /// time parameter). /// /// The time parameter, relative to the total animation time - public (double IntraKFTime, KeyFramePair KFPair) GetKFPairAndIntraKFTime(double t) + protected (double IntraKFTime, KeyFramePair KFPair) GetKFPairAndIntraKFTime(double t) { KeyValuePair firstCue, lastCue; - int kvCount = ConvertedKeyframes.Count(); + int kvCount = _convertedKeyframes.Count(); if (kvCount > 2) { if (DoubleUtils.AboutEqual(t, 0.0) || t < 0.0) { - firstCue = ConvertedKeyframes.First(); - lastCue = ConvertedKeyframes.Skip(1).First(); + firstCue = _convertedKeyframes.First(); + lastCue = _convertedKeyframes.Skip(1).First(); } else if (DoubleUtils.AboutEqual(t, 1.0) || t > 1.0) { - firstCue = ConvertedKeyframes.Skip(kvCount - 2).First(); - lastCue = ConvertedKeyframes.Last(); + firstCue = _convertedKeyframes.Skip(kvCount - 2).First(); + lastCue = _convertedKeyframes.Last(); } else { - firstCue = ConvertedKeyframes.Where(j => j.Key <= t).Last(); - lastCue = ConvertedKeyframes.Where(j => j.Key >= t).First(); + firstCue = _convertedKeyframes.Where(j => j.Key <= t).Last(); + lastCue = _convertedKeyframes.Where(j => j.Key >= t).First(); } } else { - firstCue = ConvertedKeyframes.First(); - lastCue = ConvertedKeyframes.Last(); + firstCue = _convertedKeyframes.First(); + lastCue = _convertedKeyframes.Last(); } double t0 = firstCue.Key; @@ -91,7 +91,7 @@ namespace Avalonia.Animation /// /// Runs the KeyFrames Animation. /// - public IDisposable RunKeyFrames(Animation animation, Animatable control) + private IDisposable RunKeyFrames(Animation animation, Animatable control) { var _kfStateMach = new KeyFramesStateMachine(); _kfStateMach.Initialize(animation, control, this); @@ -109,12 +109,7 @@ namespace Avalonia.Animation /// /// Interpolates a value given the desired time. /// - public abstract T DoInterpolation(double time); - - - // public abstract IObservable DoInterpolation(IObservable timer, Animation animation, - // Animatable control); - + protected abstract T DoInterpolation(double time); /// /// Verifies and converts keyframe values according to this class's target type. @@ -144,11 +139,11 @@ namespace Avalonia.Animation _normalizedCue = new Cue(k.KeyTime.Ticks / animation.Duration.Ticks); } - ConvertedKeyframes.Add(_normalizedCue.CueValue, convertedValue); + _convertedKeyframes.Add(_normalizedCue.CueValue, convertedValue); } - SortKeyFrameCues(ConvertedKeyframes); + SortKeyFrameCues(_convertedKeyframes); _isVerfifiedAndConverted = true; } @@ -158,9 +153,8 @@ namespace Avalonia.Animation bool hasStartKey, hasEndKey; hasStartKey = hasEndKey = false; - // this can be optional later, by making the default start/end keyframes - // to have a neutral value (a.k.a. the value prior to the animation). - foreach (var converted in ConvertedKeyframes.Keys) + // Make start and end keyframe mandatory. + foreach (var converted in _convertedKeyframes.Keys) { if (DoubleUtils.AboutEqual(converted, 0.0)) { @@ -177,8 +171,8 @@ namespace Avalonia.Animation ($"{this.GetType().Name} must have a starting (0% cue) and ending (100% cue) keyframe."); // Sort Cues, in case users don't order it by themselves. - ConvertedKeyframes = ConvertedKeyframes.OrderBy(p => p.Key) - .ToDictionary((k) => k.Key, (v) => v.Value); + _convertedKeyframes = _convertedKeyframes.OrderBy(p => p.Key) + .ToDictionary((k) => k.Key, (v) => v.Value); } } diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 5583cb229a..8a59a451a2 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -88,11 +88,8 @@ namespace Avalonia.Styling { return _animations ?? (_animations = new List()); } - set - { - _animations = value; - } } + /// IResourceNode IResourceNode.ResourceParent => _parent; @@ -118,8 +115,7 @@ namespace Avalonia.Styling foreach (var animation in Animations) { - // TODO: Needs more work in passing the appropriate - // observable. + IObservable obsMatch = match.ObservableResult; if (match.ImmediateResult == true) diff --git a/src/Avalonia.Visuals/Animation/TransformKeyFrames.cs b/src/Avalonia.Visuals/Animation/TransformKeyFrames.cs index 18c13f8268..113ebbce91 100644 --- a/src/Avalonia.Visuals/Animation/TransformKeyFrames.cs +++ b/src/Avalonia.Visuals/Animation/TransformKeyFrames.cs @@ -11,7 +11,7 @@ using Avalonia.Media; namespace Avalonia.Animation { /// - /// Key frames that handles properties. + /// Keyframes that handles properties. /// public class TransformKeyFrames : KeyFrames { @@ -29,7 +29,7 @@ namespace Avalonia.Animation if (childKeyFrames == null) { - InitializeInternalDoubleKeyFrames(); + InitializeChildKeyFrames(); } // It's a transform object so let's target that. @@ -49,6 +49,7 @@ namespace Avalonia.Animation } } + //TODO: determine correctly when to throw this error. //throw new InvalidOperationException($"TransformKeyFrame hasn't found an appropriate Transform object with type {Property.OwnerType} in target {control}."); return null; @@ -56,11 +57,11 @@ namespace Avalonia.Animation } else { - throw new Exception($"Unsupported property {Property}"); + throw new Exception($"Cannot apply keyframe: property {Property} is not a Transform"); } } - void InitializeInternalDoubleKeyFrames() + void InitializeChildKeyFrames() { childKeyFrames = new DoubleKeyFrames(); @@ -73,6 +74,6 @@ namespace Avalonia.Animation } /// - public override double DoInterpolation(double time) => 0; + protected override double DoInterpolation(double time) => 0; } } diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 2e962fccb3..94b1f89cc5 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -81,32 +81,6 @@ namespace Avalonia.Media (byte)(value & 0xff) ); } - - // /// - // /// Creates a from a . - // /// - // /// The value. - // /// True if the color vector is normalized floats, false if it's bytes (0-255) - // /// The color. - // public static Color FromVector4(Vector4 value, bool normalized) - // { - // if (normalized) - // { - // return new Color( - // (byte)(value.W * 255), - // (byte)(value.X * 255), - // (byte)(value.Y * 255), - // (byte)(value.Z * 255) - // ); - // } else { - // return new Color( - // (byte)(value.W), - // (byte)(value.X), - // (byte)(value.Y), - // (byte)(value.Z) - // ); - // } - // } /// /// Parses a color string. diff --git a/src/Avalonia.Visuals/Media/TransformGroup.cs b/src/Avalonia.Visuals/Media/TransformGroup.cs index 12a98d881a..dd9a3f9abf 100644 --- a/src/Avalonia.Visuals/Media/TransformGroup.cs +++ b/src/Avalonia.Visuals/Media/TransformGroup.cs @@ -18,23 +18,13 @@ namespace Avalonia.Media public TransformGroup() { Children = new Transforms(); - Children.CollectionChanged += (_, e) => + Children.ResetBehavior = ResetBehavior.Remove; + Children.CollectionChanged += delegate { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (Transform tr in e.NewItems) - { - tr.Changed += ChildTransform_Changed; - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (Transform tr in e.OldItems) - { - tr.Changed -= ChildTransform_Changed; - } - break; - } + Children.ForEachItem( + (tr) => tr.Changed += ChildTransform_Changed, + (tr) => tr.Changed -= ChildTransform_Changed, + () => { }); }; }