From 1f8400a6ff2da8f627285f2fdccd03803dedb19c Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 25 Jun 2018 17:09:53 +0800 Subject: [PATCH 001/135] Fix ghosting artifacts on both Renderers #1687 --- src/Avalonia.Visuals/Rendering/DeferredRenderer.cs | 2 ++ src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index a14923b410..ed557b8b74 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -337,6 +337,8 @@ namespace Avalonia.Rendering private void RenderComposite(Scene scene, IDrawingContextImpl context) { + context.Clear(Colors.Transparent); + var clientRect = new Rect(scene.Size); foreach (var layer in scene.Layers) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 2118b66de2..93bb36c3cd 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -224,6 +224,9 @@ namespace Avalonia.Rendering var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); + if (visual == _root) + context.PlatformImpl.Clear(Colors.Transparent); + if (visual.IsVisible && opacity > 0) { var m = Matrix.CreateTranslation(visual.Bounds.Position); From db50c65340da5ec073389d45dbad87d1f518b4c9 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 27 Jun 2018 08:08:47 +0800 Subject: [PATCH 002/135] Move ImmediateRenderer clearing code to Paint(). --- src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 93bb36c3cd..a9e251865b 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -54,6 +54,9 @@ namespace Avalonia.Rendering { using (var context = new DrawingContext(_renderTarget.CreateDrawingContext(this))) { + + context.PlatformImpl.Clear(Colors.Transparent); + using (context.PushTransformContainer()) { Render(context, _root, _root.Bounds); @@ -224,9 +227,6 @@ namespace Avalonia.Rendering var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); - if (visual == _root) - context.PlatformImpl.Clear(Colors.Transparent); - if (visual.IsVisible && opacity > 0) { var m = Matrix.CreateTranslation(visual.Bounds.Position); From f92339b251458f1cf7e417097618c33dbc7d74c7 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 27 Jun 2018 13:48:59 +0800 Subject: [PATCH 003/135] Fix nit --- src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index a9e251865b..24638ad150 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -54,7 +54,6 @@ namespace Avalonia.Rendering { using (var context = new DrawingContext(_renderTarget.CreateDrawingContext(this))) { - context.PlatformImpl.Clear(Colors.Transparent); using (context.PushTransformContainer()) From bf104a1e902e478be7c6afb250b69695e1beb167 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 19 Jul 2018 01:00:15 +0800 Subject: [PATCH 004/135] Set AlphaMode to Premultiplied in SwapChainRenderTarget.cs --- src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs index 0a23c63498..ea52081393 100644 --- a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs @@ -119,7 +119,7 @@ namespace Avalonia.Direct2D1 new BitmapProperties1( new PixelFormat { - AlphaMode = AlphaMode.Ignore, + AlphaMode = AlphaMode.Premultiplied, Format = Format.B8G8R8A8_UNorm }, _savedDpi.Width, From 0624d300376d2140b069a53a9808ff59933a44c5 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 01:24:17 +0800 Subject: [PATCH 005/135] Make ASM more Genericized. --- src/Avalonia.Animation/AnimatorStateMachine`1.cs | 12 ++++++------ src/Avalonia.Animation/Animator`1.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 1a51b897c0..414f07190a 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -7,10 +7,10 @@ namespace Avalonia.Animation /// /// Provides statefulness for an iteration of a keyframe animation. /// - internal class AnimatorStateMachine : IObservable, IDisposable + internal class AnimatorStateMachine : IObservable, IDisposable { - object _lastInterpValue; - object _firstKFValue; + T _lastInterpValue; + T _firstKFValue; private ulong _delayTotalFrameCount; private ulong _durationTotalFrameCount; @@ -34,7 +34,7 @@ namespace Avalonia.Animation private Animatable _targetControl; private T _neutralValue; internal bool _unsubscribe = false; - private IObserver _targetObserver; + private IObserver _targetObserver; [Flags] private enum KeyFramesStates @@ -100,7 +100,7 @@ namespace Avalonia.Animation { if (!_gotFirstKFValue) { - _firstKFValue = _parent.First().Value; + _firstKFValue = (T)_parent.First().Value; _gotFirstKFValue = true; } @@ -253,7 +253,7 @@ namespace Avalonia.Animation } } - public IDisposable Subscribe(IObserver observer) + public IDisposable Subscribe(IObserver observer) { _targetObserver = observer; return this; diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index a1eef87e1e..37764b34e8 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -106,7 +106,7 @@ namespace Avalonia.Animation .TakeWhile(_ => !stateMachine._unsubscribe) .Subscribe(p => stateMachine.Step(p, DoInterpolation)); - return control.Bind(Property, stateMachine, BindingPriority.Animation); + return control.Bind((AvaloniaProperty)Property, stateMachine, BindingPriority.Animation); } /// From 87934f2bfda67acb52dd363e910e4864db543c31 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 01:30:43 +0800 Subject: [PATCH 006/135] Remove underscores on ASM fields. --- .../AnimatorStateMachine`1.cs | 218 +++++++++--------- src/Avalonia.Animation/Animator`1.cs | 2 +- 2 files changed, 110 insertions(+), 110 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 414f07190a..923cdd07b3 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -9,32 +9,32 @@ namespace Avalonia.Animation /// internal class AnimatorStateMachine : IObservable, IDisposable { - T _lastInterpValue; - T _firstKFValue; - - private ulong _delayTotalFrameCount; - private ulong _durationTotalFrameCount; - private ulong _delayFrameCount; - private ulong _durationFrameCount; - private ulong _repeatCount; - private ulong _currentIteration; - - private bool _isLooping; - private bool _isRepeating; - private bool _isReversed; - private bool _checkLoopAndRepeat; - private bool _gotFirstKFValue; - - private FillMode _fillMode; - private PlaybackDirection _animationDirection; - private KeyFramesStates _currentState; - private KeyFramesStates _savedState; - private Animator _parent; - private Animation _targetAnimation; - private Animatable _targetControl; - private T _neutralValue; - internal bool _unsubscribe = false; - private IObserver _targetObserver; + T lastInterpValue; + T firstKFValue; + + private ulong delayTotalFrameCount; + private ulong durationTotalFrameCount; + private ulong delayFrameCount; + private ulong durationFrameCount; + private ulong repeatCount; + private ulong currentIteration; + + private bool isLooping; + private bool isRepeating; + private bool isReversed; + private bool checkLoopAndRepeat; + private bool gotFirstKFValue; + + private FillMode fillMode; + private PlaybackDirection animationDirection; + private KeyFramesStates currentState; + private KeyFramesStates savedState; + private Animator parent; + private Animation targetAnimation; + private Animatable targetControl; + private T neutralValue; + internal bool unsubscribe = false; + private IObserver targetObserver; [Flags] private enum KeyFramesStates @@ -53,197 +53,197 @@ namespace Avalonia.Animation public void Initialize(Animation animation, Animatable control, Animator animator) { - _parent = animator; - _targetAnimation = animation; - _targetControl = control; - _neutralValue = (T)_targetControl.GetValue(_parent.Property); + parent = animator; + targetAnimation = animation; + targetControl = control; + neutralValue = (T)targetControl.GetValue(parent.Property); - _delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks); - _durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks); + delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks); + durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks); switch (animation.RepeatCount.RepeatType) { case RepeatType.Loop: - _isLooping = true; - _checkLoopAndRepeat = true; + isLooping = true; + checkLoopAndRepeat = true; break; case RepeatType.Repeat: - _isRepeating = true; - _checkLoopAndRepeat = true; - _repeatCount = animation.RepeatCount.Value; + isRepeating = true; + checkLoopAndRepeat = true; + repeatCount = animation.RepeatCount.Value; break; } - _isReversed = (animation.PlaybackDirection & PlaybackDirection.Reverse) != 0; - _animationDirection = _targetAnimation.PlaybackDirection; - _fillMode = _targetAnimation.FillMode; + isReversed = (animation.PlaybackDirection & PlaybackDirection.Reverse) != 0; + animationDirection = targetAnimation.PlaybackDirection; + fillMode = targetAnimation.FillMode; - if (_durationTotalFrameCount > 0) - _currentState = KeyFramesStates.DoDelay; + if (durationTotalFrameCount > 0) + currentState = KeyFramesStates.DoDelay; else - _currentState = KeyFramesStates.DoRun; + currentState = KeyFramesStates.DoRun; } - public void Step(PlayState _playState, Func Interpolator) + public void Step(PlayState playState, Func Interpolator) { try { - InternalStep(_playState, Interpolator); + InternalStep(playState, Interpolator); } catch (Exception e) { - _targetObserver?.OnError(e); + targetObserver?.OnError(e); } } - private void InternalStep(PlayState _playState, Func Interpolator) + private void InternalStep(PlayState playState, Func Interpolator) { - if (!_gotFirstKFValue) + if (!gotFirstKFValue) { - _firstKFValue = (T)_parent.First().Value; - _gotFirstKFValue = true; + firstKFValue = (T)parent.First().Value; + 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; + if (playState == PlayState.Stop) + currentState = KeyFramesStates.Stop; - // Save state and pause the machine - if (_playState == PlayState.Pause && _currentState != KeyFramesStates.Pause) - { - _savedState = _currentState; - _currentState = KeyFramesStates.Pause; - } + // // Save state and pause the machine + // if (playState == PlayState.Pause && currentState != KeyFramesStates.Pause) + // { + // savedState = currentState; + // currentState = KeyFramesStates.Pause; + // } - // Resume the previous state - if (_playState != PlayState.Pause && _currentState == KeyFramesStates.Pause) - _currentState = _savedState; + // // Resume the previous state + // if (playState != PlayState.Pause && currentState == KeyFramesStates.Pause) + // currentState = savedState; - double _tempDuration = 0d, _easedTime; + double tempDuration = 0d, easedTime; bool handled = false; while (!handled) { - switch (_currentState) + switch (currentState) { case KeyFramesStates.DoDelay: - if (_fillMode == FillMode.Backward - || _fillMode == FillMode.Both) + if (fillMode == FillMode.Backward + || fillMode == FillMode.Both) { - if (_currentIteration == 0) + if (currentIteration == 0) { - _targetObserver.OnNext(_firstKFValue); + targetObserver.OnNext(firstKFValue); } else { - _targetObserver.OnNext(_lastInterpValue); + targetObserver.OnNext(lastInterpValue); } } - if (_delayFrameCount > _delayTotalFrameCount) + if (delayFrameCount > delayTotalFrameCount) { - _currentState = KeyFramesStates.DoRun; + currentState = KeyFramesStates.DoRun; } else { handled = true; - _delayFrameCount++; + delayFrameCount++; } break; case KeyFramesStates.DoRun: - if (_isReversed) - _currentState = KeyFramesStates.RunBackwards; + if (isReversed) + currentState = KeyFramesStates.RunBackwards; else - _currentState = KeyFramesStates.RunForwards; + currentState = KeyFramesStates.RunForwards; break; case KeyFramesStates.RunForwards: - if (_durationFrameCount > _durationTotalFrameCount) + if (durationFrameCount > durationTotalFrameCount) { - _currentState = KeyFramesStates.RunComplete; + currentState = KeyFramesStates.RunComplete; } else { - _tempDuration = (double)_durationFrameCount / _durationTotalFrameCount; - _currentState = KeyFramesStates.RunApplyValue; + tempDuration = (double)durationFrameCount / durationTotalFrameCount; + currentState = KeyFramesStates.RunApplyValue; } break; case KeyFramesStates.RunBackwards: - if (_durationFrameCount > _durationTotalFrameCount) + if (durationFrameCount > durationTotalFrameCount) { - _currentState = KeyFramesStates.RunComplete; + currentState = KeyFramesStates.RunComplete; } else { - _tempDuration = (double)(_durationTotalFrameCount - _durationFrameCount) / _durationTotalFrameCount; - _currentState = KeyFramesStates.RunApplyValue; + tempDuration = (double)(durationTotalFrameCount - durationFrameCount) / durationTotalFrameCount; + currentState = KeyFramesStates.RunApplyValue; } break; case KeyFramesStates.RunApplyValue: - _easedTime = _targetAnimation.Easing.Ease(_tempDuration); + easedTime = targetAnimation.Easing.Ease(tempDuration); - _durationFrameCount++; - _lastInterpValue = Interpolator(_easedTime, _neutralValue); - _targetObserver.OnNext(_lastInterpValue); - _currentState = KeyFramesStates.DoRun; + durationFrameCount++; + lastInterpValue = Interpolator(easedTime, neutralValue); + targetObserver.OnNext(lastInterpValue); + currentState = KeyFramesStates.DoRun; handled = true; break; case KeyFramesStates.RunComplete: - if (_checkLoopAndRepeat) + if (checkLoopAndRepeat) { - _delayFrameCount = 0; - _durationFrameCount = 0; + delayFrameCount = 0; + durationFrameCount = 0; - if (_isLooping) + if (isLooping) { - _currentState = KeyFramesStates.DoRun; + currentState = KeyFramesStates.DoRun; } - else if (_isRepeating) + else if (isRepeating) { - if (_currentIteration >= _repeatCount) + if (currentIteration >= repeatCount) { - _currentState = KeyFramesStates.Stop; + currentState = KeyFramesStates.Stop; } else { - _currentState = KeyFramesStates.DoRun; + currentState = KeyFramesStates.DoRun; } - _currentIteration++; + currentIteration++; } - if (_animationDirection == PlaybackDirection.Alternate - || _animationDirection == PlaybackDirection.AlternateReverse) - _isReversed = !_isReversed; + if (animationDirection == PlaybackDirection.Alternate + || animationDirection == PlaybackDirection.AlternateReverse) + isReversed = !isReversed; break; } - _currentState = KeyFramesStates.Stop; + currentState = KeyFramesStates.Stop; break; case KeyFramesStates.Stop: - if (_fillMode == FillMode.Forward - || _fillMode == FillMode.Both) + if (fillMode == FillMode.Forward + || fillMode == FillMode.Both) { - _targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue); + targetControl.SetValue(parent.Property, lastInterpValue, BindingPriority.LocalValue); } - _targetObserver.OnCompleted(); + targetObserver.OnCompleted(); handled = true; break; default: @@ -255,14 +255,14 @@ namespace Avalonia.Animation public IDisposable Subscribe(IObserver observer) { - _targetObserver = observer; + targetObserver = observer; return this; } public void Dispose() { - _unsubscribe = true; - _currentState = KeyFramesStates.Disposed; + unsubscribe = true; + currentState = KeyFramesStates.Disposed; } } } diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index 37764b34e8..654ee327f8 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -103,7 +103,7 @@ namespace Avalonia.Animation stateMachine.Initialize(animation, control, this); Timing.AnimationStateTimer - .TakeWhile(_ => !stateMachine._unsubscribe) + .TakeWhile(_ => !stateMachine.unsubscribe) .Subscribe(p => stateMachine.Step(p, DoInterpolation)); return control.Bind((AvaloniaProperty)Property, stateMachine, BindingPriority.Animation); From 127d060f6611045628ea0afa5eeae78f7d33a207 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 01:47:45 +0800 Subject: [PATCH 007/135] Add new properties for Animations. --- src/Avalonia.Animation/Animation.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 4e777b36ed..eaf6b280bc 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -75,6 +75,16 @@ namespace Avalonia.Animation /// public Easing Easing { get; set; } = new LinearEasing(); + /// + /// Sets the speed multiple for this animation. + /// + public double SpeedRatio { get; set; } = 1d; + + /// + /// Sets the behavior for having a delay between repeats for this animation. + /// + public bool DelayBetweenRepeats { get; set; } + public Animation() { this.CollectionChanged += delegate { _isChildrenChanged = true; }; From b3ebfa574865a5aee54594e6dd48ca38e11e0713 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 02:03:14 +0800 Subject: [PATCH 008/135] Add the global frame count to AnimationStateTimer. --- src/Avalonia.Animation/Timing.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index 10d65cca7f..3eddc0ac26 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -16,6 +16,7 @@ namespace Avalonia.Animation public static class Timing { static ulong _transitionsFrameCount; + static long _tickStartTimeStamp; static PlayState _globalState = PlayState.Run; /// @@ -33,12 +34,16 @@ namespace Avalonia.Animation /// static Timing() { + + _tickStartTimeStamp = Stopwatch.GetTimestamp(); + var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); AnimationStateTimer = globalTimer .Select(_ => { - return _globalState; + return (_globalState, (Stopwatch.GetTimestamp() - _tickStartTimeStamp) + / (Stopwatch.Frequency / FramesPerSecond)); }) .Publish() .RefCount(); @@ -76,7 +81,7 @@ namespace Avalonia.Animation /// defined in . /// The parameter passed to a subsciber is the current playstate of the animation. /// - internal static IObservable AnimationStateTimer + internal static IObservable<(PlayState, long)> AnimationStateTimer { get; } From 5365fd0a41220d840acb7d6d2511f3f3fda8478b Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 02:04:38 +0800 Subject: [PATCH 009/135] Add the new animation algorithm. --- src/Avalonia.Animation/Animatable.cs | 4 +- .../AnimatorStateMachine`1.cs | 284 +++++++----------- src/Avalonia.Animation/Animator`1.cs | 2 +- 3 files changed, 113 insertions(+), 177 deletions(-) diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index a27d996301..85317af1a8 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -29,7 +29,7 @@ namespace Avalonia.Animation { if (this._playState == PlayState.Pause) { - return PlayState.Pause; + return (PlayState.Pause, p.Item2); } else return p; }) @@ -41,7 +41,7 @@ namespace Avalonia.Animation /// The specific animations timer for this control. /// /// - public IObservable AnimatableTimer; + public IObservable<(PlayState, long)> AnimatableTimer; /// /// Defines the property. diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 923cdd07b3..6854645cc2 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Avalonia.Animation.Utils; using Avalonia.Data; namespace Avalonia.Animation @@ -12,91 +13,97 @@ namespace Avalonia.Animation T lastInterpValue; T firstKFValue; - private ulong delayTotalFrameCount; - private ulong durationTotalFrameCount; - private ulong delayFrameCount; - private ulong durationFrameCount; - private ulong repeatCount; - private ulong currentIteration; + private long delayFC; + private long durationFC; + private long repeatCount; + private long currentIteration; + private long firstFrameCount; private bool isLooping; private bool isRepeating; - private bool isReversed; - private bool checkLoopAndRepeat; private bool gotFirstKFValue; + private bool gotFirstFrameCount; + private bool delayBetweenIterations; private FillMode fillMode; private PlaybackDirection animationDirection; - private KeyFramesStates currentState; - private KeyFramesStates savedState; private Animator parent; - private Animation targetAnimation; private Animatable targetControl; private T neutralValue; - internal bool unsubscribe = false; - private IObserver targetObserver; + private double speedRatio; + internal bool unsubscribe; + private bool isDisposed; - [Flags] - private enum KeyFramesStates - { - Initialize, - DoDelay, - DoRun, - RunForwards, - RunBackwards, - RunApplyValue, - RunComplete, - Pause, - Stop, - Disposed - } + private Easings.Easing EaseFunc; + private IObserver targetObserver; public void Initialize(Animation animation, Animatable control, Animator animator) { + + if (animation.SpeedRatio <= 0 || DoubleUtils.AboutEqual(animation.SpeedRatio, 0)) + throw new InvalidOperationException("Speed ratio cannot be negative or zero."); + + if (animation.Duration.TotalSeconds <= 0 || DoubleUtils.AboutEqual(animation.Duration.TotalSeconds, 0)) + throw new InvalidOperationException("Animation duration cannot be negative or zero."); + parent = animator; - targetAnimation = animation; + EaseFunc = animation.Easing; targetControl = control; neutralValue = (T)targetControl.GetValue(parent.Property); - delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks); - durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks); + speedRatio = animation.SpeedRatio; + delayFC = (long)((animation.Delay.Ticks / Timing.FrameTick.Ticks) * speedRatio); + durationFC = (long)((animation.Duration.Ticks / Timing.FrameTick.Ticks) * speedRatio); switch (animation.RepeatCount.RepeatType) { + case RepeatType.None: + repeatCount = 1; + break; case RepeatType.Loop: isLooping = true; - checkLoopAndRepeat = true; break; case RepeatType.Repeat: isRepeating = true; - checkLoopAndRepeat = true; - repeatCount = animation.RepeatCount.Value; + repeatCount = (long)animation.RepeatCount.Value; break; } - isReversed = (animation.PlaybackDirection & PlaybackDirection.Reverse) != 0; - animationDirection = targetAnimation.PlaybackDirection; - fillMode = targetAnimation.FillMode; + animationDirection = animation.PlaybackDirection; + fillMode = animation.FillMode; - if (durationTotalFrameCount > 0) - currentState = KeyFramesStates.DoDelay; - else - currentState = KeyFramesStates.DoRun; } - public void Step(PlayState playState, Func Interpolator) + public void Step(PlayState playState, long frameTick, Func Interpolator) { try { - InternalStep(playState, Interpolator); + InternalStep(playState, frameTick, Interpolator); } catch (Exception e) { targetObserver?.OnError(e); } } + + private void DoComplete() + { + if (fillMode == FillMode.Forward || fillMode == FillMode.Both) + targetControl.SetValue(parent.Property, lastInterpValue, BindingPriority.LocalValue); + + targetObserver.OnCompleted(); + } - private void InternalStep(PlayState playState, Func Interpolator) + private void DoDelay() + { + if (fillMode == FillMode.Backward || fillMode == FillMode.Both) + if (currentIteration == 0) + targetObserver.OnNext(firstKFValue); + else + targetObserver.OnNext(lastInterpValue); + } + + private void InternalStep(PlayState playState, long frameTick, Func Interpolator) { if (!gotFirstKFValue) { @@ -104,13 +111,19 @@ namespace Avalonia.Animation gotFirstKFValue = true; } - if (currentState == KeyFramesStates.Disposed) + if (!gotFirstFrameCount) + { + firstFrameCount = frameTick; + gotFirstFrameCount = true; + } + + if (isDisposed) throw new InvalidProgramException("This KeyFrames Animation is already disposed."); if (playState == PlayState.Stop) - currentState = KeyFramesStates.Stop; + DoComplete(); - // // Save state and pause the machine + // Save state and pause the machine // if (playState == PlayState.Pause && currentState != KeyFramesStates.Pause) // { // savedState = currentState; @@ -121,135 +134,58 @@ namespace Avalonia.Animation // if (playState != PlayState.Pause && currentState == KeyFramesStates.Pause) // currentState = savedState; - double tempDuration = 0d, easedTime; + // get the time with the initial fc as point of origin. + var t = (frameTick - firstFrameCount); - bool handled = false; + // check if t is within the zeroth iteration + if (t <= (delayFC + durationFC)) + { + currentIteration = 0; + t = t % (delayFC + durationFC); + } + else + { + var totalDur = (double)((delayBetweenIterations ? delayFC : 0) + durationFC + 1); + currentIteration = (long)Math.Floor((double)t / totalDur); + t = t % (long)totalDur; + } + + // check if it's over the repeat count + if (currentIteration > ((long)repeatCount - 1) && !isLooping) + { + DoComplete(); + } + + // check if the current iteration should be reversed or not. + bool isCurIterReverse = animationDirection == PlaybackDirection.Normal ? false : + animationDirection == PlaybackDirection.Alternate ? (currentIteration % 2 == 0) ? false : true : + animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : + animationDirection == PlaybackDirection.Reverse ? true : false; - while (!handled) + long x1 = delayFC; + long x2 = x1 + durationFC; + + if (delayFC > 0 & t >= 0 & t <= x1) + { + if (currentIteration == 0 && delayBetweenIterations) + DoDelay(); + + } + else if (t >= x1 & t <= x2) + { + var interpVal = t / (double)durationFC; + + if (isCurIterReverse) + interpVal = 1 - interpVal; + + var easedTime = EaseFunc.Ease(interpVal); + + lastInterpValue = Interpolator(easedTime, neutralValue); + targetObserver.OnNext(lastInterpValue); + } + else if (t > x2 & (currentIteration + 1 > repeatCount & !isLooping)) { - switch (currentState) - { - case KeyFramesStates.DoDelay: - - if (fillMode == FillMode.Backward - || fillMode == FillMode.Both) - { - if (currentIteration == 0) - { - targetObserver.OnNext(firstKFValue); - } - else - { - targetObserver.OnNext(lastInterpValue); - } - } - - if (delayFrameCount > delayTotalFrameCount) - { - currentState = KeyFramesStates.DoRun; - } - else - { - handled = true; - delayFrameCount++; - } - break; - - case KeyFramesStates.DoRun: - - if (isReversed) - currentState = KeyFramesStates.RunBackwards; - else - currentState = KeyFramesStates.RunForwards; - - break; - - case KeyFramesStates.RunForwards: - - if (durationFrameCount > durationTotalFrameCount) - { - currentState = KeyFramesStates.RunComplete; - } - else - { - tempDuration = (double)durationFrameCount / durationTotalFrameCount; - currentState = KeyFramesStates.RunApplyValue; - - } - break; - - case KeyFramesStates.RunBackwards: - - if (durationFrameCount > durationTotalFrameCount) - { - currentState = KeyFramesStates.RunComplete; - } - else - { - tempDuration = (double)(durationTotalFrameCount - durationFrameCount) / durationTotalFrameCount; - currentState = KeyFramesStates.RunApplyValue; - } - break; - - case KeyFramesStates.RunApplyValue: - - easedTime = targetAnimation.Easing.Ease(tempDuration); - - durationFrameCount++; - lastInterpValue = Interpolator(easedTime, neutralValue); - targetObserver.OnNext(lastInterpValue); - currentState = KeyFramesStates.DoRun; - handled = true; - break; - - case KeyFramesStates.RunComplete: - - if (checkLoopAndRepeat) - { - delayFrameCount = 0; - durationFrameCount = 0; - - if (isLooping) - { - currentState = KeyFramesStates.DoRun; - } - else if (isRepeating) - { - if (currentIteration >= repeatCount) - { - currentState = KeyFramesStates.Stop; - } - else - { - currentState = KeyFramesStates.DoRun; - } - currentIteration++; - } - - if (animationDirection == PlaybackDirection.Alternate - || animationDirection == PlaybackDirection.AlternateReverse) - isReversed = !isReversed; - - break; - } - - currentState = KeyFramesStates.Stop; - break; - - case KeyFramesStates.Stop: - - if (fillMode == FillMode.Forward - || fillMode == FillMode.Both) - { - targetControl.SetValue(parent.Property, lastInterpValue, BindingPriority.LocalValue); - } - targetObserver.OnCompleted(); - handled = true; - break; - default: - handled = true; - break; - } + DoComplete(); } } @@ -262,7 +198,7 @@ namespace Avalonia.Animation public void Dispose() { unsubscribe = true; - currentState = KeyFramesStates.Disposed; + isDisposed = true; } } -} +} \ No newline at end of file diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index 654ee327f8..cdf15c4c2f 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -104,7 +104,7 @@ namespace Avalonia.Animation Timing.AnimationStateTimer .TakeWhile(_ => !stateMachine.unsubscribe) - .Subscribe(p => stateMachine.Step(p, DoInterpolation)); + .Subscribe(p => stateMachine.Step(p.Item1, p.Item2, DoInterpolation)); return control.Bind((AvaloniaProperty)Property, stateMachine, BindingPriority.Animation); } From 0b248e6bb27509584eebf2b69899282882314d1d Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 2 Aug 2018 02:18:55 +0800 Subject: [PATCH 010/135] Optimize & remove excess numeric castings. --- .../AnimatorStateMachine`1.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 6854645cc2..21a9fe7a50 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -13,10 +13,10 @@ namespace Avalonia.Animation T lastInterpValue; T firstKFValue; - private long delayFC; - private long durationFC; + private double delayFC; + private double durationFC; private long repeatCount; - private long currentIteration; + private double currentIteration; private long firstFrameCount; private bool isLooping; @@ -52,8 +52,9 @@ namespace Avalonia.Animation neutralValue = (T)targetControl.GetValue(parent.Property); speedRatio = animation.SpeedRatio; - delayFC = (long)((animation.Delay.Ticks / Timing.FrameTick.Ticks) * speedRatio); - durationFC = (long)((animation.Duration.Ticks / Timing.FrameTick.Ticks) * speedRatio); + delayFC = ((animation.Delay.Ticks / Timing.FrameTick.Ticks) * speedRatio); + durationFC = ((animation.Duration.Ticks / Timing.FrameTick.Ticks) * speedRatio); + delayBetweenIterations = animation.DelayBetweenRepeats; switch (animation.RepeatCount.RepeatType) { @@ -85,7 +86,7 @@ namespace Avalonia.Animation targetObserver?.OnError(e); } } - + private void DoComplete() { if (fillMode == FillMode.Forward || fillMode == FillMode.Both) @@ -135,7 +136,7 @@ namespace Avalonia.Animation // currentState = savedState; // get the time with the initial fc as point of origin. - var t = (frameTick - firstFrameCount); + double t = (frameTick - firstFrameCount); // check if t is within the zeroth iteration if (t <= (delayFC + durationFC)) @@ -146,12 +147,12 @@ namespace Avalonia.Animation else { var totalDur = (double)((delayBetweenIterations ? delayFC : 0) + durationFC + 1); - currentIteration = (long)Math.Floor((double)t / totalDur); - t = t % (long)totalDur; + currentIteration = Math.Floor(t / totalDur); + t = t % totalDur; } // check if it's over the repeat count - if (currentIteration > ((long)repeatCount - 1) && !isLooping) + if (currentIteration > (repeatCount - 1) && !isLooping) { DoComplete(); } @@ -162,8 +163,8 @@ namespace Avalonia.Animation animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : animationDirection == PlaybackDirection.Reverse ? true : false; - long x1 = delayFC; - long x2 = x1 + durationFC; + double x1 = delayFC; + double x2 = x1 + durationFC; if (delayFC > 0 & t >= 0 & t <= x1) { @@ -173,7 +174,7 @@ namespace Avalonia.Animation } else if (t >= x1 & t <= x2) { - var interpVal = t / (double)durationFC; + var interpVal = t / durationFC; if (isCurIterReverse) interpVal = 1 - interpVal; From cfabbe9b65b9514c5bf3a4a27701f2af37d32e2b Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 3 Aug 2018 12:46:07 +0800 Subject: [PATCH 011/135] More optimizations. --- .../AnimatorStateMachine`1.cs | 12 +++++------ src/Avalonia.Animation/Timing.cs | 21 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 21a9fe7a50..8bfdf8b04a 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -163,16 +163,16 @@ namespace Avalonia.Animation animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : animationDirection == PlaybackDirection.Reverse ? true : false; - double x1 = delayFC; - double x2 = x1 + durationFC; + double delayEndpoint = delayFC; + double iterationEndpoint = delayEndpoint + durationFC; - if (delayFC > 0 & t >= 0 & t <= x1) + if (delayFC > 0 & t >= 0 & t <= delayEndpoint) { - if (currentIteration == 0 && delayBetweenIterations) + if (currentIteration == 0 || delayBetweenIterations) DoDelay(); } - else if (t >= x1 & t <= x2) + else if (t >= delayEndpoint & t <= iterationEndpoint) { var interpVal = t / durationFC; @@ -184,7 +184,7 @@ namespace Avalonia.Animation lastInterpValue = Interpolator(easedTime, neutralValue); targetObserver.OnNext(lastInterpValue); } - else if (t > x2 & (currentIteration + 1 > repeatCount & !isLooping)) + else if (t > iterationEndpoint & (currentIteration + 1 > repeatCount & !isLooping)) { DoComplete(); } diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index 3eddc0ac26..fb61e15f05 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -15,15 +15,17 @@ namespace Avalonia.Animation /// public static class Timing { - static ulong _transitionsFrameCount; static long _tickStartTimeStamp; static PlayState _globalState = PlayState.Run; + static long TicksPerFrame = Stopwatch.Frequency / FramesPerSecond; + /// /// The number of frames per second. /// public const int FramesPerSecond = 60; + /// /// The time span of each frame. /// @@ -39,17 +41,18 @@ namespace Avalonia.Animation var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); + AnimationStateTimer = globalTimer .Select(_ => { return (_globalState, (Stopwatch.GetTimestamp() - _tickStartTimeStamp) - / (Stopwatch.Frequency / FramesPerSecond)); + / TicksPerFrame); }) .Publish() .RefCount(); TransitionsTimer = globalTimer - .Select(p => _transitionsFrameCount++) + .Select(p => p) .Publish() .RefCount(); } @@ -95,7 +98,7 @@ namespace Avalonia.Animation /// The parameter passed to a subsciber is the number of frames since the animation system was /// initialized. /// - public static IObservable TransitionsTimer + public static IObservable TransitionsTimer { get; } @@ -113,16 +116,14 @@ namespace Avalonia.Animation /// public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan)) { - var startTime = _transitionsFrameCount; - var _duration = (ulong)(duration.Ticks / FrameTick.Ticks); - var endTime = startTime + _duration; + var _duration = (duration.Ticks / FrameTick.Ticks); + var endTime = _duration; return TransitionsTimer .TakeWhile(x => x < endTime) - .Select(x => (double)(x - startTime) / _duration) + .Select(x => (double)x / _duration) .StartWith(0.0) .Concat(Observable.Return(1.0)); } - } -} +} \ No newline at end of file From 1757604dab071f6d09b4e1b63a1eddad1c708f72 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 3 Aug 2018 12:47:01 +0800 Subject: [PATCH 012/135] Fix onComplete assignment. --- src/Avalonia.Animation/AnimatorStateMachine`1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 32acafcfc7..18ad76c51e 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -73,7 +73,7 @@ namespace Avalonia.Animation animationDirection = animation.PlaybackDirection; fillMode = animation.FillMode; - onComplete = onComplete; + this.onComplete = onComplete; } public void Step(PlayState playState, long frameTick, Func Interpolator) From b020bddb20bae6f4ca299cff7858ee9d60af7e95 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 3 Aug 2018 12:47:01 +0800 Subject: [PATCH 013/135] Fix onComplete assignment. --- src/Avalonia.Animation/Animation.cs | 7 +------ src/Avalonia.Animation/AnimatorStateMachine`1.cs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 46179aa3f4..344b63d15c 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -85,12 +85,7 @@ namespace Avalonia.Animation /// Sets the behavior for having a delay between repeats for this animation. /// public bool DelayBetweenRepeats { get; set; } - - public Animation() - { - this.CollectionChanged += delegate { _isChildrenChanged = true; }; - } - + private (IList Animators, IList subscriptions) InterpretKeyframes(Animatable control) { var handlerList = new List<(Type type, AvaloniaProperty property)>(); diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 32acafcfc7..18ad76c51e 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -73,7 +73,7 @@ namespace Avalonia.Animation animationDirection = animation.PlaybackDirection; fillMode = animation.FillMode; - onComplete = onComplete; + this.onComplete = onComplete; } public void Step(PlayState playState, long frameTick, Func Interpolator) From 1b37397e69b97a1f9a7274a8ceb5644129475ae0 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 3 Aug 2018 17:38:41 +0800 Subject: [PATCH 014/135] Fix bug on PageTransitions by adding 1 to the zeroth iteration frame count modulo. --- src/Avalonia.Animation/AnimatorStateMachine`1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 18ad76c51e..38ccce6cb5 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -145,7 +145,7 @@ namespace Avalonia.Animation if (t <= (delayFC + durationFC)) { currentIteration = 0; - t = t % (delayFC + durationFC); + t = t % (delayFC + durationFC + 1); } else { From bfefeb9e9c63830e3cd17311be7f3c5ef7b6bb7c Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 3 Aug 2018 17:39:19 +0800 Subject: [PATCH 015/135] Make Transitions Bind strongly typed. --- src/Avalonia.Animation/Transition`1.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Animation/Transition`1.cs b/src/Avalonia.Animation/Transition`1.cs index 5db3082deb..c097b930a5 100644 --- a/src/Avalonia.Animation/Transition`1.cs +++ b/src/Avalonia.Animation/Transition`1.cs @@ -24,17 +24,7 @@ namespace Avalonia.Animation /// /// Gets the easing class to be used. /// - public Easing Easing - { - get - { - return _easing ?? (_easing = new LinearEasing()); - } - set - { - _easing = value; - } - } + public Easing Easing { get; set; } = new LinearEasing(); /// public AvaloniaProperty Property @@ -61,8 +51,8 @@ namespace Avalonia.Animation /// public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) { - var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); - return control.Bind(Property, transition, Data.BindingPriority.Animation); + var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue); + return control.Bind((AvaloniaProperty)Property, transition, Data.BindingPriority.Animation); } } } \ No newline at end of file From 714606b2ad8bd0d8adb3acc4d30701a8f195d2f7 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sun, 12 Aug 2018 01:26:03 +0800 Subject: [PATCH 016/135] Add PlayState support. Redoing the main algorithm yet again. --- .../ViewModels/AnimationsPageViewModel.cs | 10 +- src/Avalonia.Animation/Animatable.cs | 21 +---- src/Avalonia.Animation/Animation.cs | 87 ++++++++++-------- .../AnimatorStateMachine`1.cs | 92 ++++++++++--------- src/Avalonia.Animation/Animator`1.cs | 21 ++--- src/Avalonia.Animation/Timing.cs | 59 +++--------- 6 files changed, 127 insertions(+), 163 deletions(-) diff --git a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs index 626a3e7c77..c76d4db513 100644 --- a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs @@ -10,21 +10,21 @@ namespace RenderDemo.ViewModels public AnimationsPageViewModel() { - ToggleGlobalPlayState = ReactiveCommand.Create(()=>TogglePlayState()); + ToggleGlobalPlayState = ReactiveCommand.Create(() => TogglePlayState()); } void TogglePlayState() { - switch (Timing.GetGlobalPlayState()) + switch (Timing.GlobalPlayState) { case PlayState.Run: PlayStateText = "Resume all animations"; - Timing.SetGlobalPlayState(PlayState.Pause); + Timing.GlobalPlayState = PlayState.Pause; break; case PlayState.Pause: PlayStateText = "Pause all animations"; - Timing.SetGlobalPlayState(PlayState.Run); + Timing.GlobalPlayState = PlayState.Run; break; } } @@ -36,5 +36,5 @@ namespace RenderDemo.ViewModels } public ReactiveCommand ToggleGlobalPlayState { get; } - } + } } diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 85317af1a8..303e01aed8 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -23,25 +23,8 @@ namespace Avalonia.Animation /// public Animatable() { - Transitions = new Transitions(); - AnimatableTimer = Timing.AnimationStateTimer - .Select(p => - { - if (this._playState == PlayState.Pause) - { - return (PlayState.Pause, p.Item2); - } - else return p; - }) - .Publish() - .RefCount(); - } - - /// - /// The specific animations timer for this control. - /// - /// - public IObservable<(PlayState, long)> AnimatableTimer; + Transitions = new Transitions(); + } /// /// Defines the property. diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 344b63d15c..da2fc75c0b 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -21,70 +21,81 @@ namespace Avalonia.Animation /// public class Animation : AvaloniaList, IAnimation { - private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> - { - ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) - }; - - public static void RegisterAnimator(Func condition) - where TAnimator : IAnimator - { - Animators.Insert(0, (condition, typeof(TAnimator))); - } - - private static Type GetAnimatorType(AvaloniaProperty property) - { - foreach (var (condition, type) in Animators) - { - if (condition(property)) - { - return type; - } - } - return null; - } public AvaloniaList _animators { get; set; } = new AvaloniaList(); /// - /// Run time of this animation. + /// Gets or sets the active time of this animation. /// public TimeSpan Duration { get; set; } /// - /// Delay time for this animation. - /// - public TimeSpan Delay { get; set; } - - /// - /// The repeat count for this animation. + /// Gets or sets the repeat count for this animation. /// public RepeatCount RepeatCount { get; set; } /// - /// The playback direction for this animation. + /// Gets or sets the playback direction for this animation. /// public PlaybackDirection PlaybackDirection { get; set; } /// - /// The value fill mode for this animation. + /// Gets or sets the value fill mode for this animation. /// public FillMode FillMode { get; set; } /// - /// Easing function to be used. + /// Gets or sets the easing function to be used for this animation. /// public Easing Easing { get; set; } = new LinearEasing(); - + /// - /// Sets the speed multiple for this animation. + /// Gets or sets the speed multiple for this animation. /// public double SpeedRatio { get; set; } = 1d; - /// - /// Sets the behavior for having a delay between repeats for this animation. - /// - public bool DelayBetweenRepeats { get; set; } + /// + /// Gets or sets the delay time for this animation. + /// + /// + /// Describes a delay to be added before the animation starts, and optionally between + /// repeats of the animation if is set. + /// + public TimeSpan Delay { get; set; } + + /// + /// Gets or sets a value indicating whether will be applied between + /// iterations of the animation. + /// + /// + /// If this property is not set, then will only be applied to the first + /// iteration of the animation. + /// + public bool DelayBetweenIterations { get; set; } + + + private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> + { + ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) + }; + + public static void RegisterAnimator(Func condition) + where TAnimator : IAnimator + { + Animators.Insert(0, (condition, typeof(TAnimator))); + } + + private static Type GetAnimatorType(AvaloniaProperty property) + { + foreach (var (condition, type) in Animators) + { + if (condition(property)) + { + return type; + } + } + return null; + } private (IList Animators, IList subscriptions) InterpretKeyframes(Animatable control) { diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 38ccce6cb5..47bfa1c321 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -9,7 +9,7 @@ namespace Avalonia.Animation /// Provides statefulness for an iteration of a keyframe animation. /// internal class AnimatorStateMachine : IObservable, IDisposable - { + { T lastInterpValue; T firstKFValue; @@ -34,6 +34,11 @@ namespace Avalonia.Animation internal bool unsubscribe; private bool isDisposed; + private long? internalClock; + + private long? previousClock = null; + private long currentDiscreteTime; + private Easings.Easing EaseFunc; private IObserver targetObserver; private readonly Action onComplete; @@ -55,7 +60,7 @@ namespace Avalonia.Animation speedRatio = animation.SpeedRatio; delayFC = ((animation.Delay.Ticks / Timing.FrameTick.Ticks) * speedRatio); durationFC = ((animation.Duration.Ticks / Timing.FrameTick.Ticks) * speedRatio); - delayBetweenIterations = animation.DelayBetweenRepeats; + delayBetweenIterations = animation.DelayBetweenIterations; switch (animation.RepeatCount.RepeatType) { @@ -72,15 +77,15 @@ namespace Avalonia.Animation } animationDirection = animation.PlaybackDirection; - fillMode = animation.FillMode; + fillMode = animation.FillMode; this.onComplete = onComplete; } - public void Step(PlayState playState, long frameTick, Func Interpolator) + public void Step(long frameTick, Func Interpolator) { try { - InternalStep(playState, frameTick, Interpolator); + InternalStep(frameTick, Interpolator); } catch (Exception e) { @@ -107,8 +112,31 @@ namespace Avalonia.Animation targetObserver.OnNext(lastInterpValue); } - private void InternalStep(PlayState playState, long frameTick, Func Interpolator) + private void InternalStep(long time, Func Interpolator) { + if (Timing.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop) + DoComplete(); + + if (!previousClock.HasValue) + { + previousClock = time; + internalClock = 0; + } + else + { + if (Timing.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause) + { + previousClock = time; + return; + } + var delta = time - previousClock; + internalClock += delta; + previousClock = time; + } + + // currentDiscreteTime = internalClock.Value; + currentDiscreteTime++; + if (!gotFirstKFValue) { firstKFValue = (T)parent.First().Value; @@ -117,42 +145,23 @@ namespace Avalonia.Animation if (!gotFirstFrameCount) { - firstFrameCount = frameTick; + firstFrameCount = currentDiscreteTime; gotFirstFrameCount = true; } if (isDisposed) throw new InvalidProgramException("This KeyFrames Animation is already disposed."); - if (playState == PlayState.Stop) - DoComplete(); - - // Save state and pause the machine - // if (playState == PlayState.Pause && currentState != KeyFramesStates.Pause) - // { - // savedState = currentState; - // currentState = KeyFramesStates.Pause; - // } - - // // Resume the previous state - // if (playState != PlayState.Pause && currentState == KeyFramesStates.Pause) - // currentState = savedState; - // get the time with the initial fc as point of origin. - double t = (frameTick - firstFrameCount); + double t = (currentDiscreteTime - firstFrameCount); // check if t is within the zeroth iteration - if (t <= (delayFC + durationFC)) - { - currentIteration = 0; - t = t % (delayFC + durationFC + 1); - } - else - { - var totalDur = (double)((delayBetweenIterations ? delayFC : 0) + durationFC + 1); - currentIteration = Math.Floor(t / totalDur); - t = t % totalDur; - } + + double delayEndpoint = delayFC; + double iterationEndpoint = delayEndpoint + durationFC; + + currentIteration = Math.Floor(t / iterationEndpoint); + t = t % iterationEndpoint; // check if it's over the repeat count if (currentIteration > (repeatCount - 1) && !isLooping) @@ -166,19 +175,17 @@ namespace Avalonia.Animation animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : animationDirection == PlaybackDirection.Reverse ? true : false; - double delayEndpoint = delayFC; - double iterationEndpoint = delayEndpoint + durationFC; - if (delayFC > 0 & t >= 0 & t <= delayEndpoint) + if (delayFC > 0 & t <= delayEndpoint) { - if (currentIteration == 0 || delayBetweenIterations) + if (currentIteration == 0) DoDelay(); - } - else if (t >= delayEndpoint & t <= iterationEndpoint) + else if (t > delayEndpoint & t < iterationEndpoint) { - var interpVal = t / durationFC; - + double k = t - delayFC; + var interpVal = k / (double)durationFC; + if (isCurIterReverse) interpVal = 1 - interpVal; @@ -187,7 +194,7 @@ namespace Avalonia.Animation lastInterpValue = Interpolator(easedTime, neutralValue); targetObserver.OnNext(lastInterpValue); } - else if (t > iterationEndpoint & (currentIteration + 1 > repeatCount & !isLooping)) + else if (t > iterationEndpoint && !isLooping) { DoComplete(); } @@ -198,7 +205,6 @@ namespace Avalonia.Animation targetObserver = observer; return this; } - public void Dispose() { unsubscribe = true; diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index 607ccee947..a8b5ce7a27 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -21,7 +21,7 @@ namespace Avalonia.Animation /// private readonly SortedList _convertedKeyframes = new SortedList(); - private bool _isVerfifiedAndConverted; + private bool isVerfifiedAndConverted; /// /// Gets or sets the target property for the keyframe. @@ -31,18 +31,17 @@ namespace Avalonia.Animation public Animator() { // Invalidate keyframes when changed. - this.CollectionChanged += delegate { _isVerfifiedAndConverted = false; }; + this.CollectionChanged += delegate { isVerfifiedAndConverted = false; }; } /// - public virtual IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch, Action onComplete) + public virtual IDisposable Apply(Animation animation, Animatable control, IObservable Match, Action onComplete) { - if (!_isVerfifiedAndConverted) + if (!isVerfifiedAndConverted) VerifyConvertKeyFrames(); - return obsMatch - // Ignore triggers when global timers are paused. - .Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause) + return Match + .Where(p => p) .Subscribe(_ => { var timerObs = RunKeyFrames(animation, control, onComplete); @@ -101,9 +100,9 @@ namespace Avalonia.Animation { var stateMachine = new AnimatorStateMachine(animation, control, this, onComplete); - Timing.AnimationStateTimer + Timing.AnimationsTimer .TakeWhile(_ => !stateMachine.unsubscribe) - .Subscribe(p => stateMachine.Step(p.Item1, p.Item2, DoInterpolation)); + .Subscribe(p => stateMachine.Step(p, DoInterpolation)); return control.Bind((AvaloniaProperty)Property, stateMachine, BindingPriority.Animation); } @@ -124,7 +123,7 @@ namespace Avalonia.Animation } AddNeutralKeyFramesIfNeeded(); - _isVerfifiedAndConverted = true; + isVerfifiedAndConverted = true; } @@ -133,7 +132,7 @@ namespace Avalonia.Animation bool hasStartKey, hasEndKey; hasStartKey = hasEndKey = false; - // Make start and end keyframe mandatory. + // Check if there's start and end keyframes. foreach (var converted in _convertedKeyframes.Keys) { if (DoubleUtils.AboutEqual(converted, 0.0)) diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index fb61e15f05..c6def06b15 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -16,16 +16,18 @@ namespace Avalonia.Animation public static class Timing { static long _tickStartTimeStamp; - static PlayState _globalState = PlayState.Run; static long TicksPerFrame = Stopwatch.Frequency / FramesPerSecond; + /// + /// Gets or sets the animation play state for all animations + /// + public static PlayState GlobalPlayState { get; set; } = PlayState.Run; /// /// The number of frames per second. /// public const int FramesPerSecond = 60; - /// /// The time span of each frame. /// @@ -36,45 +38,20 @@ namespace Avalonia.Animation /// static Timing() { - _tickStartTimeStamp = Stopwatch.GetTimestamp(); var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); - - AnimationStateTimer = globalTimer + AnimationsTimer = globalTimer .Select(_ => { - return (_globalState, (Stopwatch.GetTimestamp() - _tickStartTimeStamp) - / TicksPerFrame); + return (Stopwatch.GetTimestamp() - _tickStartTimeStamp) + / TicksPerFrame * 2; }) .Publish() .RefCount(); - - TransitionsTimer = globalTimer - .Select(p => p) - .Publish() - .RefCount(); - } - - - /// - /// Sets the animation play state for all animations - /// - public static void SetGlobalPlayState(PlayState playState) - { - Dispatcher.UIThread.VerifyAccess(); - _globalState = playState; } - /// - /// Gets the animation play state for all animations - /// - public static PlayState GetGlobalPlayState() - { - Dispatcher.UIThread.VerifyAccess(); - return _globalState; - } /// /// Gets the animation timer. @@ -84,21 +61,7 @@ namespace Avalonia.Animation /// defined in . /// The parameter passed to a subsciber is the current playstate of the animation. /// - internal static IObservable<(PlayState, long)> AnimationStateTimer - { - get; - } - - /// - /// Gets the transitions timer. - /// - /// - /// The transitions timer increments usually 60 times per second as - /// defined in . - /// The parameter passed to a subsciber is the number of frames since the animation system was - /// initialized. - /// - public static IObservable TransitionsTimer + internal static IObservable AnimationsTimer { get; } @@ -116,10 +79,12 @@ namespace Avalonia.Animation /// public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan)) { + // TODO: Fix this mess. var _duration = (duration.Ticks / FrameTick.Ticks); - var endTime = _duration; + long? endTime = ((Stopwatch.GetTimestamp() - _tickStartTimeStamp) + / TicksPerFrame) + _duration; - return TransitionsTimer + return AnimationsTimer .TakeWhile(x => x < endTime) .Select(x => (double)x / _duration) .StartWith(0.0) From dc4c7cf4ca66e3b59580fc09e51cb40f57130e29 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sun, 12 Aug 2018 13:32:55 +0800 Subject: [PATCH 017/135] Make the algorithm use TimeSpans directly instead of converting to quantized framecounts to avoid/reduce quantization errors in interpolation. --- .../AnimatorStateMachine`1.cs | 81 +++++++++---------- src/Avalonia.Animation/Timing.cs | 30 +++---- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index 47bfa1c321..e5a6864899 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -13,11 +13,8 @@ namespace Avalonia.Animation T lastInterpValue; T firstKFValue; - private double delayFC; - private double durationFC; private long repeatCount; private double currentIteration; - private long firstFrameCount; private bool isLooping; private bool isRepeating; @@ -34,10 +31,11 @@ namespace Avalonia.Animation internal bool unsubscribe; private bool isDisposed; - private long? internalClock; - - private long? previousClock = null; - private long currentDiscreteTime; + private TimeSpan delayFC; + private TimeSpan durationFC; + private TimeSpan firstFrameCount; + private TimeSpan internalClock; + private TimeSpan? previousClock; private Easings.Easing EaseFunc; private IObserver targetObserver; @@ -58,8 +56,10 @@ namespace Avalonia.Animation neutralValue = (T)targetControl.GetValue(parent.Property); speedRatio = animation.SpeedRatio; - delayFC = ((animation.Delay.Ticks / Timing.FrameTick.Ticks) * speedRatio); - durationFC = ((animation.Duration.Ticks / Timing.FrameTick.Ticks) * speedRatio); + + delayFC = animation.Delay; + durationFC = animation.Duration; + delayBetweenIterations = animation.DelayBetweenIterations; switch (animation.RepeatCount.RepeatType) @@ -81,7 +81,7 @@ namespace Avalonia.Animation this.onComplete = onComplete; } - public void Step(long frameTick, Func Interpolator) + public void Step(TimeSpan frameTick, Func Interpolator) { try { @@ -112,31 +112,28 @@ namespace Avalonia.Animation targetObserver.OnNext(lastInterpValue); } - private void InternalStep(long time, Func Interpolator) + private void DoPlayStatesAndTime(TimeSpan systemTime) { if (Timing.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop) DoComplete(); if (!previousClock.HasValue) { - previousClock = time; - internalClock = 0; + previousClock = systemTime; + internalClock = TimeSpan.Zero; } else { if (Timing.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause) { - previousClock = time; + previousClock = systemTime; return; } - var delta = time - previousClock; - internalClock += delta; - previousClock = time; + var delta = systemTime - previousClock; + internalClock += delta.Value; + previousClock = systemTime; } - // currentDiscreteTime = internalClock.Value; - currentDiscreteTime++; - if (!gotFirstKFValue) { firstKFValue = (T)parent.First().Value; @@ -145,47 +142,47 @@ namespace Avalonia.Animation if (!gotFirstFrameCount) { - firstFrameCount = currentDiscreteTime; + firstFrameCount = internalClock; gotFirstFrameCount = true; } + } + + private void InternalStep(TimeSpan systemTime, Func Interpolator) + { + DoPlayStatesAndTime(systemTime); if (isDisposed) throw new InvalidProgramException("This KeyFrames Animation is already disposed."); - // get the time with the initial fc as point of origin. - double t = (currentDiscreteTime - firstFrameCount); - - // check if t is within the zeroth iteration + var t = internalClock - firstFrameCount; - double delayEndpoint = delayFC; - double iterationEndpoint = delayEndpoint + durationFC; + var delayEndpoint = delayFC; + var iterationEndpoint = delayEndpoint + durationFC; - currentIteration = Math.Floor(t / iterationEndpoint); - t = t % iterationEndpoint; + currentIteration = (int)Math.Floor((double)t.Ticks / iterationEndpoint.Ticks); + t = TimeSpan.FromTicks(t.Ticks % iterationEndpoint.Ticks); - // check if it's over the repeat count if (currentIteration > (repeatCount - 1) && !isLooping) - { DoComplete(); - } - // check if the current iteration should be reversed or not. + if (t > iterationEndpoint & !isLooping) + DoComplete(); + bool isCurIterReverse = animationDirection == PlaybackDirection.Normal ? false : animationDirection == PlaybackDirection.Alternate ? (currentIteration % 2 == 0) ? false : true : animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : animationDirection == PlaybackDirection.Reverse ? true : false; - - if (delayFC > 0 & t <= delayEndpoint) + if (delayFC > TimeSpan.Zero & t < delayEndpoint) { if (currentIteration == 0) DoDelay(); } - else if (t > delayEndpoint & t < iterationEndpoint) + else if (t >= delayEndpoint & t <= iterationEndpoint) { - double k = t - delayFC; - var interpVal = k / (double)durationFC; - + var k = t - delayFC; + var interpVal = (double)k.Ticks / durationFC.Ticks; + if (isCurIterReverse) interpVal = 1 - interpVal; @@ -194,10 +191,7 @@ namespace Avalonia.Animation lastInterpValue = Interpolator(easedTime, neutralValue); targetObserver.OnNext(lastInterpValue); } - else if (t > iterationEndpoint && !isLooping) - { - DoComplete(); - } + } public IDisposable Subscribe(IObserver observer) @@ -205,6 +199,7 @@ namespace Avalonia.Animation targetObserver = observer; return this; } + public void Dispose() { unsubscribe = true; diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index c6def06b15..575cedc620 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -15,9 +15,6 @@ namespace Avalonia.Animation /// public static class Timing { - static long _tickStartTimeStamp; - static long TicksPerFrame = Stopwatch.Frequency / FramesPerSecond; - /// /// Gets or sets the animation play state for all animations /// @@ -37,22 +34,18 @@ namespace Avalonia.Animation /// Initializes static members of the class. /// static Timing() - { - _tickStartTimeStamp = Stopwatch.GetTimestamp(); - + { var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); AnimationsTimer = globalTimer .Select(_ => { - return (Stopwatch.GetTimestamp() - _tickStartTimeStamp) - / TicksPerFrame * 2; + return TimeSpan.FromMilliseconds(Environment.TickCount); }) .Publish() .RefCount(); } - /// /// Gets the animation timer. /// @@ -61,7 +54,7 @@ namespace Avalonia.Animation /// defined in . /// The parameter passed to a subsciber is the current playstate of the animation. /// - internal static IObservable AnimationsTimer + internal static IObservable AnimationsTimer { get; } @@ -80,15 +73,16 @@ namespace Avalonia.Animation public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan)) { // TODO: Fix this mess. - var _duration = (duration.Ticks / FrameTick.Ticks); - long? endTime = ((Stopwatch.GetTimestamp() - _tickStartTimeStamp) - / TicksPerFrame) + _duration; + // var _duration = (duration.Ticks / FrameTick.Ticks); + // long? endTime = ((Stopwatch.GetTimestamp() - _tickStartTimeStamp) + // / TicksPerFrame) + _duration; - return AnimationsTimer - .TakeWhile(x => x < endTime) - .Select(x => (double)x / _duration) - .StartWith(0.0) - .Concat(Observable.Return(1.0)); + // return AnimationsTimer + // .TakeWhile(x => x < endTime) + // .Select(x => (double)x / _duration) + // .StartWith(0.0) + // .Concat(Observable.Return(1.0)); + return Observable.Empty(); } } } \ No newline at end of file From b6e6b7db48c8c279b74d8b0b8a20d1dc1316970d Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 14 Aug 2018 15:38:55 +0800 Subject: [PATCH 018/135] Fix Delay and Iteration Delay behaviors. AnimatorStateMachine is functionally complete. --- .../AnimatorStateMachine`1.cs | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs index e5a6864899..0a785ab496 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimatorStateMachine`1.cs @@ -6,7 +6,7 @@ using Avalonia.Data; namespace Avalonia.Animation { /// - /// Provides statefulness for an iteration of a keyframe animation. + /// Provides statefulness for keyframe animations. /// internal class AnimatorStateMachine : IObservable, IDisposable { @@ -17,10 +17,9 @@ namespace Avalonia.Animation private double currentIteration; private bool isLooping; - private bool isRepeating; private bool gotFirstKFValue; private bool gotFirstFrameCount; - private bool delayBetweenIterations; + private bool iterationDelay; private FillMode fillMode; private PlaybackDirection animationDirection; @@ -31,36 +30,34 @@ namespace Avalonia.Animation internal bool unsubscribe; private bool isDisposed; - private TimeSpan delayFC; - private TimeSpan durationFC; + private TimeSpan delay; + private TimeSpan duration; private TimeSpan firstFrameCount; private TimeSpan internalClock; private TimeSpan? previousClock; - private Easings.Easing EaseFunc; + private Easings.Easing easeFunc; private IObserver targetObserver; - private readonly Action onComplete; + private readonly Action onCompleteAction; - public AnimatorStateMachine(Animation animation, Animatable control, Animator animator, Action onComplete) + public AnimatorStateMachine(Animation animation, Animatable control, Animator animator, Action OnComplete) { - if (animation.SpeedRatio <= 0 || DoubleUtils.AboutEqual(animation.SpeedRatio, 0)) throw new InvalidOperationException("Speed ratio cannot be negative or zero."); if (animation.Duration.TotalSeconds <= 0 || DoubleUtils.AboutEqual(animation.Duration.TotalSeconds, 0)) - throw new InvalidOperationException("Animation duration cannot be negative or zero."); - + throw new InvalidOperationException("Duration cannot be negative or zero."); + parent = animator; - EaseFunc = animation.Easing; + easeFunc = animation.Easing; targetControl = control; neutralValue = (T)targetControl.GetValue(parent.Property); speedRatio = animation.SpeedRatio; - delayFC = animation.Delay; - durationFC = animation.Duration; - - delayBetweenIterations = animation.DelayBetweenIterations; + delay = animation.Delay; + duration = animation.Duration; + iterationDelay = animation.DelayBetweenIterations; switch (animation.RepeatCount.RepeatType) { @@ -71,14 +68,13 @@ namespace Avalonia.Animation isLooping = true; break; case RepeatType.Repeat: - isRepeating = true; repeatCount = (long)animation.RepeatCount.Value; break; } animationDirection = animation.PlaybackDirection; fillMode = animation.FillMode; - this.onComplete = onComplete; + onCompleteAction = OnComplete; } public void Step(TimeSpan frameTick, Func Interpolator) @@ -99,7 +95,7 @@ namespace Avalonia.Animation targetControl.SetValue(parent.Property, lastInterpValue, BindingPriority.LocalValue); targetObserver.OnCompleted(); - onComplete?.Invoke(); + onCompleteAction?.Invoke(); Dispose(); } @@ -154,44 +150,74 @@ namespace Avalonia.Animation if (isDisposed) throw new InvalidProgramException("This KeyFrames Animation is already disposed."); - var t = internalClock - firstFrameCount; + var time = internalClock - firstFrameCount; + var delayEndpoint = delay; + var iterationEndpoint = delayEndpoint + duration; - var delayEndpoint = delayFC; - var iterationEndpoint = delayEndpoint + durationFC; + //determine if time is currently in the first iteration. + if (time >= TimeSpan.Zero & time <= iterationEndpoint) + { + currentIteration = 1; + } + else if (time > iterationEndpoint) + { + //Subtract first iteration to properly get the subsequent iteration time + time -= iterationEndpoint; - currentIteration = (int)Math.Floor((double)t.Ticks / iterationEndpoint.Ticks); - t = TimeSpan.FromTicks(t.Ticks % iterationEndpoint.Ticks); + if (!iterationDelay & delayEndpoint > TimeSpan.Zero) + { + delayEndpoint = TimeSpan.Zero; + iterationEndpoint = duration; + } - if (currentIteration > (repeatCount - 1) && !isLooping) - DoComplete(); + //Calculate the current iteration number + currentIteration = (int)Math.Floor((double)time.Ticks / iterationEndpoint.Ticks) + 2; + } + else + { + previousClock = systemTime; + return; + } - if (t > iterationEndpoint & !isLooping) - DoComplete(); - + time = TimeSpan.FromTicks(time.Ticks % iterationEndpoint.Ticks); + + if (!isLooping) + { + if (currentIteration > repeatCount) + DoComplete(); + + if (time > iterationEndpoint) + DoComplete(); + } + + // Determine if the current iteration should have its normalized time inverted. bool isCurIterReverse = animationDirection == PlaybackDirection.Normal ? false : animationDirection == PlaybackDirection.Alternate ? (currentIteration % 2 == 0) ? false : true : animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false : animationDirection == PlaybackDirection.Reverse ? true : false; - if (delayFC > TimeSpan.Zero & t < delayEndpoint) + if (delayEndpoint > TimeSpan.Zero & time < delayEndpoint) { - if (currentIteration == 0) - DoDelay(); + DoDelay(); } - else if (t >= delayEndpoint & t <= iterationEndpoint) + else { - var k = t - delayFC; - var interpVal = (double)k.Ticks / durationFC.Ticks; + // Offset the delay time + time -= delayEndpoint; + iterationEndpoint -= delayEndpoint; + + // Normalize time + var interpVal = (double)time.Ticks / iterationEndpoint.Ticks; if (isCurIterReverse) interpVal = 1 - interpVal; - var easedTime = EaseFunc.Ease(interpVal); - + // Ease and interpolate + var easedTime = easeFunc.Ease(interpVal); lastInterpValue = Interpolator(easedTime, neutralValue); + targetObserver.OnNext(lastInterpValue); } - } public IDisposable Subscribe(IObserver observer) @@ -199,7 +225,7 @@ namespace Avalonia.Animation targetObserver = observer; return this; } - + public void Dispose() { unsubscribe = true; From 5d49c5f969be55d34408010aed3d1d54a69ed1fc Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 14 Aug 2018 23:27:02 +0800 Subject: [PATCH 019/135] Rename AnimatorStateMachine to AnimationsEngine to properly reflect the functions of the new algorithm. --- .../{AnimatorStateMachine`1.cs => AnimationsEngine`1.cs} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename src/Avalonia.Animation/{AnimatorStateMachine`1.cs => AnimationsEngine`1.cs} (96%) diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimationsEngine`1.cs similarity index 96% rename from src/Avalonia.Animation/AnimatorStateMachine`1.cs rename to src/Avalonia.Animation/AnimationsEngine`1.cs index 0a785ab496..169f0a7ae0 100644 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ b/src/Avalonia.Animation/AnimationsEngine`1.cs @@ -6,9 +6,10 @@ using Avalonia.Data; namespace Avalonia.Animation { /// - /// Provides statefulness for keyframe animations. + /// Handles interpolatoin and time-related functions + /// for keyframe animations. /// - internal class AnimatorStateMachine : IObservable, IDisposable + internal class AnimationsEngine : IObservable, IDisposable { T lastInterpValue; T firstKFValue; @@ -40,7 +41,7 @@ namespace Avalonia.Animation private IObserver targetObserver; private readonly Action onCompleteAction; - public AnimatorStateMachine(Animation animation, Animatable control, Animator animator, Action OnComplete) + public AnimationsEngine(Animation animation, Animatable control, Animator animator, Action OnComplete) { if (animation.SpeedRatio <= 0 || DoubleUtils.AboutEqual(animation.SpeedRatio, 0)) throw new InvalidOperationException("Speed ratio cannot be negative or zero."); From 3d7516cc0c1f82fc7d703f4c3d9add31dc9a0242 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 14 Aug 2018 23:54:48 +0800 Subject: [PATCH 020/135] Implement a new timing engine for Transitions. Transtions are fixed now.Move out some stuff from Timing.cs. --- .../ViewModels/AnimationsPageViewModel.cs | 6 +- src/Avalonia.Animation/Animation.cs | 5 +- src/Avalonia.Animation/AnimationsEngine`1.cs | 4 +- src/Avalonia.Animation/Animator`1.cs | 2 +- src/Avalonia.Animation/Timing.cs | 38 +----------- src/Avalonia.Animation/Transition`1.cs | 5 +- src/Avalonia.Animation/TransitionsEngine.cs | 58 +++++++++++++++++++ 7 files changed, 75 insertions(+), 43 deletions(-) create mode 100644 src/Avalonia.Animation/TransitionsEngine.cs diff --git a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs index c76d4db513..f724baf3c6 100644 --- a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs @@ -15,16 +15,16 @@ namespace RenderDemo.ViewModels void TogglePlayState() { - switch (Timing.GlobalPlayState) + switch (Animation.GlobalPlayState) { case PlayState.Run: PlayStateText = "Resume all animations"; - Timing.GlobalPlayState = PlayState.Pause; + Animation.GlobalPlayState = PlayState.Pause; break; case PlayState.Pause: PlayStateText = "Pause all animations"; - Timing.GlobalPlayState = PlayState.Run; + Animation.GlobalPlayState = PlayState.Run; break; } } diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index da2fc75c0b..204cc9d04d 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -21,6 +21,10 @@ namespace Avalonia.Animation /// public class Animation : AvaloniaList, IAnimation { + /// + /// Gets or sets the animation play state for all animations + /// + public static PlayState GlobalPlayState { get; set; } = PlayState.Run; public AvaloniaList _animators { get; set; } = new AvaloniaList(); @@ -73,7 +77,6 @@ namespace Avalonia.Animation /// public bool DelayBetweenIterations { get; set; } - private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> { ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) diff --git a/src/Avalonia.Animation/AnimationsEngine`1.cs b/src/Avalonia.Animation/AnimationsEngine`1.cs index 169f0a7ae0..cccb3098c0 100644 --- a/src/Avalonia.Animation/AnimationsEngine`1.cs +++ b/src/Avalonia.Animation/AnimationsEngine`1.cs @@ -111,7 +111,7 @@ namespace Avalonia.Animation private void DoPlayStatesAndTime(TimeSpan systemTime) { - if (Timing.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop) + if (Animation.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop) DoComplete(); if (!previousClock.HasValue) @@ -121,7 +121,7 @@ namespace Avalonia.Animation } else { - if (Timing.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause) + if (Animation.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause) { previousClock = systemTime; return; diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index a8b5ce7a27..8079ac69b5 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -98,7 +98,7 @@ namespace Avalonia.Animation /// private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete) { - var stateMachine = new AnimatorStateMachine(animation, control, this, onComplete); + var stateMachine = new AnimationsEngine(animation, control, this, onComplete); Timing.AnimationsTimer .TakeWhile(_ => !stateMachine.unsubscribe) diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index 575cedc620..b8282c05d0 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -15,11 +15,6 @@ namespace Avalonia.Animation /// public static class Timing { - /// - /// Gets or sets the animation play state for all animations - /// - public static PlayState GlobalPlayState { get; set; } = PlayState.Run; - /// /// The number of frames per second. /// @@ -38,14 +33,13 @@ namespace Avalonia.Animation var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); AnimationsTimer = globalTimer - .Select(_ => - { - return TimeSpan.FromMilliseconds(Environment.TickCount); - }) + .Select(_ => GetTickCount()) .Publish() .RefCount(); } + internal static TimeSpan GetTickCount() => TimeSpan.FromMilliseconds(Environment.TickCount); + /// /// Gets the animation timer. /// @@ -58,31 +52,5 @@ namespace Avalonia.Animation { get; } - - /// - /// Gets a timer that fires every frame for the specified duration with delay. - /// - /// - /// An observable that notifies the subscriber of the progress along the transition. - /// - /// - /// The parameter passed to the subscriber is the progress along the transition, with - /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 - /// immediately on subscribe and 1 at the end of the duration. - /// - public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan)) - { - // TODO: Fix this mess. - // var _duration = (duration.Ticks / FrameTick.Ticks); - // long? endTime = ((Stopwatch.GetTimestamp() - _tickStartTimeStamp) - // / TicksPerFrame) + _duration; - - // return AnimationsTimer - // .TakeWhile(x => x < endTime) - // .Select(x => (double)x / _duration) - // .StartWith(0.0) - // .Concat(Observable.Return(1.0)); - return Observable.Empty(); - } } } \ No newline at end of file diff --git a/src/Avalonia.Animation/Transition`1.cs b/src/Avalonia.Animation/Transition`1.cs index c097b930a5..346c328809 100644 --- a/src/Avalonia.Animation/Transition`1.cs +++ b/src/Avalonia.Animation/Transition`1.cs @@ -5,6 +5,7 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; using Avalonia.Animation.Easings; +using Avalonia.Animation.Utils; namespace Avalonia.Animation { @@ -51,8 +52,10 @@ namespace Avalonia.Animation /// public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) { - var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue); + var transition = DoTransition(new TransitionsEngine(Duration), (T)oldValue, (T)newValue); return control.Bind((AvaloniaProperty)Property, transition, Data.BindingPriority.Animation); } + + } } \ No newline at end of file diff --git a/src/Avalonia.Animation/TransitionsEngine.cs b/src/Avalonia.Animation/TransitionsEngine.cs new file mode 100644 index 0000000000..4d52b2bd48 --- /dev/null +++ b/src/Avalonia.Animation/TransitionsEngine.cs @@ -0,0 +1,58 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Metadata; +using System; +using System.Reactive.Linq; +using Avalonia.Animation.Easings; +using Avalonia.Animation.Utils; + +namespace Avalonia.Animation +{ + public class TransitionsEngine : IObservable, IDisposable + { + private IObserver observer; + private IDisposable timerSubscription; + private readonly TimeSpan startTime; + private readonly TimeSpan duration; + + public TransitionsEngine(TimeSpan Duration) + { + startTime = Timing.GetTickCount(); + duration = Duration; + + timerSubscription = Timing + .AnimationsTimer + .Subscribe(t => TimerTick(t)); + } + + private void TimerTick(TimeSpan t) + { + var interpVal = (double)(t.Ticks - startTime.Ticks) / duration.Ticks; + + if (interpVal > 1d + || interpVal < 0d) + { + this.Dispose(); + return; + } + + observer?.OnNext(interpVal); + } + + public void Dispose() + { + timerSubscription?.Dispose(); + observer?.OnCompleted(); + } + + public IDisposable Subscribe(IObserver Observer) + { + if (Observer is null) + throw new InvalidProgramException("Can only set the subscription once."); + + observer = Observer; + return this; + } + } +} \ No newline at end of file From 799b498afcfb416fa5d2535bd282c48d19d4b0d9 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Sat, 25 Aug 2018 16:29:37 +0100 Subject: [PATCH 021/135] Cleaning up Themes. Renamed ErrorBrushLight to ErrorLightBrush. ListBoxItem - Removed hardcoded Colors/Brushes, added additional sytling. Removed ListBoxItem Style Selector from AutoCompleteBox (inherrited). CalendarButton - Removed hardcoded Colors/Brushes. DropDownItem - Removed hardcoded Colors/Brushes, added additional sytling. ScrollBar - Removed hardcoded Colors/Brushes. Added ListBox to ControlCatalog. Slider bug fix (hopefully). --- samples/ControlCatalog/ControlCatalog.csproj | 9 +++ samples/ControlCatalog/MainView.xaml | 3 +- samples/ControlCatalog/Pages/ListBoxPage.xaml | 13 ++++ .../ControlCatalog/Pages/ListBoxPage.xaml.cs | 25 ++++++++ .../Pages/NumericUpDownPage.xaml | 4 +- src/Avalonia.Controls/Slider.cs | 8 +-- .../Accents/BaseLight.xaml | 60 +++++++++++++------ .../AutoCompleteBox.xaml | 6 +- .../CalendarButton.xaml | 5 +- .../DataValidationErrors.xaml | 4 +- src/Avalonia.Themes.Default/DropDownItem.xaml | 22 +++++-- src/Avalonia.Themes.Default/ListBoxItem.xaml | 23 ++++++- src/Avalonia.Themes.Default/ScrollBar.xaml | 10 ++-- 13 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 samples/ControlCatalog/Pages/ListBoxPage.xaml create mode 100644 samples/ControlCatalog/Pages/ListBoxPage.xaml.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index dea9b35e24..52f91b1295 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -33,6 +33,15 @@ + + + + MSBuild:Compile + + + MSBuild:Compile + + \ No newline at end of file diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 87cb5e9c5c..ec3bf799b4 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -20,8 +20,9 @@ + - + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml new file mode 100644 index 0000000000..3dd8be91c2 --- /dev/null +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -0,0 +1,13 @@ + + + ListBox + Hosts a collection of ListBoxItem. + + + + + + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs new file mode 100644 index 0000000000..dbe6c74800 --- /dev/null +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public class ListBoxPage : UserControl + { + public ListBoxPage() + { + this.InitializeComponent(); + DataContext = Enumerable.Range(1, 10).Select(i => $"Item {i}" ) + .ToArray(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + } +} diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 305bcd177c..2bb6214b58 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -14,7 +14,7 @@ AllowSpin: - + ClipValueToMinMax: @@ -77,4 +77,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 31113812d1..32b0a1f259 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -171,11 +171,7 @@ namespace Avalonia.Controls /// Value that want to snap to closest Tick. private void MoveToNextTick(double value) { - double next = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value))); - if (next != value) - { - Value = next; - } + Value = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value))); } /// @@ -194,4 +190,4 @@ namespace Avalonia.Controls return value; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index 4c85e172ff..afac69c9b3 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -2,23 +2,49 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"> - #FFFFFFFF - #FFAAAAAA - #FF888888 - #FF333333 - #FFFFFFFF - #FFAAAAAA - #FF888888 - #FF000000 - #FF808080 + + #CC119EDA + #99119EDA + #66119EDA + #33119EDA + + #FFFFFFFF + #FFAAAAAA + #FF888888 + #FF333333 + #FFFFFFFF + #FFAAAAAA + #FF888888 + #FFF0F0F0 + #FFD0D0D0 + #FF808080 + #FF000000 + #FF808080 + + #FF086F9E + #FFFF0000 + #10FF0000 - #FF086F9E - #CC119EDA - #99119EDA - #66119EDA - #33119EDA - Red - #10ff0000 + + + + + + + + + + + + + + + + + + + + 2 0.5 @@ -27,4 +53,4 @@ 12 16 - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml index 6a9af487cb..11d8a344d9 100644 --- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml +++ b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml @@ -36,8 +36,4 @@ - - - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/CalendarButton.xaml b/src/Avalonia.Themes.Default/CalendarButton.xaml index 84969c135f..b70740e0c8 100644 --- a/src/Avalonia.Themes.Default/CalendarButton.xaml +++ b/src/Avalonia.Themes.Default/CalendarButton.xaml @@ -7,6 +7,7 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/DataValidationErrors.xaml index f7f28d90d0..be45dfd1b9 100644 --- a/src/Avalonia.Themes.Default/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Default/DataValidationErrors.xaml @@ -24,7 +24,7 @@ Background="#00FFFFFF"> @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/DropDownItem.xaml b/src/Avalonia.Themes.Default/DropDownItem.xaml index 257030d8af..f52608c0a8 100644 --- a/src/Avalonia.Themes.Default/DropDownItem.xaml +++ b/src/Avalonia.Themes.Default/DropDownItem.xaml @@ -18,10 +18,24 @@ + + + - + + + + - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/ListBoxItem.xaml index fc2600c1a9..19a6e3d4ec 100644 --- a/src/Avalonia.Themes.Default/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Default/ListBoxItem.xaml @@ -1,6 +1,9 @@ + + + + + + + + - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml index b24c863be9..ef57474eaf 100644 --- a/src/Avalonia.Themes.Default/ScrollBar.xaml +++ b/src/Avalonia.Themes.Default/ScrollBar.xaml @@ -10,7 +10,7 @@ Grid.Column="0"> + Fill="{DynamicResource ThemeForegroundLightBrush}" /> + Fill="{DynamicResource ThemeForegroundLightBrush}" /> @@ -61,7 +61,7 @@ Grid.Column="0"> + Fill="{DynamicResource ThemeForegroundLightBrush}" /> + Fill="{DynamicResource ThemeForegroundLightBrush}" /> @@ -124,4 +124,4 @@ - \ No newline at end of file + From f7e31e58c09d6c3efb281f96b8a7b70e12cbf618 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Sat, 25 Aug 2018 16:32:53 +0100 Subject: [PATCH 022/135] Removed junk. --- samples/ControlCatalog/ControlCatalog.csproj | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 52f91b1295..61f2443eb7 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -33,15 +33,6 @@ - - - - MSBuild:Compile - - - MSBuild:Compile - - - \ No newline at end of file + From e9cde299bb282db2ea99a7c6e14d9b43f83b2a35 Mon Sep 17 00:00:00 2001 From: Jeffrey Ye Date: Sat, 25 Aug 2018 15:34:52 -0700 Subject: [PATCH 023/135] #1802 add SelectedItemChanged event to TreeView --- .../SelectedItemChangedEventArgs.cs | 36 +++++++++++++++++++ src/Avalonia.Controls/TreeView.cs | 27 ++++++++++++++ .../TreeViewTests.cs | 32 +++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/Avalonia.Controls/SelectedItemChangedEventArgs.cs diff --git a/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs b/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs new file mode 100644 index 0000000000..6fb8a2d313 --- /dev/null +++ b/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Avalonia; +using Avalonia.Interactivity; + +namespace Avalonia.Controls +{ + /// + /// Provides data for the event. + /// + public class SelectedItemChangedEventArgs : RoutedEventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The event being raised. + /// The items added to the selection. + /// The items removed from the selection. + public SelectedItemChangedEventArgs(RoutedEvent routedEvent, object newItem, object oldItem) + : base(routedEvent) + { + NewItem = newItem; + OldItem = oldItem; + } + + /// + /// Gets the items that were added to the selection. + /// + public object NewItem { get; } + + /// + /// Gets the items that were removed from the selection. + /// + public object OldItem { get; } + } +} diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 2e1c011685..50b45cbb9a 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -32,6 +32,14 @@ namespace Avalonia.Controls o => o.SelectedItem, (o, v) => o.SelectedItem = v); + /// + /// Defines the event. + /// + public static readonly RoutedEvent SelectedItemChangedEvent = + RoutedEvent.Register( + "SelectedItemChanged", + RoutingStrategies.Bubble); + private object _selectedItem; /// @@ -42,6 +50,15 @@ namespace Avalonia.Controls // HACK: Needed or SelectedItem property will not be found in Release build. } + /// + /// Occurs when the control's selection changes. + /// + public event EventHandler SelectedItemChanged + { + add { AddHandler(SelectedItemChangedEvent, value); } + remove { RemoveHandler(SelectedItemChangedEvent, value); } + } + /// /// Gets the for the tree view. /// @@ -75,6 +92,7 @@ namespace Avalonia.Controls MarkContainerSelected(container, false); } + var oldItem = _selectedItem; SetAndRaise(SelectedItemProperty, ref _selectedItem, value); if (_selectedItem != null) @@ -87,6 +105,15 @@ namespace Avalonia.Controls container.BringIntoView(); } } + + if (oldItem != _selectedItem) + { + var changed = new SelectedItemChangedEventArgs( + SelectedItemChangedEvent, + _selectedItem, + oldItem); + RaiseEvent(changed); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index c49c343a45..8295409f7e 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -166,6 +166,38 @@ namespace Avalonia.Controls.UnitTests Assert.True(container.IsSelected); } + + [Fact] + public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event() + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var item = tree[0].Children[1].Children[0]; + + var called = false; + target.SelectedItemChanged += (s, e) => + { + Assert.Null(e.OldItem); + Assert.Same(item, e.NewItem); + called = true; + }; + + target.SelectedItem = item; + Assert.True(called); + } + + [Fact] public void LogicalChildren_Should_Be_Set() { From 8087f81ea57ee0b955154a61e3d59550c2a20e40 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Mon, 27 Aug 2018 02:52:04 +0100 Subject: [PATCH 024/135] Cleaned up resources for CalendarDayButton,CalendarItem,DataValidationErrors,DatePicker,Expander,Slider Typeface - Added Typeface.Default. FormattedTextImpl - Fallback to Typeface.Default for null typeface. --- src/Avalonia.Themes.Default/CalendarDayButton.xaml | 6 +++--- src/Avalonia.Themes.Default/CalendarItem.xaml | 4 ++-- src/Avalonia.Themes.Default/DataValidationErrors.xaml | 2 +- src/Avalonia.Themes.Default/DatePicker.xaml | 8 ++++---- src/Avalonia.Themes.Default/Expander.xaml | 4 ++-- src/Avalonia.Themes.Default/Slider.xaml | 3 ++- src/Avalonia.Visuals/Media/Typeface.cs | 2 ++ src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 7 ++++++- 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Themes.Default/CalendarDayButton.xaml b/src/Avalonia.Themes.Default/CalendarDayButton.xaml index ee7afdc73b..4a971ef0cb 100644 --- a/src/Avalonia.Themes.Default/CalendarDayButton.xaml +++ b/src/Avalonia.Themes.Default/CalendarDayButton.xaml @@ -42,7 +42,7 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RenderTransformOrigin="0.5,0.5" - Fill="#FF000000" + Fill="{DynamicResource ThemeForegroundBrush}" Stretch="Fill" Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" /> @@ -103,7 +103,7 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/CalendarItem.xaml b/src/Avalonia.Themes.Default/CalendarItem.xaml index 3d3d75a39a..b52b7acf53 100644 --- a/src/Avalonia.Themes.Default/CalendarItem.xaml +++ b/src/Avalonia.Themes.Default/CalendarItem.xaml @@ -153,7 +153,7 @@ @@ -180,4 +180,4 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/DataValidationErrors.xaml index be45dfd1b9..16c2d6adef 100644 --- a/src/Avalonia.Themes.Default/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Default/DataValidationErrors.xaml @@ -21,7 +21,7 @@ + Background="Transparent"> - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml index 0bea0c9763..d12f704cc4 100644 --- a/src/Avalonia.Themes.Default/Expander.xaml +++ b/src/Avalonia.Themes.Default/Expander.xaml @@ -108,7 +108,7 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Slider.xaml index 58fd67b2f6..95924b31a1 100644 --- a/src/Avalonia.Themes.Default/Slider.xaml +++ b/src/Avalonia.Themes.Default/Slider.xaml @@ -80,10 +80,11 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs index 40e98d1565..6dde2bb591 100644 --- a/src/Avalonia.Visuals/Media/Typeface.cs +++ b/src/Avalonia.Visuals/Media/Typeface.cs @@ -7,6 +7,8 @@ namespace Avalonia.Media /// public class Typeface { + public static Typeface Default = new Typeface(FontFamily.Default); + /// /// Initializes a new instance of the class. /// diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index d835c83aa6..8ad88da7f4 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -30,6 +30,11 @@ namespace Avalonia.Skia SKTypeface skiaTypeface = TypefaceCache.Default; + if (typeface == null) + { + typeface = Typeface.Default; + } + if (typeface.FontFamily.Key != null) { var typefaces = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(typeface.FontFamily); @@ -723,4 +728,4 @@ namespace Avalonia.Skia } } } -} \ No newline at end of file +} From 0befa22692d1fa32652f621042bc54188a7b5769 Mon Sep 17 00:00:00 2001 From: Nicolas Musset Date: Tue, 28 Aug 2018 21:49:34 +0900 Subject: [PATCH 025/135] Support invoking a task in the dispatcher. --- src/Avalonia.Base/Threading/Dispatcher.cs | 14 ++++++++++++++ src/Avalonia.Base/Threading/IDispatcher.cs | 18 ++++++++++++++++++ .../Avalonia.UnitTests/ImmediateDispatcher.cs | 19 ++++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index aa2a7a7a8e..55a9b6984a 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -92,6 +92,20 @@ namespace Avalonia.Threading return _jobRunner.InvokeAsync(function, priority); } + /// + public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + { + Contract.Requires(function != null); + return _jobRunner.InvokeAsync(function, priority).Unwrap(); + } + + /// + public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + { + Contract.Requires(function != null); + return _jobRunner.InvokeAsync(function, priority).Unwrap(); + } + /// public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index 1fdc9da5fe..8f46f99283 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -40,5 +40,23 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Queues the specified work to run on the dispatcher thread and returns a proxy for the + /// task returned by . + /// + /// The work to execute asynchronously. + /// The priority with which to invoke the method. + /// A task that represents a proxy for the task returned by . + Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Queues the specified work to run on the dispatcher thread and returns a proxy for the + /// task returned by . + /// + /// The work to execute asynchronously. + /// The priority with which to invoke the method. + /// A task that represents a proxy for the task returned by . + Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal); } } \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs index 44d8c78054..fac4ee64e7 100644 --- a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs +++ b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs @@ -9,28 +9,45 @@ namespace Avalonia.UnitTests /// public class ImmediateDispatcher : IDispatcher { + /// public bool CheckAccess() { return true; } + /// public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { action(); } + /// public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { action(); - return Task.FromResult(null); + return Task.CompletedTask; } + /// public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) { var result = function(); return Task.FromResult(result); } + /// + public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + { + return function(); + } + + /// + public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + { + return function(); + } + + /// public void VerifyAccess() { } From a161f6a095eb990b48d9b7173326b6fcd7fceac4 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Tue, 28 Aug 2018 22:31:46 +0100 Subject: [PATCH 026/135] ScreenPage.cs - Bug fix. TreeViewItem - Added Depth property. BaseLight.xaml - Added InvisibleSelectionColor/Brush. DatePicker.xaml - Improvments. TreeViewItem.xaml - Reworked styling. FormattedTextImpl.cs - Reverted previous changes. --- samples/ControlCatalog/Pages/ScreenPage.cs | 8 +- src/Avalonia.Controls/TreeViewItem.cs | 40 ++++- .../Accents/BaseLight.xaml | 6 +- src/Avalonia.Themes.Default/DatePicker.xaml | 27 +-- src/Avalonia.Themes.Default/TreeViewItem.xaml | 154 ++++++++++-------- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 5 - 6 files changed, 153 insertions(+), 87 deletions(-) diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 34aa85b8aa..fd66185832 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -42,7 +42,11 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, workingAreaRect); - FormattedText text = new FormattedText(); + FormattedText text = new FormattedText() + { + Typeface = Typeface.Default + }; + text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text); @@ -59,4 +63,4 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10)); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 0886c05038..33b0e54868 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.Generators; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Data; using Avalonia.Input; using Avalonia.LogicalTree; @@ -21,7 +22,7 @@ namespace Avalonia.Controls /// public static readonly DirectProperty IsExpandedProperty = AvaloniaProperty.RegisterDirect( - "IsExpanded", + nameof(IsExpanded), o => o.IsExpanded, (o, v) => o.IsExpanded = v); @@ -31,11 +32,20 @@ namespace Avalonia.Controls public static readonly StyledProperty IsSelectedProperty = ListBoxItem.IsSelectedProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly DirectProperty DepthProperty = + AvaloniaProperty.RegisterDirect( + nameof(Depth), + o => o.Depth); + private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel()); private TreeView _treeView; private bool _isExpanded; + private int _depth; /// /// Initializes static members of the class. @@ -65,6 +75,34 @@ namespace Avalonia.Controls set { SetValue(IsSelectedProperty, value); } } + /// + /// Gets or sets the depth of the item. + /// + public int Depth + { + get { return this.GetDepth(this); } + } + + private int GetDepth(TreeViewItem item) + { + TreeViewItem parent; + while ((parent = GetParent(item)) != null) + { + return GetDepth(parent) + 1; + } + return 0; + } + + private static TreeViewItem GetParent(TreeViewItem item) + { + var parent = item.InheritanceParent; + while (!(parent == null || parent is TreeViewItem || parent is TreeView)) + { + parent = item.Parent; + } + return parent as TreeViewItem; + } + /// /// Gets the for the tree view. /// diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index afac69c9b3..5aac023708 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -25,6 +25,8 @@ #FFFF0000 #10FF0000 + #01000000 + @@ -45,7 +47,9 @@ - + + + 2 0.5 diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/DatePicker.xaml index 8e0bb5f0b0..93bafdb56f 100644 --- a/src/Avalonia.Themes.Default/DatePicker.xaml +++ b/src/Avalonia.Themes.Default/DatePicker.xaml @@ -6,7 +6,8 @@ --> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + + - + - - - - - - - + + + - - \ No newline at end of file + + + + + + + + + + + + diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index 8ad88da7f4..c8d051ed74 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -30,11 +30,6 @@ namespace Avalonia.Skia SKTypeface skiaTypeface = TypefaceCache.Default; - if (typeface == null) - { - typeface = Typeface.Default; - } - if (typeface.FontFamily.Key != null) { var typefaces = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(typeface.FontFamily); From d4b9ef504248585a4b34b874e8a435cf40cd6625 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Tue, 28 Aug 2018 22:32:11 +0100 Subject: [PATCH 027/135] Added missing Converter. --- .../Converters/MarginMultiplierConverter.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs diff --git a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs new file mode 100644 index 0000000000..54bd6bcf39 --- /dev/null +++ b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace Avalonia.Controls.Converters +{ + public class MarginMultiplierConverter : IValueConverter + { + public double Indent { get; set; } + + public bool Left { get; set; } = false; + + public bool Top { get; set; } = false; + + public bool Right { get; set; } = false; + + public bool Bottom { get; set; } = false; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (!(value is int depth)) + return new Thickness(0); + + return new Thickness(Left ? Indent * depth : 0, Top ? Indent * depth : 0, Right ? Indent * depth : 0, Bottom ? Indent * depth : 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new System.NotImplementedException(); + } + } +} From f180ec4dd7fed1cbadcc8fa4aba4875fec4530fc Mon Sep 17 00:00:00 2001 From: Jeffrey Ye Date: Wed, 29 Aug 2018 14:22:00 -0700 Subject: [PATCH 028/135] Use SelectionChangedEventArgs instead of SelectedItemChangedEventArgs --- .../SelectedItemChangedEventArgs.cs | 36 ------------------- src/Avalonia.Controls/TreeView.cs | 26 ++++++++++---- .../TreeViewTests.cs | 5 +-- 3 files changed, 23 insertions(+), 44 deletions(-) delete mode 100644 src/Avalonia.Controls/SelectedItemChangedEventArgs.cs diff --git a/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs b/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs deleted file mode 100644 index 6fb8a2d313..0000000000 --- a/src/Avalonia.Controls/SelectedItemChangedEventArgs.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia; -using Avalonia.Interactivity; - -namespace Avalonia.Controls -{ - /// - /// Provides data for the event. - /// - public class SelectedItemChangedEventArgs : RoutedEventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// The event being raised. - /// The items added to the selection. - /// The items removed from the selection. - public SelectedItemChangedEventArgs(RoutedEvent routedEvent, object newItem, object oldItem) - : base(routedEvent) - { - NewItem = newItem; - OldItem = oldItem; - } - - /// - /// Gets the items that were added to the selection. - /// - public object NewItem { get; } - - /// - /// Gets the items that were removed from the selection. - /// - public object OldItem { get; } - } -} diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 50b45cbb9a..d449a7cd38 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.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 System.Linq; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; @@ -35,8 +36,8 @@ namespace Avalonia.Controls /// /// Defines the event. /// - public static readonly RoutedEvent SelectedItemChangedEvent = - RoutedEvent.Register( + public static readonly RoutedEvent SelectedItemChangedEvent = + RoutedEvent.Register( "SelectedItemChanged", RoutingStrategies.Bubble); @@ -53,7 +54,7 @@ namespace Avalonia.Controls /// /// Occurs when the control's selection changes. /// - public event EventHandler SelectedItemChanged + public event EventHandler SelectedItemChanged { add { AddHandler(SelectedItemChangedEvent, value); } remove { RemoveHandler(SelectedItemChangedEvent, value); } @@ -108,10 +109,23 @@ namespace Avalonia.Controls if (oldItem != _selectedItem) { - var changed = new SelectedItemChangedEventArgs( + // Fire the SelectionChanged event + List removed = new List(); + if (oldItem != null) + { + removed.Add(oldItem); + } + + List added = new List(); + if (_selectedItem != null) + { + added.Add(_selectedItem); + } + + var changed = new SelectionChangedEventArgs( SelectedItemChangedEvent, - _selectedItem, - oldItem); + added, + removed); RaiseEvent(changed); } } diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 8295409f7e..1a913865cb 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -188,8 +188,9 @@ namespace Avalonia.Controls.UnitTests var called = false; target.SelectedItemChanged += (s, e) => { - Assert.Null(e.OldItem); - Assert.Same(item, e.NewItem); + Assert.Empty(e.RemovedItems); + Assert.Equal(1, e.AddedItems.Count); + Assert.Same(item, e.AddedItems[0]); called = true; }; From f6dfd1688ef7ec2014503b70daa65ea84f80b838 Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Thu, 30 Aug 2018 01:03:58 +0100 Subject: [PATCH 029/135] TreeViewItem updates. --- src/Avalonia.Controls/TreeViewItem.cs | 29 +--- .../LogicalTree/LogicalExtensions.cs | 15 ++ src/Avalonia.Themes.Default/TreeViewItem.xaml | 163 +++++++++--------- 3 files changed, 102 insertions(+), 105 deletions(-) diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 33b0e54868..c5d3313117 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -6,7 +6,6 @@ using Avalonia.Controls.Generators; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Data; using Avalonia.Input; using Avalonia.LogicalTree; @@ -37,8 +36,7 @@ namespace Avalonia.Controls /// public static readonly DirectProperty DepthProperty = AvaloniaProperty.RegisterDirect( - nameof(Depth), - o => o.Depth); + nameof(Depth), o => o.Depth); private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel()); @@ -80,27 +78,8 @@ namespace Avalonia.Controls /// public int Depth { - get { return this.GetDepth(this); } - } - - private int GetDepth(TreeViewItem item) - { - TreeViewItem parent; - while ((parent = GetParent(item)) != null) - { - return GetDepth(parent) + 1; - } - return 0; - } - - private static TreeViewItem GetParent(TreeViewItem item) - { - var parent = item.InheritanceParent; - while (!(parent == null || parent is TreeViewItem || parent is TreeView)) - { - parent = item.Parent; - } - return parent as TreeViewItem; + get { return _depth; } + private set { SetAndRaise(DepthProperty, ref _depth, value); } } /// @@ -127,6 +106,8 @@ namespace Avalonia.Controls base.OnAttachedToLogicalTree(e); _treeView = this.GetLogicalAncestors().OfType().FirstOrDefault(); + Depth = this.CalculateDistanceFromLogicalParent() - 1; + if (ItemTemplate == null && _treeView?.ItemTemplate != null) { ItemTemplate = _treeView.ItemTemplate; diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 276ea6c060..47550a876d 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -87,5 +87,20 @@ namespace Avalonia.LogicalTree { return target.GetLogicalAncestors().Any(x => x == logical); } + + public static int CalculateDistanceFromLogicalParent(this ILogical logical, int @default = -1) where T : class + { + Contract.Requires(logical != null); + + var result = 0; + + while (logical != null && logical.GetType() != typeof(T)) + { + ++result; + logical = logical.LogicalParent; + } + + return logical != null ? result : @default; + } } } diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/TreeViewItem.xaml index a51b6c0aca..5c5012e685 100644 --- a/src/Avalonia.Themes.Default/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Default/TreeViewItem.xaml @@ -1,91 +1,92 @@ - + xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"> + - + - - - + - + - + - + - - - + - + + + + + From 22b373948a821ddb0749f2213b15fa2ec67ec34b Mon Sep 17 00:00:00 2001 From: William David Cossey Date: Thu, 30 Aug 2018 01:05:15 +0100 Subject: [PATCH 030/135] Reverted some tab indentation. --- src/Avalonia.Themes.Default/TreeViewItem.xaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/TreeViewItem.xaml index 5c5012e685..ada86d3535 100644 --- a/src/Avalonia.Themes.Default/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Default/TreeViewItem.xaml @@ -47,10 +47,10 @@ Height="12" HorizontalAlignment="Center" VerticalAlignment="Center"> - + From 298eef1c1b8e9950578c14a6b2396551868df0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sat, 1 Sep 2018 19:23:46 +0200 Subject: [PATCH 031/135] Fixes #1847 PasswordChar change doesn't affect render --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 636c836da5..349ae80362 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -36,7 +36,11 @@ namespace Avalonia.Controls.Presenters private int _selectionEnd; private bool _caretBlink; private IBrush _highlightBrush; - + + static TextPresenter() + { + AffectsRender(PasswordCharProperty); + } public TextPresenter() { _caretTimer = new DispatcherTimer(); @@ -50,6 +54,9 @@ namespace Avalonia.Controls.Presenters this.GetObservable(CaretIndexProperty) .Subscribe(CaretIndexChanged); + + this.GetObservable(PasswordCharProperty) + .Subscribe(_ => InvalidateFormattedText()); } public int CaretIndex @@ -116,7 +123,7 @@ namespace Avalonia.Controls.Presenters var start = Math.Min(selectionStart, selectionEnd); var length = Math.Max(selectionStart, selectionEnd) - start; - // issue #600: set constaint before any FormattedText manipulation + // issue #600: set constraint before any FormattedText manipulation // see base.Render(...) implementation FormattedText.Constraint = Bounds.Size; From 1524cb1aba4ca188d04fe313841435e2536d4be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sat, 1 Sep 2018 20:03:07 +0200 Subject: [PATCH 032/135] Spellchecks comments and members, removes unused using statements and sorts them --- src/Avalonia.Controls/AppBuilderBase.cs | 6 ++--- src/Avalonia.Controls/Application.cs | 1 - src/Avalonia.Controls/AutoCompleteBox.cs | 14 +++++------ src/Avalonia.Controls/Border.cs | 3 +-- src/Avalonia.Controls/Calendar/Calendar.cs | 6 ++--- .../CalendarBlackoutDatesCollection.cs | 3 +-- .../Calendar/CalendarDateRange.cs | 1 - .../Calendar/CalendarDayButton.cs | 4 +--- .../Calendar/CalendarExtensions.cs | 3 --- .../Calendar/CalendarItem.cs | 6 ++--- src/Avalonia.Controls/Calendar/DatePicker.cs | 11 ++++----- src/Avalonia.Controls/ContextMenu.cs | 2 +- src/Avalonia.Controls/Control.cs | 16 ++----------- src/Avalonia.Controls/DataValidationErrors.cs | 1 - .../Embedding/EmbeddableControlRoot.cs | 1 - .../Embedding/Offscreen/OffscreenTopLevel.cs | 5 ---- .../Offscreen/OffscreenTopLevelImpl.cs | 3 --- src/Avalonia.Controls/Expander.cs | 1 - .../Generators/IItemContainerGenerator.cs | 4 ++-- .../Generators/ITreeItemContainerGenerator.cs | 4 +--- .../Generators/ItemContainerGenerator`1.cs | 2 -- .../Generators/MenuItemContainerGenerator.cs | 8 +------ .../Generators/TreeContainerIndex.cs | 2 +- .../Generators/TreeItemContainerGenerator.cs | 1 - src/Avalonia.Controls/GridLength.cs | 5 ++-- src/Avalonia.Controls/GridSplitter.cs | 16 ++++++------- src/Avalonia.Controls/HotkeyManager.cs | 5 ---- .../IApplicationLifecycle.cs | 4 ---- src/Avalonia.Controls/IControl.cs | 5 +--- src/Avalonia.Controls/IMenu.cs | 3 +-- src/Avalonia.Controls/IMenuElement.cs | 3 +-- src/Avalonia.Controls/IMenuItem.cs | 4 +--- src/Avalonia.Controls/IScrollable.cs | 2 -- src/Avalonia.Controls/IVirtualizingPanel.cs | 2 +- src/Avalonia.Controls/ItemsControl.cs | 2 +- src/Avalonia.Controls/Menu.cs | 2 +- .../Mixins/ContentControlMixin.cs | 1 - .../NumericUpDown/NumericUpDown.cs | 8 +++---- .../ExportWindowingSubsystemAttribute.cs | 4 ---- .../Platform/IMenuInteractionHandler.cs | 5 +--- .../Platform/IPlatformIconLoader.cs | 8 +------ .../Platform/ITopLevelImpl.cs | 24 +++++++++---------- .../Platform/IWindowBaseImpl.cs | 6 ++--- .../Platform/IWindowingPlatform.cs | 6 ----- .../Surfaces/IFramebufferPlatformSurface.cs | 7 +----- .../Presenters/CarouselPresenter.cs | 3 +-- .../Presenters/IPresenter.cs | 1 - .../Presenters/ItemVirtualizer.cs | 1 - .../Presenters/ItemVirtualizerSimple.cs | 2 +- .../Presenters/ScrollContentPresenter.cs | 3 +-- .../Presenters/TextPresenter.cs | 3 +-- .../Primitives/HeaderedItemsControl.cs | 2 -- src/Avalonia.Controls/Primitives/Popup.cs | 5 ++-- src/Avalonia.Controls/Primitives/PopupRoot.cs | 3 --- src/Avalonia.Controls/Primitives/RangeBase.cs | 2 +- .../Primitives/SelectingItemsControl.cs | 1 - src/Avalonia.Controls/Primitives/Thumb.cs | 1 - src/Avalonia.Controls/ProgressBar.cs | 4 ---- src/Avalonia.Controls/Remote/RemoteServer.cs | 4 ---- src/Avalonia.Controls/Remote/RemoteWidget.cs | 3 +-- .../Remote/Server/RemoteServerTopLevelImpl.cs | 7 ++---- src/Avalonia.Controls/Separator.cs | 5 ---- src/Avalonia.Controls/Shapes/Path.cs | 1 - src/Avalonia.Controls/Shapes/Shape.cs | 1 - src/Avalonia.Controls/Spinner.cs | 4 +--- .../Templates/FuncDataTemplate`1.cs | 6 ++--- .../Templates/FuncMemberSelector.cs | 4 ++-- .../Templates/FuncTreeDataTemplate.cs | 2 +- .../Templates/FuncTreeDataTemplate`1.cs | 2 +- .../Templates/IDataTemplateHost.cs | 1 - .../Templates/IMemberSelector.cs | 4 ++-- .../Templates/ITreeDataTemplate.cs | 4 ++-- src/Avalonia.Controls/TextBlock.cs | 4 ++-- src/Avalonia.Controls/ToolTip.cs | 2 +- src/Avalonia.Controls/ToolTipService.cs | 4 ++-- src/Avalonia.Controls/TopLevel.cs | 4 +--- src/Avalonia.Controls/TreeView.cs | 3 +-- src/Avalonia.Controls/Utils/AncestorFinder.cs | 5 +--- src/Avalonia.Controls/Utils/GridLayout.cs | 2 +- .../Utils/IEnumerableUtils.cs | 1 - .../SelectingItemsControlSelectionAdapter.cs | 7 ++---- src/Avalonia.Controls/Window.cs | 10 ++++---- src/Avalonia.Controls/WindowBase.cs | 3 --- src/Avalonia.Controls/WindowIcon.cs | 9 ++----- src/Avalonia.Controls/WrapPanel.cs | 20 ++++++++-------- 85 files changed, 121 insertions(+), 262 deletions(-) diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 83763c0836..c92d5d7694 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -57,14 +57,14 @@ namespace Avalonia.Controls public Action AfterSetupCallback { get; private set; } = builder => { }; /// - /// Gets or sets a method to call before Startis called on the . + /// Gets or sets a method to call before Start is called on the . /// public Action BeforeStartCallback { get; private set; } = builder => { }; - protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices) + protected AppBuilderBase(IRuntimePlatform platform, Action platformServices) { RuntimePlatform = platform; - RuntimePlatformServicesInitializer = () => platformSevices((TAppBuilder)this); + RuntimePlatformServicesInitializer = () => platformServices((TAppBuilder)this); } /// diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 499b65c5b7..4c549ac7d4 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -9,7 +9,6 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Input.Raw; -using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Threading; diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 96fb9be8ac..1bc402bc2f 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -352,8 +352,8 @@ namespace Avalonia.Controls private Func>> _asyncPopulator; private CancellationTokenSource _populationCancellationTokenSource; - private bool _itemTemplateIsFromValueMemeberBinding = true; - private bool _settingItemTemplateFromValueMemeberBinding; + private bool _itemTemplateIsFromValueMemberBinding = true; + private bool _settingItemTemplateFromValueMemberBinding; private object _selectedItem; private bool _isDropDownOpen; @@ -788,12 +788,12 @@ namespace Avalonia.Controls private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e) { - if (!_settingItemTemplateFromValueMemeberBinding) - _itemTemplateIsFromValueMemeberBinding = false; + if (!_settingItemTemplateFromValueMemberBinding) + _itemTemplateIsFromValueMemberBinding = false; } private void OnValueMemberBindingChanged(IBinding value) { - if(_itemTemplateIsFromValueMemeberBinding) + if(_itemTemplateIsFromValueMemberBinding) { var template = new FuncDataTemplate( @@ -805,9 +805,9 @@ namespace Avalonia.Controls return control; }); - _settingItemTemplateFromValueMemeberBinding = true; + _settingItemTemplateFromValueMemberBinding = true; ItemTemplate = template; - _settingItemTemplateFromValueMemeberBinding = false; + _settingItemTemplateFromValueMemberBinding = false; } } diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 0382c8d675..5f84421c64 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -1,7 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using Avalonia; using Avalonia.Controls.Utils; using Avalonia.Layout; using Avalonia.Media; @@ -115,4 +114,4 @@ namespace Avalonia.Controls return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs index 029e4dadc8..8f5a32634e 100644 --- a/src/Avalonia.Controls/Calendar/Calendar.cs +++ b/src/Avalonia.Controls/Calendar/Calendar.cs @@ -3,14 +3,14 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; -using System; -using System.Collections.ObjectModel; -using System.Diagnostics; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs index 0d48418683..5d883f2d14 100644 --- a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs @@ -3,11 +3,10 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. -using Avalonia.Threading; using System; using System.Collections.ObjectModel; using System.Linq; -using System.Threading; +using Avalonia.Threading; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/Calendar/CalendarDateRange.cs b/src/Avalonia.Controls/Calendar/CalendarDateRange.cs index 718cc7142b..47a0ad047b 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDateRange.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDateRange.cs @@ -4,7 +4,6 @@ // All other rights reserved. using System; -using System.Diagnostics; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs b/src/Avalonia.Controls/Calendar/CalendarDayButton.cs index f6d0fbba62..1b36f92fd3 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDayButton.cs @@ -3,11 +3,9 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. -using Avalonia.Input; using System; -using System.Collections.Generic; using System.Globalization; -using System.Text; +using Avalonia.Input; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/Calendar/CalendarExtensions.cs b/src/Avalonia.Controls/Calendar/CalendarExtensions.cs index 4fda02bff3..73de1c38f3 100644 --- a/src/Avalonia.Controls/Calendar/CalendarExtensions.cs +++ b/src/Avalonia.Controls/Calendar/CalendarExtensions.cs @@ -3,10 +3,7 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. -using System; -using System.Collections.Generic; using Avalonia.Input; -using System.Diagnostics; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index b0cbd0be53..577555333f 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -3,13 +3,13 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using System; +using System.Diagnostics; +using System.Globalization; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; -using System; -using System.Diagnostics; -using System.Globalization; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs index 08608ad359..2270598623 100644 --- a/src/Avalonia.Controls/Calendar/DatePicker.cs +++ b/src/Avalonia.Controls/Calendar/DatePicker.cs @@ -3,15 +3,14 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. -using Avalonia.Controls.Primitives; -using Avalonia.Data; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Media; using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; namespace Avalonia.Controls { @@ -1083,7 +1082,7 @@ namespace Avalonia.Controls else { // If parse error: TextBox should have the latest valid - // selecteddate value: + // SelectedDate value: if (SelectedDate != null) { string newtext = this.DateTimeToString(SelectedDate.Value); diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 0accb284b6..a69152c42b 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls /// /// Initializes a new instance of the class. /// - /// The menu iteraction handler. + /// The menu interaction handler. public ContextMenu(IMenuInteractionHandler interactionHandler) { Contract.Requires(interactionHandler != null); diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 67288972b6..a00d586233 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -1,22 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Reactive; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using Avalonia.Collections; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Data; -using Avalonia.Diagnostics; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Logging; -using Avalonia.LogicalTree; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.VisualTree; @@ -134,9 +122,9 @@ namespace Avalonia.Controls } /// - /// Gets the element that recieves the focus adorner. + /// Gets the element that receives the focus adorner. /// - /// The control that recieves the focus adorner. + /// The control that receives the focus adorner. protected virtual IControl GetTemplateFocusTarget() { return this; diff --git a/src/Avalonia.Controls/DataValidationErrors.cs b/src/Avalonia.Controls/DataValidationErrors.cs index a55bd63aa8..f0d7f8257e 100644 --- a/src/Avalonia.Controls/DataValidationErrors.cs +++ b/src/Avalonia.Controls/DataValidationErrors.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; -using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index c177d43917..224af979ab 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -1,7 +1,6 @@ using System; using Avalonia.Controls.Platform; using Avalonia.Input; -using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; using JetBrains.Annotations; diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs index 5becdc0f61..8b39cc03b8 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs @@ -1,9 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Layout; using Avalonia.Styling; namespace Avalonia.Controls.Embedding.Offscreen diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index c986c5d07c..37bb72e75a 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs index 5323939b50..1049d742f3 100644 --- a/src/Avalonia.Controls/Expander.cs +++ b/src/Avalonia.Controls/Expander.cs @@ -1,6 +1,5 @@ using Avalonia.Animation; using Avalonia.Controls.Primitives; -using Avalonia.VisualTree; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs index d139c95fd4..653a4f5dcb 100644 --- a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs @@ -28,7 +28,7 @@ namespace Avalonia.Controls.Generators Type ContainerType { get; } /// - /// Signalled whenever new containers are materialized. + /// Signaled whenever new containers are materialized. /// event EventHandler Materialized; @@ -110,4 +110,4 @@ namespace Avalonia.Controls.Generators /// The index of the container, or -1 if not found. int IndexFromContainer(IControl container); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs index 224fc9d17e..e2e591215e 100644 --- a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs @@ -1,8 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System.Collections.Generic; - namespace Avalonia.Controls.Generators { /// @@ -15,4 +13,4 @@ namespace Avalonia.Controls.Generators /// TreeContainerIndex Index { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs index 259c524d59..320d6c8faf 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Linq.Expressions; -using System.Reflection; using Avalonia.Controls.Templates; using Avalonia.Data; diff --git a/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs index c9b3a55aaa..d3cf70e0f8 100644 --- a/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Avalonia.Controls.Generators +namespace Avalonia.Controls.Generators { public class MenuItemContainerGenerator : ItemContainerGenerator { diff --git a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs index 08c11e2965..24b3fc1f32 100644 --- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs +++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls.Generators private readonly Dictionary _containerToItem = new Dictionary(); /// - /// Signalled whenever new containers are materialized. + /// Signaled whenever new containers are materialized. /// public event EventHandler Materialized; diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs index abcecb5b82..304c86dbf7 100644 --- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Avalonia.Controls.Templates; using Avalonia.Data; diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index 608879812c..f6a608cd71 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -1,11 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using Avalonia.Utilities; using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; +using Avalonia.Utilities; namespace Avalonia.Controls { @@ -218,4 +217,4 @@ namespace Avalonia.Controls } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 8112b2babd..1e4c6f2c2a 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -116,27 +116,27 @@ namespace Avalonia.Controls _orientation = DetectOrientation(); - int defenitionIndex; //row or col + int definitionIndex; //row or col if (_orientation == Orientation.Vertical) { Cursor = new Cursor(StandardCursorType.SizeWestEast); _definitions = _grid.ColumnDefinitions.Cast().ToList(); - defenitionIndex = GetValue(Grid.ColumnProperty); + definitionIndex = GetValue(Grid.ColumnProperty); PseudoClasses.Add(":vertical"); } else { Cursor = new Cursor(StandardCursorType.SizeNorthSouth); - defenitionIndex = GetValue(Grid.RowProperty); + definitionIndex = GetValue(Grid.RowProperty); _definitions = _grid.RowDefinitions.Cast().ToList(); PseudoClasses.Add(":horizontal"); } - if (defenitionIndex > 0) - _prevDefinition = _definitions[defenitionIndex - 1]; + if (definitionIndex > 0) + _prevDefinition = _definitions[definitionIndex - 1]; - if (defenitionIndex < _definitions.Count - 1) - _nextDefinition = _definitions[defenitionIndex + 1]; + if (definitionIndex < _definitions.Count - 1) + _nextDefinition = _definitions[definitionIndex + 1]; } private Orientation DetectOrientation() @@ -167,4 +167,4 @@ namespace Avalonia.Controls return Orientation.Vertical; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index a59fb86fb7..95752e7875 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Input; -using Avalonia.Controls; using Avalonia.Controls.Utils; using Avalonia.Input; diff --git a/src/Avalonia.Controls/IApplicationLifecycle.cs b/src/Avalonia.Controls/IApplicationLifecycle.cs index beb97a44ae..51f554c078 100644 --- a/src/Avalonia.Controls/IApplicationLifecycle.cs +++ b/src/Avalonia.Controls/IApplicationLifecycle.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index e7f2903249..87b66d5f81 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -1,12 +1,9 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Layout; -using Avalonia.LogicalTree; -using Avalonia.Styling; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -23,4 +20,4 @@ namespace Avalonia.Controls { new IControl Parent { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs index e118ec043c..0722a22f08 100644 --- a/src/Avalonia.Controls/IMenu.cs +++ b/src/Avalonia.Controls/IMenu.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Controls.Platform; +using Avalonia.Controls.Platform; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs index c9fc04dcc8..ee9d0fd6b6 100644 --- a/src/Avalonia.Controls/IMenuElement.cs +++ b/src/Avalonia.Controls/IMenuElement.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Avalonia.Input; namespace Avalonia.Controls diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 2657b1949f..132d565cb7 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -1,6 +1,4 @@ -using System; - -namespace Avalonia.Controls +namespace Avalonia.Controls { /// /// Represents a . diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Controls/IScrollable.cs index 9bbd0d8518..204e918d7b 100644 --- a/src/Avalonia.Controls/IScrollable.cs +++ b/src/Avalonia.Controls/IScrollable.cs @@ -1,8 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/IVirtualizingPanel.cs b/src/Avalonia.Controls/IVirtualizingPanel.cs index 2d8dcb42e3..cba196dfa8 100644 --- a/src/Avalonia.Controls/IVirtualizingPanel.cs +++ b/src/Avalonia.Controls/IVirtualizingPanel.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// Gets or sets the controller for the virtualizing panel. /// /// - /// A virtualizing controller is responsible for maintaing the controls in the virtualizing + /// A virtualizing controller is responsible for maintaining the controls in the virtualizing /// panel. This property will be set by the controller when virtualization is initialized. /// Note that this property may remain null if the panel is added to a control that does /// not act as a virtualizing controller. diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 676e0af3de..9d4cbb9260 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -365,7 +365,7 @@ namespace Avalonia.Controls } /// - /// Caled when the property changes. + /// Called when the property changes. /// /// The event args. protected virtual void ItemsChanged(AvaloniaPropertyChangedEventArgs e) diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs index edd7ed489e..00fca385a0 100644 --- a/src/Avalonia.Controls/Menu.cs +++ b/src/Avalonia.Controls/Menu.cs @@ -56,7 +56,7 @@ namespace Avalonia.Controls /// /// Initializes a new instance of the class. /// - /// The menu iteraction handler. + /// The menu interaction handler. public Menu(IMenuInteractionHandler interactionHandler) { Contract.Requires(interactionHandler != null); diff --git a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs index 95193c0432..6519fa4c14 100644 --- a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs +++ b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs @@ -9,7 +9,6 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; using Avalonia.LogicalTree; -using Avalonia.Styling; namespace Avalonia.Controls.Mixins { diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 59d2949b81..de68eb0ab0 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -526,7 +526,7 @@ namespace Avalonia.Controls return result; } - // Since the conversion from Value to text using a FormartString may not be parsable, + // Since the conversion from Value to text using a FormatString may not be parsable, // we verify that the already existing text is not the exact same value. var currentValueText = ConvertValueToText(); if (Equals(currentValueText, text)) @@ -571,7 +571,7 @@ namespace Avalonia.Controls } /// - /// Called by OnSpin when the spin direction is SpinDirection.Descrease. + /// Called by OnSpin when the spin direction is SpinDirection.Decrease. /// private void OnDecrement() { @@ -890,7 +890,7 @@ namespace Avalonia.Controls if (_isTextChangedFromUI && !parsedTextIsValid) { // Text input was made from the user and the text - // repesents an invalid value. Disable the spinner in this case. + // represents an invalid value. Disable the spinner in this case. if (Spinner != null) { Spinner.ValidSpinDirection = ValidSpinDirections.None; @@ -995,4 +995,4 @@ namespace Avalonia.Controls return false; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs index 420c56111f..e958b7aa15 100644 --- a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs +++ b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Avalonia.Platform { diff --git a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs index 342d3dd1c9..dd8503f768 100644 --- a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs @@ -1,7 +1,4 @@ -using System; -using Avalonia.Input; - -namespace Avalonia.Controls.Platform +namespace Avalonia.Controls.Platform { /// /// Handles user interaction for menus. diff --git a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs index a84a58906e..ecbc6d2234 100644 --- a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs +++ b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Media.Imaging; +using System.IO; namespace Avalonia.Platform { diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 60e25d2be6..ba022ab8d4 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -20,12 +20,12 @@ namespace Avalonia.Platform public interface ITopLevelImpl : IDisposable { /// - /// Gets the client size of the toplevel. + /// Gets the client size of the top level. /// Size ClientSize { get; } /// - /// Gets the scaling factor for the toplevel. + /// Gets the scaling factor for the top level. /// double Scaling { get; } @@ -42,38 +42,38 @@ namespace Avalonia.Platform IEnumerable Surfaces { get; } /// - /// Gets or sets a method called when the toplevel receives input. + /// Gets or sets a method called when the top level receives input. /// Action Input { get; set; } /// - /// Gets or sets a method called when the toplevel requires painting. + /// Gets or sets a method called when the top level requires painting. /// Action Paint { get; set; } /// - /// Gets or sets a method called when the toplevel is resized. + /// Gets or sets a method called when the top level is resized. /// Action Resized { get; set; } /// - /// Gets or sets a method called when the toplevel's scaling changes. + /// Gets or sets a method called when the top level's scaling changes. /// Action ScalingChanged { get; set; } /// - /// Creates a new renderer for the toplevel. + /// Creates a new renderer for the top level. /// - /// The toplevel. + /// The top level. IRenderer CreateRenderer(IRenderRoot root); /// - /// Invalidates a rect on the toplevel. + /// Invalidates a rect on the top level. /// void Invalidate(Rect rect); /// - /// Sets the for the toplevel. + /// Sets the for the top level. /// void SetInputRoot(IInputRoot inputRoot); @@ -92,7 +92,7 @@ namespace Avalonia.Platform Point PointToScreen(Point point); /// - /// Sets the cursor associated with the toplevel. + /// Sets the cursor associated with the top level. /// /// The cursor. Use null for default cursor void SetCursor(IPlatformHandle cursor); @@ -103,7 +103,7 @@ namespace Avalonia.Platform Action Closed { get; set; } /// - /// Gets a mouse device associated with toplevel + /// Gets a mouse device associated with top level /// [CanBeNull] IMouseDevice MouseDevice { get; } diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 9ba68f584e..e788b3c73e 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -6,7 +6,7 @@ namespace Avalonia.Platform public interface IWindowBaseImpl : ITopLevelImpl { /// - /// Shows the toplevel. + /// Shows the top level. /// void Show(); @@ -62,7 +62,7 @@ namespace Avalonia.Platform Size MaxClientSize { get; } /// - /// Sets the client size of the toplevel. + /// Sets the client size of the top level. /// void Resize(Size clientSize); @@ -82,4 +82,4 @@ namespace Avalonia.Platform /// IScreenImpl Screen { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs index 5dcd0a39e8..5c2c1a8da3 100644 --- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs +++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Avalonia.Platform { public interface IWindowingPlatform diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 4dc96a074d..62cd012d51 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Platform; +using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 1d5a187a73..ba2c7c91d8 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Animation; -using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; using Avalonia.Controls.Utils; using Avalonia.Data; @@ -251,4 +250,4 @@ namespace Avalonia.Controls.Presenters } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Presenters/IPresenter.cs b/src/Avalonia.Controls/Presenters/IPresenter.cs index e06be4e82b..f78ba4c19d 100644 --- a/src/Avalonia.Controls/Presenters/IPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IPresenter.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using Avalonia.Controls.Primitives; -using Avalonia.Styling; namespace Avalonia.Controls.Presenters { diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs index e293cff211..c5344b29d9 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs @@ -8,7 +8,6 @@ using System.Reactive.Linq; using Avalonia.Controls.Primitives; using Avalonia.Controls.Utils; using Avalonia.Input; -using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters { diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index b98f26b87f..f31a48b2a0 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -390,7 +390,7 @@ namespace Avalonia.Controls.Presenters /// The delta of the move. /// /// If the move is less than a page, then this method moves the containers for the items - /// that are still visible to the correct place, and recyles and moves the others. For + /// that are still visible to the correct place, and recycles and moves the others. For /// example: if there are 20 items and 10 containers visible and the user scrolls 5 /// items down, then the bottom 5 containers will be moved to the top and the top 5 will /// be moved to the bottom and recycled to display the newly visible item. Updates diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 8d0c6f16cb..2ef7941b55 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -7,7 +7,6 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Controls.Primitives; using Avalonia.Input; -using Avalonia.Layout; using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters @@ -319,4 +318,4 @@ namespace Avalonia.Controls.Presenters } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 636c836da5..3181e76c3f 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -4,7 +4,6 @@ using System; using System.Reactive.Linq; using Avalonia.Media; -using Avalonia.Styling; using Avalonia.Threading; using Avalonia.VisualTree; @@ -116,7 +115,7 @@ namespace Avalonia.Controls.Presenters var start = Math.Min(selectionStart, selectionEnd); var length = Math.Max(selectionStart, selectionEnd) - start; - // issue #600: set constaint before any FormattedText manipulation + // issue #600: set constraint before any FormattedText manipulation // see base.Render(...) implementation FormattedText.Constraint = Bounds.Size; diff --git a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs index 82ab929d53..c5aa73e56a 100644 --- a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs @@ -1,8 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Linq; using Avalonia.Controls.Mixins; using Avalonia.Controls.Presenters; diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 005717d681..5e79711f5a 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -6,11 +6,10 @@ using System.Linq; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Metadata; -using Avalonia.Rendering; using Avalonia.VisualTree; -using Avalonia.Layout; namespace Avalonia.Controls.Primitives { @@ -473,4 +472,4 @@ namespace Avalonia.Controls.Primitives } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 0ae4be5550..fdec9febd3 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -2,12 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Linq; using Avalonia.Controls.Platform; using Avalonia.Controls.Presenters; using Avalonia.Interactivity; -using Avalonia.Layout; -using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; diff --git a/src/Avalonia.Controls/Primitives/RangeBase.cs b/src/Avalonia.Controls/Primitives/RangeBase.cs index b46562e99e..76df94cdb8 100644 --- a/src/Avalonia.Controls/Primitives/RangeBase.cs +++ b/src/Avalonia.Controls/Primitives/RangeBase.cs @@ -130,7 +130,7 @@ namespace Avalonia.Controls.Primitives } /// - /// Throws an exception if the double valus is NaN or Inf. + /// Throws an exception if the double value is NaN or Inf. /// /// The value. /// The name of the property being set. diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 5451cf0701..bb39b005cc 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -11,7 +11,6 @@ using Avalonia.Controls.Generators; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Metadata; using Avalonia.Styling; using Avalonia.VisualTree; diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs index b0f9ab1f85..b01ddd5dba 100644 --- a/src/Avalonia.Controls/Primitives/Thumb.cs +++ b/src/Avalonia.Controls/Primitives/Thumb.cs @@ -4,7 +4,6 @@ using System; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Layout; namespace Avalonia.Controls.Primitives { diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index b7db352c74..085db75ce1 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -1,12 +1,8 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Reactive.Linq; -using Avalonia.Animation; using Avalonia.Controls.Primitives; -using Avalonia.Layout; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs index 9c0f3464a5..e116316904 100644 --- a/src/Avalonia.Controls/Remote/RemoteServer.cs +++ b/src/Avalonia.Controls/Remote/RemoteServer.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls.Embedding; using Avalonia.Controls.Remote.Server; using Avalonia.Platform; diff --git a/src/Avalonia.Controls/Remote/RemoteWidget.cs b/src/Avalonia.Controls/Remote/RemoteWidget.cs index ea8c3ebe52..2d4f2e6b52 100644 --- a/src/Avalonia.Controls/Remote/RemoteWidget.cs +++ b/src/Avalonia.Controls/Remote/RemoteWidget.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using Avalonia.Input; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Remote.Protocol; @@ -76,4 +75,4 @@ namespace Avalonia.Controls.Remote base.Render(context); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index cf4cec9268..b302f2f5ec 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls.Embedding.Offscreen; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; @@ -94,10 +91,10 @@ namespace Avalonia.Controls.Remote.Server RenderIfNeeded(); } - protected virtual Size Measure(Size constaint) + protected virtual Size Measure(Size constraint) { var l = (ILayoutable) InputRoot; - l.Measure(constaint); + l.Measure(constraint); return l.DesiredSize; } diff --git a/src/Avalonia.Controls/Separator.cs b/src/Avalonia.Controls/Separator.cs index 2028a5cfbb..84b2a33d7b 100644 --- a/src/Avalonia.Controls/Separator.cs +++ b/src/Avalonia.Controls/Separator.cs @@ -1,11 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls.Primitives; namespace Avalonia.Controls diff --git a/src/Avalonia.Controls/Shapes/Path.cs b/src/Avalonia.Controls/Shapes/Path.cs index 08bed79b3a..e0952d3e9b 100644 --- a/src/Avalonia.Controls/Shapes/Path.cs +++ b/src/Avalonia.Controls/Shapes/Path.cs @@ -1,7 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; using Avalonia.Media; namespace Avalonia.Controls.Shapes diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index a1848a95b1..604051ef28 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -4,7 +4,6 @@ using System; using System.Reflection; using Avalonia.Collections; -using Avalonia.Controls; using Avalonia.Media; namespace Avalonia.Controls.Shapes diff --git a/src/Avalonia.Controls/Spinner.cs b/src/Avalonia.Controls/Spinner.cs index e00ff3823c..5ee13f45c4 100644 --- a/src/Avalonia.Controls/Spinner.cs +++ b/src/Avalonia.Controls/Spinner.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Avalonia.Interactivity; namespace Avalonia.Controls @@ -54,7 +52,7 @@ namespace Avalonia.Controls public SpinDirection Direction { get; } /// - /// Get or set whheter the spin event originated from a mouse wheel event. + /// Get or set whether the spin event originated from a mouse wheel event. /// public bool UsingMouseWheel{ get; } diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs index 7154c0e558..9339aa6924 100644 --- a/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs +++ b/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs @@ -42,7 +42,7 @@ namespace Avalonia.Controls.Templates } /// - /// Casts a stongly typed match function to a weakly typed one. + /// Casts a strongly typed match function to a weakly typed one. /// /// The strongly typed function. /// The weakly typed function. @@ -52,7 +52,7 @@ namespace Avalonia.Controls.Templates } /// - /// Casts a stongly typed build function to a weakly typed one. + /// Casts a strongly typed build function to a weakly typed one. /// /// The strong data type. /// The strongly typed function. @@ -62,4 +62,4 @@ namespace Avalonia.Controls.Templates return o => f((T)o); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Templates/FuncMemberSelector.cs b/src/Avalonia.Controls/Templates/FuncMemberSelector.cs index b8f2c2e311..5ab186261e 100644 --- a/src/Avalonia.Controls/Templates/FuncMemberSelector.cs +++ b/src/Avalonia.Controls/Templates/FuncMemberSelector.cs @@ -25,11 +25,11 @@ namespace Avalonia.Controls.Templates /// /// Selects a member of an object. /// - /// The obeject. + /// The object. /// The selected member. public object Select(object o) { return (o is TObject) ? _selector((TObject)o) : default(TMember); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs index 2e55dd3552..e7c9cf8608 100644 --- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs +++ b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs @@ -9,7 +9,7 @@ using Avalonia.Data; namespace Avalonia.Controls.Templates { /// - /// A template used to build hierachical data. + /// A template used to build hierarchical data. /// public class FuncTreeDataTemplate : FuncDataTemplate, ITreeDataTemplate { diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs index d7d6a87769..4ca96f60bd 100644 --- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs +++ b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs @@ -7,7 +7,7 @@ using System.Collections; namespace Avalonia.Controls.Templates { /// - /// A template used to build hierachical data. + /// A template used to build hierarchical data. /// /// The type of the template's data. public class FuncTreeDataTemplate : FuncTreeDataTemplate diff --git a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs index 5cc12581d4..267938b3bd 100644 --- a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs +++ b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs @@ -1,7 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; namespace Avalonia.Controls.Templates { diff --git a/src/Avalonia.Controls/Templates/IMemberSelector.cs b/src/Avalonia.Controls/Templates/IMemberSelector.cs index b7172fa492..e1ec42a849 100644 --- a/src/Avalonia.Controls/Templates/IMemberSelector.cs +++ b/src/Avalonia.Controls/Templates/IMemberSelector.cs @@ -11,8 +11,8 @@ namespace Avalonia.Controls.Templates /// /// Selects a member of an object. /// - /// The obeject. + /// The object. /// The selected member. object Select(object o); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs b/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs index 580839be97..9ed8987681 100644 --- a/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs +++ b/src/Avalonia.Controls/Templates/ITreeDataTemplate.cs @@ -6,7 +6,7 @@ using Avalonia.Data; namespace Avalonia.Controls.Templates { /// - /// Interface representing a template used to build hierachical data. + /// Interface representing a template used to build hierarchical data. /// public interface ITreeDataTemplate : IDataTemplate { @@ -20,4 +20,4 @@ namespace Avalonia.Controls.Templates /// InstancedBinding ItemsSelector(object item); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index fd46efa76f..e91d2e8fa7 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -21,7 +21,7 @@ namespace Avalonia.Controls public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); - // TODO: Define these attached properties elswhere (e.g. on a Text class) and AddOwner + // TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner // them into TextBlock. /// @@ -406,4 +406,4 @@ namespace Avalonia.Controls InvalidateMeasure(); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index 10e964d014..28d1ba5e0f 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -55,7 +55,7 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterAttached("ShowDelay", 400); /// - /// Stores the curernt instance in the control. + /// Stores the current instance in the control. /// private static readonly AttachedProperty ToolTipProperty = AvaloniaProperty.RegisterAttached("ToolTip"); diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index bfd7ef0f33..384a9db0cf 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -5,7 +5,7 @@ using Avalonia.Threading; namespace Avalonia.Controls { /// - /// Handeles interaction with controls. + /// Handles interaction with controls. /// internal sealed class ToolTipService { @@ -95,4 +95,4 @@ namespace Avalonia.Controls _timer = null; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 9fdc097c3f..1161ded25f 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -2,9 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Reactive.Disposables; using System.Reactive.Linq; -using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Input.Raw; @@ -225,7 +223,7 @@ namespace Avalonia.Controls protected virtual IRenderTarget CreateRenderTarget() { if(PlatformImpl == null) - throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)"); + throw new InvalidOperationException("Can't create render target, PlatformImpl is null (might be already disposed)"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 4575fa767b..3003dab85e 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -1,7 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; using System.Linq; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; @@ -14,7 +13,7 @@ using Avalonia.VisualTree; namespace Avalonia.Controls { /// - /// Displays a hierachical tree of data. + /// Displays a hierarchical tree of data. /// public class TreeView : ItemsControl, ICustomKeyboardNavigation { diff --git a/src/Avalonia.Controls/Utils/AncestorFinder.cs b/src/Avalonia.Controls/Utils/AncestorFinder.cs index 6ee284e05d..d02202ca02 100644 --- a/src/Avalonia.Controls/Utils/AncestorFinder.cs +++ b/src/Avalonia.Controls/Utils/AncestorFinder.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; +using System; using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Avalonia.Controls.Utils { diff --git a/src/Avalonia.Controls/Utils/GridLayout.cs b/src/Avalonia.Controls/Utils/GridLayout.cs index 10a94a8c82..363428b289 100644 --- a/src/Avalonia.Controls/Utils/GridLayout.cs +++ b/src/Avalonia.Controls/Utils/GridLayout.cs @@ -376,7 +376,7 @@ namespace Avalonia.Controls.Utils // 1. Determine all one-span column's desired widths or row's desired heights. // 2. Order the multi-span conventions by its last index // (Notice that the sorted data is much smaller than the source.) - // 3. Determine each multi-span last index by calculating the maximun desired size. + // 3. Determine each multi-span last index by calculating the maximum desired size. // Before we determine the behavior of this method, we just aggregate the one-span * columns. diff --git a/src/Avalonia.Controls/Utils/IEnumerableUtils.cs b/src/Avalonia.Controls/Utils/IEnumerableUtils.cs index 361857aeb7..40ebd406c3 100644 --- a/src/Avalonia.Controls/Utils/IEnumerableUtils.cs +++ b/src/Avalonia.Controls/Utils/IEnumerableUtils.cs @@ -3,7 +3,6 @@ using System; using System.Collections; -using System.Globalization; using System.Linq; namespace Avalonia.Controls.Utils diff --git a/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs b/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs index 43c8a5aa6c..4d814170c6 100644 --- a/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs +++ b/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs @@ -4,15 +4,12 @@ // All other rights reserved. using System; +using System.Collections; using System.Linq; -using System.Collections.Generic; -using System.Text; using Avalonia.Controls.Primitives; -using Avalonia.Interactivity; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.LogicalTree; -using System.Collections; -using System.Diagnostics; namespace Avalonia.Controls.Utils { diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index bd07cf6740..39b4f05545 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -86,7 +86,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(Icon)); /// - /// Defines the proeprty. + /// Defines the property. /// public static readonly DirectProperty WindowStartupLocationProperty = AvaloniaProperty.RegisterDirect( @@ -100,7 +100,7 @@ namespace Avalonia.Controls private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; - private WindowStartupLocation _windowStartupLoction; + private WindowStartupLocation _windowStartupLocation; /// /// Initializes static members of the class. @@ -237,8 +237,8 @@ namespace Avalonia.Controls /// public WindowStartupLocation WindowStartupLocation { - get { return _windowStartupLoction; } - set { SetAndRaise(WindowStartupLocationProperty, ref _windowStartupLoction, value); } + get { return _windowStartupLocation; } + set { SetAndRaise(WindowStartupLocationProperty, ref _windowStartupLocation, value); } } /// @@ -409,7 +409,7 @@ namespace Avalonia.Controls /// The type of the result produced by the dialog. /// /// . - /// A task that can be used to retrive the result of the dialog when it closes. + /// A task that can be used to retrieve the result of the dialog when it closes. /// public Task ShowDialog() { diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index c0b664ebc3..f609432545 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; diff --git a/src/Avalonia.Controls/WindowIcon.cs b/src/Avalonia.Controls/WindowIcon.cs index 26195e6d76..9e4329372a 100644 --- a/src/Avalonia.Controls/WindowIcon.cs +++ b/src/Avalonia.Controls/WindowIcon.cs @@ -1,11 +1,6 @@ -using Avalonia.Platform; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.IO; using Avalonia.Media.Imaging; +using Avalonia.Platform; namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/WrapPanel.cs b/src/Avalonia.Controls/WrapPanel.cs index 84d3cc791e..8ee0636124 100644 --- a/src/Avalonia.Controls/WrapPanel.cs +++ b/src/Avalonia.Controls/WrapPanel.cs @@ -132,7 +132,7 @@ namespace Avalonia.Controls double accumulatedV = 0; var uvFinalSize = CreateUVSize(finalSize); var lineSize = CreateUVSize(); - int firstChildInLineindex = 0; + int firstChildInLineIndex = 0; for (int index = 0; index < Children.Count; index++) { var child = Children[index]; @@ -144,32 +144,32 @@ namespace Avalonia.Controls } else // moving to next line { - var controlsInLine = GetContolsBetween(firstChildInLineindex, index); + var controlsInLine = GetControlsBetween(firstChildInLineIndex, index); ArrangeLine(accumulatedV, lineSize.V, controlsInLine); accumulatedV += lineSize.V; lineSize = childSize; - firstChildInLineindex = index; + firstChildInLineIndex = index; } } - if (firstChildInLineindex < Children.Count) + if (firstChildInLineIndex < Children.Count) { - var controlsInLine = GetContolsBetween(firstChildInLineindex, Children.Count); + var controlsInLine = GetControlsBetween(firstChildInLineIndex, Children.Count); ArrangeLine(accumulatedV, lineSize.V, controlsInLine); } return finalSize; } - private IEnumerable GetContolsBetween(int first, int last) + private IEnumerable GetControlsBetween(int first, int last) { return Children.Skip(first).Take(last - first); } - private void ArrangeLine(double v, double lineV, IEnumerable contols) + private void ArrangeLine(double v, double lineV, IEnumerable controls) { double u = 0; bool isHorizontal = (Orientation == Orientation.Horizontal); - foreach (var child in contols) + foreach (var child in controls) { var childSize = CreateUVSize(child.DesiredSize); var x = isHorizontal ? u : v; @@ -181,9 +181,9 @@ namespace Avalonia.Controls } } /// - /// Used to not not write sepearate code for horizontal and vertical orientation. + /// Used to not not write separate code for horizontal and vertical orientation. /// U is direction in line. (x if orientation is horizontal) - /// V is direction of lines. (y if orientation is horizonral) + /// V is direction of lines. (y if orientation is horizontal) /// [DebuggerDisplay("U = {U} V = {V}")] private struct UVSize From 993f0a080ad3155c5c526c2ebb377b45797e8911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sat, 1 Sep 2018 20:07:00 +0200 Subject: [PATCH 033/135] Adds missing newline --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 349ae80362..a4dc35f736 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -41,6 +41,7 @@ namespace Avalonia.Controls.Presenters { AffectsRender(PasswordCharProperty); } + public TextPresenter() { _caretTimer = new DispatcherTimer(); From 47b78a413b842b2f46c2eba7d8ba10fe520382f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sun, 2 Sep 2018 09:16:10 +0200 Subject: [PATCH 034/135] Reverts 'top level' comment back to 'toplevel' --- .../Platform/ITopLevelImpl.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index ba022ab8d4..60e25d2be6 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -20,12 +20,12 @@ namespace Avalonia.Platform public interface ITopLevelImpl : IDisposable { /// - /// Gets the client size of the top level. + /// Gets the client size of the toplevel. /// Size ClientSize { get; } /// - /// Gets the scaling factor for the top level. + /// Gets the scaling factor for the toplevel. /// double Scaling { get; } @@ -42,38 +42,38 @@ namespace Avalonia.Platform IEnumerable Surfaces { get; } /// - /// Gets or sets a method called when the top level receives input. + /// Gets or sets a method called when the toplevel receives input. /// Action Input { get; set; } /// - /// Gets or sets a method called when the top level requires painting. + /// Gets or sets a method called when the toplevel requires painting. /// Action Paint { get; set; } /// - /// Gets or sets a method called when the top level is resized. + /// Gets or sets a method called when the toplevel is resized. /// Action Resized { get; set; } /// - /// Gets or sets a method called when the top level's scaling changes. + /// Gets or sets a method called when the toplevel's scaling changes. /// Action ScalingChanged { get; set; } /// - /// Creates a new renderer for the top level. + /// Creates a new renderer for the toplevel. /// - /// The top level. + /// The toplevel. IRenderer CreateRenderer(IRenderRoot root); /// - /// Invalidates a rect on the top level. + /// Invalidates a rect on the toplevel. /// void Invalidate(Rect rect); /// - /// Sets the for the top level. + /// Sets the for the toplevel. /// void SetInputRoot(IInputRoot inputRoot); @@ -92,7 +92,7 @@ namespace Avalonia.Platform Point PointToScreen(Point point); /// - /// Sets the cursor associated with the top level. + /// Sets the cursor associated with the toplevel. /// /// The cursor. Use null for default cursor void SetCursor(IPlatformHandle cursor); @@ -103,7 +103,7 @@ namespace Avalonia.Platform Action Closed { get; set; } /// - /// Gets a mouse device associated with top level + /// Gets a mouse device associated with toplevel /// [CanBeNull] IMouseDevice MouseDevice { get; } From 158d2d31b3d6d193e22ff7031eeb0910bf242116 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 2 Sep 2018 23:54:46 +0200 Subject: [PATCH 035/135] Add RoutedEventRegistry. Fixes #1846. --- src/Avalonia.Interactivity/RoutedEvent.cs | 8 +- .../RoutedEventRegistry.cs | 90 +++++++++++++++++++ .../RoutedEventRegistryTests.cs | 49 ++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.Interactivity/RoutedEventRegistry.cs create mode 100644 tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs diff --git a/src/Avalonia.Interactivity/RoutedEvent.cs b/src/Avalonia.Interactivity/RoutedEvent.cs index 61bb567d54..2d752133c1 100644 --- a/src/Avalonia.Interactivity/RoutedEvent.cs +++ b/src/Avalonia.Interactivity/RoutedEvent.cs @@ -72,7 +72,9 @@ namespace Avalonia.Interactivity { Contract.Requires(name != null); - return new RoutedEvent(name, routingStrategy, typeof(TOwner)); + var routedEvent = new RoutedEvent(name, routingStrategy, typeof(TOwner)); + RoutedEventRegistry.Instance.Register(typeof(TOwner), routedEvent); + return routedEvent; } public static RoutedEvent Register( @@ -83,7 +85,9 @@ namespace Avalonia.Interactivity { Contract.Requires(name != null); - return new RoutedEvent(name, routingStrategy, ownerType); + var routedEvent = new RoutedEvent(name, routingStrategy, ownerType); + RoutedEventRegistry.Instance.Register(ownerType, routedEvent); + return routedEvent; } public IDisposable AddClassHandler( diff --git a/src/Avalonia.Interactivity/RoutedEventRegistry.cs b/src/Avalonia.Interactivity/RoutedEventRegistry.cs new file mode 100644 index 0000000000..34c970a806 --- /dev/null +++ b/src/Avalonia.Interactivity/RoutedEventRegistry.cs @@ -0,0 +1,90 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Collections.Generic; + +namespace Avalonia.Interactivity +{ + /// + /// Tracks registered s. + /// + public class RoutedEventRegistry + { + private readonly Dictionary> _registeredRoutedEvents = + new Dictionary>(); + + /// + /// Gets the instance. + /// + public static RoutedEventRegistry Instance { get; } + = new RoutedEventRegistry(); + + /// + /// Registers a on a type. + /// + /// The type. + /// The event. + /// + /// You won't usually want to call this method directly, instead use the + /// + /// method. + /// + public void Register(Type type, RoutedEvent @event) + { + Contract.Requires(type != null); + Contract.Requires(@event != null); + + if (!_registeredRoutedEvents.TryGetValue(type, out var list)) + { + list = new List(); + _registeredRoutedEvents.Add(type, list); + } + list.Add(@event); + } + + /// + /// Returns all routed events, that are currently registered in the event registry. + /// + /// All routed events, that are currently registered in the event registry. + public IEnumerable GetAllRegistered() + { + foreach (var events in _registeredRoutedEvents.Values) + { + foreach (var e in events) + { + yield return e; + } + } + } + + /// + /// Returns all routed events registered with the provided type. + /// If the type is not found or does not provide any routed events, an empty list is returned. + /// + /// The type. + /// All routed events registered with the provided type. + public IReadOnlyList GetRegistered(Type type) + { + Contract.Requires(type != null); + + if (_registeredRoutedEvents.TryGetValue(type, out var events)) + { + return events; + } + + return Array.Empty(); + } + + /// + /// Returns all routed events registered with the provided type. + /// If the type is not found or does not provide any routed events, an empty list is returned. + /// + /// The type. + /// All routed events registered with the provided type. + public IReadOnlyList GetRegistered() + { + return GetRegistered(typeof(TOwner)); + } + } +} diff --git a/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs b/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs new file mode 100644 index 0000000000..b9ebdea064 --- /dev/null +++ b/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Input; +using Xunit; + +namespace Avalonia.Interactivity.UnitTests +{ + public class RoutedEventRegistryTests + { + [Fact] + public void Pointer_Events_Should_Be_Registered() + { + var expectedEvents = new List { InputElement.PointerPressedEvent, InputElement.PointerReleasedEvent }; + var registeredEvents = RoutedEventRegistry.Instance.GetRegistered(); + Assert.Contains(registeredEvents, expectedEvents.Contains); + } + + [Fact] + public void ClickEvent_Should_Be_Registered_On_Button() + { + var expectedEvents = new List { Button.ClickEvent }; + var registeredEvents = RoutedEventRegistry.Instance.GetRegistered