From 543fe599c691403514fb0000a1bcee2904556ebb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 16:32:56 +0200 Subject: [PATCH 01/34] fix: some nullable annotation warnings --- .../ViewModels/TransitioningContentControlPageViewModel.cs | 6 +++--- src/Avalonia.Controls/Avalonia.Controls.csproj | 3 --- src/Avalonia.Themes.Default/SimpleTheme.cs | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.cs | 6 ++++-- .../CompiledBindings/PropertyInfoAccessorFactory.cs | 4 +--- .../MarkupExtensions/ResourceInclude.cs | 4 ++-- src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs | 2 +- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs | 2 +- tests/Avalonia.Benchmarks/TestBindingObservable.cs | 3 ++- .../Media/TextFormatting/TextFormatterTests.cs | 4 ++-- .../Media/TextFormatting/TextLineTests.cs | 4 ++-- 12 files changed, 20 insertions(+), 22 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs index b092a07f4a..0e9522acab 100644 --- a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs @@ -45,12 +45,12 @@ namespace ControlCatalog.ViewModels public List Images { get; } = new List(); - private Bitmap? _SelectedImage; + private Bitmap _SelectedImage; /// /// Gets or Sets the selected image /// - public Bitmap? SelectedImage + public Bitmap SelectedImage { get { return _SelectedImage; } set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); } @@ -293,7 +293,7 @@ namespace ControlCatalog.ViewModels /// /// Any one of the parameters may be null, but not both. /// - private static IVisual GetVisualParent(IVisual? from, IVisual? to) + private static IVisual GetVisualParent(IVisual from, IVisual to) { var p1 = (from ?? to)!.VisualParent; var p2 = (to ?? from)!.VisualParent; diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 4d239e69f4..3896dc2735 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -2,9 +2,6 @@ net6.0;netstandard2.0 - - - diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs index 6929660757..664c95644f 100644 --- a/src/Avalonia.Themes.Default/SimpleTheme.cs +++ b/src/Avalonia.Themes.Default/SimpleTheme.cs @@ -44,7 +44,7 @@ namespace Avalonia.Themes.Default InitStyles(_baseUri); } - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index f6b47a5466..2a8e045c48 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -50,7 +50,9 @@ namespace Avalonia.Themes.Fluent /// The XAML service provider. public FluentTheme(IServiceProvider serviceProvider) { - _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + var ctx = serviceProvider.GetService(typeof(IUriContext)) as IUriContext + ?? throw new NullReferenceException("Unable retrive UriContext"); + _baseUri = ctx.BaseUri; InitStyles(_baseUri); } @@ -146,7 +148,7 @@ namespace Avalonia.Themes.Fluent IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs index c21a2d4299..ef11b06369 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Text; using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins; @@ -174,7 +172,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings WeakEvents.CollectionChanged.Unsubscribe(incc, this); } - public void OnEvent(object? sender, WeakEvent ev, NotifyCollectionChangedEventArgs args) + public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs args) { if (ShouldNotifyListeners(args)) { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs index b6137aa89f..1091b3ec7e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs @@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions bool IResourceNode.HasResources => Loaded.HasResources; - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add => Loaded.OwnerChanged += value; remove => Loaded.OwnerChanged -= value; @@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { if (!_isLoading) { - return Loaded.TryGetResource(key, out value); + return Loaded.TryGetResource(key, out value); } value = null; diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index fa4a27fc50..46b5bc0c40 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.Styling IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 1c2dd92219..4d537a16a4 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -195,7 +195,7 @@ namespace Avalonia.Win32 ShowActivated = true; } - private void TrayPopupRoot_Deactivated(object sender, EventArgs e) + private void TrayPopupRoot_Deactivated(object? sender, EventArgs e) { Close(); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index 48f5f8f871..e864f32138 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -109,7 +109,7 @@ namespace Avalonia.Win32 if (_owner is Window window) { - var visual = window.Renderer.HitTestFirst(position, _owner as Window, x => + var visual = window.Renderer.HitTestFirst(position, _owner, x => { if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsVisible)) { diff --git a/tests/Avalonia.Benchmarks/TestBindingObservable.cs b/tests/Avalonia.Benchmarks/TestBindingObservable.cs index 0721ca9855..653959ba18 100644 --- a/tests/Avalonia.Benchmarks/TestBindingObservable.cs +++ b/tests/Avalonia.Benchmarks/TestBindingObservable.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using Avalonia.Data; namespace Avalonia.Benchmarks diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 9d40898608..d395f68f96 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -602,7 +602,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting private class EndOfLineTextSource : ITextSource { - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { return new TextEndOfLine(); } @@ -617,7 +617,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting _text = text; } - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length) { diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index a47638d2ec..6321e8a336 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -547,7 +547,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { const string Text = "_A_A"; - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { switch (textSourceIndex) { @@ -755,7 +755,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting _textRuns = textRuns; } - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { var currentPosition = 0; From bb796a1a7374d4652ab453df53b3499747bf46d2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:44:42 +0200 Subject: [PATCH 02/34] fix(Carousel): PageTransition nullable --- src/Avalonia.Controls/Carousel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Carousel.cs b/src/Avalonia.Controls/Carousel.cs index 4cacf51fa1..28a4aa6436 100644 --- a/src/Avalonia.Controls/Carousel.cs +++ b/src/Avalonia.Controls/Carousel.cs @@ -20,8 +20,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = - AvaloniaProperty.Register(nameof(PageTransition)); + public static readonly StyledProperty PageTransitionProperty = + AvaloniaProperty.Register(nameof(PageTransition)); /// /// The default value of for @@ -54,7 +54,7 @@ namespace Avalonia.Controls /// /// Gets or sets the transition to use when moving between pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } From 0223eb371616722e941025b2a761112c37392069 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:25:48 +0200 Subject: [PATCH 03/34] fix: Android nullable --- src/Android/Avalonia.Android/AvaloniaView.cs | 2 +- .../Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 8177cf1f69..bbd3e0af9f 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -15,7 +15,7 @@ namespace Avalonia.Android private EmbeddableControlRoot _root; private readonly ViewImpl _view; - private IDisposable? _timerSubscription; + private IDisposable _timerSubscription; public AvaloniaView(Context context) : base(context) { diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 2b2a9dd2b4..4cae700c0a 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers return DispatchKeyEventInternal(e, out callBase); } - string? UnicodeTextInput(KeyEvent keyEvent) + string UnicodeTextInput(KeyEvent keyEvent) { return keyEvent.Action == KeyEventActions.Multiple && keyEvent.RepeatCount == 0 From d51fc3f5e2a6fbce189185b400cb188f1f4c2899 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:26:23 +0200 Subject: [PATCH 04/34] fix: X11 nullable --- src/Avalonia.X11/X11Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 066156a652..28455d5e31 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -1160,7 +1160,7 @@ namespace Avalonia.X11 } public IntPtr Handle => _owner._renderHandle; - public string? HandleDescriptor => "XID"; + public string HandleDescriptor => "XID"; } } } From 2fa549d62a44c52fb46aba55e8b9e7b4288861ec Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:26:54 +0200 Subject: [PATCH 05/34] fix: Win32 nullable --- src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs | 2 +- .../Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 1085aa1b42..1ec0ee9e2e 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -42,7 +42,7 @@ namespace Avalonia.Win32.Automation return GetOrCreate(focus); } - public void FocusChanged(object sender, EventArgs e) + public void FocusChanged(object? sender, EventArgs e) { RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); } diff --git a/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs b/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs index 1de0cf0f9b..8d6677315c 100644 --- a/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs +++ b/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Runtime.InteropServices; From fb34e56b0770a796c941a9bfd9e13e2a2e3afe59 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 11:42:43 +0200 Subject: [PATCH 06/34] fix(CarouselPresenter ): PageTransition --- src/Avalonia.Controls/Presenters/CarouselPresenter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 3d1e7eb5a8..87bbfce2a2 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = + public static readonly StyledProperty PageTransitionProperty = Carousel.PageTransitionProperty.AddOwner(); private int _selectedIndex = -1; @@ -85,7 +85,7 @@ namespace Avalonia.Controls.Presenters /// /// Gets or sets a transition to use when switching pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } From bd3bb6e47e8d7a8c61430a6aeeffa1489649ad60 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 22:15:12 -0400 Subject: [PATCH 07/34] Check if BindingValue actually has a value --- src/Avalonia.Base/Reactive/TypedBindingAdapter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs index f60ef247d5..f75917a00e 100644 --- a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs +++ b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs @@ -30,13 +30,15 @@ namespace Avalonia.Reactive } catch (InvalidCastException e) { + var unwrappedValue = value.HasValue ? value.Value : null; + Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log( _target, "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", _property.Name, _property.PropertyType, - value.Value, - value.Value?.GetType()); + unwrappedValue, + unwrappedValue?.GetType()); PublishNext(BindingValue.BindingError(e)); } } From 2a2779f2487bcf306796436d016ec243e3aa1ce3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 22:57:01 -0400 Subject: [PATCH 08/34] Stop listening for IsCancel on button detached --- src/Avalonia.Controls/Button.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index db4ca6bc43..6dba33516b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -232,6 +232,13 @@ namespace Avalonia.Controls StopListeningForDefault(inputElement); } } + if (IsCancel) + { + if (e.Root is IInputElement inputElement) + { + StopListeningForCancel(inputElement); + } + } } /// From 210727175aead0e15d77eba795e579b3f4177e83 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 23:11:44 -0400 Subject: [PATCH 09/34] Add simple IsDefault/IsCancel tests --- .../ButtonTests.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index f4acf5ea37..2e3623cc3c 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -309,6 +309,80 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, raised); } + + [Fact] + public void Button_IsDefault_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var raised = 0; + var target = new Button(); + var window = new Window { Content = target }; + window.Show(); + + target.Click += (s, e) => ++raised; + + target.IsDefault = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(0, raised); + + target.IsDefault = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(1, raised); + + target.IsDefault = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(1, raised); + + target.IsDefault = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(2, raised); + + window.Content = null; + // To check if handler was raised on the button, when it's detached, we need to pass it as a source manually. + window.RaiseEvent(CreateKeyDownEvent(Key.Enter, target)); + Assert.Equal(2, raised); + } + } + + [Fact] + public void Button_IsCancel_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var raised = 0; + var target = new Button(); + var window = new Window { Content = target }; + window.Show(); + + target.Click += (s, e) => ++raised; + + target.IsCancel = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(0, raised); + + target.IsCancel = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(1, raised); + + target.IsCancel = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(1, raised); + + target.IsCancel = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(2, raised); + + window.Content = null; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape, target)); + Assert.Equal(2, raised); + } + } + + private KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null) + { + return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source }; + } private class TestButton : Button, IRenderRoot { From bd0d81d6afea1554a8f79d1a432d86de9838efe5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:08:32 +0100 Subject: [PATCH 10/34] fix logic for deciding if chrome buttons should be shown or not in extended mode. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index cae1c09513..ef2a91ef14 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -32,7 +32,7 @@ void WindowImpl::HideOrShowTrafficLights() { } bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - bool hasTrafficLights = _isClientAreaExtended ? !wantsChrome : _decorations != SystemDecorationsFull; + bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations != SystemDecorationsFull; [[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights]; [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights]; From e446a66d6ab48ba0ab5982a6a379efe7d401d85b Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 12:29:39 +0200 Subject: [PATCH 11/34] Fix calculation of inverted matrix --- src/Avalonia.Base/Matrix.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Matrix.cs b/src/Avalonia.Base/Matrix.cs index 5bbc657385..8d3227378f 100644 --- a/src/Avalonia.Base/Matrix.cs +++ b/src/Avalonia.Base/Matrix.cs @@ -450,13 +450,13 @@ namespace Avalonia inverted = new Matrix( (_m22 * _m33 - _m32 * _m23) * invdet, - (_m13 * _m31 - _m12 * _m33) * invdet, + (_m13 * _m32 - _m12 * _m33) * invdet, (_m12 * _m23 - _m13 * _m22) * invdet, (_m23 * _m31 - _m21 * _m33) * invdet, (_m11 * _m33 - _m13 * _m31) * invdet, (_m21 * _m13 - _m11 * _m23) * invdet, (_m21 * _m32 - _m31 * _m22) * invdet, - (_m21 * _m12 - _m11 * _m32) * invdet, + (_m31 * _m12 - _m11 * _m32) * invdet, (_m11 * _m22 - _m21 * _m12) * invdet ); From 6a848a389fb2ca600ed4173d5f121b14b97e24cb Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:34:20 +0100 Subject: [PATCH 12/34] dont re-initialise nswindow on show. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index c420736b46..bf221047f9 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -90,8 +90,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { - InitialiseNSWindow(); - if(hasPosition) { SetPosition(lastPositionSet); From 809f459233f018111cacb39e2564ff242b612b4a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:39:03 +0100 Subject: [PATCH 13/34] fix osx thick titlebar setting. --- samples/ControlCatalog/MainWindow.xaml.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 20591103b7..8ee8b2ebd4 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -29,8 +29,6 @@ namespace ControlCatalog DataContext = new MainWindowViewModel(_notificationArea); _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu; - - ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar; } public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit"; From 11595184d5e87520073dbba37b0afd8cce1d3a2e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:39:14 +0100 Subject: [PATCH 14/34] fix traffic light setting. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index ef2a91ef14..f49b676ce6 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -32,11 +32,11 @@ void WindowImpl::HideOrShowTrafficLights() { } bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations != SystemDecorationsFull; + bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull; - [[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights]; - [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights]; - [[Window standardWindowButton:NSWindowZoomButton] setHidden:hasTrafficLights]; + [[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights]; } void WindowImpl::OnInitialiseNSWindow(){ From 6e916c21e5d5effba473013086b111123c38cf26 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:46:30 +0100 Subject: [PATCH 15/34] fix transition to fullscreen mode. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 9fc7ec4fec..f6dcd0cabf 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -33,6 +33,7 @@ bool _isEnabled; bool _canBecomeKeyWindow; bool _isExtended; + bool _isTransitioningToFullScreen; AvnMenu* _menu; } @@ -175,6 +176,7 @@ [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; + _isTransitioningToFullScreen = false; if(self.isDialog) { @@ -349,6 +351,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = true; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -359,6 +362,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = false; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -441,7 +445,10 @@ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } - _parent->BringToFront(); + if(!_isTransitioningToFullScreen) + { + _parent->BringToFront(); + } } break; From e2dbf68b02b3a67079d84ef7d2815ace38843b20 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 12:20:25 +0100 Subject: [PATCH 16/34] fix call to virtual method from ctor. --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 6 +----- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 -- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 7 ------- native/Avalonia.Native/src/OSX/WindowImpl.h | 3 +-- native/Avalonia.Native/src/OSX/WindowImpl.mm | 3 +++ 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index 3c5afd9424..9820a9f052 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -26,17 +26,13 @@ private: PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; } protected: virtual NSWindowStyleMask GetStyle() override { return NSWindowStyleMaskBorderless; } - - virtual void OnInitialiseNSWindow () override - { - [Window setLevel:NSPopUpMenuWindowLevel]; - } public: virtual bool ShouldTakeFocusOnShow() override diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 040ba39b6d..787827c9b4 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -106,8 +106,6 @@ protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); - - virtual void OnInitialiseNSWindow (); private: void CreateNSWindow (bool isDialog); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bf221047f9..ea9ef3c9de 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -567,11 +567,6 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } -void WindowBaseImpl::OnInitialiseNSWindow() -{ - -} - void WindowBaseImpl::InitialiseNSWindow() { if(Window != nullptr) { [Window setContentView:StandardContainer]; @@ -594,8 +589,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [GetWindowProtocol() showWindowMenuWithAppMenu]; } } - - OnInitialiseNSWindow(); } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 627e29c03d..b4b1d4e70b 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -93,8 +93,6 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog() override; - virtual void OnInitialiseNSWindow() override; - virtual void BringToFront () override; bool CanBecomeKeyWindow (); @@ -103,6 +101,7 @@ protected: virtual NSWindowStyleMask GetStyle() override; private: + void OnInitialiseNSWindow(); NSString *_lastTitle; }; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index f49b676ce6..382cd0db95 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -24,6 +24,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastTitle = @""; _parent = nullptr; WindowEvents = events; + + OnInitialiseNSWindow(); } void WindowImpl::HideOrShowTrafficLights() { @@ -41,6 +43,7 @@ void WindowImpl::HideOrShowTrafficLights() { void WindowImpl::OnInitialiseNSWindow(){ [GetWindowProtocol() setCanBecomeKeyWindow:true]; + [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; From 164757c915ec36e422231479c24676b0640ee0bd Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 3 Jun 2022 13:41:09 +0200 Subject: [PATCH 17/34] Make NumericUpDown nullable --- .../NumericUpDown/NumericUpDown.cs | 111 +++++++++++------- .../NumericUpDownValueChangedEventArgs.cs | 6 +- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 4d86a0f17c..705e68e3ea 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -107,8 +107,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty ValueProperty = - AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, + public static readonly DirectProperty ValueProperty = + AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); /// @@ -131,7 +131,7 @@ namespace Avalonia.Controls private IDisposable? _textBoxTextChangedSubscription; - private decimal _value; + private decimal? _value; private string? _text; private bool _internalValueSet; private bool _clipValueToMinMax; @@ -277,7 +277,7 @@ namespace Avalonia.Controls /// /// Gets or sets the value. /// - public decimal Value + public decimal? Value { get { return _value; } set @@ -351,7 +351,7 @@ namespace Avalonia.Controls /// protected override void OnLostFocus(RoutedEventArgs e) { - CommitInput(); + CommitInput(true); base.OnLostFocus(e); } @@ -489,9 +489,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -506,9 +506,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -530,7 +530,7 @@ namespace Avalonia.Controls /// /// The old value. /// The new value. - protected virtual void OnValueChanged(decimal oldValue, decimal newValue) + protected virtual void OnValueChanged(decimal? oldValue, decimal? newValue) { if (!_internalValueSet && IsInitialized) { @@ -573,7 +573,7 @@ namespace Avalonia.Controls /// Called when the property has to be coerced. /// /// The value. - protected virtual decimal OnCoerceValue(decimal baseValue) + protected virtual decimal? OnCoerceValue(decimal? baseValue) { return baseValue; } @@ -607,7 +607,7 @@ namespace Avalonia.Controls /// /// The old value. /// The new value. - protected virtual void RaiseValueChangedEvent(decimal oldValue, decimal newValue) + protected virtual void RaiseValueChangedEvent(decimal? oldValue, decimal? newValue) { var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); RaiseEvent(e); @@ -616,9 +616,9 @@ namespace Avalonia.Controls /// /// Converts the formatted text to a value. /// - private decimal ConvertTextToValue(string text) + private decimal? ConvertTextToValue(string? text) { - decimal result = 0; + decimal? result = null; if (string.IsNullOrEmpty(text)) { @@ -635,9 +635,9 @@ namespace Avalonia.Controls result = ConvertTextToValueCore(currentValueText, text); - if (ClipValueToMinMax) + if (ClipValueToMinMax && result.HasValue) { - return MathUtilities.Clamp(result, Minimum, Maximum); + return MathUtilities.Clamp(result.Value, Minimum, Maximum); } ValidateMinMax(result); @@ -649,7 +649,7 @@ namespace Avalonia.Controls /// Converts the value to formatted text. /// /// - private string ConvertValueToText() + private string? ConvertValueToText() { //Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind. if (FormatString.Contains("{0")) @@ -657,7 +657,7 @@ namespace Avalonia.Controls return string.Format(NumberFormat, FormatString, Value); } - return Value.ToString(FormatString, NumberFormat); + return Value?.ToString(FormatString, NumberFormat); } /// @@ -665,7 +665,16 @@ namespace Avalonia.Controls /// private void OnIncrement() { - var result = Value + Increment; + decimal result; + if (Value.HasValue) + { + result = Value.Value + Increment; + } + else + { + result = Minimum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -674,7 +683,17 @@ namespace Avalonia.Controls /// private void OnDecrement() { - var result = Value - Increment; + decimal result; + + if (Value.HasValue) + { + result = Value.Value - Increment; + } + else + { + result = Maximum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -688,6 +707,11 @@ namespace Avalonia.Controls // Zero increment always prevents spin. if (Increment != 0 && !IsReadOnly) { + if (!Value.HasValue) + { + validDirections = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; + } + if (Value < Maximum) { validDirections = validDirections | ValidSpinDirections.Increase; @@ -825,13 +849,13 @@ namespace Avalonia.Controls { if (e.Sender is NumericUpDown upDown) { - var oldValue = (decimal)e.OldValue!; - var newValue = (decimal)e.NewValue!; + var oldValue = (decimal?)e.OldValue; + var newValue = (decimal?)e.NewValue; upDown.OnValueChanged(oldValue, newValue); } } - private void SetValueInternal(decimal value) + private void SetValueInternal(decimal? value) { _internalValueSet = true; try @@ -946,9 +970,9 @@ namespace Avalonia.Controls remove { RemoveHandler(ValueChangedEvent, value); } } - private bool CommitInput() + private bool CommitInput(bool forceTextUpdate = false) { - return SyncTextAndValueProperties(true, Text); + return SyncTextAndValueProperties(true, Text, forceTextUpdate); } /// @@ -978,28 +1002,24 @@ namespace Avalonia.Controls { if (updateValueFromText) { - if (!string.IsNullOrEmpty(text)) + try { - try + var newValue = ConvertTextToValue(text); + if (!Equals(newValue, Value)) { - var newValue = ConvertTextToValue(text); - if (!Equals(newValue, Value)) - { - SetValueInternal(newValue); - } - } - catch - { - parsedTextIsValid = false; + SetValueInternal(newValue); } } + catch + { + parsedTextIsValid = false; + } } // Do not touch the ongoing text input from user. if (!_isTextChangedFromUI) { - var keepEmpty = !forceTextUpdate && string.IsNullOrEmpty(Text); - if (!keepEmpty) + if (forceTextUpdate) { var newText = ConvertValueToText(); if (!Equals(Text, newText)) @@ -1036,10 +1056,15 @@ namespace Avalonia.Controls return parsedTextIsValid; } - private decimal ConvertTextToValueCore(string currentValueText, string text) + private decimal? ConvertTextToValueCore(string? currentValueText, string? text) { decimal result; + if (string.IsNullOrEmpty(text)) + { + return null; + } + if (IsPercent(FormatString)) { result = ParsePercent(text, NumberFormat); @@ -1052,7 +1077,7 @@ namespace Avalonia.Controls var shouldThrow = true; // Check if CurrentValueText is also failing => it also contains special characters. ex : 90° - if (!decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) + if (!string.IsNullOrEmpty(currentValueText) && !decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) { // extract non-digit characters var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); @@ -1082,8 +1107,12 @@ namespace Avalonia.Controls return result; } - private void ValidateMinMax(decimal value) + private void ValidateMinMax(decimal? value) { + if (!value.HasValue) + { + return; + } if (value < Minimum) { throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum)); diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs index 9b467d682c..af835541ae 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs @@ -4,13 +4,13 @@ namespace Avalonia.Controls { public class NumericUpDownValueChangedEventArgs : RoutedEventArgs { - public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal oldValue, decimal newValue) : base(routedEvent) + public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal? oldValue, decimal? newValue) : base(routedEvent) { OldValue = oldValue; NewValue = newValue; } - public decimal OldValue { get; } - public decimal NewValue { get; } + public decimal? OldValue { get; } + public decimal? NewValue { get; } } } From 43cf66fc49fa6f6e431e26fd747d2bb6edd02eae Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 3 Jun 2022 13:41:28 +0200 Subject: [PATCH 18/34] Update demo to reflect actual selected value --- samples/ControlCatalog/Pages/NumericUpDownPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 9ddc6b6228..e32632dac2 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -65,7 +65,7 @@ Margin="2" HorizontalAlignment="Center"/> Value: - From e47b742d3eb5e2fe8b0ddf0a1b65b8c999aa35bd Mon Sep 17 00:00:00 2001 From: Patrick Tellier Date: Fri, 3 Jun 2022 14:35:46 +0200 Subject: [PATCH 19/34] Use own Pen (if set) in GeometryDrawing.GetBounds --- src/Avalonia.Base/Media/GeometryDrawing.cs | 3 +- .../Media/GeometryDrawingTests.cs | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs diff --git a/src/Avalonia.Base/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs index e46e3a7d5f..08e62df2cc 100644 --- a/src/Avalonia.Base/Media/GeometryDrawing.cs +++ b/src/Avalonia.Base/Media/GeometryDrawing.cs @@ -68,7 +68,8 @@ namespace Avalonia.Media public override Rect GetBounds() { - return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty; + IPen pen = Pen ?? s_boundsPen; + return Geometry?.GetRenderBounds(pen) ?? Rect.Empty; } } } diff --git a/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs new file mode 100644 index 0000000000..06e46c1a06 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs @@ -0,0 +1,52 @@ +using Avalonia.Media; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else + +using Avalonia.Direct2D1.RenderTests; + +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class GeometryDrawingTests : TestBase + { + public GeometryDrawingTests() + : base(@"Media\GeometryDrawing") + { + } + + private GeometryDrawing CreateGeometryDrawing() + { + GeometryDrawing geometryDrawing = new GeometryDrawing(); + EllipseGeometry ellipse = new EllipseGeometry(); + ellipse.RadiusX = 100; + ellipse.RadiusY = 100; + geometryDrawing.Geometry = ellipse; + return geometryDrawing; + } + + [Fact] + public void DrawingGeometry_WithPen() + { + GeometryDrawing geometryDrawing = CreateGeometryDrawing(); + geometryDrawing.Pen = new Pen(new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)), 10); + + Assert.Equal(210, geometryDrawing.GetBounds().Height); + Assert.Equal(210, geometryDrawing.GetBounds().Width); + + } + + [Fact] + public void DrawingGeometry_WithoutPen() + { + GeometryDrawing geometryDrawing = CreateGeometryDrawing(); + + Assert.Equal(200, geometryDrawing.GetBounds().Height); + Assert.Equal(200, geometryDrawing.GetBounds().Width); + } + + + } +} From 0470369af056c13d5af388e827cf39eaf394cec1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 15:20:22 +0100 Subject: [PATCH 20/34] osx: ensure shadow is invalidated on show. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index ea9ef3c9de..46f2ac0092 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -99,6 +99,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { } UpdateStyle(); + + [Window invalidateShadow]; if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; From e5a3820fbb05fe57de89353a2d58bf504b92e648 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 15:40:01 +0100 Subject: [PATCH 21/34] whenever we become key... dispatch invalidateShadow --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index f6dcd0cabf..ebd9f39d30 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -284,6 +284,14 @@ - (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification { _parent->BringToFront(); + + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + [self invalidateShadow]; + } + @finally{ + } + }); } - (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification From eaf8fec5cfaa6e195afeaedded1ac0234f73b4ec Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 18:10:50 +0200 Subject: [PATCH 22/34] Test for matrix inversion --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index 98b1ff0e28..f25047c5c5 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -30,6 +30,16 @@ namespace Avalonia.Base.UnitTests.Media Assert.True(matrix.HasInverse); } + [Fact] + public void Invert_Should_Work() + { + var matrix = new Matrix(1, 2, 3, 0, 1, 4,5,6,0); + var inverted = matrix.Invert(); + var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); + + Assert.Equal(expected, inverted); + } + [Fact] public void Can_Decompose_Translation() { From 987a69aafb2097c0b6c9e2bfadeb9b7f415fa727 Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 18:12:39 +0200 Subject: [PATCH 23/34] spaces --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index f25047c5c5..b30996ca1e 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -33,7 +33,7 @@ namespace Avalonia.Base.UnitTests.Media [Fact] public void Invert_Should_Work() { - var matrix = new Matrix(1, 2, 3, 0, 1, 4,5,6,0); + var matrix = new Matrix(1, 2, 3, 0, 1, 4, 5, 6, 0); var inverted = matrix.Invert(); var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); From 5f5fc6e01fd7717d7ba3b463b72e3e80c70f09e8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 17:22:33 +0100 Subject: [PATCH 24/34] osx: restore missing api for cef - electron compatibility --- native/Avalonia.Native/src/OSX/app.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 14f1f6888c..a15d0c9601 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,6 +82,17 @@ ComPtr _events; _isHandlingSendEvent = oldHandling; } } + +// This is needed for certain embedded controls DO NOT REMOVE.. +- (BOOL) isHandlingSendEvent +{ + return _isHandlingSendEvent; +} + +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent +{ + _isHandlingSendEvent = handlingSendEvent; +} @end extern void InitializeAvnApp(IAvnApplicationEvents* events) From 0907e9519a11f2b830592e96b07daebfba179dad Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 19:44:41 +0200 Subject: [PATCH 25/34] Fix test --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index b30996ca1e..4aa13fab3a 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -35,9 +35,9 @@ namespace Avalonia.Base.UnitTests.Media { var matrix = new Matrix(1, 2, 3, 0, 1, 4, 5, 6, 0); var inverted = matrix.Invert(); - var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); - Assert.Equal(expected, inverted); + Assert.Equal(matrix * inverted, Matrix.Identity); + Assert.Equal(inverted * matrix, Matrix.Identity); } [Fact] From 8c6759990d38119eed7a40b9ac9c4027bb2864d1 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 3 Jun 2022 20:45:35 +0300 Subject: [PATCH 26/34] [X11] Added support for the basic version of _NET_WM_SYNC_REQUEST protocol --- src/Avalonia.X11/X11Atoms.cs | 1 + src/Avalonia.X11/X11Info.cs | 10 ++++++++ src/Avalonia.X11/X11Window.cs | 44 +++++++++++++++++++++++++++-------- src/Avalonia.X11/XLib.cs | 17 ++++++++++++++ 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index b59883ef94..cfda68f9e8 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -155,6 +155,7 @@ namespace Avalonia.X11 public readonly IntPtr _NET_FRAME_EXTENTS; public readonly IntPtr _NET_WM_PING; public readonly IntPtr _NET_WM_SYNC_REQUEST; + public readonly IntPtr _NET_WM_SYNC_REQUEST_COUNTER; public readonly IntPtr _NET_SYSTEM_TRAY_S; public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION; public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index 9920907601..13dc460f45 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -33,6 +33,7 @@ namespace Avalonia.X11 public IntPtr LastActivityTimestamp { get; set; } public XVisualInfo? TransparentVisualInfo { get; set; } public bool HasXim { get; set; } + public bool HasXSync { get; set; } public IntPtr DefaultFontSet { get; set; } public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim) @@ -101,6 +102,15 @@ namespace Avalonia.X11 { //Ignore, XI is not supported } + + try + { + HasXSync = XSyncInitialize(display, out _, out _) != Status.Success; + } + catch + { + //Ignore, XSync is not supported + } } } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 066156a652..6d0ca9422b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -45,6 +45,8 @@ namespace Avalonia.X11 private IntPtr _handle; private IntPtr _xic; private IntPtr _renderHandle; + private IntPtr _xSyncCounter; + private XSyncValue _xSyncValue; private bool _mapped; private bool _wasMappedAtLeastOnce = false; private double? _scalingOverride; @@ -190,6 +192,16 @@ namespace Avalonia.X11 NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle); NativeControlHost = new X11NativeControlHost(_platform, this); InitializeIme(); + + XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32, + PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2); + + if (_x11.HasXSync) + { + _xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue); + XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER, + _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1); + } } class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo @@ -383,15 +395,7 @@ namespace Avalonia.X11 (ev.type == XEventName.VisibilityNotify && ev.VisibilityEvent.state < 2)) { - if (!_triggeredExpose) - { - _triggeredExpose = true; - Dispatcher.UIThread.Post(() => - { - _triggeredExpose = false; - DoPaint(); - }, DispatcherPriority.Render); - } + EnqueuePaint(); } else if (ev.type == XEventName.FocusIn) { @@ -503,6 +507,7 @@ namespace Avalonia.X11 if (_useRenderWindow) XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height); + EnqueuePaint(); } else if (ev.type == XEventName.DestroyNotify && ev.DestroyWindowEvent.window == _handle) @@ -518,7 +523,11 @@ namespace Avalonia.X11 if (Closing?.Invoke() != true) Dispose(); } - + else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST) + { + _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32(); + _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32(); + } } } else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease) @@ -730,9 +739,24 @@ namespace Avalonia.X11 ScheduleInput(mev, ref ev); } + void EnqueuePaint() + { + if (!_triggeredExpose) + { + _triggeredExpose = true; + Dispatcher.UIThread.Post(() => + { + _triggeredExpose = false; + DoPaint(); + }, DispatcherPriority.Render); + } + } + void DoPaint() { Paint?.Invoke(new Rect()); + if (_xSyncCounter != IntPtr.Zero) + XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue); } public void Invalidate(Rect rect) diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index e2b370821f..464ec4f1c8 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -542,6 +542,18 @@ namespace Avalonia.X11 public static extern int XRRQueryExtension (IntPtr dpy, out int event_base_return, out int error_base_return); + + [DllImport(libX11Ext)] + public static extern Status XSyncInitialize(IntPtr dpy, out int event_base_return, out int error_base_return); + + [DllImport(libX11Ext)] + public static extern IntPtr XSyncCreateCounter(IntPtr dpy, XSyncValue initialValue); + + [DllImport(libX11Ext)] + public static extern int XSyncDestroyCounter(IntPtr dpy, IntPtr counter); + + [DllImport(libX11Ext)] + public static extern int XSyncSetCounter(IntPtr dpy, IntPtr counter, XSyncValue value); [DllImport(libX11Randr)] public static extern int XRRQueryVersion(IntPtr dpy, @@ -627,6 +639,11 @@ namespace Avalonia.X11 public int bw; public int d; } + + public struct XSyncValue { + public int Hi; + public uint Lo; + } public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo) { From 485d9b04a9ffd779055903a2b3b16273a75b86d9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 19:51:47 +0100 Subject: [PATCH 27/34] Merge pull request #8269 from AvaloniaUI/fixes/osx-setcontent-size-shadow-invalidation Fixes/osx setcontent size shadow invalidation --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 - .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 41 ++++++------------- native/Avalonia.Native/src/OSX/WindowImpl.mm | 5 --- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 787827c9b4..2baf3b09b5 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -110,7 +110,6 @@ protected: private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); - void InitialiseNSWindow (); NSCursor *cursor; ComPtr _glContext; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 46f2ac0092..7f2bb128da 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -39,7 +39,16 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, lastMenu = nullptr; CreateNSWindow(usePanel); - InitialiseNSWindow(); + + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + [Window setOpaque:false]; + [Window setHasShadow:true]; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -90,6 +99,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + [Window setContentSize:lastSize]; + if(hasPosition) { SetPosition(lastPositionSet); @@ -292,8 +303,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if (!_shown) { BaseEvents->Resized(AvnSize{x, y}, reason); } - - if(Window != nullptr) { + else if(Window != nullptr) { [Window setContentSize:lastSize]; [Window invalidateShadow]; } @@ -569,31 +579,6 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } -void WindowBaseImpl::InitialiseNSWindow() { - if(Window != nullptr) { - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setContentSize:lastSize]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - - [Window setOpaque:false]; - - [Window setHasShadow:true]; - [Window invalidateShadow]; - - if (lastMenu != nullptr) { - [GetWindowProtocol() applyMenu:lastMenu]; - - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; - } - } - } -} - id WindowBaseImpl::GetWindowProtocol() { if(Window == nullptr) { diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 382cd0db95..6db586f3ca 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -55,11 +55,6 @@ void WindowImpl::OnInitialiseNSWindow(){ [GetWindowProtocol() setIsExtended:true]; SetExtendClientArea(true); } - - if(_parent != nullptr) - { - SetParent(_parent); - } } HRESULT WindowImpl::Show(bool activate, bool isDialog) { From 5c7a399630edde5b6fd50c70fbb3e547572fc38e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 4 Jun 2022 10:42:54 +0200 Subject: [PATCH 28/34] fix: Suppress CA1416 on Avalonia.Win32 --- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index b9a1880435..55d02b5d31 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -18,4 +18,8 @@ + + CA1416 + $(NoWarn),CA1416 + From dba9f9832e1a709d2491fb30c9d554f574920374 Mon Sep 17 00:00:00 2001 From: Oxc3 Date: Sat, 4 Jun 2022 17:03:21 -0700 Subject: [PATCH 29/34] Update AvaloniaView.razor change to the razor code so the events are invoked by the AspNetCore.EventCallbackFactory rather then an action pointer attached to the event --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index dd7eb0ec54..31425fe438 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,18 +1,18 @@ 
+ @ontouchcancel="OnTouchCancel" + @ontouchmove="OnTouchMove" + @onwheel="OnWheel" + @onkeydown="OnKeyDown" + @onkeyup="OnKeyUp" + @onpointerdown="OnPointerDown" + @onpointerup="OnPointerUp" + @onpointermove="OnPointerMove">
- From 040fe4a60307d93d8776def47c3fc9215b46bf29 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 5 Jun 2022 16:18:21 +0200 Subject: [PATCH 30/34] Fix missing PooledList usage in Calendar. --- src/Avalonia.Controls/Calendar/CalendarItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index 32fdaceacb..bcac6324ba 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -218,7 +218,7 @@ namespace Avalonia.Controls.Primitives if (YearView != null) { var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear; - var children = new List(childCount); + using var children = new PooledList(childCount); EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown; EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp; From 38b88825261005a7a53507be30aa2a45fa63c276 Mon Sep 17 00:00:00 2001 From: Oxc3 Date: Sun, 5 Jun 2022 07:21:20 -0700 Subject: [PATCH 31/34] mixing touch and pointer breaks gestures --- .../Avalonia.Web.Blazor/AvaloniaView.razor | 8 ++-- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 38 +++++-------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 31425fe438..68662bd931 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,12 +1,11 @@ 
+ @onpointermove="OnPointerMove" + @onpointercancel="OnPointerCancel"> @@ -19,6 +18,9 @@