From b71743e9a8bb483ee9b2e0112ab37ac258606b48 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Jul 2021 13:28:10 +0200 Subject: [PATCH 01/17] Handle applicationShouldTerminate on OSX. Added some new plumbing to facilitate this: - A managed event is raised `IPlatformLifetimeEventsImpl.ShutdownRequested` - This event is subscribed from `ClassicDesktopStyleApplicationLifetime` - Which tries to close all windows - And returns false to cancel the OS-level close if not all windows could be closed --- native/Avalonia.Native/src/OSX/app.mm | 6 +++++ .../ClassicDesktopStyleApplicationLifetime.cs | 22 ++++++++++++++++++- .../Platform/IPlatformLifetimeEventsImpl.cs | 16 ++++++++++++++ .../AvaloniaNativeApplicationPlatform.cs | 13 ++++++++++- src/Avalonia.Native/AvaloniaNativePlatform.cs | 3 ++- src/Avalonia.Native/avn.idl | 1 + 6 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 460c24ea3a..e1972b22f4 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -50,6 +50,12 @@ ComPtr _events; _events->FilesOpened(array); } + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + return _events->TryShutdown() ? NSTerminateNow : NSTerminateCancel; +} + @end @interface AvnApplication : NSApplication diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index aa4342f075..872820e420 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Threading; using Avalonia.Controls; @@ -42,7 +43,7 @@ namespace Avalonia.Controls.ApplicationLifetimes "Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed"); _activeLifetime = this; } - + /// public event EventHandler Startup; /// @@ -111,6 +112,11 @@ namespace Avalonia.Controls.ApplicationLifetimes ((IApplicationPlatformEvents)Application.Current).RaiseUrlsOpened(args); } + var lifetimeEvents = AvaloniaLocator.Current.GetService(); + + if (lifetimeEvents != null) + lifetimeEvents.ShutdownRequested += ShutdownRequested; + _cts = new CancellationTokenSource(); MainWindow?.Show(); Dispatcher.UIThread.MainLoop(_cts.Token); @@ -123,6 +129,20 @@ namespace Avalonia.Controls.ApplicationLifetimes if (_activeLifetime == this) _activeLifetime = null; } + + private void ShutdownRequested(object sender, CancelEventArgs e) + { + foreach (var w in Windows.ToArray()) + w.Close(); + if (Windows.Count > 0) + e.Cancel = true; + } + + private bool TryCloseAllWindows() + { + + return Windows.Count == 0; + } } public class ClassicDesktopStyleApplicationLifetimeOptions diff --git a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs new file mode 100644 index 0000000000..8e660777e9 --- /dev/null +++ b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel; + +namespace Avalonia.Platform +{ + public interface IPlatformLifetimeEventsImpl + { + /// + /// Raised by the platform when a shutdown is requested. + /// + /// + /// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit. + /// + event EventHandler ShutdownRequested; + } +} diff --git a/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs b/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs index 9579aa93b1..77c0794d04 100644 --- a/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs @@ -1,14 +1,25 @@ using System; +using System.ComponentModel; using Avalonia.Native.Interop; using Avalonia.Platform; namespace Avalonia.Native { - internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents + internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl { + public event EventHandler ShutdownRequested; + void IAvnApplicationEvents.FilesOpened(IAvnStringArray urls) { ((IApplicationPlatformEvents)Application.Current).RaiseUrlsOpened(urls.ToStringArray()); } + + public int TryShutdown() + { + if (ShutdownRequested is null) return 1; + var e = new CancelEventArgs(); + ShutdownRequested(this, e); + return (!e.Cancel).AsComBool(); + } } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 82a845ffc1..60c0b36891 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -111,7 +111,8 @@ namespace Avalonia.Native .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) - .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)); + .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) + .Bind().ToConstant(applicationPlatform); if (_options.UseGpu) { diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index adcbeb2d3a..3071333c5a 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -732,4 +732,5 @@ interface IAvnNativeControlHostTopLevelAttachment : IUnknown interface IAvnApplicationEvents : IUnknown { void FilesOpened (IAvnStringArray* urls); + bool TryShutdown(); } From bf210cb5f429ccc6507b41d43b5ca120957016c8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Jul 2021 13:40:48 +0200 Subject: [PATCH 02/17] Call terminate from Quit menu. Unifies the closing logic between clicking on the icon menu Quit and the Quit menu. --- native/Avalonia.Native/src/OSX/window.mm | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index c0936356d2..e76d8c4a7e 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1887,18 +1887,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent +(void)closeAll { - NSArray* windows = [NSArray arrayWithArray:[NSApp windows]]; - auto numWindows = [windows count]; - - for(int i = 0; i < numWindows; i++) - { - auto window = (AvnWindow*)[windows objectAtIndex:i]; - - if([window parentWindow] == nullptr) // Avalonia will handle the child windows. - { - [window performClose:nil]; - } - } + [[NSApplication sharedApplication] terminate:self]; } - (void)performClose:(id)sender From a9f1781da9fddeaa8079e79ef6b01e2221d6d8fa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Jul 2021 14:04:32 +0200 Subject: [PATCH 03/17] Remove dead code. --- .../ClassicDesktopStyleApplicationLifetime.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 872820e420..050ed59583 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -137,12 +137,6 @@ namespace Avalonia.Controls.ApplicationLifetimes if (Windows.Count > 0) e.Cancel = true; } - - private bool TryCloseAllWindows() - { - - return Windows.Count == 0; - } } public class ClassicDesktopStyleApplicationLifetimeOptions From 3ec9b65cc3ac5aee1770e470a2ff2a89831ecb50 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 3 Jul 2021 15:11:57 +0200 Subject: [PATCH 04/17] Remove unneeded list copy. --- .../ClassicDesktopStyleApplicationLifetime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 050ed59583..2c43f13c82 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -132,7 +132,7 @@ namespace Avalonia.Controls.ApplicationLifetimes private void ShutdownRequested(object sender, CancelEventArgs e) { - foreach (var w in Windows.ToArray()) + foreach (var w in Windows) w.Close(); if (Windows.Count > 0) e.Cancel = true; From a3bd88128aaa6a28e944d2226a46ff677b5700e8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 4 Jul 2021 03:46:45 -0400 Subject: [PATCH 05/17] 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 06/17] 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 07/17] 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 08/17] 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 10/17] 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 cee1a24e0330d4daedea663e858ee33d761d2227 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 7 Jul 2021 17:06:59 +0200 Subject: [PATCH 11/17] Only try to close non-owned windows on shutdown. Owned windows will be closed by their owners. --- .../ClassicDesktopStyleApplicationLifetime.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 2c43f13c82..2256f4cb54 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -132,8 +132,12 @@ namespace Avalonia.Controls.ApplicationLifetimes private void ShutdownRequested(object sender, CancelEventArgs e) { + // When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel + // shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their + // owners. foreach (var w in Windows) - w.Close(); + if (w.Owner is null) + w.Close(); if (Windows.Count > 0) e.Cancel = true; } From 8d0a4ff5ea5ca84304891a528f481f0c410bc2a1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 8 Jul 2021 02:31:39 -0400 Subject: [PATCH 12/17] 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 39394bceb6988571ceacd7f4fb85e142d41b5965 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 9 Jul 2021 09:29:58 +0200 Subject: [PATCH 13/17] Don't try to shutdown if e.Cancel already set. This allows users to add their own `ShutdownRequested` handlers which can override the lifetime's handler. --- .../ClassicDesktopStyleApplicationLifetime.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 2256f4cb54..65f1f0fff2 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -132,6 +132,9 @@ namespace Avalonia.Controls.ApplicationLifetimes private void ShutdownRequested(object sender, CancelEventArgs e) { + if (e.Cancel) + return; + // When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel // shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their // owners. From 298dd4e2997b9aab1f0f203f210355ee7e354197 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 12 Jul 2021 17:36:45 +0200 Subject: [PATCH 14/17] Added ShutdownRequested to IClassicDesktopStyleApplicationLifetime. --- .../ClassicDesktopStyleApplicationLifetime.cs | 10 +++++-- ...IClassicDesktopStyleApplicationLifetime.cs | 12 ++++++++ .../DesktopStyleApplicationLifetimeTests.cs | 30 ++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 65f1f0fff2..79780dbd0b 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -46,6 +46,10 @@ namespace Avalonia.Controls.ApplicationLifetimes /// public event EventHandler Startup; + + /// + public event EventHandler ShutdownRequested; + /// public event EventHandler Exit; @@ -115,7 +119,7 @@ namespace Avalonia.Controls.ApplicationLifetimes var lifetimeEvents = AvaloniaLocator.Current.GetService(); if (lifetimeEvents != null) - lifetimeEvents.ShutdownRequested += ShutdownRequested; + lifetimeEvents.ShutdownRequested += OnShutdownRequested; _cts = new CancellationTokenSource(); MainWindow?.Show(); @@ -130,8 +134,10 @@ namespace Avalonia.Controls.ApplicationLifetimes _activeLifetime = null; } - private void ShutdownRequested(object sender, CancelEventArgs e) + private void OnShutdownRequested(object sender, CancelEventArgs e) { + ShutdownRequested?.Invoke(this, e); + if (e.Cancel) return; diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index 212f0b8617..ecf8a0358f 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace Avalonia.Controls.ApplicationLifetimes { @@ -34,5 +35,16 @@ namespace Avalonia.Controls.ApplicationLifetimes Window MainWindow { get; set; } IReadOnlyList Windows { get; } + + /// + /// Raised by the platform when a shutdown is requested. + /// + /// + /// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit. This event + /// provides a first-chance to cancel application shutdown; if shutdown is not canceled at this point the application + /// will try to close each non-owned open window, invoking the event on each and allowing + /// each window to cancel the shutdown. + /// + event EventHandler ShutdownRequested; } } diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs index 84f02aeda5..38713834c3 100644 --- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform; using Avalonia.Threading; @@ -209,6 +210,33 @@ namespace Avalonia.Controls.UnitTests Assert.Empty(lifetime.Windows); } } + + [Fact] + public void Should_Allow_Canceling_Shutdown_Via_ShutdownRequested_Event() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using (var lifetime = new ClassicDesktopStyleApplicationLifetime()) + { + var lifetimeEvents = new Mock(); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(lifetimeEvents.Object); + lifetime.Start(Array.Empty()); + + var window = new Window(); + var raised = 0; + + window.Show(); + + lifetime.ShutdownRequested += (s, e) => + { + e.Cancel = true; + ++raised; + }; + + lifetimeEvents.Raise(x => x.ShutdownRequested += null, new CancelEventArgs()); + + Assert.Equal(1, raised); + Assert.Equal(new[] { window }, lifetime.Windows); + } + } } - } From 70c207da994e7fe3cd2d6f1c2852e5143be3d16a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 12 Jul 2021 17:38:34 +0200 Subject: [PATCH 15/17] Update ApiCompatBaseline.txt --- src/Avalonia.Controls/ApiCompatBaseline.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 1abf4fd6ff..596b17c943 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -6,12 +6,15 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Control InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.remove_ShutdownRequested(System.EventHandler)' is present in the implementation but not in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. -Total Issues: 15 +Total Issues: 18 From e69af3fa212017b317f4ca16120c85b038082a69 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 12 Jul 2021 17:43:27 +0200 Subject: [PATCH 16/17] Undo changes to AppCompatBaseline.txt --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index c917902dc3..39a4c3004c 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -5,6 +5,7 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. +TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. @@ -73,4 +74,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -Total Issues: 74 +Total Issues: 75 From eeb593f041728a45e8195e75376c730b379783bf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 20 Jul 2021 19:40:35 -0400 Subject: [PATCH 17/17] Remove TextBox margin hack --- src/Avalonia.Themes.Fluent/Controls/TextBox.xaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index 5db9398448..fca4a3bd45 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -76,9 +76,7 @@ IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> -