From a3bd88128aaa6a28e944d2226a46ff677b5700e8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 4 Jul 2021 03:46:45 -0400 Subject: [PATCH 1/9] Update RenderDemo --- samples/RenderDemo/Pages/AnimationsPage.xaml | 78 +++++++++++++++++ samples/RenderDemo/Pages/TransitionsPage.xaml | 84 ++++++++++++++++++- 2 files changed, 158 insertions(+), 4 deletions(-) diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 21c7d68b5d..48fca61d09 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -161,6 +161,81 @@ + + + + + + @@ -181,6 +256,9 @@ + + + diff --git a/samples/RenderDemo/Pages/TransitionsPage.xaml b/samples/RenderDemo/Pages/TransitionsPage.xaml index 1985074b0f..71b6ea0713 100644 --- a/samples/RenderDemo/Pages/TransitionsPage.xaml +++ b/samples/RenderDemo/Pages/TransitionsPage.xaml @@ -167,13 +167,80 @@ + + + + + + + + + + + + @@ -202,6 +269,15 @@ + + + + + + + + + From 9f7a5de29edfced86e19a72d1fe015e01bc81fb5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 4 Jul 2021 03:51:28 -0400 Subject: [PATCH 2/9] Add RelativePointAnimator --- .../Animators/RelativePointAnimator.cs | 20 +++++++++++++++++++ .../Transitions/RelativePointTransition.cs | 11 ++++++++++ src/Avalonia.Visuals/RelativePoint.cs | 7 +++++++ 3 files changed, 38 insertions(+) create mode 100644 src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs create mode 100644 src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs new file mode 100644 index 0000000000..40fa4503f0 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs @@ -0,0 +1,20 @@ +namespace Avalonia.Animation.Animators +{ + /// + /// Animator that handles properties. + /// + public class RelativePointAnimator : Animator + { + private static readonly PointAnimator s_pointAnimator = new PointAnimator(); + + public override RelativePoint Interpolate(double progress, RelativePoint oldValue, RelativePoint newValue) + { + if (oldValue.Unit != newValue.Unit) + { + return progress >= 1 ? newValue : oldValue; + } + + return new RelativePoint(s_pointAnimator.Interpolate(progress, oldValue.Point, newValue.Point), oldValue.Unit); + } + } +} diff --git a/src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs new file mode 100644 index 0000000000..4a7bfa8384 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs @@ -0,0 +1,11 @@ +using Avalonia.Animation.Animators; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with type. + /// + public class RelativePointTransition : AnimatorDrivenTransition + { + } +} diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Visuals/RelativePoint.cs index 097ea69be4..497820ec65 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -1,5 +1,7 @@ using System; using System.Globalization; + +using Avalonia.Animation.Animators; using Avalonia.Utilities; namespace Avalonia @@ -45,6 +47,11 @@ namespace Avalonia private readonly RelativeUnit _unit; + static RelativePoint() + { + Animation.Animation.RegisterAnimator(prop => typeof(RelativePoint).IsAssignableFrom(prop.PropertyType)); + } + /// /// Initializes a new instance of the struct. /// From ab071f1ba0f77e020a0f943be897e63fdf8dd8bc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 4 Jul 2021 03:52:11 -0400 Subject: [PATCH 3/9] Add IGradientBrushAnimator --- .../Animation/Animators/BaseBrushAnimator.cs | 35 ++++++--- .../Animators/GradientBrushAnimator.cs | 74 +++++++++++++++++++ .../Animators/SolidColorBrushAnimator.cs | 4 +- .../Animation/Transitions/BrushTransition.cs | 32 ++++---- src/Avalonia.Visuals/Media/GradientBrush.cs | 3 + 5 files changed, 120 insertions(+), 28 deletions(-) create mode 100644 src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index 508891fd72..be674269bf 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -4,6 +4,8 @@ using System.Reactive.Disposables; using Avalonia.Logging; using Avalonia.Media; +#nullable enable + namespace Avalonia.Animation.Animators { /// @@ -12,9 +14,9 @@ namespace Avalonia.Animation.Animators /// redirect them to the properly registered /// animators in this class. /// - public class BaseBrushAnimator : Animator + public class BaseBrushAnimator : Animator { - private IAnimator _targetAnimator; + private IAnimator? _targetAnimator; private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new List<(Func Match, Type AnimatorType)>(); @@ -31,7 +33,7 @@ namespace Avalonia.Animation.Animators /// The type of the animator to instantiate. /// public static void RegisterBrushAnimator(Func condition) - where TAnimator : IAnimator + where TAnimator : IAnimator, new() { _brushAnimators.Insert(0, (condition, typeof(TAnimator))); } @@ -40,20 +42,18 @@ namespace Avalonia.Animation.Animators public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable match, Action onComplete) { - foreach (var valueType in _brushAnimators) - { - if (!valueType.Match(this[0].Value.GetType())) continue; - - _targetAnimator = (IAnimator)Activator.CreateInstance(valueType.AnimatorType); + _targetAnimator = CreateAnimatorFromType(this[0].Value.GetType()); + if (_targetAnimator != null) + { foreach (var keyframe in this) { _targetAnimator.Add(keyframe); } _targetAnimator.Property = this.Property; - - return _targetAnimator.Apply(animation, control, clock, match, onComplete); + + return _targetAnimator.Apply(animation, control, clock, match, onComplete); } Logger.TryGet(LogEventLevel.Error, LogArea.Animations)?.Log( @@ -64,6 +64,19 @@ namespace Avalonia.Animation.Animators } /// - public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) => null; + public override IBrush? Interpolate(double progress, IBrush? oldValue, IBrush? newValue) => null; + + internal static IAnimator? CreateAnimatorFromType(Type type) + { + foreach (var (match, animatorType) in _brushAnimators) + { + if (!match(type)) + continue; + + return (IAnimator)Activator.CreateInstance(animatorType); + } + + return null; + } } } diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs new file mode 100644 index 0000000000..e51103b9b5 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Avalonia.Data; +using Avalonia.Media; +using Avalonia.Media.Immutable; + +namespace Avalonia.Animation.Animators +{ + /// + /// Animator that handles values. + /// + public class IGradientBrushAnimator : Animator + { + private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); + private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); + + public override IGradientBrush Interpolate(double progress, IGradientBrush oldValue, IGradientBrush newValue) + { + if (oldValue is null || newValue is null + || oldValue.GradientStops.Count != oldValue.GradientStops.Count) + { + return progress >= 1 ? newValue : oldValue; + } + + switch (oldValue) + { + case IRadialGradientBrush oldRadial when newValue is IRadialGradientBrush newRadial: + return new ImmutableRadialGradientBrush( + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.SpreadMethod, + s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), + s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), + s_doubleAnimator.Interpolate(progress, oldRadial.Radius, newRadial.Radius)); + + case IConicGradientBrush oldConic when newValue is IConicGradientBrush newConic: + return new ImmutableConicGradientBrush( + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.SpreadMethod, + s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), + s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); + + case ILinearGradientBrush oldLinear when newValue is ILinearGradientBrush newLinear: + return new ImmutableLinearGradientBrush( + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.SpreadMethod, + s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), + s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); + + default: + return progress >= 1 ? newValue : oldValue; + } + } + + public override IDisposable BindAnimation(Animatable control, IObservable instance) + { + return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); + } + + private IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) + { + // pool + return oldValue + .Zip(newValue, (f, s) => new ImmutableGradientStop( + s_doubleAnimator.Interpolate(progress, f.Offset, s.Offset), + ColorAnimator.InterpolateCore(progress, f.Color, s.Color))) + .ToArray(); + } + } +} diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index a56cc1de8c..ba2f2ae766 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -14,7 +14,7 @@ namespace Avalonia.Animation.Animators { if (oldValue is null || newValue is null) { - return oldValue; + return progress >= 1 ? newValue : oldValue; } return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); @@ -26,7 +26,7 @@ namespace Avalonia.Animation.Animators } } - [Obsolete] + [Obsolete("Use ISolidColorBrushAnimator instead")] public class SolidColorBrushAnimator : Animator { public override SolidColorBrush Interpolate(double progress, SolidColorBrush oldValue, SolidColorBrush newValue) diff --git a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs index cc5af1b4b1..7cc3f597b5 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; + using Avalonia.Animation.Animators; using Avalonia.Animation.Easings; using Avalonia.Media; @@ -9,34 +11,34 @@ namespace Avalonia.Animation { /// /// Transition class that handles with type. - /// Only values of will transition correctly at the moment. /// public class BrushTransition : Transition { - private static readonly ISolidColorBrushAnimator s_animator = new ISolidColorBrushAnimator(); - public override IObservable DoTransition(IObservable progress, IBrush? oldValue, IBrush? newValue) { - var oldSolidColorBrush = TryGetSolidColorBrush(oldValue); - var newSolidColorBrush = TryGetSolidColorBrush(newValue); + var type = oldValue?.GetType() ?? newValue?.GetType(); + if (type == null) + { + return new IncompatibleTransitionObservable(progress, Easing, oldValue, newValue); + } - if (oldSolidColorBrush != null && newSolidColorBrush != null) + var animator = BaseBrushAnimator.CreateAnimatorFromType(type); + if (animator == null) { - return new AnimatorTransitionObservable( - s_animator, progress, Easing, oldSolidColorBrush, newSolidColorBrush); + return new IncompatibleTransitionObservable(progress, Easing, oldValue, newValue); } - return new IncompatibleTransitionObservable(progress, Easing, oldValue, newValue); - } + var animatorType = animator.GetType(); + var animatorGenericArgument = animatorType.BaseType.GetGenericArguments().FirstOrDefault() ?? type; - private static ISolidColorBrush? TryGetSolidColorBrush(IBrush? brush) - { - if (brush is null) + var observableType = typeof(AnimatorTransitionObservable<,>).MakeGenericType(animatorGenericArgument, animatorType); + var observable = Activator.CreateInstance(observableType, animator, progress, Easing, oldValue, newValue) as IObservable; + if (observable == null) { - return Brushes.Transparent; + return new IncompatibleTransitionObservable(progress, Easing, oldValue, newValue); } - return brush as ISolidColorBrush; + return observable; } private class IncompatibleTransitionObservable : TransitionObservableBase diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Visuals/Media/GradientBrush.cs index 99923b8e06..4fb753a9de 100644 --- a/src/Avalonia.Visuals/Media/GradientBrush.cs +++ b/src/Avalonia.Visuals/Media/GradientBrush.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; + +using Avalonia.Animation.Animators; using Avalonia.Collections; using Avalonia.Metadata; @@ -28,6 +30,7 @@ namespace Avalonia.Media static GradientBrush() { + BaseBrushAnimator.RegisterBrushAnimator(match => typeof(IGradientBrush).IsAssignableFrom(match)); GradientStopsProperty.Changed.Subscribe(GradientStopsChanged); AffectsRender(SpreadMethodProperty); } From a885e673c831fe660323b1ff6e9e2ddce34e6d12 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 4 Jul 2021 20:37:48 -0400 Subject: [PATCH 4/9] Reflection free implementation with automatic convertion from solid color brush to gradient --- samples/RenderDemo/Pages/AnimationsPage.xaml | 34 ++++- .../Animation/Animators/BaseBrushAnimator.cs | 129 +++++++++++++++--- .../Animators/GradientBrushAnimator.cs | 65 +++++++-- .../Animators/SolidColorBrushAnimator.cs | 18 +-- .../Animation/Transitions/BrushTransition.cs | 40 +++--- src/Avalonia.Visuals/Media/GradientBrush.cs | 1 - src/Avalonia.Visuals/Media/SolidColorBrush.cs | 1 - 7 files changed, 225 insertions(+), 63 deletions(-) diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 48fca61d09..3981f7b51b 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -168,6 +168,9 @@ IterationCount="Infinite" PlaybackDirection="Alternate"> + + + @@ -175,6 +178,9 @@ + + + @@ -188,6 +194,31 @@ + + - @@ -226,16 +237,20 @@ - + + - + - + + + + @@ -247,10 +262,34 @@ + PlaybackDirection="Normal"> - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -258,9 +297,9 @@ - - - + + + diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 25b4a2826c..6481f815de 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -19,8 +19,7 @@ namespace Avalonia.Animation.Animators public override IGradientBrush? Interpolate(double progress, IGradientBrush? oldValue, IGradientBrush? newValue) { - if (oldValue is null || newValue is null - || oldValue.GradientStops.Count != newValue.GradientStops.Count) + if (oldValue is null || newValue is null) { return progress >= 0.5 ? newValue : oldValue; } @@ -64,13 +63,26 @@ namespace Avalonia.Animation.Animators private IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) { - var stops = new ImmutableGradientStop[oldValue.Count]; - for (int index = 0; index < oldValue.Count; index++) + var resultCount = Math.Max(oldValue.Count, newValue.Count); + var stops = new ImmutableGradientStop[resultCount]; + + for (int index = 0, oldIndex = 0, newIndex = 0; index < resultCount; index++) { stops[index] = new ImmutableGradientStop( - s_doubleAnimator.Interpolate(progress, oldValue[index].Offset, newValue[index].Offset), - ColorAnimator.InterpolateCore(progress, oldValue[index].Color, newValue[index].Color)); + s_doubleAnimator.Interpolate(progress, oldValue[oldIndex].Offset, newValue[newIndex].Offset), + ColorAnimator.InterpolateCore(progress, oldValue[oldIndex].Color, newValue[newIndex].Color)); + + if (oldIndex < oldValue.Count - 1) + { + oldIndex++; + } + + if (newIndex < newValue.Count - 1) + { + newIndex++; + } } + return stops; } @@ -80,29 +92,29 @@ namespace Avalonia.Animation.Animators { case IRadialGradientBrush oldRadial: return new ImmutableRadialGradientBrush( - CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial), solidColorBrush.Opacity, + CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( - CreateStopsFromSolidColorBrush(solidColorBrush, oldConic), solidColorBrush.Opacity, + CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( - CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear), solidColorBrush.Opacity, + CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: throw new NotSupportedException($"Gradient of type {gradientBrush?.GetType()} is not supported"); } - static IReadOnlyList CreateStopsFromSolidColorBrush(ISolidColorBrush solidColorBrush, IGradientBrush baseGradient) + static IReadOnlyList CreateStopsFromSolidColorBrush(ISolidColorBrush solidColorBrush, IReadOnlyList baseStops) { - var stops = new ImmutableGradientStop[baseGradient.GradientStops.Count]; - for (int index = 0; index < baseGradient.GradientStops.Count; index++) + var stops = new ImmutableGradientStop[baseStops.Count]; + for (int index = 0; index < baseStops.Count; index++) { - stops[index] = new ImmutableGradientStop(baseGradient.GradientStops[index].Offset, solidColorBrush.Color); + stops[index] = new ImmutableGradientStop(baseStops[index].Offset, solidColorBrush.Color); } return stops; } From e8da78cd41fffb23453567dc3c391f78101a095e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 5 Jul 2021 14:18:12 -0400 Subject: [PATCH 6/9] Update relative point animation --- .../Animation/Animators/RelativePointAnimator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs index 40fa4503f0..348a2e4a35 100644 --- a/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs @@ -11,7 +11,7 @@ { if (oldValue.Unit != newValue.Unit) { - return progress >= 1 ? newValue : oldValue; + return progress >= 0.5 ? newValue : oldValue; } return new RelativePoint(s_pointAnimator.Interpolate(progress, oldValue.Point, newValue.Point), oldValue.Unit); From 8d0a4ff5ea5ca84304891a528f481f0c410bc2a1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 8 Jul 2021 02:31:39 -0400 Subject: [PATCH 7/9] Rename GradientBrushAnimator --- .../Animation/Animators/BaseBrushAnimator.cs | 8 ++++---- .../Animation/Animators/GradientBrushAnimator.cs | 2 +- .../Animation/Transitions/BrushTransition.cs | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index 38da394088..ba7a3868c7 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -78,21 +78,21 @@ namespace Avalonia.Animation.Animators return false; } - var gradientAnimator = new IGradientBrushAnimator(); + var gradientAnimator = new GradientBrushAnimator(); gradientAnimator.Property = Property; foreach (var keyframe in this) { if (keyframe.Value is ISolidColorBrush solidColorBrush) { - gradientAnimator.Add(new AnimatorKeyFrame(typeof(IGradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) + gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) { - Value = IGradientBrushAnimator.ConvertSolidColorBrushToGradient(firstGradient, solidColorBrush) + Value = GradientBrushAnimator.ConvertSolidColorBrushToGradient(firstGradient, solidColorBrush) }); } else if (keyframe.Value is IGradientBrush) { - gradientAnimator.Add(new AnimatorKeyFrame(typeof(IGradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) + gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) { Value = keyframe.Value }); diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 6481f815de..864e12413f 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -12,7 +12,7 @@ namespace Avalonia.Animation.Animators /// /// Animator that handles values. /// - public class IGradientBrushAnimator : Animator + public class GradientBrushAnimator : Animator { private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); diff --git a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs index 2229d6edc6..4d9c8af4d5 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs @@ -13,7 +13,7 @@ namespace Avalonia.Animation /// public class BrushTransition : Transition { - private static readonly IGradientBrushAnimator s_gradientAnimator = new IGradientBrushAnimator(); + private static readonly GradientBrushAnimator s_gradientAnimator = new GradientBrushAnimator(); private static readonly ISolidColorBrushAnimator s_solidColorBrushAnimator = new ISolidColorBrushAnimator(); public override IObservable DoTransition(IObservable progress, IBrush? oldValue, IBrush? newValue) @@ -27,18 +27,18 @@ namespace Avalonia.Animation { if (newValue is IGradientBrush newGradient) { - return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, oldGradient, newGradient); + return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, oldGradient, newGradient); } else if (newValue is ISolidColorBrush newSolidColorBrushToConvert) { - var convertedSolidColorBrush = IGradientBrushAnimator.ConvertSolidColorBrushToGradient(oldGradient, newSolidColorBrushToConvert); - return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, oldGradient, convertedSolidColorBrush); + var convertedSolidColorBrush = GradientBrushAnimator.ConvertSolidColorBrushToGradient(oldGradient, newSolidColorBrushToConvert); + return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, oldGradient, convertedSolidColorBrush); } } else if (newValue is IGradientBrush newGradient && oldValue is ISolidColorBrush oldSolidColorBrushToConvert) { - var convertedSolidColorBrush = IGradientBrushAnimator.ConvertSolidColorBrushToGradient(newGradient, oldSolidColorBrushToConvert); - return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, convertedSolidColorBrush, newGradient); + var convertedSolidColorBrush = GradientBrushAnimator.ConvertSolidColorBrushToGradient(newGradient, oldSolidColorBrushToConvert); + return new AnimatorTransitionObservable(s_gradientAnimator, progress, Easing, convertedSolidColorBrush, newGradient); } if (oldValue is ISolidColorBrush oldSolidColorBrush && newValue is ISolidColorBrush newSolidColorBrush) From f589965b2df0f391d752b3ae0f11f615aa1fb672 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 16 Jul 2021 16:47:32 +0200 Subject: [PATCH 8/9] OSX: Fix showing window with no specified size. A Window without a `Width`/`Height` specified was not getting shown on OSX since 1f8b90925771c64e54afa1a264938de0fd5e3c70. Set the content view in the constructor to fix this. --- native/Avalonia.Native/src/OSX/window.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 7df9b76425..3c1a370195 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -52,6 +52,7 @@ public: [Window setBackingType:NSBackingStoreBuffered]; [Window setOpaque:false]; + [Window setContentView: StandardContainer]; } virtual HRESULT ObtainNSWindowHandle(void** ret) override @@ -124,7 +125,6 @@ public: SetPosition(lastPositionSet); UpdateStyle(); - [Window setContentView: StandardContainer]; [Window setTitle:_lastTitle]; if(ShouldTakeFocusOnShow() && activate) From de3fdbeaf151deb6473aac9e5f3d2aa268e36925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 28 Jul 2021 20:55:13 +0200 Subject: [PATCH 9/9] Add ClipGeometry and OpacityMask properties to DrawingGroup --- src/Avalonia.Visuals/Media/DrawingGroup.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Avalonia.Visuals/Media/DrawingGroup.cs b/src/Avalonia.Visuals/Media/DrawingGroup.cs index e581c8c553..eeb6318ebd 100644 --- a/src/Avalonia.Visuals/Media/DrawingGroup.cs +++ b/src/Avalonia.Visuals/Media/DrawingGroup.cs @@ -12,6 +12,12 @@ namespace Avalonia.Media public static readonly StyledProperty TransformProperty = AvaloniaProperty.Register(nameof(Transform)); + public static readonly StyledProperty ClipGeometryProperty = + AvaloniaProperty.Register(nameof(ClipGeometry)); + + public static readonly StyledProperty OpacityMaskProperty = + AvaloniaProperty.Register(nameof(OpacityMask)); + public double Opacity { get => GetValue(OpacityProperty); @@ -24,6 +30,18 @@ namespace Avalonia.Media set => SetValue(TransformProperty, value); } + public Geometry ClipGeometry + { + get => GetValue(ClipGeometryProperty); + set => SetValue(ClipGeometryProperty, value); + } + + public IBrush OpacityMask + { + get => GetValue(OpacityMaskProperty); + set => SetValue(OpacityMaskProperty, value); + } + [Content] public AvaloniaList Children { get; } = new AvaloniaList(); @@ -31,6 +49,8 @@ namespace Avalonia.Media { using (context.PushPreTransform(Transform?.Value ?? Matrix.Identity)) using (context.PushOpacity(Opacity)) + using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default(DrawingContext.PushedState)) + using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, GetBounds()) : default(DrawingContext.PushedState)) { foreach (var drawing in Children) {