From e6b8a7369d482bd94d1fac538aaea6d8961f1297 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Dec 2022 08:13:36 +0000 Subject: [PATCH 01/95] simply controlcatalog demo - to revert. --- samples/ControlCatalog/MainView.xaml | 232 +----------------------- samples/ControlCatalog/MainView.xaml.cs | 63 ------- 2 files changed, 6 insertions(+), 289 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 4a5f5bc96c..bdf5d15b6e 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -6,230 +6,10 @@ xmlns:pages="using:ControlCatalog.Pages" xmlns:viewModels="using:ControlCatalog.ViewModels" x:DataType="viewModels:MainWindowViewModel"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - None - BorderOnly - Full - - - - - FluentLight - FluentDark - SimpleLight - SimpleDark - - - - - None - Transparent - Blur - AcrylicBlur - Mica - - - - - LeftToRight - RightToLeft - - - - - - - - + + + + + + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 15e666ae7b..ab36a74c12 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -17,69 +17,6 @@ namespace ControlCatalog { AvaloniaXamlLoader.Load(this); - var sideBar = this.Get("Sidebar"); - - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) - { - var tabItems = (sideBar.Items as IList); - tabItems?.Add(new TabItem() - { - Header = "Screens", - Content = new ScreenPage() - }); - } - - var themes = this.Get("Themes"); - themes.SelectedItem = App.CurrentTheme; - themes.SelectionChanged += (sender, e) => - { - if (themes.SelectedItem is CatalogTheme theme) - { - App.SetThemeVariant(theme); - } - }; - - var flowDirections = this.Get("FlowDirection"); - flowDirections.SelectionChanged += (sender, e) => - { - if (flowDirections.SelectedItem is FlowDirection flowDirection) - { - this.FlowDirection = flowDirection; - } - }; - - var decorations = this.Get("Decorations"); - decorations.SelectionChanged += (sender, e) => - { - if (VisualRoot is Window window - && decorations.SelectedItem is SystemDecorations systemDecorations) - { - window.SystemDecorations = systemDecorations; - } - }; - - var transparencyLevels = this.Get("TransparencyLevels"); - IDisposable? backgroundSetter = null, paneBackgroundSetter = null; - transparencyLevels.SelectionChanged += (sender, e) => - { - backgroundSetter?.Dispose(); - paneBackgroundSetter?.Dispose(); - if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected - && selected != WindowTransparencyLevel.None) - { - var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.5); - backgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); - paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); - } - }; - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - var decorations = this.Get("Decorations"); - if (VisualRoot is Window window) - decorations.SelectedIndex = (int)window.SystemDecorations; } } } From 34a19ae48d3256d0b8d0692d5826b56febb87a07 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Dec 2022 08:14:05 +0000 Subject: [PATCH 02/95] make web canvas transparent by clearing gl buffers --- .../Avalonia.Browser/webapp/modules/avalonia/canvas.ts | 4 ++++ src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts index 47c501cbb7..c11493a68d 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts @@ -114,6 +114,10 @@ export class Canvas { this.htmlCanvas.height = this.newHeight ?? 0; } + const GL = (globalThis as any).AvaloniaGL; + const GLctx = GL.currentContext.GLctx as WebGLRenderingContext; + GLctx.clearColor(0.0, 0.0, 0.0, 0.0); + GLctx.clear(GLctx.COLOR_BUFFER_BIT | GLctx.DEPTH_BUFFER_BIT); this.renderFrameCallback(); this.renderLoopRequest = 0; diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts index 385cdd4c41..e8e37faf58 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts @@ -17,7 +17,6 @@ export class AvaloniaDOM { const canvas = document.createElement("canvas"); canvas.id = `canvas${randomIdPart}`; canvas.classList.add("avalonia-canvas"); - canvas.style.backgroundColor = "#ccc"; canvas.style.width = "100%"; canvas.style.position = "absolute"; From 42fa1c23771d7dc48d531a498a19bb801adfde26 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Dec 2022 08:14:14 +0000 Subject: [PATCH 03/95] add missing launch settings. --- .../Properties/launchSettings.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 samples/ControlCatalog.Browser/Properties/launchSettings.json diff --git a/samples/ControlCatalog.Browser/Properties/launchSettings.json b/samples/ControlCatalog.Browser/Properties/launchSettings.json new file mode 100644 index 0000000000..76c1834e3c --- /dev/null +++ b/samples/ControlCatalog.Browser/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "ControlCatalog.Browser": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}" + } + } +} From e3b2724f5a90cd7d0010d8ff14244e842a3b0dbf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 9 Dec 2022 03:31:14 -0500 Subject: [PATCH 04/95] Fix annoying warnings --- .../Avalonia.Browser.Blazor.csproj | 1 + .../Avalonia.Browser.Blazor/AvaloniaView.cs | 3 ++- .../BlazorSingleViewLifetime.cs | 1 - src/Browser/Avalonia.Browser/AvaloniaView.cs | 1 - .../Avalonia.Browser/BrowserSingleViewLifetime.cs | 2 -- .../Avalonia.Browser/BrowserTopLevelImpl.cs | 4 +++- .../Avalonia.Browser/Interop/CanvasHelper.cs | 1 - .../Skia/BrowserSkiaGpuRenderTarget.cs | 14 ++++++-------- .../Avalonia.Browser/Storage/BlobReadableStream.cs | 1 - .../Storage/BrowserStorageProvider.cs | 1 - .../Avalonia.Browser/Storage/WriteableStream.cs | 1 - 11 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj b/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj index a2f1b55b6f..a9cad0538f 100644 --- a/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj +++ b/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs b/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs index 2cc74273c0..68efea31d6 100644 --- a/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs @@ -8,9 +8,10 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using BrowserView = Avalonia.Browser.AvaloniaView; +[assembly: SupportedOSPlatform("browser")] + namespace Avalonia.Browser.Blazor; -[SupportedOSPlatform("browser")] public class AvaloniaView : ComponentBase { private Browser.AvaloniaView? _browserView; diff --git a/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs b/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs index 2432dc29a3..6e7f0de382 100644 --- a/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs +++ b/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs @@ -5,7 +5,6 @@ using Avalonia.Controls.ApplicationLifetimes; namespace Avalonia.Browser.Blazor; -[SupportedOSPlatform("browser")] public static class WebAppBuilder { public static T SetupWithSingleViewLifetime( diff --git a/src/Browser/Avalonia.Browser/AvaloniaView.cs b/src/Browser/Avalonia.Browser/AvaloniaView.cs index a407e1e4d8..8bc09318d7 100644 --- a/src/Browser/Avalonia.Browser/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser/AvaloniaView.cs @@ -17,7 +17,6 @@ using SkiaSharp; namespace Avalonia.Browser { - [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings public partial class AvaloniaView : ITextInputMethodImpl { private static readonly PooledList s_intermediatePointsPooledList = new(ClearMode.Never); diff --git a/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs index ee4f6eca9b..384c8d58dc 100644 --- a/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs +++ b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs @@ -7,7 +7,6 @@ using Avalonia.Platform; namespace Avalonia.Browser; -[SupportedOSPlatform("browser")] public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime { public AvaloniaView? View; @@ -24,7 +23,6 @@ public class BrowserPlatformOptions public Func FrameworkAssetPathResolver { get; set; } = new(fileName => $"./{fileName}"); } -[SupportedOSPlatform("browser")] public static class WebAppBuilder { public static T SetupBrowserApp( diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs index 69e2d27181..6bf22767c0 100644 --- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs +++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.Versioning; using Avalonia.Browser.Skia; using Avalonia.Browser.Storage; using Avalonia.Controls; @@ -14,9 +15,10 @@ using Avalonia.Platform.Storage; using Avalonia.Rendering; using Avalonia.Rendering.Composition; +[assembly: SupportedOSPlatform("browser")] + namespace Avalonia.Browser { - [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings internal class BrowserTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider { private Size _clientSize; diff --git a/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs b/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs index 8321b00658..27a2b1dcb7 100644 --- a/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs @@ -6,7 +6,6 @@ namespace Avalonia.Browser.Interop; internal record GLInfo(int ContextId, uint FboId, int Stencils, int Samples, int Depth); -[System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings internal static partial class CanvasHelper { diff --git a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs index f69dd3c344..9424122ce8 100644 --- a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs @@ -14,14 +14,12 @@ namespace Avalonia.Browser.Skia _size = browserSkiaSurface.Size; var glFbInfo = new GRGlFramebufferInfo(browserSkiaSurface.GlInfo.FboId, browserSkiaSurface.ColorType.ToGlSizedFormat()); - { - _browserSkiaSurface = browserSkiaSurface; - _renderTarget = new GRBackendRenderTarget( - (int)(browserSkiaSurface.Size.Width * browserSkiaSurface.Scaling), - (int)(browserSkiaSurface.Size.Height * browserSkiaSurface.Scaling), - browserSkiaSurface.GlInfo.Samples, - browserSkiaSurface.GlInfo.Stencils, glFbInfo); - } + _browserSkiaSurface = browserSkiaSurface; + _renderTarget = new GRBackendRenderTarget( + (int)(browserSkiaSurface.Size.Width * browserSkiaSurface.Scaling), + (int)(browserSkiaSurface.Size.Height * browserSkiaSurface.Scaling), + browserSkiaSurface.GlInfo.Samples, + browserSkiaSurface.GlInfo.Stencils, glFbInfo); } public void Dispose() diff --git a/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs b/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs index 4fce190346..3404452bca 100644 --- a/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs +++ b/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs @@ -7,7 +7,6 @@ using Avalonia.Browser.Interop; namespace Avalonia.Browser.Storage; -[System.Runtime.Versioning.SupportedOSPlatform("browser")] internal class BlobReadableStream : Stream { private JSObject? _jSReference; diff --git a/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs index 28de55092b..dc580f0fcb 100644 --- a/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs +++ b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs @@ -13,7 +13,6 @@ namespace Avalonia.Browser.Storage; internal record FilePickerAcceptType(string Description, IReadOnlyDictionary> Accept); -[SupportedOSPlatform("browser")] internal class BrowserStorageProvider : IStorageProvider { internal const string PickerCancelMessage = "The user aborted a request"; diff --git a/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs b/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs index f29f7420ac..b2f14b6a7a 100644 --- a/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs +++ b/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs @@ -7,7 +7,6 @@ using Avalonia.Browser.Interop; namespace Avalonia.Browser.Storage; -[System.Runtime.Versioning.SupportedOSPlatform("browser")] // Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream internal sealed class WriteableStream : Stream { From c757edeea5246737e2d8c2a0b573d1a52aaf00c2 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 9 Dec 2022 03:31:36 -0500 Subject: [PATCH 05/95] Fix WASM SetTransparencyLevelHint --- src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs index 6bf22767c0..95efbe2304 100644 --- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs +++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs @@ -202,7 +202,11 @@ namespace Avalonia.Browser public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { - + if (transparencyLevel == WindowTransparencyLevel.None + || transparencyLevel == WindowTransparencyLevel.Transparent) + { + TransparencyLevel = transparencyLevel; + } } public Size ClientSize => _clientSize; @@ -222,7 +226,7 @@ namespace Avalonia.Browser public IMouseDevice MouseDevice { get; } = new MouseDevice(); public IKeyboardDevice KeyboardDevice { get; } = BrowserWindowingPlatform.Keyboard; - public WindowTransparencyLevel TransparencyLevel { get; } + public WindowTransparencyLevel TransparencyLevel { get; private set; } public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } public ITextInputMethodImpl TextInputMethod => _avaloniaView; From 0dc65db7baac55d4e57c75087142a113ca59a71c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 9 Dec 2022 05:11:26 -0500 Subject: [PATCH 06/95] Revert "simply controlcatalog demo - to revert." This reverts commit e6b8a7369d482bd94d1fac538aaea6d8961f1297. --- samples/ControlCatalog/MainView.xaml | 232 +++++++++++++++++++++++- samples/ControlCatalog/MainView.xaml.cs | 63 +++++++ 2 files changed, 289 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index bdf5d15b6e..4a5f5bc96c 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -6,10 +6,230 @@ xmlns:pages="using:ControlCatalog.Pages" xmlns:viewModels="using:ControlCatalog.ViewModels" x:DataType="viewModels:MainWindowViewModel"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + BorderOnly + Full + + + + + FluentLight + FluentDark + SimpleLight + SimpleDark + + + + + None + Transparent + Blur + AcrylicBlur + Mica + + + + + LeftToRight + RightToLeft + + + + + + + + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index ab36a74c12..15e666ae7b 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -17,6 +17,69 @@ namespace ControlCatalog { AvaloniaXamlLoader.Load(this); + var sideBar = this.Get("Sidebar"); + + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) + { + var tabItems = (sideBar.Items as IList); + tabItems?.Add(new TabItem() + { + Header = "Screens", + Content = new ScreenPage() + }); + } + + var themes = this.Get("Themes"); + themes.SelectedItem = App.CurrentTheme; + themes.SelectionChanged += (sender, e) => + { + if (themes.SelectedItem is CatalogTheme theme) + { + App.SetThemeVariant(theme); + } + }; + + var flowDirections = this.Get("FlowDirection"); + flowDirections.SelectionChanged += (sender, e) => + { + if (flowDirections.SelectedItem is FlowDirection flowDirection) + { + this.FlowDirection = flowDirection; + } + }; + + var decorations = this.Get("Decorations"); + decorations.SelectionChanged += (sender, e) => + { + if (VisualRoot is Window window + && decorations.SelectedItem is SystemDecorations systemDecorations) + { + window.SystemDecorations = systemDecorations; + } + }; + + var transparencyLevels = this.Get("TransparencyLevels"); + IDisposable? backgroundSetter = null, paneBackgroundSetter = null; + transparencyLevels.SelectionChanged += (sender, e) => + { + backgroundSetter?.Dispose(); + paneBackgroundSetter?.Dispose(); + if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected + && selected != WindowTransparencyLevel.None) + { + var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.5); + backgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); + paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); + } + }; + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + var decorations = this.Get("Decorations"); + if (VisualRoot is Window window) + decorations.SelectedIndex = (int)window.SystemDecorations; } } } From 001484e71913ef880149f5986dd47d5b5ba18d9a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 9 Dec 2022 05:32:04 -0500 Subject: [PATCH 07/95] Remove glctx hack --- .../Avalonia.Browser/webapp/modules/avalonia/canvas.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts index c11493a68d..47c501cbb7 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts @@ -114,10 +114,6 @@ export class Canvas { this.htmlCanvas.height = this.newHeight ?? 0; } - const GL = (globalThis as any).AvaloniaGL; - const GLctx = GL.currentContext.GLctx as WebGLRenderingContext; - GLctx.clearColor(0.0, 0.0, 0.0, 0.0); - GLctx.clear(GLctx.COLOR_BUFFER_BIT | GLctx.DEPTH_BUFFER_BIT); this.renderFrameCallback(); this.renderLoopRequest = 0; From 08a494ac7df58ee7f8f346d42572bc861ff91a6b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 9 Dec 2022 05:32:21 -0500 Subject: [PATCH 08/95] Update view transparency selection on the control catalog --- samples/ControlCatalog/MainView.xaml | 3 +-- samples/ControlCatalog/MainView.xaml.cs | 23 +++++++++++++------ samples/ControlCatalog/MainWindow.xaml | 1 - .../Pages/WindowCustomizationsPage.xaml | 7 ------ .../ViewModels/MainWindowViewModel.cs | 7 ------ 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 4a5f5bc96c..1e558f0263 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -206,8 +206,7 @@ + HorizontalAlignment="Stretch"> None Transparent diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 15e666ae7b..f3c1a68e72 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Immutable; +using Avalonia.VisualTree; using ControlCatalog.Models; using ControlCatalog.Pages; @@ -59,17 +60,25 @@ namespace ControlCatalog }; var transparencyLevels = this.Get("TransparencyLevels"); - IDisposable? backgroundSetter = null, paneBackgroundSetter = null; + IDisposable? topLevelBackgroundSideSetter = null, sideBarBackgroundSetter = null, paneBackgroundSetter = null; transparencyLevels.SelectionChanged += (sender, e) => { - backgroundSetter?.Dispose(); + topLevelBackgroundSideSetter?.Dispose(); + sideBarBackgroundSetter?.Dispose(); paneBackgroundSetter?.Dispose(); - if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected - && selected != WindowTransparencyLevel.None) + if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected) { - var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.5); - backgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); - paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); + var topLevel = (TopLevel)this.GetVisualRoot()!; + topLevel.TransparencyLevelHint = selected; + + if (selected != WindowTransparencyLevel.None) + { + var transparentBrush = new ImmutableSolidColorBrush(Colors.White, 0); + var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.2); + topLevelBackgroundSideSetter = topLevel.SetValue(BackgroundProperty, transparentBrush, Avalonia.Data.BindingPriority.Style); + sideBarBackgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); + paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style); + } } }; } diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index cebb3e0916..442c1d37b0 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -10,7 +10,6 @@ ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}" ExtendClientAreaChromeHints="{Binding ChromeHints}" ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}" - TransparencyLevelHint="{Binding TransparencyLevel}" x:Name="MainWindow" Background="Transparent" x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" diff --git a/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml b/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml index f1403905c4..d690058b27 100644 --- a/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml +++ b/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml @@ -11,12 +11,5 @@ - - None - Transparent - Blur - AcrylicBlur - Mica - diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index b79eff780c..3bb4202166 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -17,7 +17,6 @@ namespace ControlCatalog.ViewModels private bool _isMenuItemChecked = true; private WindowState _windowState; private WindowState[] _windowStates = Array.Empty(); - private int _transparencyLevel; private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome; private bool _extendClientAreaEnabled; private bool _systemTitleBarEnabled; @@ -77,12 +76,6 @@ namespace ControlCatalog.ViewModels TitleBarHeight = -1; } - public int TransparencyLevel - { - get { return _transparencyLevel; } - set { this.RaiseAndSetIfChanged(ref _transparencyLevel, value); } - } - public ExtendClientAreaChromeHints ChromeHints { get { return _chromeHints; } From b0aaf543e843c43ebed3803c1817743b5597e03d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 12 Dec 2022 13:39:34 +0000 Subject: [PATCH 09/95] add android transparency support --- .../ControlCatalog.Android/MainActivity.cs | 2 +- .../Resources/values/styles.xml | 4 +++ .../ControlCatalog.Android/SplashActivity.cs | 2 ++ src/Android/Avalonia.Android/AvaloniaView.cs | 2 ++ .../InvalidationAwareSurfaceView.cs | 1 + .../Platform/SkiaPlatform/TopLevelImpl.cs | 31 +++++++++++++++++-- 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 62c582610c..f6fa07dbde 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -5,7 +5,7 @@ using Avalonia.Android; namespace ControlCatalog.Android { - [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AvaloniaMainActivity { } diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml index 2759d2904a..49e079a719 100644 --- a/samples/ControlCatalog.Android/Resources/values/styles.xml +++ b/samples/ControlCatalog.Android/Resources/values/styles.xml @@ -14,4 +14,8 @@ @null + + diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs index 908b5f082a..a0b68b129b 100644 --- a/samples/ControlCatalog.Android/SplashActivity.cs +++ b/samples/ControlCatalog.Android/SplashActivity.cs @@ -28,6 +28,8 @@ namespace ControlCatalog.Android base.OnResume(); StartActivity(new Intent(Application.Context, typeof(MainActivity))); + + Finish(); } } } diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 5267843bfc..f7e32f99db 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -24,6 +24,8 @@ namespace Avalonia.Android _root = new EmbeddableControlRoot(_view); _root.Prepare(); + + this.SetBackgroundColor(global::Android.Graphics.Color.Transparent); } internal TopLevelImpl TopLevelImpl => _view; diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs index 33501ece06..f205458f0e 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs @@ -22,6 +22,7 @@ namespace Avalonia.Android public InvalidationAwareSurfaceView(Context context) : base(context) { Holder.AddCallback(this); + Holder.SetFormat(global::Android.Graphics.Format.Transparent); _handler = new Handler(context.MainLooper); } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 4150b52946..62f047b826 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -27,6 +27,8 @@ using Avalonia.Rendering.Composition; using Java.Lang; using Math = System.Math; using AndroidRect = Android.Graphics.Rect; +using AndroidX.Core.App; +using Android.Graphics.Drawables; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -279,7 +281,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public Action LostFocus { get; set; } public Action TransparencyLevelChanged { get; set; } - public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None; + public WindowTransparencyLevel TransparencyLevel { get; private set; } public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1); @@ -297,7 +299,32 @@ namespace Avalonia.Android.Platform.SkiaPlatform public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { - throw new NotImplementedException(); + if (TransparencyLevel != transparencyLevel) + { + bool isLegacy = Build.VERSION.SdkInt < BuildVersionCodes.R; + if (_view.Context is AvaloniaMainActivity activity) + { + switch (transparencyLevel) + { + case WindowTransparencyLevel.None: + if (!isLegacy) + { + activity.SetTranslucent(false); + } + break; + case WindowTransparencyLevel.Transparent: + if (!isLegacy) + { + activity.SetTranslucent(true); + } + activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent)); + break; + default: + return; + } + TransparencyLevel = transparencyLevel; + } + } } } From 4f05fb47b72e563940bb34c86970dcd6fbc79745 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 12 Dec 2022 14:18:05 +0000 Subject: [PATCH 10/95] add blur support for android s + --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 66 +++++++++++++++++-- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 62f047b826..7ec3c5b1f0 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -27,7 +27,6 @@ using Avalonia.Rendering.Composition; using Java.Lang; using Math = System.Math; using AndroidRect = Android.Graphics.Rect; -using AndroidX.Core.App; using Android.Graphics.Drawables; namespace Avalonia.Android.Platform.SkiaPlatform @@ -301,26 +300,81 @@ namespace Avalonia.Android.Platform.SkiaPlatform { if (TransparencyLevel != transparencyLevel) { - bool isLegacy = Build.VERSION.SdkInt < BuildVersionCodes.R; + bool isBelowR = Build.VERSION.SdkInt < BuildVersionCodes.R; + bool isAboveR = Build.VERSION.SdkInt > BuildVersionCodes.R; if (_view.Context is AvaloniaMainActivity activity) { switch (transparencyLevel) { + case WindowTransparencyLevel.AcrylicBlur: + case WindowTransparencyLevel.ForceAcrylicBlur: + case WindowTransparencyLevel.Mica: case WindowTransparencyLevel.None: - if (!isLegacy) + if (!isBelowR) { activity.SetTranslucent(false); } + if (isAboveR) + { + activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind); + + var attr = activity.Window?.Attributes; + if (attr != null) + { + attr.BlurBehindRadius = 0; + + activity.Window.Attributes = attr; + } + } + activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White)); + + if(transparencyLevel != WindowTransparencyLevel.None) + { + return; + } break; case WindowTransparencyLevel.Transparent: - if (!isLegacy) + if (!isBelowR) { activity.SetTranslucent(true); } + if (isAboveR) + { + activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind); + + var attr = activity.Window?.Attributes; + if (attr != null) + { + attr.BlurBehindRadius = 0; + + activity.Window.Attributes = attr; + } + } activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent)); break; - default: - return; + case WindowTransparencyLevel.Blur: + if (isAboveR) + { + activity.SetTranslucent(true); + activity.Window?.AddFlags(WindowManagerFlags.BlurBehind); + + var attr = activity.Window?.Attributes; + if (attr != null) + { + attr.BlurBehindRadius = 120; + + activity.Window.Attributes = attr; + } + activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent)); + } + else + { + activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind); + activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White)); + + return; + } + break; } TransparencyLevel = transparencyLevel; } From 2f431b957043b53f98ecde0231fa3a36290416cf Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 14 Dec 2022 12:38:32 +0100 Subject: [PATCH 11/95] fix: Warning CS0436 --- src/Avalonia.X11/Avalonia.X11.csproj | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index c762349c1c..061c516ef6 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -10,7 +10,12 @@ - - - + + + + + + + + From 11b0de3481b8e9950b42fee8fc1869def260e397 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 8 Dec 2022 14:28:33 +0000 Subject: [PATCH 12/95] add hold gesture recognizer --- .../HoldGestureRecognizer.cs | 124 ++++++++++++++++++ src/Avalonia.Base/Input/Gestures.cs | 4 + .../Input/HoldGestureEventArgs.cs | 31 +++++ src/Avalonia.Controls/Control.cs | 19 +++ 4 files changed, 178 insertions(+) create mode 100644 src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs create mode 100644 src/Avalonia.Base/Input/HoldGestureEventArgs.cs diff --git a/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs new file mode 100644 index 0000000000..02fd800baf --- /dev/null +++ b/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs @@ -0,0 +1,124 @@ +๏ปฟusing System.Timers; +using Avalonia.Input.GestureRecognizers; +using Avalonia.Threading; + +namespace Avalonia.Input +{ + public class HoldGestureRecognizer : StyledElement, IGestureRecognizer + { + private const int Tolerance = 30; + private IInputElement? _target; + private IGestureRecognizerActionsDispatcher? _actions; + private int _gestureId; + private IPointer? _tracking; + private PointerPressedEventArgs? _pointerEventArgs; + private Rect _trackingBounds; + private Timer? _holdTimer; + private bool _elasped; + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoldWithMouseEnabledProperty = + AvaloniaProperty.Register( + nameof(IsHoldWithMouseEnabled)); + + /// + /// Gets or sets whether to detect hold from the mouse + /// + public bool IsHoldWithMouseEnabled + { + get => GetValue(IsHoldWithMouseEnabledProperty); + set => SetValue(IsHoldWithMouseEnabledProperty, value); + } + + public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) + { + _target = target; + _actions = actions; + + _target?.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); + _target?.AddHandler(InputElement.PointerReleasedEvent, OnPointerReleased, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); + + _holdTimer = new Timer(300); + _holdTimer.AutoReset = false; + _holdTimer.Elapsed += HoldTimer_Elapsed; + } + + private async void HoldTimer_Elapsed(object? sender, ElapsedEventArgs e) + { + _elasped = true; + _holdTimer?.Stop(); + + if(_tracking != null) + { + await Dispatcher.UIThread.InvokeAsync(() => _target?.RaiseEvent(new HoldGestureEventArgs(_gestureId, _pointerEventArgs, HoldingState.Started))); + } + } + + private void OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + PointerPressed(e); + } + + private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + PointerReleased(e); + } + + public void PointerCaptureLost(IPointer pointer) + { + if (_tracking == pointer) + { + EndHold(!_elasped); + } + } + + public void PointerMoved(PointerEventArgs e) + { + if (_tracking == e.Pointer && _target is Visual visual) + { + var currentPosition = e.GetPosition(visual); + + if (!_trackingBounds.Contains(currentPosition)) + { + EndHold(true); + } + } + } + + public void PointerPressed(PointerPressedEventArgs e) + { + if (_target != null && _target is Visual visual && (IsHoldWithMouseEnabled || e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) + { + _elasped = false; + var position = e.GetPosition(visual); + _gestureId = HoldGestureEventArgs.GetNextFreeId(); + _tracking = e.Pointer; + _pointerEventArgs = e; + + _trackingBounds = new Rect(position.X - Tolerance / 2, position.Y - Tolerance / 2, Tolerance, Tolerance); + + _holdTimer?.Start(); + } + } + + public void PointerReleased(PointerReleasedEventArgs e) + { + if (_tracking == e.Pointer) + { + EndHold(!_elasped); + } + } + + private void EndHold(bool cancelled) + { + _holdTimer?.Stop(); + + _tracking = null; + _trackingBounds = default; + + _target?.RaiseEvent(new HoldGestureEventArgs(_gestureId, _pointerEventArgs, cancelled ? HoldingState.Cancelled : HoldingState.Completed)); + } + } +} diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index b4d5feaf3b..1c2220baf4 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -58,6 +58,10 @@ namespace Avalonia.Input RoutedEvent.Register( "PullGesture", RoutingStrategies.Bubble, typeof(Gestures)); + public static readonly RoutedEvent HoldGestureEvent = + RoutedEvent.Register( + "HoldGesture", RoutingStrategies.Bubble, typeof(Gestures)); + public static readonly RoutedEvent PullGestureEndedEvent = RoutedEvent.Register( "PullGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); diff --git a/src/Avalonia.Base/Input/HoldGestureEventArgs.cs b/src/Avalonia.Base/Input/HoldGestureEventArgs.cs new file mode 100644 index 0000000000..63a508b1f4 --- /dev/null +++ b/src/Avalonia.Base/Input/HoldGestureEventArgs.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia.Interactivity; + +namespace Avalonia.Input +{ + public class HoldGestureEventArgs : RoutedEventArgs + { + public int Id { get; } + public Vector Delta { get; } + public HoldingState HoldingState { get; } + public PointerEventArgs? PointerEventArgs { get; } + + private static int _nextId = 1; + + internal static int GetNextFreeId() => _nextId++; + + public HoldGestureEventArgs(int id, PointerEventArgs? pointerEventArgs, HoldingState holdingState) : base(Gestures.HoldGestureEvent) + { + Id = id; + HoldingState = holdingState; + PointerEventArgs = pointerEventArgs; + } + } + + public enum HoldingState + { + Started, + Completed, + Cancelled, + } +} diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 88c9823952..de91fb83c9 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -216,6 +216,12 @@ namespace Avalonia.Controls /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; + public Control() + { + // Add a default HoldGestureRecognizer + GestureRecognizers.Add(new HoldGestureRecognizer()); + } + /// void ISetterValue.Initialize(ISetter setter) { @@ -366,16 +372,29 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTreeCore(e); + AddHandler(Gestures.HoldGestureEvent, OnHoldEvent); + InitializeIfNeeded(); ScheduleOnLoadedCore(); } + private void OnHoldEvent(object? sender, HoldGestureEventArgs e) + { + if(e.HoldingState == HoldingState.Started) + { + // Trigger ContentRequest when hold starts + RaiseEvent(new ContextRequestedEventArgs(e.PointerEventArgs!)); + } + } + /// protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTreeCore(e); + RemoveHandler(Gestures.HoldGestureEvent, OnHoldEvent); + OnUnloadedCore(); } From 8945e1b77c78223b4172f419c5d916593f88c62d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 9 Dec 2022 13:48:14 +0000 Subject: [PATCH 13/95] drop hold gesture recognizer --- .../HoldGestureRecognizer.cs | 124 ------------------ src/Avalonia.Base/Input/Gestures.cs | 93 ++++++++++++- .../Input/HoldGestureEventArgs.cs | 31 ----- .../Input/HoldingRoutedEventArgs.cs | 22 ++++ src/Avalonia.Base/Input/InputElement.cs | 46 +++++++ src/Avalonia.Controls/Control.cs | 18 +-- 6 files changed, 163 insertions(+), 171 deletions(-) delete mode 100644 src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs delete mode 100644 src/Avalonia.Base/Input/HoldGestureEventArgs.cs create mode 100644 src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs diff --git a/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs deleted file mode 100644 index 02fd800baf..0000000000 --- a/src/Avalonia.Base/Input/GestureRecognizers/HoldGestureRecognizer.cs +++ /dev/null @@ -1,124 +0,0 @@ -๏ปฟusing System.Timers; -using Avalonia.Input.GestureRecognizers; -using Avalonia.Threading; - -namespace Avalonia.Input -{ - public class HoldGestureRecognizer : StyledElement, IGestureRecognizer - { - private const int Tolerance = 30; - private IInputElement? _target; - private IGestureRecognizerActionsDispatcher? _actions; - private int _gestureId; - private IPointer? _tracking; - private PointerPressedEventArgs? _pointerEventArgs; - private Rect _trackingBounds; - private Timer? _holdTimer; - private bool _elasped; - - /// - /// Defines the property. - /// - public static readonly StyledProperty IsHoldWithMouseEnabledProperty = - AvaloniaProperty.Register( - nameof(IsHoldWithMouseEnabled)); - - /// - /// Gets or sets whether to detect hold from the mouse - /// - public bool IsHoldWithMouseEnabled - { - get => GetValue(IsHoldWithMouseEnabledProperty); - set => SetValue(IsHoldWithMouseEnabledProperty, value); - } - - public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) - { - _target = target; - _actions = actions; - - _target?.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); - _target?.AddHandler(InputElement.PointerReleasedEvent, OnPointerReleased, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); - - _holdTimer = new Timer(300); - _holdTimer.AutoReset = false; - _holdTimer.Elapsed += HoldTimer_Elapsed; - } - - private async void HoldTimer_Elapsed(object? sender, ElapsedEventArgs e) - { - _elasped = true; - _holdTimer?.Stop(); - - if(_tracking != null) - { - await Dispatcher.UIThread.InvokeAsync(() => _target?.RaiseEvent(new HoldGestureEventArgs(_gestureId, _pointerEventArgs, HoldingState.Started))); - } - } - - private void OnPointerPressed(object? sender, PointerPressedEventArgs e) - { - PointerPressed(e); - } - - private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) - { - PointerReleased(e); - } - - public void PointerCaptureLost(IPointer pointer) - { - if (_tracking == pointer) - { - EndHold(!_elasped); - } - } - - public void PointerMoved(PointerEventArgs e) - { - if (_tracking == e.Pointer && _target is Visual visual) - { - var currentPosition = e.GetPosition(visual); - - if (!_trackingBounds.Contains(currentPosition)) - { - EndHold(true); - } - } - } - - public void PointerPressed(PointerPressedEventArgs e) - { - if (_target != null && _target is Visual visual && (IsHoldWithMouseEnabled || e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) - { - _elasped = false; - var position = e.GetPosition(visual); - _gestureId = HoldGestureEventArgs.GetNextFreeId(); - _tracking = e.Pointer; - _pointerEventArgs = e; - - _trackingBounds = new Rect(position.X - Tolerance / 2, position.Y - Tolerance / 2, Tolerance, Tolerance); - - _holdTimer?.Start(); - } - } - - public void PointerReleased(PointerReleasedEventArgs e) - { - if (_tracking == e.Pointer) - { - EndHold(!_elasped); - } - } - - private void EndHold(bool cancelled) - { - _holdTimer?.Stop(); - - _tracking = null; - _trackingBounds = default; - - _target?.RaiseEvent(new HoldGestureEventArgs(_gestureId, _pointerEventArgs, cancelled ? HoldingState.Cancelled : HoldingState.Completed)); - } - } -} diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 1c2220baf4..06d90f1c27 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -1,6 +1,8 @@ using System; +using System.Threading; using Avalonia.Interactivity; using Avalonia.Platform; +using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Input @@ -8,6 +10,16 @@ namespace Avalonia.Input public static class Gestures { private static bool s_isDoubleTapped = false; + private static bool s_isHolding; + private static CancellationTokenSource? s_holdCancellationToken; + + /* /// + /// Defines the property. + /// + public static readonly AttachedProperty IsHoldWithMouseEnabledProperty = + AvaloniaProperty.RegisterAttached( + "IsHoldWithMouseEnabled");*/ + public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, @@ -45,6 +57,7 @@ namespace Avalonia.Input private static readonly WeakReference s_lastPress = new WeakReference(null); private static Point s_lastPressPoint; + private static IPointer? s_lastPointer; public static readonly RoutedEvent PinchEvent = RoutedEvent.Register( @@ -58,9 +71,9 @@ namespace Avalonia.Input RoutedEvent.Register( "PullGesture", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent HoldGestureEvent = - RoutedEvent.Register( - "HoldGesture", RoutingStrategies.Bubble, typeof(Gestures)); + public static readonly RoutedEvent HoldingEvent = + RoutedEvent.Register( + "Holding", RoutingStrategies.Bubble, typeof(Gestures)); public static readonly RoutedEvent PullGestureEndedEvent = RoutedEvent.Register( @@ -70,6 +83,7 @@ namespace Avalonia.Input { InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed); InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased); + InputElement.PointerMovedEvent.RouteFinished.Subscribe(PointerMoved); } public static void AddTappedHandler(Interactive element, EventHandler handler) @@ -114,11 +128,38 @@ namespace Avalonia.Input var e = (PointerPressedEventArgs)ev; var visual = (Visual)ev.Source; + if(s_lastPointer != null) + { + if(s_isHolding && ev.Source is Interactive i) + { + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled)); + } + s_holdCancellationToken?.Cancel(); + s_holdCancellationToken?.Dispose(); + s_holdCancellationToken = null; + + s_lastPointer = null; + } + + s_isHolding = false; + if (e.ClickCount % 2 == 1) { s_isDoubleTapped = false; s_lastPress.SetTarget(ev.Source); + s_lastPointer = e.Pointer; s_lastPressPoint = e.GetPosition((Visual)ev.Source); + s_holdCancellationToken = new CancellationTokenSource(); + var token = s_holdCancellationToken.Token; + var settings = AvaloniaLocator.Current.GetService(); + DispatcherTimer.RunOnce(() => + { + if (!token.IsCancellationRequested && e.Source is InputElement i && i.IsHoldingEnabled && ( e.Pointer.Type != PointerType.Mouse || i.IsHoldWithMouseEnabled)) + { + s_isHolding = true; + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started)); + } + }, TimeSpan.FromMilliseconds(300)); } else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) { @@ -152,7 +193,12 @@ namespace Avalonia.Input if (tapRect.ContainsExclusive(point.Position)) { - if (e.InitialPressMouseButton == MouseButton.Right) + if(s_isHolding) + { + s_isHolding = false; + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Completed)); + } + else if (e.InitialPressMouseButton == MouseButton.Right) { i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); } @@ -164,6 +210,45 @@ namespace Avalonia.Input } } } + + s_holdCancellationToken?.Cancel(); + s_holdCancellationToken?.Dispose(); + s_holdCancellationToken = null; + s_lastPointer = null; + } + } + + private static void PointerMoved(RoutedEventArgs ev) + { + if (ev.Route == RoutingStrategies.Bubble) + { + var e = (PointerEventArgs)ev; + if (s_lastPress.TryGetTarget(out var target)) + { + if (e.Pointer == s_lastPointer) + { + var point = e.GetCurrentPoint((Visual)target); + var settings = AvaloniaLocator.Current.GetService(); + var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4); + var tapRect = new Rect(s_lastPressPoint, new Size()) + .Inflate(new Thickness(tapSize.Width, tapSize.Height)); + + if (tapRect.ContainsExclusive(point.Position)) + { + return; + } + } + + if (s_isHolding && ev.Source is Interactive i) + { + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled)); + } + } + + s_holdCancellationToken?.Cancel(); + s_holdCancellationToken?.Dispose(); + s_holdCancellationToken = null; + s_isHolding = false; } } } diff --git a/src/Avalonia.Base/Input/HoldGestureEventArgs.cs b/src/Avalonia.Base/Input/HoldGestureEventArgs.cs deleted file mode 100644 index 63a508b1f4..0000000000 --- a/src/Avalonia.Base/Input/HoldGestureEventArgs.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Avalonia.Interactivity; - -namespace Avalonia.Input -{ - public class HoldGestureEventArgs : RoutedEventArgs - { - public int Id { get; } - public Vector Delta { get; } - public HoldingState HoldingState { get; } - public PointerEventArgs? PointerEventArgs { get; } - - private static int _nextId = 1; - - internal static int GetNextFreeId() => _nextId++; - - public HoldGestureEventArgs(int id, PointerEventArgs? pointerEventArgs, HoldingState holdingState) : base(Gestures.HoldGestureEvent) - { - Id = id; - HoldingState = holdingState; - PointerEventArgs = pointerEventArgs; - } - } - - public enum HoldingState - { - Started, - Completed, - Cancelled, - } -} diff --git a/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs new file mode 100644 index 0000000000..5826c5b994 --- /dev/null +++ b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs @@ -0,0 +1,22 @@ +using System; +using Avalonia.Interactivity; + +namespace Avalonia.Input +{ + public class HoldingRoutedEventArgs : RoutedEventArgs + { + public HoldingState HoldingState { get; } + + public HoldingRoutedEventArgs(HoldingState holdingState) : base(Gestures.HoldingEvent) + { + HoldingState = holdingState; + } + } + + public enum HoldingState + { + Started, + Completed, + Cancelled, + } +} diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index fa755277cc..7f2cbec299 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -45,6 +45,18 @@ namespace Avalonia.Input public static readonly StyledProperty CursorProperty = AvaloniaProperty.Register(nameof(Cursor), null, true); + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoldingEnabledProperty = + AvaloniaProperty.Register(nameof(IsHoldingEnabled), true); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoldWithMouseEnabledProperty = + AvaloniaProperty.Register(nameof(IsHoldWithMouseEnabled), false); + /// /// Defines the property. /// @@ -188,6 +200,11 @@ namespace Avalonia.Input /// public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; + /// + /// Defines the event. + /// + public static readonly RoutedEvent HoldingEvent = Gestures.HoldingEvent; + /// /// Defines the event. /// @@ -352,6 +369,15 @@ namespace Avalonia.Input add { AddHandler(TappedEvent, value); } remove { RemoveHandler(TappedEvent, value); } } + + /// + /// Occurs when a hold gesture occurs on the control. + /// + public event EventHandler? Holding + { + add { AddHandler(HoldingEvent, value); } + remove { RemoveHandler(HoldingEvent, value); } + } /// /// Occurs when a double-tap gesture occurs on the control. @@ -388,6 +414,26 @@ namespace Avalonia.Input get { return GetValue(CursorProperty); } set { SetValue(CursorProperty, value); } } + + /// + /// Gets or sets a value that determines whether the Holding event can originate + /// from that element. + /// + public bool IsHoldingEnabled + { + get { return GetValue(IsHoldingEnabledProperty); } + set { SetValue(IsHoldingEnabledProperty, value); } + } + + /// + /// Gets or sets a value that determines whether the Holding event can originate + /// from that element. + /// + public bool IsHoldWithMouseEnabled + { + get { return GetValue(IsHoldWithMouseEnabledProperty); } + set { SetValue(IsHoldWithMouseEnabledProperty, value); } + } /// /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements. diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index de91fb83c9..bb83f9e8bb 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -216,12 +216,6 @@ namespace Avalonia.Controls /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; - public Control() - { - // Add a default HoldGestureRecognizer - GestureRecognizers.Add(new HoldGestureRecognizer()); - } - /// void ISetterValue.Initialize(ISetter setter) { @@ -372,19 +366,19 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTreeCore(e); - AddHandler(Gestures.HoldGestureEvent, OnHoldEvent); + AddHandler(Gestures.HoldingEvent, OnHoldEvent); InitializeIfNeeded(); ScheduleOnLoadedCore(); } - private void OnHoldEvent(object? sender, HoldGestureEventArgs e) + private void OnHoldEvent(object? sender, HoldingRoutedEventArgs e) { - if(e.HoldingState == HoldingState.Started) + if(e.HoldingState == HoldingState.Completed) { - // Trigger ContentRequest when hold starts - RaiseEvent(new ContextRequestedEventArgs(e.PointerEventArgs!)); + // Trigger ContentRequest when hold is complete + RaiseEvent(new ContextRequestedEventArgs()); } } @@ -393,7 +387,7 @@ namespace Avalonia.Controls { base.OnDetachedFromVisualTreeCore(e); - RemoveHandler(Gestures.HoldGestureEvent, OnHoldEvent); + RemoveHandler(Gestures.HoldingEvent, OnHoldEvent); OnUnloadedCore(); } From c804fa00cfe323a00ca7fa6f9e1c91f4ae0f064e Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 9 Dec 2022 18:13:32 +0000 Subject: [PATCH 14/95] fix tests --- .../Input/GesturesTests.cs | 11 +++++++ .../Input/TouchDeviceTests.cs | 2 +- .../ButtonTests.cs | 3 ++ .../ComboBoxTests.cs | 4 +++ .../ListBoxTests_Single.cs | 13 +++++---- .../Mixins/PressedMixinTests.cs | 3 ++ .../Primitives/SelectingItemsControlTests.cs | 8 ++--- .../SelectingItemsControlTests_Multiple.cs | 29 ++++++++++--------- .../TreeViewTests.cs | 3 +- tests/Avalonia.UnitTests/TestServices.cs | 3 +- 10 files changed, 52 insertions(+), 27 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 59085a21ce..20d121756e 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -3,7 +3,9 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.GestureRecognizers; using Avalonia.Media; +using Avalonia.Platform; using Avalonia.UnitTests; +using Moq; using Xunit; namespace Avalonia.Base.UnitTests.Input @@ -15,6 +17,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Follow_Pointer_Pressed_Released() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -32,6 +35,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -49,6 +53,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Not_Be_Raised_For_Middle_Button() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -66,6 +71,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Not_Be_Raised_For_Right_Button() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -83,6 +89,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void RightTapped_Should_Be_Raised_For_Right_Button() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -100,6 +107,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -118,6 +126,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -136,6 +145,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Not_Be_Raised_For_Middle_Button() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -154,6 +164,7 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Not_Be_Raised_For_Right_Button() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { diff --git a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs index c0c0182622..36587ea222 100644 --- a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs @@ -207,7 +207,7 @@ namespace Avalonia.Input.UnitTests private IDisposable UnitTestApp(TimeSpan doubleClickTime = new TimeSpan()) { var unitTestApp = UnitTestApplication.Start( - new TestServices(inputManager: new InputManager())); + new TestServices(inputManager: new InputManager(), threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.GetDoubleTapTime(It.IsAny())).Returns(doubleClickTime); iSettingsMock.Setup(x => x.GetDoubleTapSize(It.IsAny())).Returns(new Size(16, 16)); diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 8bd51ec500..9cb83d3009 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -134,6 +134,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_Raises_Click() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); var pt = new Point(50, 50); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) @@ -166,6 +167,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_Does_Not_Raise_Click_When_PointerReleased_Outside() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) @@ -199,6 +201,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_With_RenderTransform_Raises_Click() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); var pt = new Point(150, 50); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 1a8061bfd9..0afcd9ed49 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -11,6 +11,8 @@ using Avalonia.Media; using Avalonia.VisualTree; using Avalonia.UnitTests; using Xunit; +using Moq; +using Avalonia.Platform; namespace Avalonia.Controls.UnitTests { @@ -21,6 +23,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_On_Control_Toggles_IsDropDownOpen() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new ComboBox { Items = new[] { "Foo", "Bar" }, @@ -41,6 +44,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_On_Control_PseudoClass() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new ComboBox { Items = new[] { "Foo", "Bar" }, diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs index 03aac5e2f4..7f9b38018b 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs @@ -8,6 +8,7 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.LogicalTree; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; using Avalonia.VisualTree; @@ -115,7 +116,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Item_Should_Select_It() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -133,7 +134,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Not_Deselect_It() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -153,7 +154,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -173,7 +174,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -195,7 +196,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -216,7 +217,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs index 0ff1f40121..74d46b18de 100644 --- a/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs @@ -1,5 +1,7 @@ ๏ปฟusing Avalonia.Controls.Mixins; +using Avalonia.Platform; using Avalonia.UnitTests; +using Moq; using Xunit; namespace Avalonia.Controls.UnitTests.Mixins @@ -19,6 +21,7 @@ namespace Avalonia.Controls.UnitTests.Mixins [Fact] public void Setting_IsSelected_Should_Add_Selected_Class() { + using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new TestControl(); _mouse.Down(target); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index e20c03f67f..c416a5de47 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1118,7 +1118,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1140,7 +1140,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var items = new ObservableCollection(new[] { "Foo", "Bar", "Baz " }); @@ -1239,7 +1239,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1257,7 +1257,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index bc6147d64c..7fa3839ed5 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -12,6 +12,7 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; +using Avalonia.Platform; using Avalonia.UnitTests; using Moq; using Xunit; @@ -703,7 +704,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Selecting_From_No_Selection_Selects_From_Start() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -726,7 +727,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_Raises_SelectionChanged_Events() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -780,7 +781,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -810,7 +811,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_Non_SelectedItem_With_Multiple_Selection_Active_Leaves_SelectedItem_The_Same() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -838,7 +839,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Ctrl_Select_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -862,7 +863,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Shift_Select_Correct_Item_When_Duplicates_Are_Present() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -886,7 +887,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Can_Shift_Select_All_Items_When_Duplicates_Are_Present() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -910,7 +911,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Selecting_Raises_SelectionChanged_Events() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -959,7 +960,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1189,7 +1190,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Left_Click_On_SelectedItem_Should_Clear_Existing_Selection() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1216,7 +1217,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1241,7 +1242,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1293,7 +1294,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Right_Click_Should_Not_Select_Multiple() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { @@ -1316,7 +1317,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Right_Click_Should_Not_Select_Multiple() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 81936711ef..3e1e25faf5 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -15,6 +15,7 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -1466,7 +1467,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() { - using (UnitTestApplication.Start()) + using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) { var tree = CreateTestTreeData(); var target = new TreeView diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index 8f132433ec..a2096997c0 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -51,7 +51,8 @@ namespace Avalonia.UnitTests inputManager: new InputManager(), assetLoader: new AssetLoader(), renderInterface: new MockPlatformRenderInterface(), - fontManagerImpl: new MockFontManagerImpl(), + fontManagerImpl: new MockFontManagerImpl(), + threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true), textShaperImpl: new MockTextShaperImpl()); public static readonly TestServices TextServices = new TestServices( From 6791a2bca129ae179da39dad3f58c6b41818dbbd Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 12 Dec 2022 09:08:41 +0000 Subject: [PATCH 15/95] Add pointer properties to hold event args, fixup some docs. --- src/Avalonia.Base/Input/Gestures.cs | 18 ++++------ .../Input/HoldingRoutedEventArgs.cs | 33 +++++++++++++++++-- src/Avalonia.Base/Input/InputElement.cs | 3 +- src/Avalonia.Controls/Control.cs | 4 +-- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 06d90f1c27..b1fda35c28 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -13,13 +13,6 @@ namespace Avalonia.Input private static bool s_isHolding; private static CancellationTokenSource? s_holdCancellationToken; - /* /// - /// Defines the property. - /// - public static readonly AttachedProperty IsHoldWithMouseEnabledProperty = - AvaloniaProperty.RegisterAttached( - "IsHoldWithMouseEnabled");*/ - public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, @@ -71,6 +64,9 @@ namespace Avalonia.Input RoutedEvent.Register( "PullGesture", RoutingStrategies.Bubble, typeof(Gestures)); + /// + /// Occurs when a user performs a press and hold gesture (with a single touch, mouse, or pen/stylus contact). + /// public static readonly RoutedEvent HoldingEvent = RoutedEvent.Register( "Holding", RoutingStrategies.Bubble, typeof(Gestures)); @@ -132,7 +128,7 @@ namespace Avalonia.Input { if(s_isHolding && ev.Source is Interactive i) { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled)); + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_lastPointer.Type)); } s_holdCancellationToken?.Cancel(); s_holdCancellationToken?.Dispose(); @@ -157,7 +153,7 @@ namespace Avalonia.Input if (!token.IsCancellationRequested && e.Source is InputElement i && i.IsHoldingEnabled && ( e.Pointer.Type != PointerType.Mouse || i.IsHoldWithMouseEnabled)) { s_isHolding = true; - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started)); + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_lastPointer.Type)); } }, TimeSpan.FromMilliseconds(300)); } @@ -196,7 +192,7 @@ namespace Avalonia.Input if(s_isHolding) { s_isHolding = false; - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Completed)); + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Completed, s_lastPressPoint, s_lastPointer!.Type)); } else if (e.InitialPressMouseButton == MouseButton.Right) { @@ -241,7 +237,7 @@ namespace Avalonia.Input if (s_isHolding && ev.Source is Interactive i) { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled)); + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_lastPointer!.Type)); } } diff --git a/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs index 5826c5b994..b9a877b2ed 100644 --- a/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs +++ b/src/Avalonia.Base/Input/HoldingRoutedEventArgs.cs @@ -5,18 +5,47 @@ namespace Avalonia.Input { public class HoldingRoutedEventArgs : RoutedEventArgs { + /// + /// Gets the state of the event. + /// public HoldingState HoldingState { get; } - - public HoldingRoutedEventArgs(HoldingState holdingState) : base(Gestures.HoldingEvent) + + /// + /// Gets the location of the touch, mouse, or pen/stylus contact. + /// + public Point Position { get; } + + /// + /// Gets the pointer type of the input source. + /// + public PointerType PointerType { get; } + + /// + /// Initializes a new instance of the class. + /// + public HoldingRoutedEventArgs(HoldingState holdingState, Point position, PointerType pointerType) : base(Gestures.HoldingEvent) { HoldingState = holdingState; + Position = position; + PointerType = pointerType; } } public enum HoldingState { + /// + /// A single contact has been detected and a time threshold is crossed without the contact being lifted, another contact detected, or another gesture started. + /// Started, + + /// + /// The single contact is lifted. + /// Completed, + + /// + /// An additional contact is detected or a subsequent gesture (such as a slide) is detected. + /// Cancelled, } } diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index 7f2cbec299..4a65134552 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -426,8 +426,7 @@ namespace Avalonia.Input } /// - /// Gets or sets a value that determines whether the Holding event can originate - /// from that element. + /// Enables or disables support for the press and hold gesture through the left button on a mouse. /// public bool IsHoldWithMouseEnabled { diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index bb83f9e8bb..26db431a0e 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -375,9 +375,9 @@ namespace Avalonia.Controls private void OnHoldEvent(object? sender, HoldingRoutedEventArgs e) { - if(e.HoldingState == HoldingState.Completed) + if(e.HoldingState == HoldingState.Started) { - // Trigger ContentRequest when hold is complete + // Trigger ContentRequest when hold has started RaiseEvent(new ContextRequestedEventArgs()); } } From 8516308e6335b63ac81d7ae57b966fe54c12ede5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 22 Dec 2022 14:02:20 +0000 Subject: [PATCH 16/95] add HoldWaitDuration to IPlatformSettings --- src/Avalonia.Base/Input/Gestures.cs | 16 ++++++++++------ .../Platform/DefaultPlatformSettings.cs | 2 ++ src/Avalonia.Base/Platform/IPlatformSettings.cs | 2 ++ src/Windows/Avalonia.Win32/Win32Platform.cs | 2 ++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index b1fda35c28..a924b391ac 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -148,14 +148,18 @@ namespace Avalonia.Input s_holdCancellationToken = new CancellationTokenSource(); var token = s_holdCancellationToken.Token; var settings = AvaloniaLocator.Current.GetService(); - DispatcherTimer.RunOnce(() => + + if (settings != null) { - if (!token.IsCancellationRequested && e.Source is InputElement i && i.IsHoldingEnabled && ( e.Pointer.Type != PointerType.Mouse || i.IsHoldWithMouseEnabled)) + DispatcherTimer.RunOnce(() => { - s_isHolding = true; - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_lastPointer.Type)); - } - }, TimeSpan.FromMilliseconds(300)); + if (!token.IsCancellationRequested && e.Source is InputElement i && i.IsHoldingEnabled && (e.Pointer.Type != PointerType.Mouse || i.IsHoldWithMouseEnabled)) + { + s_isHolding = true; + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_lastPointer.Type)); + } + }, settings.HoldWaitDuration); + } } else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) { diff --git a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs index dd3e1a4cb1..d0b27b057f 100644 --- a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs @@ -26,5 +26,7 @@ namespace Avalonia.Platform }; } public TimeSpan GetDoubleTapTime(PointerType type) => TimeSpan.FromMilliseconds(500); + + public TimeSpan HoldWaitDuration { get; set; } = TimeSpan.FromMilliseconds(300); } } diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index e7921883fd..7c4c1420eb 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -27,5 +27,7 @@ namespace Avalonia.Platform /// tap gesture. /// TimeSpan GetDoubleTapTime(PointerType type); + + TimeSpan HoldWaitDuration { get; set; } } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 93d16b5768..585a9cdf19 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -258,6 +258,8 @@ namespace Avalonia.Win32 public bool CurrentThreadIsLoopThread => _uiThread == Thread.CurrentThread; + public TimeSpan HoldWaitDuration { get; set; } = TimeSpan.FromMilliseconds(300); + public event Action Signaled; public event EventHandler ShutdownRequested; From f61c7f6cd949388e10faa0dc93818147e7ad4c3b Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 22 Dec 2022 14:03:56 +0000 Subject: [PATCH 17/95] Revert "fix tests" This reverts commit 584e18f78d29480890711b1149d946f61e0d8447. --- .../Input/GesturesTests.cs | 10 ------- .../Input/TouchDeviceTests.cs | 2 +- .../ButtonTests.cs | 3 -- .../ComboBoxTests.cs | 4 --- .../ListBoxTests_Single.cs | 13 ++++----- .../Mixins/PressedMixinTests.cs | 3 -- .../Primitives/SelectingItemsControlTests.cs | 8 ++--- .../SelectingItemsControlTests_Multiple.cs | 29 +++++++++---------- .../TreeViewTests.cs | 3 +- tests/Avalonia.UnitTests/TestServices.cs | 3 +- 10 files changed, 27 insertions(+), 51 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 20d121756e..fedcdcfb1e 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -5,7 +5,6 @@ using Avalonia.Input.GestureRecognizers; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; -using Moq; using Xunit; namespace Avalonia.Base.UnitTests.Input @@ -17,7 +16,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Follow_Pointer_Pressed_Released() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -35,7 +33,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -53,7 +50,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Not_Be_Raised_For_Middle_Button() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -71,7 +67,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void Tapped_Should_Not_Be_Raised_For_Right_Button() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -89,7 +84,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void RightTapped_Should_Be_Raised_For_Right_Button() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -107,7 +101,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -126,7 +119,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -145,7 +137,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Not_Be_Raised_For_Middle_Button() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { @@ -164,7 +155,6 @@ namespace Avalonia.Base.UnitTests.Input [Fact] public void DoubleTapped_Should_Not_Be_Raised_For_Right_Button() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); Border border = new Border(); var decorator = new Decorator { diff --git a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs index 36587ea222..c0c0182622 100644 --- a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs @@ -207,7 +207,7 @@ namespace Avalonia.Input.UnitTests private IDisposable UnitTestApp(TimeSpan doubleClickTime = new TimeSpan()) { var unitTestApp = UnitTestApplication.Start( - new TestServices(inputManager: new InputManager(), threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); + new TestServices(inputManager: new InputManager())); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.GetDoubleTapTime(It.IsAny())).Returns(doubleClickTime); iSettingsMock.Setup(x => x.GetDoubleTapSize(It.IsAny())).Returns(new Size(16, 16)); diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 9cb83d3009..8bd51ec500 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -134,7 +134,6 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_Raises_Click() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); var pt = new Point(50, 50); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) @@ -167,7 +166,6 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_Does_Not_Raise_Click_When_PointerReleased_Outside() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) @@ -201,7 +199,6 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_With_RenderTransform_Raises_Click() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var renderer = Mock.Of(); var pt = new Point(150, 50); Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 0afcd9ed49..1a8061bfd9 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -11,8 +11,6 @@ using Avalonia.Media; using Avalonia.VisualTree; using Avalonia.UnitTests; using Xunit; -using Moq; -using Avalonia.Platform; namespace Avalonia.Controls.UnitTests { @@ -23,7 +21,6 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_On_Control_Toggles_IsDropDownOpen() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new ComboBox { Items = new[] { "Foo", "Bar" }, @@ -44,7 +41,6 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_On_Control_PseudoClass() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new ComboBox { Items = new[] { "Foo", "Bar" }, diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs index 7f9b38018b..03aac5e2f4 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs @@ -8,7 +8,6 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.LogicalTree; -using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; using Avalonia.VisualTree; @@ -116,7 +115,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Item_Should_Select_It() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -134,7 +133,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Not_Deselect_It() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -154,7 +153,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -174,7 +173,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -196,7 +195,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -217,7 +216,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs index 74d46b18de..0ff1f40121 100644 --- a/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Mixins/PressedMixinTests.cs @@ -1,7 +1,5 @@ ๏ปฟusing Avalonia.Controls.Mixins; -using Avalonia.Platform; using Avalonia.UnitTests; -using Moq; using Xunit; namespace Avalonia.Controls.UnitTests.Mixins @@ -21,7 +19,6 @@ namespace Avalonia.Controls.UnitTests.Mixins [Fact] public void Setting_IsSelected_Should_Add_Selected_Class() { - using var app = UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var target = new TestControl(); _mouse.Down(target); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index c416a5de47..e20c03f67f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1118,7 +1118,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1140,7 +1140,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var items = new ObservableCollection(new[] { "Foo", "Bar", "Baz " }); @@ -1239,7 +1239,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1257,7 +1257,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 7fa3839ed5..bc6147d64c 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -12,7 +12,6 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; -using Avalonia.Platform; using Avalonia.UnitTests; using Moq; using Xunit; @@ -704,7 +703,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Selecting_From_No_Selection_Selects_From_Start() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -727,7 +726,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_Raises_SelectionChanged_Events() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -781,7 +780,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -811,7 +810,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Selecting_Non_SelectedItem_With_Multiple_Selection_Active_Leaves_SelectedItem_The_Same() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -839,7 +838,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Ctrl_Select_Correct_Item_When_Duplicate_Items_Are_Present() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -863,7 +862,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Should_Shift_Select_Correct_Item_When_Duplicates_Are_Present() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -887,7 +886,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Can_Shift_Select_All_Items_When_Duplicates_Are_Present() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -911,7 +910,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Selecting_Raises_SelectionChanged_Events() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -960,7 +959,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1190,7 +1189,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Left_Click_On_SelectedItem_Should_Clear_Existing_Selection() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1217,7 +1216,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1242,7 +1241,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1294,7 +1293,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Shift_Right_Click_Should_Not_Select_Multiple() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { @@ -1317,7 +1316,7 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Ctrl_Right_Click_Should_Not_Select_Multiple() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var target = new ListBox { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 3e1e25faf5..81936711ef 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -15,7 +15,6 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.LogicalTree; -using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -1467,7 +1466,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() { - using (UnitTestApplication.Start(new TestServices(threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)))) + using (UnitTestApplication.Start()) { var tree = CreateTestTreeData(); var target = new TreeView diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index a2096997c0..8f132433ec 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -51,8 +51,7 @@ namespace Avalonia.UnitTests inputManager: new InputManager(), assetLoader: new AssetLoader(), renderInterface: new MockPlatformRenderInterface(), - fontManagerImpl: new MockFontManagerImpl(), - threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true), + fontManagerImpl: new MockFontManagerImpl(), textShaperImpl: new MockTextShaperImpl()); public static readonly TestServices TextServices = new TestServices( From be843f699a677154d7acca6ce9eba4b287efda42 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 22 Dec 2022 14:19:01 +0000 Subject: [PATCH 18/95] add mock thread interface to touch device tests --- tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs index c0c0182622..36587ea222 100644 --- a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs @@ -207,7 +207,7 @@ namespace Avalonia.Input.UnitTests private IDisposable UnitTestApp(TimeSpan doubleClickTime = new TimeSpan()) { var unitTestApp = UnitTestApplication.Start( - new TestServices(inputManager: new InputManager())); + new TestServices(inputManager: new InputManager(), threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true))); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.GetDoubleTapTime(It.IsAny())).Returns(doubleClickTime); iSettingsMock.Setup(x => x.GetDoubleTapSize(It.IsAny())).Returns(new Size(16, 16)); From 29e7666a679e7994ba6d857543bb135753b494b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 22 Dec 2022 16:22:27 +0000 Subject: [PATCH 19/95] attempt at testing --- .../Input/GesturesTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index fedcdcfb1e..46df719d69 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -1,10 +1,13 @@ +using System; using System.Collections.Generic; +using System.Threading; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.GestureRecognizers; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; +using Moq; using Xunit; namespace Avalonia.Base.UnitTests.Input @@ -170,6 +173,35 @@ namespace Avalonia.Base.UnitTests.Input Assert.False(raised); } + [Fact] + public void Hold_Should_Not_Be_Raised_For_Multiple_Contact() + { + using var app = UnitTestApplication.Start(TestServices.MockThreadingInterface); + + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + var raised = false; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => raised = true); + + var secondMouse = new MouseTestHelper(); + + _mouse.Down(border, MouseButton.Left); + + Thread.Sleep(1000); + secondMouse.Down(border, MouseButton.Left); + + Assert.False(raised); + } + private static void AddHandlers( Decorator decorator, Border border, From 17765223976844e9ebb1908497bc8edc3d73506a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 22 Dec 2022 11:59:33 -0500 Subject: [PATCH 20/95] Mock IPlatformThreadingInterface --- .../Input/GesturesTests.cs | 100 +++++++++++++++++- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 46df719d69..82aa3eab72 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -6,6 +6,7 @@ using Avalonia.Input; using Avalonia.Input.GestureRecognizers; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.Threading; using Avalonia.UnitTests; using Moq; using Xunit; @@ -174,14 +175,52 @@ namespace Avalonia.Base.UnitTests.Input } [Fact] - public void Hold_Should_Not_Be_Raised_For_Multiple_Contact() + public void Hold_Is_Raised_When_Pointer_Pressed() { - using var app = UnitTestApplication.Start(TestServices.MockThreadingInterface); + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + var raised = false; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => raised = e.HoldingState == HoldingState.Started); + + _mouse.Down(border); + Assert.False(raised); + + // Verify timer duration, but execute it immediately. + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + Assert.True(raised); + } + + [Fact] + public void Hold_Is_Not_Raised_When_Pointer_Released_Before_Timer() + { + using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(iSettingsMock.Object); + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + Border border = new Border(); border.IsHoldWithMouseEnabled = true; var decorator = new Decorator @@ -190,18 +229,69 @@ namespace Avalonia.Base.UnitTests.Input }; var raised = false; - decorator.AddHandler(Gestures.HoldingEvent, (s, e) => raised = true); + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => raised = e.HoldingState == HoldingState.Completed); + + _mouse.Down(border); + Assert.False(raised); + + _mouse.Up(border); + Assert.False(raised); + + // Verify timer duration, but execute it immediately. + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + + Assert.False(raised); + } + + [Fact] + public void Hold_Should_Not_Be_Raised_For_Multiple_Contact() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + var raised = false; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => raised = e.HoldingState == HoldingState.Completed); var secondMouse = new MouseTestHelper(); _mouse.Down(border, MouseButton.Left); - Thread.Sleep(1000); + // Verify timer duration, but execute it immediately. + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + secondMouse.Down(border, MouseButton.Left); Assert.False(raised); } + private static IPlatformThreadingInterface CreatePlatformThreadingInterface(Action<(TimeSpan, Action)> callback) + { + var threadingInterface = new Mock(); + threadingInterface.SetupGet(p => p.CurrentThreadIsLoopThread).Returns(true); + threadingInterface.Setup(p => p + .StartTimer(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((_, t, a) => callback((t, a))); + return threadingInterface.Object; + } + private static void AddHandlers( Decorator decorator, Border border, From 08f17fa2068f09c36f1730c359eb97e717c4dfa7 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 23 Dec 2022 09:22:18 +0000 Subject: [PATCH 21/95] add more tests --- .../Input/GesturesTests.cs | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 82aa3eab72..33a01fc9cd 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.GestureRecognizers; @@ -244,6 +243,78 @@ namespace Avalonia.Base.UnitTests.Input Assert.False(raised); } + + [Fact] + public void Hold_Is_Cancelled_When_Second_Contact_Is_Detected() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + var cancelled = false; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => cancelled = e.HoldingState == HoldingState.Cancelled); + + _mouse.Down(border); + Assert.False(cancelled); + + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + + var secondMouse = new MouseTestHelper(); + + secondMouse.Down(border); + + Assert.True(cancelled); + } + + [Fact] + public void Hold_Is_Cancelled_When_Pointer_Moves() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + var cancelled = false; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => cancelled = e.HoldingState == HoldingState.Cancelled); + + _mouse.Down(border); + Assert.False(cancelled); + + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + + _mouse.Move(border, position: new Point(10, 10)); + + Assert.True(cancelled); + } [Fact] public void Hold_Should_Not_Be_Raised_For_Multiple_Contact() From 306712e4cce1f63be61357a5f1c0456374bebf0c Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 23 Dec 2022 12:33:28 +0000 Subject: [PATCH 22/95] make IsHoldingEnabledProperty and IsHoldWithMouseEnabledProperty attached properties in Gestures --- src/Avalonia.Base/Input/Gestures.cs | 30 +++++++++++++++++++++++++ src/Avalonia.Base/Input/InputElement.cs | 26 ++++++++++----------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index a924b391ac..a91e37177a 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -13,6 +13,18 @@ namespace Avalonia.Input private static bool s_isHolding; private static CancellationTokenSource? s_holdCancellationToken; + /// + /// Defines the IsHoldingEnabled attached property. + /// + public static readonly AttachedProperty IsHoldingEnabledProperty = + AvaloniaProperty.RegisterAttached("IsHoldingEnabled", typeof(Gestures), true); + + /// + /// Defines the IsHoldWithMouseEnabled attached property. + /// + public static readonly AttachedProperty IsHoldWithMouseEnabledProperty = + AvaloniaProperty.RegisterAttached("IsHoldWithMouseEnabled", typeof(Gestures), false); + public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, @@ -75,6 +87,24 @@ namespace Avalonia.Input RoutedEvent.Register( "PullGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); + public static bool GetIsHoldingEnabled(StyledElement element) + { + return element.GetValue(IsHoldingEnabledProperty); + } + public static void SetIsHoldingEnabled(StyledElement element, bool value) + { + element.SetValue(IsHoldingEnabledProperty, value); + } + + public static bool GetIsHoldWithMouseEnabled(StyledElement element) + { + return element.GetValue(IsHoldWithMouseEnabledProperty); + } + public static void SetIsHoldWithMouseEnabled(StyledElement element, bool value) + { + element.SetValue(IsHoldWithMouseEnabledProperty, value); + } + static Gestures() { InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed); diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index 4a65134552..e1835241bf 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -45,18 +45,6 @@ namespace Avalonia.Input public static readonly StyledProperty CursorProperty = AvaloniaProperty.Register(nameof(Cursor), null, true); - /// - /// Defines the property. - /// - public static readonly StyledProperty IsHoldingEnabledProperty = - AvaloniaProperty.Register(nameof(IsHoldingEnabled), true); - - /// - /// Defines the property. - /// - public static readonly StyledProperty IsHoldWithMouseEnabledProperty = - AvaloniaProperty.Register(nameof(IsHoldWithMouseEnabled), false); - /// /// Defines the property. /// @@ -209,7 +197,19 @@ namespace Avalonia.Input /// Defines the event. /// public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; - + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoldingEnabledProperty = + Gestures.IsHoldingEnabledProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsHoldWithMouseEnabledProperty = + Gestures.IsHoldWithMouseEnabledProperty.AddOwner(); + private bool _isEffectivelyEnabled = true; private bool _isFocused; private bool _isKeyboardFocusWithin; From 57255586c27a1e1296f3b01f18bc3e9598d2ac69 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 27 Dec 2022 12:50:25 +0000 Subject: [PATCH 23/95] update hold tests --- src/Avalonia.Base/Input/Gestures.cs | 8 +-- .../Input/GesturesTests.cs | 68 ++++++++++++++++--- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index a91e37177a..9708ca2b48 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -267,11 +267,11 @@ namespace Avalonia.Input { return; } - } - if (s_isHolding && ev.Source is Interactive i) - { - i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_lastPointer!.Type)); + if (s_isHolding && ev.Source is Interactive i) + { + i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_lastPointer!.Type)); + } } } diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 33a01fc9cd..1b0c941082 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -174,7 +174,46 @@ namespace Avalonia.Base.UnitTests.Input } [Fact] - public void Hold_Is_Raised_When_Pointer_Pressed() + public void Hold_Should_Be_Raised_After_Hold_Duration() + { + using var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + + var scheduledTimers = new List<(TimeSpan time, Action action)>(); + using var app = UnitTestApplication.Start(new TestServices( + threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); + + Border border = new Border(); + border.IsHoldWithMouseEnabled = true; + var decorator = new Decorator + { + Child = border + }; + HoldingState holding = HoldingState.Cancelled; + + decorator.AddHandler(Gestures.HoldingEvent, (s, e) => holding = e.HoldingState); + + _mouse.Down(border); + Assert.False(holding != HoldingState.Cancelled); + + // Verify timer duration, but execute it immediately. + var timer = Assert.Single(scheduledTimers); + Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); + timer.action(); + + Assert.True(holding == HoldingState.Started); + + _mouse.Up(border); + + Assert.True(holding == HoldingState.Completed); + } + + [Fact] + public void Hold_Should_Not_Raised_When_Pointer_Released_Before_Timer() { using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); @@ -199,16 +238,19 @@ namespace Avalonia.Base.UnitTests.Input _mouse.Down(border); Assert.False(raised); + _mouse.Up(border); + Assert.False(raised); + // Verify timer duration, but execute it immediately. var timer = Assert.Single(scheduledTimers); Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); timer.action(); - Assert.True(raised); + Assert.False(raised); } [Fact] - public void Hold_Is_Not_Raised_When_Pointer_Released_Before_Timer() + public void Hold_Should_Not_Raised_When_Pointer_Is_Moved_Before_Timer() { using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); @@ -232,8 +274,8 @@ namespace Avalonia.Base.UnitTests.Input _mouse.Down(border); Assert.False(raised); - - _mouse.Up(border); + + _mouse.Move(border, position: new Point(20, 20)); Assert.False(raised); // Verify timer duration, but execute it immediately. @@ -245,7 +287,7 @@ namespace Avalonia.Base.UnitTests.Input } [Fact] - public void Hold_Is_Cancelled_When_Second_Contact_Is_Detected() + public void Hold_Should_Be_Cancelled_When_Second_Contact_Is_Detected() { using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); @@ -282,11 +324,12 @@ namespace Avalonia.Base.UnitTests.Input } [Fact] - public void Hold_Is_Cancelled_When_Pointer_Moves() + public void Hold_Should_Be_Cancelled_When_Pointer_Moves_Too_Far() { using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300)); + iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); @@ -305,19 +348,22 @@ namespace Avalonia.Base.UnitTests.Input decorator.AddHandler(Gestures.HoldingEvent, (s, e) => cancelled = e.HoldingState == HoldingState.Cancelled); _mouse.Down(border); - Assert.False(cancelled); var timer = Assert.Single(scheduledTimers); Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.time); timer.action(); - _mouse.Move(border, position: new Point(10, 10)); + _mouse.Move(border, position: new Point(3, 3)); + + Assert.False(cancelled); + + _mouse.Move(border, position: new Point(20, 20)); Assert.True(cancelled); } - + [Fact] - public void Hold_Should_Not_Be_Raised_For_Multiple_Contact() + public void Hold_Should_Not_Be_Raised_For_Multiple_Contacts() { using var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); From 66ca4ca6399f964c30e09672aca42fb7d016b106 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 28 Dec 2022 08:55:21 +0000 Subject: [PATCH 24/95] remove hold styled properties in InputElement --- src/Avalonia.Base/Input/Gestures.cs | 2 +- src/Avalonia.Base/Input/InputElement.cs | 31 ------------------- .../Input/GesturesTests.cs | 12 +++---- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 9708ca2b48..54e61d89b2 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -183,7 +183,7 @@ namespace Avalonia.Input { DispatcherTimer.RunOnce(() => { - if (!token.IsCancellationRequested && e.Source is InputElement i && i.IsHoldingEnabled && (e.Pointer.Type != PointerType.Mouse || i.IsHoldWithMouseEnabled)) + if (!token.IsCancellationRequested && e.Source is InputElement i && GetIsHoldingEnabled(i) && (e.Pointer.Type != PointerType.Mouse || GetIsHoldWithMouseEnabled(i))) { s_isHolding = true; i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Started, s_lastPressPoint, s_lastPointer.Type)); diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index e1835241bf..f233fdce51 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -198,18 +198,6 @@ namespace Avalonia.Input /// public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; - /// - /// Defines the property. - /// - public static readonly StyledProperty IsHoldingEnabledProperty = - Gestures.IsHoldingEnabledProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly StyledProperty IsHoldWithMouseEnabledProperty = - Gestures.IsHoldWithMouseEnabledProperty.AddOwner(); - private bool _isEffectivelyEnabled = true; private bool _isFocused; private bool _isKeyboardFocusWithin; @@ -414,25 +402,6 @@ namespace Avalonia.Input get { return GetValue(CursorProperty); } set { SetValue(CursorProperty, value); } } - - /// - /// Gets or sets a value that determines whether the Holding event can originate - /// from that element. - /// - public bool IsHoldingEnabled - { - get { return GetValue(IsHoldingEnabledProperty); } - set { SetValue(IsHoldingEnabledProperty, value); } - } - - /// - /// Enables or disables support for the press and hold gesture through the left button on a mouse. - /// - public bool IsHoldWithMouseEnabled - { - get { return GetValue(IsHoldWithMouseEnabledProperty); } - set { SetValue(IsHoldWithMouseEnabledProperty, value); } - } /// /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements. diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index 1b0c941082..84ee35ba61 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -188,7 +188,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border @@ -226,7 +226,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border @@ -263,7 +263,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border @@ -300,7 +300,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border @@ -338,7 +338,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border @@ -376,7 +376,7 @@ namespace Avalonia.Base.UnitTests.Input threadingInterface: CreatePlatformThreadingInterface(t => scheduledTimers.Add(t)))); Border border = new Border(); - border.IsHoldWithMouseEnabled = true; + Gestures.SetIsHoldWithMouseEnabled(border, true); var decorator = new Decorator { Child = border From 4780567438950cd9269cd84cbef36b41d870872f Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 28 Dec 2022 15:15:35 +0200 Subject: [PATCH 25/95] Don't reomve old content from logicaltree --- src/Avalonia.Controls/ContentControl.cs | 11 +++- .../TransitioningContentControl.cs | 7 +++ .../TransitioningContentControlTests.cs | 63 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index b8a45e102f..d47a7a7809 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -116,14 +116,19 @@ namespace Avalonia.Controls return false; } - private void ContentChanged(AvaloniaPropertyChangedEventArgs e) + protected virtual void ContentChanged(AvaloniaPropertyChangedEventArgs e) { - if (e.OldValue is ILogical oldChild) + UpdateLogicalTree(e.OldValue, e.NewValue); + } + + protected void UpdateLogicalTree(object? toRemove, object? toAdd) + { + if (toRemove is ILogical oldChild) { LogicalChildren.Remove(oldChild); } - if (e.NewValue is ILogical newChild) + if (toAdd is ILogical newChild) { LogicalChildren.Add(newChild); } diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs index 70b21b7248..545032befb 100644 --- a/src/Avalonia.Controls/TransitioningContentControl.cs +++ b/src/Avalonia.Controls/TransitioningContentControl.cs @@ -71,6 +71,11 @@ public class TransitioningContentControl : ContentControl } } + protected override void ContentChanged(AvaloniaPropertyChangedEventArgs e) + { + // We do nothing becuse we should not remove old Content until the animation is over + } + /// /// Updates the content with transitions. /// @@ -89,6 +94,8 @@ public class TransitioningContentControl : ContentControl if (PageTransition != null) await PageTransition.Start(this, null, true, localToken); + UpdateLogicalTree(CurrentContent, content); + if (localToken.IsCancellationRequested) { return; diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs new file mode 100644 index 0000000000..fa523d7f78 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -0,0 +1,63 @@ +using System; +using Avalonia.LogicalTree; +using Avalonia.UnitTests; +using Xunit; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Animation; + +namespace Avalonia.Controls.UnitTests +{ + public class TransitioningContentControlTests + { + [Fact] + public void Old_Content_Shuold_Be_Removed__From_Logical_Tree_After_Out_Animation() + { + var testTransition = new TestTransition(); + + var target = new TransitioningContentControl(); + target.PageTransition = testTransition; + + var root = new TestRoot() { Child = target }; + + var oldControl = new Control(); + var newControl = new Control(); + + target.Content = oldControl; + Threading.Dispatcher.UIThread.RunJobs(); + + Assert.Equal(target, oldControl.GetLogicalParent()); + Assert.Equal(null, newControl.GetLogicalParent()); + + testTransition.BeginTransition += isFrom => + { + // Old out + if (isFrom) + { + Assert.Equal(target, oldControl.GetLogicalParent()); + Assert.Equal(null, newControl.GetLogicalParent()); + } + // New in + else + { + Assert.Equal(null, oldControl.GetLogicalParent()); + Assert.Equal(target, newControl.GetLogicalParent()); + } + }; + + target.Content = newControl; + Threading.Dispatcher.UIThread.RunJobs(); + } + } + public class TestTransition : IPageTransition + { + public event Action BeginTransition; + + public Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) + { + bool isFrom = from != null && to == null; + BeginTransition?.Invoke(isFrom); + return Task.CompletedTask; + } + } +} From 46eca014a2ba5cd4528ac755172c821695a43661 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Wed, 28 Dec 2022 15:46:54 +0100 Subject: [PATCH 26/95] ScrollGestureRecognizer: fix skipping ScrollStartDistance on start of scroll; fix end scrolling too early using InertialScrollSpeedEnd; adding VelocityTracker that uses the last 20 pointermoves for calculating scroll velocity on pointerreleased. --- .../ScrollGestureRecognizer.cs | 43 +- .../GestureRecognizers/VelocityTracker.cs | 424 ++++++++++++++++++ 2 files changed, 454 insertions(+), 13 deletions(-) create mode 100644 src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index 64fe275547..e2deea0fde 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -17,7 +17,9 @@ namespace Avalonia.Input.GestureRecognizers private bool _canVerticallyScroll; private int _gestureId; private int _scrollStartDistance = 30; - + private Point _pointerPressedPoint; + private VelocityTracker? _velocityTracker; + // Movement per second private Vector _inertia; private ulong? _lastMoveTimestamp; @@ -91,7 +93,7 @@ namespace Avalonia.Input.GestureRecognizers EndGesture(); _tracking = e.Pointer; _gestureId = ScrollGestureEventArgs.GetNextFreeId(); - _trackedRootPoint = e.GetPosition((Visual?)_target); + _trackedRootPoint = _pointerPressedPoint = e.GetPosition((Visual?)_target); } } @@ -111,6 +113,13 @@ namespace Avalonia.Input.GestureRecognizers _scrolling = true; if (_scrolling) { + _velocityTracker = new VelocityTracker(); // TODO: Should be platform specific -- this default tracker is for Android. + + // Correct _trackedRootPoint with ScrollStartDistance, so scrolling does not start with a skip of ScrollStartDistance + _trackedRootPoint = new Point( + _trackedRootPoint.X - (_trackedRootPoint.X >= rootPoint.X ? _scrollStartDistance : -_scrollStartDistance), + _trackedRootPoint.Y - (_trackedRootPoint.Y >= rootPoint.Y ? _scrollStartDistance : -_scrollStartDistance)); + _actions!.Capture(e.Pointer, this); } } @@ -118,14 +127,11 @@ namespace Avalonia.Input.GestureRecognizers if (_scrolling) { var vector = _trackedRootPoint - rootPoint; - var elapsed = _lastMoveTimestamp.HasValue && _lastMoveTimestamp < e.Timestamp ? - TimeSpan.FromMilliseconds(e.Timestamp - _lastMoveTimestamp.Value) : - TimeSpan.Zero; - + + _velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), _pointerPressedPoint - rootPoint); + _lastMoveTimestamp = e.Timestamp; _trackedRootPoint = rootPoint; - if (elapsed.TotalSeconds > 0) - _inertia = vector / elapsed.TotalSeconds; _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector)); e.Handled = true; } @@ -150,12 +156,14 @@ namespace Avalonia.Input.GestureRecognizers } } - - + + public void PointerReleased(PointerReleasedEventArgs e) { if (e.Pointer == _tracking && _scrolling) { + _inertia = _velocityTracker?.GetFlingVelocity().PixelsPerSecond ?? Vector.Zero; + e.Handled = true; if (_inertia == default || e.Timestamp == 0 @@ -183,9 +191,18 @@ namespace Avalonia.Input.GestureRecognizers var distance = speed * elapsedSinceLastTick.TotalSeconds; _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, distance)); - - - if (Math.Abs(speed.X) < InertialScrollSpeedEnd || Math.Abs(speed.Y) <= InertialScrollSpeedEnd) + // EndGesture using InertialScrollSpeedEnd only in the direction of scrolling + if (CanVerticallyScroll && CanHorizontallyScroll && Math.Abs(speed.X) < InertialScrollSpeedEnd && Math.Abs(speed.Y) <= InertialScrollSpeedEnd) + { + EndGesture(); + return false; + } + else if (CanVerticallyScroll && Math.Abs(speed.Y) <= InertialScrollSpeedEnd) + { + EndGesture(); + return false; + } + else if (CanHorizontallyScroll && Math.Abs(speed.X) < InertialScrollSpeedEnd) { EndGesture(); return false; diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs new file mode 100644 index 0000000000..909010d43d --- /dev/null +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -0,0 +1,424 @@ +๏ปฟ// Code in this file is derived from +// https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/gestures/velocity_tracker.dart + +//Copyright 2014 The Flutter Authors. All rights reserved. + +//Redistribution and use in source and binary forms, with or without modification, +//are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. + +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; +//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Diagnostics; + +namespace Avalonia.Input.GestureRecognizers +{ + // TODO: add 'IOSScrollViewFlingVelocityTracker' and 'MacOSScrollViewFlingVelocityTracker'? + + public readonly record struct Velocity(Vector PixelsPerSecond) + { + public Velocity ClampMagnitude(double minValue, double maxValue) + { + Debug.Assert(minValue >= 0.0); + Debug.Assert(maxValue >= 0.0 && maxValue >= minValue); + double valueSquared = PixelsPerSecond.SquaredLength; + if (valueSquared > maxValue * maxValue) + { + double length = PixelsPerSecond.Length; + return new Velocity(length != 0.0 ? (PixelsPerSecond / length) * maxValue : Vector.Zero); + // preventing double.NaN in Vector PixelsPerSecond is important -- if a NaN eventually gets into a + // ScrollGestureEventArgs it results in runtime errors. + } + if (valueSquared < minValue * minValue) + { + double length = PixelsPerSecond.Length; + return new Velocity(length != 0.0 ? (PixelsPerSecond / length) * minValue : Vector.Zero); + } + return this; + } + } + + /// A two dimensional velocity estimate. + /// + /// VelocityEstimates are computed by [VelocityTracker.getVelocityEstimate]. An + /// estimate's [confidence] measures how well the velocity tracker's position + /// data fit a straight line, [duration] is the time that elapsed between the + /// first and last position sample used to compute the velocity, and [offset] + /// is similarly the difference between the first and last positions. + /// + /// See also: + /// + /// * [VelocityTracker], which computes [VelocityEstimate]s. + /// * [Velocity], which encapsulates (just) a velocity vector and provides some + /// useful velocity operations. + public record VelocityEstimate(Vector PixelsPerSecond, double Confidence, TimeSpan Duration, Vector Offset); + + internal record struct PointAtTime(bool Valid, Vector Point, TimeSpan Time); + + /// Computes a pointer's velocity based on data from [PointerMoveEvent]s. + /// + /// The input data is provided by calling [addPosition]. Adding data is cheap. + /// + /// To obtain a velocity, call [getVelocity] or [getVelocityEstimate]. This will + /// compute the velocity based on the data added so far. Only call these when + /// you need to use the velocity, as they are comparatively expensive. + /// + /// The quality of the velocity estimation will be better if more data points + /// have been received. + public class VelocityTracker + { + private const int AssumePointerMoveStoppedMilliseconds = 40; + private const int HistorySize = 20; + private const int HorizonMilliseconds = 100; + private const int MinSampleSize = 3; + private const double MinFlingVelocity = 50.0; // Logical pixels / second (defined in flutter\lib\src\gesture\constants.dart) + private const double MaxFlingVelocity = 8000.0; + + private readonly PointAtTime[] _samples = new PointAtTime[HistorySize]; + private int _index = 0; + + /// + /// Adds a position as the given time to the tracker. + /// + /// + /// + public void AddPosition(TimeSpan time, Vector position) + { + _index++; + if (_index == HistorySize) + { + _index = 0; + } + _samples[_index] = new PointAtTime(true, position, time); + } + + /// Returns an estimate of the velocity of the object being tracked by the + /// tracker given the current information available to the tracker. + /// + /// Information is added using [addPosition]. + /// + /// Returns null if there is no data on which to base an estimate. + protected virtual VelocityEstimate? GetVelocityEstimate() + { + double[] x = new double[HistorySize]; + double[] y = new double[HistorySize]; + double[] w = new double[HistorySize]; + double[] time = new double[HistorySize]; + int sampleCount = 0; + int index = _index; + + var newestSample = _samples[index]; + if (!newestSample.Valid) + { + return null; + } + + var previousSample = newestSample; + var oldestSample = newestSample; + + // Starting with the most recent PointAtTime sample, iterate backwards while + // the samples represent continuous motion. + do + { + var sample = _samples[index]; + if (!sample.Valid) + { + break; + } + + double age = (newestSample.Time - sample.Time).TotalMilliseconds; + double delta = Math.Abs((sample.Time - previousSample.Time).TotalMilliseconds); + previousSample = sample; + if (age > HorizonMilliseconds || delta > AssumePointerMoveStoppedMilliseconds) + { + break; + } + + oldestSample = sample; + var position = sample.Point; + x[sampleCount] = position.X; + y[sampleCount] = position.Y; + w[sampleCount] = 1.0; + time[sampleCount] = -age; + index = (index == 0 ? HistorySize : index) - 1; + + sampleCount++; + } while (sampleCount < HistorySize); + + if (sampleCount >= MinSampleSize) + { + var xFit = LeastSquaresSolver.Solve(2, time.AsSpan(0, sampleCount), x.AsSpan(0, sampleCount), w.AsSpan(0, sampleCount)); + if (xFit != null) + { + var yFit = LeastSquaresSolver.Solve(2, time.AsSpan(0, sampleCount), y.AsSpan(0, sampleCount), w.AsSpan(0, sampleCount)); + if (yFit != null) + { + return new VelocityEstimate( // convert from pixels/ms to pixels/s + PixelsPerSecond: new Vector(xFit.Coefficients[1] * 1000, yFit.Coefficients[1] * 1000), + Confidence: xFit.Confidence * yFit.Confidence, + Duration: newestSample.Time - oldestSample.Time, + Offset: newestSample.Point - oldestSample.Point + ); + } + } + } + + // We're unable to make a velocity estimate but we did have at least one + // valid pointer position. + return new VelocityEstimate( + PixelsPerSecond: Vector.Zero, + Confidence: 1.0, + Duration: newestSample.Time - oldestSample.Time, + Offset: newestSample.Point - oldestSample.Point + ); + } + + /// + /// Computes the velocity of the pointer at the time of the last + /// provided data point. + /// + /// This can be expensive. Only call this when you need the velocity. + /// + /// Returns [Velocity.zero] if there is no data from which to compute an + /// estimate or if the estimated velocity is zero./// + /// + /// + public Velocity GetVelocity() + { + var estimate = GetVelocityEstimate(); + if (estimate == null || estimate.PixelsPerSecond.IsDefault) + { + return new Velocity(Vector.Zero); + } + return new Velocity(estimate.PixelsPerSecond); + } + + public virtual Velocity GetFlingVelocity() + { + return GetVelocity().ClampMagnitude(MinFlingVelocity, MaxFlingVelocity); + } + } + + /// An nth degree polynomial fit to a dataset. + internal class PolynomialFit + { + /// Creates a polynomial fit of the given degree. + /// + /// There are n + 1 coefficients in a fit of degree n. + internal PolynomialFit(int degree) + { + Coefficients = new double[degree + 1]; + } + + /// The polynomial coefficients of the fit. + public double[] Coefficients { get; } + + /// An indicator of the quality of the fit. + /// + /// Larger values indicate greater quality. + public double Confidence { get; set; } + } + + internal class LeastSquaresSolver + { + private const double PrecisionErrorTolerance = 1e-10; + + /// + /// Fits a polynomial of the given degree to the data points. + /// When there is not enough data to fit a curve null is returned. + /// + public static PolynomialFit? Solve(int degree, ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan w) + { + if (degree > x.Length) + { + // Not enough data to fit a curve. + return null; + } + + PolynomialFit result = new PolynomialFit(degree); + + // Shorthands for the purpose of notation equivalence to original C++ code. + int m = x.Length; + int n = degree + 1; + + // Expand the X vector to a matrix A, pre-multiplied by the weights. + _Matrix a = new _Matrix(n, m); + for (int h = 0; h < m; h += 1) + { + a.Set(0, h, w[h]); + for (int i = 1; i < n; i += 1) + { + a.Set(i, h, a.Get(i - 1, h) * x[h]); + } + } + + // Apply the Gram-Schmidt process to A to obtain its QR decomposition. + + // Orthonormal basis, column-major ordVectorer. + _Matrix q = new _Matrix(n, m); + // Upper triangular matrix, row-major order. + _Matrix r = new _Matrix(n, n); + for (int j = 0; j < n; j += 1) + { + for (int h = 0; h < m; h += 1) + { + q.Set(j, h, a.Get(j, h)); + } + for (int i = 0; i < j; i += 1) + { + double dot = q.GetRow(j) * q.GetRow(i); + for (int h = 0; h < m; h += 1) + { + q.Set(j, h, q.Get(j, h) - dot * q.Get(i, h)); + } + } + + double norm = q.GetRow(j).Norm(); + if (norm < PrecisionErrorTolerance) + { + // Vectors are linearly dependent or zero so no solution. + return null; + } + + double inverseNorm = 1.0 / norm; + for (int h = 0; h < m; h += 1) + { + q.Set(j, h, q.Get(j, h) * inverseNorm); + } + for (int i = 0; i < n; i += 1) + { + r.Set(j, i, i < j ? 0.0 : q.GetRow(j) * a.GetRow(i)); + } + } + + // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. + // We just work from bottom-right to top-left calculating B's coefficients. + _Vector wy = new _Vector(m); + for (int h = 0; h < m; h += 1) + { + wy[h] = y[h] * w[h]; + } + for (int i = n - 1; i >= 0; i -= 1) + { + result.Coefficients[i] = q.GetRow(i) * wy; + for (int j = n - 1; j > i; j -= 1) + { + result.Coefficients[i] -= r.Get(i, j) * result.Coefficients[j]; + } + result.Coefficients[i] /= r.Get(i, i); + } + + // Calculate the coefficient of determination (confidence) as: + // 1 - (sumSquaredError / sumSquaredTotal) + // ...where sumSquaredError is the residual sum of squares (variance of the + // error), and sumSquaredTotal is the total sum of squares (variance of the + // data) where each has been weighted. + double yMean = 0.0; + for (int h = 0; h < m; h += 1) + { + yMean += y[h]; + } + yMean /= m; + + double sumSquaredError = 0.0; + double sumSquaredTotal = 0.0; + for (int h = 0; h < m; h += 1) + { + double term = 1.0; + double err = y[h] - result.Coefficients[0]; + for (int i = 1; i < n; i += 1) + { + term *= x[h]; + err -= term * result.Coefficients[i]; + } + sumSquaredError += w[h] * w[h] * err * err; + double v = y[h] - yMean; + sumSquaredTotal += w[h] * w[h] * v * v; + } + + result.Confidence = sumSquaredTotal <= PrecisionErrorTolerance ? 1.0 : + 1.0 - (sumSquaredError / sumSquaredTotal); + + return result; + } + + private readonly struct _Vector + { + private readonly int _offset; + private readonly int _length; + private readonly double[] _elements; + + internal _Vector(int size) + { + _offset = 0; + _length = size; + _elements = new double[size]; + } + + internal _Vector(double[] values, int offset, int length) + { + _offset = offset; + _length = length; + _elements = values; + } + + public double this[int i] + { + get => _elements[i + _offset]; + set => _elements[i + _offset] = value; + } + + public static double operator *(_Vector a, _Vector b) + { + double result = 0.0; + for (int i = 0; i < a._length; i += 1) + { + result += a[i] * b[i]; + } + return result; + } + + public double Norm() => Math.Sqrt(this * this); + } + + private readonly struct _Matrix + { + private readonly int _columns; + private readonly double[] _elements; + + internal _Matrix(int rows, int cols) + { + _columns = cols; + _elements = new double[rows * cols]; + } + + public double Get(int row, int col) => _elements[row * _columns + col]; + public void Set(int row, int col, double value) + { + _elements[row * _columns + col] = value; + } + + public _Vector GetRow(int row) => new(_elements, row * _columns, _columns); + } + } +} From 5f5f5ebc9b8dd0ed3a76a64f170325f410ac0cc6 Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 28 Dec 2022 20:07:31 +0200 Subject: [PATCH 27/95] try fix tests --- .../TransitioningContentControlTests.cs | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs index fa523d7f78..ac944352c2 100644 --- a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -13,40 +13,43 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Old_Content_Shuold_Be_Removed__From_Logical_Tree_After_Out_Animation() { - var testTransition = new TestTransition(); + using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) + { + var testTransition = new TestTransition(); - var target = new TransitioningContentControl(); - target.PageTransition = testTransition; + var target = new TransitioningContentControl(); + target.PageTransition = testTransition; - var root = new TestRoot() { Child = target }; + var root = new TestRoot() { Child = target }; - var oldControl = new Control(); - var newControl = new Control(); + var oldControl = new Control(); + var newControl = new Control(); - target.Content = oldControl; - Threading.Dispatcher.UIThread.RunJobs(); + target.Content = oldControl; + Threading.Dispatcher.UIThread.RunJobs(); - Assert.Equal(target, oldControl.GetLogicalParent()); - Assert.Equal(null, newControl.GetLogicalParent()); + Assert.Equal(target, oldControl.GetLogicalParent()); + Assert.Equal(null, newControl.GetLogicalParent()); - testTransition.BeginTransition += isFrom => - { - // Old out - if (isFrom) - { - Assert.Equal(target, oldControl.GetLogicalParent()); - Assert.Equal(null, newControl.GetLogicalParent()); - } - // New in - else + testTransition.BeginTransition += isFrom => { - Assert.Equal(null, oldControl.GetLogicalParent()); - Assert.Equal(target, newControl.GetLogicalParent()); - } - }; + // Old out + if (isFrom) + { + Assert.Equal(target, oldControl.GetLogicalParent()); + Assert.Equal(null, newControl.GetLogicalParent()); + } + // New in + else + { + Assert.Equal(null, oldControl.GetLogicalParent()); + Assert.Equal(target, newControl.GetLogicalParent()); + } + }; - target.Content = newControl; - Threading.Dispatcher.UIThread.RunJobs(); + target.Content = newControl; + Threading.Dispatcher.UIThread.RunJobs(); + } } } public class TestTransition : IPageTransition From 21f6307c379ff98f255dc5a141f5c5ad5e83a9ba Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 28 Dec 2022 20:21:20 +0200 Subject: [PATCH 28/95] again --- .../TransitioningContentControlTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs index ac944352c2..aaa1de4da4 100644 --- a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Old_Content_Shuold_Be_Removed__From_Logical_Tree_After_Out_Animation() { - using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) + using (UnitTestApplication.Start(TestServices.StyledWindow)) { var testTransition = new TestTransition(); From 8d5d382c2f134779d8f49e17a71064f664029e06 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Dec 2022 23:52:34 -0500 Subject: [PATCH 29/95] Reuse GetVelocityEstimate arrays --- .../Input/GestureRecognizers/VelocityTracker.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index 909010d43d..526d36ba6c 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -94,6 +94,11 @@ namespace Avalonia.Input.GestureRecognizers private const double MinFlingVelocity = 50.0; // Logical pixels / second (defined in flutter\lib\src\gesture\constants.dart) private const double MaxFlingVelocity = 8000.0; + private static double[] x = new double[HistorySize]; + private static double[] y = new double[HistorySize]; + private static double[] w = new double[HistorySize]; + private static double[] time = new double[HistorySize]; + private readonly PointAtTime[] _samples = new PointAtTime[HistorySize]; private int _index = 0; @@ -120,10 +125,6 @@ namespace Avalonia.Input.GestureRecognizers /// Returns null if there is no data on which to base an estimate. protected virtual VelocityEstimate? GetVelocityEstimate() { - double[] x = new double[HistorySize]; - double[] y = new double[HistorySize]; - double[] w = new double[HistorySize]; - double[] time = new double[HistorySize]; int sampleCount = 0; int index = _index; From 4645dcaa12510fe32e35e735a228c9b95e349726 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Dec 2022 23:53:54 -0500 Subject: [PATCH 30/95] Use C# indegers --- .../GestureRecognizers/VelocityTracker.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index 526d36ba6c..9e220bc85e 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -266,10 +266,10 @@ namespace Avalonia.Input.GestureRecognizers _Matrix a = new _Matrix(n, m); for (int h = 0; h < m; h += 1) { - a.Set(0, h, w[h]); + a[0, h] = w[h]; for (int i = 1; i < n; i += 1) { - a.Set(i, h, a.Get(i - 1, h) * x[h]); + a[i, h] = a[i - 1, h] * x[h]; } } @@ -283,14 +283,14 @@ namespace Avalonia.Input.GestureRecognizers { for (int h = 0; h < m; h += 1) { - q.Set(j, h, a.Get(j, h)); + q[j, h] = a[j, h]; } for (int i = 0; i < j; i += 1) { double dot = q.GetRow(j) * q.GetRow(i); for (int h = 0; h < m; h += 1) { - q.Set(j, h, q.Get(j, h) - dot * q.Get(i, h)); + q[j, h] = q[j, h] - dot * q[i, h]; } } @@ -304,7 +304,7 @@ namespace Avalonia.Input.GestureRecognizers double inverseNorm = 1.0 / norm; for (int h = 0; h < m; h += 1) { - q.Set(j, h, q.Get(j, h) * inverseNorm); + q[j, h] = q[j, h] * inverseNorm; } for (int i = 0; i < n; i += 1) { @@ -324,9 +324,9 @@ namespace Avalonia.Input.GestureRecognizers result.Coefficients[i] = q.GetRow(i) * wy; for (int j = n - 1; j > i; j -= 1) { - result.Coefficients[i] -= r.Get(i, j) * result.Coefficients[j]; + result.Coefficients[i] -= r[i, j] * result.Coefficients[j]; } - result.Coefficients[i] /= r.Get(i, i); + result.Coefficients[i] /= r[i, i]; } // Calculate the coefficient of determination (confidence) as: @@ -413,10 +413,10 @@ namespace Avalonia.Input.GestureRecognizers _elements = new double[rows * cols]; } - public double Get(int row, int col) => _elements[row * _columns + col]; - public void Set(int row, int col, double value) + public double this[int row, int col] { - _elements[row * _columns + col] = value; + get => _elements[row * _columns + col]; + set => _elements[row * _columns + col] = value; } public _Vector GetRow(int row) => new(_elements, row * _columns, _columns); From 6d6ec49dd3ff93e76625027efe2e4f3897321dd3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 29 Dec 2022 00:09:58 -0500 Subject: [PATCH 31/95] Use Span instead of _Vector --- .../GestureRecognizers/VelocityTracker.cs | 59 ++++++------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index 9e220bc85e..d290faf114 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -275,7 +275,7 @@ namespace Avalonia.Input.GestureRecognizers // Apply the Gram-Schmidt process to A to obtain its QR decomposition. - // Orthonormal basis, column-major ordVectorer. + // Orthonormal basis, column-major order Vector. _Matrix q = new _Matrix(n, m); // Upper triangular matrix, row-major order. _Matrix r = new _Matrix(n, n); @@ -287,14 +287,14 @@ namespace Avalonia.Input.GestureRecognizers } for (int i = 0; i < j; i += 1) { - double dot = q.GetRow(j) * q.GetRow(i); + double dot = Multiply(q.GetRow(j), q.GetRow(i)); for (int h = 0; h < m; h += 1) { q[j, h] = q[j, h] - dot * q[i, h]; } } - double norm = q.GetRow(j).Norm(); + double norm = Norm(q.GetRow(j)); if (norm < PrecisionErrorTolerance) { // Vectors are linearly dependent or zero so no solution. @@ -308,20 +308,21 @@ namespace Avalonia.Input.GestureRecognizers } for (int i = 0; i < n; i += 1) { - r.Set(j, i, i < j ? 0.0 : q.GetRow(j) * a.GetRow(i)); + r[j, i] = i < j ? 0.0 : Multiply(q.GetRow(j), a.GetRow(i)); } } // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. // We just work from bottom-right to top-left calculating B's coefficients. - _Vector wy = new _Vector(m); + // "m" isn't expected to be bigger than HistorySize=20, so allocation on stack is safe. + Span wy = stackalloc double[m]; for (int h = 0; h < m; h += 1) { wy[h] = y[h] * w[h]; } for (int i = n - 1; i >= 0; i -= 1) { - result.Coefficients[i] = q.GetRow(i) * wy; + result.Coefficients[i] = Multiply(q.GetRow(i), wy); for (int j = n - 1; j > i; j -= 1) { result.Coefficients[i] -= r[i, j] * result.Coefficients[j]; @@ -363,43 +364,19 @@ namespace Avalonia.Input.GestureRecognizers return result; } - private readonly struct _Vector + private static double Multiply(Span v1, Span v2) { - private readonly int _offset; - private readonly int _length; - private readonly double[] _elements; - - internal _Vector(int size) - { - _offset = 0; - _length = size; - _elements = new double[size]; - } - - internal _Vector(double[] values, int offset, int length) - { - _offset = offset; - _length = length; - _elements = values; - } - - public double this[int i] - { - get => _elements[i + _offset]; - set => _elements[i + _offset] = value; - } - - public static double operator *(_Vector a, _Vector b) + double result = 0.0; + for (int i = 0; i < v1.Length; i += 1) { - double result = 0.0; - for (int i = 0; i < a._length; i += 1) - { - result += a[i] * b[i]; - } - return result; + result += v1[i] * v2[i]; } - - public double Norm() => Math.Sqrt(this * this); + return result; + } + + private static double Norm(Span v) + { + return Math.Sqrt(Multiply(v, v)); } private readonly struct _Matrix @@ -419,7 +396,7 @@ namespace Avalonia.Input.GestureRecognizers set => _elements[row * _columns + col] = value; } - public _Vector GetRow(int row) => new(_elements, row * _columns, _columns); + public Span GetRow(int row) => _elements.AsSpan(row * _columns, _columns); } } } From ebdf7df1624fe826211c0facb8968be19000c8c6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 29 Dec 2022 00:21:00 -0500 Subject: [PATCH 32/95] Use stackalloc instead of reused arrays, so this code can potentially work with multitouch This reverts commit 8d5d382c2f134779d8f49e17a71064f664029e06. --- .../Input/GestureRecognizers/VelocityTracker.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index d290faf114..d4705bcdca 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -30,6 +30,7 @@ using System; using System.Diagnostics; +using Avalonia.Utilities; namespace Avalonia.Input.GestureRecognizers { @@ -94,11 +95,6 @@ namespace Avalonia.Input.GestureRecognizers private const double MinFlingVelocity = 50.0; // Logical pixels / second (defined in flutter\lib\src\gesture\constants.dart) private const double MaxFlingVelocity = 8000.0; - private static double[] x = new double[HistorySize]; - private static double[] y = new double[HistorySize]; - private static double[] w = new double[HistorySize]; - private static double[] time = new double[HistorySize]; - private readonly PointAtTime[] _samples = new PointAtTime[HistorySize]; private int _index = 0; @@ -125,6 +121,10 @@ namespace Avalonia.Input.GestureRecognizers /// Returns null if there is no data on which to base an estimate. protected virtual VelocityEstimate? GetVelocityEstimate() { + Span x = stackalloc double[HistorySize]; + Span y = stackalloc double[HistorySize]; + Span w = stackalloc double[HistorySize]; + Span time = stackalloc double[HistorySize]; int sampleCount = 0; int index = _index; @@ -168,10 +168,10 @@ namespace Avalonia.Input.GestureRecognizers if (sampleCount >= MinSampleSize) { - var xFit = LeastSquaresSolver.Solve(2, time.AsSpan(0, sampleCount), x.AsSpan(0, sampleCount), w.AsSpan(0, sampleCount)); + var xFit = LeastSquaresSolver.Solve(2, time.Slice(0, sampleCount), x.Slice(0, sampleCount), w.Slice(0, sampleCount)); if (xFit != null) { - var yFit = LeastSquaresSolver.Solve(2, time.AsSpan(0, sampleCount), y.AsSpan(0, sampleCount), w.AsSpan(0, sampleCount)); + var yFit = LeastSquaresSolver.Solve(2, time.Slice(0, sampleCount), y.Slice(0, sampleCount), w.Slice(0, sampleCount)); if (yFit != null) { return new VelocityEstimate( // convert from pixels/ms to pixels/s From 899dc69d7283d86c9d5189d5d4fa51e1d383b0e2 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 29 Dec 2022 10:02:11 +0100 Subject: [PATCH 33/95] removed unnecessary comments --- .../ScrollGestureRecognizer.cs | 2 +- .../GestureRecognizers/VelocityTracker.cs | 31 ++----------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index e2deea0fde..cec98ec66b 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -113,7 +113,7 @@ namespace Avalonia.Input.GestureRecognizers _scrolling = true; if (_scrolling) { - _velocityTracker = new VelocityTracker(); // TODO: Should be platform specific -- this default tracker is for Android. + _velocityTracker = new VelocityTracker(); // Correct _trackedRootPoint with ScrollStartDistance, so scrolling does not start with a skip of ScrollStartDistance _trackedRootPoint = new Point( diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index d4705bcdca..9482542b45 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -1,40 +1,13 @@ ๏ปฟ// Code in this file is derived from // https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/gestures/velocity_tracker.dart -//Copyright 2014 The Flutter Authors. All rights reserved. - -//Redistribution and use in source and binary forms, with or without modification, -//are permitted provided that the following conditions are met: - -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. - -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; -//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - using System; using System.Diagnostics; using Avalonia.Utilities; namespace Avalonia.Input.GestureRecognizers { - // TODO: add 'IOSScrollViewFlingVelocityTracker' and 'MacOSScrollViewFlingVelocityTracker'? + // Possible enhancement: add Flutter's 'IOSScrollViewFlingVelocityTracker' and 'MacOSScrollViewFlingVelocityTracker'? public readonly record struct Velocity(Vector PixelsPerSecond) { @@ -92,7 +65,7 @@ namespace Avalonia.Input.GestureRecognizers private const int HistorySize = 20; private const int HorizonMilliseconds = 100; private const int MinSampleSize = 3; - private const double MinFlingVelocity = 50.0; // Logical pixels / second (defined in flutter\lib\src\gesture\constants.dart) + private const double MinFlingVelocity = 50.0; // Logical pixels / second private const double MaxFlingVelocity = 8000.0; private readonly PointAtTime[] _samples = new PointAtTime[HistorySize]; From 9332fef8af52bc5eea6555354613187612eccbea Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 29 Dec 2022 10:05:11 +0100 Subject: [PATCH 34/95] flutter added to notice.md --- NOTICE.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/NOTICE.md b/NOTICE.md index e97fc654c9..bd26b65d70 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -303,3 +303,34 @@ https://github.com/chromium/chromium // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Flutter + +https://github.com/flutter/flutter + +//Copyright 2014 The Flutter Authors. All rights reserved. + +//Redistribution and use in source and binary forms, with or without modification, +//are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. + +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; +//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 0ad6dec3a9e43e7533298f7166b6cd475c5ec781 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 29 Dec 2022 10:17:51 +0100 Subject: [PATCH 35/95] VelocityTracker types marked as internal --- .../Input/GestureRecognizers/VelocityTracker.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index 9482542b45..f72ac61c4b 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -9,7 +9,7 @@ namespace Avalonia.Input.GestureRecognizers { // Possible enhancement: add Flutter's 'IOSScrollViewFlingVelocityTracker' and 'MacOSScrollViewFlingVelocityTracker'? - public readonly record struct Velocity(Vector PixelsPerSecond) + internal readonly record struct Velocity(Vector PixelsPerSecond) { public Velocity ClampMagnitude(double minValue, double maxValue) { @@ -45,7 +45,7 @@ namespace Avalonia.Input.GestureRecognizers /// * [VelocityTracker], which computes [VelocityEstimate]s. /// * [Velocity], which encapsulates (just) a velocity vector and provides some /// useful velocity operations. - public record VelocityEstimate(Vector PixelsPerSecond, double Confidence, TimeSpan Duration, Vector Offset); + internal record VelocityEstimate(Vector PixelsPerSecond, double Confidence, TimeSpan Duration, Vector Offset); internal record struct PointAtTime(bool Valid, Vector Point, TimeSpan Time); @@ -59,7 +59,7 @@ namespace Avalonia.Input.GestureRecognizers /// /// The quality of the velocity estimation will be better if more data points /// have been received. - public class VelocityTracker + internal class VelocityTracker { private const int AssumePointerMoveStoppedMilliseconds = 40; private const int HistorySize = 20; @@ -177,7 +177,7 @@ namespace Avalonia.Input.GestureRecognizers /// estimate or if the estimated velocity is zero./// /// /// - public Velocity GetVelocity() + internal Velocity GetVelocity() { var estimate = GetVelocityEstimate(); if (estimate == null || estimate.PixelsPerSecond.IsDefault) @@ -187,7 +187,7 @@ namespace Avalonia.Input.GestureRecognizers return new Velocity(estimate.PixelsPerSecond); } - public virtual Velocity GetFlingVelocity() + internal virtual Velocity GetFlingVelocity() { return GetVelocity().ClampMagnitude(MinFlingVelocity, MaxFlingVelocity); } From 157ac02dc661f2ddb23b615545ed825626eae9de Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 29 Dec 2022 10:58:26 +0100 Subject: [PATCH 36/95] _Matrix memory optimized using stack --- .../Input/GestureRecognizers/VelocityTracker.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index f72ac61c4b..206a73f436 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -236,7 +236,7 @@ namespace Avalonia.Input.GestureRecognizers int n = degree + 1; // Expand the X vector to a matrix A, pre-multiplied by the weights. - _Matrix a = new _Matrix(n, m); + _Matrix a = new _Matrix(m, stackalloc double[n * m]); for (int h = 0; h < m; h += 1) { a[0, h] = w[h]; @@ -249,9 +249,9 @@ namespace Avalonia.Input.GestureRecognizers // Apply the Gram-Schmidt process to A to obtain its QR decomposition. // Orthonormal basis, column-major order Vector. - _Matrix q = new _Matrix(n, m); + _Matrix q = new _Matrix(m, stackalloc double[n * m]); // Upper triangular matrix, row-major order. - _Matrix r = new _Matrix(n, n); + _Matrix r = new _Matrix(n, stackalloc double[n * n]); for (int j = 0; j < n; j += 1) { for (int h = 0; h < m; h += 1) @@ -352,15 +352,15 @@ namespace Avalonia.Input.GestureRecognizers return Math.Sqrt(Multiply(v, v)); } - private readonly struct _Matrix + private readonly ref struct _Matrix { private readonly int _columns; - private readonly double[] _elements; + private readonly Span _elements; - internal _Matrix(int rows, int cols) + internal _Matrix(int cols, Span elements) { _columns = cols; - _elements = new double[rows * cols]; + _elements = elements;// new double[rows * cols]; } public double this[int row, int col] @@ -369,7 +369,7 @@ namespace Avalonia.Input.GestureRecognizers set => _elements[row * _columns + col] = value; } - public Span GetRow(int row) => _elements.AsSpan(row * _columns, _columns); + public Span GetRow(int row) => _elements.Slice(row * _columns, _columns); } } } From c7c819295f82706d6f6ec0546ce9c83d1e095605 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Thu, 29 Dec 2022 11:26:28 +0100 Subject: [PATCH 37/95] removed comment --- src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs index 206a73f436..ce41aa6308 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs @@ -360,7 +360,7 @@ namespace Avalonia.Input.GestureRecognizers internal _Matrix(int cols, Span elements) { _columns = cols; - _elements = elements;// new double[rows * cols]; + _elements = elements; } public double this[int row, int col] From c099e2fd2c5e9924e98446bc34b0c79fb1870a7c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 30 Dec 2022 19:14:38 +0600 Subject: [PATCH 38/95] Use for(...) dst[c]=src[c] loop on arm due to a JIT bug --- .../Composition/Transport/BatchStream.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs index a3cad3cebd..2dd15dfedc 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Server; @@ -27,6 +28,37 @@ public record struct BatchStreamSegment public int ElementCount { get; set; } } + +// Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm, +// see https://github.com/dotnet/runtime/issues/80068 +static unsafe class UnalignedMemoryHelper +{ + public static T ReadUnaligned(byte* src) where T : unmanaged + { +#if NET6_0_OR_GREATER + Unsafe.SkipInit(out var rv); +#else + T rv; +#endif + UnalignedMemcpy((byte*)&rv, src, Unsafe.SizeOf()); + return rv; + } + + public static void WriteUnaligned(byte* dst, T value) where T : unmanaged + { + UnalignedMemcpy(dst, (byte*)&value, Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void UnalignedMemcpy(byte* dst, byte* src, int count) + { + for (var c = 0; c < count; c++) + { + dst[c] = src[c]; + } + } +} + internal class BatchStreamWriter : IDisposable { private readonly BatchStreamData _output; @@ -74,7 +106,15 @@ internal class BatchStreamWriter : IDisposable var size = Unsafe.SizeOf(); if (_currentDataSegment.Data == IntPtr.Zero || _currentDataSegment.ElementCount + size > _memoryPool.BufferSize) NextDataSegment(); - Unsafe.WriteUnaligned((byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount, item); + var ptr = (byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount; + + // Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm32, + // see https://github.com/dotnet/runtime/issues/80068 + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + UnalignedMemoryHelper.WriteUnaligned(ptr, item); + else + *(T*)ptr = item; + _currentDataSegment.ElementCount += size; } @@ -125,7 +165,16 @@ internal class BatchStreamReader : IDisposable if (_memoryOffset + size > _currentDataSegment.ElementCount) throw new InvalidOperationException("Attempted to read more memory then left in the current segment"); - var rv = Unsafe.ReadUnaligned((byte*)_currentDataSegment.Data + _memoryOffset); + var ptr = (byte*)_currentDataSegment.Data + _memoryOffset; + T rv; + + // Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm32, + // see https://github.com/dotnet/runtime/issues/80068 + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + rv = UnalignedMemoryHelper.ReadUnaligned(ptr); + else + rv = *(T*)ptr; + _memoryOffset += size; if (_memoryOffset == _currentDataSegment.ElementCount) { From 617c57376a34aa2511caa4b8d39283dbe4d9c801 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 30 Dec 2022 21:49:37 +0600 Subject: [PATCH 39/95] Use Unsafe.Read/WriteUnaligned for other platforms since it might be required for something like wasm --- .../Rendering/Composition/Transport/BatchStream.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs index 2dd15dfedc..5a88f0e91d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs @@ -113,7 +113,7 @@ internal class BatchStreamWriter : IDisposable if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) UnalignedMemoryHelper.WriteUnaligned(ptr, item); else - *(T*)ptr = item; + Unsafe.WriteUnaligned(ptr, item); _currentDataSegment.ElementCount += size; } @@ -173,7 +173,7 @@ internal class BatchStreamReader : IDisposable if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) rv = UnalignedMemoryHelper.ReadUnaligned(ptr); else - rv = *(T*)ptr; + rv = Unsafe.ReadUnaligned(ptr); _memoryOffset += size; if (_memoryOffset == _currentDataSegment.ElementCount) From 50c21198b4032faa0b8d188383a003ff830ced9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Dec 2022 22:04:09 +0000 Subject: [PATCH 40/95] Bump flat and mocha Bumps [flat](https://github.com/hughsk/flat) to 5.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together. Updates `flat` from 4.1.0 to 5.0.2 - [Release notes](https://github.com/hughsk/flat/releases) - [Commits](https://github.com/hughsk/flat/compare/4.1.0...5.0.2) Updates `mocha` from 8.1.3 to 8.4.0 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v8.1.3...v8.4.0) --- updated-dependencies: - dependency-name: flat dependency-type: indirect - dependency-name: mocha dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- .../HtmlTransport/webapp/package-lock.json | 791 ++++++------------ .../Remote/HtmlTransport/webapp/package.json | 2 +- 2 files changed, 250 insertions(+), 543 deletions(-) diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json index eb57cfb8da..272be1c8cb 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json @@ -372,6 +372,12 @@ "csstype": "^3.0.2" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -405,9 +411,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -444,18 +450,6 @@ "sprintf-js": "~1.0.2" } }, - "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" - } - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -469,9 +463,9 @@ "dev": true }, "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "brace-expansion": { @@ -538,9 +532,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -554,19 +548,19 @@ "dev": true }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "clean-stack": { @@ -576,40 +570,46 @@ "dev": true }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.1" } } } @@ -708,15 +708,6 @@ "strip-bom": "^4.0.0" } }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -724,68 +715,23 @@ "dev": true }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -829,13 +775,10 @@ } }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "foreground-child": { "version": "2.0.0", @@ -860,18 +803,12 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -911,9 +848,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -937,27 +874,12 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, "hasha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", @@ -1008,12 +930,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1023,51 +939,27 @@ "binary-extensions": "^2.0.0" } }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-callable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", - "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", - "dev": true - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1075,24 +967,9 @@ "dev": true }, "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-stream": { @@ -1101,21 +978,6 @@ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -1128,12 +990,6 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1222,22 +1078,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "iterate-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", - "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1339,36 +1179,91 @@ "dev": true }, "mocha": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", - "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { + "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.2", - "debug": "4.1.1", - "diff": "4.0.2", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.14.0", + "js-yaml": "4.0.0", "log-symbols": "4.0.0", "minimatch": "3.0.4", - "ms": "2.1.2", - "object.assign": "4.1.0", - "promise.allsettled": "1.0.2", - "serialize-javascript": "4.0.0", - "strip-json-comments": "3.0.1", - "supports-color": "7.1.0", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.0.0", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.1" + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "moq.ts": { @@ -1386,6 +1281,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -1570,30 +1471,6 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1604,12 +1481,12 @@ } }, "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { @@ -1679,9 +1556,9 @@ "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pkg-dir": { @@ -1741,19 +1618,6 @@ "fromentries": "^1.2.0" } }, - "promise.allsettled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", - "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", - "dev": true, - "requires": { - "array.prototype.map": "^1.0.1", - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "iterate-value": "^1.0.0" - } - }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -1792,9 +1656,9 @@ "dev": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -1858,9 +1722,9 @@ "dev": true }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -1947,30 +1811,10 @@ "strip-ansi": "^4.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -1983,9 +1827,9 @@ "dev": true }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { @@ -2100,70 +1944,52 @@ } }, "workerpool": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", - "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.1" } } } @@ -2193,214 +2019,89 @@ "dev": true }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "dependencies": { "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.1" } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true } } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yargs-unparser": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", - "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "decamelize": "^1.2.0", - "flat": "^4.1.0", - "is-plain-obj": "^1.1.0", - "yargs": "^14.2.3" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "yargs": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -2409,6 +2110,12 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json index dbb12e192a..da3a748c39 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json @@ -16,7 +16,7 @@ "@types/mocha": "8.0.3", "@types/react": "^16.3.14", "chai": "^4.2.0", - "mocha": "^8.1.3", + "mocha": "^8.4.0", "moq.ts": "^6.4.0", "nyc": "^15.1.0", "react": "^16.3.2", From bad0160a2d74b30f041fbe474a5c81e29ee5020d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Dec 2022 02:08:17 +0000 Subject: [PATCH 41/95] Bump json5 Bumps [json5](https://github.com/json5/json5) from 2.1.3 to 2.2.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.1.3...v2.2.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../HtmlTransport/webapp/package-lock.json | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json index 272be1c8cb..7ae6c4c8d9 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json @@ -1101,13 +1101,10 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", + "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "dev": true }, "locate-path": { "version": "6.0.0", @@ -1172,12 +1169,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, "mocha": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", From fb88efce5f4b980a00581a1e2626eade90e39e89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Dec 2022 06:29:27 +0000 Subject: [PATCH 42/95] Bump nanoid and mocha Bumps [nanoid](https://github.com/ai/nanoid) to 3.3.3 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together. Updates `nanoid` from 3.1.20 to 3.3.3 - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.1.20...3.3.3) Updates `mocha` from 8.4.0 to 10.2.0 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v8.4.0...v10.2.0) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect - dependency-name: mocha dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- .../HtmlTransport/webapp/package-lock.json | 290 +++++++----------- .../Remote/HtmlTransport/webapp/package.json | 2 +- 2 files changed, 110 insertions(+), 182 deletions(-) diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json index 7ae6c4c8d9..0c0857b680 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json @@ -372,12 +372,6 @@ "csstype": "^3.0.2" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -395,9 +389,9 @@ "dev": true }, "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -548,19 +542,19 @@ "dev": true }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "clean-stack": { @@ -578,40 +572,6 @@ "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "color-convert": { @@ -868,12 +828,6 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -946,9 +900,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { @@ -984,6 +938,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -1128,12 +1088,13 @@ "dev": true }, "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, "loose-envify": { @@ -1170,33 +1131,29 @@ } }, "mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "requires": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", + "glob": "7.2.0", "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -1209,9 +1166,9 @@ "dev": true }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -1231,15 +1188,60 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1273,9 +1275,9 @@ "dev": true }, "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "node-preload": { @@ -1647,9 +1649,9 @@ "dev": true }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -1713,9 +1715,9 @@ "dev": true }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -1793,22 +1795,23 @@ "dev": true }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -1925,19 +1928,10 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { @@ -1949,40 +1943,6 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "wrappy": { @@ -2024,38 +1984,6 @@ "yargs-parser": "^20.2.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json index da3a748c39..580c4059ba 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json @@ -16,7 +16,7 @@ "@types/mocha": "8.0.3", "@types/react": "^16.3.14", "chai": "^4.2.0", - "mocha": "^8.4.0", + "mocha": "^10.2.0", "moq.ts": "^6.4.0", "nyc": "^15.1.0", "react": "^16.3.2", From 64c12e9253914c4516a50333ae76eb3a00ff0273 Mon Sep 17 00:00:00 2001 From: Martijn Sneijders Date: Sat, 31 Dec 2022 14:57:59 +0100 Subject: [PATCH 43/95] [AndroidMotionEventsHelper] fixes eventTime to uptime android in milliseconds --- .../Platform/Specific/Helpers/AndroidMotionEventsHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs index 6d0e6be0ad..3d0f3b2652 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs @@ -38,7 +38,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers return null; } - var eventTime = (ulong)DateTime.Now.Millisecond; + var eventTime = (ulong)e.EventTime; var inputRoot = _view.InputRoot; var actionMasked = e.ActionMasked; var modifiers = GetModifiers(e.MetaState, e.ButtonState); From dcb4f759d16674078accdee887edf39568d499b2 Mon Sep 17 00:00:00 2001 From: daniel Date: Sun, 1 Jan 2023 13:58:14 +0200 Subject: [PATCH 44/95] Make FlowDirection's Path of CheckBox to fixed LTR --- src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index 9abcf5d32b..28f8649e2d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -50,7 +50,8 @@ Opacity="0" Fill="{DynamicResource CheckBoxCheckGlyphForegroundUnchecked}" Stretch="Uniform" - VerticalAlignment="Center" /> + VerticalAlignment="Center" + FlowDirection="LeftToRight" /> @@ -146,7 +147,6 @@ - From 5a3d9f15743d6e5caf75e5e5fe96898fe0d30e1c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Jan 2023 19:18:32 +0600 Subject: [PATCH 45/95] Use parent clip rect of the adorned visual --- .../Server/ServerCompositionVisual.cs | 19 ++++- .../Controls/AdornerTests.cs | 73 ++++++++++++++++++ tests/Avalonia.RenderTests/TestBase.cs | 9 ++- ...s_Adorner_Is_Properly_Clipped.expected.png | Bin 0 -> 673 bytes ...s_Adorner_Is_Properly_Clipped.expected.png | Bin 0 -> 673 bytes 5 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.RenderTests/Controls/AdornerTests.cs create mode 100644 tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png create mode 100644 tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 0e15cbd54b..0b2df6d2b3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -42,11 +42,18 @@ namespace Avalonia.Rendering.Composition.Server Root!.RenderedVisuals++; + if (Opacity != 1) + canvas.PushOpacity(Opacity); + if (AdornedVisual != null) + { + canvas.PostTransform = Matrix.Identity; + canvas.Transform = Matrix.Identity; + canvas.PushClip(AdornedVisual._combinedTransformedClipBounds); + } var transform = GlobalTransformMatrix; canvas.PostTransform = MatrixUtils.ToMatrix(transform); canvas.Transform = Matrix.Identity; - if (Opacity != 1) - canvas.PushOpacity(Opacity); + var boundsRect = new Rect(new Size(Size.X, Size.Y)); if (ClipToBounds && !HandlesClipToBounds) canvas.PushClip(Root!.SnapToDevicePixels(boundsRect)); @@ -67,6 +74,8 @@ namespace Avalonia.Rendering.Composition.Server canvas.PopGeometryClip(); if (ClipToBounds && !HandlesClipToBounds) canvas.PopClip(); + if (AdornedVisual != null) + canvas.PopClip(); if(Opacity != 1) canvas.PopOpacity(); } @@ -155,8 +164,12 @@ namespace Avalonia.Rendering.Composition.Server _clipSizeDirty = false; } + + _combinedTransformedClipBounds = + AdornedVisual?._combinedTransformedClipBounds + ?? Parent?._combinedTransformedClipBounds + ?? new Rect(Root!.Size); - _combinedTransformedClipBounds = Parent?._combinedTransformedClipBounds ?? new Rect(Root!.Size); if (_transformedClipBounds != null) _combinedTransformedClipBounds = _combinedTransformedClipBounds.Intersect(_transformedClipBounds.Value); diff --git a/tests/Avalonia.RenderTests/Controls/AdornerTests.cs b/tests/Avalonia.RenderTests/Controls/AdornerTests.cs new file mode 100644 index 0000000000..c833017212 --- /dev/null +++ b/tests/Avalonia.RenderTests/Controls/AdornerTests.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.Media; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests; +#else +namespace Avalonia.Direct2D1.RenderTests.Controls; +#endif + +public class AdornerTests : TestBase +{ + public AdornerTests() + : base(@"Controls\Adorner") + { + } + + [Fact] + public async Task Focus_Adorner_Is_Properly_Clipped() + { + Border adorned; + var tree = new Decorator + { + Child = new VisualLayerManager + { + Child = new Border + { + Background = Brushes.Red, + Padding = new Thickness(10, 50, 10,10), + Child = new Border() + { + Background = Brushes.White, + ClipToBounds = true, + Padding = new Thickness(0, -30, 0, 0), + Child = adorned = new Border + { + Background = Brushes.Green, + VerticalAlignment = VerticalAlignment.Top, + Height = 100, + Width = 50 + } + } + } + }, + Width = 200, + Height = 200 + }; + var adorner = new Border + { + BorderThickness = new Thickness(2), + BorderBrush = Brushes.Black + }; + + var size = new Size(tree.Width, tree.Height); + tree.Measure(size); + tree.Arrange(new Rect(size)); + + + adorned.AttachedToVisualTree += delegate + { + AdornerLayer.SetAdornedElement(adorner, adorned); + AdornerLayer.GetAdornerLayer(adorned)!.Children.Add(adorner); + }; + tree.Measure(size); + tree.Arrange(new Rect(size)); + + await RenderToFile(tree); + CompareImages(skipImmediate: true, skipDeferred: true); + } +} \ No newline at end of file diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 8a127897d7..313281d6c6 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -156,7 +156,8 @@ namespace Avalonia.Direct2D1.RenderTests public ILockedFramebuffer Lock() => _bitmap.Lock(); } - protected void CompareImages([CallerMemberName] string testName = "") + protected void CompareImages([CallerMemberName] string testName = "", + bool skipImmediate = false, bool skipDeferred = false, bool skipCompositor = false) { var expectedPath = Path.Combine(OutputPath, testName + ".expected.png"); var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); @@ -172,17 +173,17 @@ namespace Avalonia.Direct2D1.RenderTests var deferredError = CompareImages(deferred, expected); var compositedError = CompareImages(composited, expected); - if (immediateError > 0.022) + if (immediateError > 0.022 && !skipImmediate) { Assert.True(false, immediatePath + ": Error = " + immediateError); } - if (deferredError > 0.022) + if (deferredError > 0.022 && !skipDeferred) { Assert.True(false, deferredPath + ": Error = " + deferredError); } - if (compositedError > 0.022) + if (compositedError > 0.022 && !skipCompositor) { Assert.True(false, compositedPath + ": Error = " + compositedError); } diff --git a/tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png b/tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..6a67087d41828128492aa2cb44f7d53f067db4f1 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|Qfpr;B4q z#hkZSH+nfc3OHPhJ=y;J{BfllC%-D5QmgWtEE3Jb^Zo3+t!a$&tE_DqY>XQn1UOje zPqf^AI^B4`#j)%g)%O4H^!I(N{eJxXji*uayZ)-!D`Iz4IT|2zRSq{DZ!m$3x^zZ)*9ThlOnjD4(@g_3ckDu}UDb_{>0S+c~;sf9P X|Dv4+8exvWG{@lS>gTe~DWM4f2)3Ck literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png b/tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..6a67087d41828128492aa2cb44f7d53f067db4f1 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|Qfpr;B4q z#hkZSH+nfc3OHPhJ=y;J{BfllC%-D5QmgWtEE3Jb^Zo3+t!a$&tE_DqY>XQn1UOje zPqf^AI^B4`#j)%g)%O4H^!I(N{eJxXji*uayZ)-!D`Iz4IT|2zRSq{DZ!m$3x^zZ)*9ThlOnjD4(@g_3ckDu}UDb_{>0S+c~;sf9P X|Dv4+8exvWG{@lS>gTe~DWM4f2)3Ck literal 0 HcmV?d00001 From 78b4c6a96e80b8ac6f9a103c71154bd3d8caea20 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Jan 2023 20:25:26 +0600 Subject: [PATCH 46/95] Ignore Opacity for hit-testing purposes --- .../Server/ServerCompositionVisual.cs | 13 ++++++-- .../Rendering/CompositorHitTestingTests.cs | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 0e15cbd54b..c4f0ebcd3b 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -162,8 +162,14 @@ namespace Avalonia.Rendering.Composition.Server EffectiveOpacity = Opacity * (Parent?.EffectiveOpacity ?? 1); - IsVisibleInFrame = _parent?.IsVisibleInFrame != false && Visible && EffectiveOpacity > 0.04 && !_isBackface && - !_combinedTransformedClipBounds.IsDefault; + IsHitTestVisibleInFrame = _parent?.IsHitTestVisibleInFrame != false + && Visible + && !_isBackface + && !_combinedTransformedClipBounds.IsDefault; + + IsVisibleInFrame = IsHitTestVisibleInFrame + && _parent?.IsVisibleInFrame != false + && EffectiveOpacity > 0.04; if (wasVisible != IsVisibleInFrame || positionChanged) { @@ -187,7 +193,7 @@ namespace Avalonia.Rendering.Composition.Server readback.Revision = root.Revision; readback.Matrix = GlobalTransformMatrix; readback.TargetId = Root.Id; - readback.Visible = IsVisibleInFrame; + readback.Visible = IsHitTestVisibleInFrame; } void AddDirtyRect(Rect rc) @@ -248,6 +254,7 @@ namespace Avalonia.Rendering.Composition.Server } public bool IsVisibleInFrame { get; set; } + public bool IsHitTestVisibleInFrame { get; set; } public double EffectiveOpacity { get; set; } public Rect TransformedOwnContentBounds { get; set; } public virtual Rect OwnContentBounds => new Rect(0, 0, Size.X, Size.Y); diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs index 27faf1e13c..3d8369faeb 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs @@ -82,6 +82,39 @@ public class CompositorHitTestingTests : CompositorTestsBase } } + [Theory, + InlineData(false, false), + InlineData(true, false), + InlineData(false, true), + InlineData(true, true), + ] + public void HitTest_Should_Find_Zero_Opacity_Controls_At_Point(bool parent, bool child) + { + + using (var s = new CompositorServices(new Size(200, 200))) + { + Border visible, border; + s.TopLevel.Content = border = new Border + { + Width = 100, + Height = 100, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Opacity = parent ? 0 : 1, + Child = visible = new Border + { + Opacity = child ? 0 : 1, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch, + } + }; + + s.AssertHitTest(new Point(100, 100), null, visible, border); + } + } + [Fact] public void HitTest_Should_Not_Find_Control_Outside_Point() { From 11b8bc2322ed4da16e115a28e3580e35a5eb4ab5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 2 Jan 2023 15:01:14 +0000 Subject: [PATCH 47/95] keep app builder instance --- .../Avalonia.Android/AvaloniaSplashActivity.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs index ec26ee5599..38038ef26c 100644 --- a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs @@ -1,6 +1,5 @@ using Android.OS; using AndroidX.AppCompat.App; -using Avalonia.Controls; namespace Avalonia.Android { @@ -8,15 +7,22 @@ namespace Avalonia.Android { protected abstract AppBuilder CreateAppBuilder(); + private static AppBuilder s_appBuilder; + protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); - var builder = CreateAppBuilder(); + if (s_appBuilder == null) + { + var builder = CreateAppBuilder(); + + var lifetime = new SingleViewLifetime(); - var lifetime = new SingleViewLifetime(); + builder.SetupWithLifetime(lifetime); - builder.SetupWithLifetime(lifetime); + s_appBuilder = builder; + } } } From 5ff712109ba30fc82f4116e11691086d7cb52c98 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 3 Jan 2023 15:22:12 +1100 Subject: [PATCH 48/95] avoid nuget refs on net6 System.ValueTuple, Runtime.CompilerServices.Unsafe, and System.Memory should not be required no net6 and up --- build/Base.props | 2 +- build/System.Memory.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Base.props b/build/Base.props index 100c9088cd..9ec1c3c2d3 100644 --- a/build/Base.props +++ b/build/Base.props @@ -1,5 +1,5 @@ ๏ปฟ - + diff --git a/build/System.Memory.props b/build/System.Memory.props index b36998a780..a413e18927 100644 --- a/build/System.Memory.props +++ b/build/System.Memory.props @@ -1,5 +1,5 @@ - + From a845fc02b66690adeae8926d9542faf5a72bb0b8 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 12:32:31 +0100 Subject: [PATCH 49/95] fix: Obsolete --- src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGrid.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridRow.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridRows.cs | 4 ++-- src/Avalonia.Controls/BorderVisual.cs | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs index a991f2f657..55893e9890 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs @@ -303,7 +303,7 @@ namespace Avalonia.Rendering.SceneGraph if (ClipToBounds) { context.Transform = Matrix.Identity; - if (ClipToBoundsRadius.IsEmpty) + if (ClipToBoundsRadius.IsDefault) context.PushClip(ClipBounds); else context.PushClip(new RoundedRect(ClipBounds, ClipToBoundsRadius)); diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 5dcad83601..58d5deb029 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -3299,7 +3299,7 @@ namespace Avalonia.Controls newCell.IsVisible = column.IsVisible; if (row.OwningGrid.CellTheme is {} cellTheme) { - newCell.SetValue(ThemeProperty, cellTheme, BindingPriority.TemplatedParent); + newCell.SetValue(ThemeProperty, cellTheme, BindingPriority.Template); } } row.Cells.Insert(column.Index, newCell); diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 75101dc487..d9d80acdc3 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -897,7 +897,7 @@ namespace Avalonia.Controls result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty]; if (OwningGrid.ColumnHeaderTheme is {} columnTheme) { - result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.TemplatedParent); + result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template); } return result; diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 252868847a..b3e106a7bf 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -673,7 +673,7 @@ namespace Avalonia.Controls }; if (OwningGrid.ColumnHeaderTheme is {} columnHeaderTheme) { - dragIndicator.SetValue(ThemeProperty, columnHeaderTheme, BindingPriority.TemplatedParent); + dragIndicator.SetValue(ThemeProperty, columnHeaderTheme, BindingPriority.Template); } dragIndicator.PseudoClasses.Add(":dragIndicator"); diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index dfa6ea2e46..641360dbe4 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -253,7 +253,7 @@ namespace Avalonia.Controls }; if (OwningGrid.CellTheme is {} cellTheme) { - _fillerCell.SetValue(ThemeProperty, cellTheme, BindingPriority.TemplatedParent); + _fillerCell.SetValue(ThemeProperty, cellTheme, BindingPriority.Template); } if (_cellsElement != null) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 4d3bccee70..00e035270c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -1029,7 +1029,7 @@ namespace Avalonia.Controls dataGridRow.DataContext = dataContext; if (RowTheme is {} rowTheme) { - dataGridRow.SetValue(ThemeProperty, rowTheme, BindingPriority.TemplatedParent); + dataGridRow.SetValue(ThemeProperty, rowTheme, BindingPriority.Template); } CompleteCellsCollection(dataGridRow); @@ -2743,7 +2743,7 @@ namespace Avalonia.Controls groupHeader.Level = rowGroupInfo.Level; if (RowGroupTheme is {} rowGroupTheme) { - groupHeader.SetValue(ThemeProperty, rowGroupTheme, BindingPriority.TemplatedParent); + groupHeader.SetValue(ThemeProperty, rowGroupTheme, BindingPriority.Template); } // Set the RowGroupHeader's PropertyName. Unfortunately, CollectionViewGroup doesn't have this diff --git a/src/Avalonia.Controls/BorderVisual.cs b/src/Avalonia.Controls/BorderVisual.cs index 7afbf9edcf..18448edac4 100644 --- a/src/Avalonia.Controls/BorderVisual.cs +++ b/src/Avalonia.Controls/BorderVisual.cs @@ -50,7 +50,7 @@ class CompositionBorderVisual : CompositionDrawListVisual if (ClipToBounds) { var clipRect = Root!.SnapToDevicePixels(new Rect(new Size(Size.X, Size.Y))); - if (_cornerRadius.IsEmpty) + if (_cornerRadius.IsDefault) canvas.PushClip(clipRect); else canvas.PushClip(new RoundedRect(clipRect, _cornerRadius)); @@ -73,4 +73,4 @@ class CompositionBorderVisual : CompositionDrawListVisual protected override bool HandlesClipToBounds => true; } -} \ No newline at end of file +} From bd8794dbdad7837e79f0cce7806da020baa715fd Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 14:48:15 +0100 Subject: [PATCH 50/95] fix: Warnings CS1574 --- .../IClassicDesktopStyleApplicationLifetime.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index 4b88f6b537..22b5f8236d 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes @@ -19,7 +18,7 @@ namespace Avalonia.Controls.ApplicationLifetimes /// /// Gets the arguments passed to the - /// + /// /// method. /// string[]? Args { get; } From 48eba14e700d3efb63100ab4bb7895e9ccac3667 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 14:47:23 +0100 Subject: [PATCH 51/95] fix: Warnings CA1823 --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 5dcad83601..87e596d332 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -51,15 +51,10 @@ namespace Avalonia.Controls private const string DATAGRID_elementTopLeftCornerHeaderName = "PART_TopLeftCornerHeader"; private const string DATAGRID_elementTopRightCornerHeaderName = "PART_TopRightCornerHeader"; private const string DATAGRID_elementBottomRightCornerHeaderName = "PART_BottomRightCorner"; - private const string DATAGRID_elementValidationSummary = "PART_ValidationSummary"; private const string DATAGRID_elementVerticalScrollbarName = "PART_VerticalScrollbar"; - - private const bool DATAGRID_defaultAutoGenerateColumns = true; internal const bool DATAGRID_defaultCanUserReorderColumns = true; internal const bool DATAGRID_defaultCanUserResizeColumns = true; internal const bool DATAGRID_defaultCanUserSortColumns = true; - private const DataGridRowDetailsVisibilityMode DATAGRID_defaultRowDetailsVisibility = DataGridRowDetailsVisibilityMode.VisibleWhenSelected; - private const DataGridSelectionMode DATAGRID_defaultSelectionMode = DataGridSelectionMode.Extended; /// /// The default order to use for columns when there is no From 5c5cc06d27bc5d18c46240085bfe1c03771a1d5c Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 14:31:56 +0100 Subject: [PATCH 52/95] fix: Warnings CS0436 conflicts with the imported type 'GetProcAddressAttribute' in 'Avalonia.Base' --- src/Avalonia.OpenGL/Avalonia.OpenGL.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index a9136c2c99..9b38c150fa 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -13,4 +13,9 @@ + + + + + From 306ea5b54dc10ae686b0b45af07c57d6a07cc084 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 15:08:11 +0100 Subject: [PATCH 53/95] feat: Enable Rule CA2211 --- .editorconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.editorconfig b/.editorconfig index ff7ac5d69e..3620896f34 100644 --- a/.editorconfig +++ b/.editorconfig @@ -169,6 +169,8 @@ dotnet_diagnostic.CA1828.severity = warning dotnet_diagnostic.CA1829.severity = warning #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters dotnet_diagnostic.CA1847.severity = warning +#CACA2211:Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = error # Wrapping preferences csharp_wrap_before_ternary_opsigns = false From bd2e2d1e9b0175de4001765a7a102296f79339e6 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 3 Jan 2023 15:29:34 +0100 Subject: [PATCH 54/95] feat: Address Rule CA2211 --- .../ColorPalettes/FlatColorPalette.cs | 4 +++- .../ColorPalettes/FlatHalfColorPalette.cs | 4 +++- .../ColorPalettes/MaterialColorPalette.cs | 4 +++- .../ColorPalettes/MaterialHalfColorPalette.cs | 4 +++- src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 2 +- src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs index 2cc5b99b2e..0188e61bec 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs @@ -289,8 +289,10 @@ namespace Avalonia.Controls }; // See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static Color[,]? _colorChart = null; - protected static object _colorChartMutex = new object(); +#pragma warning restore CA2211 // Non-constant fields should not be visible + protected static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs index 2758124fae..1bffd66994 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs @@ -10,8 +10,10 @@ namespace Avalonia.Controls /// public class FlatHalfColorPalette : IColorPalette { +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static Color[,]? _colorChart = null; - protected static object _colorChartMutex = new object(); +#pragma warning restore CA2211 // Non-constant fields should not be visible + protected static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs index d009926bc5..a9024bc18c 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs @@ -344,8 +344,10 @@ namespace Avalonia.Controls // See: https://material.io/design/color/the-color-system.html#tools-for-picking-colors // This is a reduced palette for uniformity +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static Color[,]? _colorChart = null; - protected static object _colorChartMutex = new object(); +#pragma warning restore CA2211 // Non-constant fields should not be visible + protected static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs index 01d44aa65d..371c94504b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs @@ -10,8 +10,10 @@ namespace Avalonia.Controls /// public class MaterialHalfColorPalette : IColorPalette { +#pragma warning disable CA2211 // Non-constant fields should not be visible protected static Color[,]? _colorChart = null; - protected static object _colorChartMutex = new object(); +#pragma warning restore CA2211 // Non-constant fields should not be visible + protected static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs index c308312398..8988be9b08 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static StyledProperty IsThreeStateProperty = + public readonly static StyledProperty IsThreeStateProperty = CheckBox.IsThreeStateProperty.AddOwner(); /// diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 75101dc487..f6c503ac83 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -185,7 +185,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static StyledProperty IsVisibleProperty = + public static readonly StyledProperty IsVisibleProperty = Control.IsVisibleProperty.AddOwner(); /// diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs index 9fddb91231..99616e4cdf 100644 --- a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs +++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs @@ -8,6 +8,6 @@ /// /// Provides access to the internal for use in DevTools. /// - public static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; + public readonly static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; } } From 5846c3b1b6e9e13559e2221ac2332245783c5c47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:41:08 +0000 Subject: [PATCH 55/95] Bump json5 from 1.0.1 to 1.0.2 in /src/Browser/Avalonia.Browser/webapp Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../Avalonia.Browser/webapp/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Browser/Avalonia.Browser/webapp/package-lock.json b/src/Browser/Avalonia.Browser/webapp/package-lock.json index 06e94629d7..2d875e84db 100644 --- a/src/Browser/Avalonia.Browser/webapp/package-lock.json +++ b/src/Browser/Avalonia.Browser/webapp/package-lock.json @@ -2162,9 +2162,9 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -4699,9 +4699,9 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" From 5d1e5d97c2bf4d99e83a21c7e314761942e09ac8 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Tue, 3 Jan 2023 23:04:21 +0300 Subject: [PATCH 56/95] [Text] [Selection] fix double click selection on word end position --- src/Avalonia.Controls/SelectableTextBlock.cs | 6 ++- src/Avalonia.Controls/TextBox.cs | 6 ++- src/Avalonia.Controls/Utils/StringUtils.cs | 49 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs index b343439f98..f4c4d54951 100644 --- a/src/Avalonia.Controls/SelectableTextBlock.cs +++ b/src/Avalonia.Controls/SelectableTextBlock.cs @@ -300,7 +300,11 @@ namespace Avalonia.Controls _wordSelectionStart = SelectionStart; - SelectionEnd = StringUtils.NextWord(text, index); + if (!StringUtils.IsEndOfWord(text, index)) + { + SelectionEnd = StringUtils.NextWord(text, index); + } + break; case 3: _wordSelectionStart = -1; diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 1bdec878d9..9a2ee12dc3 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1475,7 +1475,11 @@ namespace Avalonia.Controls _wordSelectionStart = SelectionStart; - SelectionEnd = StringUtils.NextWord(text, index); + if (!StringUtils.IsEndOfWord(text, index)) + { + SelectionEnd = StringUtils.NextWord(text, index); + } + break; case 3: _wordSelectionStart = -1; diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs index b2e56434b2..6c459d7985 100644 --- a/src/Avalonia.Controls/Utils/StringUtils.cs +++ b/src/Avalonia.Controls/Utils/StringUtils.cs @@ -67,6 +67,55 @@ namespace Avalonia.Controls.Utils } } + public static bool IsEndOfWord(string text, int index) + { + if (index >= text.Length) + { + return true; + } + + var codepoint = new Codepoint(text[index]); + + if (!codepoint.IsWhiteSpace) + { + return false; + } + // A 'word' starts with an AlphaNumeric or some punctuation symbols immediately + // preceeded by lwsp. + if (index > 0) + { + var nextCodePoint = new Codepoint(text[index + 1]); + + if (nextCodePoint.IsBreakChar) + { + return true; + } + } + + switch (codepoint.GeneralCategory) + { + case GeneralCategory.LowercaseLetter: + case GeneralCategory.TitlecaseLetter: + case GeneralCategory.UppercaseLetter: + case GeneralCategory.DecimalNumber: + case GeneralCategory.LetterNumber: + case GeneralCategory.OtherNumber: + case GeneralCategory.DashPunctuation: + case GeneralCategory.InitialPunctuation: + case GeneralCategory.OpenPunctuation: + case GeneralCategory.CurrencySymbol: + case GeneralCategory.MathSymbol: + return false; + + // TODO: How do you do this in .NET? + // case UnicodeCategory.OtherPunctuation: + // // words cannot start with '.', but they can start with '&' or '*' (for example) + // return g_unichar_break_type(buffer->text[index]) == G_UNICODE_BREAK_ALPHABETIC; + default: + return true; + } + } + public static int PreviousWord(string text, int cursor) { if (string.IsNullOrEmpty(text)) From 876fa088124941aeb60e81f00bdb2bf3a26b882a Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:18:35 +1100 Subject: [PATCH 57/95] move to a consistent langversion of 11 --- Directory.Build.props | 1 + build/SharedVersion.props | 1 - src/Browser/Avalonia.Browser/Avalonia.Browser.csproj | 1 - src/tools/DevAnalyzers/DevAnalyzers.csproj | 1 - src/tools/DevGenerators/DevGenerators.csproj | 1 - tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj | 1 - .../Avalonia.Controls.DataGrid.UnitTests.csproj | 1 - .../Avalonia.Controls.UnitTests.csproj | 1 - .../Avalonia.Markup.Xaml.UnitTests.csproj | 1 - tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj | 1 - tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj | 1 - 11 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 42daa2df7f..c19a55e8ea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,5 +7,6 @@ false false False + 11 diff --git a/build/SharedVersion.props b/build/SharedVersion.props index e9c3d65b41..eca3ba37b0 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -8,7 +8,6 @@ https://github.com/AvaloniaUI/Avalonia/ true $(NoWarn);CS1591 - preview MIT Icon.png Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly. diff --git a/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj index 014d387cb2..2fc3cc885a 100644 --- a/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj +++ b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj @@ -1,7 +1,6 @@ net7.0 - preview enable true diff --git a/src/tools/DevAnalyzers/DevAnalyzers.csproj b/src/tools/DevAnalyzers/DevAnalyzers.csproj index 53e3e74e76..e5c2fc6cf6 100644 --- a/src/tools/DevAnalyzers/DevAnalyzers.csproj +++ b/src/tools/DevAnalyzers/DevAnalyzers.csproj @@ -2,7 +2,6 @@ netstandard2.0 - 10 enable diff --git a/src/tools/DevGenerators/DevGenerators.csproj b/src/tools/DevGenerators/DevGenerators.csproj index 069ff159fc..30da940514 100644 --- a/src/tools/DevGenerators/DevGenerators.csproj +++ b/src/tools/DevGenerators/DevGenerators.csproj @@ -4,7 +4,6 @@ netstandard2.0 enable false - 10 diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 0162f53f5e..4cdce8df26 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -3,7 +3,6 @@ net6.0 Library true - latest diff --git a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj index eb1bf24d0c..20c2f711ad 100644 --- a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj +++ b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj @@ -1,7 +1,6 @@ net6.0 - latest Library true diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 12eb290fde..471f19f948 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -1,7 +1,6 @@ ๏ปฟ net6.0 - latest Library true diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index 6dce5eaab5..fa4957c24c 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -3,7 +3,6 @@ net6.0;net47 Library true - latest diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index ccde66a50e..ea91b8c196 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -1,7 +1,6 @@ ๏ปฟ net6.0 - latest diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index cb6884cad8..b9c2a619d9 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -1,7 +1,6 @@ ๏ปฟ netstandard2.0 - latest false Library false From 2e68b148e3f797e22f43c80e33b6601451564bb7 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:21:02 +1100 Subject: [PATCH 58/95] move sdk to 7.0.101 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index a9318b212f..a3e251852f 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "7.0.101", "rollForward": "latestFeature" }, "msbuild-sdks": { From 9cc8381d3e544e2a9026669fda0eb7d5e20291b0 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:34:34 +1100 Subject: [PATCH 59/95] fix minor typos * adversly => adversely * visiblility => visibility --- samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs | 2 +- src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs index 11f504f4db..7520dabf37 100644 --- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs @@ -17,7 +17,7 @@ namespace ControlCatalog.Pages this.Get("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " + "to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " + "are easy to use with touch or mouse, and they can be styled and configured in several different ways. " + - "12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden."; + "12 - hour or 24 - hour clock and visibility of AM / PM is dynamically set based on user time settings, or can be overridden."; } diff --git a/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs b/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs index 8ecd9119f6..ecd6154e0b 100644 --- a/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs +++ b/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs @@ -16,7 +16,7 @@ public class GenericVirtualAnalyzer : DiagnosticAnalyzer "Performance", DiagnosticSeverity.Warning, isEnabledByDefault: true, - description: "Generic virtual methods affect JIT startup time adversly and should be avoided."); + description: "Generic virtual methods affect JIT startup time adversely and should be avoided."); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); From 9de36fc19deed18f32c33e24221c595a7166aca1 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:37:45 +1100 Subject: [PATCH 60/95] fix Committed typo Commited => Committed --- src/Avalonia.Base/Rendering/Composition/Compositor.cs | 2 +- .../Composition/Server/ServerCompositionDrawListVisual.cs | 4 ++-- .../Composition/Server/ServerCustomCompositionVisual.cs | 6 +++--- .../Rendering/Composition/Server/ServerList.cs | 4 ++-- .../Rendering/Composition/Server/ServerObject.cs | 8 ++++---- .../Rendering/Composition/Transport/Batch.cs | 2 +- src/Avalonia.Controls/BorderVisual.cs | 4 ++-- src/tools/DevGenerators/CompositionGenerator/Generator.cs | 6 +++--- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs index b4817bfe9a..7fc5487171 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs @@ -111,7 +111,7 @@ namespace Avalonia.Rendering.Composition } } - batch.CommitedAt = Server.Clock.Elapsed; + batch.CommittedAt = Server.Clock.Elapsed; _server.EnqueueBatch(batch); lock (_pendingBatchLock) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs index 8dc088fed1..aebe3a5cdd 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs @@ -48,7 +48,7 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua } } - protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) + protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt) { if (reader.Read() == 1) { @@ -56,7 +56,7 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua _renderCommands = reader.ReadObject(); _contentBounds = null; } - base.DeserializeChangesCore(reader, commitedAt); + base.DeserializeChangesCore(reader, committedAt); } protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs index 227df87a87..74889c9bfe 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs @@ -16,9 +16,9 @@ internal class ServerCompositionCustomVisual : ServerCompositionContainerVisual, _handler.Attach(this); } - protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) + protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt) { - base.DeserializeChangesCore(reader, commitedAt); + base.DeserializeChangesCore(reader, committedAt); var count = reader.Read(); for (var c = 0; c < count; c++) { @@ -79,4 +79,4 @@ internal class ServerCompositionCustomVisual : ServerCompositionContainerVisual, ?.Log(_handler, $"Exception in {_handler.GetType().Name}.{nameof(CompositionCustomVisualHandler.OnRender)} {{0}}", e); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs index 08a3fdce22..2500358866 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs @@ -14,7 +14,7 @@ namespace Avalonia.Rendering.Composition.Server { public List List { get; } = new List(); - protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) + protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt) { if (reader.Read() == 1) { @@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition.Server for (var c = 0; c < count; c++) List.Add(reader.ReadObject()); } - base.DeserializeChangesCore(reader, commitedAt); + base.DeserializeChangesCore(reader, committedAt); } public override long LastChangedBy diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs index 257d3b29a2..31cb16ec20 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs @@ -104,13 +104,13 @@ namespace Avalonia.Rendering.Composition.Server } protected void SetAnimatedValue(CompositionProperty prop, ref T field, - TimeSpan commitedAt, IAnimationInstance animation) where T : struct + TimeSpan committedAt, IAnimationInstance animation) where T : struct { if (IsActive && _animations.TryGetValue(prop, out var oldAnimation)) oldAnimation.Deactivate(); _animations[prop] = animation; - animation.Initialize(commitedAt, ExpressionVariant.Create(field), prop); + animation.Initialize(committedAt, ExpressionVariant.Create(field), prop); if(IsActive) animation.Activate(); @@ -165,7 +165,7 @@ namespace Avalonia.Rendering.Composition.Server public virtual CompositionProperty? GetCompositionProperty(string fieldName) => null; - protected virtual void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) + protected virtual void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt) { if (this is IDisposable disp && reader.Read() == 1) @@ -174,7 +174,7 @@ namespace Avalonia.Rendering.Composition.Server public void DeserializeChanges(BatchStreamReader reader, Batch batch) { - DeserializeChangesCore(reader, batch.CommitedAt); + DeserializeChangesCore(reader, batch.CommittedAt); ValuesInvalidated(); ItselfLastChangedBy = batch.SequenceId; } diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs b/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs index d3e3664f84..803ea809c0 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs @@ -29,7 +29,7 @@ namespace Avalonia.Rendering.Composition.Transport public BatchStreamData Changes { get; private set; } - public TimeSpan CommitedAt { get; set; } + public TimeSpan CommittedAt { get; set; } public void Complete() { diff --git a/src/Avalonia.Controls/BorderVisual.cs b/src/Avalonia.Controls/BorderVisual.cs index 18448edac4..b0e5c30e2f 100644 --- a/src/Avalonia.Controls/BorderVisual.cs +++ b/src/Avalonia.Controls/BorderVisual.cs @@ -63,9 +63,9 @@ class CompositionBorderVisual : CompositionDrawListVisual } - protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt) + protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt) { - base.DeserializeChangesCore(reader, commitedAt); + base.DeserializeChangesCore(reader, committedAt); if (reader.Read()) _cornerRadius = reader.Read(); } diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.cs index f8d5ad826e..3b5d3d8c3f 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.cs @@ -257,7 +257,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator if (cl.Properties.Count > 0) { server = server.AddMembers(((MethodDeclarationSyntax)ParseMemberDeclaration( - $"protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt){{}}") + $"protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt){{}}") !) .WithBody(ApplyDeserializeChangesEpilogue(deserializeMethodBody, cl))); server = server.AddMembers(MethodDeclaration(ParseTypeName("void"), "OnFieldsDeserialized") @@ -481,7 +481,7 @@ return; private static BlockSyntax DeserializeChangesPrologue(GClass cl) { return Block(ParseStatement($@" -base.DeserializeChangesCore(reader, commitedAt); +base.DeserializeChangesCore(reader, committedAt); DeserializeChangesExtra(reader); var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); ")); @@ -500,7 +500,7 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); { code = $@" if((changed & {changedFieldsType}.{prop.Name}Animated) == {changedFieldsType}.{prop.Name}Animated) - SetAnimatedValue({CompositionPropertyField(prop)}, ref {PropertyBackingFieldName(prop)}, commitedAt, reader.ReadObject()); + SetAnimatedValue({CompositionPropertyField(prop)}, ref {PropertyBackingFieldName(prop)}, committedAt, reader.ReadObject()); else "; } From ba5e44a4dfdda506fb34e20e697e84535bf91d5d Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:51:33 +1100 Subject: [PATCH 61/95] 7.0.101 in pipelines --- azure-pipelines-integrationtests.yml | 8 ++++---- azure-pipelines.yml | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml index 43253ac6be..02e8bba43f 100644 --- a/azure-pipelines-integrationtests.yml +++ b/azure-pipelines-integrationtests.yml @@ -18,9 +18,9 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100' + displayName: 'Use .NET Core SDK 7.0.101' inputs: - version: 7.0.100 + version: 7.0.101 - script: system_profiler SPDisplaysDataType |grep Resolution @@ -56,9 +56,9 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100' + displayName: 'Use .NET Core SDK 7.0.101' inputs: - version: 7.0.100 + version: 7.0.101 - task: Windows Application Driver@0 inputs: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a3bbc33418..fb04d4dd25 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,9 +35,9 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0' + displayName: 'Use .NET Core SDK 7.0.101' inputs: - version: 7.0.100 + version: 7.0.101 - task: CmdLine@2 displayName: 'Install Workloads' @@ -72,9 +72,9 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100' + displayName: 'Use .NET Core SDK 7.0.101' inputs: - version: 7.0.100 + version: 7.0.101 - task: CmdLine@2 displayName: 'Install Workloads' @@ -143,9 +143,9 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100' + displayName: 'Use .NET Core SDK 7.0.101' inputs: - version: 7.0.100 + version: 7.0.101 - task: CmdLine@2 displayName: 'Install Workloads' From 773f24dfc672eeb1bde3e0cdac73578901ec6636 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 4 Jan 2023 10:53:42 +1100 Subject: [PATCH 62/95] use SDK 6.0.404 --- azure-pipelines-integrationtests.yml | 8 ++++---- azure-pipelines.yml | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml index 43253ac6be..33e65405d0 100644 --- a/azure-pipelines-integrationtests.yml +++ b/azure-pipelines-integrationtests.yml @@ -13,9 +13,9 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.401' + displayName: 'Use .NET Core SDK 6.0.404' inputs: - version: 6.0.401 + version: 6.0.404 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100' @@ -51,9 +51,9 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.401' + displayName: 'Use .NET Core SDK 6.0.404' inputs: - version: 6.0.401 + version: 6.0.404 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a3bbc33418..c61b40de6c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,9 +30,9 @@ jobs: vmImage: 'ubuntu-20.04' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.401' + displayName: 'Use .NET Core SDK 6.0.404' inputs: - version: 6.0.401 + version: 6.0.404 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0' @@ -67,9 +67,9 @@ jobs: vmImage: 'macos-12' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.401' + displayName: 'Use .NET Core SDK 6.0.404' inputs: - version: 6.0.401 + version: 6.0.404 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100' @@ -138,9 +138,9 @@ jobs: SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.401' + displayName: 'Use .NET Core SDK 6.0.404' inputs: - version: 6.0.401 + version: 6.0.404 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100' From 8f745ba6fcfe975d62b8b3c75082d11db6dc4f9a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 4 Jan 2023 09:23:36 +0100 Subject: [PATCH 63/95] fix: Address Review --- .../ColorPalettes/FlatColorPalette.cs | 6 ++---- .../ColorPalettes/FlatHalfColorPalette.cs | 6 ++---- .../ColorPalettes/MaterialColorPalette.cs | 6 ++---- .../ColorPalettes/MaterialHalfColorPalette.cs | 6 ++---- src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs | 2 +- src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs | 2 +- 6 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs index 0188e61bec..aaf272c6d2 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs @@ -289,10 +289,8 @@ namespace Avalonia.Controls }; // See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png -#pragma warning disable CA2211 // Non-constant fields should not be visible - protected static Color[,]? _colorChart = null; -#pragma warning restore CA2211 // Non-constant fields should not be visible - protected static readonly object _colorChartMutex = new(); + private static Color[,]? _colorChart = null; + private static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs index 1bffd66994..4a30fb6cb4 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs @@ -10,10 +10,8 @@ namespace Avalonia.Controls /// public class FlatHalfColorPalette : IColorPalette { -#pragma warning disable CA2211 // Non-constant fields should not be visible - protected static Color[,]? _colorChart = null; -#pragma warning restore CA2211 // Non-constant fields should not be visible - protected static readonly object _colorChartMutex = new(); + private static Color[,]? _colorChart = null; + private static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs index a9024bc18c..d4b904163c 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs @@ -344,10 +344,8 @@ namespace Avalonia.Controls // See: https://material.io/design/color/the-color-system.html#tools-for-picking-colors // This is a reduced palette for uniformity -#pragma warning disable CA2211 // Non-constant fields should not be visible - protected static Color[,]? _colorChart = null; -#pragma warning restore CA2211 // Non-constant fields should not be visible - protected static readonly object _colorChartMutex = new(); + private static Color[,]? _colorChart = null; + private static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs index 371c94504b..4432642675 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs @@ -10,10 +10,8 @@ namespace Avalonia.Controls /// public class MaterialHalfColorPalette : IColorPalette { -#pragma warning disable CA2211 // Non-constant fields should not be visible - protected static Color[,]? _colorChart = null; -#pragma warning restore CA2211 // Non-constant fields should not be visible - protected static readonly object _colorChartMutex = new(); + private static Color[,]? _colorChart = null; + private static readonly object _colorChartMutex = new(); /// /// Initializes all color chart colors. diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs index 8988be9b08..39c1e4c118 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public readonly static StyledProperty IsThreeStateProperty = + public static readonly StyledProperty IsThreeStateProperty = CheckBox.IsThreeStateProperty.AddOwner(); /// diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs index 99616e4cdf..343b628fb9 100644 --- a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs +++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs @@ -8,6 +8,6 @@ /// /// Provides access to the internal for use in DevTools. /// - public readonly static AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; + public static readonly AvaloniaProperty ToolTipProperty = ToolTip.ToolTipProperty; } } From 3eebc5498c6b91267a421528f4d5e0103191d88f Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 5 Jan 2023 16:10:44 +1100 Subject: [PATCH 64/95] remove MSBuild.Sdk.Extras and AggregatePackage.NuGet.Sdk --- global.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/global.json b/global.json index a3e251852f..e4a0cc20c2 100644 --- a/global.json +++ b/global.json @@ -4,8 +4,6 @@ "rollForward": "latestFeature" }, "msbuild-sdks": { - "Microsoft.Build.Traversal": "1.0.43", - "MSBuild.Sdk.Extras": "3.0.22", - "AggregatePackage.NuGet.Sdk" : "0.1.12" + "Microsoft.Build.Traversal": "1.0.43" } } From 91a7a6bad121e64159ffa8dc3d1dfc7b6457ea44 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 5 Jan 2023 21:24:19 +1100 Subject: [PATCH 65/95] remove _notificationManager it is not assigned or used --- samples/ControlCatalog/ViewModels/MainWindowViewModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 3bb4202166..47c6f70714 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -12,8 +12,6 @@ namespace ControlCatalog.ViewModels { class MainWindowViewModel : ViewModelBase { - private IManagedNotificationManager _notificationManager; - private bool _isMenuItemChecked = true; private WindowState _windowState; private WindowState[] _windowStates = Array.Empty(); From f84cdbeda946113ff74a211fa23cf82f6f4feeac Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 5 Jan 2023 21:37:03 +1100 Subject: [PATCH 66/95] respect success in LoadGroupSre --- .../AvaloniaXamlIlRuntimeCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index 2bb91e8a32..e3d154ffdd 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -179,7 +179,7 @@ namespace Avalonia.Markup.Xaml.XamlIl } finally { - if( _sreCanSave) + if(!success && _sreCanSave) DumpRuntimeCompilationResults(); } } From 5fef8af151e7673264f20a41e96c97a2a4846a68 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Thu, 5 Jan 2023 18:31:02 +0300 Subject: [PATCH 67/95] [Text] [Rename] ShapeableTextCharacters to UnshapedTextRun and ShapedTextCharacters to ShapedTextRun --- .../TextFormatting/InterWordJustification.cs | 2 +- ...apedTextCharacters.cs => ShapedTextRun.cs} | 12 +++--- .../Media/TextFormatting/TextCharacters.cs | 18 ++++----- .../TextFormatting/TextEllipsisHelper.cs | 2 +- .../Media/TextFormatting/TextFormatterImpl.cs | 28 ++++++------- .../TextLeadingPrefixCharacterEllipsis.cs | 4 +- .../Media/TextFormatting/TextLineImpl.cs | 40 +++++++++---------- ...leTextCharacters.cs => UnshapedTextRun.cs} | 16 ++++---- .../TextFormatting/TextFormatterTests.cs | 4 +- .../Media/TextFormatting/TextLayoutTests.cs | 28 ++++++------- .../Media/TextFormatting/TextLineTests.cs | 30 +++++++------- 11 files changed, 92 insertions(+), 92 deletions(-) rename src/Avalonia.Base/Media/TextFormatting/{ShapedTextCharacters.cs => ShapedTextRun.cs} (92%) rename src/Avalonia.Base/Media/TextFormatting/{ShapeableTextCharacters.cs => UnshapedTextRun.cs} (61%) diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs index 3c3a46c209..21e8ce089a 100644 --- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs +++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs @@ -91,7 +91,7 @@ namespace Avalonia.Media.TextFormatting continue; } - if (textRun is ShapedTextCharacters shapedText) + if (textRun is ShapedTextRun shapedText) { var glyphRun = shapedText.GlyphRun; var shapedBuffer = shapedText.ShapedBuffer; diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs similarity index 92% rename from src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index 3035eb7b18..3149bc2cda 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -6,11 +6,11 @@ namespace Avalonia.Media.TextFormatting /// /// A text run that holds shaped characters. /// - public sealed class ShapedTextCharacters : DrawableTextRun + public sealed class ShapedTextRun : DrawableTextRun { private GlyphRun? _glyphRun; - public ShapedTextCharacters(ShapedBuffer shapedBuffer, TextRunProperties properties) + public ShapedTextRun(ShapedBuffer shapedBuffer, TextRunProperties properties) { ShapedBuffer = shapedBuffer; CharacterBufferReference = shapedBuffer.CharacterBufferRange.CharacterBufferReference; @@ -155,7 +155,7 @@ namespace Avalonia.Media.TextFormatting return length > 0; } - internal SplitResult Split(int length) + internal SplitResult Split(int length) { if (IsReversed) { @@ -171,7 +171,7 @@ namespace Avalonia.Media.TextFormatting var splitBuffer = ShapedBuffer.Split(length); - var first = new ShapedTextCharacters(splitBuffer.First, Properties); + var first = new ShapedTextRun(splitBuffer.First, Properties); #if DEBUG @@ -182,9 +182,9 @@ namespace Avalonia.Media.TextFormatting #endif - var second = new ShapedTextCharacters(splitBuffer.Second!, Properties); + var second = new ShapedTextRun(splitBuffer.Second!, Properties); - return new SplitResult(first, second); + return new SplitResult(first, second); } internal GlyphRun CreateGlyphRun() diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index 0be753bd04..db035b8750 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -91,12 +91,12 @@ namespace Avalonia.Media.TextFormatting public override TextRunProperties Properties { get; } /// - /// Gets a list of . + /// Gets a list of . /// /// The shapeable text characters. - internal IReadOnlyList GetShapeableCharacters(CharacterBufferRange characterBufferRange, sbyte biDiLevel, ref TextRunProperties? previousProperties) + internal IReadOnlyList GetShapeableCharacters(CharacterBufferRange characterBufferRange, sbyte biDiLevel, ref TextRunProperties? previousProperties) { - var shapeableCharacters = new List(2); + var shapeableCharacters = new List(2); while (characterBufferRange.Length > 0) { @@ -120,7 +120,7 @@ namespace Avalonia.Media.TextFormatting /// The bidi level of the run. /// /// A list of shapeable text runs. - private static ShapeableTextCharacters CreateShapeableRun(CharacterBufferRange characterBufferRange, + private static UnshapedTextRun CreateShapeableRun(CharacterBufferRange characterBufferRange, TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties) { var defaultTypeface = defaultProperties.Typeface; @@ -133,12 +133,12 @@ namespace Avalonia.Media.TextFormatting { if (TryGetShapeableLength(characterBufferRange, previousTypeface.Value, null, out var fallbackCount, out _)) { - return new ShapeableTextCharacters(characterBufferRange.CharacterBufferReference, fallbackCount, + return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, fallbackCount, defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); } } - return new ShapeableTextCharacters(characterBufferRange.CharacterBufferReference, count, defaultProperties.WithTypeface(currentTypeface), + return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -146,7 +146,7 @@ namespace Avalonia.Media.TextFormatting { if (TryGetShapeableLength(characterBufferRange, previousTypeface.Value, defaultTypeface, out count, out _)) { - return new ShapeableTextCharacters(characterBufferRange.CharacterBufferReference, count, + return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); } } @@ -176,7 +176,7 @@ namespace Avalonia.Media.TextFormatting if (matchFound && TryGetShapeableLength(characterBufferRange, currentTypeface, defaultTypeface, out count, out _)) { //Fallback found - return new ShapeableTextCharacters(characterBufferRange.CharacterBufferReference, count, defaultProperties.WithTypeface(currentTypeface), + return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -199,7 +199,7 @@ namespace Avalonia.Media.TextFormatting count += grapheme.Text.Length; } - return new ShapeableTextCharacters(characterBufferRange.CharacterBufferReference, count, defaultProperties, biDiLevel); + return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties, biDiLevel); } /// diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs index a1b8985b43..086ea85d97 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs @@ -31,7 +31,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { currentWidth += shapedRun.Size.Width; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 93eb4811b9..ef2abdfea0 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -124,7 +124,7 @@ namespace Avalonia.Media.TextFormatting var second = new List(secondCount); - if (currentRun is ShapedTextCharacters shapedTextCharacters) + if (currentRun is ShapedTextRun shapedTextCharacters) { var split = shapedTextCharacters.Split(length - currentLength); @@ -206,16 +206,16 @@ namespace Avalonia.Media.TextFormatting break; } - case ShapeableTextCharacters shapeableRun: + case UnshapedTextRun shapeableRun: { - var groupedRuns = new List(2) { shapeableRun }; + var groupedRuns = new List(2) { shapeableRun }; var characterBufferReference = currentRun.CharacterBufferReference; var length = currentRun.Length; var offsetToFirstCharacter = characterBufferReference.OffsetToFirstChar; while (index + 1 < processedRuns.Count) { - if (processedRuns[index + 1] is not ShapeableTextCharacters nextRun) + if (processedRuns[index + 1] is not UnshapedTextRun nextRun) { break; } @@ -258,10 +258,10 @@ namespace Avalonia.Media.TextFormatting return drawableTextRuns; } - private static IReadOnlyList ShapeTogether( - IReadOnlyList textRuns, CharacterBufferReference text, int length, TextShaperOptions options) + private static IReadOnlyList ShapeTogether( + IReadOnlyList textRuns, CharacterBufferReference text, int length, TextShaperOptions options) { - var shapedRuns = new List(textRuns.Count); + var shapedRuns = new List(textRuns.Count); var shapedBuffer = TextShaper.Current.ShapeText(text, length, options); @@ -271,7 +271,7 @@ namespace Avalonia.Media.TextFormatting var splitResult = shapedBuffer.Split(currentRun.Length); - shapedRuns.Add(new ShapedTextCharacters(splitResult.First, currentRun.Properties)); + shapedRuns.Add(new ShapedTextRun(splitResult.First, currentRun.Properties)); shapedBuffer = splitResult.Second!; } @@ -280,9 +280,9 @@ namespace Avalonia.Media.TextFormatting } /// - /// Coalesces ranges of the same bidi level to form + /// Coalesces ranges of the same bidi level to form /// - /// The text characters to form from. + /// The text characters to form from. /// The bidi levels. /// private static IEnumerable> CoalesceLevels(IReadOnlyList textCharacters, ArraySlice levels) @@ -474,7 +474,7 @@ namespace Avalonia.Media.TextFormatting { switch (currentRun) { - case ShapedTextCharacters shapedTextCharacters: + case ShapedTextRun shapedTextCharacters: { if(shapedTextCharacters.ShapedBuffer.Length > 0) { @@ -538,7 +538,7 @@ namespace Avalonia.Media.TextFormatting var shapedBuffer = new ShapedBuffer(characterBufferRange, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, (sbyte)flowDirection); - var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; + var textRuns = new List { new ShapedTextRun(shapedBuffer, properties) }; return new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection).FinalizeLine(); } @@ -744,7 +744,7 @@ namespace Avalonia.Media.TextFormatting /// /// The shaped symbol. /// - internal static ShapedTextCharacters CreateSymbol(TextRun textRun, FlowDirection flowDirection) + internal static ShapedTextRun CreateSymbol(TextRun textRun, FlowDirection flowDirection) { var textShaper = TextShaper.Current; @@ -760,7 +760,7 @@ namespace Avalonia.Media.TextFormatting var shapedBuffer = textShaper.ShapeText(characterBuffer, textRun.Length, shaperOptions); - return new ShapedTextCharacters(shapedBuffer, textRun.Properties); + return new ShapedTextRun(shapedBuffer, textRun.Properties); } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index 2752af8f0c..7b80d5ce40 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -65,7 +65,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { currentWidth += currentRun.Size.Width; @@ -118,7 +118,7 @@ namespace Avalonia.Media.TextFormatting switch (run) { - case ShapedTextCharacters endShapedRun: + case ShapedTextRun endShapedRun: { if (endShapedRun.TryMeasureCharactersBackwards(availableSuffixWidth, out var suffixCount, out var suffixWidth)) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index d893468052..5fb1171221 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -192,14 +192,14 @@ namespace Avalonia.Media.TextFormatting { var currentRun = _textRuns[i]; - if (currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight) + if (currentRun is ShapedTextRun shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight) { var rightToLeftIndex = i; currentPosition += currentRun.Length; while (rightToLeftIndex + 1 <= _textRuns.Count - 1) { - var nextShaped = _textRuns[++rightToLeftIndex] as ShapedTextCharacters; + var nextShaped = _textRuns[++rightToLeftIndex] as ShapedTextRun; if (nextShaped == null || nextShaped.ShapedBuffer.IsLeftToRight) { @@ -255,7 +255,7 @@ namespace Avalonia.Media.TextFormatting switch (run) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); @@ -303,7 +303,7 @@ namespace Avalonia.Media.TextFormatting { var currentRun = _textRuns[index]; - if (currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight) + if (currentRun is ShapedTextRun shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight) { var i = index; @@ -313,7 +313,7 @@ namespace Avalonia.Media.TextFormatting { var nextRun = _textRuns[i + 1]; - if (nextRun is ShapedTextCharacters nextShapedRun && !nextShapedRun.ShapedBuffer.IsLeftToRight) + if (nextRun is ShapedTextRun nextShapedRun && !nextShapedRun.ShapedBuffer.IsLeftToRight) { i++; @@ -407,7 +407,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedTextCharacters: + case ShapedTextRun shapedTextCharacters: { currentGlyphRun = shapedTextCharacters.GlyphRun; @@ -476,7 +476,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { nextCharacterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(characterHit); break; @@ -550,7 +550,7 @@ namespace Avalonia.Media.TextFormatting double combinedWidth; - if (currentRun is ShapedTextCharacters currentShapedRun) + if (currentRun is ShapedTextRun currentShapedRun) { var firstCluster = currentShapedRun.GlyphRun.Metrics.FirstCluster; @@ -592,7 +592,7 @@ namespace Avalonia.Media.TextFormatting var rightToLeftIndex = index; var rightToLeftWidth = currentShapedRun.Size.Width; - while (rightToLeftIndex + 1 <= _textRuns.Count - 1 && _textRuns[rightToLeftIndex + 1] is ShapedTextCharacters nextShapedRun) + while (rightToLeftIndex + 1 <= _textRuns.Count - 1 && _textRuns[rightToLeftIndex + 1] is ShapedTextRun nextShapedRun) { if (nextShapedRun == null || nextShapedRun.ShapedBuffer.IsLeftToRight) { @@ -624,12 +624,12 @@ namespace Avalonia.Media.TextFormatting for (int i = rightToLeftIndex - 1; i >= index; i--) { - if (TextRuns[i] is not ShapedTextCharacters) + if (TextRuns[i] is not ShapedTextRun) { continue; } - currentShapedRun = (ShapedTextCharacters)TextRuns[i]; + currentShapedRun = (ShapedTextRun)TextRuns[i]; currentRunBounds = GetRightToLeftTextRunBounds(currentShapedRun, startX, firstTextSourceIndex, characterIndex, currentPosition, remainingLength); @@ -769,7 +769,7 @@ namespace Avalonia.Media.TextFormatting var characterLength = 0; var endX = startX; - if (currentRun is ShapedTextCharacters currentShapedRun) + if (currentRun is ShapedTextRun currentShapedRun) { var offset = Math.Max(0, firstTextSourceIndex - currentPosition); @@ -883,7 +883,7 @@ namespace Avalonia.Media.TextFormatting return result; } - private TextRunBounds GetRightToLeftTextRunBounds(ShapedTextCharacters currentRun, double endX, int firstTextSourceIndex, int characterIndex, int currentPosition, int remainingLength) + private TextRunBounds GetRightToLeftTextRunBounds(ShapedTextRun currentRun, double endX, int firstTextSourceIndex, int characterIndex, int currentPosition, int remainingLength) { var startX = endX; @@ -945,7 +945,7 @@ namespace Avalonia.Media.TextFormatting private static sbyte GetRunBidiLevel(DrawableTextRun run, FlowDirection flowDirection) { - if (run is ShapedTextCharacters shapedTextCharacters) + if (run is ShapedTextRun shapedTextCharacters) { return shapedTextCharacters.BidiLevel; } @@ -1027,7 +1027,7 @@ namespace Avalonia.Media.TextFormatting { if (current.Level >= minLevelToReverse && current.Level % 2 != 0) { - if (current.Run is ShapedTextCharacters { IsReversed: false } shapedTextCharacters) + if (current.Run is ShapedTextRun { IsReversed: false } shapedTextCharacters) { shapedTextCharacters.Reverse(); } @@ -1145,7 +1145,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); @@ -1230,7 +1230,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); @@ -1294,7 +1294,7 @@ namespace Avalonia.Media.TextFormatting switch (currentRun) { - case ShapedTextCharacters shapedRun: + case ShapedTextRun shapedRun: { var firstCluster = shapedRun.GlyphRun.Metrics.FirstCluster; @@ -1303,7 +1303,7 @@ namespace Avalonia.Media.TextFormatting break; } - if (previousRun is ShapedTextCharacters previousShaped && !previousShaped.ShapedBuffer.IsLeftToRight) + if (previousRun is ShapedTextRun previousShaped && !previousShaped.ShapedBuffer.IsLeftToRight) { if (shapedRun.ShapedBuffer.IsLeftToRight) { @@ -1394,7 +1394,7 @@ namespace Avalonia.Media.TextFormatting { switch (_textRuns[index]) { - case ShapedTextCharacters textRun: + case ShapedTextRun textRun: { var textMetrics = new TextMetrics(textRun.Properties.Typeface.GlyphTypeface, textRun.Properties.FontRenderingEmSize); diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/UnshapedTextRun.cs similarity index 61% rename from src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/UnshapedTextRun.cs index 0e8d6e3e4a..817086db88 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/UnshapedTextRun.cs @@ -5,9 +5,9 @@ namespace Avalonia.Media.TextFormatting /// /// A group of characters that can be shaped. /// - public sealed class ShapeableTextCharacters : TextRun + public sealed class UnshapedTextRun : TextRun { - public ShapeableTextCharacters(CharacterBufferReference characterBufferReference, int length, + public UnshapedTextRun(CharacterBufferReference characterBufferReference, int length, TextRunProperties properties, sbyte biDiLevel) { CharacterBufferReference = characterBufferReference; @@ -24,30 +24,30 @@ namespace Avalonia.Media.TextFormatting public sbyte BidiLevel { get; } - public bool CanShapeTogether(ShapeableTextCharacters shapeableTextCharacters) + public bool CanShapeTogether(UnshapedTextRun unshapedTextRun) { - if (!CharacterBufferReference.Equals(shapeableTextCharacters.CharacterBufferReference)) + if (!CharacterBufferReference.Equals(unshapedTextRun.CharacterBufferReference)) { return false; } - if (BidiLevel != shapeableTextCharacters.BidiLevel) + if (BidiLevel != unshapedTextRun.BidiLevel) { return false; } if (!MathUtilities.AreClose(Properties.FontRenderingEmSize, - shapeableTextCharacters.Properties.FontRenderingEmSize)) + unshapedTextRun.Properties.FontRenderingEmSize)) { return false; } - if (Properties.Typeface != shapeableTextCharacters.Properties.Typeface) + if (Properties.Typeface != unshapedTextRun.Properties.Typeface) { return false; } - if (Properties.BaselineAlignment != shapeableTextCharacters.Properties.BaselineAlignment) + if (Properties.BaselineAlignment != unshapedTextRun.Properties.BaselineAlignment) { return false; } diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 33d4fba5f1..7fc27b01f4 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -520,7 +520,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var expectedTextLine = formatter.FormatLine(new SingleBufferTextSource(text, defaultProperties), 0, double.PositiveInfinity, paragraphProperties); - var expectedRuns = expectedTextLine.TextRuns.Cast().ToList(); + var expectedRuns = expectedTextLine.TextRuns.Cast().ToList(); var expectedGlyphs = expectedRuns.SelectMany(x => x.GlyphRun.GlyphIndices).ToList(); @@ -539,7 +539,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); - var shapedRuns = textLine.TextRuns.Cast().ToList(); + var shapedRuns = textLine.TextRuns.Cast().ToList(); var actualGlyphs = shapedRuns.SelectMany(x => x.GlyphRun.GlyphIndices).ToList(); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index a407b38eb1..2790bd6096 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -141,7 +141,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting black, textWrapping: TextWrapping.Wrap); - var expectedGlyphs = expected.TextLines.Select(x => string.Join('|', x.TextRuns.Cast() + var expectedGlyphs = expected.TextLines.Select(x => string.Join('|', x.TextRuns.Cast() .SelectMany(x => x.ShapedBuffer.GlyphIndices))).ToList(); var outer = new GraphemeEnumerator(new CharacterBufferRange(text)); @@ -174,7 +174,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting textWrapping: TextWrapping.Wrap, textStyleOverrides: spans); - var actualGlyphs = actual.TextLines.Select(x => string.Join('|', x.TextRuns.Cast() + var actualGlyphs = actual.TextLines.Select(x => string.Join('|', x.TextRuns.Cast() .SelectMany(x => x.ShapedBuffer.GlyphIndices))).ToList(); Assert.Equal(expectedGlyphs.Count, actualGlyphs.Count); @@ -447,7 +447,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting 12.0f, Brushes.Black.ToImmutable()); - var shapedRun = (ShapedTextCharacters)layout.TextLines[0].TextRuns[0]; + var shapedRun = (ShapedTextRun)layout.TextLines[0].TextRuns[0]; var glyphRun = shapedRun.GlyphRun; @@ -481,7 +481,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var textRun in textLine.TextRuns) { - var shapedRun = (ShapedTextCharacters)textRun; + var shapedRun = (ShapedTextRun)textRun; var glyphClusters = shapedRun.ShapedBuffer.GlyphClusters; @@ -514,13 +514,13 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, layout.TextLines[0].TextRuns.Count); - Assert.Equal(expectedLength, ((ShapedTextCharacters)layout.TextLines[0].TextRuns[0]).GlyphRun.GlyphClusters.Count); + Assert.Equal(expectedLength, ((ShapedTextRun)layout.TextLines[0].TextRuns[0]).GlyphRun.GlyphClusters.Count); - Assert.Equal(5, ((ShapedTextCharacters)layout.TextLines[0].TextRuns[0]).ShapedBuffer.GlyphClusters[5]); + Assert.Equal(5, ((ShapedTextRun)layout.TextLines[0].TextRuns[0]).ShapedBuffer.GlyphClusters[5]); if (expectedLength == 7) { - Assert.Equal(5, ((ShapedTextCharacters)layout.TextLines[0].TextRuns[0]).ShapedBuffer.GlyphClusters[6]); + Assert.Equal(5, ((ShapedTextRun)layout.TextLines[0].TextRuns[0]).ShapedBuffer.GlyphClusters[6]); } } } @@ -555,7 +555,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = layout.TextLines[0]; - var textRun = (ShapedTextCharacters)textLine.TextRuns[0]; + var textRun = (ShapedTextRun)textLine.TextRuns[0]; Assert.Equal(7, textRun.Length); @@ -775,7 +775,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(textLine.WidthIncludingTrailingWhitespace, rect.Width); } - var rects = layout.TextLines.SelectMany(x => x.TextRuns.Cast()) + var rects = layout.TextLines.SelectMany(x => x.TextRuns.Cast()) .SelectMany(x => x.ShapedBuffer.GlyphAdvances).ToArray(); for (var i = 0; i < SingleLineText.Length; i++) @@ -814,7 +814,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { Assert.True(textLine.Width <= maxWidth); - var actual = new string(textLine.TextRuns.Cast() + var actual = new string(textLine.TextRuns.Cast() .OrderBy(x => x.CharacterBufferReference.OffsetToFirstChar) .SelectMany(x => new CharacterBufferRange(x.CharacterBufferReference, x.Length)).ToArray()); @@ -855,7 +855,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Brushes.Black, flowDirection: FlowDirection.RightToLeft); - var firstRun = layout.TextLines[0].TextRuns[0] as ShapedTextCharacters; + var firstRun = layout.TextLines[0].TextRuns[0] as ShapedTextRun; var hit = layout.HitTestPoint(new Point()); @@ -881,7 +881,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting currentX += advance; } - var secondRun = layout.TextLines[0].TextRuns[1] as ShapedTextCharacters; + var secondRun = layout.TextLines[0].TextRuns[1] as ShapedTextRun; hit = layout.HitTestPoint(new Point(firstRun.Size.Width, 0)); @@ -928,7 +928,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = layout.TextLines[0]; - var firstRun = (ShapedTextCharacters)textLine.TextRuns[0]; + var firstRun = (ShapedTextRun)textLine.TextRuns[0]; var firstCluster = firstRun.ShapedBuffer.GlyphClusters[0]; @@ -987,7 +987,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = layout.TextLines[0]; - var shapedRuns = textLine.TextRuns.Cast().ToList(); + var shapedRuns = textLine.TextRuns.Cast().ToList(); var clusters = shapedRuns.SelectMany(x => x.ShapedBuffer.GlyphClusters).ToList(); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index d6257a0de8..ac2467407b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -92,7 +92,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var textRun in textLine.TextRuns.OrderBy(x => x.CharacterBufferReference.OffsetToFirstChar)) { - var shapedRun = (ShapedTextCharacters)textRun; + var shapedRun = (ShapedTextRun)textRun; clusters.AddRange(shapedRun.IsReversed ? shapedRun.ShapedBuffer.GlyphClusters.Reverse() : @@ -139,7 +139,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var textRun in textLine.TextRuns.OrderBy(x => x.CharacterBufferReference.OffsetToFirstChar)) { - var shapedRun = (ShapedTextCharacters)textRun; + var shapedRun = (ShapedTextRun)textRun; clusters.AddRange(shapedRun.IsReversed ? shapedRun.ShapedBuffer.GlyphClusters.Reverse() : @@ -246,7 +246,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting formatter.FormatLine(textSource, 0, double.PositiveInfinity, new GenericTextParagraphProperties(defaultProperties)); - var clusters = textLine.TextRuns.Cast().SelectMany(x => x.ShapedBuffer.GlyphClusters) + var clusters = textLine.TextRuns.Cast().SelectMany(x => x.ShapedBuffer.GlyphClusters) .ToArray(); var previousCharacterHit = new CharacterHit(text.Length); @@ -308,7 +308,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var run in textLine.TextRuns) { - var textRun = (ShapedTextCharacters)run; + var textRun = (ShapedTextRun)run; var glyphRun = textRun.GlyphRun; @@ -634,7 +634,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting formatter.FormatLine(textSource, 0, double.PositiveInfinity, new GenericTextParagraphProperties(defaultProperties)); - var textRuns = textLine.TextRuns.Cast().ToList(); + var textRuns = textLine.TextRuns.Cast().ToList(); var lineWidth = textLine.WidthIncludingTrailingWhitespace; @@ -732,14 +732,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting private static bool IsRightToLeft(TextLine textLine) { - return textLine.TextRuns.Cast().Any(x => !x.ShapedBuffer.IsLeftToRight); + return textLine.TextRuns.Cast().Any(x => !x.ShapedBuffer.IsLeftToRight); } private static List BuildGlyphClusters(TextLine textLine) { var glyphClusters = new List(); - var shapedTextRuns = textLine.TextRuns.Cast().ToList(); + var shapedTextRuns = textLine.TextRuns.Cast().ToList(); var lastCluster = -1; @@ -774,7 +774,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var lastCluster = -1; - var shapedTextRuns = textLine.TextRuns.Cast().ToList(); + var shapedTextRuns = textLine.TextRuns.Cast().ToList(); foreach (var textRun in shapedTextRuns) { @@ -820,16 +820,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var text = "0123"; var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); - var firstRun = new ShapedTextCharacters(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties); + var firstRun = new ShapedTextRun(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties); var textRuns = new List { new CustomDrawableRun(), firstRun, new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties), + new ShapedTextRun(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties), new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties) + new ShapedTextRun(TextShaper.Current.ShapeText(text, shaperOption), defaultProperties) }; var textSource = new FixedRunsTextSource(textRuns); @@ -885,14 +885,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textBounds = textLine.GetTextBounds(0, 3); - var firstRun = textLine.TextRuns[0] as ShapedTextCharacters; + var firstRun = textLine.TextRuns[0] as ShapedTextRun; Assert.Equal(1, textBounds.Count); Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); textBounds = textLine.GetTextBounds(3, 4); - var secondRun = textLine.TextRuns[1] as ShapedTextCharacters; + var secondRun = textLine.TextRuns[1] as ShapedTextRun; Assert.Equal(1, textBounds.Count); Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); @@ -932,14 +932,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textBounds = textLine.GetTextBounds(0, 4); - var secondRun = textLine.TextRuns[1] as ShapedTextCharacters; + var secondRun = textLine.TextRuns[1] as ShapedTextRun; Assert.Equal(1, textBounds.Count); Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); textBounds = textLine.GetTextBounds(4, 3); - var firstRun = textLine.TextRuns[0] as ShapedTextCharacters; + var firstRun = textLine.TextRuns[0] as ShapedTextRun; Assert.Equal(1, textBounds.Count); From dea807609fca0f355744291e22bba56acab6df9a Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 5 Jan 2023 18:13:53 +0200 Subject: [PATCH 68/95] Move UpdateLogicalTree Call to CurrentContent Change --- src/Avalonia.Controls/TransitioningContentControl.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs index 545032befb..2e0a36ad19 100644 --- a/src/Avalonia.Controls/TransitioningContentControl.cs +++ b/src/Avalonia.Controls/TransitioningContentControl.cs @@ -69,6 +69,10 @@ public class TransitioningContentControl : ContentControl { Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content)); } + else if (change.Property == CurrentContentProperty) + { + UpdateLogicalTree(change.OldValue, change.NewValue); + } } protected override void ContentChanged(AvaloniaPropertyChangedEventArgs e) @@ -94,8 +98,6 @@ public class TransitioningContentControl : ContentControl if (PageTransition != null) await PageTransition.Start(this, null, true, localToken); - UpdateLogicalTree(CurrentContent, content); - if (localToken.IsCancellationRequested) { return; From 490c9b1ffe6a69037dee03b723bc1ba80b0ba0b2 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Fri, 6 Jan 2023 00:58:20 +0300 Subject: [PATCH 69/95] [Text] fix tab width. It's too long --- samples/ControlCatalog/Pages/TextBoxPage.xaml | 2 +- .../Media/TextFormatting/TextParagraphProperties.cs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 6a4d9b7d0e..7408399873 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -38,7 +38,7 @@ UseFloatingWatermark="True" PasswordChar="*" Text="Password" /> - + /// Gets the default incremental tab width. /// - public virtual double DefaultIncrementalTab - { - get { return 4 * DefaultTextRunProperties.FontRenderingEmSize; } - } + public virtual double DefaultIncrementalTab => 0; /// /// Gets the letter spacing. From fb37ab1e7725deef8b54a69d21a17f92ba361c8d Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 10:46:29 +1100 Subject: [PATCH 70/95] leverage GetRequiredService --- samples/ControlCatalog.Desktop/Program.cs | 2 +- samples/ControlCatalog/Pages/TabControlPage.xaml.cs | 2 +- .../ViewModels/CursorPageViewModel.cs | 2 +- .../TransitioningContentControlPageViewModel.cs | 2 +- src/Avalonia.Base/Input/Cursor.cs | 3 +-- src/Avalonia.Base/Media/FontManager.cs | 5 +---- .../Media/TextFormatting/TextShaper.cs | 5 +---- src/Avalonia.Base/Rendering/RenderLoop.cs | 3 +-- src/Avalonia.Base/Threading/DispatcherTimer.cs | 8 +------- src/Avalonia.Controls/Platform/PlatformManager.cs | 13 +++---------- src/Avalonia.DesignerSupport/DesignWindowLoader.cs | 5 +---- .../Internal/ManagedFileChooserViewModel.cs | 2 +- src/Avalonia.OpenGL/Egl/EglInterface.cs | 2 +- src/Avalonia.X11/X11Framebuffer.cs | 2 +- src/Avalonia.X11/X11Platform.cs | 2 +- .../Converters/BitmapTypeConverter.cs | 2 +- .../Converters/IconTypeConverter.cs | 2 +- src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs | 2 +- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 6 +++--- .../Media/DWriteResourceFontLoader.cs | 2 +- .../Avalonia.Direct2D1/Media/DrawingContextImpl.cs | 2 +- .../Wpf/Direct2DImageSurface.cs | 2 +- .../Media/Fonts/FontFamilyLoaderTests.cs | 2 +- .../Avalonia.Controls.UnitTests/ContextMenuTests.cs | 2 +- tests/Avalonia.Controls.UnitTests/FlyoutTests.cs | 2 +- .../MaskedTextBoxTests.cs | 2 +- .../Primitives/PopupTests.cs | 2 +- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 4 ++-- tests/Avalonia.Controls.UnitTests/TreeViewTests.cs | 9 ++++----- .../AutoSuspendHelperTest.cs | 6 +++--- tests/Avalonia.RenderTests/TestBase.cs | 2 +- tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs | 7 +------ 32 files changed, 42 insertions(+), 72 deletions(-) diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 7b8b27fff7..4d28f15e2c 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -23,7 +23,7 @@ namespace ControlCatalog private static void ConfigureAssetAssembly(AppBuilder builder) { AvaloniaLocator.CurrentMutable - .GetService() + .GetRequiredService() .SetDefaultAssembly(typeof(App).Assembly); } } diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs index 413b6e1c75..f3f4bf6e93 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -51,7 +51,7 @@ namespace ControlCatalog.Pages private static IBitmap LoadBitmap(string uri) { - var assets = AvaloniaLocator.Current!.GetService()!; + var assets = AvaloniaLocator.Current.GetRequiredService(); return new Bitmap(assets.Open(new Uri(uri))); } } diff --git a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs index 8a3f0ba947..12ca6af4f5 100644 --- a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels .Select(x => new StandardCursorModel(x)) .ToList(); - var loader = AvaloniaLocator.Current!.GetService()!; + var loader = AvaloniaLocator.Current.GetRequiredService()!; var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png")); var bitmap = new Bitmap(s); CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16)); diff --git a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs index 93857fd899..f4e0bc8912 100644 --- a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs @@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels { public TransitioningContentControlPageViewModel() { - var assetLoader = AvaloniaLocator.Current?.GetService()!; + var assetLoader = AvaloniaLocator.Current.GetRequiredService()!; var images = new string[] { diff --git a/src/Avalonia.Base/Input/Cursor.cs b/src/Avalonia.Base/Input/Cursor.cs index 8e79206f93..c555087879 100644 --- a/src/Avalonia.Base/Input/Cursor.cs +++ b/src/Avalonia.Base/Input/Cursor.cs @@ -71,8 +71,7 @@ namespace Avalonia.Input private static ICursorFactory GetCursorFactory() { - return AvaloniaLocator.Current.GetService() ?? - throw new Exception("Could not create Cursor: ICursorFactory not registered."); + return AvaloniaLocator.Current.GetRequiredService(); } } } diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index d92d003c2a..e82d5b7ba5 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -47,10 +47,7 @@ namespace Avalonia.Media return current; } - var fontManagerImpl = AvaloniaLocator.Current.GetService(); - - if (fontManagerImpl == null) - throw new InvalidOperationException("No font manager implementation was registered."); + var fontManagerImpl = AvaloniaLocator.Current.GetRequiredService(); current = new FontManager(fontManagerImpl); diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs index 4aacec7c48..c161b08d20 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs @@ -29,10 +29,7 @@ namespace Avalonia.Media.TextFormatting return current; } - var textShaperImpl = AvaloniaLocator.Current.GetService(); - - if (textShaperImpl == null) - throw new InvalidOperationException("No text shaper implementation was registered."); + var textShaperImpl = AvaloniaLocator.Current.GetRequiredService(); current = new TextShaper(textShaperImpl); diff --git a/src/Avalonia.Base/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs index 5a08bfc6a1..1f58ca3827 100644 --- a/src/Avalonia.Base/Rendering/RenderLoop.cs +++ b/src/Avalonia.Base/Rendering/RenderLoop.cs @@ -50,8 +50,7 @@ namespace Avalonia.Rendering { get { - return _timer ??= AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Cannot locate IRenderTimer."); + return _timer ??= AvaloniaLocator.Current.GetRequiredService(); } } diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 0c25d89722..8fc91a02d8 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -176,13 +176,7 @@ namespace Avalonia.Threading { if (!IsEnabled) { - var threading = AvaloniaLocator.Current.GetService(); - - if (threading == null) - { - throw new Exception("Could not start timer: IPlatformThreadingInterface is not registered."); - } - + var threading = AvaloniaLocator.Current.GetRequiredService(); _timer = threading.StartTimer(_priority, Interval, InternalTick); } } diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index de7708e869..b01fc28831 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -21,26 +21,19 @@ namespace Avalonia.Controls.Platform } public static ITrayIconImpl? CreateTrayIcon() => - s_designerMode ? null : AvaloniaLocator.Current.GetService()?.CreateTrayIcon(); + s_designerMode ? null : AvaloniaLocator.Current.GetRequiredService().CreateTrayIcon(); public static IWindowImpl CreateWindow() { - var platform = AvaloniaLocator.Current.GetService(); - - if (platform == null) - { - throw new Exception("Could not CreateWindow(): IWindowingPlatform is not registered."); - } + var platform = AvaloniaLocator.Current.GetRequiredService(); return s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow(); } public static IWindowImpl CreateEmbeddableWindow() { - var platform = AvaloniaLocator.Current.GetService(); - if (platform == null) - throw new Exception("Could not CreateEmbeddableWindow(): IWindowingPlatform is not registered."); + var platform = AvaloniaLocator.Current.GetRequiredService(); return platform.CreateEmbeddableWindow(); } } diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index b4cfffcdca..eff190c39e 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -18,12 +18,9 @@ namespace Avalonia.DesignerSupport Control control; using (PlatformManager.DesignerMode()) { - var loader = AvaloniaLocator.Current.GetService(); + var loader = AvaloniaLocator.Current.GetRequiredService(); var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)); - if (loader == null) - throw new XamlLoadException("Runtime XAML loader is not registered"); - Uri baseUri = null; if (assemblyPath != null) { diff --git a/src/Avalonia.Dialogs/Internal/ManagedFileChooserViewModel.cs b/src/Avalonia.Dialogs/Internal/ManagedFileChooserViewModel.cs index 7d0072c6ea..3de79927b2 100644 --- a/src/Avalonia.Dialogs/Internal/ManagedFileChooserViewModel.cs +++ b/src/Avalonia.Dialogs/Internal/ManagedFileChooserViewModel.cs @@ -117,7 +117,7 @@ namespace Avalonia.Dialogs.Internal ?? new ManagedFileChooserSources(); var sub1 = AvaloniaLocator.Current - .GetService() + .GetRequiredService() .Listen(ManagedFileChooserSources.MountedVolumes); var sub2 = Observable.FromEventPattern(ManagedFileChooserSources.MountedVolumes, diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index a913c05996..06aafb4f57 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -35,7 +35,7 @@ namespace Avalonia.OpenGL.Egl static Func Load(string library) { - var dyn = AvaloniaLocator.Current.GetService(); + var dyn = AvaloniaLocator.Current.GetRequiredService(); var lib = dyn.LoadLibrary(library); return (s) => dyn.GetProcAddress(lib, s, true); } diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 94f930e9ec..a9fedff8b5 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -25,7 +25,7 @@ namespace Avalonia.X11 RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * height); + _blob = AvaloniaLocator.Current.GetRequiredService().AllocBlob(RowBytes * height); Address = _blob.Address; } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 9692e0a384..0f9ef72a3f 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -105,7 +105,7 @@ namespace Avalonia.X11 var gl = AvaloniaLocator.Current.GetService(); if (options.UseCompositor) - Compositor = new Compositor(AvaloniaLocator.Current.GetService()!, gl); + Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), gl); else RenderInterface = new(gl); diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs index dcd60f7a79..fc6a8557f3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs @@ -24,7 +24,7 @@ namespace Avalonia.Markup.Xaml.Converters if(uri.IsAbsoluteUri && uri.IsFile) return new Bitmap(uri.LocalPath); - var assets = AvaloniaLocator.Current.GetService(); + var assets = AvaloniaLocator.Current.GetRequiredService(); return new Bitmap(assets.Open(uri, context.GetContextBaseUri())); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs index 24b690b6f1..698f5a9327 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs @@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml.Converters if(uri.IsAbsoluteUri && uri.IsFile) return new WindowIcon(uri.LocalPath); - var assets = AvaloniaLocator.Current.GetService(); + var assets = AvaloniaLocator.Current.GetRequiredService(); return new WindowIcon(assets.Open(uri, context.GetContextBaseUri())); } } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index 5ca7f40d17..f7a86c11ff 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -37,7 +37,7 @@ namespace Avalonia.Skia var typeFaceCollection = new SKTypefaceCollection(); - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); foreach (var asset in fontAssets) { diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index dcb267b2a3..12d9471204 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -96,9 +96,9 @@ namespace Avalonia.Skia SKColorType colorType = format.ToSkColorType(); SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); - - var runtimePlatform = AvaloniaLocator.Current?.GetService(); - + + var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); + if (runtimePlatform != null) { _bitmap = new SKBitmap(); diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs index e5f87e71a2..4663a6561f 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs @@ -22,7 +22,7 @@ namespace Avalonia.Direct2D1.Media { var factory1 = factory; - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); foreach (var asset in fontAssets) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 180ae491b3..3f2298eb22 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -409,7 +409,7 @@ namespace Avalonia.Direct2D1.Media } else { - var platform = AvaloniaLocator.Current.GetService(); + var platform = AvaloniaLocator.Current.GetRequiredService(); var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height); var pixelSize = PixelSize.FromSizeWithDpi(size, dpi); return (IDrawingContextLayerImpl)platform.CreateRenderTargetBitmap(pixelSize, dpi); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs index a5474803a1..e607e255a5 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs @@ -127,7 +127,7 @@ namespace Avalonia.Win32.Interop.Wpf DeviceWindowHandle = GetDesktopWindow(), PresentationInterval = PresentInterval.Default }; - s_dxDevice = s_dxDevice ?? AvaloniaLocator.Current.GetService() + s_dxDevice = s_dxDevice ?? AvaloniaLocator.Current.GetRequiredService() .QueryInterface(); s_d3DDevice = new DeviceEx(s_d3DContext, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, presentparams); diff --git a/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs index aa042ffec8..afc25ab88e 100644 --- a/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs @@ -97,7 +97,7 @@ namespace Avalonia.Base.UnitTests.Media.Fonts { using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) { - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); var fontFamily = new FontFamily("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests#Noto Mono"); diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index d6c521decd..baf933bd66 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -596,7 +596,7 @@ namespace Avalonia.Controls.UnitTests private static Window PreparedWindow(object content = null) { var renderer = new Mock(); - var platform = AvaloniaLocator.Current.GetService(); + var platform = AvaloniaLocator.Current.GetRequiredService(); var windowImpl = Mock.Get(platform.CreateWindow()); windowImpl.Setup(x => x.CreateRenderer(It.IsAny())).Returns(renderer.Object); diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs index 496b5bc1b6..02767a21eb 100644 --- a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs +++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs @@ -570,7 +570,7 @@ namespace Avalonia.Controls.UnitTests private static Window PreparedWindow(object content = null) { var renderer = new Mock(); - var platform = AvaloniaLocator.Current.GetService(); + var platform = AvaloniaLocator.Current.GetRequiredService(); var windowImpl = Mock.Get(platform.CreateWindow()); windowImpl.Setup(x => x.CreateRenderer(It.IsAny())).Returns(renderer.Object); diff --git a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs index 90c6fd3b21..c1d9fad6f4 100644 --- a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs @@ -827,7 +827,7 @@ namespace Avalonia.Controls.UnitTests { AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); - var clipboard = AvaloniaLocator.CurrentMutable.GetService(); + var clipboard = AvaloniaLocator.CurrentMutable.GetRequiredService(); clipboard.SetTextAsync(textInput).GetAwaiter().GetResult(); RaiseKeyEvent(target, Key.V, KeyModifiers.Control); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index d4193d33ee..65957fda6d 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -564,7 +564,7 @@ namespace Avalonia.Controls.UnitTests.Primitives using (CreateServices()) { var renderer = new Mock(); - var platform = AvaloniaLocator.Current.GetService(); + var platform = AvaloniaLocator.Current.GetRequiredService(); var windowImpl = Mock.Get(platform.CreateWindow()); windowImpl.Setup(x => x.CreateRenderer(It.IsAny())).Returns(renderer.Object); diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index bd6d5d55e2..531a2869cd 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -767,7 +767,7 @@ namespace Avalonia.Controls.UnitTests { AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); - var clipboard = AvaloniaLocator.CurrentMutable.GetService(); + var clipboard = AvaloniaLocator.CurrentMutable.GetRequiredService(); clipboard.SetTextAsync(textInput).GetAwaiter().GetResult(); RaiseKeyEvent(target, Key.V, KeyModifiers.Control); @@ -876,7 +876,7 @@ namespace Avalonia.Controls.UnitTests AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); - var clipboard = AvaloniaLocator.CurrentMutable.GetService(); + var clipboard = AvaloniaLocator.CurrentMutable.GetRequiredService(); clipboard.SetTextAsync(Environment.NewLine).GetAwaiter().GetResult(); RaiseKeyEvent(target, Key.V, KeyModifiers.Control); diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 81936711ef..f526465b9b 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1248,7 +1248,7 @@ namespace Avalonia.Controls.UnitTests using (Application()) { var focus = FocusManager.Instance; - var navigation = AvaloniaLocator.Current.GetService(); + var navigation = AvaloniaLocator.Current.GetRequiredService(); var data = CreateTestTreeData(); var target = new TreeView @@ -1293,7 +1293,6 @@ namespace Avalonia.Controls.UnitTests using (Application()) { var focus = FocusManager.Instance; - var navigation = AvaloniaLocator.Current.GetService(); var data = CreateTestTreeData(); var selectedNode = new Node { Value = "Out of Tree Selected Item" }; @@ -1353,7 +1352,7 @@ namespace Avalonia.Controls.UnitTests var rootNode = tree[0]; - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = AvaloniaLocator.Current.GetRequiredService(); var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1400,7 +1399,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = AvaloniaLocator.Current.GetRequiredService(); var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1447,7 +1446,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = AvaloniaLocator.Current.GetRequiredService(); var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index 196375fb40..30326adba5 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -47,7 +47,7 @@ namespace Avalonia.ReactiveUI.UnitTests using (var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var isLaunchingReceived = false; - var application = AvaloniaLocator.Current.GetService(); + var application = AvaloniaLocator.Current.GetRequiredService(); application.ApplicationLifetime = lifetime; // Initialize ReactiveUI Suspension as in real-world scenario. @@ -65,7 +65,7 @@ namespace Avalonia.ReactiveUI.UnitTests using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) using (var lifetime = new ExoticApplicationLifetimeWithoutLifecycleEvents()) { - var application = AvaloniaLocator.Current.GetService(); + var application = AvaloniaLocator.Current.GetRequiredService(); application.ApplicationLifetime = lifetime; Assert.Throws(() => new AutoSuspendHelper(application.ApplicationLifetime)); } @@ -88,7 +88,7 @@ namespace Avalonia.ReactiveUI.UnitTests using (var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var shouldPersistReceived = false; - var application = AvaloniaLocator.Current.GetService(); + var application = AvaloniaLocator.Current.GetRequiredService(); application.ApplicationLifetime = lifetime; // Initialize ReactiveUI Suspension as in real-world scenario. diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 313281d6c6..edde62f041 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -91,7 +91,7 @@ namespace Avalonia.Direct2D1.RenderTests var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png"); var compositedPath = Path.Combine(OutputPath, testName + ".composited.out.png"); - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetRequiredService(); var pixelSize = new PixelSize((int)target.Width, (int)target.Height); var size = new Size(target.Width, target.Height); var dpiVector = new Vector(dpi, dpi); diff --git a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs index 2b1685f358..55ac16054d 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs @@ -76,13 +76,8 @@ namespace Avalonia.UnitTests var asset = fontAssets.First(); - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); - if (assetLoader == null) - { - throw new NotSupportedException("IAssetLoader is not registered."); - } - var stream = assetLoader.Open(asset); return new HarfBuzzGlyphTypefaceImpl(stream); From 698977d2836e41bc6bb4083f14bf1ced746bb2fb Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 10:47:27 +1100 Subject: [PATCH 71/95] . --- samples/ControlCatalog/ViewModels/CursorPageViewModel.cs | 2 +- .../ViewModels/TransitioningContentControlPageViewModel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs index 12ca6af4f5..f44d927801 100644 --- a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels .Select(x => new StandardCursorModel(x)) .ToList(); - var loader = AvaloniaLocator.Current.GetRequiredService()!; + var loader = AvaloniaLocator.Current.GetRequiredService(); var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png")); var bitmap = new Bitmap(s); CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16)); diff --git a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs index f4e0bc8912..4505c11e95 100644 --- a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs @@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels { public TransitioningContentControlPageViewModel() { - var assetLoader = AvaloniaLocator.Current.GetRequiredService()!; + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); var images = new string[] { From 16ffe2f6f99d41adab83e58b9f0f927daaf92bae Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 10:50:23 +1100 Subject: [PATCH 72/95] use default literal --- .../Data/Converters/FuncMultiValueConverter.cs | 2 +- src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs | 2 +- src/Avalonia.Base/Media/DrawingContext.cs | 2 +- src/Avalonia.Base/Media/DrawingGroup.cs | 4 ++-- src/Avalonia.Base/Media/ImmediateDrawingContext.cs | 2 +- .../Composition/Expressions/ExpressionVariant.cs | 2 +- src/Avalonia.Base/Rendering/ImmediateRenderer.cs | 6 +++--- .../Rendering/Utilities/TileBrushCalculator.cs | 2 +- src/Avalonia.Base/Utilities/SingleOrDictionary.cs | 2 +- src/Avalonia.Base/Utilities/StringTokenizer.cs | 4 ++-- src/Avalonia.Controls.DataGrid/DataGrid.cs | 4 ++-- src/Avalonia.Controls.DataGrid/IndexToValueTable.cs | 2 +- src/Avalonia.Controls/Platform/InProcessDragSource.cs | 2 +- .../Presenters/ScrollContentPresenter.cs | 4 ++-- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 2 +- src/Avalonia.FreeDesktop/DBusMenu.cs | 8 ++++---- .../Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs | 2 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 4 ++-- src/Skia/Avalonia.Skia/GeometryImpl.cs | 2 +- .../Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs | 4 ++-- src/Windows/Avalonia.Win32/DataObject.cs | 2 +- src/Windows/Avalonia.Win32/OleDropTarget.cs | 2 +- .../ItemsPresenterTests_Virtualization_Simple.cs | 2 +- 23 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs index 2577cac743..5084d8e822 100644 --- a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs @@ -38,7 +38,7 @@ namespace Avalonia.Data.Converters } else if (Equals(obj, default(TIn))) { - yield return default(TIn); + yield return default; } } } diff --git a/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs b/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs index 91fa459acb..fa298b53a3 100644 --- a/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs +++ b/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs @@ -7,7 +7,7 @@ namespace Avalonia.Layout { internal struct UvMeasure { - internal static readonly UvMeasure Zero = default(UvMeasure); + internal static readonly UvMeasure Zero = default; internal double U { get; set; } diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs index eabd7c8274..077816c645 100644 --- a/src/Avalonia.Base/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -279,7 +279,7 @@ namespace Avalonia.Media OpacityMask, } - public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix)) + public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default) { if (context._states is null) throw new ObjectDisposedException(nameof(DrawingContext)); diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index 4a46d89153..7d3b4c056e 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -76,8 +76,8 @@ namespace Avalonia.Media { using (context.PushPreTransform(Transform?.Value ?? Matrix.Identity)) using (context.PushOpacity(Opacity)) - using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default(DrawingContext.PushedState)) - using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, GetBounds()) : default(DrawingContext.PushedState)) + using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default) + using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, GetBounds()) : default) { foreach (var drawing in Children) { diff --git a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs index 1e1a73437d..eb6f105680 100644 --- a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs +++ b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs @@ -218,7 +218,7 @@ namespace Avalonia.Media OpacityMask, } - internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default(Matrix)) + internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default) { if (context._states is null) throw new ObjectDisposedException(nameof(ImmediateDrawingContext)); diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs index 6e53a138cd..21f14283b5 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs @@ -654,7 +654,7 @@ namespace Avalonia.Rendering.Composition.Expressions } } - res = default(T); + res = default; return false; } diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index 4909c78ed1..60b144e806 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -330,11 +330,11 @@ namespace Avalonia.Rendering ? visual is IVisualWithRoundRectClip roundClipVisual ? context.PushClip(new RoundedRect(bounds, roundClipVisual.ClipToBoundsRadius)) : context.PushClip(bounds) - : default(DrawingContext.PushedState)) + : default) #pragma warning restore CS0618 // Type or member is obsolete - using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState)) - using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState)) + using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default) + using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default) using (context.PushTransformContainer()) { visual.Render(context); diff --git a/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs index af2c7f71dc..6b34b44337 100644 --- a/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs @@ -109,7 +109,7 @@ namespace Avalonia.Rendering.Utilities { if (IntermediateTransform != Matrix.Identity) return true; - if (SourceRect.Position != default(Point)) + if (SourceRect.Position != default) return true; if (SourceRect.Size.AspectRatio == _imageSize.AspectRatio) return false; diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs index 00cc5864f5..0eb7ae2e31 100644 --- a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -42,7 +42,7 @@ namespace Avalonia.Utilities { if (!_singleValue.HasValue || !EqualityComparer.Default.Equals(_singleValue.Value.Key, key)) { - value = default(TValue); + value = default; return false; } else diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs index 726c9735ef..aad742e02b 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -63,7 +63,7 @@ namespace Avalonia.Utilities } else { - result = default(Int32); + result = default; return false; } } @@ -87,7 +87,7 @@ namespace Avalonia.Utilities } else { - result = default(double); + result = default; return false; } } diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 78de00078e..83a0dd3ce1 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -1118,7 +1118,7 @@ namespace Avalonia.Controls EnsureColumnHeadersVisibility(); if (!newValueCols) { - _columnHeadersPresenter.Measure(default(Size)); + _columnHeadersPresenter.Measure(default); } else { @@ -1159,7 +1159,7 @@ namespace Avalonia.Controls _topLeftCornerHeader.IsVisible = newValueRows && newValueCols; if (_topLeftCornerHeader.IsVisible) { - _topLeftCornerHeader.Measure(default(Size)); + _topLeftCornerHeader.Measure(default); } } diff --git a/src/Avalonia.Controls.DataGrid/IndexToValueTable.cs b/src/Avalonia.Controls.DataGrid/IndexToValueTable.cs index 65c3af344c..68a2fc562f 100644 --- a/src/Avalonia.Controls.DataGrid/IndexToValueTable.cs +++ b/src/Avalonia.Controls.DataGrid/IndexToValueTable.cs @@ -422,7 +422,7 @@ namespace Avalonia.Controls else { found = false; - return default(T); + return default; } } diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs index 5b2356a7ce..196fd898cf 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -41,7 +41,7 @@ namespace Avalonia.Platform { _draggedData = data; _lastRoot = null; - _lastPosition = default(Point); + _lastPosition = default; _allowedEffects = allowedEffects; using (_inputManager.PreProcess.OfType().Subscribe(ProcessMouseEvents)) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 328facba0b..51f85a3e3d 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -500,7 +500,7 @@ namespace Avalonia.Controls.Presenters if (e.OldValue != null) { - Offset = default(Vector); + Offset = default; } } @@ -542,7 +542,7 @@ namespace Avalonia.Controls.Presenters if (logicalScroll != scrollable.IsLogicalScrollEnabled) { UpdateScrollableSubscription(Child); - Offset = default(Vector); + Offset = default; InvalidateMeasure(); } else if (scrollable.IsLogicalScrollEnabled) diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 6ff72751a6..7b663dd017 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -49,7 +49,7 @@ namespace Avalonia.Controls.Utils public bool TryGetLastState(out TState? _state) { - _state = default(TState); + _state = default; if (!IsLastState) return false; diff --git a/src/Avalonia.FreeDesktop/DBusMenu.cs b/src/Avalonia.FreeDesktop/DBusMenu.cs index 3a1c65e7c9..7e22988270 100644 --- a/src/Avalonia.FreeDesktop/DBusMenu.cs +++ b/src/Avalonia.FreeDesktop/DBusMenu.cs @@ -36,10 +36,10 @@ namespace Avalonia.FreeDesktop.DBusMenu [Dictionary] class DBusMenuProperties { - public uint Version { get; set; } = default (uint); - public string? TextDirection { get; set; } = default (string); - public string? Status { get; set; } = default (string); - public string[]? IconThemePath { get; set; } = default (string[]); + public uint Version { get; set; } = default; + public string? TextDirection { get; set; } = default; + public string? Status { get; set; } = default; + public string[]? IconThemePath { get; set; } = default; } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index ac54365f51..6032f3a92c 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -25,7 +25,7 @@ namespace Avalonia.LinuxFramebuffer Surfaces = new object[] { _outputBackend }; - Invalidate(default(Rect)); + Invalidate(default); _inputBackend.Initialize(this, e => Input?.Invoke(e)); } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 48e8c761eb..8b71f4e17e 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -224,9 +224,9 @@ namespace Avalonia.Skia var impl = (GeometryImpl) geometry; var size = geometry.Bounds.Size; - using (var fill = brush != null ? CreatePaint(_fillPaint, brush, size) : default(PaintWrapper)) + using (var fill = brush != null ? CreatePaint(_fillPaint, brush, size) : default) using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen, - size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0))) : default(PaintWrapper)) + size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0))) : default) { if (fill.Paint != null) { diff --git a/src/Skia/Avalonia.Skia/GeometryImpl.cs b/src/Skia/Avalonia.Skia/GeometryImpl.cs index 1141763097..15a3ebff40 100644 --- a/src/Skia/Avalonia.Skia/GeometryImpl.cs +++ b/src/Skia/Avalonia.Skia/GeometryImpl.cs @@ -245,7 +245,7 @@ namespace Avalonia.Skia { CachedStrokePath?.Dispose(); CachedGeometryRenderBounds = default; - _cachedStrokeWidth = default(float); + _cachedStrokeWidth = default; } } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs index a5474803a1..5526ee69ac 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs @@ -156,7 +156,7 @@ namespace Avalonia.Win32.Interop.Wpf _impl.ImageSource = _image; RemoveAndDispose(ref _backBuffer); - if (size == default(IntSize)) + if (size == default) { _image.Lock(); _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); @@ -171,7 +171,7 @@ namespace Avalonia.Win32.Interop.Wpf static void RemoveAndDispose(ref T d) where T : IDisposable { d?.Dispose(); - d = default(T); + d = default; } void Swap() diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index b7a567ea97..7d22e57a30 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -205,7 +205,7 @@ namespace Avalonia.Win32 if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) return DV_E_FORMATETC; - * medium = default(Interop.STGMEDIUM); + * medium = default; medium->tymed = TYMED.TYMED_HGLOBAL; return WriteDataToHGlobal(fmt, ref medium->unionmember); } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index b4eb6d8851..8bbdfe5fd9 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -121,7 +121,7 @@ namespace Avalonia.Win32 _dragDevice, RawDragEventType.DragLeave, _target, - default(Point), + default, null, DragDropEffects.None, RawInputModifiers.None diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index 2d1dad6be0..469bcecfb9 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -569,7 +569,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var target = CreateTarget(itemCount: 10); var items = (IList)target.Items; target.ApplyTemplate(); - target.Measure(default(Size)); + target.Measure(default); target.Arrange(default); // Check for issue #591: this should not throw. From 208b0264c64e25282870c036e02c7794daf173ee Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 10:54:22 +1100 Subject: [PATCH 73/95] broken GetRequiredService for _useGL --- src/Browser/Avalonia.Browser/AvaloniaView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Browser/Avalonia.Browser/AvaloniaView.cs b/src/Browser/Avalonia.Browser/AvaloniaView.cs index 153a8c6c1d..294216ee03 100644 --- a/src/Browser/Avalonia.Browser/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser/AvaloniaView.cs @@ -106,7 +106,7 @@ namespace Avalonia.Browser _dpi = DomHelper.ObserveDpi(OnDpiChanged); - _useGL = AvaloniaLocator.Current.GetRequiredService() != null; + _useGL = AvaloniaLocator.Current.GetService() != null; if (_useGL) { From 9ea174ce0af7f1f9f89c0bc5492d74cb177b1f9d Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 10:58:47 +1100 Subject: [PATCH 74/95] Storage/getItem can be null https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem --- src/Browser/Avalonia.Browser/Interop/StorageHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs index c44af810d1..e50f8790ef 100644 --- a/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs @@ -45,7 +45,7 @@ internal static partial class StorageHelper [JSImport("StorageItem.getItems", AvaloniaModule.StorageModuleName)] [return: JSMarshalAs>] - public static partial Task GetItems(JSObject item); + public static partial Task GetItems(JSObject item); [JSImport("StorageItems.itemsArray", AvaloniaModule.StorageModuleName)] public static partial JSObject[] ItemsArray(JSObject item); From 4b1a5a28b16d19aafa3155d33914a2e4535a3206 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:06:47 +1100 Subject: [PATCH 75/95] OpenFilePickerAsync cant return null --- samples/ControlCatalog/ViewModels/ContextPageViewModel.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs index 4cfb00625e..4a5462a58b 100644 --- a/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs @@ -56,12 +56,9 @@ namespace ControlCatalog.ViewModels var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true }); - if (result != null) + foreach (var file in result) { - foreach (var file in result) - { - System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}"); - } + System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}"); } } From 374c0c4e3bd596d7166e3634c1ae0e146f894210 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:08:04 +1100 Subject: [PATCH 76/95] screens in ScreenPage sampel cant be null --- samples/ControlCatalog/Pages/ScreenPage.cs | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 6af4cf353e..b5b80fb147 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -36,44 +36,44 @@ namespace ControlCatalog.Pages var drawBrush = Brushes.Black; Pen p = new Pen(drawBrush); - if (screens != null) - foreach (Screen screen in screens) + + foreach (Screen screen in screens) + { + if (screen.Bounds.X / 10f < _leftMost) { - if (screen.Bounds.X / 10f < _leftMost) - { - _leftMost = screen.Bounds.X / 10f; - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - return; - } + _leftMost = screen.Bounds.X / 10f; + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + return; + } - Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f, - screen.Bounds.Height / 10f); - Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f, - screen.WorkingArea.Height / 10f); - - context.DrawRectangle(p, boundsRect); - context.DrawRectangle(p, workingAreaRect); + Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f, + screen.Bounds.Height / 10f); + Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f, + screen.WorkingArea.Height / 10f); + context.DrawRectangle(p, boundsRect); + context.DrawRectangle(p, workingAreaRect); - var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); - context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height)); - formattedText = - CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); - context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20)); + var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height)); - formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%"); - context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); + formattedText = + CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20)); - formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}"); + formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); - context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); + formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}"); - formattedText = - CreateFormattedText( - $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}"); - context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80)); - } + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); + + formattedText = + CreateFormattedText( + $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80)); + } context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); } From 1b71714b76938278b744d475eb64b08d9156c187 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:09:22 +1100 Subject: [PATCH 77/95] brush in cataloge sample can be null --- samples/ControlCatalog/Pages/PointerCanvas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/PointerCanvas.cs b/samples/ControlCatalog/Pages/PointerCanvas.cs index 7d26da8603..da1ff5442d 100644 --- a/samples/ControlCatalog/Pages/PointerCanvas.cs +++ b/samples/ControlCatalog/Pages/PointerCanvas.cs @@ -24,7 +24,7 @@ public class PointerCanvas : Control { struct CanvasPoint { - public IBrush Brush; + public IBrush? Brush; public Point Point; public double Radius; public double? Pressure; From c89f221f6d2f7bc9fef9ea9bca7de7fc41ba8105 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:14:29 +1100 Subject: [PATCH 78/95] remove some duplicate casts --- .../Controls/NameScopeExtensions.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Controls/NameScopeExtensions.cs b/src/Avalonia.Base/Controls/NameScopeExtensions.cs index 3895b6ceb9..7d80d2fde7 100644 --- a/src/Avalonia.Base/Controls/NameScopeExtensions.cs +++ b/src/Avalonia.Base/Controls/NameScopeExtensions.cs @@ -25,13 +25,18 @@ namespace Avalonia.Controls var result = nameScope.Find(name); - if (result != null && !(result is T)) + if (result == null) { - throw new InvalidOperationException( - $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); + return (T?)result; + } + + if (result is T typed) + { + return typed; } - return (T?)result; + throw new InvalidOperationException( + $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); } /// @@ -74,13 +79,13 @@ namespace Avalonia.Controls throw new KeyNotFoundException($"Could not find control '{name}'."); } - if (!(result is T)) + if (result is T typed) { - throw new InvalidOperationException( - $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); + return typed; } - return (T)result; + throw new InvalidOperationException( + $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); } /// From 4ab3eb106423fe0e2baaa43311e5bb1e561db98b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:18:40 +1100 Subject: [PATCH 79/95] . --- src/Avalonia.Build.Tasks/ComInteropHelper.cs | 4 +--- .../ScrollViewerIRefreshInfoProviderAdapter.cs | 4 ++-- .../Templates/FuncTreeDataTemplate`1.cs | 4 ++-- .../ViewModels/ControlDetailsViewModel.cs | 8 ++++---- .../Markup/Parsers/SelectorGrammar.cs | 12 +++++++----- src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs | 2 +- .../Avalonia.Win32/WindowImpl.CustomCaptionProc.cs | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Build.Tasks/ComInteropHelper.cs b/src/Avalonia.Build.Tasks/ComInteropHelper.cs index 007231417d..c990741e1e 100644 --- a/src/Avalonia.Build.Tasks/ComInteropHelper.cs +++ b/src/Avalonia.Build.Tasks/ComInteropHelper.cs @@ -65,10 +65,8 @@ namespace Avalonia.Build.Tasks { Instruction instruction = instructions[i]; - if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference) + if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference methodDescription) { - var methodDescription = (MethodReference)instruction.Operand; - if (methodDescription.Name.StartsWith("Calli") && methodDescription.DeclaringType.Name == "LocalInterop") { var callSite = new CallSite(methodDescription.ReturnType) { CallingConvention = MethodCallingConvention.StdCall }; diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs index c3aebc82c5..844973bd28 100644 --- a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs +++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs @@ -197,12 +197,12 @@ namespace Avalonia.Controls.PullToRefresh throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content property must be a Visual"); } - if (content.Parent is not InputElement) + if (content.Parent is not InputElement parent) { throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content parent must be an InputElement"); } - MakeInteractionSource(content.Parent as InputElement); + MakeInteractionSource(parent); if (_scrollViewer != null) { diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs index d4ecdd6cf0..a093d976f7 100644 --- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs +++ b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs @@ -59,7 +59,7 @@ namespace Avalonia.Controls.Templates /// The untyped function. private static Func CastMatch(Func f) { - return o => (o is T) && f((T)o); + return o => o is T arg && f(arg); } /// @@ -72,7 +72,7 @@ namespace Avalonia.Controls.Templates { return (o, s) => f((T)o!, s); } - + /// /// Casts a function with a typed parameter to an untyped function. /// diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 5cf9e17ecf..8bff9ccde0 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -33,12 +33,12 @@ namespace Avalonia.Diagnostics.ViewModels { _avaloniaObject = avaloniaObject; - TreePage = treePage; - Layout = avaloniaObject is Visual - ? new ControlLayoutViewModel((Visual)avaloniaObject) + TreePage = treePage; + Layout = avaloniaObject is Visual visual + ? new ControlLayoutViewModel(visual) : default; - NavigateToProperty(_avaloniaObject, (_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString()); + NavigateToProperty(_avaloniaObject, (_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString()); AppliedStyles = new ObservableCollection(); PseudoClasses = new ObservableCollection(); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index 4d6d16a3ce..dbf37f0900 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -552,7 +552,8 @@ namespace Avalonia.Markup.Parsers public override bool Equals(object? obj) { - return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class; + return obj is ClassSyntax syntax && + syntax.Class == Class; } } @@ -562,7 +563,8 @@ namespace Avalonia.Markup.Parsers public override bool Equals(object? obj) { - return obj is NameSyntax && ((NameSyntax)obj).Name == Name; + return obj is NameSyntax syntax && + syntax.Name == Name; } } @@ -574,9 +576,9 @@ namespace Avalonia.Markup.Parsers public override bool Equals(object? obj) { - return obj is PropertySyntax && - ((PropertySyntax)obj).Property == Property && - ((PropertySyntax)obj).Value == Value; + return obj is PropertySyntax syntax && + syntax.Property == Property && + syntax.Value == Value; } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs index b8648dfc50..3bfbf4bd92 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs @@ -28,7 +28,7 @@ namespace Avalonia.Win32.Interop.Wpf public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; - return obj is IntSize && Equals((IntSize) obj); + return obj is IntSize size && Equals(size); } public override int GetHashCode() diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index d393bd304a..fb27ab7856 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, (Visual)_owner, x => + var visual = window.Renderer.HitTestFirst(position, window, x => { if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsEffectivelyVisible)) { From 0520d25ed580b86b3e3d1d7d4c4d614ce009d887 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:23:21 +1100 Subject: [PATCH 80/95] missing OnItemTemplatePropertyChanged call in AutoCompleteBox --- src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index e0d986f2b4..f9978d2795 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -474,6 +474,7 @@ namespace Avalonia.Controls FilterModeProperty.Changed.AddClassHandler((x,e) => x.OnFilterModePropertyChanged(e)); ItemFilterProperty.Changed.AddClassHandler((x,e) => x.OnItemFilterPropertyChanged(e)); ItemsProperty.Changed.AddClassHandler((x,e) => x.OnItemsPropertyChanged(e)); + ItemTemplateProperty.Changed.AddClassHandler((x,e) => x.OnItemTemplatePropertyChanged(e)); IsEnabledProperty.Changed.AddClassHandler((x,e) => x.OnControlIsEnabledChanged(e)); } From 81c06203676899fd45135e9390aeb002f155de61 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:24:35 +1100 Subject: [PATCH 81/95] Update src/Avalonia.Controls/Platform/PlatformManager.cs Co-authored-by: Max Katz --- src/Avalonia.Controls/Platform/PlatformManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index b01fc28831..79ace0b329 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -21,7 +21,7 @@ namespace Avalonia.Controls.Platform } public static ITrayIconImpl? CreateTrayIcon() => - s_designerMode ? null : AvaloniaLocator.Current.GetRequiredService().CreateTrayIcon(); + s_designerMode ? null : AvaloniaLocator.Current.GetService()?.CreateTrayIcon(); public static IWindowImpl CreateWindow() From cc6f0d1af15cf4a37e16f0ea9469c4171d740f8f Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:28:59 +1100 Subject: [PATCH 82/95] should GetLayoutVisibleWindowDiscardAnchor be used? --- .../Repeater/ViewportManager.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index 10e3dd57a0..28d6911350 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -124,22 +124,6 @@ namespace Avalonia.Controls } } } - - private Rect GetLayoutVisibleWindowDiscardAnchor() - { - var visibleWindow = _visibleWindow; - - if (HasScroller) - { - visibleWindow = new Rect( - visibleWindow.X + _layoutExtent.X + _expectedViewportShift.X + _unshiftableShift.X, - visibleWindow.Y + _layoutExtent.Y + _expectedViewportShift.Y + _unshiftableShift.Y, - visibleWindow.Width, - visibleWindow.Height); - } - - return visibleWindow; - } public Rect GetLayoutVisibleWindow() { From ebb7a95c0e6046bea6fc6a13ede18991e94abc56 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 11:32:04 +1100 Subject: [PATCH 83/95] missing ScrollGestureEndedEvent mapping --- src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 328facba0b..61ad5e802b 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -94,6 +94,7 @@ namespace Avalonia.Controls.Presenters { AddHandler(RequestBringIntoViewEvent, BringIntoViewRequested); AddHandler(Gestures.ScrollGestureEvent, OnScrollGesture); + AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription); } From a642cba9d24a91dc1006d91fc7fb28f7a7870233 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 14:51:01 +1100 Subject: [PATCH 84/95] Update NameScopeExtensions.cs --- src/Avalonia.Base/Controls/NameScopeExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Controls/NameScopeExtensions.cs b/src/Avalonia.Base/Controls/NameScopeExtensions.cs index 7d80d2fde7..0abd5e56f7 100644 --- a/src/Avalonia.Base/Controls/NameScopeExtensions.cs +++ b/src/Avalonia.Base/Controls/NameScopeExtensions.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls if (result == null) { - return (T?)result; + return null; } if (result is T typed) From 7db93a75e42535c8125437d71a199e6547a2cdbe Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 6 Jan 2023 08:54:04 +0200 Subject: [PATCH 85/95] Move missing calling InvalidateMirrorTransform --- src/Avalonia.Base/Visual.cs | 1 + src/Avalonia.Controls/Control.cs | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 5930df5483..7e22303964 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -480,6 +480,7 @@ namespace Avalonia { AttachToCompositor(compositingRenderer.Compositor); } + InvalidateMirrorTransform(); OnAttachedToVisualTree(e); AttachedToVisualTree?.Invoke(this, e); InvalidateVisual(); diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 26db431a0e..ed24c3c7c2 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -392,14 +392,6 @@ namespace Avalonia.Controls OnUnloadedCore(); } - /// - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - - InvalidateMirrorTransform(); - } - /// protected override void OnGotFocus(GotFocusEventArgs e) { From 1396c5aac7ba0987e06d828589b1c53b52726a62 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 19:48:05 +1100 Subject: [PATCH 86/95] Update WriteableBitmapImpl.cs --- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 12d9471204..9864a14a9c 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -97,7 +97,7 @@ namespace Avalonia.Skia SKColorType colorType = format.ToSkColorType(); SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); - var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); + var runtimePlatform = AvaloniaLocator.Current.GetService(); if (runtimePlatform != null) { From 5fdb513303b7b6cccca5b5e7416e34c3e061afa5 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Fri, 6 Jan 2023 12:18:15 +0300 Subject: [PATCH 87/95] [Text] [Debug] fix Text output of TextRunDebuggerProxy --- src/Avalonia.Base/Media/TextFormatting/TextRun.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextRun.cs b/src/Avalonia.Base/Media/TextFormatting/TextRun.cs index 0306054767..e79c2ed8b3 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextRun.cs @@ -44,7 +44,7 @@ namespace Avalonia.Media.TextFormatting fixed (char* charsPtr = characterBuffer.Span) { - return new string(charsPtr, 0, characterBuffer.Span.Length); + return new string(charsPtr, _textRun.CharacterBufferReference.OffsetToFirstChar, _textRun.Length); } } } From ac01ee00e68e0bdfbe6f0992a4743bf4472561ae Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Jan 2023 20:38:05 +1100 Subject: [PATCH 88/95] . --- samples/Directory.Build.props | 5 ++--- tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 471c42ec07..3b14f0ce12 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -2,9 +2,8 @@ false $(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll + false + 11 - - false - diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index b9c2a619d9..17448ade76 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -4,7 +4,6 @@ false Library false - latest ..\..\build\avalonia.snk false true From d1037a73699bfff23cd25a9c9d08bf66e3948e70 Mon Sep 17 00:00:00 2001 From: AlmightyJu Date: Fri, 6 Jan 2023 14:26:00 +0000 Subject: [PATCH 89/95] Fix DataGridTemplateColumn reusing edit control when exiting edit mode --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 1 + src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 11 +++++++++++ .../DataGridTemplateColumn.cs | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 78de00078e..cadecc03bc 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -4152,6 +4152,7 @@ namespace Avalonia.Controls if (exitEditingMode) { + CurrentColumn.EndCellEditInternal(); _editingColumnIndex = -1; editingCell.UpdatePseudoClasses(); diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index d9d80acdc3..63e7e1e2b3 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -800,11 +800,22 @@ namespace Avalonia.Controls protected internal virtual void RefreshCellContent(Control element, string propertyName) { } + /// + /// When overridden in a derived class, called when a cell in the column exits editing mode. + /// + protected virtual void EndCellEdit() + { } + internal void CancelCellEditInternal(Control editingElement, object uneditedValue) { CancelCellEdit(editingElement, uneditedValue); } + internal void EndCellEditInternal() + { + EndCellEdit(); + } + /// /// Coerces a DataGridLength to a valid value. If any value components are double.NaN, this method /// coerces them to a proper initial value. For star columns, the desired width is calculated based diff --git a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs index 24ae358dcc..516e9cf6c2 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs @@ -55,17 +55,25 @@ namespace Avalonia.Controls get => _cellEditingCellTemplate; set => SetAndRaise(CellEditingTemplateProperty, ref _cellEditingCellTemplate, value); } - - private static void OnCellTemplateChanged(AvaloniaPropertyChangedEventArgs e) + + private bool _forceGenerateCellFromTemplate; + + protected override void EndCellEdit() { - var oldValue = (IDataTemplate)e.OldValue; - var value = (IDataTemplate)e.NewValue; + //the next call to generate element should not resuse the current content as we need to exit edit mode + _forceGenerateCellFromTemplate = true; + base.EndCellEdit(); } protected override Control GenerateElement(DataGridCell cell, object dataItem) { if (CellTemplate != null) { + if (_forceGenerateCellFromTemplate) + { + _forceGenerateCellFromTemplate = false; + return CellTemplate.Build(dataItem); + } return (CellTemplate is IRecyclingDataTemplate recyclingDataTemplate) ? recyclingDataTemplate.Build(dataItem, cell.Content as Control) : CellTemplate.Build(dataItem); From 84206e93dd9980e9b590398e50e1ba780cb5ce7c Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 03:45:50 +0300 Subject: [PATCH 90/95] [Text] Optimize grapheme and add performance benchmark --- .../TextFormatting/FormattedTextSource.cs | 2 +- .../Media/TextFormatting/TextCharacters.cs | 4 +- .../Media/TextFormatting/Unicode/Grapheme.cs | 14 +++-- .../Unicode/GraphemeEnumerator.cs | 4 +- src/Avalonia.Controls/TextBox.cs | 2 +- .../GraphemeBreakClassTrieGeneratorTests.cs | 4 +- .../Text/HugeTextLayout.cs | 62 +++++++++++++++++++ .../Media/TextFormatting/TextLayoutTests.cs | 8 +-- 8 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs index 4472ba87eb..49d94b511d 100644 --- a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs @@ -132,7 +132,7 @@ namespace Avalonia.Media.TextFormatting { var grapheme = graphemeEnumerator.Current; - finalLength += grapheme.Text.Length; + finalLength += grapheme.Length; if (finalLength >= length) { diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index db035b8750..1a48151834 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -196,7 +196,7 @@ namespace Avalonia.Media.TextFormatting break; } - count += grapheme.Text.Length; + count += grapheme.Length; } return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties, biDiLevel); @@ -264,7 +264,7 @@ namespace Avalonia.Media.TextFormatting } } - length += currentGrapheme.Text.Length; + length += currentGrapheme.Length; } return length > 0; diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs index 69015fb17d..f75168083c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs @@ -7,10 +7,11 @@ namespace Avalonia.Media.TextFormatting.Unicode /// public readonly ref struct Grapheme { - public Grapheme(Codepoint firstCodepoint, ReadOnlySpan text) + public Grapheme(Codepoint firstCodepoint, int offset, int length) { FirstCodepoint = firstCodepoint; - Text = text; + Offset = offset; + Length = length; } /// @@ -19,8 +20,13 @@ namespace Avalonia.Media.TextFormatting.Unicode public Codepoint FirstCodepoint { get; } /// - /// The text that is representing the . + /// The Offset to the FirstCodepoint /// - public ReadOnlySpan Text { get; } + public int Offset { get; } + + /// + /// The length of the grapheme cluster + /// + public int Length { get; } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs index 5ca120c856..dc21e06813 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs @@ -185,9 +185,7 @@ namespace Avalonia.Media.TextFormatting.Unicode Return: - var text = _text.Take(processor.CurrentCodeUnitOffset); - - Current = new Grapheme(firstCodepoint, text.Span); + Current = new Grapheme(firstCodepoint, _text.OffsetToFirstChar, processor.CurrentCodeUnitOffset); _text = _text.Skip(processor.CurrentCodeUnitOffset); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 9a2ee12dc3..0e1024c027 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -981,7 +981,7 @@ namespace Avalonia.Controls } } - length += grapheme.Text.Length; + length += grapheme.Length; } if (length < input.Length) diff --git a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs index 4e0207a85d..c57bd6c002 100644 --- a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs @@ -42,7 +42,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting enumerator.MoveNext(); - var actual = enumerator.Current.Text; + var actual = text.AsSpan(enumerator.Current.Offset, enumerator.Current.Length); var pass = true; @@ -93,7 +93,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting while (enumerator.MoveNext()) { - Assert.Equal(1, enumerator.Current.Text.Length); + Assert.Equal(1, enumerator.Current.Length); count++; } diff --git a/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs b/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs new file mode 100644 index 0000000000..e1b3c6a99c --- /dev/null +++ b/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs @@ -0,0 +1,62 @@ +using System; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; +using Avalonia.UnitTests; +using BenchmarkDotNet.Attributes; + +namespace Avalonia.Benchmarks.Text; + +[MemoryDiagnoser] +public class HugeTextLayout : IDisposable +{ + private readonly IDisposable _app; + + public HugeTextLayout() + { + _app = UnitTestApplication.Start( + TestServices.StyledWindow.With( + renderInterface: new NullRenderingPlatform(), + threadingInterface: new NullThreadingPlatform(), + standardCursorFactory: new NullCursorFactory())); + } + + private const string Text = @"Though, the objectives of the development of the prominent landmarks can be neglected in most cases, it should be realized that after the completion of the strategic decision gives rise to The Expertise of Regular Program (Carlton Cartwright in The Book of the Key Factor) +A number of key issues arise from the belief that the explicit examination of strategic management should correlate with the conceptual design. +By all means, the unification of the reliably developed techniques indicates the importance of the ultimate advantage of episodic skill over alternate practices. +Let's consider, that the portion of the continuing support can be regarded as relentlessly insignificant. The hardware maintenance focuses our attention on the structure absorption. The real reason of the permanent growth drastically the preliminary action plan the ultimate advantage of useful probability over alternate practices. +Let it not be said that a section of the essential component discards the principle of the more interconnection of critical thinking with productivity boosting of the referential arguments. +One should, however, not forget that concentration of violations of the strategic management requires urgent actions to be taken towards the comprehensive set of policy statements. Therefore, the concept of the design aspects can be treated as the only solution. +In a loose sense concentration of the center of the critical thinking provides a deep insight into the emergency planning. The comparison is quite a powerful matter. +Resulting from review or analysis of threats and opportunities, we can presume that either significant improvement or basics of planning and scheduling reveals the patterns of the final draft. Therefore, the concept of the crucial component can be treated as the only solution. +One should, nevertheless, consider that the exceptional results of the diverse sources of information gives an overview of the production cycle. It should rather be regarded as an integral part of the direct access to key resources. +Admitting that the possibility of achieving the results of the constructive criticism, as far as the strategic management is questionable, cannot rely only on the critical thinking. It may reveal how the systems approach partially the comprehensive project management. We must be ready for outline design stage and network development investigation of every contradiction between the effective time management and the efficient decision the network development. Everyone understands what it takes to the draft analysis and prior decisions and early design solutions. In any case, we can systematically change the mechanism of the sources and influences of the continuing financing doctrine. This could exceedingly be a result of a task analysis the hardware maintenance. The real reason of the strategic planning seemingly the influence on eventual productivity. Everyone understands what it takes to the well-known practice. Therefore, the concept of the productivity boost can be treated as the only solution the driving factor. It may reveal how the matters of peculiar interest slowly the goals and objectives or the diverse sources of information the positive influence of any major outcomes complete failure of the supposed theory. +In respect that the structure of the sufficient amount poses problems and challenges for both the set of related commands and controls and the ability bias."; + + [Benchmark] + public TextLayout BuildTextLayout() => new TextLayout(Text, Typeface.Default, 12d, Brushes.Black); + + private const string Emojis = @"๐Ÿ˜€ ๐Ÿ˜ ๐Ÿ˜‚ ๐Ÿคฃ ๐Ÿ˜ƒ ๐Ÿ˜„ ๐Ÿ˜… ๐Ÿ˜† ๐Ÿ˜‰ ๐Ÿ˜Š ๐Ÿ˜‹ ๐Ÿ˜Ž ๐Ÿ˜ ๐Ÿ˜˜ ๐Ÿฅฐ ๐Ÿ˜— ๐Ÿ˜™ ๐Ÿ˜š โ˜บ๏ธ ๐Ÿ™‚ ๐Ÿค— ๐Ÿคฉ ๐Ÿค” ๐Ÿคจ ๐Ÿ˜ ๐Ÿ˜‘ ๐Ÿ˜ถ ๐Ÿ™„ ๐Ÿ˜ ๐Ÿ˜ฃ ๐Ÿ˜ฅ ๐Ÿ˜ฎ ๐Ÿค ๐Ÿ˜ฏ ๐Ÿ˜ช ๐Ÿ˜ซ ๐Ÿ˜ด ๐Ÿ˜Œ ๐Ÿ˜› ๐Ÿ˜œ ๐Ÿ˜ ๐Ÿคค ๐Ÿ˜’ ๐Ÿ˜“ ๐Ÿ˜” ๐Ÿ˜• ๐Ÿ™ƒ ๐Ÿค‘ ๐Ÿ˜ฒ โ˜น๏ธ ๐Ÿ™ ๐Ÿ˜– ๐Ÿ˜ž ๐Ÿ˜Ÿ ๐Ÿ˜ค ๐Ÿ˜ข ๐Ÿ˜ญ ๐Ÿ˜ฆ ๐Ÿ˜ง ๐Ÿ˜จ ๐Ÿ˜ฉ ๐Ÿคฏ ๐Ÿ˜ฌ ๐Ÿ˜ฐ ๐Ÿ˜ฑ ๐Ÿฅต ๐Ÿฅถ ๐Ÿ˜ณ ๐Ÿคช ๐Ÿ˜ต ๐Ÿ˜ก ๐Ÿ˜  ๐Ÿคฌ ๐Ÿ˜ท ๐Ÿค’ ๐Ÿค• ๐Ÿคข ๐Ÿคฎ ๐Ÿคง ๐Ÿ˜‡ ๐Ÿค  ๐Ÿคก ๐Ÿฅณ ๐Ÿฅด ๐Ÿฅบ ๐Ÿคฅ ๐Ÿคซ ๐Ÿคญ ๐Ÿง ๐Ÿค“ ๐Ÿ˜ˆ ๐Ÿ‘ฟ ๐Ÿ‘น ๐Ÿ‘บ ๐Ÿ’€ ๐Ÿ‘ป ๐Ÿ‘ฝ ๐Ÿค– ๐Ÿ’ฉ ๐Ÿ˜บ ๐Ÿ˜ธ ๐Ÿ˜น ๐Ÿ˜ป ๐Ÿ˜ผ ๐Ÿ˜ฝ ๐Ÿ™€ ๐Ÿ˜ฟ ๐Ÿ˜พ +๐Ÿ‘ถ ๐Ÿ‘ง ๐Ÿง’ ๐Ÿ‘ฆ ๐Ÿ‘ฉ ๐Ÿง‘ ๐Ÿ‘จ ๐Ÿ‘ต ๐Ÿง“ ๐Ÿ‘ด ๐Ÿ‘ฒ ๐Ÿ‘ณโ€โ™€๏ธ ๐Ÿ‘ณโ€โ™‚๏ธ ๐Ÿง• ๐Ÿง” ๐Ÿ‘ฑโ€โ™‚๏ธ ๐Ÿ‘ฑโ€โ™€๏ธ ๐Ÿ‘จโ€๐Ÿฆฐ ๐Ÿ‘ฉโ€๐Ÿฆฐ ๐Ÿ‘จโ€๐Ÿฆฑ ๐Ÿ‘ฉโ€๐Ÿฆฑ ๐Ÿ‘จโ€๐Ÿฆฒ ๐Ÿ‘ฉโ€๐Ÿฆฒ ๐Ÿ‘จโ€๐Ÿฆณ ๐Ÿ‘ฉโ€๐Ÿฆณ ๐Ÿฆธโ€โ™€๏ธ ๐Ÿฆธโ€โ™‚๏ธ ๐Ÿฆนโ€โ™€๏ธ ๐Ÿฆนโ€โ™‚๏ธ ๐Ÿ‘ฎโ€โ™€๏ธ ๐Ÿ‘ฎโ€โ™‚๏ธ ๐Ÿ‘ทโ€โ™€๏ธ ๐Ÿ‘ทโ€โ™‚๏ธ ๐Ÿ’‚โ€โ™€๏ธ ๐Ÿ’‚โ€โ™‚๏ธ ๐Ÿ•ต๏ธโ€โ™€๏ธ ๐Ÿ•ต๏ธโ€โ™‚๏ธ ๐Ÿ‘ฉโ€โš•๏ธ ๐Ÿ‘จโ€โš•๏ธ ๐Ÿ‘ฉโ€๐ŸŒพ ๐Ÿ‘จโ€๐ŸŒพ ๐Ÿ‘ฉโ€๐Ÿณ ๐Ÿ‘จโ€๐Ÿณ ๐Ÿ‘ฉโ€๐ŸŽ“ ๐Ÿ‘จโ€๐ŸŽ“ ๐Ÿ‘ฉโ€๐ŸŽค ๐Ÿ‘จโ€๐ŸŽค ๐Ÿ‘ฉโ€๐Ÿซ ๐Ÿ‘จโ€๐Ÿซ ๐Ÿ‘ฉโ€๐Ÿญ ๐Ÿ‘จโ€๐Ÿญ ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿ‘จโ€๐Ÿ’ป ๐Ÿ‘ฉโ€๐Ÿ’ผ ๐Ÿ‘จโ€๐Ÿ’ผ ๐Ÿ‘ฉโ€๐Ÿ”ง ๐Ÿ‘จโ€๐Ÿ”ง ๐Ÿ‘ฉโ€๐Ÿ”ฌ ๐Ÿ‘จโ€๐Ÿ”ฌ ๐Ÿ‘ฉโ€๐ŸŽจ ๐Ÿ‘จโ€๐ŸŽจ ๐Ÿ‘ฉโ€๐Ÿš’ ๐Ÿ‘จโ€๐Ÿš’ ๐Ÿ‘ฉโ€โœˆ๏ธ ๐Ÿ‘จโ€โœˆ๏ธ ๐Ÿ‘ฉโ€๐Ÿš€ ๐Ÿ‘จโ€๐Ÿš€ ๐Ÿ‘ฉโ€โš–๏ธ ๐Ÿ‘จโ€โš–๏ธ ๐Ÿ‘ฐ ๐Ÿคต ๐Ÿ‘ธ ๐Ÿคด ๐Ÿคถ ๐ŸŽ… ๐Ÿง™โ€โ™€๏ธ ๐Ÿง™โ€โ™‚๏ธ ๐Ÿงโ€โ™€๏ธ ๐Ÿงโ€โ™‚๏ธ ๐Ÿง›โ€โ™€๏ธ ๐Ÿง›โ€โ™‚๏ธ ๐ŸงŸโ€โ™€๏ธ ๐ŸงŸโ€โ™‚๏ธ ๐Ÿงžโ€โ™€๏ธ ๐Ÿงžโ€โ™‚๏ธ ๐Ÿงœโ€โ™€๏ธ ๐Ÿงœโ€โ™‚๏ธ ๐Ÿงšโ€โ™€๏ธ ๐Ÿงšโ€โ™‚๏ธ ๐Ÿ‘ผ ๐Ÿคฐ ๐Ÿคฑ ๐Ÿ™‡โ€โ™€๏ธ ๐Ÿ™‡โ€โ™‚๏ธ ๐Ÿ’โ€โ™€๏ธ ๐Ÿ’โ€โ™‚๏ธ ๐Ÿ™…โ€โ™€๏ธ ๐Ÿ™…โ€โ™‚๏ธ ๐Ÿ™†โ€โ™€๏ธ ๐Ÿ™†โ€โ™‚๏ธ ๐Ÿ™‹โ€โ™€๏ธ ๐Ÿ™‹โ€โ™‚๏ธ ๐Ÿคฆโ€โ™€๏ธ ๐Ÿคฆโ€โ™‚๏ธ ๐Ÿคทโ€โ™€๏ธ ๐Ÿคทโ€โ™‚๏ธ ๐Ÿ™Žโ€โ™€๏ธ ๐Ÿ™Žโ€โ™‚๏ธ ๐Ÿ™โ€โ™€๏ธ ๐Ÿ™โ€โ™‚๏ธ ๐Ÿ’‡โ€โ™€๏ธ ๐Ÿ’‡โ€โ™‚๏ธ ๐Ÿ’†โ€โ™€๏ธ ๐Ÿ’†โ€โ™‚๏ธ ๐Ÿง–โ€โ™€๏ธ ๐Ÿง–โ€โ™‚๏ธ ๐Ÿ’… ๐Ÿคณ ๐Ÿ’ƒ ๐Ÿ•บ ๐Ÿ‘ฏโ€โ™€๏ธ ๐Ÿ‘ฏโ€โ™‚๏ธ ๐Ÿ•ด ๐Ÿšถโ€โ™€๏ธ ๐Ÿšถโ€โ™‚๏ธ ๐Ÿƒโ€โ™€๏ธ ๐Ÿƒโ€โ™‚๏ธ ๐Ÿ‘ซ ๐Ÿ‘ญ ๐Ÿ‘ฌ ๐Ÿ’‘ ๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ ๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ ๐Ÿ’ ๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ ๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ ๐Ÿ‘ช ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง ๐Ÿ‘ฉโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘ง ๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง ๐Ÿ‘จโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘ง ๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง ๐Ÿคฒ ๐Ÿ‘ ๐Ÿ™Œ ๐Ÿ‘ ๐Ÿค ๐Ÿ‘ ๐Ÿ‘Ž ๐Ÿ‘Š โœŠ ๐Ÿค› ๐Ÿคœ ๐Ÿคž โœŒ๏ธ ๐ŸคŸ ๐Ÿค˜ ๐Ÿ‘Œ ๐Ÿ‘ˆ ๐Ÿ‘‰ ๐Ÿ‘† ๐Ÿ‘‡ โ˜๏ธ โœ‹ ๐Ÿคš ๐Ÿ– ๐Ÿ–– ๐Ÿ‘‹ ๐Ÿค™ ๐Ÿ’ช ๐Ÿฆต ๐Ÿฆถ ๐Ÿ–• โœ๏ธ ๐Ÿ™ ๐Ÿ’ ๐Ÿ’„ ๐Ÿ’‹ ๐Ÿ‘„ ๐Ÿ‘… ๐Ÿ‘‚ ๐Ÿ‘ƒ ๐Ÿ‘ฃ ๐Ÿ‘ ๐Ÿ‘€ ๐Ÿง  ๐Ÿฆด ๐Ÿฆท ๐Ÿ—ฃ ๐Ÿ‘ค ๐Ÿ‘ฅ +๐Ÿงฅ ๐Ÿ‘š ๐Ÿ‘• ๐Ÿ‘– ๐Ÿ‘” ๐Ÿ‘— ๐Ÿ‘™ ๐Ÿ‘˜ ๐Ÿ‘  ๐Ÿ‘ก ๐Ÿ‘ข ๐Ÿ‘ž ๐Ÿ‘Ÿ ๐Ÿฅพ ๐Ÿฅฟ ๐Ÿงฆ ๐Ÿงค ๐Ÿงฃ ๐ŸŽฉ ๐Ÿงข ๐Ÿ‘’ ๐ŸŽ“ โ›‘ ๐Ÿ‘‘ ๐Ÿ‘ ๐Ÿ‘› ๐Ÿ‘œ ๐Ÿ’ผ ๐ŸŽ’ ๐Ÿ‘“ ๐Ÿ•ถ ๐Ÿฅฝ ๐Ÿฅผ ๐ŸŒ‚ ๐Ÿงต ๐Ÿงถ +๐Ÿ‘ถ๐Ÿป ๐Ÿ‘ฆ๐Ÿป ๐Ÿ‘ง๐Ÿป ๐Ÿ‘จ๐Ÿป ๐Ÿ‘ฉ๐Ÿป ๐Ÿ‘ฑ๐Ÿปโ€โ™€๏ธ ๐Ÿ‘ฑ๐Ÿป ๐Ÿ‘ด๐Ÿป ๐Ÿ‘ต๐Ÿป ๐Ÿ‘ฒ๐Ÿป ๐Ÿ‘ณ๐Ÿปโ€โ™€๏ธ ๐Ÿ‘ณ๐Ÿป ๐Ÿ‘ฎ๐Ÿปโ€โ™€๏ธ ๐Ÿ‘ฎ๐Ÿป ๐Ÿ‘ท๐Ÿปโ€โ™€๏ธ ๐Ÿ‘ท๐Ÿป ๐Ÿ’‚๐Ÿปโ€โ™€๏ธ ๐Ÿ’‚๐Ÿป ๐Ÿ•ต๐Ÿปโ€โ™€๏ธ ๐Ÿ•ต๐Ÿป ๐Ÿ‘ฉ๐Ÿปโ€โš•๏ธ ๐Ÿ‘จ๐Ÿปโ€โš•๏ธ ๐Ÿ‘ฉ๐Ÿปโ€๐ŸŒพ ๐Ÿ‘จ๐Ÿปโ€๐ŸŒพ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿณ ๐Ÿ‘จ๐Ÿปโ€๐Ÿณ ๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽ“ ๐Ÿ‘จ๐Ÿปโ€๐ŸŽ“ ๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽค ๐Ÿ‘จ๐Ÿปโ€๐ŸŽค ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿซ ๐Ÿ‘จ๐Ÿปโ€๐Ÿซ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿญ ๐Ÿ‘จ๐Ÿปโ€๐Ÿญ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ผ ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ผ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ง ๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ง ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ฌ ๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ฌ ๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽจ ๐Ÿ‘จ๐Ÿปโ€๐ŸŽจ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš’ ๐Ÿ‘จ๐Ÿปโ€๐Ÿš’ ๐Ÿ‘ฉ๐Ÿปโ€โœˆ๏ธ ๐Ÿ‘จ๐Ÿปโ€โœˆ๏ธ ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš€ ๐Ÿ‘จ๐Ÿปโ€๐Ÿš€ ๐Ÿ‘ฉ๐Ÿปโ€โš–๏ธ ๐Ÿ‘จ๐Ÿปโ€โš–๏ธ ๐Ÿคถ๐Ÿป ๐ŸŽ…๐Ÿป ๐Ÿ‘ธ๐Ÿป ๐Ÿคด๐Ÿป ๐Ÿ‘ฐ๐Ÿป ๐Ÿคต๐Ÿป ๐Ÿ‘ผ๐Ÿป ๐Ÿคฐ๐Ÿป ๐Ÿ™‡๐Ÿปโ€โ™€๏ธ ๐Ÿ™‡๐Ÿป ๐Ÿ’๐Ÿป ๐Ÿ’๐Ÿปโ€โ™‚๏ธ ๐Ÿ™…๐Ÿป ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ ๐Ÿ™†๐Ÿป ๐Ÿ™†๐Ÿปโ€โ™‚๏ธ ๐Ÿ™‹๐Ÿป ๐Ÿ™‹๐Ÿปโ€โ™‚๏ธ ๐Ÿคฆ๐Ÿปโ€โ™€๏ธ ๐Ÿคฆ๐Ÿปโ€โ™‚๏ธ ๐Ÿคท๐Ÿปโ€โ™€๏ธ ๐Ÿคท๐Ÿปโ€โ™‚๏ธ ๐Ÿ™Ž๐Ÿป ๐Ÿ™Ž๐Ÿปโ€โ™‚๏ธ ๐Ÿ™๐Ÿป ๐Ÿ™๐Ÿปโ€โ™‚๏ธ ๐Ÿ’‡๐Ÿป ๐Ÿ’‡๐Ÿปโ€โ™‚๏ธ ๐Ÿ’†๐Ÿป ๐Ÿ’†๐Ÿปโ€โ™‚๏ธ ๐Ÿ•ด๐Ÿป ๐Ÿ’ƒ๐Ÿป ๐Ÿ•บ๐Ÿป ๐Ÿšถ๐Ÿปโ€โ™€๏ธ ๐Ÿšถ๐Ÿป ๐Ÿƒ๐Ÿปโ€โ™€๏ธ ๐Ÿƒ๐Ÿป ๐Ÿคฒ๐Ÿป ๐Ÿ‘๐Ÿป ๐Ÿ™Œ๐Ÿป ๐Ÿ‘๐Ÿป ๐Ÿ™๐Ÿป ๐Ÿ‘๐Ÿป ๐Ÿ‘Ž๐Ÿป ๐Ÿ‘Š๐Ÿป โœŠ๐Ÿป ๐Ÿค›๐Ÿป ๐Ÿคœ๐Ÿป ๐Ÿคž๐Ÿป โœŒ๐Ÿป ๐ŸคŸ๐Ÿป ๐Ÿค˜๐Ÿป ๐Ÿ‘Œ๐Ÿป ๐Ÿ‘ˆ๐Ÿป ๐Ÿ‘‰๐Ÿป ๐Ÿ‘†๐Ÿป ๐Ÿ‘‡๐Ÿป โ˜๐Ÿป โœ‹๐Ÿป ๐Ÿคš๐Ÿป ๐Ÿ–๐Ÿป ๐Ÿ––๐Ÿป ๐Ÿ‘‹๐Ÿป ๐Ÿค™๐Ÿป ๐Ÿ’ช๐Ÿป ๐Ÿ–•๐Ÿป โœ๐Ÿป ๐Ÿคณ๐Ÿป ๐Ÿ’…๐Ÿป ๐Ÿ‘‚๐Ÿป ๐Ÿ‘ƒ๐Ÿป +๐Ÿ‘ถ๐Ÿผ ๐Ÿ‘ฆ๐Ÿผ ๐Ÿ‘ง๐Ÿผ ๐Ÿ‘จ๐Ÿผ ๐Ÿ‘ฉ๐Ÿผ ๐Ÿ‘ฑ๐Ÿผโ€โ™€๏ธ ๐Ÿ‘ฑ๐Ÿผ ๐Ÿ‘ด๐Ÿผ ๐Ÿ‘ต๐Ÿผ ๐Ÿ‘ฒ๐Ÿผ ๐Ÿ‘ณ๐Ÿผโ€โ™€๏ธ ๐Ÿ‘ณ๐Ÿผ ๐Ÿ‘ฎ๐Ÿผโ€โ™€๏ธ ๐Ÿ‘ฎ๐Ÿผ ๐Ÿ‘ท๐Ÿผโ€โ™€๏ธ ๐Ÿ‘ท๐Ÿผ ๐Ÿ’‚๐Ÿผโ€โ™€๏ธ ๐Ÿ’‚๐Ÿผ ๐Ÿ•ต๐Ÿผโ€โ™€๏ธ ๐Ÿ•ต๐Ÿผ ๐Ÿ‘ฉ๐Ÿผโ€โš•๏ธ ๐Ÿ‘จ๐Ÿผโ€โš•๏ธ ๐Ÿ‘ฉ๐Ÿผโ€๐ŸŒพ ๐Ÿ‘จ๐Ÿผโ€๐ŸŒพ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿณ ๐Ÿ‘จ๐Ÿผโ€๐Ÿณ ๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽ“ ๐Ÿ‘จ๐Ÿผโ€๐ŸŽ“ ๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽค ๐Ÿ‘จ๐Ÿผโ€๐ŸŽค ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ ๐Ÿ‘จ๐Ÿผโ€๐Ÿซ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿญ ๐Ÿ‘จ๐Ÿผโ€๐Ÿญ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป ๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ผ ๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ผ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ง ๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ง ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ฌ ๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ฌ ๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽจ ๐Ÿ‘จ๐Ÿผโ€๐ŸŽจ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš’ ๐Ÿ‘จ๐Ÿผโ€๐Ÿš’ ๐Ÿ‘ฉ๐Ÿผโ€โœˆ๏ธ ๐Ÿ‘จ๐Ÿผโ€โœˆ๏ธ ๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš€ ๐Ÿ‘จ๐Ÿผโ€๐Ÿš€ ๐Ÿ‘ฉ๐Ÿผโ€โš–๏ธ ๐Ÿ‘จ๐Ÿผโ€โš–๏ธ ๐Ÿคถ๐Ÿผ ๐ŸŽ…๐Ÿผ ๐Ÿ‘ธ๐Ÿผ ๐Ÿคด๐Ÿผ ๐Ÿ‘ฐ๐Ÿผ ๐Ÿคต๐Ÿผ ๐Ÿ‘ผ๐Ÿผ ๐Ÿคฐ๐Ÿผ ๐Ÿ™‡๐Ÿผโ€โ™€๏ธ ๐Ÿ™‡๐Ÿผ ๐Ÿ’๐Ÿผ ๐Ÿ’๐Ÿผโ€โ™‚๏ธ ๐Ÿ™…๐Ÿผ ๐Ÿ™…๐Ÿผโ€โ™‚๏ธ ๐Ÿ™†๐Ÿผ ๐Ÿ™†๐Ÿผโ€โ™‚๏ธ ๐Ÿ™‹๐Ÿผ ๐Ÿ™‹๐Ÿผโ€โ™‚๏ธ ๐Ÿคฆ๐Ÿผโ€โ™€๏ธ ๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ ๐Ÿคท๐Ÿผโ€โ™€๏ธ ๐Ÿคท๐Ÿผโ€โ™‚๏ธ ๐Ÿ™Ž๐Ÿผ ๐Ÿ™Ž๐Ÿผโ€โ™‚๏ธ ๐Ÿ™๐Ÿผ ๐Ÿ™๐Ÿผโ€โ™‚๏ธ ๐Ÿ’‡๐Ÿผ ๐Ÿ’‡๐Ÿผโ€โ™‚๏ธ ๐Ÿ’†๐Ÿผ ๐Ÿ’†๐Ÿผโ€โ™‚๏ธ ๐Ÿ•ด๐Ÿผ ๐Ÿ’ƒ๐Ÿผ ๐Ÿ•บ๐Ÿผ ๐Ÿšถ๐Ÿผโ€โ™€๏ธ ๐Ÿšถ๐Ÿผ ๐Ÿƒ๐Ÿผโ€โ™€๏ธ ๐Ÿƒ๐Ÿผ ๐Ÿคฒ๐Ÿผ ๐Ÿ‘๐Ÿผ ๐Ÿ™Œ๐Ÿผ ๐Ÿ‘๐Ÿผ ๐Ÿ™๐Ÿผ ๐Ÿ‘๐Ÿผ ๐Ÿ‘Ž๐Ÿผ ๐Ÿ‘Š๐Ÿผ โœŠ๐Ÿผ ๐Ÿค›๐Ÿผ ๐Ÿคœ๐Ÿผ ๐Ÿคž๐Ÿผ โœŒ๐Ÿผ ๐ŸคŸ๐Ÿผ ๐Ÿค˜๐Ÿผ ๐Ÿ‘Œ๐Ÿผ ๐Ÿ‘ˆ๐Ÿผ ๐Ÿ‘‰๐Ÿผ ๐Ÿ‘†๐Ÿผ ๐Ÿ‘‡๐Ÿผ โ˜๐Ÿผ โœ‹๐Ÿผ ๐Ÿคš๐Ÿผ ๐Ÿ–๐Ÿผ ๐Ÿ––๐Ÿผ ๐Ÿ‘‹๐Ÿผ ๐Ÿค™๐Ÿผ ๐Ÿ’ช๐Ÿผ ๐Ÿ–•๐Ÿผ โœ๐Ÿผ ๐Ÿคณ๐Ÿผ ๐Ÿ’…๐Ÿผ ๐Ÿ‘‚๐Ÿผ ๐Ÿ‘ƒ๐Ÿผ +๐Ÿ‘ถ๐Ÿฝ ๐Ÿ‘ฆ๐Ÿฝ ๐Ÿ‘ง๐Ÿฝ ๐Ÿ‘จ๐Ÿฝ ๐Ÿ‘ฉ๐Ÿฝ ๐Ÿ‘ฑ๐Ÿฝโ€โ™€๏ธ ๐Ÿ‘ฑ๐Ÿฝ ๐Ÿ‘ด๐Ÿฝ ๐Ÿ‘ต๐Ÿฝ ๐Ÿ‘ฒ๐Ÿฝ ๐Ÿ‘ณ๐Ÿฝโ€โ™€๏ธ ๐Ÿ‘ณ๐Ÿฝ ๐Ÿ‘ฎ๐Ÿฝโ€โ™€๏ธ ๐Ÿ‘ฎ๐Ÿฝ ๐Ÿ‘ท๐Ÿฝโ€โ™€๏ธ ๐Ÿ‘ท๐Ÿฝ ๐Ÿ’‚๐Ÿฝโ€โ™€๏ธ ๐Ÿ’‚๐Ÿฝ ๐Ÿ•ต๐Ÿฝโ€โ™€๏ธ ๐Ÿ•ต๐Ÿฝ ๐Ÿ‘ฉ๐Ÿฝโ€โš•๏ธ ๐Ÿ‘จ๐Ÿฝโ€โš•๏ธ ๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŒพ ๐Ÿ‘จ๐Ÿฝโ€๐ŸŒพ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿณ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿณ ๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽ“ ๐Ÿ‘จ๐Ÿฝโ€๐ŸŽ“ ๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽค ๐Ÿ‘จ๐Ÿฝโ€๐ŸŽค ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿซ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿซ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿญ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿญ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป ๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ป ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ผ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ผ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ง ๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ง ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ฌ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ฌ ๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽจ ๐Ÿ‘จ๐Ÿฝโ€๐ŸŽจ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš’ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿš’ ๐Ÿ‘ฉ๐Ÿฝโ€โœˆ๏ธ ๐Ÿ‘จ๐Ÿฝโ€โœˆ๏ธ ๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš€ ๐Ÿ‘จ๐Ÿฝโ€๐Ÿš€ ๐Ÿ‘ฉ๐Ÿฝโ€โš–๏ธ ๐Ÿ‘จ๐Ÿฝโ€โš–๏ธ ๐Ÿคถ๐Ÿฝ ๐ŸŽ…๐Ÿฝ ๐Ÿ‘ธ๐Ÿฝ ๐Ÿคด๐Ÿฝ ๐Ÿ‘ฐ๐Ÿฝ ๐Ÿคต๐Ÿฝ ๐Ÿ‘ผ๐Ÿฝ ๐Ÿคฐ๐Ÿฝ ๐Ÿ™‡๐Ÿฝโ€โ™€๏ธ ๐Ÿ™‡๐Ÿฝ ๐Ÿ’๐Ÿฝ ๐Ÿ’๐Ÿฝโ€โ™‚๏ธ ๐Ÿ™…๐Ÿฝ ๐Ÿ™…๐Ÿฝโ€โ™‚๏ธ ๐Ÿ™†๐Ÿฝ ๐Ÿ™†๐Ÿฝโ€โ™‚๏ธ ๐Ÿ™‹๐Ÿฝ ๐Ÿ™‹๐Ÿฝโ€โ™‚๏ธ ๐Ÿคฆ๐Ÿฝโ€โ™€๏ธ ๐Ÿคฆ๐Ÿฝโ€โ™‚๏ธ ๐Ÿคท๐Ÿฝโ€โ™€๏ธ ๐Ÿคท๐Ÿฝโ€โ™‚๏ธ ๐Ÿ™Ž๐Ÿฝ ๐Ÿ™Ž๐Ÿฝโ€โ™‚๏ธ ๐Ÿ™๐Ÿฝ ๐Ÿ™๐Ÿฝโ€โ™‚๏ธ ๐Ÿ’‡๐Ÿฝ ๐Ÿ’‡๐Ÿฝโ€โ™‚๏ธ ๐Ÿ’†๐Ÿฝ ๐Ÿ’†๐Ÿฝโ€โ™‚๏ธ ๐Ÿ•ด๐Ÿผ ๐Ÿ’ƒ๐Ÿฝ ๐Ÿ•บ๐Ÿฝ ๐Ÿšถ๐Ÿฝโ€โ™€๏ธ ๐Ÿšถ๐Ÿฝ ๐Ÿƒ๐Ÿฝโ€โ™€๏ธ ๐Ÿƒ๐Ÿฝ ๐Ÿคฒ๐Ÿฝ ๐Ÿ‘๐Ÿฝ ๐Ÿ™Œ๐Ÿฝ ๐Ÿ‘๐Ÿฝ ๐Ÿ™๐Ÿฝ ๐Ÿ‘๐Ÿฝ ๐Ÿ‘Ž๐Ÿฝ ๐Ÿ‘Š๐Ÿฝ โœŠ๐Ÿฝ ๐Ÿค›๐Ÿฝ ๐Ÿคœ๐Ÿฝ ๐Ÿคž๐Ÿฝ โœŒ๐Ÿฝ ๐ŸคŸ๐Ÿฝ ๐Ÿค˜๐Ÿฝ ๐Ÿ‘Œ๐Ÿฝ ๐Ÿ‘ˆ๐Ÿฝ ๐Ÿ‘‰๐Ÿฝ ๐Ÿ‘†๐Ÿฝ ๐Ÿ‘‡๐Ÿฝ โ˜๐Ÿฝ โœ‹๐Ÿฝ ๐Ÿคš๐Ÿฝ ๐Ÿ–๐Ÿฝ ๐Ÿ––๐Ÿฝ ๐Ÿ‘‹๐Ÿฝ ๐Ÿค™๐Ÿฝ ๐Ÿ’ช๐Ÿฝ ๐Ÿ–•๐Ÿฝ โœ๐Ÿฝ ๐Ÿคณ๐Ÿฝ ๐Ÿ’…๐Ÿฝ ๐Ÿ‘‚๐Ÿฝ ๐Ÿ‘ƒ๐Ÿฝ +๐Ÿ‘ถ๐Ÿพ ๐Ÿ‘ฆ๐Ÿพ ๐Ÿ‘ง๐Ÿพ ๐Ÿ‘จ๐Ÿพ ๐Ÿ‘ฉ๐Ÿพ ๐Ÿ‘ฑ๐Ÿพโ€โ™€๏ธ ๐Ÿ‘ฑ๐Ÿพ ๐Ÿ‘ด๐Ÿพ ๐Ÿ‘ต๐Ÿพ ๐Ÿ‘ฒ๐Ÿพ ๐Ÿ‘ณ๐Ÿพโ€โ™€๏ธ ๐Ÿ‘ณ๐Ÿพ ๐Ÿ‘ฎ๐Ÿพโ€โ™€๏ธ ๐Ÿ‘ฎ๐Ÿพ ๐Ÿ‘ท๐Ÿพโ€โ™€๏ธ ๐Ÿ‘ท๐Ÿพ ๐Ÿ’‚๐Ÿพโ€โ™€๏ธ ๐Ÿ’‚๐Ÿพ ๐Ÿ•ต๐Ÿพโ€โ™€๏ธ ๐Ÿ•ต๐Ÿพ ๐Ÿ‘ฉ๐Ÿพโ€โš•๏ธ ๐Ÿ‘จ๐Ÿพโ€โš•๏ธ ๐Ÿ‘ฉ๐Ÿพโ€๐ŸŒพ ๐Ÿ‘จ๐Ÿพโ€๐ŸŒพ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿณ ๐Ÿ‘จ๐Ÿพโ€๐Ÿณ ๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽ“ ๐Ÿ‘จ๐Ÿพโ€๐ŸŽ“ ๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽค ๐Ÿ‘จ๐Ÿพโ€๐ŸŽค ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ ๐Ÿ‘จ๐Ÿพโ€๐Ÿซ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿญ ๐Ÿ‘จ๐Ÿพโ€๐Ÿญ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ป ๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ป ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ผ ๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ผ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ง ๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ง ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ฌ ๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ฌ ๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽจ ๐Ÿ‘จ๐Ÿพโ€๐ŸŽจ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš’ ๐Ÿ‘จ๐Ÿพโ€๐Ÿš’ ๐Ÿ‘ฉ๐Ÿพโ€โœˆ๏ธ ๐Ÿ‘จ๐Ÿพโ€โœˆ๏ธ ๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš€ ๐Ÿ‘จ๐Ÿพโ€๐Ÿš€ ๐Ÿ‘ฉ๐Ÿพโ€โš–๏ธ ๐Ÿ‘จ๐Ÿพโ€โš–๏ธ ๐Ÿคถ๐Ÿพ ๐ŸŽ…๐Ÿพ ๐Ÿ‘ธ๐Ÿพ ๐Ÿคด๐Ÿพ ๐Ÿ‘ฐ๐Ÿพ ๐Ÿคต๐Ÿพ ๐Ÿ‘ผ๐Ÿพ ๐Ÿคฐ๐Ÿพ ๐Ÿ™‡๐Ÿพโ€โ™€๏ธ ๐Ÿ™‡๐Ÿพ ๐Ÿ’๐Ÿพ ๐Ÿ’๐Ÿพโ€โ™‚๏ธ ๐Ÿ™…๐Ÿพ ๐Ÿ™…๐Ÿพโ€โ™‚๏ธ ๐Ÿ™†๐Ÿพ ๐Ÿ™†๐Ÿพโ€โ™‚๏ธ ๐Ÿ™‹๐Ÿพ ๐Ÿ™‹๐Ÿพโ€โ™‚๏ธ ๐Ÿคฆ๐Ÿพโ€โ™€๏ธ ๐Ÿคฆ๐Ÿพโ€โ™‚๏ธ ๐Ÿคท๐Ÿพโ€โ™€๏ธ ๐Ÿคท๐Ÿพโ€โ™‚๏ธ ๐Ÿ™Ž๐Ÿพ ๐Ÿ™Ž๐Ÿพโ€โ™‚๏ธ ๐Ÿ™๐Ÿพ ๐Ÿ™๐Ÿพโ€โ™‚๏ธ ๐Ÿ’‡๐Ÿพ ๐Ÿ’‡๐Ÿพโ€โ™‚๏ธ ๐Ÿ’†๐Ÿพ ๐Ÿ’†๐Ÿพโ€โ™‚๏ธ ๐Ÿ•ด๐Ÿพ ๐Ÿ’ƒ๐Ÿพ ๐Ÿ•บ๐Ÿพ ๐Ÿšถ๐Ÿพโ€โ™€๏ธ ๐Ÿšถ๐Ÿพ ๐Ÿƒ๐Ÿพโ€โ™€๏ธ ๐Ÿƒ๐Ÿพ ๐Ÿคฒ๐Ÿพ ๐Ÿ‘๐Ÿพ ๐Ÿ™Œ๐Ÿพ ๐Ÿ‘๐Ÿพ ๐Ÿ™๐Ÿพ ๐Ÿ‘๐Ÿพ ๐Ÿ‘Ž๐Ÿพ ๐Ÿ‘Š๐Ÿพ โœŠ๐Ÿพ ๐Ÿค›๐Ÿพ ๐Ÿคœ๐Ÿพ ๐Ÿคž๐Ÿพ โœŒ๐Ÿพ ๐ŸคŸ๐Ÿพ ๐Ÿค˜๐Ÿพ ๐Ÿ‘Œ๐Ÿพ ๐Ÿ‘ˆ๐Ÿพ ๐Ÿ‘‰๐Ÿพ ๐Ÿ‘†๐Ÿพ ๐Ÿ‘‡๐Ÿพ โ˜๐Ÿพ โœ‹๐Ÿพ ๐Ÿคš๐Ÿพ ๐Ÿ–๐Ÿพ ๐Ÿ––๐Ÿพ ๐Ÿ‘‹๐Ÿพ ๐Ÿค™๐Ÿพ ๐Ÿ’ช๐Ÿพ ๐Ÿ–•๐Ÿพ โœ๐Ÿพ ๐Ÿคณ๐Ÿพ ๐Ÿ’…๐Ÿพ ๐Ÿ‘‚๐Ÿพ ๐Ÿ‘ƒ๐Ÿพ +๐Ÿ‘ถ๐Ÿฟ ๐Ÿ‘ฆ๐Ÿฟ ๐Ÿ‘ง๐Ÿฟ ๐Ÿ‘จ๐Ÿฟ ๐Ÿ‘ฉ๐Ÿฟ ๐Ÿ‘ฑ๐Ÿฟโ€โ™€๏ธ ๐Ÿ‘ฑ๐Ÿฟ ๐Ÿ‘ด๐Ÿฟ ๐Ÿ‘ต๐Ÿฟ ๐Ÿ‘ฒ๐Ÿฟ ๐Ÿ‘ณ๐Ÿฟโ€โ™€๏ธ ๐Ÿ‘ณ๐Ÿฟ ๐Ÿ‘ฎ๐Ÿฟโ€โ™€๏ธ ๐Ÿ‘ฎ๐Ÿฟ ๐Ÿ‘ท๐Ÿฟโ€โ™€๏ธ ๐Ÿ‘ท๐Ÿฟ ๐Ÿ’‚๐Ÿฟโ€โ™€๏ธ ๐Ÿ’‚๐Ÿฟ ๐Ÿ•ต๐Ÿฟโ€โ™€๏ธ ๐Ÿ•ต๐Ÿฟ ๐Ÿ‘ฉ๐Ÿฟโ€โš•๏ธ ๐Ÿ‘จ๐Ÿฟโ€โš•๏ธ ๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŒพ ๐Ÿ‘จ๐Ÿฟโ€๐ŸŒพ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿณ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿณ ๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽ“ ๐Ÿ‘จ๐Ÿฟโ€๐ŸŽ“ ๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽค ๐Ÿ‘จ๐Ÿฟโ€๐ŸŽค ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿซ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿซ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿญ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿญ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ป ๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ป ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ผ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ผ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ง ๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ง ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ฌ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ฌ ๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽจ ๐Ÿ‘จ๐Ÿฟโ€๐ŸŽจ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš’ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿš’ ๐Ÿ‘ฉ๐Ÿฟโ€โœˆ๏ธ ๐Ÿ‘จ๐Ÿฟโ€โœˆ๏ธ ๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš€ ๐Ÿ‘จ๐Ÿฟโ€๐Ÿš€ ๐Ÿ‘ฉ๐Ÿฟโ€โš–๏ธ ๐Ÿ‘จ๐Ÿฟโ€โš–๏ธ ๐Ÿคถ๐Ÿฟ ๐ŸŽ…๐Ÿฟ ๐Ÿ‘ธ๐Ÿฟ ๐Ÿคด๐Ÿฟ ๐Ÿ‘ฐ๐Ÿฟ ๐Ÿคต๐Ÿฟ ๐Ÿ‘ผ๐Ÿฟ ๐Ÿคฐ๐Ÿฟ ๐Ÿ™‡๐Ÿฟโ€โ™€๏ธ ๐Ÿ™‡๐Ÿฟ ๐Ÿ’๐Ÿฟ ๐Ÿ’๐Ÿฟโ€โ™‚๏ธ ๐Ÿ™…๐Ÿฟ ๐Ÿ™…๐Ÿฟโ€โ™‚๏ธ ๐Ÿ™†๐Ÿฟ ๐Ÿ™†๐Ÿฟโ€โ™‚๏ธ ๐Ÿ™‹๐Ÿฟ ๐Ÿ™‹๐Ÿฟโ€โ™‚๏ธ ๐Ÿคฆ๐Ÿฟโ€โ™€๏ธ ๐Ÿคฆ๐Ÿฟโ€โ™‚๏ธ ๐Ÿคท๐Ÿฟโ€โ™€๏ธ ๐Ÿคท๐Ÿฟโ€โ™‚๏ธ ๐Ÿ™Ž๐Ÿฟ ๐Ÿ™Ž๐Ÿฟโ€โ™‚๏ธ ๐Ÿ™๐Ÿฟ ๐Ÿ™๐Ÿฟโ€โ™‚๏ธ ๐Ÿ’‡๐Ÿฟ ๐Ÿ’‡๐Ÿฟโ€โ™‚๏ธ ๐Ÿ’†๐Ÿฟ ๐Ÿ’†๐Ÿฟโ€โ™‚๏ธ ๐Ÿ•ด๐Ÿฟ ๐Ÿ’ƒ๐Ÿฟ ๐Ÿ•บ๐Ÿฟ ๐Ÿšถ๐Ÿฟโ€โ™€๏ธ ๐Ÿšถ๐Ÿฟ ๐Ÿƒ๐Ÿฟโ€โ™€๏ธ ๐Ÿƒ๐Ÿฟ ๐Ÿคฒ๐Ÿฟ ๐Ÿ‘๐Ÿฟ ๐Ÿ™Œ๐Ÿฟ ๐Ÿ‘๐Ÿฟ ๐Ÿ™๐Ÿฟ ๐Ÿ‘๐Ÿฟ ๐Ÿ‘Ž๐Ÿฟ ๐Ÿ‘Š๐Ÿฟ โœŠ๐Ÿฟ ๐Ÿค›๐Ÿฟ ๐Ÿคœ๐Ÿฟ ๐Ÿคž๐Ÿฟ โœŒ๐Ÿฟ ๐ŸคŸ๐Ÿฟ ๐Ÿค˜๐Ÿฟ ๐Ÿ‘Œ๐Ÿฟ ๐Ÿ‘ˆ๐Ÿฟ ๐Ÿ‘‰๐Ÿฟ ๐Ÿ‘†๐Ÿฟ ๐Ÿ‘‡๐Ÿฟ โ˜๐Ÿฟ โœ‹๐Ÿฟ ๐Ÿคš๐Ÿฟ ๐Ÿ–๐Ÿฟ ๐Ÿ––๐Ÿฟ ๐Ÿ‘‹๐Ÿฟ ๐Ÿค™๐Ÿฟ ๐Ÿ’ช๐Ÿฟ ๐Ÿ–•๐Ÿฟ โœ๐Ÿฟ ๐Ÿคณ๐Ÿฟ ๐Ÿ’…๐Ÿฟ ๐Ÿ‘‚๐Ÿฟ ๐Ÿ‘ƒ๐Ÿฟ +๐Ÿถ ๐Ÿฑ ๐Ÿญ ๐Ÿน ๐Ÿฐ ๐ŸฆŠ ๐Ÿฆ ๐Ÿป ๐Ÿผ ๐Ÿฆ˜ ๐Ÿฆก ๐Ÿจ ๐Ÿฏ ๐Ÿฆ ๐Ÿฎ ๐Ÿท ๐Ÿฝ ๐Ÿธ ๐Ÿต ๐Ÿ™ˆ ๐Ÿ™‰ ๐Ÿ™Š ๐Ÿ’ ๐Ÿ” ๐Ÿง ๐Ÿฆ ๐Ÿค ๐Ÿฃ ๐Ÿฅ ๐Ÿฆ† ๐Ÿฆข ๐Ÿฆ… ๐Ÿฆ‰ ๐Ÿฆš ๐Ÿฆœ ๐Ÿฆ‡ ๐Ÿบ ๐Ÿ— ๐Ÿด ๐Ÿฆ„ ๐Ÿ ๐Ÿ› ๐Ÿฆ‹ ๐ŸŒ ๐Ÿš ๐Ÿž ๐Ÿœ ๐Ÿฆ— ๐Ÿ•ท ๐Ÿ•ธ ๐Ÿฆ‚ ๐ŸฆŸ ๐Ÿฆ  ๐Ÿข ๐Ÿ ๐ŸฆŽ ๐Ÿฆ– ๐Ÿฆ• ๐Ÿ™ ๐Ÿฆ‘ ๐Ÿฆ ๐Ÿฆ€ ๐Ÿก ๐Ÿ  ๐ŸŸ ๐Ÿฌ ๐Ÿณ ๐Ÿ‹ ๐Ÿฆˆ ๐ŸŠ ๐Ÿ… ๐Ÿ† ๐Ÿฆ“ ๐Ÿฆ ๐Ÿ˜ ๐Ÿฆ ๐Ÿฆ› ๐Ÿช ๐Ÿซ ๐Ÿฆ™ ๐Ÿฆ’ ๐Ÿƒ ๐Ÿ‚ ๐Ÿ„ ๐ŸŽ ๐Ÿ– ๐Ÿ ๐Ÿ‘ ๐Ÿ ๐ŸฆŒ ๐Ÿ• ๐Ÿฉ ๐Ÿˆ ๐Ÿ“ ๐Ÿฆƒ ๐Ÿ•Š ๐Ÿ‡ ๐Ÿ ๐Ÿ€ ๐Ÿฟ ๐Ÿฆ” ๐Ÿพ ๐Ÿ‰ ๐Ÿฒ ๐ŸŒต ๐ŸŽ„ ๐ŸŒฒ ๐ŸŒณ ๐ŸŒด ๐ŸŒฑ ๐ŸŒฟ โ˜˜๏ธ ๐Ÿ€ ๐ŸŽ ๐ŸŽ‹ ๐Ÿƒ ๐Ÿ‚ ๐Ÿ ๐Ÿ„ ๐ŸŒพ ๐Ÿ’ ๐ŸŒท ๐ŸŒน ๐Ÿฅ€ ๐ŸŒบ ๐ŸŒธ ๐ŸŒผ ๐ŸŒป ๐ŸŒž ๐ŸŒ ๐ŸŒ› ๐ŸŒœ ๐ŸŒš ๐ŸŒ• ๐ŸŒ– ๐ŸŒ— ๐ŸŒ˜ ๐ŸŒ‘ ๐ŸŒ’ ๐ŸŒ“ ๐ŸŒ” ๐ŸŒ™ ๐ŸŒŽ ๐ŸŒ ๐ŸŒ ๐Ÿ’ซ โญ๏ธ ๐ŸŒŸ โœจ โšก๏ธ โ˜„๏ธ ๐Ÿ’ฅ ๐Ÿ”ฅ ๐ŸŒช ๐ŸŒˆ โ˜€๏ธ ๐ŸŒค โ›…๏ธ ๐ŸŒฅ โ˜๏ธ ๐ŸŒฆ ๐ŸŒง โ›ˆ ๐ŸŒฉ ๐ŸŒจ โ„๏ธ โ˜ƒ๏ธ โ›„๏ธ ๐ŸŒฌ ๐Ÿ’จ ๐Ÿ’ง ๐Ÿ’ฆ โ˜”๏ธ โ˜‚๏ธ ๐ŸŒŠ ๐ŸŒซ +๐Ÿ ๐ŸŽ ๐Ÿ ๐ŸŠ ๐Ÿ‹ ๐ŸŒ ๐Ÿ‰ ๐Ÿ‡ ๐Ÿ“ ๐Ÿˆ ๐Ÿ’ ๐Ÿ‘ ๐Ÿ ๐Ÿฅญ ๐Ÿฅฅ ๐Ÿฅ ๐Ÿ… ๐Ÿ† ๐Ÿฅ‘ ๐Ÿฅฆ ๐Ÿฅ’ ๐Ÿฅฌ ๐ŸŒถ ๐ŸŒฝ ๐Ÿฅ• ๐Ÿฅ” ๐Ÿ  ๐Ÿฅ ๐Ÿž ๐Ÿฅ– ๐Ÿฅจ ๐Ÿฅฏ ๐Ÿง€ ๐Ÿฅš ๐Ÿณ ๐Ÿฅž ๐Ÿฅ“ ๐Ÿฅฉ ๐Ÿ— ๐Ÿ– ๐ŸŒญ ๐Ÿ” ๐ŸŸ ๐Ÿ• ๐Ÿฅช ๐Ÿฅ™ ๐ŸŒฎ ๐ŸŒฏ ๐Ÿฅ— ๐Ÿฅ˜ ๐Ÿฅซ ๐Ÿ ๐Ÿœ ๐Ÿฒ ๐Ÿ› ๐Ÿฃ ๐Ÿฑ ๐ŸฅŸ ๐Ÿค ๐Ÿ™ ๐Ÿš ๐Ÿ˜ ๐Ÿฅ ๐Ÿฅฎ ๐Ÿฅ  ๐Ÿข ๐Ÿก ๐Ÿง ๐Ÿจ ๐Ÿฆ ๐Ÿฅง ๐Ÿฐ ๐ŸŽ‚ ๐Ÿฎ ๐Ÿญ ๐Ÿฌ ๐Ÿซ ๐Ÿฟ ๐Ÿง‚ ๐Ÿฉ ๐Ÿช ๐ŸŒฐ ๐Ÿฅœ ๐Ÿฏ ๐Ÿฅ› ๐Ÿผ โ˜•๏ธ ๐Ÿต ๐Ÿฅค ๐Ÿถ ๐Ÿบ ๐Ÿป ๐Ÿฅ‚ ๐Ÿท ๐Ÿฅƒ ๐Ÿธ ๐Ÿน ๐Ÿพ ๐Ÿฅ„ ๐Ÿด ๐Ÿฝ ๐Ÿฅฃ ๐Ÿฅก ๐Ÿฅข +โšฝ๏ธ ๐Ÿ€ ๐Ÿˆ โšพ๏ธ ๐ŸฅŽ ๐Ÿ ๐Ÿ‰ ๐ŸŽพ ๐Ÿฅ ๐ŸŽฑ ๐Ÿ“ ๐Ÿธ ๐Ÿฅ… ๐Ÿ’ ๐Ÿ‘ ๐Ÿฅ ๐Ÿ โ›ณ๏ธ ๐Ÿน ๐ŸŽฃ ๐ŸฅŠ ๐Ÿฅ‹ ๐ŸŽฝ โ›ธ ๐ŸฅŒ ๐Ÿ›ท ๐Ÿ›น ๐ŸŽฟ โ›ท ๐Ÿ‚ ๐Ÿ‹๏ธโ€โ™€๏ธ ๐Ÿ‹๐Ÿปโ€โ™€๏ธ ๐Ÿ‹๐Ÿผโ€โ™€๏ธ ๐Ÿ‹๐Ÿฝโ€โ™€๏ธ ๐Ÿ‹๐Ÿพโ€โ™€๏ธ ๐Ÿ‹๐Ÿฟโ€โ™€๏ธ ๐Ÿ‹๏ธโ€โ™‚๏ธ ๐Ÿ‹๐Ÿปโ€โ™‚๏ธ ๐Ÿ‹๐Ÿผโ€โ™‚๏ธ ๐Ÿ‹๐Ÿฝโ€โ™‚๏ธ ๐Ÿ‹๐Ÿพโ€โ™‚๏ธ ๐Ÿ‹๐Ÿฟโ€โ™‚๏ธ ๐Ÿคผโ€โ™€๏ธ ๐Ÿคผโ€โ™‚๏ธ ๐Ÿคธโ€โ™€๏ธ ๐Ÿคธ๐Ÿปโ€โ™€๏ธ ๐Ÿคธ๐Ÿผโ€โ™€๏ธ ๐Ÿคธ๐Ÿฝโ€โ™€๏ธ ๐Ÿคธ๐Ÿพโ€โ™€๏ธ ๐Ÿคธ๐Ÿฟโ€โ™€๏ธ ๐Ÿคธโ€โ™‚๏ธ ๐Ÿคธ๐Ÿปโ€โ™‚๏ธ ๐Ÿคธ๐Ÿผโ€โ™‚๏ธ ๐Ÿคธ๐Ÿฝโ€โ™‚๏ธ ๐Ÿคธ๐Ÿพโ€โ™‚๏ธ ๐Ÿคธ๐Ÿฟโ€โ™‚๏ธ โ›น๏ธโ€โ™€๏ธ โ›น๐Ÿปโ€โ™€๏ธ โ›น๐Ÿผโ€โ™€๏ธ โ›น๐Ÿฝโ€โ™€๏ธ โ›น๐Ÿพโ€โ™€๏ธ โ›น๐Ÿฟโ€โ™€๏ธ โ›น๏ธโ€โ™‚๏ธ โ›น๐Ÿปโ€โ™‚๏ธ โ›น๐Ÿผโ€โ™‚๏ธ โ›น๐Ÿฝโ€โ™‚๏ธ โ›น๐Ÿพโ€โ™‚๏ธ โ›น๐Ÿฟโ€โ™‚๏ธ ๐Ÿคบ ๐Ÿคพโ€โ™€๏ธ ๐Ÿคพ๐Ÿปโ€โ™€๏ธ ๐Ÿคพ๐Ÿผโ€โ™€๏ธ ๐Ÿคพ๐Ÿพโ€โ™€๏ธ ๐Ÿคพ๐Ÿพโ€โ™€๏ธ ๐Ÿคพ๐Ÿฟโ€โ™€๏ธ ๐Ÿคพโ€โ™‚๏ธ ๐Ÿคพ๐Ÿปโ€โ™‚๏ธ ๐Ÿคพ๐Ÿผโ€โ™‚๏ธ ๐Ÿคพ๐Ÿฝโ€โ™‚๏ธ ๐Ÿคพ๐Ÿพโ€โ™‚๏ธ ๐Ÿคพ๐Ÿฟโ€โ™‚๏ธ ๐ŸŒ๏ธโ€โ™€๏ธ ๐ŸŒ๐Ÿปโ€โ™€๏ธ ๐ŸŒ๐Ÿผโ€โ™€๏ธ ๐ŸŒ๐Ÿฝโ€โ™€๏ธ ๐ŸŒ๐Ÿพโ€โ™€๏ธ ๐ŸŒ๐Ÿฟโ€โ™€๏ธ ๐ŸŒ๏ธโ€โ™‚๏ธ ๐ŸŒ๐Ÿปโ€โ™‚๏ธ ๐ŸŒ๐Ÿผโ€โ™‚๏ธ ๐ŸŒ๐Ÿฝโ€โ™‚๏ธ ๐ŸŒ๐Ÿพโ€โ™‚๏ธ ๐ŸŒ๐Ÿฟโ€โ™‚๏ธ ๐Ÿ‡ ๐Ÿ‡๐Ÿป ๐Ÿ‡๐Ÿผ ๐Ÿ‡๐Ÿฝ ๐Ÿ‡๐Ÿพ ๐Ÿ‡๐Ÿฟ ๐Ÿง˜โ€โ™€๏ธ ๐Ÿง˜๐Ÿปโ€โ™€๏ธ ๐Ÿง˜๐Ÿผโ€โ™€๏ธ ๐Ÿง˜๐Ÿฝโ€โ™€๏ธ ๐Ÿง˜๐Ÿพโ€โ™€๏ธ ๐Ÿง˜๐Ÿฟโ€โ™€๏ธ ๐Ÿง˜โ€โ™‚๏ธ ๐Ÿง˜๐Ÿปโ€โ™‚๏ธ ๐Ÿง˜๐Ÿผโ€โ™‚๏ธ ๐Ÿง˜๐Ÿฝโ€โ™‚๏ธ ๐Ÿง˜๐Ÿพโ€โ™‚๏ธ ๐Ÿง˜๐Ÿฟโ€โ™‚๏ธ ๐Ÿ„โ€โ™€๏ธ ๐Ÿ„๐Ÿปโ€โ™€๏ธ ๐Ÿ„๐Ÿผโ€โ™€๏ธ ๐Ÿ„๐Ÿฝโ€โ™€๏ธ ๐Ÿ„๐Ÿพโ€โ™€๏ธ ๐Ÿ„๐Ÿฟโ€โ™€๏ธ ๐Ÿ„โ€โ™‚๏ธ ๐Ÿ„๐Ÿปโ€โ™‚๏ธ ๐Ÿ„๐Ÿผโ€โ™‚๏ธ ๐Ÿ„๐Ÿฝโ€โ™‚๏ธ ๐Ÿ„๐Ÿพโ€โ™‚๏ธ ๐Ÿ„๐Ÿฟโ€โ™‚๏ธ ๐ŸŠโ€โ™€๏ธ ๐ŸŠ๐Ÿปโ€โ™€๏ธ ๐ŸŠ๐Ÿผโ€โ™€๏ธ ๐ŸŠ๐Ÿฝโ€โ™€๏ธ ๐ŸŠ๐Ÿพโ€โ™€๏ธ ๐ŸŠ๐Ÿฟโ€โ™€๏ธ ๐ŸŠโ€โ™‚๏ธ ๐ŸŠ๐Ÿปโ€โ™‚๏ธ ๐ŸŠ๐Ÿผโ€โ™‚๏ธ ๐ŸŠ๐Ÿฝโ€โ™‚๏ธ ๐ŸŠ๐Ÿพโ€โ™‚๏ธ ๐ŸŠ๐Ÿฟโ€โ™‚๏ธ ๐Ÿคฝโ€โ™€๏ธ ๐Ÿคฝ๐Ÿปโ€โ™€๏ธ ๐Ÿคฝ๐Ÿผโ€โ™€๏ธ ๐Ÿคฝ๐Ÿฝโ€โ™€๏ธ ๐Ÿคฝ๐Ÿพโ€โ™€๏ธ ๐Ÿคฝ๐Ÿฟโ€โ™€๏ธ ๐Ÿคฝโ€โ™‚๏ธ ๐Ÿคฝ๐Ÿปโ€โ™‚๏ธ ๐Ÿคฝ๐Ÿผโ€โ™‚๏ธ ๐Ÿคฝ๐Ÿฝโ€โ™‚๏ธ ๐Ÿคฝ๐Ÿพโ€โ™‚๏ธ ๐Ÿคฝ๐Ÿฟโ€โ™‚๏ธ ๐Ÿšฃโ€โ™€๏ธ ๐Ÿšฃ๐Ÿปโ€โ™€๏ธ ๐Ÿšฃ๐Ÿผโ€โ™€๏ธ ๐Ÿšฃ๐Ÿฝโ€โ™€๏ธ ๐Ÿšฃ๐Ÿพโ€โ™€๏ธ ๐Ÿšฃ๐Ÿฟโ€โ™€๏ธ ๐Ÿšฃโ€โ™‚๏ธ ๐Ÿšฃ๐Ÿปโ€โ™‚๏ธ ๐Ÿšฃ๐Ÿผโ€โ™‚๏ธ ๐Ÿšฃ๐Ÿฝโ€โ™‚๏ธ ๐Ÿšฃ๐Ÿพโ€โ™‚๏ธ ๐Ÿšฃ๐Ÿฟโ€โ™‚๏ธ ๐Ÿง—โ€โ™€๏ธ ๐Ÿง—๐Ÿปโ€โ™€๏ธ ๐Ÿง—๐Ÿผโ€โ™€๏ธ ๐Ÿง—๐Ÿฝโ€โ™€๏ธ ๐Ÿง—๐Ÿพโ€โ™€๏ธ ๐Ÿง—๐Ÿฟโ€โ™€๏ธ ๐Ÿง—โ€โ™‚๏ธ ๐Ÿง—๐Ÿปโ€โ™‚๏ธ ๐Ÿง—๐Ÿผโ€โ™‚๏ธ ๐Ÿง—๐Ÿฝโ€โ™‚๏ธ ๐Ÿง—๐Ÿพโ€โ™‚๏ธ ๐Ÿง—๐Ÿฟโ€โ™‚๏ธ ๐Ÿšตโ€โ™€๏ธ ๐Ÿšต๐Ÿปโ€โ™€๏ธ ๐Ÿšต๐Ÿผโ€โ™€๏ธ ๐Ÿšต๐Ÿฝโ€โ™€๏ธ ๐Ÿšต๐Ÿพโ€โ™€๏ธ ๐Ÿšต๐Ÿฟโ€โ™€๏ธ ๐Ÿšตโ€โ™‚๏ธ ๐Ÿšต๐Ÿปโ€โ™‚๏ธ ๐Ÿšต๐Ÿผโ€โ™‚๏ธ ๐Ÿšต๐Ÿฝโ€โ™‚๏ธ ๐Ÿšต๐Ÿพโ€โ™‚๏ธ ๐Ÿšต๐Ÿฟโ€โ™‚๏ธ ๐Ÿšดโ€โ™€๏ธ ๐Ÿšด๐Ÿปโ€โ™€๏ธ ๐Ÿšด๐Ÿผโ€โ™€๏ธ ๐Ÿšด๐Ÿฝโ€โ™€๏ธ ๐Ÿšด๐Ÿพโ€โ™€๏ธ ๐Ÿšด๐Ÿฟโ€โ™€๏ธ ๐Ÿšดโ€โ™‚๏ธ ๐Ÿšด๐Ÿปโ€โ™‚๏ธ ๐Ÿšด๐Ÿผโ€โ™‚๏ธ ๐Ÿšด๐Ÿฝโ€โ™‚๏ธ ๐Ÿšด๐Ÿพโ€โ™‚๏ธ ๐Ÿšด๐Ÿฟโ€โ™‚๏ธ ๐Ÿ† ๐Ÿฅ‡ ๐Ÿฅˆ ๐Ÿฅ‰ ๐Ÿ… ๐ŸŽ– ๐Ÿต ๐ŸŽ— ๐ŸŽซ ๐ŸŽŸ ๐ŸŽช ๐Ÿคนโ€โ™€๏ธ ๐Ÿคน๐Ÿปโ€โ™€๏ธ ๐Ÿคน๐Ÿผโ€โ™€๏ธ ๐Ÿคน๐Ÿฝโ€โ™€๏ธ ๐Ÿคน๐Ÿพโ€โ™€๏ธ ๐Ÿคน๐Ÿฟโ€โ™€๏ธ ๐Ÿคนโ€โ™‚๏ธ ๐Ÿคน๐Ÿปโ€โ™‚๏ธ ๐Ÿคน๐Ÿผโ€โ™‚๏ธ ๐Ÿคน๐Ÿฝโ€โ™‚๏ธ ๐Ÿคน๐Ÿพโ€โ™‚๏ธ ๐Ÿคน๐Ÿฟโ€โ™‚๏ธ ๐ŸŽญ ๐ŸŽจ ๐ŸŽฌ ๐ŸŽค ๐ŸŽง ๐ŸŽผ ๐ŸŽน ๐Ÿฅ ๐ŸŽท ๐ŸŽบ ๐ŸŽธ ๐ŸŽป ๐ŸŽฒ ๐Ÿงฉ โ™Ÿ ๐ŸŽฏ ๐ŸŽณ ๐ŸŽฎ ๐ŸŽฐ +๐Ÿš— ๐Ÿš• ๐Ÿš™ ๐ŸšŒ ๐ŸšŽ ๐ŸŽ ๐Ÿš“ ๐Ÿš‘ ๐Ÿš’ ๐Ÿš ๐Ÿšš ๐Ÿš› ๐Ÿšœ ๐Ÿ›ด ๐Ÿšฒ ๐Ÿ›ต ๐Ÿ ๐Ÿšจ ๐Ÿš” ๐Ÿš ๐Ÿš˜ ๐Ÿš– ๐Ÿšก ๐Ÿš  ๐ŸšŸ ๐Ÿšƒ ๐Ÿš‹ ๐Ÿšž ๐Ÿš ๐Ÿš„ ๐Ÿš… ๐Ÿšˆ ๐Ÿš‚ ๐Ÿš† ๐Ÿš‡ ๐ŸšŠ ๐Ÿš‰ โœˆ๏ธ ๐Ÿ›ซ ๐Ÿ›ฌ ๐Ÿ›ฉ ๐Ÿ’บ ๐Ÿ›ฐ ๐Ÿš€ ๐Ÿ›ธ ๐Ÿš ๐Ÿ›ถ โ›ต๏ธ ๐Ÿšค ๐Ÿ›ฅ ๐Ÿ›ณ โ›ด ๐Ÿšข โš“๏ธ โ›ฝ๏ธ ๐Ÿšง ๐Ÿšฆ ๐Ÿšฅ ๐Ÿš ๐Ÿ—บ ๐Ÿ—ฟ ๐Ÿ—ฝ ๐Ÿ—ผ ๐Ÿฐ ๐Ÿฏ ๐ŸŸ ๐ŸŽก ๐ŸŽข ๐ŸŽ  โ›ฒ๏ธ โ›ฑ ๐Ÿ– ๐Ÿ ๐Ÿœ ๐ŸŒ‹ โ›ฐ ๐Ÿ” ๐Ÿ—ป ๐Ÿ• โ›บ๏ธ ๐Ÿ  ๐Ÿก ๐Ÿ˜ ๐Ÿš ๐Ÿ— ๐Ÿญ ๐Ÿข ๐Ÿฌ ๐Ÿฃ ๐Ÿค ๐Ÿฅ ๐Ÿฆ ๐Ÿจ ๐Ÿช ๐Ÿซ ๐Ÿฉ ๐Ÿ’’ ๐Ÿ› โ›ช๏ธ ๐Ÿ•Œ ๐Ÿ• ๐Ÿ•‹ โ›ฉ ๐Ÿ›ค ๐Ÿ›ฃ ๐Ÿ—พ ๐ŸŽ‘ ๐Ÿž ๐ŸŒ… ๐ŸŒ„ ๐ŸŒ  ๐ŸŽ‡ ๐ŸŽ† ๐ŸŒ‡ ๐ŸŒ† ๐Ÿ™ ๐ŸŒƒ ๐ŸŒŒ ๐ŸŒ‰ ๐ŸŒ +โŒš๏ธ ๐Ÿ“ฑ ๐Ÿ“ฒ ๐Ÿ’ป โŒจ๏ธ ๐Ÿ–ฅ ๐Ÿ–จ ๐Ÿ–ฑ ๐Ÿ–ฒ ๐Ÿ•น ๐Ÿ—œ ๐Ÿ’ฝ ๐Ÿ’พ ๐Ÿ’ฟ ๐Ÿ“€ ๐Ÿ“ผ ๐Ÿ“ท ๐Ÿ“ธ ๐Ÿ“น ๐ŸŽฅ ๐Ÿ“ฝ ๐ŸŽž ๐Ÿ“ž โ˜Ž๏ธ ๐Ÿ“Ÿ ๐Ÿ“  ๐Ÿ“บ ๐Ÿ“ป ๐ŸŽ™ ๐ŸŽš ๐ŸŽ› โฑ โฒ โฐ ๐Ÿ•ฐ โŒ›๏ธ โณ ๐Ÿ“ก ๐Ÿ”‹ ๐Ÿ”Œ ๐Ÿ’ก ๐Ÿ”ฆ ๐Ÿ•ฏ ๐Ÿ—‘ ๐Ÿ›ข ๐Ÿ’ธ ๐Ÿ’ต ๐Ÿ’ด ๐Ÿ’ถ ๐Ÿ’ท ๐Ÿ’ฐ ๐Ÿ’ณ ๐Ÿงพ ๐Ÿ’Ž โš–๏ธ ๐Ÿ”ง ๐Ÿ”จ โš’ ๐Ÿ›  โ› ๐Ÿ”ฉ โš™๏ธ โ›“ ๐Ÿ”ซ ๐Ÿ’ฃ ๐Ÿ”ช ๐Ÿ—ก โš”๏ธ ๐Ÿ›ก ๐Ÿšฌ โšฐ๏ธ โšฑ๏ธ ๐Ÿบ ๐Ÿงญ ๐Ÿงฑ ๐Ÿ”ฎ ๐Ÿงฟ ๐Ÿงธ ๐Ÿ“ฟ ๐Ÿ’ˆ โš—๏ธ ๐Ÿ”ญ ๐Ÿงฐ ๐Ÿงฒ ๐Ÿงช ๐Ÿงซ ๐Ÿงฌ ๐Ÿงฏ ๐Ÿ”ฌ ๐Ÿ•ณ ๐Ÿ’Š ๐Ÿ’‰ ๐ŸŒก ๐Ÿšฝ ๐Ÿšฐ ๐Ÿšฟ ๐Ÿ› ๐Ÿ›€ ๐Ÿ›€๐Ÿป ๐Ÿ›€๐Ÿผ ๐Ÿ›€๐Ÿฝ ๐Ÿ›€๐Ÿพ ๐Ÿ›€๐Ÿฟ ๐Ÿงด ๐Ÿงต ๐Ÿงถ ๐Ÿงท ๐Ÿงน ๐Ÿงบ ๐Ÿงป ๐Ÿงผ ๐Ÿงฝ ๐Ÿ›Ž ๐Ÿ”‘ ๐Ÿ— ๐Ÿšช ๐Ÿ›‹ ๐Ÿ› ๐Ÿ›Œ ๐Ÿ–ผ ๐Ÿ› ๐Ÿงณ ๐Ÿ›’ ๐ŸŽ ๐ŸŽˆ ๐ŸŽ ๐ŸŽ€ ๐ŸŽŠ ๐ŸŽ‰ ๐Ÿงจ ๐ŸŽŽ ๐Ÿฎ ๐ŸŽ ๐Ÿงง โœ‰๏ธ ๐Ÿ“ฉ ๐Ÿ“จ ๐Ÿ“ง ๐Ÿ’Œ ๐Ÿ“ฅ ๐Ÿ“ค ๐Ÿ“ฆ ๐Ÿท ๐Ÿ“ช ๐Ÿ“ซ ๐Ÿ“ฌ ๐Ÿ“ญ ๐Ÿ“ฎ ๐Ÿ“ฏ ๐Ÿ“œ ๐Ÿ“ƒ ๐Ÿ“„ ๐Ÿ“‘ ๐Ÿ“Š ๐Ÿ“ˆ ๐Ÿ“‰ ๐Ÿ—’ ๐Ÿ—“ ๐Ÿ“† ๐Ÿ“… ๐Ÿ“‡ ๐Ÿ—ƒ ๐Ÿ—ณ ๐Ÿ—„ ๐Ÿ“‹ ๐Ÿ“ ๐Ÿ“‚ ๐Ÿ—‚ ๐Ÿ—ž ๐Ÿ“ฐ ๐Ÿ““ ๐Ÿ“” ๐Ÿ“’ ๐Ÿ“• ๐Ÿ“— ๐Ÿ“˜ ๐Ÿ“™ ๐Ÿ“š ๐Ÿ“– ๐Ÿ”– ๐Ÿ”— ๐Ÿ“Ž ๐Ÿ–‡ ๐Ÿ“ ๐Ÿ“ ๐Ÿ“Œ ๐Ÿ“ โœ‚๏ธ ๐Ÿ–Š ๐Ÿ–‹ โœ’๏ธ ๐Ÿ–Œ ๐Ÿ– ๐Ÿ“ โœ๏ธ ๐Ÿ” ๐Ÿ”Ž ๐Ÿ” ๐Ÿ” ๐Ÿ”’ ๐Ÿ”“ +โค๏ธ ๐Ÿงก ๐Ÿ’› ๐Ÿ’š ๐Ÿ’™ ๐Ÿ’œ ๐Ÿ–ค ๐Ÿ’” โฃ๏ธ ๐Ÿ’• ๐Ÿ’ž ๐Ÿ’“ ๐Ÿ’— ๐Ÿ’– ๐Ÿ’˜ ๐Ÿ’ ๐Ÿ’Ÿ โ˜ฎ๏ธ โœ๏ธ โ˜ช๏ธ ๐Ÿ•‰ โ˜ธ๏ธ โœก๏ธ ๐Ÿ”ฏ ๐Ÿ•Ž โ˜ฏ๏ธ โ˜ฆ๏ธ ๐Ÿ› โ›Ž โ™ˆ๏ธ โ™‰๏ธ โ™Š๏ธ โ™‹๏ธ โ™Œ๏ธ โ™๏ธ โ™Ž๏ธ โ™๏ธ โ™๏ธ โ™‘๏ธ โ™’๏ธ โ™“๏ธ ๐Ÿ†” โš›๏ธ ๐Ÿ‰‘ โ˜ข๏ธ โ˜ฃ๏ธ ๐Ÿ“ด ๐Ÿ“ณ ๐Ÿˆถ ๐Ÿˆš๏ธ ๐Ÿˆธ ๐Ÿˆบ ๐Ÿˆท๏ธ โœด๏ธ ๐Ÿ†š ๐Ÿ’ฎ ๐Ÿ‰ ใŠ™๏ธ ใŠ—๏ธ ๐Ÿˆด ๐Ÿˆต ๐Ÿˆน ๐Ÿˆฒ ๐Ÿ…ฐ๏ธ ๐Ÿ…ฑ๏ธ ๐Ÿ†Ž ๐Ÿ†‘ ๐Ÿ…พ๏ธ ๐Ÿ†˜ โŒ โญ•๏ธ ๐Ÿ›‘ โ›”๏ธ ๐Ÿ“› ๐Ÿšซ ๐Ÿ’ฏ ๐Ÿ’ข โ™จ๏ธ ๐Ÿšท ๐Ÿšฏ ๐Ÿšณ ๐Ÿšฑ ๐Ÿ”ž ๐Ÿ“ต ๐Ÿšญ โ—๏ธ โ• โ“ โ” โ€ผ๏ธ โ‰๏ธ ๐Ÿ”… ๐Ÿ”† ใ€ฝ๏ธ โš ๏ธ ๐Ÿšธ ๐Ÿ”ฑ โšœ๏ธ ๐Ÿ”ฐ โ™ป๏ธ โœ… ๐Ÿˆฏ๏ธ ๐Ÿ’น โ‡๏ธ โœณ๏ธ โŽ ๐ŸŒ ๐Ÿ’  โ“‚๏ธ ๐ŸŒ€ ๐Ÿ’ค ๐Ÿง ๐Ÿšพ โ™ฟ๏ธ ๐Ÿ…ฟ๏ธ ๐Ÿˆณ ๐Ÿˆ‚๏ธ ๐Ÿ›‚ ๐Ÿ›ƒ ๐Ÿ›„ ๐Ÿ›… ๐Ÿšน ๐Ÿšบ ๐Ÿšผ ๐Ÿšป ๐Ÿšฎ ๐ŸŽฆ ๐Ÿ“ถ ๐Ÿˆ ๐Ÿ”ฃ โ„น๏ธ ๐Ÿ”ค ๐Ÿ”ก ๐Ÿ”  ๐Ÿ†– ๐Ÿ†— ๐Ÿ†™ ๐Ÿ†’ ๐Ÿ†• ๐Ÿ†“ 0๏ธโƒฃ 1๏ธโƒฃ 2๏ธโƒฃ 3๏ธโƒฃ 4๏ธโƒฃ 5๏ธโƒฃ 6๏ธโƒฃ 7๏ธโƒฃ 8๏ธโƒฃ 9๏ธโƒฃ ๐Ÿ”Ÿ ๐Ÿ”ข #๏ธโƒฃ *๏ธโƒฃ โ๏ธ โ–ถ๏ธ โธ โฏ โน โบ โญ โฎ โฉ โช โซ โฌ โ—€๏ธ ๐Ÿ”ผ ๐Ÿ”ฝ โžก๏ธ โฌ…๏ธ โฌ†๏ธ โฌ‡๏ธ โ†—๏ธ โ†˜๏ธ โ†™๏ธ โ†–๏ธ โ†•๏ธ โ†”๏ธ โ†ช๏ธ โ†ฉ๏ธ โคด๏ธ โคต๏ธ ๐Ÿ”€ ๐Ÿ” ๐Ÿ”‚ ๐Ÿ”„ ๐Ÿ”ƒ ๐ŸŽต ๐ŸŽถ โž• โž– โž— โœ–๏ธ โ™พ ๐Ÿ’ฒ ๐Ÿ’ฑ โ„ข๏ธ ยฉ๏ธ ยฎ๏ธ ใ€ฐ๏ธ โžฐ โžฟ ๐Ÿ”š ๐Ÿ”™ ๐Ÿ”› ๐Ÿ” ๐Ÿ”œ โœ”๏ธ โ˜‘๏ธ ๐Ÿ”˜ โšช๏ธ โšซ๏ธ ๐Ÿ”ด ๐Ÿ”ต ๐Ÿ”บ ๐Ÿ”ป ๐Ÿ”ธ ๐Ÿ”น ๐Ÿ”ถ ๐Ÿ”ท ๐Ÿ”ณ ๐Ÿ”ฒ โ–ช๏ธ โ–ซ๏ธ โ—พ๏ธ โ—ฝ๏ธ โ—ผ๏ธ โ—ป๏ธ โฌ›๏ธ โฌœ๏ธ ๐Ÿ”ˆ ๐Ÿ”‡ ๐Ÿ”‰ ๐Ÿ”Š ๐Ÿ”” ๐Ÿ”• ๐Ÿ“ฃ ๐Ÿ“ข ๐Ÿ‘โ€๐Ÿ—จ ๐Ÿ’ฌ ๐Ÿ’ญ ๐Ÿ—ฏ โ™ ๏ธ โ™ฃ๏ธ โ™ฅ๏ธ โ™ฆ๏ธ ๐Ÿƒ ๐ŸŽด ๐Ÿ€„๏ธ ๐Ÿ• ๐Ÿ•‘ ๐Ÿ•’ ๐Ÿ•“ ๐Ÿ•” ๐Ÿ•• ๐Ÿ•– ๐Ÿ•— ๐Ÿ•˜ ๐Ÿ•™ ๐Ÿ•š ๐Ÿ•› ๐Ÿ•œ ๐Ÿ• ๐Ÿ•ž ๐Ÿ•Ÿ ๐Ÿ•  ๐Ÿ•ก ๐Ÿ•ข ๐Ÿ•ฃ ๐Ÿ•ค ๐Ÿ•ฅ ๐Ÿ•ฆ ๐Ÿ•ง +๐Ÿณ๏ธ ๐Ÿด ๐Ÿ ๐Ÿšฉ ๐Ÿณ๏ธโ€๐ŸŒˆ ๐Ÿดโ€โ˜ ๏ธ ๐Ÿ‡ฆ๐Ÿ‡ซ ๐Ÿ‡ฆ๐Ÿ‡ฝ ๐Ÿ‡ฆ๐Ÿ‡ฑ ๐Ÿ‡ฉ๐Ÿ‡ฟ ๐Ÿ‡ฆ๐Ÿ‡ธ ๐Ÿ‡ฆ๐Ÿ‡ฉ ๐Ÿ‡ฆ๐Ÿ‡ด ๐Ÿ‡ฆ๐Ÿ‡ฎ ๐Ÿ‡ฆ๐Ÿ‡ถ ๐Ÿ‡ฆ๐Ÿ‡ฌ ๐Ÿ‡ฆ๐Ÿ‡ท ๐Ÿ‡ฆ๐Ÿ‡ฒ ๐Ÿ‡ฆ๐Ÿ‡ผ ๐Ÿ‡ฆ๐Ÿ‡บ ๐Ÿ‡ฆ๐Ÿ‡น ๐Ÿ‡ฆ๐Ÿ‡ฟ ๐Ÿ‡ง๐Ÿ‡ธ ๐Ÿ‡ง๐Ÿ‡ญ ๐Ÿ‡ง๐Ÿ‡ฉ ๐Ÿ‡ง๐Ÿ‡ง ๐Ÿ‡ง๐Ÿ‡พ ๐Ÿ‡ง๐Ÿ‡ช ๐Ÿ‡ง๐Ÿ‡ฟ ๐Ÿ‡ง๐Ÿ‡ฏ ๐Ÿ‡ง๐Ÿ‡ฒ ๐Ÿ‡ง๐Ÿ‡น ๐Ÿ‡ง๐Ÿ‡ด ๐Ÿ‡ง๐Ÿ‡ฆ ๐Ÿ‡ง๐Ÿ‡ผ ๐Ÿ‡ง๐Ÿ‡ท ๐Ÿ‡ฎ๐Ÿ‡ด ๐Ÿ‡ป๐Ÿ‡ฌ ๐Ÿ‡ง๐Ÿ‡ณ ๐Ÿ‡ง๐Ÿ‡ฌ ๐Ÿ‡ง๐Ÿ‡ซ ๐Ÿ‡ง๐Ÿ‡ฎ ๐Ÿ‡ฐ๐Ÿ‡ญ ๐Ÿ‡จ๐Ÿ‡ฒ ๐Ÿ‡จ๐Ÿ‡ฆ ๐Ÿ‡ฎ๐Ÿ‡จ ๐Ÿ‡จ๐Ÿ‡ป ๐Ÿ‡ง๐Ÿ‡ถ ๐Ÿ‡ฐ๐Ÿ‡พ ๐Ÿ‡จ๐Ÿ‡ซ ๐Ÿ‡น๐Ÿ‡ฉ ๐Ÿ‡จ๐Ÿ‡ฑ ๐Ÿ‡จ๐Ÿ‡ณ ๐Ÿ‡จ๐Ÿ‡ฝ ๐Ÿ‡จ๐Ÿ‡จ ๐Ÿ‡จ๐Ÿ‡ด ๐Ÿ‡ฐ๐Ÿ‡ฒ ๐Ÿ‡จ๐Ÿ‡ฌ ๐Ÿ‡จ๐Ÿ‡ฉ ๐Ÿ‡จ๐Ÿ‡ฐ ๐Ÿ‡จ๐Ÿ‡ท ๐Ÿ‡จ๐Ÿ‡ฎ ๐Ÿ‡ญ๐Ÿ‡ท ๐Ÿ‡จ๐Ÿ‡บ ๐Ÿ‡จ๐Ÿ‡ผ ๐Ÿ‡จ๐Ÿ‡พ ๐Ÿ‡จ๐Ÿ‡ฟ ๐Ÿ‡ฉ๐Ÿ‡ฐ ๐Ÿ‡ฉ๐Ÿ‡ฏ ๐Ÿ‡ฉ๐Ÿ‡ฒ ๐Ÿ‡ฉ๐Ÿ‡ด ๐Ÿ‡ช๐Ÿ‡จ ๐Ÿ‡ช๐Ÿ‡ฌ ๐Ÿ‡ธ๐Ÿ‡ป ๐Ÿ‡ฌ๐Ÿ‡ถ ๐Ÿ‡ช๐Ÿ‡ท ๐Ÿ‡ช๐Ÿ‡ช ๐Ÿ‡ช๐Ÿ‡น ๐Ÿ‡ช๐Ÿ‡บ ๐Ÿ‡ซ๐Ÿ‡ฐ ๐Ÿ‡ซ๐Ÿ‡ด ๐Ÿ‡ซ๐Ÿ‡ฏ ๐Ÿ‡ซ๐Ÿ‡ฎ ๐Ÿ‡ซ๐Ÿ‡ท ๐Ÿ‡ฌ๐Ÿ‡ซ ๐Ÿ‡ต๐Ÿ‡ซ ๐Ÿ‡น๐Ÿ‡ซ ๐Ÿ‡ฌ๐Ÿ‡ฆ ๐Ÿ‡ฌ๐Ÿ‡ฒ ๐Ÿ‡ฌ๐Ÿ‡ช ๐Ÿ‡ฉ๐Ÿ‡ช ๐Ÿ‡ฌ๐Ÿ‡ญ ๐Ÿ‡ฌ๐Ÿ‡ฎ ๐Ÿ‡ฌ๐Ÿ‡ท ๐Ÿ‡ฌ๐Ÿ‡ฑ ๐Ÿ‡ฌ๐Ÿ‡ฉ ๐Ÿ‡ฌ๐Ÿ‡ต ๐Ÿ‡ฌ๐Ÿ‡บ ๐Ÿ‡ฌ๐Ÿ‡น ๐Ÿ‡ฌ๐Ÿ‡ฌ ๐Ÿ‡ฌ๐Ÿ‡ณ ๐Ÿ‡ฌ๐Ÿ‡ผ ๐Ÿ‡ฌ๐Ÿ‡พ ๐Ÿ‡ญ๐Ÿ‡น ๐Ÿ‡ญ๐Ÿ‡ณ ๐Ÿ‡ญ๐Ÿ‡ฐ ๐Ÿ‡ญ๐Ÿ‡บ ๐Ÿ‡ฎ๐Ÿ‡ธ ๐Ÿ‡ฎ๐Ÿ‡ณ ๐Ÿ‡ฎ๐Ÿ‡ฉ ๐Ÿ‡ฎ๐Ÿ‡ท ๐Ÿ‡ฎ๐Ÿ‡ถ ๐Ÿ‡ฎ๐Ÿ‡ช ๐Ÿ‡ฎ๐Ÿ‡ฒ ๐Ÿ‡ฎ๐Ÿ‡ฑ ๐Ÿ‡ฎ๐Ÿ‡น ๐Ÿ‡ฏ๐Ÿ‡ฒ ๐Ÿ‡ฏ๐Ÿ‡ต ๐ŸŽŒ ๐Ÿ‡ฏ๐Ÿ‡ช ๐Ÿ‡ฏ๐Ÿ‡ด ๐Ÿ‡ฐ๐Ÿ‡ฟ ๐Ÿ‡ฐ๐Ÿ‡ช ๐Ÿ‡ฐ๐Ÿ‡ฎ ๐Ÿ‡ฝ๐Ÿ‡ฐ ๐Ÿ‡ฐ๐Ÿ‡ผ ๐Ÿ‡ฐ๐Ÿ‡ฌ ๐Ÿ‡ฑ๐Ÿ‡ฆ ๐Ÿ‡ฑ๐Ÿ‡ป ๐Ÿ‡ฑ๐Ÿ‡ง ๐Ÿ‡ฑ๐Ÿ‡ธ ๐Ÿ‡ฑ๐Ÿ‡ท ๐Ÿ‡ฑ๐Ÿ‡พ ๐Ÿ‡ฑ๐Ÿ‡ฎ ๐Ÿ‡ฑ๐Ÿ‡น ๐Ÿ‡ฑ๐Ÿ‡บ ๐Ÿ‡ฒ๐Ÿ‡ด ๐Ÿ‡ฒ๐Ÿ‡ฐ ๐Ÿ‡ฒ๐Ÿ‡ฌ ๐Ÿ‡ฒ๐Ÿ‡ผ ๐Ÿ‡ฒ๐Ÿ‡พ ๐Ÿ‡ฒ๐Ÿ‡ป ๐Ÿ‡ฒ๐Ÿ‡ฑ ๐Ÿ‡ฒ๐Ÿ‡น ๐Ÿ‡ฒ๐Ÿ‡ญ ๐Ÿ‡ฒ๐Ÿ‡ถ ๐Ÿ‡ฒ๐Ÿ‡ท ๐Ÿ‡ฒ๐Ÿ‡บ ๐Ÿ‡พ๐Ÿ‡น ๐Ÿ‡ฒ๐Ÿ‡ฝ ๐Ÿ‡ซ๐Ÿ‡ฒ ๐Ÿ‡ฒ๐Ÿ‡ฉ ๐Ÿ‡ฒ๐Ÿ‡จ ๐Ÿ‡ฒ๐Ÿ‡ณ ๐Ÿ‡ฒ๐Ÿ‡ช ๐Ÿ‡ฒ๐Ÿ‡ธ ๐Ÿ‡ฒ๐Ÿ‡ฆ ๐Ÿ‡ฒ๐Ÿ‡ฟ ๐Ÿ‡ฒ๐Ÿ‡ฒ ๐Ÿ‡ณ๐Ÿ‡ฆ ๐Ÿ‡ณ๐Ÿ‡ท ๐Ÿ‡ณ๐Ÿ‡ต ๐Ÿ‡ณ๐Ÿ‡ฑ ๐Ÿ‡ณ๐Ÿ‡จ ๐Ÿ‡ณ๐Ÿ‡ฟ ๐Ÿ‡ณ๐Ÿ‡ฎ ๐Ÿ‡ณ๐Ÿ‡ช ๐Ÿ‡ณ๐Ÿ‡ฌ ๐Ÿ‡ณ๐Ÿ‡บ ๐Ÿ‡ณ๐Ÿ‡ซ ๐Ÿ‡ฐ๐Ÿ‡ต ๐Ÿ‡ฒ๐Ÿ‡ต ๐Ÿ‡ณ๐Ÿ‡ด ๐Ÿ‡ด๐Ÿ‡ฒ ๐Ÿ‡ต๐Ÿ‡ฐ ๐Ÿ‡ต๐Ÿ‡ผ ๐Ÿ‡ต๐Ÿ‡ธ ๐Ÿ‡ต๐Ÿ‡ฆ ๐Ÿ‡ต๐Ÿ‡ฌ ๐Ÿ‡ต๐Ÿ‡พ ๐Ÿ‡ต๐Ÿ‡ช ๐Ÿ‡ต๐Ÿ‡ญ ๐Ÿ‡ต๐Ÿ‡ณ ๐Ÿ‡ต๐Ÿ‡ฑ ๐Ÿ‡ต๐Ÿ‡น ๐Ÿ‡ต๐Ÿ‡ท ๐Ÿ‡ถ๐Ÿ‡ฆ ๐Ÿ‡ท๐Ÿ‡ช ๐Ÿ‡ท๐Ÿ‡ด ๐Ÿ‡ท๐Ÿ‡บ ๐Ÿ‡ท๐Ÿ‡ผ ๐Ÿ‡ผ๐Ÿ‡ธ ๐Ÿ‡ธ๐Ÿ‡ฒ ๐Ÿ‡ธ๐Ÿ‡ฆ ๐Ÿ‡ธ๐Ÿ‡ณ ๐Ÿ‡ท๐Ÿ‡ธ ๐Ÿ‡ธ๐Ÿ‡จ ๐Ÿ‡ธ๐Ÿ‡ฑ ๐Ÿ‡ธ๐Ÿ‡ฌ ๐Ÿ‡ธ๐Ÿ‡ฝ ๐Ÿ‡ธ๐Ÿ‡ฐ ๐Ÿ‡ธ๐Ÿ‡ฎ ๐Ÿ‡ฌ๐Ÿ‡ธ ๐Ÿ‡ธ๐Ÿ‡ง ๐Ÿ‡ธ๐Ÿ‡ด ๐Ÿ‡ฟ๐Ÿ‡ฆ ๐Ÿ‡ฐ๐Ÿ‡ท ๐Ÿ‡ธ๐Ÿ‡ธ ๐Ÿ‡ช๐Ÿ‡ธ ๐Ÿ‡ฑ๐Ÿ‡ฐ ๐Ÿ‡ง๐Ÿ‡ฑ ๐Ÿ‡ธ๐Ÿ‡ญ ๐Ÿ‡ฐ๐Ÿ‡ณ ๐Ÿ‡ฑ๐Ÿ‡จ ๐Ÿ‡ต๐Ÿ‡ฒ ๐Ÿ‡ป๐Ÿ‡จ ๐Ÿ‡ธ๐Ÿ‡ฉ ๐Ÿ‡ธ๐Ÿ‡ท ๐Ÿ‡ธ๐Ÿ‡ฟ ๐Ÿ‡ธ๐Ÿ‡ช ๐Ÿ‡จ๐Ÿ‡ญ ๐Ÿ‡ธ๐Ÿ‡พ ๐Ÿ‡น๐Ÿ‡ผ ๐Ÿ‡น๐Ÿ‡ฏ ๐Ÿ‡น๐Ÿ‡ฟ ๐Ÿ‡น๐Ÿ‡ญ ๐Ÿ‡น๐Ÿ‡ฑ ๐Ÿ‡น๐Ÿ‡ฌ ๐Ÿ‡น๐Ÿ‡ฐ ๐Ÿ‡น๐Ÿ‡ด ๐Ÿ‡น๐Ÿ‡น ๐Ÿ‡น๐Ÿ‡ณ ๐Ÿ‡น๐Ÿ‡ท ๐Ÿ‡น๐Ÿ‡ฒ ๐Ÿ‡น๐Ÿ‡จ ๐Ÿ‡น๐Ÿ‡ป ๐Ÿ‡ป๐Ÿ‡ฎ ๐Ÿ‡บ๐Ÿ‡ฌ ๐Ÿ‡บ๐Ÿ‡ฆ ๐Ÿ‡ฆ๐Ÿ‡ช ๐Ÿ‡ฌ๐Ÿ‡ง ๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ ๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ ๐Ÿ‡บ๐Ÿ‡ณ ๐Ÿ‡บ๐Ÿ‡ธ ๐Ÿ‡บ๐Ÿ‡พ ๐Ÿ‡บ๐Ÿ‡ฟ ๐Ÿ‡ป๐Ÿ‡บ ๐Ÿ‡ป๐Ÿ‡ฆ ๐Ÿ‡ป๐Ÿ‡ช ๐Ÿ‡ป๐Ÿ‡ณ ๐Ÿ‡ผ๐Ÿ‡ซ ๐Ÿ‡ช๐Ÿ‡ญ ๐Ÿ‡พ๐Ÿ‡ช ๐Ÿ‡ฟ๐Ÿ‡ฒ ๐Ÿ‡ฟ๐Ÿ‡ผ +๐Ÿฅฑ ๐Ÿค ๐Ÿฆพ ๐Ÿฆฟ ๐Ÿฆป ๐Ÿง ๐Ÿงโ€โ™‚๏ธ ๐Ÿงโ€โ™€๏ธ ๐Ÿง ๐Ÿงโ€โ™‚๏ธ ๐Ÿงโ€โ™€๏ธ ๐ŸงŽ ๐ŸงŽโ€โ™‚๏ธ ๐ŸงŽโ€โ™€๏ธ ๐Ÿ‘จโ€๐Ÿฆฏ ๐Ÿ‘ฉโ€๐Ÿฆฏ ๐Ÿ‘จโ€๐Ÿฆผ ๐Ÿ‘ฉโ€๐Ÿฆผ ๐Ÿ‘จโ€๐Ÿฆฝ ๐Ÿ‘ฉโ€๐Ÿฆฝ ๐Ÿฆง ๐Ÿฆฎ ๐Ÿ•โ€๐Ÿฆบ ๐Ÿฆฅ ๐Ÿฆฆ ๐Ÿฆจ ๐Ÿฆฉ ๐Ÿง„ ๐Ÿง… ๐Ÿง‡ ๐Ÿง† ๐Ÿงˆ ๐Ÿฆช ๐Ÿงƒ ๐Ÿง‰ ๐ŸงŠ ๐Ÿ›• ๐Ÿฆฝ ๐Ÿฆผ ๐Ÿ›บ ๐Ÿช‚ ๐Ÿช ๐Ÿคฟ ๐Ÿช€ ๐Ÿช ๐Ÿฆบ ๐Ÿฅป ๐Ÿฉฑ ๐Ÿฉฒ ๐Ÿฉณ ๐Ÿฉฐ ๐Ÿช• ๐Ÿช” ๐Ÿช“ ๐Ÿฆฏ ๐Ÿฉธ ๐Ÿฉน ๐Ÿฉบ ๐Ÿช‘ ๐Ÿช’ ๐ŸคŽ ๐Ÿค ๐ŸŸ  ๐ŸŸก ๐ŸŸข ๐ŸŸฃ ๐ŸŸค ๐ŸŸฅ ๐ŸŸง ๐ŸŸจ ๐ŸŸฉ ๐ŸŸฆ ๐ŸŸช ๐ŸŸซ"; + + [Benchmark] + public TextLayout BuildEmojisTextLayout() => new TextLayout(Emojis, Typeface.Default, 12d, Brushes.Black); + + public void Dispose() + { + _app?.Dispose(); + } +} diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index 2790bd6096..7501bf21fe 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -153,7 +153,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { while (inner.MoveNext()) { - j += inner.Current.Text.Length; + j += inner.Current.Length; if (j + i > text.Length) { @@ -192,7 +192,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting inner = new GraphemeEnumerator(new CharacterBufferRange(text)); - i += outer.Current.Text.Length; + i += outer.Current.Length; } } } @@ -974,9 +974,9 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { var grapheme = graphemeEnumerator.Current; - var textStyleOverrides = new[] { new ValueSpan(i, grapheme.Text.Length, new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Red)) }; + var textStyleOverrides = new[] { new ValueSpan(i, grapheme.Length, new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Red)) }; - i += grapheme.Text.Length; + i += grapheme.Length; var layout = new TextLayout( text, From 8c30948e62dcf565d26dbda39020c5dbb15f096a Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 04:59:46 +0300 Subject: [PATCH 91/95] [Text] optimize CharacterBufferReference indexator --- src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs index d76f212f26..86e44a5269 100644 --- a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs +++ b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs @@ -140,7 +140,7 @@ namespace Avalonia.Media.TextFormatting throw new ArgumentOutOfRangeException(nameof(index)); } #endif - return Span[index]; + return CharacterBufferReference.CharacterBuffer.Span[CharacterBufferReference.OffsetToFirstChar + index]; } } From 5bd28141b78d88bb72eb92fe4edb00451dc1e7d8 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 05:07:27 +0300 Subject: [PATCH 92/95] [Text] minor optimize of CharacterBufferRange --- .../TextFormatting/CharacterBufferRange.cs | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs index 86e44a5269..599b35dd2c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs +++ b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs @@ -147,12 +147,12 @@ namespace Avalonia.Media.TextFormatting /// /// Gets a reference to the character buffer /// - public CharacterBufferReference CharacterBufferReference { get; } + public readonly CharacterBufferReference CharacterBufferReference; /// /// Gets the number of characters in text source character store /// - public int Length { get; } + public readonly int Length; /// /// Gets a span from the character buffer range @@ -163,27 +163,18 @@ namespace Avalonia.Media.TextFormatting /// /// Gets the character memory buffer /// - internal ReadOnlyMemory CharacterBuffer - { - get { return CharacterBufferReference.CharacterBuffer; } - } + internal ReadOnlyMemory CharacterBuffer => CharacterBufferReference.CharacterBuffer; /// /// Gets the character offset relative to the beginning of buffer to /// the first character of the run /// - internal int OffsetToFirstChar - { - get { return CharacterBufferReference.OffsetToFirstChar; } - } + internal int OffsetToFirstChar => CharacterBufferReference.OffsetToFirstChar; /// /// Indicate whether the character buffer range is empty /// - internal bool IsEmpty - { - get { return CharacterBufferReference.CharacterBuffer.Length == 0 || Length <= 0; } - } + internal bool IsEmpty => CharacterBufferReference.CharacterBuffer.Length == 0 || Length <= 0; internal CharacterBufferRange Take(int length) { @@ -280,14 +271,8 @@ namespace Avalonia.Media.TextFormatting int IReadOnlyCollection.Count => Length; - public IEnumerator GetEnumerator() - { - return new ImmutableReadOnlyListStructEnumerator(this); - } + public IEnumerator GetEnumerator() => new ImmutableReadOnlyListStructEnumerator(this); - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } From ba9bf5d3641df0d75c780c4ef021b7762764f3d6 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 05:13:50 +0300 Subject: [PATCH 93/95] [Text] Clone CodePoint ReadAt for Span --- .../Media/TextFormatting/Unicode/Codepoint.cs | 68 ++++++++++++++++++- .../Unicode/CodepointEnumerator.cs | 2 +- .../Unicode/GraphemeEnumerator.cs | 2 +- .../Avalonia.UnitTests/MockTextShaperImpl.cs | 2 +- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs index e6408bcfa6..ecd0681e15 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs @@ -1,4 +1,5 @@ -๏ปฟusing System.Collections.Generic; +๏ปฟusing System; +using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Avalonia.Media.TextFormatting.Unicode @@ -222,6 +223,71 @@ namespace Avalonia.Media.TextFormatting.Unicode return new Codepoint(code); } + + /// + /// Reads the at specified position. + /// + /// The buffer to read from. + /// The index to read at. + /// The count of character that were read. + /// + public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count) + { + count = 1; + + if (index >= text.Length) + { + return ReplacementCodepoint; + } + + var code = text[index]; + + ushort hi, low; + + //# High surrogate + if (0xD800 <= code && code <= 0xDBFF) + { + hi = code; + + if (index + 1 == text.Length) + { + return ReplacementCodepoint; + } + + low = text[index + 1]; + + if (0xDC00 <= low && low <= 0xDFFF) + { + count = 2; + return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)); + } + + return ReplacementCodepoint; + } + + //# Low surrogate + if (0xDC00 <= code && code <= 0xDFFF) + { + if (index == 0) + { + return ReplacementCodepoint; + } + + hi = text[index - 1]; + + low = code; + + if (0xD800 <= hi && hi <= 0xDBFF) + { + count = 2; + return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)); + } + + return ReplacementCodepoint; + } + + return new Codepoint(code); + } /// /// Returns if is between diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs index a2c36d9a13..a2e29812d3 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs @@ -30,7 +30,7 @@ namespace Avalonia.Media.TextFormatting.Unicode return false; } - Current = Codepoint.ReadAt(_text, 0, out var count); + Current = Codepoint.ReadAt(_text.Span, 0, out var count); _text = _text.Skip(count); diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs index dc21e06813..2488b620e7 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs @@ -245,7 +245,7 @@ namespace Avalonia.Media.TextFormatting.Unicode if (CurrentCodeUnitOffset < _buffer.Length) { - CurrentCodepoint = Codepoint.ReadAt(_buffer, CurrentCodeUnitOffset, + CurrentCodepoint = Codepoint.ReadAt(_buffer.Span, CurrentCodeUnitOffset, out _codeUnitLengthOfCurrentScalar); } else diff --git a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs index 00bcef295a..d010f9ebf2 100644 --- a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs @@ -18,7 +18,7 @@ namespace Avalonia.UnitTests { var glyphCluster = i + text.OffsetToFirstChar; - var codepoint = Codepoint.ReadAt(characterBufferRange, i, out var count); + var codepoint = Codepoint.ReadAt(characterBufferRange.Span, i, out var count); var glyphIndex = typeface.GetGlyph(codepoint); From 80972e5f5b287809caeabe0b8873c845061f7c34 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 05:23:58 +0300 Subject: [PATCH 94/95] [Text] a bit better usage of Codepoint.ReadAt --- src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs | 2 +- .../Media/TextFormatting/Unicode/CodepointEnumerator.cs | 2 +- .../Media/TextFormatting/Unicode/GraphemeEnumerator.cs | 2 +- tests/Avalonia.UnitTests/MockTextShaperImpl.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs index ecd0681e15..8b9d1c1d02 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs @@ -231,7 +231,7 @@ namespace Avalonia.Media.TextFormatting.Unicode /// The index to read at. /// The count of character that were read. /// - public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count) + public static Codepoint ReadAt(CharacterBufferRange text, int index, out int count) { count = 1; diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs index a2e29812d3..a2c36d9a13 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs @@ -30,7 +30,7 @@ namespace Avalonia.Media.TextFormatting.Unicode return false; } - Current = Codepoint.ReadAt(_text.Span, 0, out var count); + Current = Codepoint.ReadAt(_text, 0, out var count); _text = _text.Skip(count); diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs index 2488b620e7..dc21e06813 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs @@ -245,7 +245,7 @@ namespace Avalonia.Media.TextFormatting.Unicode if (CurrentCodeUnitOffset < _buffer.Length) { - CurrentCodepoint = Codepoint.ReadAt(_buffer.Span, CurrentCodeUnitOffset, + CurrentCodepoint = Codepoint.ReadAt(_buffer, CurrentCodeUnitOffset, out _codeUnitLengthOfCurrentScalar); } else diff --git a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs index d010f9ebf2..00bcef295a 100644 --- a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs @@ -18,7 +18,7 @@ namespace Avalonia.UnitTests { var glyphCluster = i + text.OffsetToFirstChar; - var codepoint = Codepoint.ReadAt(characterBufferRange.Span, i, out var count); + var codepoint = Codepoint.ReadAt(characterBufferRange, i, out var count); var glyphIndex = typeface.GetGlyph(codepoint); From c9bf6310a8582afc10a504ae1b938267884a58f0 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sat, 7 Jan 2023 14:18:25 +0300 Subject: [PATCH 95/95] [Text] revert fields to props in CharacterBufferRange --- .../Media/TextFormatting/CharacterBufferRange.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs index 599b35dd2c..f5d39e4371 100644 --- a/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs +++ b/src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs @@ -147,12 +147,12 @@ namespace Avalonia.Media.TextFormatting /// /// Gets a reference to the character buffer /// - public readonly CharacterBufferReference CharacterBufferReference; + public CharacterBufferReference CharacterBufferReference { get; } /// /// Gets the number of characters in text source character store /// - public readonly int Length; + public int Length { get; } /// /// Gets a span from the character buffer range