From 5437f8a76e1654d5f67d5d102497a43799d9cc69 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sat, 1 Apr 2023 11:44:12 +0200 Subject: [PATCH 001/391] Added PageUp and PageDown KeyGestures --- .../Input/Platform/PlatformHotkeyConfiguration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index b87ca5eded..1bd54e37ea 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -85,6 +85,14 @@ namespace Avalonia.Input.Platform { new KeyGesture(Key.Left, KeyModifiers.Alt) }; + PageUp = new List + { + new KeyGesture(Key.PageUp) + }; + PageDown = new List + { + new KeyGesture(Key.PageDown) + }; } public KeyModifiers CommandModifiers { get; set; } @@ -106,5 +114,7 @@ namespace Avalonia.Input.Platform public List MoveCursorToTheEndOfDocumentWithSelection { get; set; } public List OpenContextMenu { get; set; } public List Back { get; set; } + public List PageUp { get; set; } + public List PageDown { get; set; } } } From e635d900ff80ee2bcc81eabd9dcbad83e88a87b8 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sun, 2 Apr 2023 14:36:44 +0200 Subject: [PATCH 002/391] Added PageRight and PageLeft Keygestures in PlatformHotkeyConfiguration --- .../Input/Platform/PlatformHotkeyConfiguration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index 1bd54e37ea..23950c2a71 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -85,6 +85,14 @@ namespace Avalonia.Input.Platform { new KeyGesture(Key.Left, KeyModifiers.Alt) }; + PageLeft = new List + { + new KeyGesture(Key.PageUp, KeyModifiers.Shift) + }; + PageRight = new List + { + new KeyGesture(Key.PageDown, KeyModifiers.Shift) + }; PageUp = new List { new KeyGesture(Key.PageUp) @@ -116,5 +124,7 @@ namespace Avalonia.Input.Platform public List Back { get; set; } public List PageUp { get; set; } public List PageDown { get; set; } + public List PageRight { get; set; } + public List PageLeft { get; set; } } } From 92ba4bc19c8fb36f574f74ba5067d619df045188 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sun, 2 Apr 2023 14:39:56 +0200 Subject: [PATCH 003/391] Added PageLeft, PageRight, PageUp, PageDown handling in TextBox Added cases in OnKeyDown method and added FetchChildScrollViewer method to fetch the child ScrollViewer in the VisualChildren. --- src/Avalonia.Controls/TextBox.cs | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 9bb68ba419..9c4be3a907 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -19,6 +19,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Automation.Peers; using Avalonia.Threading; using Avalonia.Platform; +using Avalonia.Collections; namespace Avalonia.Controls { @@ -1240,6 +1241,34 @@ namespace Avalonia.Controls selection = true; handled = true; } + else if (Match(keymap.PageLeft)) + { + MovePageLeft(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageRight)) + { + MovePageRight(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageUp)) + { + MovePageUp(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageDown)) + { + MovePageDown(); + movement = true; + selection = false; + handled = true; + } else { bool hasWholeWordModifiers = modifiers.HasAllFlags(keymap.WholeWordTextActionModifiers); @@ -1755,6 +1784,69 @@ namespace Avalonia.Controls } } + private void MovePageRight() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageRight(); + } + + private void MovePageLeft() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageLeft(); + } + private void MovePageUp() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageUp(); + } + + private void MovePageDown() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageDown(); + } + + private ScrollViewer? FetchChildScrollViewer(in int maxSearchDepth, int currentSearchDepth, IAvaloniaList visualChildren) + { + foreach(Visual innerChild in visualChildren) + { + if(innerChild is ScrollViewer) + { + return (ScrollViewer?)innerChild; + } + else + { + if (currentSearchDepth < maxSearchDepth) + { + ScrollViewer? innerScrollViewer = FetchChildScrollViewer(maxSearchDepth, currentSearchDepth + 1, innerChild.VisualChildren); + if (innerScrollViewer != null) + { + return innerScrollViewer; + } + } + } + } + + return null; + } + /// /// Select all text in the TextBox /// From ca66c4d00db4d1b1ed2fc9c0c466626449978ed6 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sat, 1 Apr 2023 11:44:12 +0200 Subject: [PATCH 004/391] Added PageUp and PageDown KeyGestures --- .../Input/Platform/PlatformHotkeyConfiguration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index b87ca5eded..1bd54e37ea 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -85,6 +85,14 @@ namespace Avalonia.Input.Platform { new KeyGesture(Key.Left, KeyModifiers.Alt) }; + PageUp = new List + { + new KeyGesture(Key.PageUp) + }; + PageDown = new List + { + new KeyGesture(Key.PageDown) + }; } public KeyModifiers CommandModifiers { get; set; } @@ -106,5 +114,7 @@ namespace Avalonia.Input.Platform public List MoveCursorToTheEndOfDocumentWithSelection { get; set; } public List OpenContextMenu { get; set; } public List Back { get; set; } + public List PageUp { get; set; } + public List PageDown { get; set; } } } From 01cbf9790355e5ec909006b31811131b0ae086c5 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sun, 2 Apr 2023 14:36:44 +0200 Subject: [PATCH 005/391] Added PageRight and PageLeft Keygestures in PlatformHotkeyConfiguration --- .../Input/Platform/PlatformHotkeyConfiguration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index 1bd54e37ea..23950c2a71 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -85,6 +85,14 @@ namespace Avalonia.Input.Platform { new KeyGesture(Key.Left, KeyModifiers.Alt) }; + PageLeft = new List + { + new KeyGesture(Key.PageUp, KeyModifiers.Shift) + }; + PageRight = new List + { + new KeyGesture(Key.PageDown, KeyModifiers.Shift) + }; PageUp = new List { new KeyGesture(Key.PageUp) @@ -116,5 +124,7 @@ namespace Avalonia.Input.Platform public List Back { get; set; } public List PageUp { get; set; } public List PageDown { get; set; } + public List PageRight { get; set; } + public List PageLeft { get; set; } } } From d25b9a4f88ed28267e28e5dc4d0531563a320566 Mon Sep 17 00:00:00 2001 From: nicola36631 Date: Sun, 2 Apr 2023 14:39:56 +0200 Subject: [PATCH 006/391] Added PageLeft, PageRight, PageUp, PageDown handling in TextBox Added cases in OnKeyDown method and added FetchChildScrollViewer method to fetch the child ScrollViewer in the VisualChildren. --- src/Avalonia.Controls/TextBox.cs | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 9bb68ba419..9c4be3a907 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -19,6 +19,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Automation.Peers; using Avalonia.Threading; using Avalonia.Platform; +using Avalonia.Collections; namespace Avalonia.Controls { @@ -1240,6 +1241,34 @@ namespace Avalonia.Controls selection = true; handled = true; } + else if (Match(keymap.PageLeft)) + { + MovePageLeft(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageRight)) + { + MovePageRight(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageUp)) + { + MovePageUp(); + movement = true; + selection = false; + handled = true; + } + else if (Match(keymap.PageDown)) + { + MovePageDown(); + movement = true; + selection = false; + handled = true; + } else { bool hasWholeWordModifiers = modifiers.HasAllFlags(keymap.WholeWordTextActionModifiers); @@ -1755,6 +1784,69 @@ namespace Avalonia.Controls } } + private void MovePageRight() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageRight(); + } + + private void MovePageLeft() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageLeft(); + } + private void MovePageUp() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageUp(); + } + + private void MovePageDown() + { + ScrollViewer? childScrollviewer = FetchChildScrollViewer(10, 0, this.VisualChildren); + if (childScrollviewer == null) + { + return; + } + childScrollviewer.PageDown(); + } + + private ScrollViewer? FetchChildScrollViewer(in int maxSearchDepth, int currentSearchDepth, IAvaloniaList visualChildren) + { + foreach(Visual innerChild in visualChildren) + { + if(innerChild is ScrollViewer) + { + return (ScrollViewer?)innerChild; + } + else + { + if (currentSearchDepth < maxSearchDepth) + { + ScrollViewer? innerScrollViewer = FetchChildScrollViewer(maxSearchDepth, currentSearchDepth + 1, innerChild.VisualChildren); + if (innerScrollViewer != null) + { + return innerScrollViewer; + } + } + } + } + + return null; + } + /// /// Select all text in the TextBox /// From 5416c6028801b23f5660f32a7d0f098e7b71d3c2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 17 May 2023 16:01:42 +0200 Subject: [PATCH 007/391] update sample for simulate issue --- .../ViewModels/ListBoxPageViewModel.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs index 7f32536b11..9c30992624 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -21,7 +21,7 @@ namespace ControlCatalog.ViewModels public ListBoxPageViewModel() { Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); - + Selection = new SelectionModel(); Selection.Select(1); @@ -34,7 +34,13 @@ namespace ControlCatalog.ViewModels (t ? Avalonia.Controls.SelectionMode.Toggle : 0) | (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0)); - AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem())); + AddItemCommand = MiniCommand.Create(() => + { + var item = GenerateItem(); + Items.Add(item); + Selection.Clear(); + Selection.Select(Items.Count - 1); + }); RemoveItemCommand = MiniCommand.Create(() => { @@ -96,7 +102,7 @@ namespace ControlCatalog.ViewModels public MiniCommand RemoveItemCommand { get; } public MiniCommand SelectRandomItemCommand { get; } - private ItemModel GenerateItem() => new ItemModel(_counter ++); + private ItemModel GenerateItem() => new ItemModel(_counter++); } /// From 74412481d8a8a5b74bf3354c632294c080c9a447 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 17 May 2023 16:02:27 +0200 Subject: [PATCH 008/391] fix: Issue #6263 --- .../Primitives/SelectingItemsControl.cs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index af82a89517..03c6eebf13 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -5,9 +5,7 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Xml.Linq; using Avalonia.Controls.Selection; -using Avalonia.Controls.Utils; using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; @@ -171,7 +169,7 @@ namespace Avalonia.Controls.Primitives /// public event EventHandler? SelectionChanged { - add => AddHandler(SelectionChangedEvent, value); + add => AddHandler(SelectionChangedEvent, value); remove => RemoveHandler(SelectionChangedEvent, value); } @@ -369,7 +367,7 @@ namespace Avalonia.Controls.Primitives /// public bool WrapSelection { - get => GetValue(WrapSelectionProperty); + get => GetValue(WrapSelectionProperty); set => SetValue(WrapSelectionProperty, value); } @@ -382,7 +380,7 @@ namespace Avalonia.Controls.Primitives /// protected SelectionMode SelectionMode { - get => GetValue(SelectionModeProperty); + get => GetValue(SelectionModeProperty); set => SetValue(SelectionModeProperty, value); } @@ -465,7 +463,10 @@ namespace Avalonia.Controls.Primitives protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - AutoScrollToSelectedItemIfNecessary(); + if (Selection?.AnchorIndex is int index) + { + AutoScrollToSelectedItemIfNecessary(index); + } } /// @@ -476,7 +477,10 @@ namespace Avalonia.Controls.Primitives void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e) { LayoutUpdated -= ExecuteScrollWhenLayoutUpdated; - AutoScrollToSelectedItemIfNecessary(); + if (Selection?.AnchorIndex is int index) + { + AutoScrollToSelectedItemIfNecessary(index); + } } if (AutoScrollToSelectedItem) @@ -657,7 +661,10 @@ namespace Avalonia.Controls.Primitives if (change.Property == AutoScrollToSelectedItemProperty) { - AutoScrollToSelectedItemIfNecessary(); + if (Selection?.AnchorIndex is int index) + { + AutoScrollToSelectedItemIfNecessary(index); + } } else if (change.Property == SelectionModeProperty && _selection is object) { @@ -916,8 +923,11 @@ namespace Avalonia.Controls.Primitives if (e.PropertyName == nameof(ISelectionModel.AnchorIndex)) { _hasScrolledToSelectedItem = false; - KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(Selection.AnchorIndex)); - AutoScrollToSelectedItemIfNecessary(); + if (Selection?.AnchorIndex is int index) + { + KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(index)); + AutoScrollToSelectedItemIfNecessary(index); + } } else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex) && _oldSelectedIndex != SelectedIndex) { @@ -1045,7 +1055,7 @@ namespace Avalonia.Controls.Primitives return value; } else - { + { return AvaloniaProperty.UnsetValue; } } @@ -1103,16 +1113,19 @@ namespace Avalonia.Controls.Primitives } } - private void AutoScrollToSelectedItemIfNecessary() + private void AutoScrollToSelectedItemIfNecessary(int anchorIndex) { if (AutoScrollToSelectedItem && !_hasScrolledToSelectedItem && Presenter is object && - Selection.AnchorIndex >= 0 && + anchorIndex >= 0 && IsAttachedToVisualTree) { - ScrollIntoView(Selection.AnchorIndex); - _hasScrolledToSelectedItem = true; + Dispatcher.UIThread.Post(state => + { + ScrollIntoView((int)state!); + _hasScrolledToSelectedItem = true; + }, anchorIndex); } } From 176a6a83c5e1d3b1be5013713653a78d5869661c Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 17 May 2023 17:26:32 +0200 Subject: [PATCH 009/391] fix(test): call Threading.Dispatcher.UIThread.RunJobs(); --- tests/Avalonia.Controls.UnitTests/ListBoxTests.cs | 12 ++++++++++++ .../Primitives/SelectingItemsControlTests.cs | 10 +++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 72f476a3b0..732f888e49 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -431,6 +431,8 @@ namespace Avalonia.Controls.UnitTests items.Remove("1"); lm.ExecuteLayoutPass(); + Threading.Dispatcher.UIThread.RunJobs(); + Assert.Equal("30", target.ContainerFromIndex(items.Count - 1).DataContext); Assert.Equal("29", target.ContainerFromIndex(items.Count - 2).DataContext); Assert.Equal("28", target.ContainerFromIndex(items.Count - 3).DataContext); @@ -456,8 +458,13 @@ namespace Avalonia.Controls.UnitTests Prepare(target); + Threading.Dispatcher.UIThread.RunJobs(); + // First an item that is not index 0 must be selected. _mouse.Click(target.Presenter.Panel.Children[1]); + + Threading.Dispatcher.UIThread.RunJobs(); + Assert.Equal(1, target.Selection.AnchorIndex); // We're going to be clicking on item 9. @@ -470,6 +477,7 @@ namespace Avalonia.Controls.UnitTests // into view due to SelectionMode.AlwaysSelected. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => { + Assert.Same(item, e.TargetObject); ++raised; }); @@ -477,6 +485,8 @@ namespace Avalonia.Controls.UnitTests // Click item 9. _mouse.Click(item); + Threading.Dispatcher.UIThread.RunJobs(); + Assert.Equal(1, raised); } } @@ -743,6 +753,8 @@ namespace Avalonia.Controls.UnitTests items.Reverse(); Layout(target); + Threading.Dispatcher.UIThread.RunJobs(); + realized = target.GetRealizedContainers() .Cast() .Select(x => (string)x.DataContext) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 51745e1687..7ce9992313 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1536,7 +1536,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Prepare(target); target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.SelectedIndex = 2; - + Threading.Dispatcher.UIThread.RunJobs(); Assert.True(raised); } @@ -1561,7 +1561,7 @@ namespace Avalonia.Controls.UnitTests.Primitives target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.SelectedIndex = 2; Prepare(target); - + Threading.Dispatcher.UIThread.RunJobs(); Assert.True(raised); } @@ -1632,7 +1632,7 @@ namespace Avalonia.Controls.UnitTests.Primitives root.Child = null; target.SelectedIndex = 1; root.Child = target; - + Threading.Dispatcher.UIThread.RunJobs(); Assert.True(raised); } @@ -1689,11 +1689,11 @@ namespace Avalonia.Controls.UnitTests.Primitives var raised = false; target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true); target.SelectedIndex = 2; - + Threading.Dispatcher.UIThread.RunJobs(); Assert.False(raised); target.AutoScrollToSelectedItem = true; - + Threading.Dispatcher.UIThread.RunJobs(); Assert.True(raised); } From b6ce709d97991596d70fc393e926af162dca4b2e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 21:42:34 -0400 Subject: [PATCH 010/391] Fix win forms TryGetPlatformHandle usage + add missing docs and adjustments --- src/Avalonia.Controls/TopLevel.cs | 2 +- .../Avalonia.Win32.Interop.csproj | 3 +- .../WinForms/WinFormsAvaloniaControlHost.cs | 37 +++++++++++-------- .../Avalonia.Win32.Interop/Wpf/CursorShim.cs | 4 +- .../Wpf/Direct2DImageSurface.cs | 10 ++--- .../Avalonia.Win32.Interop/Wpf/IntSize.cs | 2 +- .../Wpf/WpfAvaloniaHost.cs | 33 ++++++++++++++--- .../Wpf/WpfInteropExtensions.cs | 2 +- .../Wpf/WpfMouseDevice.cs | 5 +-- .../Wpf/WpfTopLevelImpl.cs | 6 +-- .../Wpf/WritableBitmapSurface.cs | 2 +- 11 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 98c96e4974..c7f1f45b5d 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -383,7 +383,7 @@ namespace Avalonia.Controls /// An describing the window handle, or null if the handle /// could not be retrieved. /// - public IPlatformHandle? TryGetPlatformHandle() => ((IWindowBaseImpl?) PlatformImpl)?.Handle; + public IPlatformHandle? TryGetPlatformHandle() => (PlatformImpl as IWindowBaseImpl)?.Handle; /// /// Gets the renderer for the window. diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj index 60bb75a342..6ee33b370a 100644 --- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj +++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj @@ -1,10 +1,9 @@ - net461 + net461;net6.0-windows true true true - true true Avalonia.Win32.Interoperability diff --git a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs index 8d73bde919..f61466ead3 100644 --- a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs @@ -2,38 +2,45 @@ using System.ComponentModel; using System.Windows.Forms; using Avalonia.Controls.Embedding; -using Avalonia.Input; -using Avalonia.VisualTree; -using Avalonia.Win32.Interop; using WinFormsControl = System.Windows.Forms.Control; -namespace Avalonia.Win32.Embedding +namespace Avalonia.Win32.Interop { + /// + /// An element that allows you to host a Avalonia control on a Windows Forms page. + /// [ToolboxItem(true)] public class WinFormsAvaloniaControlHost : WinFormsControl { - private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); + private readonly EmbeddableControlRoot _root = new(); - private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero; + private IntPtr WindowHandle => _root?.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero; + /// + /// Initializes a new instance of the class. + /// public WinFormsAvaloniaControlHost() { SetStyle(ControlStyles.AllPaintingInWmPaint, true); UnmanagedMethods.SetParent(WindowHandle, Handle); _root.Prepare(); if (_root.IsFocused) - _root.FocusManager.ClearFocus(); + _root.FocusManager?.ClearFocus(); _root.GotFocus += RootGotFocus; FixPosition(); } + /// + /// Gets or sets the Avalonia control hosted by the element. + /// public Avalonia.Controls.Control Content { - get { return (Avalonia.Controls.Control)_root.Content; } - set { _root.Content = value; } + get => (Avalonia.Controls.Control)_root.Content; + set => _root.Content = value; } + /// protected override void Dispose(bool disposing) { if (disposing) @@ -46,27 +53,27 @@ namespace Avalonia.Win32.Embedding UnmanagedMethods.SetFocus(WindowHandle); } + /// protected override void OnGotFocus(EventArgs e) { if (_root != null) UnmanagedMethods.SetFocus(WindowHandle); } - - - void FixPosition() + + private void FixPosition() { if (_root != null && Width > 0 && Height > 0) UnmanagedMethods.MoveWindow(WindowHandle, 0, 0, Width, Height, true); } - - - + + /// protected override void OnResize(EventArgs e) { FixPosition(); base.OnResize(e); } + /// protected override void OnPaint(PaintEventArgs e) { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs index 2ad6287689..fb94509c16 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs @@ -7,7 +7,7 @@ using System.Windows.Input; namespace Avalonia.Win32.Interop.Wpf { - static class CursorShim + internal static class CursorShim { public static Cursor FromHCursor(IntPtr hcursor) { @@ -20,7 +20,7 @@ namespace Avalonia.Win32.Interop.Wpf return rv; } - class SafeHandleShim : SafeHandle + private class SafeHandleShim : SafeHandle { public SafeHandleShim(IntPtr hcursor) : base(new IntPtr(-1), false) { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs index f900204504..f3c6d55444 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs @@ -18,9 +18,9 @@ using Usage = SharpDX.Direct3D9.Usage; namespace Avalonia.Win32.Interop.Wpf { - class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable + internal class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable { - class SwapBuffer: IDisposable + private class SwapBuffer: IDisposable { private readonly Query _event; private readonly SharpDX.Direct3D11.Resource _resource; @@ -114,7 +114,7 @@ namespace Avalonia.Win32.Interop.Wpf [DllImport("user32.dll", SetLastError = false)] private static extern IntPtr GetDesktopWindow(); - static void EnsureDirectX() + private static void EnsureDirectX() { if(s_d3DDevice != null) return; @@ -168,13 +168,13 @@ namespace Avalonia.Win32.Interop.Wpf return _backBuffer.Target; } - static void RemoveAndDispose(ref T d) where T : IDisposable + private static void RemoveAndDispose(ref T d) where T : IDisposable { d?.Dispose(); d = default; } - void Swap() + private void Swap() { _backBuffer.Flush(); _image.Lock(); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs index 3bfbf4bd92..6feb204c52 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs @@ -2,7 +2,7 @@ namespace Avalonia.Win32.Interop.Wpf { - struct IntSize : IEquatable + internal struct IntSize : IEquatable { public bool Equals(IntSize other) { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs index 7883c28153..b2b53f301b 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs @@ -2,15 +2,24 @@ using System.Threading; using System.Windows; using System.Windows.Markup; +using Avalonia.Win32.Interop.Wpf; +using AvControl = Avalonia.Controls.Control; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interop { + /// + /// An element that allows you to host a Avalonia control on a WPF page. + /// [ContentProperty("Content")] public class WpfAvaloniaHost : FrameworkElement, IDisposable, IAddChild { private WpfTopLevelImpl _impl; private readonly SynchronizationContext _sync; private bool _hasChildren; + + /// + /// Initializes a new instance of the class. + /// public WpfAvaloniaHost() { _sync = SynchronizationContext.Current; @@ -38,14 +47,17 @@ namespace Avalonia.Win32.Interop.Wpf } } - public object Content + /// + /// Gets or sets the Avalonia control hosted by the element. + /// + public AvControl Content { - get => _impl.ControlRoot.Content; + get => (AvControl)_impl.ControlRoot.Content; set => _impl.ControlRoot.Content = value; } //Separate class is needed to prevent accidental resurrection - class Disposer + private class Disposer { private readonly WpfTopLevelImpl _impl; @@ -60,6 +72,7 @@ namespace Avalonia.Win32.Interop.Wpf } } + /// protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) { _impl.InvalidateMeasure(); @@ -67,13 +80,17 @@ namespace Avalonia.Win32.Interop.Wpf return _impl.DesiredSize; } + /// protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) { _impl.Arrange(new System.Windows.Rect(arrangeSize)); return arrangeSize; } + /// protected override int VisualChildrenCount => 1; + + /// protected override System.Windows.Media.Visual GetVisualChild(int index) => _impl; ~WpfAvaloniaHost() @@ -82,6 +99,7 @@ namespace Avalonia.Win32.Interop.Wpf _sync.Post(new Disposer(_impl).Callback, null); } + /// public void Dispose() { if (_impl != null) @@ -97,9 +115,12 @@ namespace Avalonia.Win32.Interop.Wpf void IAddChild.AddChild(object value) { if (Content == null) - Content = value; + if (value is AvControl avControl) + Content = avControl; + else + throw new InvalidOperationException("WpfAvaloniaHost.Content only accepts value of Avalonia.Controls.Control type."); else - throw new InvalidOperationException(); + throw new InvalidOperationException("WpfAvaloniaHost.Content was already set."); } void IAddChild.AddText(string text) diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs index cbfc259eda..a9955e64cc 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs @@ -1,6 +1,6 @@ namespace Avalonia.Win32.Interop.Wpf { - static class WpfInteropExtensions + internal static class WpfInteropExtensions { public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y); public static System.Windows.Point ToWpfPoint(this PixelPoint pt) => new System.Windows.Point(pt.X, pt.Y); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs index 2be9c1a558..e13505fd39 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs @@ -5,7 +5,7 @@ using Avalonia.VisualTree; namespace Avalonia.Win32.Interop.Wpf { - class WpfMouseDevice : MouseDevice + internal class WpfMouseDevice : MouseDevice { private readonly WpfTopLevelImpl _impl; @@ -14,7 +14,7 @@ namespace Avalonia.Win32.Interop.Wpf _impl = impl; } - class WpfMousePointer : Pointer + private class WpfMousePointer : Pointer { private readonly WpfTopLevelImpl _impl; @@ -35,6 +35,5 @@ namespace Avalonia.Win32.Interop.Wpf System.Windows.Input.Mouse.Capture(_impl); } } - } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 8025779c90..d554451ba4 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -19,7 +19,7 @@ using MouseButton = System.Windows.Input.MouseButton; namespace Avalonia.Win32.Interop.Wpf { - class WpfTopLevelImpl : FrameworkElement, ITopLevelImpl + internal class WpfTopLevelImpl : FrameworkElement, ITopLevelImpl { private HwndSource _currentHwndSource; private readonly HwndSourceHook _hook; @@ -137,7 +137,7 @@ namespace Avalonia.Win32.Interop.Wpf protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke(); - static RawInputModifiers GetModifiers(MouseEventArgs e) + private static RawInputModifiers GetModifiers(MouseEventArgs e) { var state = Keyboard.Modifiers; var rv = default(RawInputModifiers); @@ -161,7 +161,7 @@ namespace Avalonia.Win32.Interop.Wpf return rv; } - void MouseEvent(RawPointerEventType type, MouseEventArgs e) + private void MouseEvent(RawPointerEventType type, MouseEventArgs e) => _ttl.Input?.Invoke(new RawPointerEventArgs(_mouse, (uint)e.Timestamp, _inputRoot, type, e.GetPosition(this).ToAvaloniaPoint(), GetModifiers(e))); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs index 04b4a53580..8aed6be15b 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs @@ -8,7 +8,7 @@ using PixelFormat = Avalonia.Platform.PixelFormat; namespace Avalonia.Win32.Interop.Wpf { - class WritableBitmapSurface : IFramebufferPlatformSurface + internal class WritableBitmapSurface : IFramebufferPlatformSurface { private readonly WpfTopLevelImpl _impl; private WriteableBitmap _bitmap; From 4be09e3063bd1addf1b3d26e5323eadd43099dd7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 21:44:54 -0400 Subject: [PATCH 011/391] Move to Avalonia.Win32.Interoperability project for correctness --- Avalonia.sln | 3 ++- samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml | 2 +- samples/interop/WindowsInteropTest/WindowsInteropTest.csproj | 2 +- src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj | 2 +- .../Avalonia.Win32.Interoperability.csproj} | 1 - .../WinForms/WinFormsAvaloniaControlHost.cs | 3 ++- .../Wpf/CursorShim.cs | 2 +- .../Wpf/Direct2DImageSurface.cs | 2 +- .../Wpf/IntSize.cs | 2 +- .../Wpf/WpfAvaloniaHost.cs | 4 ++-- .../Wpf/WpfInteropExtensions.cs | 2 +- .../Wpf/WpfMouseDevice.cs | 2 +- .../Wpf/WpfTopLevelImpl.cs | 3 ++- .../Wpf/WritableBitmapSurface.cs | 2 +- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 2 +- 15 files changed, 18 insertions(+), 16 deletions(-) rename src/Windows/{Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj => Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj} (91%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/WinForms/WinFormsAvaloniaControlHost.cs (97%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/CursorShim.cs (95%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/Direct2DImageSurface.cs (99%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/IntSize.cs (96%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/WpfAvaloniaHost.cs (97%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/WpfInteropExtensions.cs (93%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/WpfMouseDevice.cs (96%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/WpfTopLevelImpl.cs (99%) rename src/Windows/{Avalonia.Win32.Interop => Avalonia.Win32.Interoperability}/Wpf/WritableBitmapSurface.cs (98%) diff --git a/Avalonia.sln b/Avalonia.sln index 9670327d67..0bae725d33 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -135,7 +135,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interoperability", "src\Windows\Avalonia.Win32.Interoperability\Avalonia.Win32.Interoperability.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}" EndProject @@ -251,6 +251,7 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppWithoutLifetime", "samples\AppWithoutLifetime\AppWithoutLifetime.csproj", "{F8928267-688E-4A51-989C-612A72446D33}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo", "samples\SafeAreaDemo\SafeAreaDemo.csproj", "{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Android", "samples\SafeAreaDemo.Android\SafeAreaDemo.Android.csproj", "{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}" diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 1d8dc32a69..f7ad18d569 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -6,7 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WindowsInteropTest" xmlns:embedding="clr-namespace:Avalonia.Win32.Embedding;assembly=Avalonia.Win32" - xmlns:wpf="clr-namespace:Avalonia.Win32.Interop.Wpf;assembly=Avalonia.Win32.Interop" + xmlns:wpf="clr-namespace:Avalonia.Win32.Interop.Wpf;assembly=Avalonia.Win32.Interoperability" mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400" MinWidth="500" MinHeight="400"> diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 95f77f6df9..e33091680e 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index 3c2d7b3322..bbe2a92074 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj similarity index 91% rename from src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj rename to src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index 6ee33b370a..9565bd86be 100644 --- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -5,7 +5,6 @@ true true true - Avalonia.Win32.Interoperability diff --git a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs similarity index 97% rename from src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs rename to src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs index f61466ead3..d04984811a 100644 --- a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs @@ -2,9 +2,10 @@ using System.ComponentModel; using System.Windows.Forms; using Avalonia.Controls.Embedding; +using Avalonia.Win32.Interop; using WinFormsControl = System.Windows.Forms.Control; -namespace Avalonia.Win32.Interop +namespace Avalonia.Win32.Interoperability { /// /// An element that allows you to host a Avalonia control on a Windows Forms page. diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs similarity index 95% rename from src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs index fb94509c16..a019f72cd8 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/CursorShim.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Windows.Input; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal static class CursorShim { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs similarity index 99% rename from src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs index f3c6d55444..a19fd4d3a6 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs @@ -16,7 +16,7 @@ using RenderTarget = SharpDX.Direct2D1.RenderTarget; using Surface = SharpDX.DXGI.Surface; using Usage = SharpDX.Direct3D9.Usage; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs similarity index 96% rename from src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs index 6feb204c52..a65313b6a9 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/IntSize.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal struct IntSize : IEquatable { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs similarity index 97% rename from src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs index b2b53f301b..1ca6abe033 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs @@ -2,10 +2,10 @@ using System.Threading; using System.Windows; using System.Windows.Markup; -using Avalonia.Win32.Interop.Wpf; +using Avalonia.Win32.Interoperability.Wpf; using AvControl = Avalonia.Controls.Control; -namespace Avalonia.Win32.Interop +namespace Avalonia.Win32.Interoperability { /// /// An element that allows you to host a Avalonia control on a WPF page. diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs similarity index 93% rename from src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs index a9955e64cc..a2d7c5dcd7 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal static class WpfInteropExtensions { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs similarity index 96% rename from src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs index e13505fd39..e47631c06f 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs @@ -3,7 +3,7 @@ using Avalonia.Controls.Embedding; using Avalonia.Input; using Avalonia.VisualTree; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal class WpfMouseDevice : MouseDevice { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs similarity index 99% rename from src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs index d554451ba4..e46f029089 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs @@ -13,11 +13,12 @@ using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.Composition; +using Avalonia.Win32.Interop; using Key = Avalonia.Input.Key; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using MouseButton = System.Windows.Input.MouseButton; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal class WpfTopLevelImpl : FrameworkElement, ITopLevelImpl { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs similarity index 98% rename from src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs rename to src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs index 8aed6be15b..6a0fac3472 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs @@ -6,7 +6,7 @@ using Avalonia.Controls.Platform.Surfaces; using Avalonia.Platform; using PixelFormat = Avalonia.Platform.PixelFormat; -namespace Avalonia.Win32.Interop.Wpf +namespace Avalonia.Win32.Interoperability.Wpf { internal class WritableBitmapSurface : IFramebufferPlatformSurface { diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index 61f5996a94..2384ef6b6c 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -27,7 +27,7 @@ $(NoWarn);CA1416 - + From 79ed3b2536fe4789522b6221cfd77dd239372cdf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 22:08:42 -0400 Subject: [PATCH 012/391] Add nullable and trimming attributes --- .../Avalonia.Win32.Interoperability.csproj | 3 ++ .../WinForms/WinFormsAvaloniaControlHost.cs | 17 +++--- .../Wpf/CursorShim.cs | 2 +- .../Wpf/Direct2DImageSurface.cs | 30 ++++++----- .../Wpf/IntSize.cs | 54 ++----------------- .../Wpf/WpfAvaloniaHost.cs | 25 +++++---- .../Wpf/WpfMouseDevice.cs | 2 +- .../Wpf/WpfTopLevelImpl.cs | 42 +++++++-------- .../Wpf/WritableBitmapSurface.cs | 2 +- 9 files changed, 72 insertions(+), 105 deletions(-) diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index 9565bd86be..74b578ded4 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -5,6 +5,7 @@ true true true + true @@ -13,5 +14,7 @@ + + diff --git a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs index d04984811a..12c8d201a8 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using Avalonia.Controls.Embedding; using Avalonia.Win32.Interop; using WinFormsControl = System.Windows.Forms.Control; +using AvControl = Avalonia.Controls.Control; namespace Avalonia.Win32.Interoperability { @@ -35,9 +36,9 @@ namespace Avalonia.Win32.Interoperability /// /// Gets or sets the Avalonia control hosted by the element. /// - public Avalonia.Controls.Control Content + public AvControl? Content { - get => (Avalonia.Controls.Control)_root.Content; + get => (AvControl?)_root.Content; set => _root.Content = value; } @@ -49,7 +50,7 @@ namespace Avalonia.Win32.Interoperability base.Dispose(disposing); } - private void RootGotFocus(object sender, Interactivity.RoutedEventArgs e) + private void RootGotFocus(object? sender, Interactivity.RoutedEventArgs e) { UnmanagedMethods.SetFocus(WindowHandle); } @@ -57,14 +58,16 @@ namespace Avalonia.Win32.Interoperability /// protected override void OnGotFocus(EventArgs e) { - if (_root != null) - UnmanagedMethods.SetFocus(WindowHandle); + var handle = WindowHandle; + if (handle != default) + UnmanagedMethods.SetFocus(handle); } private void FixPosition() { - if (_root != null && Width > 0 && Height > 0) - UnmanagedMethods.MoveWindow(WindowHandle, 0, 0, Width, Height, true); + var handle = WindowHandle; + if (handle != default && Width > 0 && Height > 0) + UnmanagedMethods.MoveWindow(handle, 0, 0, Width, Height, true); } /// diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs index a019f72cd8..8719d8d1cf 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs @@ -9,7 +9,7 @@ namespace Avalonia.Win32.Interoperability.Wpf { internal static class CursorShim { - public static Cursor FromHCursor(IntPtr hcursor) + public static Cursor? FromHCursor(IntPtr hcursor) { var field = typeof(Cursor).GetFields(BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(f => f.FieldType == typeof(SafeHandle)); diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs index a19fd4d3a6..76f262e17b 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs @@ -95,19 +95,19 @@ namespace Avalonia.Win32.Interoperability.Wpf public void Flush() { - s_dxDevice.ImmediateContext.ResolveSubresource(_resource, 0, _sharedResource, 0, Format.B8G8R8A8_UNorm); + s_dxDevice!.ImmediateContext.ResolveSubresource(_resource, 0, _sharedResource, 0, Format.B8G8R8A8_UNorm); s_dxDevice.ImmediateContext.Flush(); s_dxDevice.ImmediateContext.End(_event); s_dxDevice.ImmediateContext.GetData(_event).Dispose(); } } - private D3DImage _image; - private SwapBuffer _backBuffer; + private D3DImage? _image; + private SwapBuffer? _backBuffer; private readonly WpfTopLevelImpl _impl; - private static Device s_dxDevice; - private static Direct3DEx s_d3DContext; - private static DeviceEx s_d3DDevice; + private static Device? s_dxDevice; + private static Direct3DEx? s_d3DContext; + private static DeviceEx? s_d3DDevice; private Vector _oldDpi; @@ -138,7 +138,7 @@ namespace Avalonia.Win32.Interoperability.Wpf _impl = impl; } - public RenderTarget GetOrCreateRenderTarget() + public RenderTarget? GetOrCreateRenderTarget() { EnsureDirectX(); var scale = _impl.GetScaling(); @@ -168,7 +168,7 @@ namespace Avalonia.Win32.Interoperability.Wpf return _backBuffer.Target; } - private static void RemoveAndDispose(ref T d) where T : IDisposable + private static void RemoveAndDispose(ref T? d) where T : IDisposable { d?.Dispose(); d = default; @@ -176,11 +176,15 @@ namespace Avalonia.Win32.Interoperability.Wpf private void Swap() { - _backBuffer.Flush(); - _image.Lock(); - _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _backBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true); - _image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight)); - _image.Unlock(); + _backBuffer?.Flush(); + if (_image is not null) + { + _image.Lock(); + _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, + _backBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true); + _image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight)); + _image.Unlock(); + } } public void DestroyRenderTarget() diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs index a65313b6a9..5b156c69e2 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs @@ -1,55 +1,9 @@ -using System; +namespace Avalonia.Win32.Interoperability.Wpf; -namespace Avalonia.Win32.Interoperability.Wpf +internal record struct IntSize(double Width, double Height) { - internal struct IntSize : IEquatable + public static implicit operator IntSize(System.Windows.Size size) { - public bool Equals(IntSize other) - { - return Width == other.Width && Height == other.Height; - } - - public IntSize(int width, int height) - { - Width = width; - Height = height; - } - - public IntSize(double width, double height) : this((int) width, (int) height) - { - - } - - public static implicit operator IntSize(System.Windows.Size size) - { - return new IntSize {Width = (int) size.Width, Height = (int) size.Height}; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is IntSize size && Equals(size); - } - - public override int GetHashCode() - { - unchecked - { - return (Width * 397) ^ Height; - } - } - - public static bool operator ==(IntSize left, IntSize right) - { - return left.Equals(right); - } - - public static bool operator !=(IntSize left, IntSize right) - { - return !left.Equals(right); - } - - public int Width { get; set; } - public int Height { get; set; } + return new IntSize {Width = (int) size.Width, Height = (int) size.Height}; } } diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs index 1ca6abe033..e8beea5c4b 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs @@ -13,16 +13,18 @@ namespace Avalonia.Win32.Interoperability [ContentProperty("Content")] public class WpfAvaloniaHost : FrameworkElement, IDisposable, IAddChild { - private WpfTopLevelImpl _impl; + private WpfTopLevelImpl? _impl; private readonly SynchronizationContext _sync; private bool _hasChildren; + private WpfTopLevelImpl Impl => _impl ?? throw new ObjectDisposedException("WpfAvaloniaHost was already disposed."); + /// /// Initializes a new instance of the class. /// public WpfAvaloniaHost() { - _sync = SynchronizationContext.Current; + _sync = SynchronizationContext.Current!; _impl = new WpfTopLevelImpl(); _impl.ControlRoot.Prepare(); _impl.Visibility = Visibility.Visible; @@ -50,10 +52,10 @@ namespace Avalonia.Win32.Interoperability /// /// Gets or sets the Avalonia control hosted by the element. /// - public AvControl Content + public AvControl? Content { - get => (AvControl)_impl.ControlRoot.Content; - set => _impl.ControlRoot.Content = value; + get => (AvControl?)Impl.ControlRoot.Content; + set => Impl.ControlRoot.Content = value; } //Separate class is needed to prevent accidental resurrection @@ -66,7 +68,7 @@ namespace Avalonia.Win32.Interoperability _impl = impl; } - public void Callback(object state) + public void Callback(object? state) { _impl.Dispose(); } @@ -75,15 +77,16 @@ namespace Avalonia.Win32.Interoperability /// protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) { - _impl.InvalidateMeasure(); - _impl.Measure(constraint); - return _impl.DesiredSize; + var impl = Impl; + impl.InvalidateMeasure(); + impl.Measure(constraint); + return impl.DesiredSize; } /// protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) { - _impl.Arrange(new System.Windows.Rect(arrangeSize)); + Impl.Arrange(new System.Windows.Rect(arrangeSize)); return arrangeSize; } @@ -91,7 +94,7 @@ namespace Avalonia.Win32.Interoperability protected override int VisualChildrenCount => 1; /// - protected override System.Windows.Media.Visual GetVisualChild(int index) => _impl; + protected override System.Windows.Media.Visual GetVisualChild(int index) => Impl; ~WpfAvaloniaHost() { diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs index e47631c06f..f66b17fc89 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs @@ -23,7 +23,7 @@ namespace Avalonia.Win32.Interoperability.Wpf _impl = impl; } - protected override void PlatformCapture(IInputElement control) + protected override void PlatformCapture(IInputElement? control) { if (control == null) { diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs index e46f029089..8b87e2d9d5 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs @@ -22,17 +22,17 @@ namespace Avalonia.Win32.Interoperability.Wpf { internal class WpfTopLevelImpl : FrameworkElement, ITopLevelImpl { - private HwndSource _currentHwndSource; + private HwndSource? _currentHwndSource; + private IInputRoot? _inputRoot; private readonly HwndSourceHook _hook; private readonly ITopLevelImpl _ttl; - private IInputRoot _inputRoot; private readonly IEnumerable _surfaces; private readonly IMouseDevice _mouse; private readonly IKeyboardDevice _keyboard; private Size _finalSize; public EmbeddableControlRoot ControlRoot { get; } - internal ImageSource ImageSource { get; set; } + internal ImageSource? ImageSource { get; set; } public class CustomControlRoot : EmbeddableControlRoot, IEmbeddedLayoutRoot { @@ -43,7 +43,7 @@ namespace Avalonia.Win32.Interoperability.Wpf protected override void OnMeasureInvalidated() { - ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); + ((FrameworkElement?)PlatformImpl)?.InvalidateMeasure(); } public Size AllocatedSize => ClientSize; @@ -56,7 +56,7 @@ namespace Avalonia.Win32.Interoperability.Wpf _ttl = this; _surfaces = new object[] {new WritableBitmapSurface(this), new Direct2DImageSurface(this)}; _mouse = new WpfMouseDevice(this); - _keyboard = AvaloniaLocator.Current.GetService(); + _keyboard = AvaloniaLocator.Current.GetRequiredService(); ControlRoot = new CustomControlRoot(this); SnapsToDevicePixels = true; @@ -138,7 +138,7 @@ namespace Avalonia.Win32.Interoperability.Wpf protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke(); - private static RawInputModifiers GetModifiers(MouseEventArgs e) + private static RawInputModifiers GetModifiers(MouseEventArgs? e) { var state = Keyboard.Modifiers; var rv = default(RawInputModifiers); @@ -163,7 +163,7 @@ namespace Avalonia.Win32.Interoperability.Wpf } private void MouseEvent(RawPointerEventType type, MouseEventArgs e) - => _ttl.Input?.Invoke(new RawPointerEventArgs(_mouse, (uint)e.Timestamp, _inputRoot, type, + => _ttl.Input?.Invoke(new RawPointerEventArgs(_mouse, (uint)e.Timestamp, _inputRoot!, type, e.GetPosition(this).ToAvaloniaPoint(), GetModifiers(e))); protected override void OnMouseDown(MouseButtonEventArgs e) @@ -202,25 +202,25 @@ namespace Avalonia.Win32.Interoperability.Wpf } protected override void OnMouseWheel(MouseWheelEventArgs e) => - _ttl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, (uint) e.Timestamp, _inputRoot, + _ttl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, (uint) e.Timestamp, _inputRoot!, e.GetPosition(this).ToAvaloniaPoint(), new Vector(0, e.Delta), GetModifiers(e))); protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawPointerEventType.LeaveWindow, e); protected override void OnKeyDown(KeyEventArgs e) - => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, RawKeyEventType.KeyDown, + => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot!, RawKeyEventType.KeyDown, (Key) e.Key, GetModifiers(null))); protected override void OnKeyUp(KeyEventArgs e) - => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot, RawKeyEventType.KeyUp, + => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot!, RawKeyEventType.KeyUp, (Key)e.Key, GetModifiers(null))); protected override void OnTextInput(TextCompositionEventArgs e) - => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text)); + => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot!, e.Text)); - void ITopLevelImpl.SetCursor(ICursorImpl cursor) + void ITopLevelImpl.SetCursor(ICursorImpl? cursor) { if (cursor == null) Cursor = Cursors.Arrow; @@ -228,15 +228,15 @@ namespace Avalonia.Win32.Interoperability.Wpf Cursor = CursorShim.FromHCursor(handle.Handle); } - Action ITopLevelImpl.Input { get; set; } //TODO - Action ITopLevelImpl.Paint { get; set; } - Action ITopLevelImpl.Resized { get; set; } - Action ITopLevelImpl.ScalingChanged { get; set; } + Action? ITopLevelImpl.Input { get; set; } //TODO + Action? ITopLevelImpl.Paint { get; set; } + Action? ITopLevelImpl.Resized { get; set; } + Action? ITopLevelImpl.ScalingChanged { get; set; } - Action ITopLevelImpl.TransparencyLevelChanged { get; set; } + Action? ITopLevelImpl.TransparencyLevelChanged { get; set; } - Action ITopLevelImpl.Closed { get; set; } - public new Action LostFocus { get; set; } + Action? ITopLevelImpl.Closed { get; set; } + public new Action? LostFocus { get; set; } internal Vector GetScaling() { @@ -246,7 +246,7 @@ namespace Avalonia.Win32.Interoperability.Wpf return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22); } - public IPopupImpl CreatePopup() => null; + public IPopupImpl? CreatePopup() => null; public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { } @@ -256,6 +256,6 @@ namespace Avalonia.Win32.Interoperability.Wpf public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1); - public object TryGetFeature(Type featureType) => null; + public object? TryGetFeature(Type featureType) => null; } } diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs index 6a0fac3472..bf824c9ea5 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs @@ -11,7 +11,7 @@ namespace Avalonia.Win32.Interoperability.Wpf internal class WritableBitmapSurface : IFramebufferPlatformSurface { private readonly WpfTopLevelImpl _impl; - private WriteableBitmap _bitmap; + private WriteableBitmap? _bitmap; public WritableBitmapSurface(WpfTopLevelImpl impl) { _impl = impl; From 691ff9f33a6e832abfdeda32ee321a2122134d1f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 22:10:12 -0400 Subject: [PATCH 013/391] Make Avalonia.Win32.Interoperability build again on CI --- dirs.proj | 6 +++--- nukebuild/_build.csproj | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dirs.proj b/dirs.proj index d29aa61fcb..120212bd9f 100644 --- a/dirs.proj +++ b/dirs.proj @@ -14,11 +14,11 @@ + - - + + - diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 8999b7ca76..30e1200220 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -35,6 +35,12 @@ + + + + dirs.proj + + From c3738875e67bfe21ea15ffef68c10d96e7958481 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 22:14:06 -0400 Subject: [PATCH 014/391] Migrate to net462 --- build/SampleApp.props | 2 +- samples/BindingDemo/BindingDemo.csproj | 2 +- .../ControlCatalog.Desktop/ControlCatalog.Desktop.csproj | 2 +- samples/RenderDemo/RenderDemo.csproj | 2 +- samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml | 2 +- .../interop/WindowsInteropTest/WindowsInteropTest.csproj | 2 +- .../Avalonia.Win32.Interoperability.csproj | 2 +- .../Avalonia.Designer.HostApp.csproj | 2 +- src/tools/Avalonia.Designer.HostApp/Program.cs | 2 +- tests/Avalonia.UnitTests/InvariantCultureFixture.cs | 9 --------- 10 files changed, 9 insertions(+), 18 deletions(-) diff --git a/build/SampleApp.props b/build/SampleApp.props index 285f880129..6a7548e717 100644 --- a/build/SampleApp.props +++ b/build/SampleApp.props @@ -1,5 +1,5 @@ - + WinExe diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index f094c0081c..ad89120522 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index c6405dabb6..40c96a44d1 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -2,7 +2,7 @@ Exe - net461 + net462 x64 diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index b37ecf01b9..af746e0aa5 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -21,7 +21,7 @@ - + diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index f7ad18d569..27bb57afdf 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -6,7 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WindowsInteropTest" xmlns:embedding="clr-namespace:Avalonia.Win32.Embedding;assembly=Avalonia.Win32" - xmlns:wpf="clr-namespace:Avalonia.Win32.Interop.Wpf;assembly=Avalonia.Win32.Interoperability" + xmlns:wpf="clr-namespace:Avalonia.Win32.Interopiblity;assembly=Avalonia.Win32.Interoperability" mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400" MinWidth="500" MinHeight="400"> diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index e33091680e..58a58df46b 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -1,7 +1,7 @@  WinExe - net461 + net462 x64 true true diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index 74b578ded4..d6dcae1cf4 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -1,6 +1,6 @@ - net461;net6.0-windows + net462;net6.0-windows true true true diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index f3af312d1a..20b5323ed0 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -1,7 +1,7 @@  Exe - net461;netcoreapp2.0 + net462;netcoreapp2.0 diff --git a/src/tools/Avalonia.Designer.HostApp/Program.cs b/src/tools/Avalonia.Designer.HostApp/Program.cs index 4472dac4e3..43e6101446 100644 --- a/src/tools/Avalonia.Designer.HostApp/Program.cs +++ b/src/tools/Avalonia.Designer.HostApp/Program.cs @@ -8,7 +8,7 @@ namespace Avalonia.Designer.HostApp { class Program { -#if NET461 +#if NETFRAMEWORK private static string s_appDir; private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) diff --git a/tests/Avalonia.UnitTests/InvariantCultureFixture.cs b/tests/Avalonia.UnitTests/InvariantCultureFixture.cs index 6dfc6d4202..c46dca5de6 100644 --- a/tests/Avalonia.UnitTests/InvariantCultureFixture.cs +++ b/tests/Avalonia.UnitTests/InvariantCultureFixture.cs @@ -17,22 +17,13 @@ namespace Avalonia.UnitTests public InvariantCultureFixture() { -#if NET461 - _restore = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; -#else _restore = CultureInfo.CurrentCulture; CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; -#endif } public void Dispose() { -#if NET461 - Thread.CurrentThread.CurrentCulture = _restore; -#else CultureInfo.CurrentCulture = _restore; -#endif } } } From 7618cd74c3fb710f5db7fe58af08828db7fc7177 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 22:23:43 -0400 Subject: [PATCH 015/391] Fix some minor issues with ControlCatalog on NetFramework --- .../ControlCatalog.Desktop.csproj | 7 +++++++ samples/ControlCatalog.Desktop/Program.cs | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 40c96a44d1..77b3515164 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -4,10 +4,17 @@ Exe net462 x64 + true + ../ControlCatalog.NetCore/app.manifest + + + + + diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 4d28f15e2c..eeb2095201 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -3,6 +3,8 @@ using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Platform; +using ControlCatalog.NetCore; +using ControlCatalog.Pages; namespace ControlCatalog { @@ -18,6 +20,15 @@ namespace ControlCatalog public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .LogToTrace() + .AfterSetup(builder => + { + builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() + { + StartupScreenIndex = 1, + }); + + EmbedSample.Implementation = new EmbedSampleWin(); + }) .UsePlatformDetect(); private static void ConfigureAssetAssembly(AppBuilder builder) From f31b42d9010ba8f3721d5eda2684e3d8c9367d93 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 23:01:19 -0400 Subject: [PATCH 016/391] Start WF control render timer when handle is created and stop when handle is destroyed --- .../WindowsInteropTest/EmbedToWinFormsDemo.cs | 23 +-- .../WinForms/WinFormsAvaloniaControlHost.cs | 147 +++++++++++------- 2 files changed, 89 insertions(+), 81 deletions(-) diff --git a/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs b/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs index e6603a817b..d37ed13559 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs @@ -1,35 +1,14 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using Avalonia.Controls; -using Avalonia.Rendering; -using Avalonia.VisualTree; +using System.Windows.Forms; using ControlCatalog; namespace WindowsInteropTest { public partial class EmbedToWinFormsDemo : Form { - private readonly IRenderer _renderer; - public EmbedToWinFormsDemo() { InitializeComponent(); avaloniaHost.Content = new MainView(); - _renderer = ((TopLevel)avaloniaHost.Content.GetVisualRoot()).Renderer; - _renderer.Start(); - } - - protected override void OnClosed(EventArgs e) - { - _renderer.Stop(); - base.OnClosed(e); } } } diff --git a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs index 12c8d201a8..9ac00a8b8d 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs @@ -6,81 +6,110 @@ using Avalonia.Win32.Interop; using WinFormsControl = System.Windows.Forms.Control; using AvControl = Avalonia.Controls.Control; -namespace Avalonia.Win32.Interoperability +namespace Avalonia.Win32.Interoperability; + +/// +/// An element that allows you to host a Avalonia control on a Windows Forms page. +/// +[ToolboxItem(true)] +public class WinFormsAvaloniaControlHost : WinFormsControl { + private AvControl? _content; + private EmbeddableControlRoot? _root; + + private IntPtr WindowHandle => _root?.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero; + /// - /// An element that allows you to host a Avalonia control on a Windows Forms page. + /// Initializes a new instance of the class. /// - [ToolboxItem(true)] - public class WinFormsAvaloniaControlHost : WinFormsControl + public WinFormsAvaloniaControlHost() { - private readonly EmbeddableControlRoot _root = new(); - - private IntPtr WindowHandle => _root?.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero; + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + } - /// - /// Initializes a new instance of the class. - /// - public WinFormsAvaloniaControlHost() + /// + /// Gets or sets the Avalonia control hosted by the element. + /// + public AvControl? Content + { + get => _content; + set { - SetStyle(ControlStyles.AllPaintingInWmPaint, true); - UnmanagedMethods.SetParent(WindowHandle, Handle); - _root.Prepare(); - if (_root.IsFocused) - _root.FocusManager?.ClearFocus(); - _root.GotFocus += RootGotFocus; - - FixPosition(); + if (_content != value) + { + _content = value; + if (_root is not null) + { + _root.Content = value; + } + } } + } - /// - /// Gets or sets the Avalonia control hosted by the element. - /// - public AvControl? Content - { - get => (AvControl?)_root.Content; - set => _root.Content = value; - } + /// + protected override void OnHandleCreated(EventArgs e) + { + _root = new(); + _root.Content = _content; + _root.Prepare(); + _root.Renderer.Start(); + _root.GotFocus += RootGotFocus; - /// - protected override void Dispose(bool disposing) - { - if (disposing) - _root.Dispose(); - base.Dispose(disposing); - } + FixPosition(); + + UnmanagedMethods.SetParent(WindowHandle, Handle); + base.OnHandleCreated(e); + } - private void RootGotFocus(object? sender, Interactivity.RoutedEventArgs e) - { - UnmanagedMethods.SetFocus(WindowHandle); - } + /// + protected override void OnHandleDestroyed(EventArgs e) + { + _root?.Dispose(); + _root = null; + base.OnHandleDestroyed(e); + } - /// - protected override void OnGotFocus(EventArgs e) + /// + protected override void Dispose(bool disposing) + { + if (disposing) { - var handle = WindowHandle; - if (handle != default) - UnmanagedMethods.SetFocus(handle); + _root?.Dispose(); + _root = null; } + base.Dispose(disposing); + } + + private void RootGotFocus(object? sender, Interactivity.RoutedEventArgs e) + { + UnmanagedMethods.SetFocus(WindowHandle); + } + + /// + protected override void OnGotFocus(EventArgs e) + { + var handle = WindowHandle; + if (handle != default) + UnmanagedMethods.SetFocus(handle); + } - private void FixPosition() - { - var handle = WindowHandle; - if (handle != default && Width > 0 && Height > 0) - UnmanagedMethods.MoveWindow(handle, 0, 0, Width, Height, true); - } + private void FixPosition() + { + var handle = WindowHandle; + if (handle != default && Width > 0 && Height > 0) + UnmanagedMethods.MoveWindow(handle, 0, 0, Width, Height, true); + } - /// - protected override void OnResize(EventArgs e) - { - FixPosition(); - base.OnResize(e); - } + /// + protected override void OnResize(EventArgs e) + { + FixPosition(); + base.OnResize(e); + } - /// - protected override void OnPaint(PaintEventArgs e) - { + /// + protected override void OnPaint(PaintEventArgs e) + { - } } } From d97b9acb4705d04f582d6fc71ee4370a88778117 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 23:27:39 -0400 Subject: [PATCH 017/391] Reimplement WpfAvaloniaHost to use HwndHost --- .../Avalonia.Win32.Interoperability.csproj | 3 +- .../Wpf/CursorShim.cs | 35 --- .../Wpf/Direct2DImageSurface.cs | 206 -------------- .../Wpf/IntSize.cs | 9 - .../Wpf/WpfAvaloniaHost.cs | 166 ++++------- .../Wpf/WpfInteropExtensions.cs | 12 - .../Wpf/WpfMouseDevice.cs | 39 --- .../Wpf/WpfTopLevelImpl.cs | 261 ------------------ .../Wpf/WritableBitmapSurface.cs | 67 ----- 9 files changed, 59 insertions(+), 739 deletions(-) delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index d6dcae1cf4..b967cfd5f2 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -4,16 +4,15 @@ true true true - true true + - diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs deleted file mode 100644 index 8719d8d1cf..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/CursorShim.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Windows.Input; - -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal static class CursorShim - { - public static Cursor? FromHCursor(IntPtr hcursor) - { - var field = typeof(Cursor).GetFields(BindingFlags.NonPublic | BindingFlags.Instance) - .FirstOrDefault(f => f.FieldType == typeof(SafeHandle)); - if (field == null) - return null; - var rv = (Cursor) FormatterServices.GetUninitializedObject(typeof(Cursor)); - field.SetValue(rv, new SafeHandleShim(hcursor)); - return rv; - } - - private class SafeHandleShim : SafeHandle - { - public SafeHandleShim(IntPtr hcursor) : base(new IntPtr(-1), false) - { - this.handle = hcursor; - } - - protected override bool ReleaseHandle() => true; - - public override bool IsInvalid => false; - } - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs deleted file mode 100644 index 76f262e17b..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/Direct2DImageSurface.cs +++ /dev/null @@ -1,206 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Interop; -using Avalonia.Direct2D1; -using SharpDX.Direct2D1; -using SharpDX.Direct3D11; -using SharpDX.Direct3D9; -using SharpDX.DXGI; -using AlphaMode = SharpDX.Direct2D1.AlphaMode; -using Device = SharpDX.Direct3D11.Device; -using Format = SharpDX.DXGI.Format; -using Query = SharpDX.Direct3D11.Query; -using QueryType = SharpDX.Direct3D11.QueryType; -using RenderTarget = SharpDX.Direct2D1.RenderTarget; -using Surface = SharpDX.DXGI.Surface; -using Usage = SharpDX.Direct3D9.Usage; - -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable - { - private class SwapBuffer: IDisposable - { - private readonly Query _event; - private readonly SharpDX.Direct3D11.Resource _resource; - private readonly SharpDX.Direct3D11.Resource _sharedResource; - public SharpDX.Direct3D9.Surface Texture { get; } - public RenderTarget Target { get;} - public IntSize Size { get; } - - public SwapBuffer(IntSize size, Vector dpi) - { - int width = (int) size.Width; - int height = (int) size.Height; - _event = new Query(s_dxDevice, new QueryDescription {Type = QueryType.Event}); - using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription - { - Width = width, - Height = height, - ArraySize = 1, - MipLevels = 1, - Format = Format.B8G8R8A8_UNorm, - Usage = ResourceUsage.Default, - SampleDescription = new SampleDescription(2, 0), - BindFlags = BindFlags.RenderTarget, - })) - using (var surface = texture.QueryInterface()) - - { - _resource = texture.QueryInterface(); - - Target = new RenderTarget(Direct2D1Platform.Direct2D1Factory, surface, - new RenderTargetProperties - { - DpiX = (float) dpi.X, - DpiY = (float) dpi.Y, - MinLevel = FeatureLevel.Level_10, - PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied), - - }); - } - using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription - { - Width = width, - Height = height, - ArraySize = 1, - MipLevels = 1, - Format = Format.B8G8R8A8_UNorm, - Usage = ResourceUsage.Default, - SampleDescription = new SampleDescription(1, 0), - BindFlags = BindFlags.RenderTarget|BindFlags.ShaderResource, - OptionFlags = ResourceOptionFlags.Shared, - })) - using (var resource = texture.QueryInterface()) - { - _sharedResource = texture.QueryInterface(); - var handle = resource.SharedHandle; - using (var texture9 = new Texture(s_d3DDevice, texture.Description.Width, - texture.Description.Height, 1, - Usage.RenderTarget, SharpDX.Direct3D9.Format.A8R8G8B8, Pool.Default, ref handle)) - Texture = texture9.GetSurfaceLevel(0); - } - Size = size; - } - - public void Dispose() - { - Texture?.Dispose(); - Target?.Dispose(); - _resource?.Dispose(); - _sharedResource?.Dispose(); - _event?.Dispose(); - } - - public void Flush() - { - s_dxDevice!.ImmediateContext.ResolveSubresource(_resource, 0, _sharedResource, 0, Format.B8G8R8A8_UNorm); - s_dxDevice.ImmediateContext.Flush(); - s_dxDevice.ImmediateContext.End(_event); - s_dxDevice.ImmediateContext.GetData(_event).Dispose(); - } - } - - private D3DImage? _image; - private SwapBuffer? _backBuffer; - private readonly WpfTopLevelImpl _impl; - private static Device? s_dxDevice; - private static Direct3DEx? s_d3DContext; - private static DeviceEx? s_d3DDevice; - private Vector _oldDpi; - - - [DllImport("user32.dll", SetLastError = false)] - private static extern IntPtr GetDesktopWindow(); - - private static void EnsureDirectX() - { - if(s_d3DDevice != null) - return; - s_d3DContext = new Direct3DEx(); - - SharpDX.Direct3D9.PresentParameters presentparams = new SharpDX.Direct3D9.PresentParameters - { - Windowed = true, - SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard, - DeviceWindowHandle = GetDesktopWindow(), - PresentationInterval = PresentInterval.Default - }; - 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); - - } - - public Direct2DImageSurface(WpfTopLevelImpl impl) - { - _impl = impl; - } - - public RenderTarget? GetOrCreateRenderTarget() - { - EnsureDirectX(); - var scale = _impl.GetScaling(); - var size = new IntSize(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y); - var dpi = scale * 96; - - if (_backBuffer!=null && _backBuffer.Size == size) - return _backBuffer.Target; - - if (_image == null || _oldDpi.X != dpi.X || _oldDpi.Y != dpi.Y) - { - _image = new D3DImage(dpi.X, dpi.Y); - _oldDpi = dpi; - } - _impl.ImageSource = _image; - - RemoveAndDispose(ref _backBuffer); - if (size == default) - { - _image.Lock(); - _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); - _image.Unlock(); - return null; - } - _backBuffer = new SwapBuffer(size, dpi); - - return _backBuffer.Target; - } - - private static void RemoveAndDispose(ref T? d) where T : IDisposable - { - d?.Dispose(); - d = default; - } - - private void Swap() - { - _backBuffer?.Flush(); - if (_image is not null) - { - _image.Lock(); - _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, - _backBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true); - _image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight)); - _image.Unlock(); - } - } - - public void DestroyRenderTarget() - { - RemoveAndDispose(ref _backBuffer); - } - - public void BeforeDrawing() - { - - } - - public void AfterDrawing() => Swap(); - public void Dispose() - { - RemoveAndDispose(ref _backBuffer); - } - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs deleted file mode 100644 index 5b156c69e2..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/IntSize.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Avalonia.Win32.Interoperability.Wpf; - -internal record struct IntSize(double Width, double Height) -{ - public static implicit operator IntSize(System.Windows.Size size) - { - return new IntSize {Width = (int) size.Width, Height = (int) size.Height}; - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs index e8beea5c4b..d9dfa6b5d0 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs @@ -1,134 +1,84 @@ using System; -using System.Threading; +using System.Runtime.InteropServices; using System.Windows; +using System.Windows.Interop; using System.Windows.Markup; -using Avalonia.Win32.Interoperability.Wpf; +using Avalonia.Controls.Embedding; +using Avalonia.Win32.Interop; using AvControl = Avalonia.Controls.Control; -namespace Avalonia.Win32.Interoperability +namespace Avalonia.Win32.Interoperability; + +/// +/// An element that allows you to host a Avalonia control on a WPF page. +/// +[ContentProperty("Content")] +public class WpfAvaloniaHost : HwndHost { + private EmbeddableControlRoot? _root; + private AvControl? _content; + /// - /// An element that allows you to host a Avalonia control on a WPF page. + /// Initializes a new instance of the class. /// - [ContentProperty("Content")] - public class WpfAvaloniaHost : FrameworkElement, IDisposable, IAddChild + public WpfAvaloniaHost() { - private WpfTopLevelImpl? _impl; - private readonly SynchronizationContext _sync; - private bool _hasChildren; + DataContextChanged += AvaloniaHwndHost_DataContextChanged; + } - private WpfTopLevelImpl Impl => _impl ?? throw new ObjectDisposedException("WpfAvaloniaHost was already disposed."); - - /// - /// Initializes a new instance of the class. - /// - public WpfAvaloniaHost() + private void AvaloniaHwndHost_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) + { + if (Content != null) { - _sync = SynchronizationContext.Current!; - _impl = new WpfTopLevelImpl(); - _impl.ControlRoot.Prepare(); - _impl.Visibility = Visibility.Visible; - SnapsToDevicePixels = true; - UseLayoutRounding = true; - PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); + Content.DataContext = e.NewValue; } + } - private void OnSourceChanged(object sender, SourceChangedEventArgs e) + /// + /// Gets or sets the Avalonia control hosted by the element. + /// + public AvControl? Content + { + get => _content; + set { - if (e.NewSource != null && !_hasChildren) - { - AddLogicalChild(_impl); - AddVisualChild(_impl); - _hasChildren = true; - } - else + if (_content != value) { - RemoveVisualChild(_impl); - RemoveLogicalChild(_impl); - _hasChildren = false; + _content = value; + if (_root is not null) + { + _root.Content = value; + } + if (value != null) + { + value.DataContext = DataContext; + } } } + } - /// - /// Gets or sets the Avalonia control hosted by the element. - /// - public AvControl? Content - { - get => (AvControl?)Impl.ControlRoot.Content; - set => Impl.ControlRoot.Content = value; - } - - //Separate class is needed to prevent accidental resurrection - private class Disposer - { - private readonly WpfTopLevelImpl _impl; - - public Disposer(WpfTopLevelImpl impl) - { - _impl = impl; - } - - public void Callback(object? state) - { - _impl.Dispose(); - } - } + /// + protected override HandleRef BuildWindowCore(HandleRef hwndParent) + { + _root = new EmbeddableControlRoot(); + _root.Content = _content; + _root.Prepare(); + _root.Renderer.Start(); - /// - protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) - { - var impl = Impl; - impl.InvalidateMeasure(); - impl.Measure(constraint); - return impl.DesiredSize; - } + var handle = _root.TryGetPlatformHandle()?.Handle + ?? throw new InvalidOperationException("WpfAvaloniaHost is unable to create EmbeddableControlRoot."); - /// - protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) + if (PresentationSource.FromVisual(this) is HwndSource source) { - Impl.Arrange(new System.Windows.Rect(arrangeSize)); - return arrangeSize; + _ = UnmanagedMethods.SetParent(handle, source.Handle); } - /// - protected override int VisualChildrenCount => 1; - - /// - protected override System.Windows.Media.Visual GetVisualChild(int index) => Impl; - - ~WpfAvaloniaHost() - { - if (_impl != null) - _sync.Post(new Disposer(_impl).Callback, null); - } - - /// - public void Dispose() - { - if (_impl != null) - { - RemoveVisualChild(_impl); - RemoveLogicalChild(_impl); - _impl.Dispose(); - _impl = null; - GC.SuppressFinalize(this); - } - } - - void IAddChild.AddChild(object value) - { - if (Content == null) - if (value is AvControl avControl) - Content = avControl; - else - throw new InvalidOperationException("WpfAvaloniaHost.Content only accepts value of Avalonia.Controls.Control type."); - else - throw new InvalidOperationException("WpfAvaloniaHost.Content was already set."); - } + return new HandleRef(_root, handle); + } - void IAddChild.AddText(string text) - { - // - } + /// + protected override void DestroyWindowCore(HandleRef hwnd) + { + _root?.Dispose(); } } diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs deleted file mode 100644 index a2d7c5dcd7..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfInteropExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal static class WpfInteropExtensions - { - public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y); - public static System.Windows.Point ToWpfPoint(this PixelPoint pt) => new System.Windows.Point(pt.X, pt.Y); - public static Point ToAvaloniaPoint(this System.Windows.Point pt) => new Point(pt.X, pt.Y); - public static PixelPoint ToAvaloniaPixelPoint(this System.Windows.Point pt) => new PixelPoint((int)pt.X, (int)pt.Y); - public static System.Windows.Size ToWpfSize(this Size pt) => new System.Windows.Size(pt.Width, pt.Height); - public static Size ToAvaloniaSize(this System.Windows.Size pt) => new Size(pt.Width, pt.Height); - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs deleted file mode 100644 index f66b17fc89..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfMouseDevice.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Avalonia.Controls.Embedding; -using Avalonia.Input; -using Avalonia.VisualTree; - -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal class WpfMouseDevice : MouseDevice - { - private readonly WpfTopLevelImpl _impl; - - public WpfMouseDevice(WpfTopLevelImpl impl) : base(new WpfMousePointer(impl)) - { - _impl = impl; - } - - private class WpfMousePointer : Pointer - { - private readonly WpfTopLevelImpl _impl; - - public WpfMousePointer(WpfTopLevelImpl impl) : base(Pointer.GetNextFreeId(), PointerType.Mouse, true) - { - _impl = impl; - } - - protected override void PlatformCapture(IInputElement? control) - { - if (control == null) - { - System.Windows.Input.Mouse.Capture(null); - } - else if (((control as Visual)?.GetVisualRoot() as EmbeddableControlRoot)?.PlatformImpl != _impl) - throw new ArgumentException("Visual belongs to unknown toplevel"); - else - System.Windows.Input.Mouse.Capture(_impl); - } - } - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs deleted file mode 100644 index 8b87e2d9d5..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfTopLevelImpl.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Media; -using Avalonia.Controls; -using Avalonia.Controls.Embedding; -using Avalonia.Input; -using Avalonia.Input.Raw; -using Avalonia.Layout; -using Avalonia.Platform; -using Avalonia.Rendering; -using Avalonia.Rendering.Composition; -using Avalonia.Win32.Interop; -using Key = Avalonia.Input.Key; -using KeyEventArgs = System.Windows.Input.KeyEventArgs; -using MouseButton = System.Windows.Input.MouseButton; - -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal class WpfTopLevelImpl : FrameworkElement, ITopLevelImpl - { - private HwndSource? _currentHwndSource; - private IInputRoot? _inputRoot; - private readonly HwndSourceHook _hook; - private readonly ITopLevelImpl _ttl; - private readonly IEnumerable _surfaces; - private readonly IMouseDevice _mouse; - private readonly IKeyboardDevice _keyboard; - private Size _finalSize; - - public EmbeddableControlRoot ControlRoot { get; } - internal ImageSource? ImageSource { get; set; } - - public class CustomControlRoot : EmbeddableControlRoot, IEmbeddedLayoutRoot - { - public CustomControlRoot(WpfTopLevelImpl impl) : base(impl) - { - EnforceClientSize = false; - } - - protected override void OnMeasureInvalidated() - { - ((FrameworkElement?)PlatformImpl)?.InvalidateMeasure(); - } - - public Size AllocatedSize => ClientSize; - } - - public WpfTopLevelImpl() - { - PresentationSource.AddSourceChangedHandler(this, OnSourceChanged); - _hook = WndProc; - _ttl = this; - _surfaces = new object[] {new WritableBitmapSurface(this), new Direct2DImageSurface(this)}; - _mouse = new WpfMouseDevice(this); - _keyboard = AvaloniaLocator.Current.GetRequiredService(); - - ControlRoot = new CustomControlRoot(this); - SnapsToDevicePixels = true; - Focusable = true; - DataContextChanged += delegate - { - ControlRoot.DataContext = DataContext; - }; - } - - private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) - { - if (msg == (int)UnmanagedMethods.WindowsMessage.WM_DPICHANGED) - _ttl.ScalingChanged?.Invoke(_ttl.RenderScaling); - return IntPtr.Zero; - } - - private void OnSourceChanged(object sender, SourceChangedEventArgs e) - { - _currentHwndSource?.RemoveHook(_hook); - _currentHwndSource = e.NewSource as HwndSource; - _currentHwndSource?.AddHook(_hook); - _ttl.ScalingChanged?.Invoke(_ttl.RenderScaling); - } - - - public IRenderer CreateRenderer(IRenderRoot root) - { - return new CompositingRenderer(root, Win32Platform.Compositor, () => _surfaces); - } - - public void Dispose() - { - _ttl.Closed?.Invoke(); - foreach(var d in _surfaces.OfType()) - d.Dispose(); - } - - Size ITopLevelImpl.ClientSize => _finalSize; - Size? ITopLevelImpl.FrameSize => null; - - double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; - - IEnumerable ITopLevelImpl.Surfaces => _surfaces; - - private Size _previousSize; - protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) - { - _finalSize = finalSize.ToAvaloniaSize(); - if (_finalSize == _previousSize) - return finalSize; - _previousSize = _finalSize; - _ttl.Resized?.Invoke(finalSize.ToAvaloniaSize(), WindowResizeReason.Unspecified); - return base.ArrangeOverride(finalSize); - } - - protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) - { - ControlRoot.Measure(availableSize.ToAvaloniaSize()); - return ControlRoot.DesiredSize.ToWpfSize(); - } - - protected override void OnRender(DrawingContext drawingContext) - { - if(ActualHeight == 0 || ActualWidth == 0) - return; - _ttl.Paint?.Invoke(new Rect(0, 0, ActualWidth, ActualHeight)); - if (ImageSource != null) - drawingContext.DrawImage(ImageSource, new System.Windows.Rect(0, 0, ActualWidth, ActualHeight)); - } - - - - void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; - - Point ITopLevelImpl.PointToClient(PixelPoint point) => PointFromScreen(point.ToWpfPoint()).ToAvaloniaPoint(); - - PixelPoint ITopLevelImpl.PointToScreen(Point point) => PointToScreen(point.ToWpfPoint()).ToAvaloniaPixelPoint(); - - protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke(); - - private static RawInputModifiers GetModifiers(MouseEventArgs? e) - { - var state = Keyboard.Modifiers; - var rv = default(RawInputModifiers); - if (state.HasFlag(ModifierKeys.Windows)) - rv |= RawInputModifiers.Meta; - if (state.HasFlag(ModifierKeys.Alt)) - rv |= RawInputModifiers.Alt; - if (state.HasFlag(ModifierKeys.Control)) - rv |= RawInputModifiers.Control; - if (state.HasFlag(ModifierKeys.Shift)) - rv |= RawInputModifiers.Shift; - if (e != null) - { - if (e.LeftButton == MouseButtonState.Pressed) - rv |= RawInputModifiers.LeftMouseButton; - if (e.RightButton == MouseButtonState.Pressed) - rv |= RawInputModifiers.RightMouseButton; - if (e.MiddleButton == MouseButtonState.Pressed) - rv |= RawInputModifiers.MiddleMouseButton; - } - return rv; - } - - private void MouseEvent(RawPointerEventType type, MouseEventArgs e) - => _ttl.Input?.Invoke(new RawPointerEventArgs(_mouse, (uint)e.Timestamp, _inputRoot!, type, - e.GetPosition(this).ToAvaloniaPoint(), GetModifiers(e))); - - protected override void OnMouseDown(MouseButtonEventArgs e) - { - RawPointerEventType type; - if(e.ChangedButton == MouseButton.Left) - type = RawPointerEventType.LeftButtonDown; - else if (e.ChangedButton == MouseButton.Middle) - type = RawPointerEventType.MiddleButtonDown; - else if (e.ChangedButton == MouseButton.Right) - type = RawPointerEventType.RightButtonDown; - else - return; - MouseEvent(type, e); - Focus(); - } - - protected override void OnMouseUp(MouseButtonEventArgs e) - { - RawPointerEventType type; - if (e.ChangedButton == MouseButton.Left) - type = RawPointerEventType.LeftButtonUp; - else if (e.ChangedButton == MouseButton.Middle) - type = RawPointerEventType.MiddleButtonUp; - else if (e.ChangedButton == MouseButton.Right) - type = RawPointerEventType.RightButtonUp; - else - return; - MouseEvent(type, e); - Focus(); - } - - protected override void OnMouseMove(MouseEventArgs e) - { - MouseEvent(RawPointerEventType.Move, e); - } - - protected override void OnMouseWheel(MouseWheelEventArgs e) => - _ttl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, (uint) e.Timestamp, _inputRoot!, - e.GetPosition(this).ToAvaloniaPoint(), new Vector(0, e.Delta), GetModifiers(e))); - - protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawPointerEventType.LeaveWindow, e); - - protected override void OnKeyDown(KeyEventArgs e) - => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot!, RawKeyEventType.KeyDown, - (Key) e.Key, - GetModifiers(null))); - - protected override void OnKeyUp(KeyEventArgs e) - => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot!, RawKeyEventType.KeyUp, - (Key)e.Key, - GetModifiers(null))); - - protected override void OnTextInput(TextCompositionEventArgs e) - => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot!, e.Text)); - - void ITopLevelImpl.SetCursor(ICursorImpl? cursor) - { - if (cursor == null) - Cursor = Cursors.Arrow; - else if (cursor is IPlatformHandle handle) - Cursor = CursorShim.FromHCursor(handle.Handle); - } - - Action? ITopLevelImpl.Input { get; set; } //TODO - Action? ITopLevelImpl.Paint { get; set; } - Action? ITopLevelImpl.Resized { get; set; } - Action? ITopLevelImpl.ScalingChanged { get; set; } - - Action? ITopLevelImpl.TransparencyLevelChanged { get; set; } - - Action? ITopLevelImpl.Closed { get; set; } - public new Action? LostFocus { get; set; } - - internal Vector GetScaling() - { - var src = PresentationSource.FromVisual(this)?.CompositionTarget; - if (src == null) - return new Vector(1, 1); - return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22); - } - - public IPopupImpl? CreatePopup() => null; - - public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { } - - public WindowTransparencyLevel TransparencyLevel { get; private set; } - - public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { } - - public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1); - - public object? TryGetFeature(Type featureType) => null; - } -} diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs deleted file mode 100644 index bf824c9ea5..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WritableBitmapSurface.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Windows; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Platform; -using PixelFormat = Avalonia.Platform.PixelFormat; - -namespace Avalonia.Win32.Interoperability.Wpf -{ - internal class WritableBitmapSurface : IFramebufferPlatformSurface - { - private readonly WpfTopLevelImpl _impl; - private WriteableBitmap? _bitmap; - public WritableBitmapSurface(WpfTopLevelImpl impl) - { - _impl = impl; - } - - public ILockedFramebuffer Lock() - { - var scale = _impl.GetScaling(); - var size = new Size(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y); - var dpi = scale * 96; - if (_bitmap == null || _bitmap.PixelWidth != (int) size.Width || _bitmap.PixelHeight != (int) size.Height) - { - _bitmap = new WriteableBitmap((int) size.Width, (int) size.Height, dpi.X, dpi.Y, - System.Windows.Media.PixelFormats.Bgra32, null); - } - return new LockedFramebuffer(_impl, _bitmap, dpi); - } - - internal class LockedFramebuffer : ILockedFramebuffer - { - private readonly WpfTopLevelImpl _impl; - private readonly WriteableBitmap _bitmap; - - public LockedFramebuffer(WpfTopLevelImpl impl, WriteableBitmap bitmap, Vector dpi) - { - _impl = impl; - _bitmap = bitmap; - Dpi = dpi; - _bitmap.Lock(); - } - - public void Dispose() - { - _bitmap.AddDirtyRect(new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight)); - _bitmap.Unlock(); - /* - using (var fileStream = new FileStream("c:\\tools\\wat.png", FileMode.Create)) - { - BitmapEncoder encoder = new PngBitmapEncoder(); - encoder.Frames.Add(BitmapFrame.Create(_bitmap)); - encoder.Save(fileStream); - }*/ - _impl.ImageSource = _bitmap; - } - - public IntPtr Address => _bitmap.BackBuffer; - public PixelSize Size => new PixelSize(_bitmap.PixelWidth, _bitmap.PixelHeight); - public int RowBytes => _bitmap.BackBufferStride; - public Vector Dpi { get; } - public PixelFormat Format => PixelFormat.Bgra8888; - } - } -} From da157ff509f058aa5bbcb4ee2898e5314d742050 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 23:28:01 -0400 Subject: [PATCH 018/391] Fix WindowsInteropTest --- .../EmbedToWinFormsDemo.Designer.cs | 4 +- .../WindowsInteropTest/EmbedToWpfDemo.xaml | 14 +++---- .../WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 39 ++++--------------- .../WindowsInteropTest.csproj | 3 +- 4 files changed, 17 insertions(+), 43 deletions(-) diff --git a/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs b/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs index b5dccffd4d..d8b0724520 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs @@ -1,4 +1,4 @@ -using Avalonia.Win32.Embedding; +using Avalonia.Win32.Interoperability; namespace WindowsInteropTest { @@ -116,4 +116,4 @@ namespace WindowsInteropTest private System.Windows.Forms.GroupBox groupBox2; private WinFormsAvaloniaControlHost avaloniaHost; } -} \ No newline at end of file +} diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 27bb57afdf..4d043f780c 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -4,9 +4,7 @@ xmlns:av="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:WindowsInteropTest" - xmlns:embedding="clr-namespace:Avalonia.Win32.Embedding;assembly=Avalonia.Win32" - xmlns:wpf="clr-namespace:Avalonia.Win32.Interopiblity;assembly=Avalonia.Win32.Interoperability" + xmlns:avalonia="clr-namespace:Avalonia.Win32.Interoperability;assembly=Avalonia.Win32.Interoperability" mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400" MinWidth="500" MinHeight="400"> @@ -17,17 +15,17 @@ - + - + - + - + - + diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index e1476106ee..e21ed72fa5 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -1,52 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using System.Windows; using Avalonia; using Avalonia.Controls; -using Avalonia.Rendering; -using Avalonia.VisualTree; using ControlCatalog; -using Window = System.Windows.Window; namespace WindowsInteropTest { - /// - /// Interaction logic for EmbedToWpfDemo.xaml - /// - public partial class EmbedToWpfDemo : Window + public partial class EmbedToWpfDemo { - private IRenderer _renderer; public EmbedToWpfDemo() { InitializeComponent(); - var view = new MainView(); - Host.Content = view; - var tl = (TopLevel)view.GetVisualRoot(); - tl.AttachDevTools(); - _renderer = tl.Renderer; - _renderer.Start(); - var btn = (Avalonia.Controls.Button) RightBtn.Content; + Host.Content = new MainView(); + + var btn = (Button) RightBtn.Content!; btn.Click += delegate { btn.Content += "!"; }; + Loaded += OnLoaded; } - protected override void OnClosed(EventArgs e) + private void OnLoaded(object sender, RoutedEventArgs e) { - _renderer.Stop(); - base.OnClosed(e); + TopLevel.GetTopLevel((MainView)Host.Content)!.AttachDevTools(); } } } diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 58a58df46b..6a9098b203 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -1,8 +1,7 @@  WinExe - net462 - x64 + net462;net6.0-windows true true From 7bf2b7668c583620466312ec4ae8f6d851c962df Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 22 May 2023 23:39:03 -0400 Subject: [PATCH 019/391] Use Skia in windows interop test + remove old hacks for old wpf control --- samples/interop/WindowsInteropTest/Program.cs | 7 +------ .../interop/WindowsInteropTest/WindowsInteropTest.csproj | 1 + .../Avalonia.Win32.Interoperability.csproj | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/samples/interop/WindowsInteropTest/Program.cs b/samples/interop/WindowsInteropTest/Program.cs index c2d30c67bb..830082aafe 100644 --- a/samples/interop/WindowsInteropTest/Program.cs +++ b/samples/interop/WindowsInteropTest/Program.cs @@ -16,12 +16,7 @@ namespace WindowsInteropTest System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); AppBuilder.Configure() .UseWin32() - .UseDirect2D1() - .With(new Win32PlatformOptions - { - UseWindowsUIComposition = false, - ShouldRenderOnUIThread = true // necessary for WPF - }) + .UseSkia() .SetupWithoutStarting(); System.Windows.Forms.Application.Run(new SelectorForm()); } diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 6a9098b203..ce82403bc5 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index b967cfd5f2..74e9610ad6 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -10,7 +10,6 @@ - From 0e436dc9c1ac34c928a8825894940092423f5b39 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 23 May 2023 00:07:49 -0400 Subject: [PATCH 020/391] Fix AvaloniaPreviewerNetFullToolPath --- packages/Avalonia/Avalonia.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index a2da228887..f1dae1089b 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -1,7 +1,7 @@ $(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll - $(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe + $(MSBuildThisFileDirectory)\..\tools\net462\designer\Avalonia.Designer.HostApp.exe $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll false $(UsedAvaloniaProducts);AvaloniaUI From 5c53e0cd1883fb1eb76c36ee5bed8d3cc41e4ce1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 23 May 2023 00:44:27 -0400 Subject: [PATCH 021/391] Fix missing net461 --- packages/Avalonia/Avalonia.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 8f1b39ae12..ea7524fecd 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,6 +1,6 @@  - net6.0;netstandard2.0;net461;netcoreapp2.0 + net6.0;netstandard2.0;net462;netcoreapp2.0 Avalonia @@ -35,8 +35,8 @@ false None - <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net461/Avalonia.Designer.HostApp.exe"> - tools/net461/designer + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net462/Avalonia.Designer.HostApp.exe"> + tools/net462/designer false None From 82501a689a96808fe72e00d798eba7e19e624a13 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 23 May 2023 00:47:10 -0400 Subject: [PATCH 022/391] Update PackageTags --- build/SharedVersion.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 2849262591..b7dccb1cf8 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -12,7 +12,7 @@ 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. - avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin + avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net462;uwp;xamarin https://github.com/AvaloniaUI/Avalonia/releases git $(MSBuildThisFileDirectory)\avalonia.snk From c406b51cd01d6c44994fcbf3383934758b274ad2 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 23 May 2023 02:53:41 -0400 Subject: [PATCH 023/391] Remove WPF integration after internal discussion --- .../WindowsInteropTest/EmbedToWpfDemo.xaml | 31 ----- .../WindowsInteropTest/EmbedToWpfDemo.xaml.cs | 29 ----- samples/interop/WindowsInteropTest/Program.cs | 2 +- .../SelectorForm.Designer.cs | 76 ----------- .../WindowsInteropTest/SelectorForm.cs | 30 ----- .../WindowsInteropTest/SelectorForm.resx | 120 ------------------ .../Wpf/WpfAvaloniaHost.cs | 84 ------------ 7 files changed, 1 insertion(+), 371 deletions(-) delete mode 100644 samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml delete mode 100644 samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs delete mode 100644 samples/interop/WindowsInteropTest/SelectorForm.Designer.cs delete mode 100644 samples/interop/WindowsInteropTest/SelectorForm.cs delete mode 100644 samples/interop/WindowsInteropTest/SelectorForm.resx delete mode 100644 src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml deleted file mode 100644 index 4d043f780c..0000000000 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs deleted file mode 100644 index e21ed72fa5..0000000000 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Windows; -using Avalonia; -using Avalonia.Controls; -using ControlCatalog; - -namespace WindowsInteropTest -{ - public partial class EmbedToWpfDemo - { - public EmbedToWpfDemo() - { - InitializeComponent(); - Host.Content = new MainView(); - - var btn = (Button) RightBtn.Content!; - btn.Click += delegate - { - btn.Content += "!"; - }; - - Loaded += OnLoaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - TopLevel.GetTopLevel((MainView)Host.Content)!.AttachDevTools(); - } - } -} diff --git a/samples/interop/WindowsInteropTest/Program.cs b/samples/interop/WindowsInteropTest/Program.cs index 830082aafe..4ebb88642b 100644 --- a/samples/interop/WindowsInteropTest/Program.cs +++ b/samples/interop/WindowsInteropTest/Program.cs @@ -18,7 +18,7 @@ namespace WindowsInteropTest .UseWin32() .UseSkia() .SetupWithoutStarting(); - System.Windows.Forms.Application.Run(new SelectorForm()); + System.Windows.Forms.Application.Run(new EmbedToWinFormsDemo()); } } } diff --git a/samples/interop/WindowsInteropTest/SelectorForm.Designer.cs b/samples/interop/WindowsInteropTest/SelectorForm.Designer.cs deleted file mode 100644 index 7cf402913e..0000000000 --- a/samples/interop/WindowsInteropTest/SelectorForm.Designer.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace WindowsInteropTest -{ - partial class SelectorForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.btnEmbedToWinForms = new System.Windows.Forms.Button(); - this.btnEmbedToWpf = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // btnEmbedToWinForms - // - this.btnEmbedToWinForms.Location = new System.Drawing.Point(12, 12); - this.btnEmbedToWinForms.Name = "btnEmbedToWinForms"; - this.btnEmbedToWinForms.Size = new System.Drawing.Size(201, 86); - this.btnEmbedToWinForms.TabIndex = 0; - this.btnEmbedToWinForms.Text = "Embed to WinForms"; - this.btnEmbedToWinForms.UseVisualStyleBackColor = true; - this.btnEmbedToWinForms.Click += new System.EventHandler(this.btnEmbedToWinForms_Click); - // - // btnEmbedToWpf - // - this.btnEmbedToWpf.Location = new System.Drawing.Point(219, 12); - this.btnEmbedToWpf.Name = "btnEmbedToWpf"; - this.btnEmbedToWpf.Size = new System.Drawing.Size(201, 86); - this.btnEmbedToWpf.TabIndex = 1; - this.btnEmbedToWpf.Text = "Embed to WPF"; - this.btnEmbedToWpf.UseVisualStyleBackColor = true; - this.btnEmbedToWpf.Click += new System.EventHandler(this.btnEmbedToWpf_Click); - // - // SelectorForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(432, 284); - this.Controls.Add(this.btnEmbedToWpf); - this.Controls.Add(this.btnEmbedToWinForms); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; - this.Name = "SelectorForm"; - this.Text = "Interop"; - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.Button btnEmbedToWinForms; - private System.Windows.Forms.Button btnEmbedToWpf; - } -} - diff --git a/samples/interop/WindowsInteropTest/SelectorForm.cs b/samples/interop/WindowsInteropTest/SelectorForm.cs deleted file mode 100644 index 6430694f79..0000000000 --- a/samples/interop/WindowsInteropTest/SelectorForm.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace WindowsInteropTest -{ - public partial class SelectorForm : Form - { - public SelectorForm() - { - InitializeComponent(); - } - - private void btnEmbedToWinForms_Click(object sender, EventArgs e) - { - new EmbedToWinFormsDemo().ShowDialog(this); - } - - private void btnEmbedToWpf_Click(object sender, EventArgs e) - { - new EmbedToWpfDemo().ShowDialog(); - } - } -} diff --git a/samples/interop/WindowsInteropTest/SelectorForm.resx b/samples/interop/WindowsInteropTest/SelectorForm.resx deleted file mode 100644 index 1af7de150c..0000000000 --- a/samples/interop/WindowsInteropTest/SelectorForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs b/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs deleted file mode 100644 index d9dfa6b5d0..0000000000 --- a/src/Windows/Avalonia.Win32.Interoperability/Wpf/WpfAvaloniaHost.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Interop; -using System.Windows.Markup; -using Avalonia.Controls.Embedding; -using Avalonia.Win32.Interop; -using AvControl = Avalonia.Controls.Control; - -namespace Avalonia.Win32.Interoperability; - -/// -/// An element that allows you to host a Avalonia control on a WPF page. -/// -[ContentProperty("Content")] -public class WpfAvaloniaHost : HwndHost -{ - private EmbeddableControlRoot? _root; - private AvControl? _content; - - /// - /// Initializes a new instance of the class. - /// - public WpfAvaloniaHost() - { - DataContextChanged += AvaloniaHwndHost_DataContextChanged; - } - - private void AvaloniaHwndHost_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) - { - if (Content != null) - { - Content.DataContext = e.NewValue; - } - } - - /// - /// Gets or sets the Avalonia control hosted by the element. - /// - public AvControl? Content - { - get => _content; - set - { - if (_content != value) - { - _content = value; - if (_root is not null) - { - _root.Content = value; - } - if (value != null) - { - value.DataContext = DataContext; - } - } - } - } - - /// - protected override HandleRef BuildWindowCore(HandleRef hwndParent) - { - _root = new EmbeddableControlRoot(); - _root.Content = _content; - _root.Prepare(); - _root.Renderer.Start(); - - var handle = _root.TryGetPlatformHandle()?.Handle - ?? throw new InvalidOperationException("WpfAvaloniaHost is unable to create EmbeddableControlRoot."); - - if (PresentationSource.FromVisual(this) is HwndSource source) - { - _ = UnmanagedMethods.SetParent(handle, source.Handle); - } - - return new HandleRef(_root, handle); - } - - /// - protected override void DestroyWindowCore(HandleRef hwnd) - { - _root?.Dispose(); - } -} From 59ffdd99502dbe36dd6b6767e8ed1ea55b58c8da Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 23 May 2023 03:14:11 -0400 Subject: [PATCH 024/391] Revert to net461 --- build/SampleApp.props | 2 +- build/SharedVersion.props | 2 +- packages/Avalonia/Avalonia.csproj | 6 +++--- packages/Avalonia/Avalonia.props | 2 +- samples/BindingDemo/BindingDemo.csproj | 2 +- .../ControlCatalog.Desktop/ControlCatalog.Desktop.csproj | 2 +- samples/RenderDemo/RenderDemo.csproj | 2 +- .../interop/WindowsInteropTest/WindowsInteropTest.csproj | 2 +- .../Avalonia.Win32.Interoperability.csproj | 2 +- .../Avalonia.Designer.HostApp.csproj | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/SampleApp.props b/build/SampleApp.props index 6a7548e717..285f880129 100644 --- a/build/SampleApp.props +++ b/build/SampleApp.props @@ -1,5 +1,5 @@ - + WinExe diff --git a/build/SharedVersion.props b/build/SharedVersion.props index b7dccb1cf8..2849262591 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -12,7 +12,7 @@ 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. - avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net462;uwp;xamarin + avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin https://github.com/AvaloniaUI/Avalonia/releases git $(MSBuildThisFileDirectory)\avalonia.snk diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index ea7524fecd..8f1b39ae12 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,6 +1,6 @@  - net6.0;netstandard2.0;net462;netcoreapp2.0 + net6.0;netstandard2.0;net461;netcoreapp2.0 Avalonia @@ -35,8 +35,8 @@ false None - <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net462/Avalonia.Designer.HostApp.exe"> - tools/net462/designer + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net461/Avalonia.Designer.HostApp.exe"> + tools/net461/designer false None diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index f1dae1089b..a2da228887 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -1,7 +1,7 @@ $(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll - $(MSBuildThisFileDirectory)\..\tools\net462\designer\Avalonia.Designer.HostApp.exe + $(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll false $(UsedAvaloniaProducts);AvaloniaUI diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index ad89120522..f094c0081c 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 77b3515164..2c47effa38 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -2,7 +2,7 @@ Exe - net462 + net461 x64 true ../ControlCatalog.NetCore/app.manifest diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index af746e0aa5..b37ecf01b9 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -21,7 +21,7 @@ - + diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index ce82403bc5..22387c5a05 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -1,7 +1,7 @@  WinExe - net462;net6.0-windows + net461;net6.0-windows true true diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index 74e9610ad6..bf1e83fb66 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -1,6 +1,6 @@ - net462;net6.0-windows + net461;net6.0-windows true true true diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index 20b5323ed0..f3af312d1a 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -1,7 +1,7 @@  Exe - net462;netcoreapp2.0 + net461;netcoreapp2.0 From 8f952396c912f9abdd0b2c6fa1dcb46c87177947 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 18:03:11 -0400 Subject: [PATCH 025/391] Make GetVisualRoot trimmable + some other helper methods --- src/Avalonia.Base/Utilities/ThrowHelper.cs | 24 +++++++++++++++++++ .../VisualTree/VisualExtensions.cs | 19 ++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.Base/Utilities/ThrowHelper.cs diff --git a/src/Avalonia.Base/Utilities/ThrowHelper.cs b/src/Avalonia.Base/Utilities/ThrowHelper.cs new file mode 100644 index 0000000000..e97ab754e5 --- /dev/null +++ b/src/Avalonia.Base/Utilities/ThrowHelper.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Avalonia.Utilities; + +/// +/// Helper method to help inlining methods that do a throw check. +/// Equivalent of .NET6+ ArgumentNullException.ThrowIfNull() for netstandard2.0+ +/// +internal class ThrowHelper +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowIfNull([NotNull] object? argument, string paramName) + { + if (argument is null) + { + ThrowArgumentNullException(paramName); + } + } + + [DoesNotReturn] + private static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName); +} diff --git a/src/Avalonia.Base/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs index b3cdd70fa0..e244323ed9 100644 --- a/src/Avalonia.Base/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Base/VisualTree/VisualExtensions.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using Avalonia.Rendering; +using Avalonia.Utilities; namespace Avalonia.VisualTree { @@ -125,9 +128,9 @@ namespace Avalonia.VisualTree /// The visual's ancestors. public static IEnumerable GetVisualAncestors(this Visual visual) { - Visual? v = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); - v = v.VisualParent; + var v = visual.VisualParent; while (v != null) { @@ -194,7 +197,7 @@ namespace Avalonia.VisualTree /// The visual and its ancestors. public static IEnumerable GetSelfAndVisualAncestors(this Visual visual) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); yield return visual; @@ -275,7 +278,7 @@ namespace Avalonia.VisualTree /// The visual at the requested point. public static Visual? GetVisualAt(this Visual visual, Point p) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); return visual.GetVisualAt(p, x => x.IsVisible); } @@ -292,7 +295,7 @@ namespace Avalonia.VisualTree /// The visual at the requested point. public static Visual? GetVisualAt(this Visual visual, Point p, Func filter) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); var root = visual.GetVisualRoot(); @@ -321,7 +324,7 @@ namespace Avalonia.VisualTree this Visual visual, Point p) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); return visual.GetVisualsAt(p, x => x.IsVisible); } @@ -341,7 +344,7 @@ namespace Avalonia.VisualTree Point p, Func filter) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); var root = visual.GetVisualRoot(); @@ -435,7 +438,7 @@ namespace Avalonia.VisualTree /// public static IRenderRoot? GetVisualRoot(this Visual visual) { - _ = visual ?? throw new ArgumentNullException(nameof(visual)); + ThrowHelper.ThrowIfNull(visual, nameof(visual)); return visual as IRenderRoot ?? visual.VisualRoot; } From 0041401e252bd9aad146623def90f90d28e6899c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 18:04:38 -0400 Subject: [PATCH 026/391] Introduce IPlatformSettings to the public API --- src/Avalonia.Base/Input/IInputRoot.cs | 11 ++++++++++- src/Avalonia.Base/Platform/DefaultPlatformSettings.cs | 2 ++ src/Avalonia.Base/Platform/IPlatformSettings.cs | 6 +++++- src/Avalonia.Controls/TopLevel.cs | 3 +++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index d0921eea2d..f358658005 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -1,4 +1,5 @@ using Avalonia.Metadata; +using Avalonia.Platform; namespace Avalonia.Input { @@ -17,10 +18,18 @@ namespace Avalonia.Input /// Gets focus manager of the root. /// /// - /// Focus manager can be null only if application wasn't initialized yet. + /// Focus manager can be null only if window wasn't initialized yet. /// IFocusManager? FocusManager { get; } + /// + /// Represents a contract for accessing platform-specific settings and information. + /// + /// + /// Focus manager can be null only if window wasn't initialized yet. + /// + IPlatformSettings? PlatformSettings { get; } + /// /// Gets or sets the input element that the pointer is currently over. /// diff --git a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs index 08fcdb50aa..54528efec1 100644 --- a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// A default implementation of for platforms. /// + [PrivateApi] public class DefaultPlatformSettings : IPlatformSettings { public virtual Size GetTapSize(PointerType type) diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index b167cf0edc..c5d46cb7e6 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -4,7 +4,11 @@ using Avalonia.Metadata; namespace Avalonia.Platform { - [Unstable] + /// + /// The interface represents a contract for accessing platform-specific settings and information. + /// Some of these settings might be changed by used globally in the OS in runtime. + /// + [NotClientImplementable] public interface IPlatformSettings { /// diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index ec6b3f7421..be9df4188d 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -487,6 +487,9 @@ namespace Avalonia.Controls /// public IFocusManager? FocusManager => AvaloniaLocator.Current.GetService(); + /// + public IPlatformSettings? PlatformSettings => AvaloniaLocator.Current.GetService(); + /// Point IRenderRoot.PointToClient(PixelPoint p) { From 350dd60792779c9e59f4fe7f739474e8f259e307 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 18:04:51 -0400 Subject: [PATCH 027/391] Introduce PlatformHotkeyConfiguration.Instance to the public PAI --- .../Platform/PlatformHotkeyConfiguration.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index b87ca5eded..a4cf90e221 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -1,16 +1,29 @@ using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Input.Platform { - public class PlatformHotkeyConfiguration + /// + /// The PlatformHotkeyConfiguration class represents a configuration for platform-specific hotkeys in an Avalonia application. + /// + public sealed class PlatformHotkeyConfiguration { + /// + /// Retrieves shared static instance of PlatformHotkeyConfiguration. + /// + /// + /// Return value might be null, when application wasn't yet initialized. + /// + public static PlatformHotkeyConfiguration? Instance => + AvaloniaLocator.Current.GetService(); + + [PrivateApi] public PlatformHotkeyConfiguration() : this(KeyModifiers.Control) { } + [PrivateApi] public PlatformHotkeyConfiguration(KeyModifiers commandModifiers, KeyModifiers selectionModifiers = KeyModifiers.Shift, KeyModifiers wholeWordTextActionModifiers = KeyModifiers.Control) From 3515143219e40f69a4ed3b82eacfe7675566fa2f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 18:05:34 -0400 Subject: [PATCH 028/391] Use PlatformSettings from the top level internally --- src/Avalonia.Base/Input/Gestures.cs | 10 ++++----- src/Avalonia.Base/Input/MouseDevice.cs | 17 +++++++++------ src/Avalonia.Base/Input/PenDevice.cs | 27 +++++++++++++++--------- src/Avalonia.Base/Input/TouchDevice.cs | 29 ++++++++++++++++---------- src/Avalonia.Controls/ListBoxItem.cs | 2 +- tests/Avalonia.UnitTests/TestRoot.cs | 1 + 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 167f61eead..e9d23632b1 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -182,7 +182,7 @@ namespace Avalonia.Input s_lastPressPoint = e.GetPosition((Visual)ev.Source); s_holdCancellationToken = new CancellationTokenSource(); var token = s_holdCancellationToken.Token; - var settings = AvaloniaLocator.Current.GetService(); + var settings = ((IInputRoot?)visual.GetVisualRoot())?.PlatformSettings; if (settings != null) { @@ -221,7 +221,7 @@ namespace Avalonia.Input e.Source is Interactive i) { var point = e.GetCurrentPoint((Visual)target); - var settings = AvaloniaLocator.Current.GetService(); + var settings = ((IInputRoot?)i.GetVisualRoot())?.PlatformSettings; 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)); @@ -260,10 +260,10 @@ namespace Avalonia.Input var e = (PointerEventArgs)ev; if (s_lastPress.TryGetTarget(out var target)) { - if (e.Pointer == s_lastPointer) + if (e.Pointer == s_lastPointer && ev.Source is Interactive i) { var point = e.GetCurrentPoint((Visual)target); - var settings = AvaloniaLocator.Current.GetService(); + var settings = ((IInputRoot?)i.GetVisualRoot())?.PlatformSettings; 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)); @@ -273,7 +273,7 @@ namespace Avalonia.Input return; } - if (s_isHolding && ev.Source is Interactive i) + if (s_isHolding) { i.RaiseEvent(new HoldingRoutedEventArgs(HoldingState.Cancelled, s_lastPressPoint, s_lastPointer!.Type)); } diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index f3e77433a9..69c7a83900 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -2,9 +2,12 @@ using System; using System.Collections.Generic; using Avalonia.Reactive; using Avalonia.Input.Raw; +using Avalonia.Interactivity; using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Utilities; +using Avalonia.VisualTree; + #pragma warning disable CS0618 namespace Avalonia.Input @@ -126,9 +129,10 @@ namespace Avalonia.Input if (source != null) { _pointer.Capture(source); - if (source != null) + + var settings = ((IInputRoot?)(source as Interactive)?.GetVisualRoot())?.PlatformSettings; + if (settings is not null) { - var settings = AvaloniaLocator.Current.GetRequiredService(); var doubleClickTime = settings.GetDoubleTapTime(PointerType.Mouse).TotalMilliseconds; var doubleClickSize = settings.GetDoubleTapSize(PointerType.Mouse); @@ -141,11 +145,12 @@ namespace Avalonia.Input _lastClickTime = timestamp; _lastClickRect = new Rect(p, new Size()) .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); - _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); - var e = new PointerPressedEventArgs(source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount); - source.RaiseEvent(e); - return e.Handled; } + + _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); + var e = new PointerPressedEventArgs(source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount); + source.RaiseEvent(e); + return e.Handled; } return false; diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index 832f32fb03..d5ccfdb18d 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -3,8 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Avalonia.Input.Raw; +using Avalonia.Interactivity; using Avalonia.Metadata; using Avalonia.Platform; +using Avalonia.VisualTree; + #pragma warning disable CS0618 namespace Avalonia.Input @@ -80,19 +83,23 @@ namespace Avalonia.Input if (source != null) { pointer.Capture(source); - var settings = AvaloniaLocator.Current.GetService(); - var doubleClickTime = settings?.GetDoubleTapTime(PointerType.Pen).TotalMilliseconds ?? 500; - var doubleClickSize = settings?.GetDoubleTapSize(PointerType.Pen) ?? new Size(4, 4); - - if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) + var settings = ((IInputRoot?)(source as Interactive)?.GetVisualRoot())?.PlatformSettings; + if (settings is not null) { - _clickCount = 0; + var doubleClickTime = settings.GetDoubleTapTime(PointerType.Pen).TotalMilliseconds; + var doubleClickSize = settings.GetDoubleTapSize(PointerType.Pen); + + if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) + { + _clickCount = 0; + } + + ++_clickCount; + _lastClickTime = timestamp; + _lastClickRect = new Rect(p, new Size()) + .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); } - ++_clickCount; - _lastClickTime = timestamp; - _lastClickRect = new Rect(p, new Size()) - .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); var e = new PointerPressedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount); source.RaiseEvent(e); diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index 78b570da14..74c5837b84 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -3,8 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Avalonia.Input.Raw; +using Avalonia.Interactivity; using Avalonia.Metadata; using Avalonia.Platform; +using Avalonia.VisualTree; + #pragma warning disable CS0618 namespace Avalonia.Input @@ -62,19 +65,23 @@ namespace Avalonia.Input } else { - var settings = AvaloniaLocator.Current.GetRequiredService(); - var doubleClickTime = settings.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds; - var doubleClickSize = settings.GetDoubleTapSize(PointerType.Touch); - - if (!_lastClickRect.Contains(args.Position) - || ev.Timestamp - _lastClickTime > doubleClickTime) + var settings = ((IInputRoot?)(target as Interactive)?.GetVisualRoot())?.PlatformSettings; + if (settings is not null) { - _clickCount = 0; + var doubleClickTime = settings.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds; + var doubleClickSize = settings.GetDoubleTapSize(PointerType.Touch); + + if (!_lastClickRect.Contains(args.Position) + || ev.Timestamp - _lastClickTime > doubleClickTime) + { + _clickCount = 0; + } + + ++_clickCount; + _lastClickTime = ev.Timestamp; + _lastClickRect = new Rect(args.Position, new Size()) + .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); } - ++_clickCount; - _lastClickTime = ev.Timestamp; - _lastClickRect = new Rect(args.Position, new Size()) - .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); } target.RaiseEvent(new PointerPressedEventArgs(target, pointer, diff --git a/src/Avalonia.Controls/ListBoxItem.cs b/src/Avalonia.Controls/ListBoxItem.cs index c2044661e2..15136e5992 100644 --- a/src/Avalonia.Controls/ListBoxItem.cs +++ b/src/Avalonia.Controls/ListBoxItem.cs @@ -90,7 +90,7 @@ namespace Avalonia.Controls e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right) { var point = e.GetCurrentPoint(this); - var settings = AvaloniaLocator.Current.GetService(); + var settings = TopLevel.GetTopLevel(e.Source as Visual)?.PlatformSettings; var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4); var tapRect = new Rect(_pointerDownPoint, new Size()) .Inflate(new Thickness(tapSize.Width, tapSize.Height)); diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs index 569e5d114c..fcfd845602 100644 --- a/tests/Avalonia.UnitTests/TestRoot.cs +++ b/tests/Avalonia.UnitTests/TestRoot.cs @@ -67,6 +67,7 @@ namespace Avalonia.UnitTests public IKeyboardNavigationHandler KeyboardNavigationHandler => null; public IFocusManager FocusManager => AvaloniaLocator.Current.GetService(); + public IPlatformSettings PlatformSettings => AvaloniaLocator.Current.GetService(); public IInputElement PointerOverElement { get; set; } From 596f876868bb35849cf06882830444f7f81eeb44 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 18:06:08 -0400 Subject: [PATCH 029/391] Use PlatformHotkeyConfiguration.Instance instead of locator --- .../Utils/KeyboardHelper.cs | 6 ++--- src/Avalonia.Controls/ContextMenu.cs | 2 +- src/Avalonia.Controls/Control.cs | 2 +- .../Flyouts/PopupFlyoutBase.cs | 2 +- src/Avalonia.Controls/ListBox.cs | 4 ++-- src/Avalonia.Controls/MaskedTextBox.cs | 2 +- src/Avalonia.Controls/SelectableTextBlock.cs | 2 +- src/Avalonia.Controls/TextBox.cs | 11 ++++----- src/Avalonia.Controls/TopLevel.cs | 2 +- src/Avalonia.Controls/TreeView.cs | 4 ++-- src/Avalonia.Native/AvaloniaNativePlatform.cs | 4 ++-- .../ListBoxTests_Multiple.cs | 24 +++++++++---------- .../ListBoxTests_Single.cs | 14 +++++------ .../Primitives/SelectingItemsControlTests.cs | 8 +++---- .../TreeViewTests.cs | 6 ++--- 15 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs b/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs index d2b1fd4b8e..78b349fd23 100644 --- a/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs +++ b/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs @@ -24,9 +24,9 @@ namespace Avalonia.Controls.Utils } public static KeyModifiers GetPlatformCtrlOrCmdKeyModifier() - { - var keymap = AvaloniaLocator.Current.GetService(); - return keymap?.CommandModifiers ?? KeyModifiers.Control; + { + var keymap = PlatformHotkeyConfiguration.Instance; + return keymap.CommandModifiers; } } } diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 3dc1514667..238ccd091c 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -405,7 +405,7 @@ namespace Avalonia.Controls { if (IsOpen) { - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = PlatformHotkeyConfiguration.Instance; if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true && !CancelClosing()) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 8d140bdd62..4a9737a267 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -480,7 +480,7 @@ namespace Avalonia.Controls if (e.Source == this && !e.Handled) { - var keymap = AvaloniaLocator.Current.GetService()?.OpenContextMenu; + var keymap = PlatformHotkeyConfiguration.Instance?.OpenContextMenu; if (keymap is null) { diff --git a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs index 7fd9fad605..c94200ac8b 100644 --- a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs @@ -392,7 +392,7 @@ namespace Avalonia.Controls.Primitives && IsOpen && Target?.ContextFlyout == this) { - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = PlatformHotkeyConfiguration.Instance; if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true) { diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index f3f0b135b8..cd3c72c3a4 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -127,7 +127,7 @@ namespace Avalonia.Controls protected override void OnKeyDown(KeyEventArgs e) { - var hotkeys = AvaloniaLocator.Current.GetService(); + var hotkeys = PlatformHotkeyConfiguration.Instance; var ctrl = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers); if (!ctrl && @@ -165,7 +165,7 @@ namespace Avalonia.Controls internal bool UpdateSelectionFromPointerEvent(Control source, PointerEventArgs e) { - var hotkeys = AvaloniaLocator.Current.GetService(); + var hotkeys = PlatformHotkeyConfiguration.Instance; var toggle = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers); return UpdateSelectionFromEventSource( diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index 4800b7b1e4..fa4b8b2fb4 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -204,7 +204,7 @@ namespace Avalonia.Controls return; } - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = PlatformHotkeyConfiguration.Instance; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs index 8b32638835..973f086e2a 100644 --- a/src/Avalonia.Controls/SelectableTextBlock.cs +++ b/src/Avalonia.Controls/SelectableTextBlock.cs @@ -198,7 +198,7 @@ namespace Avalonia.Controls var handled = false; var modifiers = e.KeyModifiers; - var keymap = AvaloniaLocator.Current.GetRequiredService(); + var keymap = PlatformHotkeyConfiguration.Instance!; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 7bc26bf3b0..2479622b31 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -32,20 +32,17 @@ namespace Avalonia.Controls /// /// Gets a platform-specific for the Cut action /// - public static KeyGesture? CutGesture { get; } = AvaloniaLocator.Current - .GetService()?.Cut.FirstOrDefault(); + public static KeyGesture? CutGesture { get; } = PlatformHotkeyConfiguration.Instance?.Cut.FirstOrDefault(); /// /// Gets a platform-specific for the Copy action /// - public static KeyGesture? CopyGesture { get; } = AvaloniaLocator.Current - .GetService()?.Copy.FirstOrDefault(); + public static KeyGesture? CopyGesture { get; } = PlatformHotkeyConfiguration.Instance?.Copy.FirstOrDefault(); /// /// Gets a platform-specific for the Paste action /// - public static KeyGesture? PasteGesture { get; } = AvaloniaLocator.Current - .GetService()?.Paste.FirstOrDefault(); + public static KeyGesture? PasteGesture { get; } = PlatformHotkeyConfiguration.Instance?.Paste.FirstOrDefault(); /// /// Defines the property @@ -1103,7 +1100,7 @@ namespace Avalonia.Controls var handled = false; var modifiers = e.KeyModifiers; - var keymap = AvaloniaLocator.Current.GetRequiredService(); + var keymap = PlatformHotkeyConfiguration.Instance!; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); bool DetectSelection() => e.KeyModifiers.HasAllFlags(keymap.SelectionModifiers); diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index be9df4188d..5da82813e2 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -250,7 +250,7 @@ namespace Avalonia.Controls if (e is RawKeyEventArgs rawKeyEventArgs && rawKeyEventArgs.Type == RawKeyEventType.KeyDown) { - var keymap = AvaloniaLocator.Current.GetService()?.Back; + var keymap = PlatformHotkeyConfiguration.Instance?.Back; if (keymap != null) { diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index a499829d4a..d449590bd8 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -571,7 +571,7 @@ namespace Avalonia.Controls if (!e.Handled) { - var keymap = AvaloniaLocator.Current.GetService(); + var keymap = PlatformHotkeyConfiguration.Instance; bool Match(List? gestures) => gestures?.Any(g => g.Matches(e)) ?? false; if (this.SelectionMode == SelectionMode.Multiple && Match(keymap?.SelectAll)) @@ -656,7 +656,7 @@ namespace Avalonia.Controls e.Source, true, e.KeyModifiers.HasAllFlags(KeyModifiers.Shift), - e.KeyModifiers.HasAllFlags(AvaloniaLocator.Current.GetRequiredService().CommandModifiers), + e.KeyModifiers.HasAllFlags(PlatformHotkeyConfiguration.Instance!.CommandModifiers), point.Properties.IsRightButtonPressed); } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index c471cf91d2..a1ab90b3f7 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -113,8 +113,8 @@ namespace Avalonia.Native .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) .Bind().ToConstant(applicationPlatform) .Bind().ToConstant(new MacOSNativeMenuCommands(_factory.CreateApplicationCommands())); - - var hotkeys = AvaloniaLocator.Current.GetService(); + + var hotkeys = PlatformHotkeyConfiguration.Instance; if (hotkeys is not null) { hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs index ed19d1dc97..21e25a6f1c 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Shift); Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems); @@ -60,7 +60,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); SelectionChangedEventArgs receivedArgs = null; @@ -118,7 +118,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[1]); _helper.Click(target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control); _helper.Click(target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control); @@ -152,7 +152,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[1]); _helper.Click(target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control); @@ -183,7 +183,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[3]); _helper.Click(target.Presenter.Panel.Children[4], modifiers: KeyModifiers.Control); @@ -211,7 +211,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[3]); _helper.Click(target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift); @@ -239,7 +239,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[0]); _helper.Click(target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift); @@ -266,7 +266,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); SelectionChangedEventArgs receivedArgs = null; @@ -319,7 +319,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[0]); Assert.Equal(new[] { "Foo" }, target.SelectedItems); @@ -414,7 +414,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[0]); _helper.Click(target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Shift); @@ -444,7 +444,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[0]); _helper.Click(target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Shift); @@ -471,7 +471,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); root.LayoutManager.ExecuteInitialLayoutPass(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); _helper.Click(target.Presenter.Panel.Children[0]); _helper.Click(target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Control); diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs index 92ea2df2a9..f442202e26 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs @@ -49,7 +49,7 @@ namespace Avalonia.Controls.UnitTests Template = new FuncControlTemplate(CreateListBoxTemplate), ItemsSource = new[] { "Foo", "Bar", "Baz " }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs @@ -79,7 +79,7 @@ namespace Avalonia.Controls.UnitTests Template = new FuncControlTemplate(CreateListBoxTemplate), ItemsSource = new[] { "Foo", "Bar", "Baz " }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); _mouse.Click(target.Presenter.Panel.Children[0]); @@ -97,7 +97,7 @@ namespace Avalonia.Controls.UnitTests Template = new FuncControlTemplate(CreateListBoxTemplate), ItemsSource = new[] { "Foo", "Bar", "Baz " }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); target.SelectedIndex = 0; @@ -118,7 +118,7 @@ namespace Avalonia.Controls.UnitTests ItemsSource = new[] { "Foo", "Bar", "Baz " }, SelectionMode = SelectionMode.Single | SelectionMode.Toggle, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); _mouse.Click(target.Presenter.Panel.Children[0]); @@ -139,7 +139,7 @@ namespace Avalonia.Controls.UnitTests SelectionMode = SelectionMode.Toggle, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); target.SelectedIndex = 0; @@ -160,7 +160,7 @@ namespace Avalonia.Controls.UnitTests ItemsSource = new[] { "Foo", "Bar", "Baz " }, SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); target.SelectedIndex = 0; @@ -181,7 +181,7 @@ namespace Avalonia.Controls.UnitTests ItemsSource = new[] { "Foo", "Bar", "Baz " }, SelectionMode = SelectionMode.Single | SelectionMode.Toggle, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); ApplyTemplate(target); target.SelectedIndex = 1; diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index e456f25238..c41f3c18cb 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1234,7 +1234,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), ItemsSource = new[] { "Foo", "Bar", "Baz " }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); Prepare(target); var container = target.ContainerFromIndex(1)!; @@ -1258,7 +1258,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), ItemsSource = items, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); Prepare(target); _helper.Down(target.Presenter.Panel.Children[1]); @@ -1355,7 +1355,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), ItemsSource = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); Prepare(target); _helper.Down((Interactive)target.Presenter.Panel.Children[3]); @@ -1373,7 +1373,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), ItemsSource = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new PlatformHotkeyConfiguration()); Prepare(target); _helper.Down((Interactive)target.Presenter.Panel.Children[3]); diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index f4306f63db..f5e7992203 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1045,7 +1045,7 @@ namespace Avalonia.Controls.UnitTests var data = CreateTestTreeData(); var target = CreateTarget(data: data, multiSelect: true); var rootNode = data[0]; - var keymap = AvaloniaLocator.Current.GetRequiredService(); + var keymap = PlatformHotkeyConfiguration.Instance!; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1075,7 +1075,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetRequiredService(); + var keymap = PlatformHotkeyConfiguration.Instance!; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1105,7 +1105,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetRequiredService(); + var keymap = PlatformHotkeyConfiguration.Instance!; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs From 6d57d3cf7d81c4c83bf38ab0f6c726cfca9d00e6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 20:30:26 -0400 Subject: [PATCH 030/391] Introduce Application.PlatformSettings --- src/Avalonia.Base/Input/IInputRoot.cs | 2 +- src/Avalonia.Controls/Application.cs | 23 ++++++++++--- .../Accents/SystemAccentColors.cs | 34 ++++++++++++------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index f358658005..8e2a5d6932 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -26,7 +26,7 @@ namespace Avalonia.Input /// Represents a contract for accessing platform-specific settings and information. /// /// - /// Focus manager can be null only if window wasn't initialized yet. + /// PlatformSettings can be null only if window wasn't initialized yet. /// IPlatformSettings? PlatformSettings { get; } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index b98ad123f1..1187ada8a7 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -180,6 +180,17 @@ namespace Avalonia /// public IApplicationLifetime? ApplicationLifetime { get; set; } + /// + /// Represents a contract for accessing platform-specific settings and information. + /// + /// + /// PlatformSettings can be null only if application wasn't initialized yet. + /// 's is an equivalent API + /// which should always be preferred over a global one, + /// as specific top levels might have different settings set-up. + /// + public IPlatformSettings? PlatformSettings => AvaloniaLocator.Current.GetService(); + event Action>? IGlobalStyles.GlobalStylesAdded { add => _stylesAdded += value; @@ -229,10 +240,12 @@ namespace Avalonia var focusManager = new FocusManager(); InputManager = new InputManager(); - var settings = AvaloniaLocator.Current.GetRequiredService(); - settings.ColorValuesChanged += OnColorValuesChanged; - OnColorValuesChanged(settings, settings.GetColorValues()); - + if (PlatformSettings is { } settings) + { + settings.ColorValuesChanged += OnColorValuesChanged; + OnColorValuesChanged(settings, settings.GetColorValues()); + } + AvaloniaLocator.CurrentMutable .Bind().ToTransient() .Bind().ToConstant(this) @@ -242,7 +255,7 @@ namespace Avalonia .Bind().ToConstant(InputManager) .Bind().ToTransient() .Bind().ToConstant(DragDropDevice.Instance); - + // TODO: Fix this, for now we keep this behavior since someone might be relying on it in 0.9.x if (AvaloniaLocator.Current.GetService() == null) AvaloniaLocator.CurrentMutable diff --git a/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs b/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs index a4ef15f950..a796a5704d 100644 --- a/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs +++ b/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs @@ -16,18 +16,12 @@ internal class SystemAccentColors : IResourceProvider public const string AccentLight2Key = "SystemAccentColorLight2"; public const string AccentLight3Key = "SystemAccentColorLight3"; - private static readonly Color s_defaultSystemAccentColor = Color.FromRgb(0, 120, 215); - private readonly IPlatformSettings? _platformSettings; + private static readonly Color s_defaultSystemAccentColor = Color.FromRgb(0, 120, 215); private bool _invalidateColors = true; private Color _systemAccentColor; private Color _systemAccentColorDark1, _systemAccentColorDark2, _systemAccentColorDark3; private Color _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3; - public SystemAccentColors() - { - _platformSettings = AvaloniaLocator.Current.GetService(); - } - public bool HasResources => true; public bool TryGetResource(object key, ThemeVariant? theme, out object? value) { @@ -96,10 +90,12 @@ internal class SystemAccentColors : IResourceProvider Owner = owner; OwnerChanged?.Invoke(this, EventArgs.Empty); - if (_platformSettings is not null) + if (GetFromOwner(owner) is { } platformSettings) { - _platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged; + platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged; } + + _invalidateColors = true; } } @@ -110,10 +106,12 @@ internal class SystemAccentColors : IResourceProvider Owner = null; OwnerChanged?.Invoke(this, EventArgs.Empty); - if (_platformSettings is not null) + if (GetFromOwner(owner) is { } platformSettings) { - _platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged; + platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged; } + + _invalidateColors = true; } } @@ -122,13 +120,25 @@ internal class SystemAccentColors : IResourceProvider if (_invalidateColors) { _invalidateColors = false; + + var platformSettings = GetFromOwner(Owner); - _systemAccentColor = _platformSettings?.GetColorValues().AccentColor1 ?? s_defaultSystemAccentColor; + _systemAccentColor = platformSettings?.GetColorValues().AccentColor1 ?? s_defaultSystemAccentColor; (_systemAccentColorDark1,_systemAccentColorDark2, _systemAccentColorDark3, _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3) = CalculateAccentShades(_systemAccentColor); } } + private static IPlatformSettings? GetFromOwner(IResourceHost? owner) + { + return owner switch + { + Application app => app.PlatformSettings, + Visual visual => TopLevel.GetTopLevel(visual)?.PlatformSettings, + _ => null + }; + } + public static (Color d1, Color d2, Color d3, Color l1, Color l2, Color l3) CalculateAccentShades(Color accentColor) { // dark1step = (hslAccent.L - SystemAccentColorDark1.L) * 255 From 0d6306725ad129a46c6e4f14720472f9b76a8d07 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 22:14:28 -0400 Subject: [PATCH 031/391] Fix ButtonTests --- .../ButtonTests.cs | 76 ++++++++----------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 1d62588ec0..0ddc247be6 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -3,9 +3,11 @@ using System.Windows.Input; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Threading; using Avalonia.UnitTests; using Avalonia.VisualTree; using Moq; @@ -14,7 +16,7 @@ using MouseButton = Avalonia.Input.MouseButton; namespace Avalonia.Controls.UnitTests { - public class ButtonTests + public class ButtonTests : ScopedTestBase { private MouseTestHelper _helper = new MouseTestHelper(); @@ -139,11 +141,19 @@ namespace Avalonia.Controls.UnitTests renderer.Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns>((p, r, f) => r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]); - - var target = new TestButton(renderer.Object) + + using var _ = UnitTestApplication.Start(TestServices.StyledWindow); + + var root = new Window() { HitTesterOverride = renderer.Object }; + var target = new Button() { - Bounds = new Rect(0, 0, 100, 100) + Width = 100, + Height = 100, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left }; + root.Content = target; + root.Show(); bool clicked = false; @@ -165,16 +175,13 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Button_Does_Not_Raise_Click_When_PointerReleased_Outside() { - var renderer = new Mock(); - - renderer.Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns>((p, r, f) => - r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]); - - var target = new TestButton(renderer.Object) + var root = new TestRoot(); + var target = new Button() { - Bounds = new Rect(0, 0, 100, 100) + Width = 100, + Height = 100 }; + root.Child = target; bool clicked = false; @@ -203,12 +210,20 @@ namespace Avalonia.Controls.UnitTests .Returns>((p, r, f) => r.Bounds.Contains(p.Transform(r.RenderTransform.Value.Invert())) ? new Visual[] { r } : new Visual[0]); - - var target = new TestButton(renderer.Object) + + using var _ = UnitTestApplication.Start(TestServices.StyledWindow); + + var root = new Window() { HitTesterOverride = renderer.Object }; + var target = new Button() { - Bounds = new Rect(0, 0, 100, 100), + Width = 100, + Height = 100, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left, RenderTransform = new TranslateTransform { X = 100, Y = 0 } }; + root.Content = target; + root.Show(); //actual bounds of button should be 100,0,100,100 x -> translated 100 pixels //so mouse with x=150 coordinates should trigger click @@ -381,37 +396,6 @@ namespace Avalonia.Controls.UnitTests return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source }; } - private class TestButton : Button, IRenderRoot - { - public TestButton(IHitTester hit) - { - IsVisible = true; - HitTester = hit; - Renderer = new NullRenderer(); - } - - public new Rect Bounds - { - get => base.Bounds; - set => base.Bounds = value; - } - - public Size ClientSize => throw new NotImplementedException(); - - public IRenderer Renderer { get; } - public IHitTester HitTester { get; } - - public double RenderScaling => throw new NotImplementedException(); - - public IRenderTarget CreateRenderTarget() => throw new NotImplementedException(); - - public void Invalidate(Rect rect) => throw new NotImplementedException(); - - public Point PointToClient(PixelPoint p) => throw new NotImplementedException(); - - public PixelPoint PointToScreen(Point p) => throw new NotImplementedException(); - } - private void RaisePointerPressed(Button button, int clickCount, MouseButton mouseButton, Point position) { _helper.Down(button, mouseButton, position, clickCount: clickCount); From 94efefedb91fcf45dc947b78d7b6b8b243c86b83 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 22:49:10 -0400 Subject: [PATCH 032/391] Fix gesture tests --- .../Input/GesturesTests.cs | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index c3630c36b7..14fa242146 100644 --- a/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -24,13 +24,13 @@ namespace Avalonia.Base.UnitTests.Input public void Tapped_Should_Follow_Pointer_Pressed_Released() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var result = new List(); - AddHandlers(decorator, border, result, false); + AddHandlers(root, border, result, false); _mouse.Click(border); @@ -41,13 +41,13 @@ namespace Avalonia.Base.UnitTests.Input public void Tapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var result = new List(); - AddHandlers(decorator, border, result, true); + AddHandlers(root, border, result, true); _mouse.Click(border); @@ -58,13 +58,13 @@ namespace Avalonia.Base.UnitTests.Input public void Tapped_Should_Not_Be_Raised_For_Middle_Button() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); + root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Middle); @@ -75,13 +75,13 @@ namespace Avalonia.Base.UnitTests.Input public void Tapped_Should_Not_Be_Raised_For_Right_Button() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); + root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); @@ -92,13 +92,13 @@ namespace Avalonia.Base.UnitTests.Input public void RightTapped_Should_Be_Raised_For_Right_Button() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.RightTappedEvent, (_, _) => raised = true); + root.AddHandler(Gestures.RightTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); @@ -109,13 +109,13 @@ namespace Avalonia.Base.UnitTests.Input public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var result = new List(); - AddHandlers(decorator, border, result, false); + AddHandlers(root, border, result, false); _mouse.Click(border); _mouse.Down(border, clickCount: 2); @@ -127,13 +127,13 @@ namespace Avalonia.Base.UnitTests.Input public void DoubleTapped_Should_Be_Raised_Even_When_Pressed_Released_Handled() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var result = new List(); - AddHandlers(decorator, border, result, true); + AddHandlers(root, border, result, true); _mouse.Click(border); _mouse.Down(border, clickCount: 2); @@ -145,13 +145,13 @@ namespace Avalonia.Base.UnitTests.Input public void DoubleTapped_Should_Not_Be_Raised_For_Middle_Button() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); + root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Middle); _mouse.Down(border, MouseButton.Middle, clickCount: 2); @@ -163,13 +163,13 @@ namespace Avalonia.Base.UnitTests.Input public void DoubleTapped_Should_Not_Be_Raised_For_Right_Button() { Border border = new Border(); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); + root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true); _mouse.Click(border, MouseButton.Right); _mouse.Down(border, MouseButton.Right, clickCount: 2); @@ -191,13 +191,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var root = new TestRoot { Child = border }; HoldingState holding = HoldingState.Cancelled; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => holding = e.HoldingState); + root.AddHandler(Gestures.HoldingEvent, (_, e) => holding = e.HoldingState); _mouse.Down(border); Assert.False(holding != HoldingState.Cancelled); @@ -227,13 +227,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Started); + root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Started); _mouse.Down(border); Assert.False(raised); @@ -262,13 +262,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); + root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); _mouse.Down(border); Assert.False(raised); @@ -297,13 +297,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var cancelled = false; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); + root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); _mouse.Down(border); Assert.False(cancelled); @@ -333,13 +333,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var root = new TestRoot() { Child = border }; var cancelled = false; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); + root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled); _mouse.Down(border); @@ -369,13 +369,13 @@ namespace Avalonia.Base.UnitTests.Input Border border = new Border(); Gestures.SetIsHoldWithMouseEnabled(border, true); - var decorator = new Decorator + var testRoot = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); + testRoot.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed); var secondMouse = new MouseTestHelper(); @@ -392,12 +392,12 @@ namespace Avalonia.Base.UnitTests.Input } private static void AddHandlers( - Decorator decorator, + TestRoot root, Border border, IList result, bool markHandled) { - decorator.AddHandler(InputElement.PointerPressedEvent, (_, e) => + root.AddHandler(InputElement.PointerPressedEvent, (_, e) => { result.Add("dp"); @@ -407,7 +407,7 @@ namespace Avalonia.Base.UnitTests.Input } }); - decorator.AddHandler(InputElement.PointerReleasedEvent, (_, e) => + root.AddHandler(InputElement.PointerReleasedEvent, (_, e) => { result.Add("dr"); @@ -420,8 +420,8 @@ namespace Avalonia.Base.UnitTests.Input border.AddHandler(InputElement.PointerPressedEvent, (_, _) => result.Add("bp")); border.AddHandler(InputElement.PointerReleasedEvent, (_, _) => result.Add("br")); - decorator.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("dt")); - decorator.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("ddt")); + root.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("dt")); + root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("ddt")); border.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("bt")); border.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("bdt")); } @@ -438,13 +438,13 @@ namespace Avalonia.Base.UnitTests.Input Background = new SolidColorBrush(Colors.Red) }; border.GestureRecognizers.Add(new PinchGestureRecognizer()); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); + root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); var firstPoint = new Point(5, 5); var secondPoint = new Point(10, 10); @@ -466,13 +466,13 @@ namespace Avalonia.Base.UnitTests.Input Background = new SolidColorBrush(Colors.Red) }; border.GestureRecognizers.Add(new PinchGestureRecognizer()); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); + root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true); var firstPoint = new Point(5, 5); var secondPoint = new Point(10, 10); @@ -502,13 +502,13 @@ namespace Avalonia.Base.UnitTests.Input CanVerticallyScroll = true, ScrollStartDistance = 50 }); - var decorator = new Decorator + var root = new TestRoot { Child = border }; var raised = false; - decorator.AddHandler(Gestures.ScrollGestureEvent, (_, _) => raised = true); + root.AddHandler(Gestures.ScrollGestureEvent, (_, _) => raised = true); var firstTouch = new TouchTestHelper(); From 9ba579a15a8cf0151130af2c841bfd4ba032448e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 May 2023 23:00:39 -0400 Subject: [PATCH 033/391] Update documentation a bit --- src/Avalonia.Base/Input/IInputRoot.cs | 2 +- src/Avalonia.Controls/Application.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index 8e2a5d6932..2be5847220 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -23,7 +23,7 @@ namespace Avalonia.Input IFocusManager? FocusManager { get; } /// - /// Represents a contract for accessing platform-specific settings and information. + /// Represents a contract for accessing top-level platform-specific settings. /// /// /// PlatformSettings can be null only if window wasn't initialized yet. diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 1187ada8a7..30791551f5 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -181,7 +181,7 @@ namespace Avalonia public IApplicationLifetime? ApplicationLifetime { get; set; } /// - /// Represents a contract for accessing platform-specific settings and information. + /// Represents a contract for accessing global platform-specific settings. /// /// /// PlatformSettings can be null only if application wasn't initialized yet. From 917a9b4209ab8dc2c8ac24be2c5c3eb8ce6001ef Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 00:07:21 -0400 Subject: [PATCH 034/391] Keep namespace info always public, as it's a nested class, to avoid problems --- src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index d4c4ae8667..1b611db62b 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -401,8 +401,7 @@ namespace Avalonia.Build.Tasks contextClass, document.PopulateMethod, document.BuildMethod, - builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name, - document.IsPublic), + builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name, true), (closureName, closureBaseType) => populateBuilder.DefineSubType(closureBaseType, closureName, false), (closureName, returnType, parameterTypes) => From cc91a97433832acc79b686e09207bd75a82159e7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 00:07:34 -0400 Subject: [PATCH 035/391] Hide more XAML files that should be internal --- .../Themes/Fluent/ColorPicker.xaml | 3 ++- .../Themes/Fluent/ColorPreviewer.xaml | 3 ++- .../Themes/Fluent/ColorSlider.xaml | 3 ++- .../Themes/Fluent/ColorSpectrum.xaml | 3 ++- src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml | 3 ++- .../Themes/Simple/ColorPicker.xaml | 3 ++- .../Themes/Simple/ColorPreviewer.xaml | 3 ++- .../Themes/Simple/ColorSlider.xaml | 3 ++- .../Themes/Simple/ColorSpectrum.xaml | 3 ++- src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml | 3 ++- .../Diagnostics/Controls/FilterTextBox.axaml | 3 ++- .../Diagnostics/Controls/ThicknessEditor.axaml | 3 ++- 12 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml index f3100a648a..104314e54f 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml @@ -1,7 +1,8 @@  + xmlns:primitives="using:Avalonia.Controls.Primitives" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml index fabc5d0349..520e6e8db3 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml @@ -1,6 +1,7 @@  + xmlns:pc="using:Avalonia.Controls.Primitives.Converters" + x:ClassModifier="internal"> 80 diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml index 789473571b..edc6246ddd 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -1,5 +1,6 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml index ebbb006fd1..c9972125a0 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml @@ -1,6 +1,7 @@  + xmlns:controls="using:Avalonia.Controls" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml index 8793467b36..9ca55566f3 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml @@ -4,7 +4,8 @@ xmlns:converters="using:Avalonia.Controls.Converters" xmlns:primitives="using:Avalonia.Controls.Primitives" xmlns:pc="using:Avalonia.Controls.Primitives.Converters" - xmlns:globalization="using:System.Globalization"> + xmlns:globalization="using:System.Globalization" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml index d9ba8bb9d2..c20f7935f4 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml @@ -1,7 +1,8 @@  + xmlns:primitives="using:Avalonia.Controls.Primitives" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml index 9e123b2a1f..23f0b34ddd 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml @@ -1,6 +1,7 @@  + xmlns:pc="using:Avalonia.Controls.Primitives.Converters" + x:ClassModifier="internal"> 80 diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml index 472f1ca100..6b4ad68275 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml @@ -1,5 +1,6 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml index a7ad56d8b4..101a06ccb9 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml @@ -1,6 +1,7 @@  + xmlns:controls="using:Avalonia.Controls" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml index d4f02933f2..994a5e1bbc 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml @@ -4,7 +4,8 @@ xmlns:converters="using:Avalonia.Controls.Converters" xmlns:primitives="using:Avalonia.Controls.Primitives" xmlns:pc="using:Avalonia.Controls.Primitives.Converters" - xmlns:globalization="using:System.Globalization"> + xmlns:globalization="using:System.Globalization" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml b/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml index 1b5f431f36..e5af7c51de 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml @@ -1,7 +1,8 @@  + x:DataType="controls:FilterTextBox" + x:ClassModifier="internal"> diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml b/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml index 3e0ddc4b4d..1ad6985070 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml @@ -1,6 +1,7 @@  + xmlns:controls="using:Avalonia.Diagnostics.Controls" + x:ClassModifier="internal"> From d267d06a08e3576700b998bf8202cbc6662b6573 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 09:01:31 -0400 Subject: [PATCH 036/391] Move PlatformHotkeyConfiguration to IPlatformSettings --- .../Platform/PlatformHotkeyConfiguration.cs | 9 ------ .../Platform/DefaultPlatformSettings.cs | 7 ++++- .../Platform/IPlatformSettings.cs | 6 ++++ src/Avalonia.Controls.DataGrid/DataGrid.cs | 30 +++++++++---------- .../DataGridColumn.cs | 2 +- .../DataGridColumnHeader.cs | 2 +- .../Utils/KeyboardHelper.cs | 12 ++++---- src/Avalonia.Controls/ContextMenu.cs | 2 +- src/Avalonia.Controls/Control.cs | 2 +- .../Flyouts/PopupFlyoutBase.cs | 3 +- src/Avalonia.Controls/ListBox.cs | 5 ++-- src/Avalonia.Controls/MaskedTextBox.cs | 3 +- src/Avalonia.Controls/SelectableTextBlock.cs | 2 +- src/Avalonia.Controls/TextBox.cs | 8 ++--- src/Avalonia.Controls/TopLevel.cs | 2 +- src/Avalonia.Controls/TreeView.cs | 5 ++-- src/Avalonia.Native/AvaloniaNativePlatform.cs | 16 +++++----- .../TreeViewTests.cs | 6 ++-- .../Avalonia.UnitTests/UnitTestApplication.cs | 3 +- 19 files changed, 65 insertions(+), 60 deletions(-) diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index a4cf90e221..2eed0980fb 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -8,15 +8,6 @@ namespace Avalonia.Input.Platform /// public sealed class PlatformHotkeyConfiguration { - /// - /// Retrieves shared static instance of PlatformHotkeyConfiguration. - /// - /// - /// Return value might be null, when application wasn't yet initialized. - /// - public static PlatformHotkeyConfiguration? Instance => - AvaloniaLocator.Current.GetService(); - [PrivateApi] public PlatformHotkeyConfiguration() : this(KeyModifiers.Control) { diff --git a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs index 54528efec1..a4d14b0084 100644 --- a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs @@ -1,7 +1,9 @@ using System; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Media; using Avalonia.Metadata; +using Avalonia.VisualTree; namespace Avalonia.Platform { @@ -30,7 +32,10 @@ namespace Avalonia.Platform public virtual TimeSpan GetDoubleTapTime(PointerType type) => TimeSpan.FromMilliseconds(500); public virtual TimeSpan HoldWaitDuration => TimeSpan.FromMilliseconds(300); - + + public PlatformHotkeyConfiguration HotkeyConfiguration => + AvaloniaLocator.Current.GetRequiredService(); + public virtual PlatformColorValues GetColorValues() { return new PlatformColorValues diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index c5d46cb7e6..46980c6d51 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Metadata; namespace Avalonia.Platform @@ -37,6 +38,11 @@ namespace Avalonia.Platform /// TimeSpan HoldWaitDuration { get; } + /// + /// Get a configuration for platform-specific hotkeys in an Avalonia application. + /// + PlatformHotkeyConfiguration HotkeyConfiguration { get; } + /// /// Gets current system color values including dark mode and accent colors. /// diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index bfcd4750e3..7ade9f2437 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -2660,25 +2660,25 @@ namespace Avalonia.Controls internal bool ProcessDownKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessDownKeyInternal(shift, ctrl); } internal bool ProcessEndKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessEndKey(shift, ctrl); } internal bool ProcessEnterKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessEnterKey(shift, ctrl); } internal bool ProcessHomeKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessHomeKey(shift, ctrl); } @@ -2718,25 +2718,25 @@ namespace Avalonia.Controls internal bool ProcessLeftKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessLeftKey(shift, ctrl); } internal bool ProcessNextKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessNextKey(shift, ctrl); } internal bool ProcessPriorKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessPriorKey(shift, ctrl); } internal bool ProcessRightKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessRightKey(shift, ctrl); } @@ -2854,7 +2854,7 @@ namespace Avalonia.Controls internal bool ProcessUpKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessUpKey(shift, ctrl); } @@ -3124,13 +3124,13 @@ namespace Avalonia.Controls //TODO: Ensure right button is checked for internal bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit) { - KeyboardHelper.GetMetaKeyState(pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift); return UpdateStateOnMouseRightButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl); } //TODO: Ensure left button is checked for internal bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit) { - KeyboardHelper.GetMetaKeyState(pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift); return UpdateStateOnMouseLeftButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl); } @@ -4654,7 +4654,7 @@ namespace Avalonia.Controls private bool ProcessAKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift, out bool alt); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift, out bool alt); if (ctrl && !shift && !alt && SelectionMode == DataGridSelectionMode.Extended) { @@ -4923,7 +4923,7 @@ namespace Avalonia.Controls private bool ProcessF2Key(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); if (!shift && !ctrl && _editingColumnIndex == -1 && CurrentColumnIndex != -1 && GetRowSelection(CurrentSlot) && @@ -5280,7 +5280,7 @@ namespace Avalonia.Controls private bool ProcessTabKey(KeyEventArgs e) { - KeyboardHelper.GetMetaKeyState(e.KeyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift); return ProcessTabKey(e, shift, ctrl); } @@ -6099,7 +6099,7 @@ namespace Avalonia.Controls /// Whether or not the DataGrid handled the key press. private bool ProcessCopyKey(KeyModifiers modifiers) { - KeyboardHelper.GetMetaKeyState(modifiers, out bool ctrl, out bool shift, out bool alt); + KeyboardHelper.GetMetaKeyState(this, modifiers, out bool ctrl, out bool shift, out bool alt); if (ctrl && !shift && !alt && ClipboardCopyMode != DataGridClipboardCopyMode.None && SelectedItems.Count > 0) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index d1e1efdd85..96d072260c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -703,7 +703,7 @@ namespace Avalonia.Controls public void ClearSort() { //InvokeProcessSort is already validating if sorting is possible - _headerCell?.InvokeProcessSort(KeyboardHelper.GetPlatformCtrlOrCmdKeyModifier()); + _headerCell?.InvokeProcessSort(KeyboardHelper.GetPlatformCtrlOrCmdKeyModifier(OwningGrid)); } /// diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index ef1e84c745..0755864c25 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -241,7 +241,7 @@ namespace Avalonia.Controls DataGrid owningGrid = OwningGrid; DataGridSortDescription newSort; - KeyboardHelper.GetMetaKeyState(keyModifiers, out bool ctrl, out bool shift); + KeyboardHelper.GetMetaKeyState(this, keyModifiers, out bool ctrl, out bool shift); DataGridSortDescription sort = OwningColumn.GetSortDescription(); IDataGridCollectionView collectionView = owningGrid.DataConnection.CollectionView; diff --git a/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs b/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs index 78b349fd23..bcb30888dc 100644 --- a/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs +++ b/src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs @@ -10,22 +10,22 @@ namespace Avalonia.Controls.Utils { internal static class KeyboardHelper { - public static void GetMetaKeyState(KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift) + public static void GetMetaKeyState(Control target, KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift) { - ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier()); + ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier(target)); shift = modifiers.HasFlag(KeyModifiers.Shift); } - public static void GetMetaKeyState(KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift, out bool alt) + public static void GetMetaKeyState(Control target, KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift, out bool alt) { - ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier()); + ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier(target)); shift = modifiers.HasFlag(KeyModifiers.Shift); alt = modifiers.HasFlag(KeyModifiers.Alt); } - public static KeyModifiers GetPlatformCtrlOrCmdKeyModifier() + public static KeyModifiers GetPlatformCtrlOrCmdKeyModifier(Control target) { - var keymap = PlatformHotkeyConfiguration.Instance; + var keymap = TopLevel.GetTopLevel(target)!.PlatformSettings!.HotkeyConfiguration; return keymap.CommandModifiers; } } diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 238ccd091c..e3a419d7b3 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -405,7 +405,7 @@ namespace Avalonia.Controls { if (IsOpen) { - var keymap = PlatformHotkeyConfiguration.Instance; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true && !CancelClosing()) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 4a9737a267..6e0063c9ec 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -480,7 +480,7 @@ namespace Avalonia.Controls if (e.Source == this && !e.Handled) { - var keymap = PlatformHotkeyConfiguration.Instance?.OpenContextMenu; + var keymap = Application.Current!.PlatformSettings?.HotkeyConfiguration.OpenContextMenu; if (keymap is null) { diff --git a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs index c94200ac8b..c3cf1f4897 100644 --- a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs @@ -9,6 +9,7 @@ using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.Logging; using Avalonia.Reactive; +using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives { @@ -392,7 +393,7 @@ namespace Avalonia.Controls.Primitives && IsOpen && Target?.ContextFlyout == this) { - var keymap = PlatformHotkeyConfiguration.Instance; + var keymap = Application.Current!.PlatformSettings?.HotkeyConfiguration; if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true) { diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index cd3c72c3a4..220903cc04 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Selection; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -127,7 +128,7 @@ namespace Avalonia.Controls protected override void OnKeyDown(KeyEventArgs e) { - var hotkeys = PlatformHotkeyConfiguration.Instance; + var hotkeys = Application.Current!.PlatformSettings?.HotkeyConfiguration; var ctrl = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers); if (!ctrl && @@ -165,7 +166,7 @@ namespace Avalonia.Controls internal bool UpdateSelectionFromPointerEvent(Control source, PointerEventArgs e) { - var hotkeys = PlatformHotkeyConfiguration.Instance; + var hotkeys = Application.Current!.PlatformSettings?.HotkeyConfiguration; var toggle = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers); return UpdateSelectionFromEventSource( diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index fa4b8b2fb4..10eae5e7c0 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -7,6 +7,7 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.Styling; +using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -204,7 +205,7 @@ namespace Avalonia.Controls return; } - var keymap = PlatformHotkeyConfiguration.Instance; + var keymap = Application.Current!.PlatformSettings?.HotkeyConfiguration; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs index 973f086e2a..5e21681024 100644 --- a/src/Avalonia.Controls/SelectableTextBlock.cs +++ b/src/Avalonia.Controls/SelectableTextBlock.cs @@ -198,7 +198,7 @@ namespace Avalonia.Controls var handled = false; var modifiers = e.KeyModifiers; - var keymap = PlatformHotkeyConfiguration.Instance!; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 2479622b31..5f3ba2ae11 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -32,17 +32,17 @@ namespace Avalonia.Controls /// /// Gets a platform-specific for the Cut action /// - public static KeyGesture? CutGesture { get; } = PlatformHotkeyConfiguration.Instance?.Cut.FirstOrDefault(); + public static KeyGesture? CutGesture => Application.Current?.PlatformSettings?.HotkeyConfiguration.Cut.FirstOrDefault(); /// /// Gets a platform-specific for the Copy action /// - public static KeyGesture? CopyGesture { get; } = PlatformHotkeyConfiguration.Instance?.Copy.FirstOrDefault(); + public static KeyGesture? CopyGesture => Application.Current?.PlatformSettings?.HotkeyConfiguration.Copy.FirstOrDefault(); /// /// Gets a platform-specific for the Paste action /// - public static KeyGesture? PasteGesture { get; } = PlatformHotkeyConfiguration.Instance?.Paste.FirstOrDefault(); + public static KeyGesture? PasteGesture => Application.Current?.PlatformSettings?.HotkeyConfiguration.Paste.FirstOrDefault(); /// /// Defines the property @@ -1100,7 +1100,7 @@ namespace Avalonia.Controls var handled = false; var modifiers = e.KeyModifiers; - var keymap = PlatformHotkeyConfiguration.Instance!; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; bool Match(List gestures) => gestures.Any(g => g.Matches(e)); bool DetectSelection() => e.KeyModifiers.HasAllFlags(keymap.SelectionModifiers); diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 5da82813e2..1324cd858a 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -250,7 +250,7 @@ namespace Avalonia.Controls if (e is RawKeyEventArgs rawKeyEventArgs && rawKeyEventArgs.Type == RawKeyEventType.KeyDown) { - var keymap = PlatformHotkeyConfiguration.Instance?.Back; + var keymap = PlatformSettings?.HotkeyConfiguration.Back; if (keymap != null) { diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index d449590bd8..7bf8d3bb68 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -571,7 +571,7 @@ namespace Avalonia.Controls if (!e.Handled) { - var keymap = PlatformHotkeyConfiguration.Instance; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; bool Match(List? gestures) => gestures?.Any(g => g.Matches(e)) ?? false; if (this.SelectionMode == SelectionMode.Multiple && Match(keymap?.SelectAll)) @@ -652,11 +652,12 @@ namespace Avalonia.Controls if (point.Properties.IsLeftButtonPressed || point.Properties.IsRightButtonPressed) { + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; e.Handled = UpdateSelectionFromEventSource( e.Source, true, e.KeyModifiers.HasAllFlags(KeyModifiers.Shift), - e.KeyModifiers.HasAllFlags(PlatformHotkeyConfiguration.Instance!.CommandModifiers), + e.KeyModifiers.HasAllFlags(keymap.CommandModifiers), point.Properties.IsRightButtonPressed); } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index a1ab90b3f7..b32382084d 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -108,20 +108,18 @@ namespace Avalonia.Native .Bind().ToConstant(this) .Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard())) .Bind().ToConstant(new DefaultRenderTimer(60)) - .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta, wholeWordTextActionModifiers: KeyModifiers.Alt)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) .Bind().ToConstant(applicationPlatform) .Bind().ToConstant(new MacOSNativeMenuCommands(_factory.CreateApplicationCommands())); - var hotkeys = PlatformHotkeyConfiguration.Instance; - if (hotkeys is not null) - { - hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); - hotkeys.MoveCursorToTheStartOfLineWithSelection.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); - hotkeys.MoveCursorToTheEndOfLine.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers)); - hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); - } + var hotkeys = new PlatformHotkeyConfiguration(KeyModifiers.Meta, wholeWordTextActionModifiers: KeyModifiers.Alt); + hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); + hotkeys.MoveCursorToTheStartOfLineWithSelection.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); + hotkeys.MoveCursorToTheEndOfLine.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers)); + hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); + + AvaloniaLocator.CurrentMutable.Bind().ToConstant(hotkeys); if (_options.UseGpu) { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index f5e7992203..11e9349ffe 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1045,7 +1045,7 @@ namespace Avalonia.Controls.UnitTests var data = CreateTestTreeData(); var target = CreateTarget(data: data, multiSelect: true); var rootNode = data[0]; - var keymap = PlatformHotkeyConfiguration.Instance!; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1075,7 +1075,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = PlatformHotkeyConfiguration.Instance!; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs @@ -1105,7 +1105,7 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, KeyModifiers.None); ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = PlatformHotkeyConfiguration.Instance!; + var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration; var selectAllGesture = keymap.SelectAll.First(); var keyEvent = new KeyEventArgs diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs index 870e6391d0..5f7f3f20ec 100644 --- a/tests/Avalonia.UnitTests/UnitTestApplication.cs +++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs @@ -77,7 +77,8 @@ namespace Avalonia.UnitTests .Bind().ToConstant(Services.DispatcherImpl) .Bind().ToConstant(Services.StandardCursorFactory) .Bind().ToConstant(Services.WindowingPlatform) - .Bind().ToSingleton(); + .Bind().ToSingleton() + .Bind().ToSingleton(); // This is a hack to make tests work, we need to refactor the way font manager is registered // See https://github.com/AvaloniaUI/Avalonia/issues/10081 From 23886742246edf610e2917a0b7a068f1ffaae36b Mon Sep 17 00:00:00 2001 From: Julian Preece Date: Tue, 30 May 2023 14:42:26 +0100 Subject: [PATCH 037/391] Fix vnc colour format --- .../HeadlessVncPlatformExtensions.cs | 4 +++- .../Avalonia.Headless/AvaloniaHeadlessPlatform.cs | 12 +++++++++--- src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs | 6 ++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs index 8e5cd1a316..29ca911a82 100644 --- a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs +++ b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs @@ -5,6 +5,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Headless; using Avalonia.Headless.Vnc; +using Avalonia.Platform; using RemoteViewing.Vnc; using RemoteViewing.Vnc.Server; @@ -22,7 +23,8 @@ namespace Avalonia return builder .UseHeadless(new AvaloniaHeadlessPlatformOptions { - UseHeadlessDrawing = false + UseHeadlessDrawing = false, + FrameBufferFormat = PixelFormat.Bgra8888 }) .AfterSetup(_ => { diff --git a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs index 411cdd5a95..2d8506f92b 100644 --- a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs +++ b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs @@ -51,11 +51,16 @@ namespace Avalonia.Headless private class HeadlessWindowingPlatform : IWindowingPlatform { - public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false); + readonly PixelFormat _frameBufferFormat; + public HeadlessWindowingPlatform(PixelFormat frameBufferFormat) + { + _frameBufferFormat = frameBufferFormat; + } + public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false, _frameBufferFormat); public IWindowImpl CreateEmbeddableWindow() => throw new PlatformNotSupportedException(); - public IPopupImpl CreatePopup() => new HeadlessWindowImpl(true); + public IPopupImpl CreatePopup() => new HeadlessWindowImpl(true, _frameBufferFormat); public ITrayIconImpl? CreateTrayIcon() => null; } @@ -70,7 +75,7 @@ namespace Avalonia.Headless .Bind().ToSingleton() .Bind().ToConstant(new KeyboardDevice()) .Bind().ToConstant(new RenderTimer(60)) - .Bind().ToConstant(new HeadlessWindowingPlatform()) + .Bind().ToConstant(new HeadlessWindowingPlatform(opts.FrameBufferFormat)) .Bind().ToSingleton(); Compositor = new Compositor( null); } @@ -92,6 +97,7 @@ namespace Avalonia.Headless public class AvaloniaHeadlessPlatformOptions { public bool UseHeadlessDrawing { get; set; } = true; + public PixelFormat FrameBufferFormat { get; set; } = PixelFormat.Rgba8888; } public static class AvaloniaHeadlessPlatformExtensions diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs index 3f486d98f6..7807b24cf8 100644 --- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs @@ -24,9 +24,10 @@ namespace Avalonia.Headless private readonly Pointer _mousePointer; private WriteableBitmap? _lastRenderedFrame; private readonly object _sync = new object(); + private readonly PixelFormat _frameBufferFormat; public bool IsPopup { get; } - public HeadlessWindowImpl(bool isPopup) + public HeadlessWindowImpl(bool isPopup, PixelFormat frameBufferFormat) { IsPopup = isPopup; Surfaces = new object[] { this }; @@ -34,6 +35,7 @@ namespace Avalonia.Headless _mousePointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); MouseDevice = new MouseDevice(_mousePointer); ClientSize = new Size(1024, 768); + _frameBufferFormat = frameBufferFormat; } public void Dispose() @@ -201,7 +203,7 @@ namespace Avalonia.Headless public ILockedFramebuffer Lock() { - var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling, PixelFormat.Rgba8888, AlphaFormat.Premul); + var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling, _frameBufferFormat, AlphaFormat.Premul); var fb = bmp.Lock(); return new FramebufferProxy(fb, () => { From 5df3a4664501e35d81462de2d48f4837a4d4c104 Mon Sep 17 00:00:00 2001 From: Julian Preece Date: Tue, 30 May 2023 14:56:05 +0100 Subject: [PATCH 038/391] Handle keyboard input for vnc --- .../HeadlessVncFramebufferSource.cs | 259 +++++++++++++++++- .../HeadlessWindowExtensions.cs | 6 + .../Avalonia.Headless/HeadlessWindowImpl.cs | 8 + .../Avalonia.Headless/IHeadlessWindow.cs | 1 + 4 files changed, 272 insertions(+), 2 deletions(-) diff --git a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs index 1a7bb2ec2f..086c4d06d3 100644 --- a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs +++ b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs @@ -1,8 +1,7 @@ using System; -using System.Runtime.InteropServices; +using System.Globalization; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Platform; using Avalonia.Threading; using RemoteViewing.Vnc; using RemoteViewing.Vnc.Server; @@ -16,6 +15,8 @@ namespace Avalonia.Headless.Vnc public VncFramebuffer _framebuffer = new VncFramebuffer("Avalonia", 1, 1, VncPixelFormat.RGB32); private VncButton _previousButtons; + private RawInputModifiers _keyState; + public HeadlessVncFramebufferSource(VncServerSession session, Window window) { Window = window; @@ -49,9 +50,263 @@ namespace Avalonia.Headless.Vnc _previousButtons = buttons; }, DispatcherPriority.Input); }; + + session.KeyChanged += (_, args) => + { + Key? key = TranslateKey(args.Keysym); + if (key == null) + return; + + //we only care about text input on key up + string? inputText = args.Pressed ? null : KeyToText(args.Keysym); + Dispatcher.UIThread.Post(() => + { + if (args.Pressed) + Window?.KeyPress(key.Value, _keyState); + else + Window?.KeyRelease(key.Value, _keyState); + + if (inputText != null) + Window?.KeyTextInput(inputText); + }, DispatcherPriority.Input); + }; + } + + private bool CheckKeyIsInputModifier(KeyChangedEventArgs args) + { + RawInputModifiers? toggleModifier = args.Keysym switch + { + KeySym.ShiftLeft or KeySym.ShiftRight => RawInputModifiers.Shift, + KeySym.ControlLeft or KeySym.ControlRight => RawInputModifiers.Control, + KeySym.AltLeft or KeySym.AltRight => RawInputModifiers.Alt, + _ => null + }; + if(!toggleModifier.HasValue) + return false; + + if(args.Pressed) + _keyState |= toggleModifier.Value; + else + _keyState &= ~toggleModifier.Value; + + return true; + } + + private static string? KeyToText(KeySym key) + { + int keyCode = (int)key; + if (key >= KeySym.Space && key <= KeySym.AsciiTilde) + return new string((char)key, 1); + + //handle as normal text chars 0-9 + if (key >= KeySym.NumPad0 && key <= KeySym.NumPad9) + return new string((char)(key - 65408), 1); + + switch (key) + { + case KeySym.NumPadAdd: return "+"; + case KeySym.NumPadSubtract: return "-"; + case KeySym.NumPadMultiply: return "*"; + case KeySym.NumPadDivide: return "/"; + case KeySym.NumPadSeparator: return NumberFormatInfo.CurrentInfo.NumberDecimalSeparator; + } + + return null; } + private static Key? TranslateKey(KeySym key) => + key switch + { + KeySym.Backspace => Key.Back, + KeySym.Tab => Key.Tab, + KeySym.LineFeed => Key.LineFeed, + KeySym.Clear => Key.Clear, + KeySym.Return => Key.Return, + KeySym.Pause => Key.Pause, + KeySym.Escape => Key.Escape, + KeySym.Delete => Key.Delete, + KeySym.Home => Key.Home, + KeySym.Left => Key.Left, + KeySym.Up => Key.Up, + KeySym.Right => Key.Right, + KeySym.Down => Key.Down, + KeySym.PageUp => Key.PageUp, + KeySym.PageDown => Key.PageDown, + KeySym.End => Key.End, + KeySym.Begin => Key.Home, + KeySym.Select => Key.Select, + KeySym.Print => Key.Print, + KeySym.Execute => Key.Execute, + KeySym.Insert => Key.Insert, + KeySym.Cancel => Key.Cancel, + KeySym.Help => Key.Help, + KeySym.Break => Key.Pause, + KeySym.Num_Lock => Key.NumLock, + KeySym.NumPadSpace => Key.Space, + KeySym.NumPadTab => Key.Tab, + KeySym.NumPadEnter => Key.Enter, + KeySym.NumPadF1 => Key.F1, + KeySym.NumPadF2 => Key.F2, + KeySym.NumPadF3 => Key.F3, + KeySym.NumPadF4 => Key.F4, + KeySym.NumPadHome => Key.Home, + KeySym.NumPadLeft => Key.Left, + KeySym.NumPadUp => Key.Up, + KeySym.NumPadRight => Key.Right, + KeySym.NumPadDown => Key.Down, + KeySym.NumPadPageUp => Key.PageUp, + KeySym.NumPadPageDown => Key.PageDown, + KeySym.NumPadEnd => Key.End, + KeySym.NumPadBegin => Key.Home, + KeySym.NumPadInsert => Key.Insert, + KeySym.NumPadDelete => Key.Delete, + KeySym.NumPadEqual => Key.Return, + KeySym.NumPadMultiply => Key.Multiply, + KeySym.NumPadAdd => Key.Add, + KeySym.NumPadSeparator => Key.Separator, + KeySym.NumPadSubtract => Key.Subtract, + KeySym.NumPadDecimal => Key.Decimal, + KeySym.NumPadDivide => Key.Divide, + KeySym.NumPad0 => Key.NumPad0, + KeySym.NumPad1 => Key.NumPad1, + KeySym.NumPad2 => Key.NumPad2, + KeySym.NumPad3 => Key.NumPad3, + KeySym.NumPad4 => Key.NumPad4, + KeySym.NumPad5 => Key.NumPad5, + KeySym.NumPad6 => Key.NumPad6, + KeySym.NumPad7 => Key.NumPad7, + KeySym.NumPad8 => Key.NumPad8, + KeySym.NumPad9 => Key.NumPad9, + KeySym.F1 => Key.F1, + KeySym.F2 => Key.F2, + KeySym.F3 => Key.F3, + KeySym.F4 => Key.F4, + KeySym.F5 => Key.F5, + KeySym.F6 => Key.F6, + KeySym.F7 => Key.F7, + KeySym.F8 => Key.F8, + KeySym.F9 => Key.F9, + KeySym.F10 => Key.F10, + KeySym.F11 => Key.F11, + KeySym.F12 => Key.F12, + KeySym.F13 => Key.F13, + KeySym.F14 => Key.F14, + KeySym.F15 => Key.F15, + KeySym.F16 => Key.F16, + KeySym.F17 => Key.F17, + KeySym.F18 => Key.F18, + KeySym.F19 => Key.F19, + KeySym.F20 => Key.F20, + KeySym.F21 => Key.F21, + KeySym.F22 => Key.F22, + KeySym.F23 => Key.F23, + KeySym.F24 => Key.F24, + KeySym.ShiftLeft => Key.LeftShift, + KeySym.ShiftRight => Key.RightShift, + KeySym.ControlLeft => Key.LeftCtrl, + KeySym.ControlRight => Key.RightCtrl, + KeySym.CapsLock => Key.CapsLock, + KeySym.AltLeft => Key.LeftAlt, + KeySym.AltRight => Key.RightAlt, + KeySym.Space => Key.Space, + KeySym.Exclamation => Key.D1, + KeySym.Quote => Key.D2, + KeySym.NumberSign => Key.D3, + KeySym.Dollar => Key.D4, + KeySym.Percent => Key.D5, + KeySym.Ampersand => Key.D7, + KeySym.Apostrophe => Key.Oem3, + KeySym.ParenthesisLeft => Key.D9, + KeySym.ParenthesisRight => Key.D0, + KeySym.Asterisk => Key.D8, + KeySym.Plus => Key.OemPlus, + KeySym.Comma => Key.OemComma, + KeySym.Minus => Key.OemMinus, + KeySym.Period => Key.OemPeriod, + KeySym.Slash => Key.OemQuestion, + KeySym.D0 => Key.D0, + KeySym.D1 => Key.D1, + KeySym.D2 => Key.D2, + KeySym.D3 => Key.D3, + KeySym.D4 => Key.D4, + KeySym.D5 => Key.D5, + KeySym.D6 => Key.D6, + KeySym.D7 => Key.D7, + KeySym.D8 => Key.D8, + KeySym.D9 => Key.D9, + KeySym.Colon => Key.OemSemicolon, + KeySym.Semicolon => Key.OemSemicolon, + KeySym.Less => Key.OemComma, + KeySym.Equal => Key.OemPlus, + KeySym.Greater => Key.OemPeriod, + KeySym.Question => Key.OemQuestion, + KeySym.At => Key.Oem3, + KeySym.A => Key.A, + KeySym.B => Key.B, + KeySym.C => Key.C, + KeySym.D => Key.D, + KeySym.E => Key.E, + KeySym.F => Key.F, + KeySym.G => Key.G, + KeySym.H => Key.H, + KeySym.I => Key.I, + KeySym.J => Key.J, + KeySym.K => Key.K, + KeySym.L => Key.L, + KeySym.M => Key.M, + KeySym.N => Key.N, + KeySym.O => Key.O, + KeySym.P => Key.P, + KeySym.Q => Key.Q, + KeySym.R => Key.R, + KeySym.S => Key.S, + KeySym.T => Key.T, + KeySym.U => Key.U, + KeySym.V => Key.V, + KeySym.W => Key.W, + KeySym.X => Key.X, + KeySym.Y => Key.Y, + KeySym.Z => Key.Z, + KeySym.BracketLeft => Key.OemOpenBrackets, + KeySym.Backslash => Key.OemPipe, + KeySym.Bracketright => Key.OemCloseBrackets, + KeySym.Underscore => Key.OemMinus, + KeySym.Grave => Key.Oem8, + KeySym.a => Key.A, + KeySym.b => Key.B, + KeySym.c => Key.C, + KeySym.d => Key.D, + KeySym.e => Key.E, + KeySym.f => Key.F, + KeySym.g => Key.G, + KeySym.h => Key.H, + KeySym.i => Key.I, + KeySym.j => Key.J, + KeySym.k => Key.K, + KeySym.l => Key.L, + KeySym.m => Key.M, + KeySym.n => Key.M, + KeySym.o => Key.O, + KeySym.p => Key.P, + KeySym.q => Key.Q, + KeySym.r => Key.R, + KeySym.s => Key.S, + KeySym.t => Key.T, + KeySym.u => Key.U, + KeySym.v => Key.V, + KeySym.w => Key.W, + KeySym.x => Key.X, + KeySym.y => Key.Y, + KeySym.z => Key.Z, + KeySym.BraceLeft => Key.OemOpenBrackets, + KeySym.Bar => Key.OemPipe, + KeySym.BraceRight => Key.OemCloseBrackets, + KeySym.AsciiTilde => Key.OemTilde, + _ => null + }; + + [Flags] enum VncButton { diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs b/src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs index 61659dee2b..3d07cac64e 100644 --- a/src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs +++ b/src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs @@ -52,6 +52,12 @@ public static class HeadlessWindowExtensions public static void KeyRelease(this TopLevel topLevel, Key key, RawInputModifiers modifiers) => RunJobsOnImpl(topLevel, w => w.KeyRelease(key, modifiers)); + /// + /// Simulates a text input event on the headless window/toplevel + /// + public static void KeyTextInput(this TopLevel topLevel, string text) => + RunJobsOnImpl(topLevel, w => w.TextInput(text)); + /// /// Simulates mouse down on the headless window/toplevel. /// diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs index 3f486d98f6..117d315cba 100644 --- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs @@ -274,6 +274,14 @@ namespace Avalonia.Headless Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot!, RawKeyEventType.KeyUp, key, modifiers)); } + void IHeadlessWindow.TextInput(string text) + { + if (InputRoot == null) + return; + + Input?.Invoke(new RawTextInputEventArgs(_keyboard, 0, InputRoot, text)); + } + void IHeadlessWindow.MouseDown(Point point, MouseButton button, RawInputModifiers modifiers) { Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot!, diff --git a/src/Headless/Avalonia.Headless/IHeadlessWindow.cs b/src/Headless/Avalonia.Headless/IHeadlessWindow.cs index ef501b3800..6d34dbbd4b 100644 --- a/src/Headless/Avalonia.Headless/IHeadlessWindow.cs +++ b/src/Headless/Avalonia.Headless/IHeadlessWindow.cs @@ -11,6 +11,7 @@ namespace Avalonia.Headless WriteableBitmap? GetLastRenderedFrame(); void KeyPress(Key key, RawInputModifiers modifiers); void KeyRelease(Key key, RawInputModifiers modifiers); + void TextInput(string text); void MouseDown(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None); void MouseMove(Point point, RawInputModifiers modifiers = RawInputModifiers.None); void MouseUp(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None); From 0027ace53b65244617dbe072d6166b9817aef95f Mon Sep 17 00:00:00 2001 From: Julian Preece Date: Tue, 30 May 2023 14:56:23 +0100 Subject: [PATCH 039/391] handle mouse wheel for vnc --- .../Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs index 086c4d06d3..b1cedb684e 100644 --- a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs +++ b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs @@ -47,6 +47,14 @@ namespace Avalonia.Headless.Vnc foreach (var btn in CheckedButtons) if (!_previousButtons.HasFlag(btn) && buttons.HasFlag(btn)) Window?.MouseDown(pt, TranslateButton(btn), modifiers); + + + if (buttons == VncButton.ScrollUp) + Window?.MouseWheel(pt, Vector.One, modifiers); + + else if (buttons == VncButton.ScrollDown) + Window?.MouseWheel(pt, Vector.One.Negate(), modifiers); + _previousButtons = buttons; }, DispatcherPriority.Input); }; From 4511be1765f65a8c55f464a1abcd2cc40bbfeb40 Mon Sep 17 00:00:00 2001 From: Julian Preece Date: Tue, 30 May 2023 15:31:33 +0100 Subject: [PATCH 040/391] Fix using key modifier for input --- .../HeadlessVncFramebufferSource.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs index b1cedb684e..91ef942723 100644 --- a/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs +++ b/src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs @@ -61,12 +61,19 @@ namespace Avalonia.Headless.Vnc session.KeyChanged += (_, args) => { + bool isModifierKey = CheckKeyIsInputModifier(args); + if (isModifierKey) + return; + Key? key = TranslateKey(args.Keysym); if (key == null) return; - //we only care about text input on key up - string? inputText = args.Pressed ? null : KeyToText(args.Keysym); + //we only care about text input on key up if not using Ctrl or Alt + string? inputText = args.Pressed || _keyState.HasFlag(RawInputModifiers.Control) || _keyState.HasFlag(RawInputModifiers.Alt) + ? null + : KeyToText(args.Keysym); + Dispatcher.UIThread.Post(() => { if (args.Pressed) From 9b95fa58030634faa2d3e8aabed202d797d1f82a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 11:12:19 -0400 Subject: [PATCH 041/391] Make it work after merge conflicts --- .../Avalonia.Win32.Interoperability.csproj | 4 ---- .../WinForms/WinFormsAvaloniaControlHost.cs | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj index cc1dfb385c..68c6fa69c9 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj +++ b/src/Windows/Avalonia.Win32.Interoperability/Avalonia.Win32.Interoperability.csproj @@ -2,12 +2,8 @@ net461;net6.0-windows true - true true true - true - true - true Avalonia.Win32.Interoperability diff --git a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs index 9ac00a8b8d..1f8c30030c 100644 --- a/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs @@ -52,7 +52,7 @@ public class WinFormsAvaloniaControlHost : WinFormsControl _root = new(); _root.Content = _content; _root.Prepare(); - _root.Renderer.Start(); + _root.StartRendering(); _root.GotFocus += RootGotFocus; FixPosition(); @@ -64,6 +64,7 @@ public class WinFormsAvaloniaControlHost : WinFormsControl /// protected override void OnHandleDestroyed(EventArgs e) { + _root?.StopRendering(); _root?.Dispose(); _root = null; base.OnHandleDestroyed(e); From eb05dac696f332bb0ca7861aae8b673db1eb5244 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 11:45:20 -0400 Subject: [PATCH 042/391] Remove IKeyboardDevice/IPointerDevice/IDragDropDevice from public API, as they are not really used anywhere --- src/Avalonia.Base/Input/IKeyboardDevice.cs | 2 +- src/Avalonia.Base/Input/IMouseDevice.cs | 2 +- src/Avalonia.Base/Input/IPenDevice.cs | 5 ++++- src/Avalonia.Base/Input/IPointerDevice.cs | 2 +- src/Avalonia.Base/Input/KeyEventArgs.cs | 2 -- src/Avalonia.Base/Input/KeyboardDevice.cs | 2 -- src/Avalonia.Base/Input/Raw/IDragDropDevice.cs | 2 +- src/Avalonia.Base/Input/TextInputEventArgs.cs | 4 +--- .../Primitives/SelectingItemsControlTests.cs | 2 -- 9 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs index 172b58068c..dfab1cc83d 100644 --- a/src/Avalonia.Base/Input/IKeyboardDevice.cs +++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs @@ -43,7 +43,7 @@ namespace Avalonia.Input PenBarrelButton = 2048 } - [NotClientImplementable] + [PrivateApi] public interface IKeyboardDevice : IInputDevice { } diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs index 00c436bf21..4fd1ccad22 100644 --- a/src/Avalonia.Base/Input/IMouseDevice.cs +++ b/src/Avalonia.Base/Input/IMouseDevice.cs @@ -5,7 +5,7 @@ namespace Avalonia.Input /// /// Represents a mouse device. /// - [NotClientImplementable] + [PrivateApi] public interface IMouseDevice : IPointerDevice { } diff --git a/src/Avalonia.Base/Input/IPenDevice.cs b/src/Avalonia.Base/Input/IPenDevice.cs index 1cc0fcf76d..a6cb669f51 100644 --- a/src/Avalonia.Base/Input/IPenDevice.cs +++ b/src/Avalonia.Base/Input/IPenDevice.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Input +using Avalonia.Metadata; + +namespace Avalonia.Input { /// /// Represents a pen/stylus device. /// + [PrivateApi] public interface IPenDevice : IPointerDevice { diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs index e0aebda9c5..afcea9e4cb 100644 --- a/src/Avalonia.Base/Input/IPointerDevice.cs +++ b/src/Avalonia.Base/Input/IPointerDevice.cs @@ -3,7 +3,7 @@ using Avalonia.Metadata; namespace Avalonia.Input { - [NotClientImplementable] + [PrivateApi] public interface IPointerDevice : IInputDevice { /// diff --git a/src/Avalonia.Base/Input/KeyEventArgs.cs b/src/Avalonia.Base/Input/KeyEventArgs.cs index 9fa097c4b1..4de0c2d233 100644 --- a/src/Avalonia.Base/Input/KeyEventArgs.cs +++ b/src/Avalonia.Base/Input/KeyEventArgs.cs @@ -5,8 +5,6 @@ namespace Avalonia.Input { public class KeyEventArgs : RoutedEventArgs { - public IKeyboardDevice? Device { get; init; } - public Key Key { get; init; } public KeyModifiers KeyModifiers { get; init; } diff --git a/src/Avalonia.Base/Input/KeyboardDevice.cs b/src/Avalonia.Base/Input/KeyboardDevice.cs index a81bc6b2e0..c5e3ef5bf0 100644 --- a/src/Avalonia.Base/Input/KeyboardDevice.cs +++ b/src/Avalonia.Base/Input/KeyboardDevice.cs @@ -192,7 +192,6 @@ namespace Avalonia.Input KeyEventArgs ev = new KeyEventArgs { RoutedEvent = routedEvent, - Device = this, Key = keyInput.Key, KeyModifiers = keyInput.Modifiers.ToKeyModifiers(), Source = element, @@ -241,7 +240,6 @@ namespace Avalonia.Input { var ev = new TextInputEventArgs() { - Device = this, Text = text.Text, Source = element, RoutedEvent = InputElement.TextInputEvent diff --git a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs index 3bcc9fadd3..5473b5a9e5 100644 --- a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs @@ -2,7 +2,7 @@ namespace Avalonia.Input.Raw { - [NotClientImplementable] + [PrivateApi] public interface IDragDropDevice : IInputDevice { } diff --git a/src/Avalonia.Base/Input/TextInputEventArgs.cs b/src/Avalonia.Base/Input/TextInputEventArgs.cs index cda0103749..f4acd694a0 100644 --- a/src/Avalonia.Base/Input/TextInputEventArgs.cs +++ b/src/Avalonia.Base/Input/TextInputEventArgs.cs @@ -4,8 +4,6 @@ namespace Avalonia.Input { public class TextInputEventArgs : RoutedEventArgs { - public IKeyboardDevice? Device { get; set; } - - public string? Text { get; set; } + public string? Text { get; init; } } } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index e456f25238..9cc068006b 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -2141,7 +2141,6 @@ namespace Avalonia.Controls.UnitTests.Primitives target.RaiseEvent(new TextInputEventArgs { RoutedEvent = InputElement.TextInputEvent, - Device = KeyboardDevice.Instance, Text = "Foo" }); @@ -2152,7 +2151,6 @@ namespace Avalonia.Controls.UnitTests.Primitives target.RaiseEvent(new TextInputEventArgs { RoutedEvent = InputElement.TextInputEvent, - Device = KeyboardDevice.Instance, Text = "Foo" }); From aaf13944c8ba28b6423c21e5a02ee386e6d85e50 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 19:45:43 -0700 Subject: [PATCH 043/391] Update BclStorageFile.cs --- src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs index f9f02ec339..fed5328b94 100644 --- a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs +++ b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs @@ -59,12 +59,13 @@ internal class BclStorageFile : IStorageBookmarkFile public Task OpenReadAsync() { - return Task.FromResult(FileInfo.OpenRead()); + var stream = new FileStream(FileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.None); + return Task.FromResult(stream); } public Task OpenWriteAsync() { - return Task.FromResult(FileInfo.OpenWrite()); + return Task.FromResult(File.Create(FileInfo.FullName)); } public virtual Task SaveBookmarkAsync() From 6f6ec9b79d219d2c96783cda4918ac598169aee9 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 30 May 2023 22:49:49 -0400 Subject: [PATCH 044/391] Update BclStorageFile.cs --- src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs index fed5328b94..0c455ab511 100644 --- a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs +++ b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs @@ -59,13 +59,13 @@ internal class BclStorageFile : IStorageBookmarkFile public Task OpenReadAsync() { - var stream = new FileStream(FileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.None); - return Task.FromResult(stream); + return Task.FromResult(FileInfo.OpenRead()); } public Task OpenWriteAsync() { - return Task.FromResult(File.Create(FileInfo.FullName)); + var stream = new FileStream(FileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.Write); + return Task.FromResult(stream); } public virtual Task SaveBookmarkAsync() From 9971c6de22a0bec4b6c34f1622d1c9c6970a325c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 31 May 2023 11:16:51 +0200 Subject: [PATCH 045/391] Make some interfaces/classes a PrivateApi. Instead of internal. Required to allow people to port existing unit tests without having to rewrite using Headless. --- src/Avalonia.Base/Layout/ILayoutManager.cs | 4 ++-- src/Avalonia.Base/Layout/LayoutManager.cs | 5 +++-- src/Avalonia.Base/Rendering/IRenderRoot.cs | 4 ++-- src/Avalonia.Base/Rendering/IRenderer.cs | 6 ++++-- src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs | 4 +++- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs index 063c8e99d2..5035d3a48d 100644 --- a/src/Avalonia.Base/Layout/ILayoutManager.cs +++ b/src/Avalonia.Base/Layout/ILayoutManager.cs @@ -6,8 +6,8 @@ namespace Avalonia.Layout /// /// Manages measuring and arranging of controls. /// - [NotClientImplementable] - internal interface ILayoutManager : IDisposable + [PrivateApi] + public interface ILayoutManager : IDisposable { /// /// Raised when the layout manager completes a layout pass. diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index 6d698ae5ce..c64c2714c3 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/src/Avalonia.Base/Layout/LayoutManager.cs @@ -2,9 +2,9 @@ using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using Avalonia.Logging; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering; using Avalonia.Threading; using Avalonia.Utilities; @@ -16,7 +16,8 @@ namespace Avalonia.Layout /// /// Manages measuring and arranging of controls. /// - internal class LayoutManager : ILayoutManager, IDisposable + [PrivateApi] + public class LayoutManager : ILayoutManager, IDisposable { private const int MaxPasses = 10; private readonly Layoutable _owner; diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs index 371d34b1f4..820840afbc 100644 --- a/src/Avalonia.Base/Rendering/IRenderRoot.cs +++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs @@ -16,9 +16,9 @@ namespace Avalonia.Rendering /// /// Gets the renderer for the window. /// - internal IRenderer Renderer { get; } + public IRenderer Renderer { get; } - internal IHitTester HitTester { get; } + public IHitTester HitTester { get; } /// /// The scaling factor to use in rendering. diff --git a/src/Avalonia.Base/Rendering/IRenderer.cs b/src/Avalonia.Base/Rendering/IRenderer.cs index 4cb79d803d..227ff53897 100644 --- a/src/Avalonia.Base/Rendering/IRenderer.cs +++ b/src/Avalonia.Base/Rendering/IRenderer.cs @@ -9,7 +9,8 @@ namespace Avalonia.Rendering /// /// Defines the interface for a renderer. /// - internal interface IRenderer : IDisposable + [PrivateApi] + public interface IRenderer : IDisposable { /// /// Gets a value indicating whether the renderer should draw specific diagnostics. @@ -73,7 +74,8 @@ namespace Avalonia.Rendering Compositor Compositor { get; } } - internal interface IHitTester + [PrivateApi] + public interface IHitTester { /// /// Hit tests a location to find the visuals at the specified point. diff --git a/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs b/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs index fcccd9c752..552ecb14ff 100644 --- a/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs +++ b/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs @@ -1,11 +1,13 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Rendering { /// /// Provides data for the event. /// - internal class SceneInvalidatedEventArgs : EventArgs + [PrivateApi] + public class SceneInvalidatedEventArgs : EventArgs { /// /// Initializes a new instance of the class. From d29657340424fafdd80b300e98c4578faffe0c7a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 31 May 2023 11:15:12 +0200 Subject: [PATCH 046/391] fix: Warning CS0436 --- .../Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index f3af312d1a..ae2d3dd847 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -9,8 +9,6 @@ - - From d99697f4ae211ffbe5ddd16a5f73ac641a0251de Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 12:58:21 +0100 Subject: [PATCH 047/391] fixes the overlay popup glitch where it renders at 0,0 before applying the offet. use the media context api to sync with layout and render. --- src/Avalonia.Base/Media/MediaContext.cs | 9 +++++++-- src/Avalonia.Controls/Primitives/OverlayPopupHost.cs | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index c261dd80b8..a41107360a 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/src/Avalonia.Base/Media/MediaContext.cs @@ -212,7 +212,12 @@ internal partial class MediaContext : ICompositorScheduler } while (count > 0); } - + + /// + /// Executes the callback in the layout pass that will occur + /// immediately before the next rendered frame. + /// + /// Code to execute. public void BeginInvokeOnRender(Action callback) { if (_invokeOnRenderCallbacks == null) @@ -224,4 +229,4 @@ internal partial class MediaContext : ICompositorScheduler if (!_isRendering) ScheduleRender(true); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 2236d87dd1..5dd0f23e8e 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -120,11 +120,11 @@ namespace Avalonia.Controls.Primitives void IManagedPopupPositionerPopup.MoveAndResize(Point devicePoint, Size virtualSize) { _lastRequestedPosition = devicePoint; - Dispatcher.UIThread.Post(() => + MediaContext.Instance.BeginInvokeOnRender(() => { - OverlayLayer.SetLeft(this, _lastRequestedPosition.X); - OverlayLayer.SetTop(this, _lastRequestedPosition.Y); - }, DispatcherPriority.Render); + Canvas.SetLeft(this, _lastRequestedPosition.X); + Canvas.SetTop(this, _lastRequestedPosition.Y); + }); } double IManagedPopupPositionerPopup.Scaling => 1; From 2c9a1fbeb68283a58ec94ab9a6a46bbe06efa4cd Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 13:08:27 +0100 Subject: [PATCH 048/391] fix issue loading when changing between targetframeworks and targetframework. --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 8f1b39ae12..a4101afac1 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From 864ef9b3d9c48dc3df6f2f650c87f4f2740faa0b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 13:20:50 +0100 Subject: [PATCH 049/391] improve comment. --- src/Avalonia.Base/Media/MediaContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index a41107360a..05872a3e50 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/src/Avalonia.Base/Media/MediaContext.cs @@ -214,8 +214,8 @@ internal partial class MediaContext : ICompositorScheduler } /// - /// Executes the callback in the layout pass that will occur - /// immediately before the next rendered frame. + /// Executes the callback in the next iteration of the current UI-thread + /// render loop / layout pass that. /// /// Code to execute. public void BeginInvokeOnRender(Action callback) From 42fccd1af633a8c840e7aaeea13e62c7c1f3b6ec Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 25 May 2023 11:58:11 +0000 Subject: [PATCH 050/391] track currently active gesture in the pointer --- .../GestureRecognizerCollection.cs | 6 +++++ src/Avalonia.Base/Input/IPointer.cs | 1 + src/Avalonia.Base/Input/Pointer.cs | 25 ++++++++++++++++++- .../PullToRefresh/RefreshVisualizer.cs | 3 --- .../DefaultMenuInteractionHandlerTests.cs | 1 + 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs index 3b9b2d0de6..8101e79af9 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs @@ -114,7 +114,13 @@ namespace Avalonia.Input.GestureRecognizers void IGestureRecognizerActionsDispatcher.Capture(IPointer pointer, IGestureRecognizer recognizer) { + var p = pointer as Pointer; + if (p != null && p.CapturedGestureRecognizer != null && recognizer != p.CapturedGestureRecognizer) + return; + pointer.Capture(_inputElement); + p?.CaptureGestureRecognizer(recognizer); + _pointerGrabs![pointer] = recognizer; foreach (var r in _recognizers!) { diff --git a/src/Avalonia.Base/Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs index 52605bb6ae..050adbabaa 100644 --- a/src/Avalonia.Base/Input/IPointer.cs +++ b/src/Avalonia.Base/Input/IPointer.cs @@ -1,3 +1,4 @@ +using Avalonia.Input.GestureRecognizers; using Avalonia.Metadata; namespace Avalonia.Input diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index 4713364f00..a600624276 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.Input.GestureRecognizers; using Avalonia.VisualTree; namespace Avalonia.Input @@ -52,6 +53,9 @@ namespace Avalonia.Input if (Captured is Visual v3) v3.DetachedFromVisualTree += OnCaptureDetached; + + if (Captured == null) + CaptureGestureRecognizer(null); } static IInputElement? GetNextCapture(Visual parent) @@ -69,6 +73,25 @@ namespace Avalonia.Input public PointerType Type { get; } public bool IsPrimary { get; } - public void Dispose() => Capture(null); + + /// + /// Gets the gesture recognizer that is currently capturing by the pointer, if any. + /// + internal IGestureRecognizer? CapturedGestureRecognizer { get; private set; } + + public void Dispose() + { + Capture(null); + } + + /// + /// Captures pointer input to the specified gesture recognizer. + /// + /// The gesture recognizer. + /// + internal void CaptureGestureRecognizer(IGestureRecognizer? gestureRecognizer) + { + CapturedGestureRecognizer = gestureRecognizer; + } } } diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs index 5c7071a161..39ff8e3a92 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs +++ b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs @@ -238,7 +238,6 @@ namespace Avalonia.Controls visualizerVisual.Offset = IsPullDirectionVertical ? new Vector3(visualizerVisual.Offset.X, 0, 0) : new Vector3(0, visualizerVisual.Offset.Y, 0); - visual.Offset = default; _content.InvalidateMeasure(); break; case RefreshVisualizerState.Interacting: @@ -452,8 +451,6 @@ namespace Avalonia.Controls _interactionRatioSubscription = RefreshInfoProvider.GetObservable(RefreshInfoProvider.InteractionRatioProperty) .Subscribe(InteractionRatioObserver); - var visual = RefreshInfoProvider.Visual; - _executingRatio = RefreshInfoProvider.ExecutionRatio; } else diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs index 506a62525c..3259ec1a4c 100644 --- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs @@ -2,6 +2,7 @@ using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Input.GestureRecognizers; using Avalonia.Interactivity; using Avalonia.VisualTree; using Moq; From 250959acc863124dd978729d1fb55b141932ac0d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 25 May 2023 15:15:39 +0000 Subject: [PATCH 051/391] notify current gesture recognizer that it's lost capture --- src/Avalonia.Base/Input/Pointer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index a600624276..30c32ae48c 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -91,6 +91,9 @@ namespace Avalonia.Input /// internal void CaptureGestureRecognizer(IGestureRecognizer? gestureRecognizer) { + if (CapturedGestureRecognizer != gestureRecognizer) + CapturedGestureRecognizer?.PointerCaptureLost(this); + CapturedGestureRecognizer = gestureRecognizer; } } From 8c0dfbaf08f84e1c810d1870aca47ae6e387b298 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 26 May 2023 11:32:57 +0000 Subject: [PATCH 052/391] rework gesture routing #11522 --- .../GestureRecognizerCollection.cs | 50 +++++-------------- .../GestureRecognizers/IGestureRecognizer.cs | 1 + .../PinchGestureRecognizer.cs | 4 +- .../PullGestureRecognizer.cs | 6 ++- .../ScrollGestureRecognizer.cs | 6 ++- src/Avalonia.Base/Input/InputElement.cs | 46 ++++++++++++----- src/Avalonia.Base/Input/MouseDevice.cs | 36 ++++++++++++- src/Avalonia.Base/Input/PenDevice.cs | 35 ++++++++++++- src/Avalonia.Base/Input/Pointer.cs | 5 +- src/Avalonia.Base/Input/PointerEventArgs.cs | 10 ++++ src/Avalonia.Base/Input/TouchDevice.cs | 35 ++++++++++--- src/Avalonia.Controls/Primitives/Thumb.cs | 2 + 12 files changed, 171 insertions(+), 65 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs index 8101e79af9..8ec9cb7301 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs @@ -10,8 +10,6 @@ namespace Avalonia.Input.GestureRecognizers { private readonly IInputElement _inputElement; private List? _recognizers; - private Dictionary? _pointerGrabs; - public GestureRecognizerCollection(IInputElement inputElement) { @@ -24,7 +22,6 @@ namespace Avalonia.Input.GestureRecognizers { // We initialize the collection when the first recognizer is added _recognizers = new List(); - _pointerGrabs = new Dictionary(); } _recognizers.Add(recognizer); @@ -57,8 +54,6 @@ namespace Avalonia.Input.GestureRecognizers return false; foreach (var r in _recognizers) { - if (e.Handled) - break; r.PointerPressed(e); } @@ -69,17 +64,15 @@ namespace Avalonia.Input.GestureRecognizers { if (_recognizers == null) return false; - if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture)) + var pointer = e.Pointer as Pointer; + + foreach (var r in _recognizers) { - capture.PointerReleased(e); + if (pointer?.CapturedGestureRecognizer != null) + break; + + r.PointerReleased(e); } - else - foreach (var r in _recognizers) - { - if (e.Handled) - break; - r.PointerReleased(e); - } return e.Handled; } @@ -87,41 +80,24 @@ namespace Avalonia.Input.GestureRecognizers { if (_recognizers == null) return false; - if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture)) - { - capture.PointerMoved(e); - } - else - foreach (var r in _recognizers) - { - if (e.Handled) - break; - r.PointerMoved(e); - } - return e.Handled; - } + var pointer = e.Pointer as Pointer; - internal void HandlePointerCaptureLost(PointerCaptureLostEventArgs e) - { - if (_recognizers == null) - return; - _pointerGrabs!.Remove(e.Pointer); foreach (var r in _recognizers) { - r.PointerCaptureLost(e.Pointer); + if (pointer?.CapturedGestureRecognizer != null) + break; + + r.PointerMoved(e); } + return e.Handled; } void IGestureRecognizerActionsDispatcher.Capture(IPointer pointer, IGestureRecognizer recognizer) { var p = pointer as Pointer; - if (p != null && p.CapturedGestureRecognizer != null && recognizer != p.CapturedGestureRecognizer) - return; - pointer.Capture(_inputElement); p?.CaptureGestureRecognizer(recognizer); - _pointerGrabs![pointer] = recognizer; foreach (var r in _recognizers!) { if (r != recognizer) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs index c1d9ae5304..51c36a93de 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs @@ -2,6 +2,7 @@ namespace Avalonia.Input.GestureRecognizers { public interface IGestureRecognizer { + IInputElement? Target { get; } void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions); void PointerPressed(PointerPressedEventArgs e); void PointerReleased(PointerReleasedEventArgs e); diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs index 3b83d0cb87..4390167e98 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs @@ -2,7 +2,7 @@ namespace Avalonia.Input { - public class PinchGestureRecognizer : StyledElement, IGestureRecognizer + public class PinchGestureRecognizer : AvaloniaObject, IGestureRecognizer { private IInputElement? _target; private IGestureRecognizerActionsDispatcher? _actions; @@ -13,6 +13,8 @@ namespace Avalonia.Input private Point _secondPoint; private Point _origin; + public IInputElement? Target => _target; + public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) { _target = target; diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs index 6784677520..7391cdbaca 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs @@ -1,10 +1,9 @@ using System; -using System.Diagnostics; using Avalonia.Input.GestureRecognizers; namespace Avalonia.Input { - public class PullGestureRecognizer : StyledElement, IGestureRecognizer + public class PullGestureRecognizer : AvaloniaObject, IGestureRecognizer { internal static int MinPullDetectionSize = 50; @@ -27,6 +26,8 @@ namespace Avalonia.Input set => SetValue(PullDirectionProperty, value); } + public IInputElement? Target => _target; + public PullGestureRecognizer(PullDirection pullDirection) { PullDirection = pullDirection; @@ -54,6 +55,7 @@ namespace Avalonia.Input { var currentPosition = e.GetPosition(visual); _actions!.Capture(e.Pointer, this); + e.PreventGestureRecognition(); Vector delta = default; switch (PullDirection) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index b510d44e63..3206c3903a 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -91,7 +91,9 @@ namespace Avalonia.Input.GestureRecognizers { get => _scrollStartDistance; set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value); - } + } + + public IInputElement? Target => _target; public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) { @@ -132,6 +134,8 @@ namespace Avalonia.Input.GestureRecognizers _trackedRootPoint.Y - (_trackedRootPoint.Y >= rootPoint.Y ? ScrollStartDistance : -ScrollStartDistance)); _actions!.Capture(e.Pointer, this); + + e.PreventGestureRecognition(); } } diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index 68131e5bf7..46f543d25b 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -225,6 +225,11 @@ namespace Avalonia.Input PointerReleasedEvent.AddClassHandler((x, e) => x.OnPointerReleased(e)); PointerCaptureLostEvent.AddClassHandler((x, e) => x.OnPointerCaptureLost(e)); PointerWheelChangedEvent.AddClassHandler((x, e) => x.OnPointerWheelChanged(e)); + + // Gesture only handlers + PointerMovedEvent.AddClassHandler((x, e) => x.OnGesturePointerMoved(e), handledEventsToo: true); + PointerPressedEvent.AddClassHandler((x, e) => x.OnGesturePointerPressed(e), handledEventsToo: true); + PointerReleasedEvent.AddClassHandler((x, e) => x.OnGesturePointerReleased(e), handledEventsToo: true); } public InputElement() @@ -583,10 +588,6 @@ namespace Avalonia.Input /// The event args. protected virtual void OnPointerMoved(PointerEventArgs e) { - if (_gestureRecognizers?.HandlePointerMoved(e) == true) - { - e.Handled = true; - } } /// @@ -595,10 +596,6 @@ namespace Avalonia.Input /// The event args. protected virtual void OnPointerPressed(PointerPressedEventArgs e) { - if (_gestureRecognizers?.HandlePointerPressed(e) == true) - { - e.Handled = true; - } } /// @@ -607,10 +604,33 @@ namespace Avalonia.Input /// The event args. protected virtual void OnPointerReleased(PointerReleasedEventArgs e) { - if (_gestureRecognizers?.HandlePointerReleased(e) == true) - { - e.Handled = true; - } + } + + private void OnGesturePointerReleased(PointerReleasedEventArgs e) + { + if (!e.IsGestureRecognitionSkipped) + if (_gestureRecognizers?.HandlePointerReleased(e) == true) + { + e.Handled = true; + } + } + + private void OnGesturePointerPressed(PointerPressedEventArgs e) + { + if (!e.IsGestureRecognitionSkipped) + if (_gestureRecognizers?.HandlePointerPressed(e) == true) + { + e.Handled = true; + } + } + + private void OnGesturePointerMoved(PointerEventArgs e) + { + if (!e.IsGestureRecognitionSkipped) + if (_gestureRecognizers?.HandlePointerMoved(e) == true) + { + e.Handled = true; + } } /// @@ -619,7 +639,7 @@ namespace Avalonia.Input /// The event args. protected virtual void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { - _gestureRecognizers?.HandlePointerCaptureLost(e); + } /// diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 69c7a83900..bb8313106f 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -163,7 +163,22 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var source = _pointer.Captured ?? hitTest; + IInputElement source; + if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer) + { + source = gestureRecognizer.Target ?? hitTest; + + if(source != null) + { + var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root, + p, timestamp, properties, inputModifiers, intermediatePoints); + gestureRecognizer.PointerMoved(e); + + return e.Handled; + } + } + + source = _pointer.Captured ?? hitTest; if (source is object) { @@ -174,6 +189,7 @@ namespace Avalonia.Input return e.Handled; } + return false; } @@ -183,7 +199,22 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var source = _pointer.Captured ?? hitTest; + IInputElement source; + if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer) + { + source = gestureRecognizer.Target ?? hitTest; + + if (source != null) + { + var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers, + _lastMouseDownButton); + gestureRecognizer.PointerReleased(e); + + return e.Handled; + } + } + + source = _pointer.Captured ?? hitTest; if (source is not null) { @@ -192,6 +223,7 @@ namespace Avalonia.Input source?.RaiseEvent(e); _pointer.Capture(null); + _pointer.CaptureGestureRecognizer(null); _lastMouseDownButton = default; return e.Handled; } diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index d5ccfdb18d..fcf7d31e03 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -114,7 +114,22 @@ namespace Avalonia.Input KeyModifiers inputModifiers, IInputElement? hitTest, Lazy?>? intermediatePoints) { - var source = pointer.Captured ?? hitTest; + IInputElement source; + if (pointer.CapturedGestureRecognizer is { } gestureRecognizer) + { + source = gestureRecognizer.Target ?? hitTest; + + if (source != null) + { + var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, (Visual)root, + p, timestamp, properties, inputModifiers, intermediatePoints); + gestureRecognizer.PointerMoved(e); + + return e.Handled; + } + } + + source = pointer.Captured ?? hitTest; if (source is not null) { @@ -132,7 +147,22 @@ namespace Avalonia.Input IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement? hitTest) { - var source = pointer.Captured ?? hitTest; + IInputElement source; + if (pointer.CapturedGestureRecognizer is { } gestureRecognizer) + { + source = gestureRecognizer.Target ?? hitTest; + + if (source != null) + { + var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, + _lastMouseDownButton); + gestureRecognizer.PointerReleased(e); + + return e.Handled; + } + } + + source = pointer.Captured ?? hitTest; if (source is not null) { @@ -141,6 +171,7 @@ namespace Avalonia.Input source.RaiseEvent(e); pointer.Capture(null); + pointer.CaptureGestureRecognizer(null); _lastMouseDownButton = default; return e.Handled; } diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index 30c32ae48c..3abda3637e 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -54,7 +54,7 @@ namespace Avalonia.Input if (Captured is Visual v3) v3.DetachedFromVisualTree += OnCaptureDetached; - if (Captured == null) + if (Captured != null) CaptureGestureRecognizer(null); } @@ -94,6 +94,9 @@ namespace Avalonia.Input if (CapturedGestureRecognizer != gestureRecognizer) CapturedGestureRecognizer?.PointerCaptureLost(this); + if (gestureRecognizer != null) + Capture(null); + CapturedGestureRecognizer = gestureRecognizer; } } diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 9c24e5c314..cd742cc4e4 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -58,6 +58,8 @@ namespace Avalonia.Input /// public ulong Timestamp { get; } + internal bool IsGestureRecognitionSkipped { get; private set; } + /// /// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated. /// @@ -121,6 +123,14 @@ namespace Avalonia.Input return points; } + /// + /// Prevents this event from being handled by other gesture recognizers in the route + /// + public void PreventGestureRecognition() + { + IsGestureRecognitionSkipped = true; + } + /// /// Returns the current pointer point properties /// diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index 74c5837b84..c5b372defa 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -52,6 +52,7 @@ namespace Avalonia.Input } var target = pointer.Captured ?? args.Root; + var gestureTarget = pointer.CapturedGestureRecognizer?.Target; var updateKind = args.Type.ToUpdateKind(); var keyModifier = args.InputModifiers.ToKeyModifiers(); @@ -95,10 +96,19 @@ namespace Avalonia.Input _pointers.Remove(args.RawPointerId); using (pointer) { - target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, - (Visual)args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), - keyModifier, MouseButton.Left)); + target = gestureTarget ?? target; + var e = new PointerReleasedEventArgs(target, pointer, + (Visual)args.Root, args.Position, ev.Timestamp, + new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), + keyModifier, MouseButton.Left); + if (gestureTarget != null) + { + pointer?.CapturedGestureRecognizer?.PointerReleased(e); + } + else + { + target.RaiseEvent(e); + } } } @@ -106,15 +116,28 @@ namespace Avalonia.Input { _pointers.Remove(args.RawPointerId); using (pointer) + { pointer.Capture(null); + pointer.CaptureGestureRecognizer(null); + } } if (args.Type == RawPointerEventType.TouchUpdate) { - target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root, + target = gestureTarget ?? target; + var e = new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), - keyModifier, args.IntermediatePoints)); + keyModifier, args.IntermediatePoints); + + if (gestureTarget != null) + { + pointer?.CapturedGestureRecognizer?.PointerMoved(e); + } + else + { + target.RaiseEvent(e); + } } } diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs index c5d73dc00f..7299300cc7 100644 --- a/src/Avalonia.Controls/Primitives/Thumb.cs +++ b/src/Avalonia.Controls/Primitives/Thumb.cs @@ -113,6 +113,8 @@ namespace Avalonia.Controls.Primitives PseudoClasses.Add(":pressed"); + e.PreventGestureRecognition(); + RaiseEvent(ev); } From 5ad9e4ea4ed478875ff46d4669b29800018a15da Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 26 May 2023 12:16:17 +0000 Subject: [PATCH 053/391] fix tests --- src/Avalonia.Base/Input/MouseDevice.cs | 10 +++++----- src/Avalonia.Base/Input/PenDevice.cs | 9 +++++---- src/Avalonia.Base/Input/TouchDevice.cs | 6 +++--- tests/Avalonia.UnitTests/TouchTestHelper.cs | 20 ++++++++++++++++---- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index bb8313106f..328d5e945f 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -7,7 +7,7 @@ using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Utilities; using Avalonia.VisualTree; - +using Avalonia.Input.GestureRecognizers; #pragma warning disable CS0618 namespace Avalonia.Input @@ -163,8 +163,8 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - IInputElement source; - if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer) + IInputElement? source; + if (_pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) { source = gestureRecognizer.Target ?? hitTest; @@ -199,8 +199,8 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - IInputElement source; - if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer) + IInputElement? source; + if (_pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) { source = gestureRecognizer.Target ?? hitTest; diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index fcf7d31e03..a183dbbaf8 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Avalonia.Input.GestureRecognizers; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Metadata; @@ -114,8 +115,8 @@ namespace Avalonia.Input KeyModifiers inputModifiers, IInputElement? hitTest, Lazy?>? intermediatePoints) { - IInputElement source; - if (pointer.CapturedGestureRecognizer is { } gestureRecognizer) + IInputElement? source; + if (pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) { source = gestureRecognizer.Target ?? hitTest; @@ -147,8 +148,8 @@ namespace Avalonia.Input IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement? hitTest) { - IInputElement source; - if (pointer.CapturedGestureRecognizer is { } gestureRecognizer) + IInputElement? source; + if (pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) { source = gestureRecognizer.Target ?? hitTest; diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index c5b372defa..02f3479e95 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -117,15 +117,15 @@ namespace Avalonia.Input _pointers.Remove(args.RawPointerId); using (pointer) { - pointer.Capture(null); - pointer.CaptureGestureRecognizer(null); + pointer?.Capture(null); + pointer?.CaptureGestureRecognizer(null); } } if (args.Type == RawPointerEventType.TouchUpdate) { target = gestureTarget ?? target; - var e = new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root, + var e = new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer!, (Visual)args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), keyModifier, args.IntermediatePoints); diff --git a/tests/Avalonia.UnitTests/TouchTestHelper.cs b/tests/Avalonia.UnitTests/TouchTestHelper.cs index db70f570a2..703cb45b96 100644 --- a/tests/Avalonia.UnitTests/TouchTestHelper.cs +++ b/tests/Avalonia.UnitTests/TouchTestHelper.cs @@ -27,8 +27,13 @@ namespace Avalonia.UnitTests public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default) { - target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position, - Timestamp(), PointerPointProperties.None, modifiers)); + var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position, + Timestamp(), PointerPointProperties.None, modifiers); + if (_pointer.CapturedGestureRecognizer != null) + _pointer.CapturedGestureRecognizer.PointerMoved(e); + else + target.RaiseEvent(e); + } public void Up(Interactive target, Point position = default, KeyModifiers modifiers = default) @@ -36,9 +41,16 @@ namespace Avalonia.UnitTests public void Up(Interactive target, Interactive source, Point position = default, KeyModifiers modifiers = default) { - source.RaiseEvent(new PointerReleasedEventArgs(source, _pointer, (Visual)target, position, Timestamp(), PointerPointProperties.None, - modifiers, MouseButton.None)); + var e = new PointerReleasedEventArgs(source, _pointer, (Visual)target, position, Timestamp(), PointerPointProperties.None, + modifiers, MouseButton.None); + + if (_pointer.CapturedGestureRecognizer != null) + _pointer.CapturedGestureRecognizer.PointerReleased(e); + else + source.RaiseEvent(e); + _pointer.Capture(null); + _pointer.CaptureGestureRecognizer(null); } public void Tap(Interactive target, Point position = default, KeyModifiers modifiers = default) From 0042c19121f185a0895f1e3f6572b2706b72fca5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 29 May 2023 15:48:48 +0000 Subject: [PATCH 054/391] replace IGestureRecognizer with abstrac GestureRecognizer. remove related obsolete interfaces. --- .../GestureRecognizers/GestureRecognizer.cs | 23 +++++++++++++++ .../GestureRecognizerCollection.cs | 29 +++++-------------- .../GestureRecognizers/IGestureRecognizer.cs | 24 --------------- .../PinchGestureRecognizer.cs | 20 ++++++------- .../PullGestureRecognizer.cs | 18 +++++------- .../ScrollGestureRecognizer.cs | 20 ++++++------- src/Avalonia.Base/Input/MouseDevice.cs | 24 ++++++--------- src/Avalonia.Base/Input/PenDevice.cs | 22 +++++--------- src/Avalonia.Base/Input/Pointer.cs | 4 +-- 9 files changed, 75 insertions(+), 109 deletions(-) create mode 100644 src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs delete mode 100644 src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs new file mode 100644 index 0000000000..fd6748dee7 --- /dev/null +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Input.GestureRecognizers +{ + public abstract class GestureRecognizer : StyledElement + { + public abstract IInputElement? Target { get; } + public abstract void Initialize(IInputElement target); + public abstract void PointerPressed(PointerPressedEventArgs e); + public abstract void PointerReleased(PointerReleasedEventArgs e); + public abstract void PointerMoved(PointerEventArgs e); + public abstract void PointerCaptureLost(IPointer pointer); + + protected void Capture(IPointer pointer) + { + (pointer as Pointer)?.CaptureGestureRecognizer(this); + } + } +} diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs index 8ec9cb7301..e7c6513c43 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs @@ -6,26 +6,26 @@ using Avalonia.Reactive; namespace Avalonia.Input.GestureRecognizers { - public class GestureRecognizerCollection : IReadOnlyCollection, IGestureRecognizerActionsDispatcher + public class GestureRecognizerCollection : IReadOnlyCollection { private readonly IInputElement _inputElement; - private List? _recognizers; + private List? _recognizers; public GestureRecognizerCollection(IInputElement inputElement) { _inputElement = inputElement; } - public void Add(IGestureRecognizer recognizer) + public void Add(GestureRecognizer recognizer) { if (_recognizers == null) { // We initialize the collection when the first recognizer is added - _recognizers = new List(); + _recognizers = new List(); } _recognizers.Add(recognizer); - recognizer.Initialize(_inputElement, this); + recognizer.Initialize(_inputElement); // Hacks to make bindings work @@ -38,16 +38,15 @@ namespace Avalonia.Input.GestureRecognizers } } - static readonly List s_Empty = new List(); + static readonly List s_Empty = new List(); - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() => _recognizers?.GetEnumerator() ?? s_Empty.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public int Count => _recognizers?.Count ?? 0; - internal bool HandlePointerPressed(PointerPressedEventArgs e) { if (_recognizers == null) @@ -91,19 +90,5 @@ namespace Avalonia.Input.GestureRecognizers } return e.Handled; } - - void IGestureRecognizerActionsDispatcher.Capture(IPointer pointer, IGestureRecognizer recognizer) - { - var p = pointer as Pointer; - - p?.CaptureGestureRecognizer(recognizer); - - foreach (var r in _recognizers!) - { - if (r != recognizer) - r.PointerCaptureLost(pointer); - } - } - } } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs deleted file mode 100644 index 51c36a93de..0000000000 --- a/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Avalonia.Input.GestureRecognizers -{ - public interface IGestureRecognizer - { - IInputElement? Target { get; } - void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions); - void PointerPressed(PointerPressedEventArgs e); - void PointerReleased(PointerReleasedEventArgs e); - void PointerMoved(PointerEventArgs e); - void PointerCaptureLost(IPointer pointer); - } - - public interface IGestureRecognizerActionsDispatcher - { - void Capture(IPointer pointer, IGestureRecognizer recognizer); - } - - public enum GestureRecognizerResult - { - None, - Capture, - ReleaseCapture - } -} diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs index 4390167e98..133fd76947 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs @@ -2,10 +2,9 @@ namespace Avalonia.Input { - public class PinchGestureRecognizer : AvaloniaObject, IGestureRecognizer + public class PinchGestureRecognizer : GestureRecognizer { private IInputElement? _target; - private IGestureRecognizerActionsDispatcher? _actions; private float _initialDistance; private IPointer? _firstContact; private Point _firstPoint; @@ -13,12 +12,11 @@ namespace Avalonia.Input private Point _secondPoint; private Point _origin; - public IInputElement? Target => _target; + public override IInputElement? Target => _target; - public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) + public override void Initialize(IInputElement target) { _target = target; - _actions = actions; } private void OnPointerPressed(object? sender, PointerPressedEventArgs e) @@ -31,12 +29,12 @@ namespace Avalonia.Input PointerReleased(e); } - public void PointerCaptureLost(IPointer pointer) + public override void PointerCaptureLost(IPointer pointer) { RemoveContact(pointer); } - public void PointerMoved(PointerEventArgs e) + public override void PointerMoved(PointerEventArgs e) { if (_target != null && _target is Visual visual) { @@ -67,7 +65,7 @@ namespace Avalonia.Input } } - public void PointerPressed(PointerPressedEventArgs e) + public override void PointerPressed(PointerPressedEventArgs e) { if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -94,13 +92,13 @@ namespace Avalonia.Input _origin = new Point((_firstPoint.X + _secondPoint.X) / 2.0f, (_firstPoint.Y + _secondPoint.Y) / 2.0f); - _actions!.Capture(_firstContact, this); - _actions!.Capture(_secondContact, this); + Capture(_firstContact); + Capture(_secondContact); } } } - public void PointerReleased(PointerReleasedEventArgs e) + public override void PointerReleased(PointerReleasedEventArgs e) { RemoveContact(e.Pointer); } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs index 7391cdbaca..f6ab1c4508 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs @@ -3,12 +3,11 @@ using Avalonia.Input.GestureRecognizers; namespace Avalonia.Input { - public class PullGestureRecognizer : AvaloniaObject, IGestureRecognizer + public class PullGestureRecognizer : GestureRecognizer { internal static int MinPullDetectionSize = 50; private IInputElement? _target; - private IGestureRecognizerActionsDispatcher? _actions; private Point _initialPosition; private int _gestureId; private IPointer? _tracking; @@ -26,7 +25,7 @@ namespace Avalonia.Input set => SetValue(PullDirectionProperty, value); } - public IInputElement? Target => _target; + public override IInputElement? Target => _target; public PullGestureRecognizer(PullDirection pullDirection) { @@ -35,13 +34,12 @@ namespace Avalonia.Input public PullGestureRecognizer() { } - public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) + public override void Initialize(IInputElement target) { _target = target; - _actions = actions; } - public void PointerCaptureLost(IPointer pointer) + public override void PointerCaptureLost(IPointer pointer) { if (_tracking == pointer) { @@ -49,12 +47,12 @@ namespace Avalonia.Input } } - public void PointerMoved(PointerEventArgs e) + public override void PointerMoved(PointerEventArgs e) { if (_tracking == e.Pointer && _target is Visual visual) { var currentPosition = e.GetPosition(visual); - _actions!.Capture(e.Pointer, this); + Capture(e.Pointer); e.PreventGestureRecognition(); Vector delta = default; @@ -94,7 +92,7 @@ namespace Avalonia.Input } } - public void PointerPressed(PointerPressedEventArgs e) + public override void PointerPressed(PointerPressedEventArgs e) { if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -129,7 +127,7 @@ namespace Avalonia.Input } } - public void PointerReleased(PointerReleasedEventArgs e) + public override void PointerReleased(PointerReleasedEventArgs e) { if (_tracking == e.Pointer && _pullInProgress) { diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index 3206c3903a..ecc72c8f60 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -4,7 +4,7 @@ using Avalonia.Threading; namespace Avalonia.Input.GestureRecognizers { - public class ScrollGestureRecognizer : AvaloniaObject, IGestureRecognizer + public class ScrollGestureRecognizer : GestureRecognizer { // Pixels per second speed that is considered to be the stop of inertial scroll internal const double InertialScrollSpeedEnd = 5; @@ -19,7 +19,6 @@ namespace Avalonia.Input.GestureRecognizers private Point _trackedRootPoint; private IPointer? _tracking; private IInputElement? _target; - private IGestureRecognizerActionsDispatcher? _actions; private int _gestureId; private Point _pointerPressedPoint; private VelocityTracker? _velocityTracker; @@ -93,15 +92,14 @@ namespace Avalonia.Input.GestureRecognizers set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value); } - public IInputElement? Target => _target; + public override IInputElement? Target => _target; - public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) + public override void Initialize(IInputElement target) { _target = target; - _actions = actions; } - public void PointerPressed(PointerPressedEventArgs e) + public override void PointerPressed(PointerPressedEventArgs e) { if (e.Pointer.IsPrimary && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) @@ -112,8 +110,8 @@ namespace Avalonia.Input.GestureRecognizers _trackedRootPoint = _pointerPressedPoint = e.GetPosition((Visual?)_target); } } - - public void PointerMoved(PointerEventArgs e) + + public override void PointerMoved(PointerEventArgs e) { if (e.Pointer == _tracking) { @@ -133,7 +131,7 @@ namespace Avalonia.Input.GestureRecognizers _trackedRootPoint.X - (_trackedRootPoint.X >= rootPoint.X ? ScrollStartDistance : -ScrollStartDistance), _trackedRootPoint.Y - (_trackedRootPoint.Y >= rootPoint.Y ? ScrollStartDistance : -ScrollStartDistance)); - _actions!.Capture(e.Pointer, this); + Capture(e.Pointer); e.PreventGestureRecognition(); } @@ -153,7 +151,7 @@ namespace Avalonia.Input.GestureRecognizers } } - public void PointerCaptureLost(IPointer pointer) + public override void PointerCaptureLost(IPointer pointer) { if (pointer == _tracking) EndGesture(); } @@ -173,7 +171,7 @@ namespace Avalonia.Input.GestureRecognizers } - public void PointerReleased(PointerReleasedEventArgs e) + public override void PointerReleased(PointerReleasedEventArgs e) { if (e.Pointer == _tracking && _scrolling) { diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 328d5e945f..4da8062897 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -163,14 +163,11 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - IInputElement? source; - if (_pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) + if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) { - source = gestureRecognizer.Target ?? hitTest; - - if(source != null) + if(gestureRecognizer.Target != null) { - var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root, + var e = new PointerEventArgs(InputElement.PointerMovedEvent, gestureRecognizer.Target, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, intermediatePoints); gestureRecognizer.PointerMoved(e); @@ -178,7 +175,7 @@ namespace Avalonia.Input } } - source = _pointer.Captured ?? hitTest; + var source = _pointer.Captured ?? hitTest; if (source is object) { @@ -198,15 +195,12 @@ namespace Avalonia.Input { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - - IInputElement? source; - if (_pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) + + if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) { - source = gestureRecognizer.Target ?? hitTest; - - if (source != null) + if (gestureRecognizer.Target != null) { - var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers, + var e = new PointerReleasedEventArgs(gestureRecognizer.Target, _pointer, (Visual)root, p, timestamp, props, inputModifiers, _lastMouseDownButton); gestureRecognizer.PointerReleased(e); @@ -214,7 +208,7 @@ namespace Avalonia.Input } } - source = _pointer.Captured ?? hitTest; + var source = _pointer.Captured ?? hitTest; if (source is not null) { diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index a183dbbaf8..fab0f1320c 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -115,14 +115,11 @@ namespace Avalonia.Input KeyModifiers inputModifiers, IInputElement? hitTest, Lazy?>? intermediatePoints) { - IInputElement? source; - if (pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) + if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) { - source = gestureRecognizer.Target ?? hitTest; - - if (source != null) + if (gestureRecognizer.Target != null) { - var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, (Visual)root, + var e = new PointerEventArgs(InputElement.PointerMovedEvent, gestureRecognizer.Target, pointer, (Visual)root, p, timestamp, properties, inputModifiers, intermediatePoints); gestureRecognizer.PointerMoved(e); @@ -130,7 +127,7 @@ namespace Avalonia.Input } } - source = pointer.Captured ?? hitTest; + var source = pointer.Captured ?? hitTest; if (source is not null) { @@ -148,14 +145,11 @@ namespace Avalonia.Input IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement? hitTest) { - IInputElement? source; - if (pointer.CapturedGestureRecognizer is IGestureRecognizer gestureRecognizer) + if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) { - source = gestureRecognizer.Target ?? hitTest; - - if (source != null) + if (gestureRecognizer.Target != null) { - var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, + var e = new PointerReleasedEventArgs(gestureRecognizer.Target, pointer, (Visual)root, p, timestamp, properties, inputModifiers, _lastMouseDownButton); gestureRecognizer.PointerReleased(e); @@ -163,7 +157,7 @@ namespace Avalonia.Input } } - source = pointer.Captured ?? hitTest; + var source = pointer.Captured ?? hitTest; if (source is not null) { diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index 3abda3637e..65c0d7f538 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -77,7 +77,7 @@ namespace Avalonia.Input /// /// Gets the gesture recognizer that is currently capturing by the pointer, if any. /// - internal IGestureRecognizer? CapturedGestureRecognizer { get; private set; } + internal GestureRecognizer? CapturedGestureRecognizer { get; private set; } public void Dispose() { @@ -89,7 +89,7 @@ namespace Avalonia.Input /// /// The gesture recognizer. /// - internal void CaptureGestureRecognizer(IGestureRecognizer? gestureRecognizer) + internal void CaptureGestureRecognizer(GestureRecognizer? gestureRecognizer) { if (CapturedGestureRecognizer != gestureRecognizer) CapturedGestureRecognizer?.PointerCaptureLost(this); From 0ac73d1a734bd4b63dbf2bf80265af3d5d80c58d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 30 May 2023 09:11:09 +0000 Subject: [PATCH 055/391] remove GestureRecognizer.Initialize --- .../GestureRecognizers/GestureRecognizer.cs | 12 +++-------- .../GestureRecognizerCollection.cs | 2 +- .../PinchGestureRecognizer.cs | 16 ++++----------- .../PullGestureRecognizer.cs | 17 ++++------------ .../ScrollGestureRecognizer.cs | 20 ++++++------------- 5 files changed, 18 insertions(+), 49 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs index fd6748dee7..c7f40b4468 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs @@ -1,15 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Avalonia.Input.GestureRecognizers +namespace Avalonia.Input.GestureRecognizers { public abstract class GestureRecognizer : StyledElement { - public abstract IInputElement? Target { get; } - public abstract void Initialize(IInputElement target); + protected internal IInputElement? Target { get; internal set; } + public abstract void PointerPressed(PointerPressedEventArgs e); public abstract void PointerReleased(PointerReleasedEventArgs e); public abstract void PointerMoved(PointerEventArgs e); diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs index e7c6513c43..d8c2d5a21b 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs @@ -25,7 +25,7 @@ namespace Avalonia.Input.GestureRecognizers } _recognizers.Add(recognizer); - recognizer.Initialize(_inputElement); + recognizer.Target = _inputElement; // Hacks to make bindings work diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs index 133fd76947..b5342ae180 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs @@ -4,7 +4,6 @@ namespace Avalonia.Input { public class PinchGestureRecognizer : GestureRecognizer { - private IInputElement? _target; private float _initialDistance; private IPointer? _firstContact; private Point _firstPoint; @@ -12,13 +11,6 @@ namespace Avalonia.Input private Point _secondPoint; private Point _origin; - public override IInputElement? Target => _target; - - public override void Initialize(IInputElement target) - { - _target = target; - } - private void OnPointerPressed(object? sender, PointerPressedEventArgs e) { PointerPressed(e); @@ -36,7 +28,7 @@ namespace Avalonia.Input public override void PointerMoved(PointerEventArgs e) { - if (_target != null && _target is Visual visual) + if (Target != null && Target is Visual visual) { if(_firstContact == e.Pointer) { @@ -58,7 +50,7 @@ namespace Avalonia.Input var scale = distance / _initialDistance; var pinchEventArgs = new PinchEventArgs(scale, _origin); - _target?.RaiseEvent(pinchEventArgs); + Target?.RaiseEvent(pinchEventArgs); e.Handled = pinchEventArgs.Handled; } @@ -67,7 +59,7 @@ namespace Avalonia.Input public override void PointerPressed(PointerPressedEventArgs e) { - if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) + if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { if (_firstContact == null) { @@ -118,7 +110,7 @@ namespace Avalonia.Input _secondContact = null; } - _target?.RaiseEvent(new PinchEndedEventArgs()); + Target?.RaiseEvent(new PinchEndedEventArgs()); } } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs index f6ab1c4508..4d463203e6 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs @@ -6,8 +6,6 @@ namespace Avalonia.Input public class PullGestureRecognizer : GestureRecognizer { internal static int MinPullDetectionSize = 50; - - private IInputElement? _target; private Point _initialPosition; private int _gestureId; private IPointer? _tracking; @@ -25,8 +23,6 @@ namespace Avalonia.Input set => SetValue(PullDirectionProperty, value); } - public override IInputElement? Target => _target; - public PullGestureRecognizer(PullDirection pullDirection) { PullDirection = pullDirection; @@ -34,11 +30,6 @@ namespace Avalonia.Input public PullGestureRecognizer() { } - public override void Initialize(IInputElement target) - { - _target = target; - } - public override void PointerCaptureLost(IPointer pointer) { if (_tracking == pointer) @@ -49,7 +40,7 @@ namespace Avalonia.Input public override void PointerMoved(PointerEventArgs e) { - if (_tracking == e.Pointer && _target is Visual visual) + if (_tracking == e.Pointer && Target is Visual visual) { var currentPosition = e.GetPosition(visual); Capture(e.Pointer); @@ -86,7 +77,7 @@ namespace Avalonia.Input _pullInProgress = true; var pullEventArgs = new PullGestureEventArgs(_gestureId, delta, PullDirection); - _target?.RaiseEvent(pullEventArgs); + Target?.RaiseEvent(pullEventArgs); e.Handled = pullEventArgs.Handled; } @@ -94,7 +85,7 @@ namespace Avalonia.Input public override void PointerPressed(PointerPressedEventArgs e) { - if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) + if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { var position = e.GetPosition(visual); @@ -141,7 +132,7 @@ namespace Avalonia.Input _initialPosition = default; _pullInProgress = false; - _target?.RaiseEvent(new PullGestureEndedEventArgs(_gestureId, PullDirection)); + Target?.RaiseEvent(new PullGestureEndedEventArgs(_gestureId, PullDirection)); } } } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index ecc72c8f60..2f2afd5289 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -18,7 +18,6 @@ namespace Avalonia.Input.GestureRecognizers private bool _scrolling; private Point _trackedRootPoint; private IPointer? _tracking; - private IInputElement? _target; private int _gestureId; private Point _pointerPressedPoint; private VelocityTracker? _velocityTracker; @@ -91,13 +90,6 @@ namespace Avalonia.Input.GestureRecognizers get => _scrollStartDistance; set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value); } - - public override IInputElement? Target => _target; - - public override void Initialize(IInputElement target) - { - _target = target; - } public override void PointerPressed(PointerPressedEventArgs e) { @@ -107,7 +99,7 @@ namespace Avalonia.Input.GestureRecognizers EndGesture(); _tracking = e.Pointer; _gestureId = ScrollGestureEventArgs.GetNextFreeId(); - _trackedRootPoint = _pointerPressedPoint = e.GetPosition((Visual?)_target); + _trackedRootPoint = _pointerPressedPoint = e.GetPosition((Visual?)Target); } } @@ -115,7 +107,7 @@ namespace Avalonia.Input.GestureRecognizers { if (e.Pointer == _tracking) { - var rootPoint = e.GetPosition((Visual?)_target); + var rootPoint = e.GetPosition((Visual?)Target); if (!_scrolling) { if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance) @@ -145,7 +137,7 @@ namespace Avalonia.Input.GestureRecognizers _lastMoveTimestamp = e.Timestamp; _trackedRootPoint = rootPoint; - _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector)); + Target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector)); e.Handled = true; } } @@ -163,7 +155,7 @@ namespace Avalonia.Input.GestureRecognizers { _inertia = default; _scrolling = false; - _target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId)); + Target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId)); _gestureId = 0; _lastMoveTimestamp = null; } @@ -190,7 +182,7 @@ namespace Avalonia.Input.GestureRecognizers var savedGestureId = _gestureId; var st = Stopwatch.StartNew(); var lastTime = TimeSpan.Zero; - _target!.RaiseEvent(new ScrollGestureInertiaStartingEventArgs(_gestureId, _inertia)); + Target!.RaiseEvent(new ScrollGestureInertiaStartingEventArgs(_gestureId, _inertia)); DispatcherTimer.Run(() => { // Another gesture has started, finish the current one @@ -205,7 +197,7 @@ namespace Avalonia.Input.GestureRecognizers var speed = _inertia * Math.Pow(InertialResistance, st.Elapsed.TotalSeconds); var distance = speed * elapsedSinceLastTick.TotalSeconds; var scrollGestureEventArgs = new ScrollGestureEventArgs(_gestureId, distance); - _target!.RaiseEvent(scrollGestureEventArgs); + Target!.RaiseEvent(scrollGestureEventArgs); if (!scrollGestureEventArgs.Handled || scrollGestureEventArgs.ShouldEndScrollGesture) { From 9b41e8e8f820fdc7c3c7c6bcb59535f0122f0a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 30 May 2023 10:07:15 +0000 Subject: [PATCH 056/391] make pointer handler protected internal --- .../Input/GestureRecognizers/GestureRecognizer.cs | 8 ++++---- .../Input/GestureRecognizers/PinchGestureRecognizer.cs | 8 ++++---- .../Input/GestureRecognizers/PullGestureRecognizer.cs | 8 ++++---- .../GestureRecognizers/ScrollGestureRecognizer.cs | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs index c7f40b4468..e96c397d11 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs @@ -4,10 +4,10 @@ { protected internal IInputElement? Target { get; internal set; } - public abstract void PointerPressed(PointerPressedEventArgs e); - public abstract void PointerReleased(PointerReleasedEventArgs e); - public abstract void PointerMoved(PointerEventArgs e); - public abstract void PointerCaptureLost(IPointer pointer); + protected internal abstract void PointerPressed(PointerPressedEventArgs e); + protected internal abstract void PointerReleased(PointerReleasedEventArgs e); + protected internal abstract void PointerMoved(PointerEventArgs e); + protected internal abstract void PointerCaptureLost(IPointer pointer); protected void Capture(IPointer pointer) { diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs index b5342ae180..b12c2cc701 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs @@ -21,12 +21,12 @@ namespace Avalonia.Input PointerReleased(e); } - public override void PointerCaptureLost(IPointer pointer) + protected internal override void PointerCaptureLost(IPointer pointer) { RemoveContact(pointer); } - public override void PointerMoved(PointerEventArgs e) + protected internal override void PointerMoved(PointerEventArgs e) { if (Target != null && Target is Visual visual) { @@ -57,7 +57,7 @@ namespace Avalonia.Input } } - public override void PointerPressed(PointerPressedEventArgs e) + protected internal override void PointerPressed(PointerPressedEventArgs e) { if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -90,7 +90,7 @@ namespace Avalonia.Input } } - public override void PointerReleased(PointerReleasedEventArgs e) + protected internal override void PointerReleased(PointerReleasedEventArgs e) { RemoveContact(e.Pointer); } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs index 4d463203e6..5358eb4cda 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs @@ -30,7 +30,7 @@ namespace Avalonia.Input public PullGestureRecognizer() { } - public override void PointerCaptureLost(IPointer pointer) + protected internal override void PointerCaptureLost(IPointer pointer) { if (_tracking == pointer) { @@ -38,7 +38,7 @@ namespace Avalonia.Input } } - public override void PointerMoved(PointerEventArgs e) + protected internal override void PointerMoved(PointerEventArgs e) { if (_tracking == e.Pointer && Target is Visual visual) { @@ -83,7 +83,7 @@ namespace Avalonia.Input } } - public override void PointerPressed(PointerPressedEventArgs e) + protected internal override void PointerPressed(PointerPressedEventArgs e) { if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -118,7 +118,7 @@ namespace Avalonia.Input } } - public override void PointerReleased(PointerReleasedEventArgs e) + protected internal override void PointerReleased(PointerReleasedEventArgs e) { if (_tracking == e.Pointer && _pullInProgress) { diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index 2f2afd5289..80c0583d90 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -90,8 +90,8 @@ namespace Avalonia.Input.GestureRecognizers get => _scrollStartDistance; set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value); } - - public override void PointerPressed(PointerPressedEventArgs e) + + protected internal override void PointerPressed(PointerPressedEventArgs e) { if (e.Pointer.IsPrimary && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) @@ -103,7 +103,7 @@ namespace Avalonia.Input.GestureRecognizers } } - public override void PointerMoved(PointerEventArgs e) + protected internal override void PointerMoved(PointerEventArgs e) { if (e.Pointer == _tracking) { @@ -143,7 +143,7 @@ namespace Avalonia.Input.GestureRecognizers } } - public override void PointerCaptureLost(IPointer pointer) + protected internal override void PointerCaptureLost(IPointer pointer) { if (pointer == _tracking) EndGesture(); } @@ -163,7 +163,7 @@ namespace Avalonia.Input.GestureRecognizers } - public override void PointerReleased(PointerReleasedEventArgs e) + protected internal override void PointerReleased(PointerReleasedEventArgs e) { if (e.Pointer == _tracking && _scrolling) { From abcad6e78ad3ee908ffa1a88202a22f4bf347c41 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 31 May 2023 18:46:00 +0600 Subject: [PATCH 057/391] Added InterpolatingTransitionBase --- .../Animation/InterpolatingTransitionBase.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Avalonia.Base/Animation/InterpolatingTransitionBase.cs diff --git a/src/Avalonia.Base/Animation/InterpolatingTransitionBase.cs b/src/Avalonia.Base/Animation/InterpolatingTransitionBase.cs new file mode 100644 index 0000000000..44e3129b76 --- /dev/null +++ b/src/Avalonia.Base/Animation/InterpolatingTransitionBase.cs @@ -0,0 +1,28 @@ +using System; +using Avalonia.Animation.Animators; + +namespace Avalonia.Animation; + +/// +/// The base class for user-defined transition that are doing simple value interpolation +/// +public abstract class InterpolatingTransitionBase : Transition +{ + class Animator : Animator + { + private readonly InterpolatingTransitionBase _parent; + + public Animator(InterpolatingTransitionBase parent) + { + _parent = parent; + } + + public override T Interpolate(double progress, T oldValue, T newValue) => + _parent.Interpolate(progress, oldValue, newValue); + } + + protected abstract T Interpolate(double progress, T from, T to); + + internal override IObservable DoTransition(IObservable progress, T oldValue, T newValue) => + new AnimatorTransitionObservable(new Animator(this), progress, Easing, oldValue, newValue); +} \ No newline at end of file From c12fe17315dade003858ea1359704e7ff64b9bfb Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 31 May 2023 12:50:03 +0000 Subject: [PATCH 058/391] Added internal helpers for protected gesture events --- .../GestureRecognizers/GestureRecognizer.cs | 28 ++++++++++++-- .../GestureRecognizerCollection.cs | 6 +-- .../PinchGestureRecognizer.cs | 8 ++-- .../PullGestureRecognizer.cs | 8 ++-- .../ScrollGestureRecognizer.cs | 8 ++-- src/Avalonia.Base/Input/MouseDevice.cs | 38 +++++-------------- src/Avalonia.Base/Input/PenDevice.cs | 38 +++++-------------- src/Avalonia.Base/Input/Pointer.cs | 2 +- src/Avalonia.Base/Input/TouchDevice.cs | 4 +- tests/Avalonia.UnitTests/TouchTestHelper.cs | 4 +- 10 files changed, 64 insertions(+), 80 deletions(-) diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs index e96c397d11..2cb97b4707 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizer.cs @@ -4,10 +4,30 @@ { protected internal IInputElement? Target { get; internal set; } - protected internal abstract void PointerPressed(PointerPressedEventArgs e); - protected internal abstract void PointerReleased(PointerReleasedEventArgs e); - protected internal abstract void PointerMoved(PointerEventArgs e); - protected internal abstract void PointerCaptureLost(IPointer pointer); + protected abstract void PointerPressed(PointerPressedEventArgs e); + protected abstract void PointerReleased(PointerReleasedEventArgs e); + protected abstract void PointerMoved(PointerEventArgs e); + protected abstract void PointerCaptureLost(IPointer pointer); + + internal void PointerPressedInternal(PointerPressedEventArgs e) + { + PointerPressed(e); + } + + internal void PointerReleasedInternal(PointerReleasedEventArgs e) + { + PointerReleased(e); + } + + internal void PointerMovedInternal(PointerEventArgs e) + { + PointerMoved(e); + } + + internal void PointerCaptureLostInternal(IPointer pointer) + { + PointerCaptureLost(pointer); + } protected void Capture(IPointer pointer) { diff --git a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs index d8c2d5a21b..05dce8214b 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs @@ -53,7 +53,7 @@ namespace Avalonia.Input.GestureRecognizers return false; foreach (var r in _recognizers) { - r.PointerPressed(e); + r.PointerPressedInternal(e); } return e.Handled; @@ -70,7 +70,7 @@ namespace Avalonia.Input.GestureRecognizers if (pointer?.CapturedGestureRecognizer != null) break; - r.PointerReleased(e); + r.PointerReleasedInternal(e); } return e.Handled; } @@ -86,7 +86,7 @@ namespace Avalonia.Input.GestureRecognizers if (pointer?.CapturedGestureRecognizer != null) break; - r.PointerMoved(e); + r.PointerMovedInternal(e); } return e.Handled; } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs index b12c2cc701..b02c82a066 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs @@ -21,12 +21,12 @@ namespace Avalonia.Input PointerReleased(e); } - protected internal override void PointerCaptureLost(IPointer pointer) + protected override void PointerCaptureLost(IPointer pointer) { RemoveContact(pointer); } - protected internal override void PointerMoved(PointerEventArgs e) + protected override void PointerMoved(PointerEventArgs e) { if (Target != null && Target is Visual visual) { @@ -57,7 +57,7 @@ namespace Avalonia.Input } } - protected internal override void PointerPressed(PointerPressedEventArgs e) + protected override void PointerPressed(PointerPressedEventArgs e) { if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -90,7 +90,7 @@ namespace Avalonia.Input } } - protected internal override void PointerReleased(PointerReleasedEventArgs e) + protected override void PointerReleased(PointerReleasedEventArgs e) { RemoveContact(e.Pointer); } diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs index 5358eb4cda..57faf5bfd8 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs @@ -30,7 +30,7 @@ namespace Avalonia.Input public PullGestureRecognizer() { } - protected internal override void PointerCaptureLost(IPointer pointer) + protected override void PointerCaptureLost(IPointer pointer) { if (_tracking == pointer) { @@ -38,7 +38,7 @@ namespace Avalonia.Input } } - protected internal override void PointerMoved(PointerEventArgs e) + protected override void PointerMoved(PointerEventArgs e) { if (_tracking == e.Pointer && Target is Visual visual) { @@ -83,7 +83,7 @@ namespace Avalonia.Input } } - protected internal override void PointerPressed(PointerPressedEventArgs e) + protected override void PointerPressed(PointerPressedEventArgs e) { if (Target != null && Target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { @@ -118,7 +118,7 @@ namespace Avalonia.Input } } - protected internal override void PointerReleased(PointerReleasedEventArgs e) + protected override void PointerReleased(PointerReleasedEventArgs e) { if (_tracking == e.Pointer && _pullInProgress) { diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs index 80c0583d90..beecfcc3ce 100644 --- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -91,7 +91,7 @@ namespace Avalonia.Input.GestureRecognizers set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value); } - protected internal override void PointerPressed(PointerPressedEventArgs e) + protected override void PointerPressed(PointerPressedEventArgs e) { if (e.Pointer.IsPrimary && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) @@ -103,7 +103,7 @@ namespace Avalonia.Input.GestureRecognizers } } - protected internal override void PointerMoved(PointerEventArgs e) + protected override void PointerMoved(PointerEventArgs e) { if (e.Pointer == _tracking) { @@ -143,7 +143,7 @@ namespace Avalonia.Input.GestureRecognizers } } - protected internal override void PointerCaptureLost(IPointer pointer) + protected override void PointerCaptureLost(IPointer pointer) { if (pointer == _tracking) EndGesture(); } @@ -163,7 +163,7 @@ namespace Avalonia.Input.GestureRecognizers } - protected internal override void PointerReleased(PointerReleasedEventArgs e) + protected override void PointerReleased(PointerReleasedEventArgs e) { if (e.Pointer == _tracking && _scrolling) { diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 4da8062897..db333dbd8b 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -163,26 +163,17 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) - { - if(gestureRecognizer.Target != null) - { - var e = new PointerEventArgs(InputElement.PointerMovedEvent, gestureRecognizer.Target, _pointer, (Visual)root, - p, timestamp, properties, inputModifiers, intermediatePoints); - gestureRecognizer.PointerMoved(e); - - return e.Handled; - } - } - - var source = _pointer.Captured ?? hitTest; + var source = _pointer.CapturedGestureRecognizer?.Target ?? _pointer.Captured ?? hitTest; if (source is object) { var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, intermediatePoints); - source.RaiseEvent(e); + if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + gestureRecognizer.PointerMovedInternal(e); + else + source.RaiseEvent(e); return e.Handled; } @@ -195,27 +186,18 @@ namespace Avalonia.Input { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - - if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) - { - if (gestureRecognizer.Target != null) - { - var e = new PointerReleasedEventArgs(gestureRecognizer.Target, _pointer, (Visual)root, p, timestamp, props, inputModifiers, - _lastMouseDownButton); - gestureRecognizer.PointerReleased(e); - - return e.Handled; - } - } - var source = _pointer.Captured ?? hitTest; + var source = _pointer.CapturedGestureRecognizer?.Target ?? _pointer.Captured ?? hitTest; if (source is not null) { var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers, _lastMouseDownButton); - source?.RaiseEvent(e); + if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + gestureRecognizer.PointerReleasedInternal(e); + else + source?.RaiseEvent(e); _pointer.Capture(null); _pointer.CaptureGestureRecognizer(null); _lastMouseDownButton = default; diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index fab0f1320c..09bf18d3fd 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -115,26 +115,17 @@ namespace Avalonia.Input KeyModifiers inputModifiers, IInputElement? hitTest, Lazy?>? intermediatePoints) { - if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) - { - if (gestureRecognizer.Target != null) - { - var e = new PointerEventArgs(InputElement.PointerMovedEvent, gestureRecognizer.Target, pointer, (Visual)root, - p, timestamp, properties, inputModifiers, intermediatePoints); - gestureRecognizer.PointerMoved(e); - - return e.Handled; - } - } - - var source = pointer.Captured ?? hitTest; + var source = pointer.CapturedGestureRecognizer?.Target ?? pointer.Captured ?? hitTest; if (source is not null) { var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, intermediatePoints); - source.RaiseEvent(e); + if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + gestureRecognizer.PointerMovedInternal(e); + else + source.RaiseEvent(e); return e.Handled; } @@ -145,26 +136,17 @@ namespace Avalonia.Input IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement? hitTest) { - if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) - { - if (gestureRecognizer.Target != null) - { - var e = new PointerReleasedEventArgs(gestureRecognizer.Target, pointer, (Visual)root, p, timestamp, properties, inputModifiers, - _lastMouseDownButton); - gestureRecognizer.PointerReleased(e); - - return e.Handled; - } - } - - var source = pointer.Captured ?? hitTest; + var source = pointer.CapturedGestureRecognizer?.Target ?? pointer.Captured ?? hitTest; if (source is not null) { var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, _lastMouseDownButton); - source.RaiseEvent(e); + if (pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + gestureRecognizer.PointerReleasedInternal(e); + else + source.RaiseEvent(e); pointer.Capture(null); pointer.CaptureGestureRecognizer(null); _lastMouseDownButton = default; diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index 65c0d7f538..91358712a0 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -92,7 +92,7 @@ namespace Avalonia.Input internal void CaptureGestureRecognizer(GestureRecognizer? gestureRecognizer) { if (CapturedGestureRecognizer != gestureRecognizer) - CapturedGestureRecognizer?.PointerCaptureLost(this); + CapturedGestureRecognizer?.PointerCaptureLostInternal(this); if (gestureRecognizer != null) Capture(null); diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index 02f3479e95..8868e966f0 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -103,7 +103,7 @@ namespace Avalonia.Input keyModifier, MouseButton.Left); if (gestureTarget != null) { - pointer?.CapturedGestureRecognizer?.PointerReleased(e); + pointer?.CapturedGestureRecognizer?.PointerReleasedInternal(e); } else { @@ -132,7 +132,7 @@ namespace Avalonia.Input if (gestureTarget != null) { - pointer?.CapturedGestureRecognizer?.PointerMoved(e); + pointer?.CapturedGestureRecognizer?.PointerMovedInternal(e); } else { diff --git a/tests/Avalonia.UnitTests/TouchTestHelper.cs b/tests/Avalonia.UnitTests/TouchTestHelper.cs index 703cb45b96..574599d1ad 100644 --- a/tests/Avalonia.UnitTests/TouchTestHelper.cs +++ b/tests/Avalonia.UnitTests/TouchTestHelper.cs @@ -30,7 +30,7 @@ namespace Avalonia.UnitTests var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position, Timestamp(), PointerPointProperties.None, modifiers); if (_pointer.CapturedGestureRecognizer != null) - _pointer.CapturedGestureRecognizer.PointerMoved(e); + _pointer.CapturedGestureRecognizer.PointerMovedInternal(e); else target.RaiseEvent(e); @@ -45,7 +45,7 @@ namespace Avalonia.UnitTests modifiers, MouseButton.None); if (_pointer.CapturedGestureRecognizer != null) - _pointer.CapturedGestureRecognizer.PointerReleased(e); + _pointer.CapturedGestureRecognizer.PointerReleasedInternal(e); else source.RaiseEvent(e); From b34f8321e7be03d00187e0746318abfe19e520da Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 17:39:09 +0100 Subject: [PATCH 059/391] implement msbuild property to give access to internal apis. --- packages/Avalonia/Avalonia.targets | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/Avalonia/Avalonia.targets b/packages/Avalonia/Avalonia.targets index 50306f2cdc..0961c82b5c 100644 --- a/packages/Avalonia/Avalonia.targets +++ b/packages/Avalonia/Avalonia.targets @@ -1,3 +1,19 @@ + + + + + + + + + + + + + + + + From a87cd48ae1aaf0b1a7e8c990d5a4a8b68ebfdf5a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 17:45:02 +0100 Subject: [PATCH 060/391] use our own targets file for private apis. --- packages/Avalonia/Avalonia.targets | 17 +-------------- packages/Avalonia/AvaloniaPrivateApis.targets | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 packages/Avalonia/AvaloniaPrivateApis.targets diff --git a/packages/Avalonia/Avalonia.targets b/packages/Avalonia/Avalonia.targets index 0961c82b5c..eacd287b59 100644 --- a/packages/Avalonia/Avalonia.targets +++ b/packages/Avalonia/Avalonia.targets @@ -1,19 +1,4 @@ - - - - - - - - - - - - - - - - + diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets new file mode 100644 index 0000000000..fdf60a24e5 --- /dev/null +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -0,0 +1,21 @@ + + + net6.0 + netstandard2.0 + + + + + + + + + + + + + + + + + From a9e6f335c763f53565d5dcdfdd7a80c5de544b36 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 17:49:45 +0100 Subject: [PATCH 061/391] add a warning. --- packages/Avalonia/AvaloniaPrivateApis.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index fdf60a24e5..8d44c7bbbf 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -17,5 +17,6 @@ + From c8676f275b38d3bd44389cf59f9fc907d57beb88 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:31:35 +0100 Subject: [PATCH 062/391] fix targets file. --- packages/Avalonia/AvaloniaPrivateApis.targets | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index 8d44c7bbbf..a98fecce40 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -1,7 +1,7 @@ - net6.0 - netstandard2.0 + net6.0 + netstandard2.0 @@ -13,9 +13,9 @@ - - - + + + From 59fb1536a6a1c02a4b775f2f333e1432a5f53203 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:37:12 +0100 Subject: [PATCH 063/391] better warning message --- packages/Avalonia/AvaloniaPrivateApis.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index a98fecce40..ec93dcb5dc 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -17,6 +17,6 @@ - + From 6f598fff06873d6f39b92cdc27b17177f7f7686e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:40:30 +0100 Subject: [PATCH 064/391] fix referencing unstable apis. --- packages/Avalonia/AvaloniaPrivateApis.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index ec93dcb5dc..5f6d19773c 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -13,9 +13,9 @@ - - - + + + From c142c5a0e722a6a5e2853d5be0173d70b80d5870 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:43:19 +0100 Subject: [PATCH 065/391] prefix the property name --- packages/Avalonia/AvaloniaPrivateApis.targets | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index 5f6d19773c..95c08520a9 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -1,8 +1,4 @@ - - net6.0 - netstandard2.0 - @@ -12,9 +8,13 @@ + + net6.0 + netstandard2.0 + - - + + From 7e0c578f3e27e0e7cc4629129325c594ceeac182 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:45:03 +0100 Subject: [PATCH 066/391] fix bug --- packages/Avalonia/AvaloniaPrivateApis.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index 95c08520a9..368d69322d 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -15,7 +15,7 @@ - + From 036e6c3c4f9cf6d240aed91f25c0cf16037acc0c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 18:50:55 +0100 Subject: [PATCH 067/391] add error if user is publishing a library on nuget. --- packages/Avalonia/AvaloniaPrivateApis.targets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index 368d69322d..e7807aeb78 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -18,5 +18,7 @@ + From 0dc6b6b042e46f270799cd30a22e5be5cd4530ec Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 19:45:57 +0100 Subject: [PATCH 068/391] ensure packing error does not occur on normal builds. --- packages/Avalonia/AvaloniaPrivateApis.targets | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index e7807aeb78..5343559c07 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -1,5 +1,5 @@ - + @@ -7,7 +7,11 @@ - + + + + net6.0 netstandard2.0 @@ -18,7 +22,5 @@ - From 51da158ad6a4a6f289a661b234d88c8520a71528 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Wed, 31 May 2023 21:10:34 +0200 Subject: [PATCH 069/391] Fix DBus Tray Icon when the org.kde.StatusNotifierWatcher service is unavailable --- src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index 9abf4f97a7..61e46b651e 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -60,9 +60,9 @@ namespace Avalonia.FreeDesktop { try { - _serviceWatchDisposable = await _dBus!.WatchNameOwnerChangedAsync((_, x) => OnNameChange(x.Item2)); + _serviceWatchDisposable = await _dBus!.WatchNameOwnerChangedAsync((_, x) => OnNameChange(x.Item1, x.Item3)); var nameOwner = await _dBus.GetNameOwnerAsync("org.kde.StatusNotifierWatcher"); - OnNameChange(nameOwner); + OnNameChange("org.kde.StatusNotifierWatcher", nameOwner); } catch { @@ -72,9 +72,9 @@ namespace Avalonia.FreeDesktop } } - private void OnNameChange(string? newOwner) + private void OnNameChange(string name, string? newOwner) { - if (_isDisposed || _connection is null) + if (_isDisposed || _connection is null || name != "org.kde.StatusNotifierWatcher") return; if (!_serviceConnected & newOwner is not null) From c650d835221a80d4c18776856abf11feef0968fc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 31 May 2023 20:51:40 +0100 Subject: [PATCH 070/391] build services fix. --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index a4101afac1..3f313ead1b 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From fb9baae0125525856292857cd699a54626039828 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 31 May 2023 10:43:31 +0200 Subject: [PATCH 071/391] Fixed ManagedDispatcherImpl wait --- .../Platform/ManagedDispatcherImpl.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs index fdc098777a..c8225f775b 100644 --- a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs +++ b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs @@ -99,9 +99,15 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl continue; } - if (_nextTimer != null) + TimeSpan? nextTimer; + lock (_lock) + { + nextTimer = _nextTimer; + } + + if (nextTimer != null) { - var waitFor = _clock.Elapsed - _nextTimer.Value; + var waitFor = nextTimer.Value - _clock.Elapsed; if (waitFor.TotalMilliseconds < 1) continue; _wakeup.WaitOne(waitFor); @@ -112,4 +118,4 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl registration.Dispose(); } -} \ No newline at end of file +} From af343bdf027c9b56e8d92ffcafcdb1d3347514b8 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 31 May 2023 18:28:33 +0200 Subject: [PATCH 072/391] Implemented a proper framebuffer for RemoteServerTopLevelImpl --- .../Offscreen/OffscreenTopLevelImpl.cs | 19 +-- .../RemoteServerTopLevelImpl.Framebuffer.cs | 116 +++++++++++++ .../Remote/Server/RemoteServerTopLevelImpl.cs | 154 ++++++++---------- .../Remote/PreviewerWindowImpl.cs | 2 +- .../Remote/PreviewerWindowingPlatform.cs | 2 +- 5 files changed, 190 insertions(+), 103 deletions(-) create mode 100644 src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index f379120638..106ac8dff5 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -1,14 +1,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Media; using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Rendering.Composition; -using Avalonia.Threading; namespace Avalonia.Controls.Embedding.Offscreen { @@ -17,7 +13,6 @@ namespace Avalonia.Controls.Embedding.Offscreen { private double _scaling = 1; private Size _clientSize; - private ManualRenderTimer _manualRenderTimer = new(); public IInputRoot? InputRoot { get; private set; } public bool IsDisposed { get; private set; } @@ -27,21 +22,10 @@ namespace Avalonia.Controls.Embedding.Offscreen IsDisposed = true; } - class ManualRenderTimer : IRenderTimer - { - static Stopwatch St = Stopwatch.StartNew(); - public event Action? Tick; - public bool RunsInBackground => false; - public void TriggerTick() => Tick?.Invoke(St.Elapsed); - } - public Compositor Compositor { get; } public OffscreenTopLevelImplBase() - { - Compositor = new Compositor(new RenderLoop(_manualRenderTimer), null, false, - MediaContext.Instance, false); - } + => Compositor = new Compositor(null); public abstract IEnumerable Surfaces { get; } @@ -76,7 +60,6 @@ namespace Avalonia.Controls.Embedding.Offscreen public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { } - /// public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1); public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot; diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs new file mode 100644 index 0000000000..361c3033b5 --- /dev/null +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs @@ -0,0 +1,116 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform; +using Avalonia.Remote.Protocol.Viewport; +using PlatformPixelFormat = Avalonia.Platform.PixelFormat; +using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; + +namespace Avalonia.Controls.Remote.Server +{ + internal partial class RemoteServerTopLevelImpl + { + private enum FrameStatus + { + NotRendered, + Rendered, + CopiedToMessage + } + + private sealed class Framebuffer + { + public static Framebuffer Empty { get; } = + new(ProtocolPixelFormat.Rgba8888, default, new Vector(96.0, 96.0)); + + private readonly object _dataLock = new(); + private readonly byte[] _data; // for rendering only + private readonly byte[] _dataCopy; // for messages only + private FrameStatus _status = FrameStatus.NotRendered; + + public Framebuffer(ProtocolPixelFormat format, Size clientSize, Vector dpi) + { + var frameSize = PixelSize.FromSizeWithDpi(clientSize, dpi); + if (frameSize.Width <= 0 || frameSize.Height <= 0) + frameSize = PixelSize.Empty; + + var bpp = format == ProtocolPixelFormat.Rgb565 ? 2 : 4; + var stride = frameSize.Width * bpp; + var dataLength = Math.Max(0, stride * frameSize.Height); + + Format = format; + FrameSize = frameSize; + ClientSize = clientSize; + Dpi = dpi; + + (Stride, _data, _dataCopy) = dataLength > 0 ? + (stride, new byte[dataLength], new byte[dataLength]) : + (0, Array.Empty(), Array.Empty()); + } + + public ProtocolPixelFormat Format { get; } + + public Size ClientSize { get; } + + public Vector Dpi { get; } + + public PixelSize FrameSize { get; } + + public int Stride { get; } + + public FrameStatus GetStatus() + { + lock (_dataLock) + return _status; + } + + public ILockedFramebuffer Lock(Action onUnlocked) + { + var handle = GCHandle.Alloc(_data, GCHandleType.Pinned); + Monitor.Enter(_dataLock); + + try + { + return new LockedFramebuffer( + handle.AddrOfPinnedObject(), + FrameSize, + Stride, + Dpi, + new PlatformPixelFormat((PixelFormatEnum)Format), + () => + { + handle.Free(); + Array.Copy(_data, _dataCopy, _data.Length); + _status = FrameStatus.Rendered; + Monitor.Exit(_dataLock); + onUnlocked(); + }); + } + catch + { + handle.Free(); + Monitor.Exit(_dataLock); + throw; + } + } + + /// The returned message must be kept around, as it contains a shared buffer. + public FrameMessage ToMessage(long sequenceId) + { + lock (_dataLock) + _status = FrameStatus.CopiedToMessage; + + return new FrameMessage + { + SequenceId = sequenceId, + Data = _dataCopy, + Format = Format, + Width = FrameSize.Width, + Height = FrameSize.Height, + Stride = Stride, + DpiX = Dpi.X, + DpiY = Dpi.Y + }; + } + } + } +} diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 8e4123a790..f8fc1957a4 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using Avalonia.Controls.Embedding.Offscreen; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; @@ -11,7 +10,6 @@ using Avalonia.Platform; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Input; using Avalonia.Remote.Protocol.Viewport; -using Avalonia.Rendering; using Avalonia.Threading; using Key = Avalonia.Input.Key; using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; @@ -20,28 +18,29 @@ using ProtocolMouseButton = Avalonia.Remote.Protocol.Input.MouseButton; namespace Avalonia.Controls.Remote.Server { [Unstable] - internal class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface, ITopLevelImpl + internal partial class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface, ITopLevelImpl { private readonly IAvaloniaRemoteTransportConnection _transport; - private LockedFramebuffer? _framebuffer; private readonly object _lock = new(); + private readonly Action _sendLastFrameIfNeeded; + private readonly Action _renderAndSendFrameIfNeeded; + private Framebuffer _framebuffer = Framebuffer.Empty; private long _lastSentFrame = -1; private long _lastReceivedFrame = -1; private long _nextFrameNumber = 1; private ClientViewportAllocatedMessage? _pendingAllocation; - private bool _queuedNextRender; - private bool _inRender; - private Vector _dpi = new Vector(96, 96); - private ProtocolPixelFormat[]? _supportedFormats; + private Vector _dpi = new(96, 96); + private ProtocolPixelFormat? _format; public RemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport) { + _sendLastFrameIfNeeded = SendLastFrameIfNeeded; + _renderAndSendFrameIfNeeded = RenderAndSendFrameIfNeeded; + _transport = transport; _transport.OnMessage += OnMessage; KeyboardDevice = AvaloniaLocator.Current.GetRequiredService(); - QueueNextRender(); - Compositor.AfterCommit += QueueNextRender; } private static RawPointerEventType GetAvaloniaEventType(ProtocolMouseButton button, bool pressed) @@ -118,23 +117,23 @@ namespace Avalonia.Controls.Remote.Server { _lastReceivedFrame = Math.Max(lastFrame.SequenceId, _lastReceivedFrame); } - Dispatcher.UIThread.Post(RenderIfNeeded); + Dispatcher.UIThread.Post(_sendLastFrameIfNeeded); } - if(obj is ClientRenderInfoMessage renderInfo) + if (obj is ClientRenderInfoMessage renderInfo) { - lock(_lock) + lock (_lock) { _dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY); - _queuedNextRender = true; } - - Dispatcher.UIThread.Post(RenderIfNeeded); + Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); } if (obj is ClientSupportedPixelFormatsMessage supportedFormats) { lock (_lock) - _supportedFormats = supportedFormats.Formats; - Dispatcher.UIThread.Post(RenderIfNeeded); + { + _format = TryGetValidPixelFormat(supportedFormats.Formats); + } + Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); } if (obj is MeasureViewportMessage measure) Dispatcher.UIThread.Post(() => @@ -161,7 +160,7 @@ namespace Avalonia.Controls.Remote.Server } _dpi = new Vector(allocation.DpiX, allocation.DpiY); ClientSize = new Size(allocation.Width, allocation.Height); - RenderIfNeeded(); + RenderAndSendFrameIfNeeded(); }); _pendingAllocation = allocated; @@ -250,10 +249,24 @@ namespace Avalonia.Controls.Remote.Server } } + private static ProtocolPixelFormat? TryGetValidPixelFormat(ProtocolPixelFormat[]? formats) + { + if (formats is not null) + { + foreach (var format in formats) + { + if (format is >= 0 and <= ProtocolPixelFormat.MaxValue) + return format; + } + } + + return null; + } + protected void SetDpi(Vector dpi) { _dpi = dpi; - RenderIfNeeded(); + RenderAndSendFrameIfNeeded(); } protected virtual Size Measure(Size constraint) @@ -265,88 +278,63 @@ namespace Avalonia.Controls.Remote.Server public override IEnumerable Surfaces => new[] { this }; - private FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format) + private Framebuffer GetOrCreateFramebuffer() { - var scalingX = _dpi.X / 96.0; - var scalingY = _dpi.Y / 96.0; - - width = (int)(width * scalingX); - height = (int)(height * scalingY); - - var fmt = format ?? ProtocolPixelFormat.Rgba8888; - var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4; - var data = new byte[width * height * bpp]; - var handle = GCHandle.Alloc(data, GCHandleType.Pinned); - - try - { - if (width > 0 && height > 0) - { - _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, new((PixelFormatEnum)fmt), - null); - Paint?.Invoke(new Rect(0, 0, width, height)); - } - } - finally + lock (_lock) { - _framebuffer = null; - handle.Free(); + if (_format is not { } format) + _framebuffer = Framebuffer.Empty; + else if (_framebuffer.Format != format || _framebuffer.ClientSize != ClientSize || _framebuffer.Dpi != _dpi) + _framebuffer = new Framebuffer(format, ClientSize, _dpi); + + return _framebuffer; } - return new FrameMessage - { - Data = data, - Format = fmt, - Width = width, - Height = height, - Stride = width * bpp, - DpiX = _dpi.X, - DpiY = _dpi.Y - }; } public ILockedFramebuffer Lock() - { - if (_framebuffer == null) - throw new InvalidOperationException("Paint was not requested, wait for Paint event"); - return _framebuffer; - } + => GetOrCreateFramebuffer().Lock(_sendLastFrameIfNeeded); - protected void RenderIfNeeded() + private void SendLastFrameIfNeeded() { - lock (_lock) - { - if (_lastReceivedFrame != _lastSentFrame || !_queuedNextRender || _supportedFormats == null) - return; + if (IsDisposed) + return; - } - - var format = ProtocolPixelFormat.Rgba8888; - foreach(var fmt in _supportedFormats) - if (fmt <= ProtocolPixelFormat.MaxValue) - { - format = fmt; - break; - } + Framebuffer framebuffer; + long sequenceId; - _inRender = true; - var frame = RenderFrame((int) ClientSize.Width, (int) ClientSize.Height, format); lock (_lock) { + // Ideally we should only send a frame if its status is Rendered: since the renderer might not be + // initialized at the start, we're sending black frames in this case. However, this was the historical + // behavior and some external programs are depending on receiving a frame asap. + if (_lastReceivedFrame != _lastSentFrame || _framebuffer.GetStatus() == FrameStatus.CopiedToMessage) + return; + + framebuffer = _framebuffer; _lastSentFrame = _nextFrameNumber++; - frame.SequenceId = _lastSentFrame; - _queuedNextRender = false; + sequenceId = _lastSentFrame; } - _inRender = false; - _transport.Send(frame); + + _transport.Send(framebuffer.ToMessage(sequenceId)); } - private void QueueNextRender() + protected void RenderAndSendFrameIfNeeded() { - if (!_inRender && !IsDisposed) + if (IsDisposed) + return; + + lock (_lock) { - _queuedNextRender = true; - DispatcherTimer.RunOnce(RenderIfNeeded, TimeSpan.FromMilliseconds(2), DispatcherPriority.Background); + if (_lastReceivedFrame != _lastSentFrame || _format is null) + return; } + + var framebuffer = GetOrCreateFramebuffer(); + + if (framebuffer.Stride > 0) + Paint?.Invoke(new Rect(framebuffer.ClientSize)); + + SendLastFrameIfNeeded(); } public override IMouseDevice MouseDevice { get; } = new MouseDevice(); diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index e0fcf8e530..5fe9f203e7 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -67,7 +67,7 @@ namespace Avalonia.DesignerSupport.Remote Height = clientSize.Height }); ClientSize = clientSize; - RenderIfNeeded(); + RenderAndSendFrameIfNeeded(); } public void Move(PixelPoint point) diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs index d2787e73ae..ba9dd592ce 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs @@ -52,7 +52,7 @@ namespace Avalonia.DesignerSupport.Remote .Bind().ToConstant(Keyboard) .Bind().ToSingleton() .Bind().ToConstant(new ManagedDispatcherImpl(null)) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new UiThreadRenderTimer(60)) .Bind().ToConstant(instance) .Bind().ToSingleton() .Bind().ToSingleton(); From c45d49942bc87831b7242bf49901117d4bb38fef Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 31 May 2023 19:22:13 +0200 Subject: [PATCH 073/391] RemoteServerTopLevelImpl cleanup --- .../Remote/Server/RemoteServerTopLevelImpl.cs | 221 +++++++++--------- 1 file changed, 108 insertions(+), 113 deletions(-) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index f8fc1957a4..632e7fa8a3 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -111,45 +111,38 @@ namespace Avalonia.Controls.Remote.Server { lock (_lock) { - if (obj is FrameReceivedMessage lastFrame) + switch (obj) { - lock (_lock) - { + case FrameReceivedMessage lastFrame: _lastReceivedFrame = Math.Max(lastFrame.SequenceId, _lastReceivedFrame); - } - Dispatcher.UIThread.Post(_sendLastFrameIfNeeded); - } - if (obj is ClientRenderInfoMessage renderInfo) - { - lock (_lock) - { + Dispatcher.UIThread.Post(_sendLastFrameIfNeeded); + break; + + case ClientRenderInfoMessage renderInfo: _dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY); - } - Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); - } - if (obj is ClientSupportedPixelFormatsMessage supportedFormats) - { - lock (_lock) - { + Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); + break; + + case ClientSupportedPixelFormatsMessage supportedFormats: _format = TryGetValidPixelFormat(supportedFormats.Formats); - } - Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); - } - if (obj is MeasureViewportMessage measure) - Dispatcher.UIThread.Post(() => - { - var m = Measure(new Size(measure.Width, measure.Height)); - _transport.Send(new MeasureViewportMessage + Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); + break; + + case MeasureViewportMessage measure: + Dispatcher.UIThread.Post(() => { - Width = m.Width, - Height = m.Height + var m = Measure(new Size(measure.Width, measure.Height)); + _transport.Send(new MeasureViewportMessage + { + Width = m.Width, + Height = m.Height + }); }); - }); - if (obj is ClientViewportAllocatedMessage allocated) - { - lock (_lock) - { + break; + + case ClientViewportAllocatedMessage allocated: if (_pendingAllocation == null) + { Dispatcher.UIThread.Post(() => { ClientViewportAllocatedMessage allocation; @@ -158,93 +151,95 @@ namespace Avalonia.Controls.Remote.Server allocation = _pendingAllocation!; _pendingAllocation = null; } + _dpi = new Vector(allocation.DpiX, allocation.DpiY); ClientSize = new Size(allocation.Width, allocation.Height); RenderAndSendFrameIfNeeded(); }); + } _pendingAllocation = allocated; - } - } - if(obj is PointerMovedEventMessage pointer) - { - Dispatcher.UIThread.Post(() => - { - Input?.Invoke(new RawPointerEventArgs( - MouseDevice, - 0, - InputRoot!, - RawPointerEventType.Move, - new Point(pointer.X, pointer.Y), - GetAvaloniaRawInputModifiers(pointer.Modifiers))); - }, DispatcherPriority.Input); - } - if(obj is PointerPressedEventMessage pressed) - { - Dispatcher.UIThread.Post(() => - { - Input?.Invoke(new RawPointerEventArgs( - MouseDevice, - 0, - InputRoot!, - GetAvaloniaEventType(pressed.Button, true), - new Point(pressed.X, pressed.Y), - GetAvaloniaRawInputModifiers(pressed.Modifiers))); - }, DispatcherPriority.Input); - } - if (obj is PointerReleasedEventMessage released) - { - Dispatcher.UIThread.Post(() => - { - Input?.Invoke(new RawPointerEventArgs( - MouseDevice, - 0, - InputRoot!, - GetAvaloniaEventType(released.Button, false), - new Point(released.X, released.Y), - GetAvaloniaRawInputModifiers(released.Modifiers))); - }, DispatcherPriority.Input); - } - if(obj is ScrollEventMessage scroll) - { - Dispatcher.UIThread.Post(() => - { - Input?.Invoke(new RawMouseWheelEventArgs( - MouseDevice, - 0, - InputRoot!, - new Point(scroll.X, scroll.Y), - new Vector(scroll.DeltaX, scroll.DeltaY), - GetAvaloniaRawInputModifiers(scroll.Modifiers))); - }, DispatcherPriority.Input); - } - if(obj is KeyEventMessage key) - { - Dispatcher.UIThread.Post(() => - { - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - - Input?.Invoke(new RawKeyEventArgs( - KeyboardDevice, - 0, - InputRoot!, - key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - (Key)key.Key, - GetAvaloniaRawInputModifiers(key.Modifiers))); - }, DispatcherPriority.Input); - } - if(obj is TextInputEventMessage text) - { - Dispatcher.UIThread.Post(() => - { - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - - Input?.Invoke(new RawTextInputEventArgs( - KeyboardDevice, - 0, - InputRoot!, - text.Text)); - }, DispatcherPriority.Input); + break; + + case PointerMovedEventMessage pointer: + Dispatcher.UIThread.Post(() => + { + Input?.Invoke(new RawPointerEventArgs( + MouseDevice, + 0, + InputRoot!, + RawPointerEventType.Move, + new Point(pointer.X, pointer.Y), + GetAvaloniaRawInputModifiers(pointer.Modifiers))); + }, DispatcherPriority.Input); + break; + + case PointerPressedEventMessage pressed: + Dispatcher.UIThread.Post(() => + { + Input?.Invoke(new RawPointerEventArgs( + MouseDevice, + 0, + InputRoot!, + GetAvaloniaEventType(pressed.Button, true), + new Point(pressed.X, pressed.Y), + GetAvaloniaRawInputModifiers(pressed.Modifiers))); + }, DispatcherPriority.Input); + break; + + case PointerReleasedEventMessage released: + Dispatcher.UIThread.Post(() => + { + Input?.Invoke(new RawPointerEventArgs( + MouseDevice, + 0, + InputRoot!, + GetAvaloniaEventType(released.Button, false), + new Point(released.X, released.Y), + GetAvaloniaRawInputModifiers(released.Modifiers))); + }, DispatcherPriority.Input); + break; + + case ScrollEventMessage scroll: + Dispatcher.UIThread.Post(() => + { + Input?.Invoke(new RawMouseWheelEventArgs( + MouseDevice, + 0, + InputRoot!, + new Point(scroll.X, scroll.Y), + new Vector(scroll.DeltaX, scroll.DeltaY), + GetAvaloniaRawInputModifiers(scroll.Modifiers))); + }, DispatcherPriority.Input); + break; + + case KeyEventMessage key: + Dispatcher.UIThread.Post(() => + { + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + + Input?.Invoke(new RawKeyEventArgs( + KeyboardDevice, + 0, + InputRoot!, + key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, + (Key)key.Key, + GetAvaloniaRawInputModifiers(key.Modifiers))); + }, DispatcherPriority.Input); + break; + + case TextInputEventMessage text: + Dispatcher.UIThread.Post(() => + { + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + + Input?.Invoke(new RawTextInputEventArgs( + KeyboardDevice, + 0, + InputRoot!, + text.Text)); + }, DispatcherPriority.Input); + break; } } } From 4adf0b68cb32e4c968f006bc08d3179e6134a6b7 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 31 May 2023 23:17:30 +0200 Subject: [PATCH 074/391] Fixed previewer render scaling --- .../RemoteServerTopLevelImpl.Framebuffer.cs | 32 +++++++++---------- .../Remote/Server/RemoteServerTopLevelImpl.cs | 20 +++++------- .../Remote/PreviewerWindowImpl.cs | 6 +++- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs index 361c3033b5..593adfb225 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs @@ -19,17 +19,18 @@ namespace Avalonia.Controls.Remote.Server private sealed class Framebuffer { - public static Framebuffer Empty { get; } = - new(ProtocolPixelFormat.Rgba8888, default, new Vector(96.0, 96.0)); + public static Framebuffer Empty { get; } = new(ProtocolPixelFormat.Rgba8888, default, 1.0); + private readonly double _dpi; + private readonly PixelSize _frameSize; private readonly object _dataLock = new(); private readonly byte[] _data; // for rendering only private readonly byte[] _dataCopy; // for messages only private FrameStatus _status = FrameStatus.NotRendered; - public Framebuffer(ProtocolPixelFormat format, Size clientSize, Vector dpi) + public Framebuffer(ProtocolPixelFormat format, Size clientSize, double renderScaling) { - var frameSize = PixelSize.FromSizeWithDpi(clientSize, dpi); + var frameSize = PixelSize.FromSize(clientSize, renderScaling); if (frameSize.Width <= 0 || frameSize.Height <= 0) frameSize = PixelSize.Empty; @@ -37,10 +38,11 @@ namespace Avalonia.Controls.Remote.Server var stride = frameSize.Width * bpp; var dataLength = Math.Max(0, stride * frameSize.Height); + _dpi = renderScaling * 96.0; + _frameSize = frameSize; Format = format; - FrameSize = frameSize; ClientSize = clientSize; - Dpi = dpi; + RenderScaling = renderScaling; (Stride, _data, _dataCopy) = dataLength > 0 ? (stride, new byte[dataLength], new byte[dataLength]) : @@ -51,9 +53,7 @@ namespace Avalonia.Controls.Remote.Server public Size ClientSize { get; } - public Vector Dpi { get; } - - public PixelSize FrameSize { get; } + public double RenderScaling { get; } public int Stride { get; } @@ -72,9 +72,9 @@ namespace Avalonia.Controls.Remote.Server { return new LockedFramebuffer( handle.AddrOfPinnedObject(), - FrameSize, + _frameSize, Stride, - Dpi, + new Vector(_dpi, _dpi), new PlatformPixelFormat((PixelFormatEnum)Format), () => { @@ -93,7 +93,7 @@ namespace Avalonia.Controls.Remote.Server } } - /// The returned message must be kept around, as it contains a shared buffer. + /// The returned message must NOT be kept around, as it contains a shared buffer. public FrameMessage ToMessage(long sequenceId) { lock (_dataLock) @@ -104,11 +104,11 @@ namespace Avalonia.Controls.Remote.Server SequenceId = sequenceId, Data = _dataCopy, Format = Format, - Width = FrameSize.Width, - Height = FrameSize.Height, + Width = _frameSize.Width, + Height = _frameSize.Height, Stride = Stride, - DpiX = Dpi.X, - DpiY = Dpi.Y + DpiX = _dpi, + DpiY = _dpi }; } } diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 632e7fa8a3..49af6a71a0 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -29,7 +29,6 @@ namespace Avalonia.Controls.Remote.Server private long _lastReceivedFrame = -1; private long _nextFrameNumber = 1; private ClientViewportAllocatedMessage? _pendingAllocation; - private Vector _dpi = new(96, 96); private ProtocolPixelFormat? _format; public RemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport) @@ -119,8 +118,11 @@ namespace Avalonia.Controls.Remote.Server break; case ClientRenderInfoMessage renderInfo: - _dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY); - Dispatcher.UIThread.Post(_renderAndSendFrameIfNeeded); + Dispatcher.UIThread.Post(() => + { + RenderScaling = renderInfo.DpiX / 96.0; + RenderAndSendFrameIfNeeded(); + }); break; case ClientSupportedPixelFormatsMessage supportedFormats: @@ -152,7 +154,7 @@ namespace Avalonia.Controls.Remote.Server _pendingAllocation = null; } - _dpi = new Vector(allocation.DpiX, allocation.DpiY); + RenderScaling = allocation.DpiX / 96.0; ClientSize = new Size(allocation.Width, allocation.Height); RenderAndSendFrameIfNeeded(); }); @@ -258,12 +260,6 @@ namespace Avalonia.Controls.Remote.Server return null; } - protected void SetDpi(Vector dpi) - { - _dpi = dpi; - RenderAndSendFrameIfNeeded(); - } - protected virtual Size Measure(Size constraint) { var l = (Layoutable) InputRoot!; @@ -279,8 +275,8 @@ namespace Avalonia.Controls.Remote.Server { if (_format is not { } format) _framebuffer = Framebuffer.Empty; - else if (_framebuffer.Format != format || _framebuffer.ClientSize != ClientSize || _framebuffer.Dpi != _dpi) - _framebuffer = new Framebuffer(format, ClientSize, _dpi); + else if (_framebuffer.Format != format || _framebuffer.ClientSize != ClientSize || _framebuffer.RenderScaling != RenderScaling) + _framebuffer = new Framebuffer(format, ClientSize, RenderScaling); return _framebuffer; } diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 5fe9f203e7..b6c0c3ae3d 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -53,7 +53,11 @@ namespace Avalonia.DesignerSupport.Remote // In previewer mode we completely ignore client-side viewport size if (obj is ClientViewportAllocatedMessage alloc) { - Dispatcher.UIThread.Post(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY))); + Dispatcher.UIThread.Post(() => + { + RenderScaling = alloc.DpiX / 96.0; + RenderAndSendFrameIfNeeded(); + }); return; } base.OnMessage(transport, obj); From bfdd3fa325880fa88bce9047b817ab2eb2435e60 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 1 Jun 2023 07:13:36 +0200 Subject: [PATCH 075/391] Prevent infinite loop --- samples/Sandbox/MainWindow.axaml | 1 + .../Media/TextFormatting/TextLineImpl.cs | 22 +++++++++------ .../Media/TextFormatting/TextLineTests.cs | 28 ++++++++++++++++++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6929f192c7..f0f099b95b 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,4 +1,5 @@ + diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index c2ec78e187..0b1bd2e189 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -371,14 +371,16 @@ namespace Avalonia.Media.TextFormatting IndexedTextRun currentIndexedRun = _indexedTextRuns[i]; - while(currentIndexedRun.TextSourceCharacterIndex != currentPosition) + while (currentIndexedRun.TextSourceCharacterIndex != currentPosition) { - if(i + 1 < _indexedTextRuns.Count) + if (i + 1 == _indexedTextRuns.Count) { - i++; - - currentIndexedRun = _indexedTextRuns[i]; + break; } + + i++; + + currentIndexedRun = _indexedTextRuns[i]; } return currentIndexedRun; @@ -604,12 +606,14 @@ namespace Avalonia.Media.TextFormatting while (currentIndexedRun.TextSourceCharacterIndex != currentPosition) { - if (i + 1 < _indexedTextRuns.Count) + if (i + 1 == _indexedTextRuns.Count) { - i++; - - currentIndexedRun = _indexedTextRuns[i]; + break; } + + i++; + + currentIndexedRun = _indexedTextRuns[i]; } return currentIndexedRun; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 1d07e780e6..6373fb4e91 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -9,6 +9,7 @@ using Avalonia.Media.TextFormatting; using Avalonia.UnitTests; using Avalonia.Utilities; using Xunit; +using static System.Net.Mime.MediaTypeNames; namespace Avalonia.Skia.UnitTests.Media.TextFormatting { @@ -1072,7 +1073,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } [Fact] - public void Should_GetTextBounds_BiDi() + public void Should_GetTextBounds_Bidi() { var text = "אבגדה 12345 ABCDEF אבגדה"; @@ -1120,6 +1121,31 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_GetTextBounds_Bidi_2() + { + var text = "אבג ABC אבג 123"; + + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new SingleBufferTextSource(text, defaultProperties, true); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, + true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); + + var bounds = textLine.GetTextBounds(0, text.Length); + + Assert.Equal(5, bounds.Count); + + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, bounds.Last().Rectangle.Right); + } + } + private class FixedRunsTextSource : ITextSource { private readonly IReadOnlyList _textRuns; From f4f3670c384d312e4d31161e637aff3f1ddf2f05 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 1 Jun 2023 07:32:21 +0200 Subject: [PATCH 076/391] Remove redundant using --- src/Avalonia.Controls/TextBox.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 873febd421..3016dc8239 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -17,7 +17,6 @@ using Avalonia.Controls.Metadata; using Avalonia.Media.TextFormatting; using Avalonia.Automation.Peers; using Avalonia.Threading; -using Avalonia.Collections; namespace Avalonia.Controls { From 3d9ef67c493144dbacb4347c487bce36ee77e5dd Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2023 15:42:51 +0600 Subject: [PATCH 077/391] Fixed _requestedCommits enumeration --- src/Avalonia.Base/Media/MediaContext.Compositor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/MediaContext.Compositor.cs b/src/Avalonia.Base/Media/MediaContext.Compositor.cs index 9bdd77960d..4ddc2ea9eb 100644 --- a/src/Avalonia.Base/Media/MediaContext.Compositor.cs +++ b/src/Avalonia.Base/Media/MediaContext.Compositor.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Avalonia.Platform; using Avalonia.Rendering.Composition; @@ -78,7 +79,7 @@ partial class MediaContext // Nothing to do, and there are no pending commits return false; - foreach (var c in _requestedCommits) + foreach (var c in _requestedCommits.ToArray()) CommitCompositor(c); _requestedCommits.Clear(); From 685682c5dba9ae69afd8a36215ad5ebd013b037b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Jun 2023 15:43:15 +0600 Subject: [PATCH 078/391] Fixed race condition in Compositor.Commit --- src/Avalonia.Base/Rendering/Composition/Compositor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs index dde9dcd6fb..5838811e9e 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs @@ -130,7 +130,7 @@ namespace Avalonia.Rendering.Composition Dispatcher.UIThread.VerifyAccess(); using var noPump = NonPumpingLockHelper.Use(); - _nextCommit ??= new(); + var commit = _nextCommit ??= new(); (_invokeBeforeCommitRead, _invokeBeforeCommitWrite) = (_invokeBeforeCommitWrite, _invokeBeforeCommitRead); while (_invokeBeforeCommitRead.Count > 0) @@ -188,7 +188,7 @@ namespace Avalonia.Rendering.Composition }, TaskContinuationOptions.ExecuteSynchronously); _nextCommit = null; - return _pendingBatch; + return commit; } } From 7808da21603f63e8d3974cc701bae3a758cd966b Mon Sep 17 00:00:00 2001 From: rabbitism Date: Fri, 2 Jun 2023 12:25:56 +0800 Subject: [PATCH 079/391] feat: attach transitions after ToggleSwitch loaded. --- src/Avalonia.Controls/ToggleSwitch.cs | 34 +++++++++++++++++-- .../Controls/ToggleSwitch.xaml | 21 ++++++------ .../Controls/ToggleSwitch.xaml | 18 +++++++--- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/ToggleSwitch.cs b/src/Avalonia.Controls/ToggleSwitch.cs index a68a022e67..989c3cb261 100644 --- a/src/Avalonia.Controls/ToggleSwitch.cs +++ b/src/Avalonia.Controls/ToggleSwitch.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls.Metadata; +using Avalonia.Animation; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; @@ -42,6 +43,10 @@ namespace Avalonia.Controls x.UpdateKnobPos(x.IsChecked.Value); } }); + KnobTransitionsProperty.Changed.AddClassHandler((x, e) => + { + x.AssignTransitions(); + }); } /// @@ -68,6 +73,8 @@ namespace Avalonia.Controls public static readonly StyledProperty OnContentTemplateProperty = AvaloniaProperty.Register(nameof(OnContentTemplate)); + public static readonly StyledProperty KnobTransitionsProperty = AvaloniaProperty.Register(nameof(KnobTransitions)); + /// /// Gets or Sets the Content that is displayed when in the On State. /// @@ -116,6 +123,14 @@ namespace Avalonia.Controls set { SetValue(OnContentTemplateProperty, value); } } + public Transitions KnobTransitions + { + get { return GetValue(KnobTransitionsProperty); } + set { SetValue(KnobTransitionsProperty, value); } + } + + + private void OffContentChanged(AvaloniaPropertyChangedEventArgs e) { if (e.OldValue is ILogical oldChild) @@ -176,8 +191,23 @@ namespace Avalonia.Controls { UpdateKnobPos(IsChecked.Value); } + } - + + protected override void OnLoaded() + { + base.OnLoaded(); + AssignTransitions(); + } + + private void AssignTransitions() + { + if (_knobsPanel != null) + { + _knobsPanel.Transitions = KnobTransitions; + } + } + private void KnobsPanel_PointerPressed(object? sender, Input.PointerPressedEventArgs e) { _switchStartPoint = e.GetPosition(_switchKnob); diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml index 47d7953096..63c11f6ff8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml @@ -28,6 +28,14 @@ + + + + + @@ -134,16 +142,9 @@ - - diff --git a/src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml b/src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml index a51aeb9ee6..1178c96419 100644 --- a/src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml @@ -46,6 +46,14 @@ + + + + + - - - - - + + + - - - - - - + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs new file mode 100644 index 0000000000..c75da84519 --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml.cs @@ -0,0 +1,131 @@ +using System; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Media.Immutable; + +namespace Avalonia.Diagnostics.Controls +{ + [TemplatePart("PART_ClearButton", typeof(Button))] + partial class BrushEditor : TemplatedControl + { + private readonly EventHandler clearHandler; + private Button? _cleraButton = default; + private readonly ColorView _colorView = new() + { + HexInputAlphaPosition = AlphaComponentPosition.Leading, // Always match XAML + }; + + + public BrushEditor() + { + FlyoutBase.SetAttachedFlyout(this, new Flyout { Content = _colorView }); + _colorView.ColorChanged += (_, e) => Brush = new ImmutableSolidColorBrush(e.NewColor); + clearHandler = (s, e) => Brush = default; + } + + protected override Type StyleKeyOverride => typeof(BrushEditor); + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + if (_cleraButton is not null) + { + _cleraButton.Click -= clearHandler; + } + _cleraButton = e.NameScope.Find + + + diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs index e19c0ff098..0df21c2c1d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs @@ -19,6 +19,12 @@ namespace Avalonia.Rendering.Composition.Animations public static ScalarInterpolator Instance { get; } = new ScalarInterpolator(); } + class DoubleInterpolator : IInterpolator + { + public double Interpolate(double @from, double to, float progress) => @from + (to - @from) * progress; + + public static DoubleInterpolator Instance { get; } = new (); + } class Vector2Interpolator : IInterpolator { @@ -28,6 +34,15 @@ namespace Avalonia.Rendering.Composition.Animations public static Vector2Interpolator Instance { get; } = new Vector2Interpolator(); } + class VectorInterpolator : IInterpolator + { + public Vector Interpolate(Vector @from, Vector to, float progress) + => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress), + DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress)); + + public static VectorInterpolator Instance { get; } = new (); + } + class Vector3Interpolator : IInterpolator { public Vector3 Interpolate(Vector3 @from, Vector3 to, float progress) @@ -36,6 +51,16 @@ namespace Avalonia.Rendering.Composition.Animations public static Vector3Interpolator Instance { get; } = new Vector3Interpolator(); } + class Vector3DInterpolator : IInterpolator + { + public Vector3D Interpolate(Vector3D @from, Vector3D to, float progress) + => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress), + DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress), + DoubleInterpolator.Instance.Interpolate(from.Z, to.Z, progress)); + + public static Vector3DInterpolator Instance { get; } = new (); + } + class Vector4Interpolator : IInterpolator { public Vector4 Interpolate(Vector4 @from, Vector4 to, float progress) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 61a3f7e831..368743f1b5 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -241,8 +241,8 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester continue; // TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time - comp.Offset = new Vector3((float)visual.Bounds.Left, (float)visual.Bounds.Top, 0); - comp.Size = new Vector2((float)visual.Bounds.Width, (float)visual.Bounds.Height); + comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0); + comp.Size = new (visual.Bounds.Width, visual.Bounds.Height); comp.Visible = visual.IsVisible; comp.Opacity = (float)visual.Opacity; comp.ClipToBounds = visual.ClipToBounds; @@ -269,7 +269,7 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester renderTransform *= (-offset) * visual.RenderTransform.Value * (offset); } - comp.TransformMatrix = MatrixUtils.ToMatrix4x4(renderTransform); + comp.TransformMatrix = renderTransform; try { diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs index b7ed6fe612..598d4163d1 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs @@ -28,7 +28,7 @@ public abstract class CompositionCustomVisualHandler _host.Compositor.VerifyAccess(); } - protected Vector2 EffectiveSize + protected Vector EffectiveSize { get { diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index 91d2fb59de..121d11bf2a 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs @@ -70,7 +70,7 @@ namespace Avalonia.Rendering.Composition return false; } - var m33 = MatrixUtils.ToMatrix(m.Value); + var m33 = m.Value; return m33.TryInvert(out matrix); } diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs index 896ae4fc3a..2fdf163153 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs @@ -16,6 +16,7 @@ namespace Avalonia.Rendering.Composition.Expressions private readonly DelegateExpressionFfi _registry; static float Lerp(float a, float b, float p) => p * (b - a) + a; + static double Lerp(double a, double b, double p) => p * (b - a) + a; static Matrix3x2 Inverse(Matrix3x2 m) { @@ -34,6 +35,13 @@ namespace Avalonia.Rendering.Composition.Expressions var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); return t * t * (3.0f - 2.0f * t); } + + static double SmoothStep(double edge0, double edge1, double x) + { + var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + return t * t * (3.0f - 2.0f * t); + } + static Vector2 SmoothStep(Vector2 edge0, Vector2 edge1, Vector2 x) { @@ -43,6 +51,15 @@ namespace Avalonia.Rendering.Composition.Expressions ); } + + static Vector SmoothStep(Vector edge0, Vector edge1, Vector x) + { + return new ( + SmoothStep(edge0.X, edge1.X, x.X), + SmoothStep(edge0.Y, edge1.Y, x.Y) + ); + } + static Vector3 SmoothStep(Vector3 edge0, Vector3 edge1, Vector3 x) { return new Vector3( @@ -52,6 +69,15 @@ namespace Avalonia.Rendering.Composition.Expressions ); } + + static Vector3D SmoothStep(Vector3D edge0, Vector3D edge1, Vector3D x) + { + return new ( + SmoothStep(edge0.X, edge1.X, x.X), + SmoothStep(edge0.Y, edge1.Y, x.Y), + SmoothStep(edge0.Z, edge1.Z, x.Z) + ); + } static Vector4 SmoothStep(Vector4 edge0, Vector4 edge1, Vector4 x) { @@ -69,7 +95,9 @@ namespace Avalonia.Rendering.Composition.Expressions { {"Abs", (float f) => Math.Abs(f)}, {"Abs", (Vector2 v) => Vector2.Abs(v)}, + {"Abs", (Vector v) => v.Abs()}, {"Abs", (Vector3 v) => Vector3.Abs(v)}, + {"Abs", (Vector3D v) => v.Abs()}, {"Abs", (Vector4 v) => Vector4.Abs(v)}, {"ACos", (float f) => (float) Math.Acos(f)}, @@ -79,7 +107,9 @@ namespace Avalonia.Rendering.Composition.Expressions {"Clamp", (float a1, float a2, float a3) => MathUtilities.Clamp(a1, a2, a3)}, {"Clamp", (Vector2 a1, Vector2 a2, Vector2 a3) => Vector2.Clamp(a1, a2, a3)}, + {"Clamp", (Vector a1, Vector a2, Vector a3) => Vector.Clamp(a1, a2, a3)}, {"Clamp", (Vector3 a1, Vector3 a2, Vector3 a3) => Vector3.Clamp(a1, a2, a3)}, + {"Clamp", (Vector3D a1, Vector3D a2, Vector3D a3) => Vector3D.Clamp(a1, a2, a3)}, {"Clamp", (Vector4 a1, Vector4 a2, Vector4 a3) => Vector4.Clamp(a1, a2, a3)}, {"Concatenate", (Quaternion a1, Quaternion a2) => Quaternion.Concatenate(a1, a2)}, @@ -109,11 +139,15 @@ namespace Avalonia.Rendering.Composition.Expressions }, {"Distance", (Vector2 a1, Vector2 a2) => Vector2.Distance(a1, a2)}, + {"Distance", (Vector a1, Vector a2) => Vector.Distance(a1, a2)}, {"Distance", (Vector3 a1, Vector3 a2) => Vector3.Distance(a1, a2)}, + {"Distance", (Vector3D a1, Vector3D a2) => Vector3D.Distance(a1, a2)}, {"Distance", (Vector4 a1, Vector4 a2) => Vector4.Distance(a1, a2)}, {"DistanceSquared", (Vector2 a1, Vector2 a2) => Vector2.DistanceSquared(a1, a2)}, + {"DistanceSquared", (Vector a1, Vector a2) => Vector.DistanceSquared(a1, a2)}, {"DistanceSquared", (Vector3 a1, Vector3 a2) => Vector3.DistanceSquared(a1, a2)}, + {"DistanceSquared", (Vector3D a1, Vector3D a2) => Vector3D.DistanceSquared(a1, a2)}, {"DistanceSquared", (Vector4 a1, Vector4 a2) => Vector4.DistanceSquared(a1, a2)}, {"Floor", (float v) => (float) Math.Floor(v)}, @@ -123,18 +157,24 @@ namespace Avalonia.Rendering.Composition.Expressions {"Length", (Vector2 a1) => a1.Length()}, + {"Length", (Vector a1) => a1.Length}, {"Length", (Vector3 a1) => a1.Length()}, + {"Length", (Vector3D a1) => a1.Length}, {"Length", (Vector4 a1) => a1.Length()}, {"Length", (Quaternion a1) => a1.Length()}, {"LengthSquared", (Vector2 a1) => a1.LengthSquared()}, + {"LengthSquared", (Vector a1) => a1*a1}, {"LengthSquared", (Vector3 a1) => a1.LengthSquared()}, + {"LengthSquared", (Vector3D a1) => Vector3D.Dot(a1, a1)}, {"LengthSquared", (Vector4 a1) => a1.LengthSquared()}, {"LengthSquared", (Quaternion a1) => a1.LengthSquared()}, {"Lerp", (float a1, float a2, float a3) => Lerp(a1, a2, a3)}, {"Lerp", (Vector2 a1, Vector2 a2, float a3) => Vector2.Lerp(a1, a2, a3)}, + {"Lerp", (Vector a1, Vector a2, float a3) => new Vector(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3))}, {"Lerp", (Vector3 a1, Vector3 a2, float a3) => Vector3.Lerp(a1, a2, a3)}, + {"Lerp", (Vector3D a1, Vector3D a2, float a3) => new Vector3D(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3), Lerp(a1.Z, a2.Z, a3))}, {"Lerp", (Vector4 a1, Vector4 a2, float a3) => Vector4.Lerp(a1, a2, a3)}, @@ -173,24 +213,31 @@ namespace Avalonia.Rendering.Composition.Expressions {"Max", (float a1, float a2) => Math.Max(a1, a2)}, {"Max", (Vector2 a1, Vector2 a2) => Vector2.Max(a1, a2)}, + {"Max", (Vector a1, Vector a2) => Vector.Max(a1, a2)}, {"Max", (Vector3 a1, Vector3 a2) => Vector3.Max(a1, a2)}, + {"Max", (Vector3D a1, Vector3D a2) => Vector3D.Max(a1, a2)}, {"Max", (Vector4 a1, Vector4 a2) => Vector4.Max(a1, a2)}, {"Min", (float a1, float a2) => Math.Min(a1, a2)}, {"Min", (Vector2 a1, Vector2 a2) => Vector2.Min(a1, a2)}, + {"Min", (Vector a1, Vector a2) => Vector.Min(a1, a2)}, {"Min", (Vector3 a1, Vector3 a2) => Vector3.Min(a1, a2)}, + {"Min", (Vector3D a1, Vector3D a2) => Vector3D.Min(a1, a2)}, {"Min", (Vector4 a1, Vector4 a2) => Vector4.Min(a1, a2)}, {"Mod", (float a, float b) => a % b}, {"Normalize", (Quaternion a) => Quaternion.Normalize(a)}, {"Normalize", (Vector2 a) => Vector2.Normalize(a)}, + {"Normalize", (Vector a) => Vector.Normalize(a)}, {"Normalize", (Vector3 a) => Vector3.Normalize(a)}, + {"Normalize", (Vector3D a) => Vector3D.Normalize(a)}, {"Normalize", (Vector4 a) => Vector4.Normalize(a)}, {"Pow", (float a, float b) => (float) Math.Pow(a, b)}, {"Quaternion.CreateFromAxisAngle", (Vector3 a, float b) => Quaternion.CreateFromAxisAngle(a, b)}, + {"Quaternion.CreateFromAxisAngle", (Vector3D a, float b) => Quaternion.CreateFromAxisAngle(a.ToVector3(), b)}, {"Quaternion", (float a, float b, float c, float d) => new Quaternion(a, b, c, d)}, {"Round", (float a) => (float) Math.Round(a)}, @@ -198,14 +245,18 @@ namespace Avalonia.Rendering.Composition.Expressions {"Scale", (Matrix3x2 a, float b) => a * b}, {"Scale", (Matrix4x4 a, float b) => a * b}, {"Scale", (Vector2 a, float b) => a * b}, + {"Scale", (Vector a, float b) => a * b}, {"Scale", (Vector3 a, float b) => a * b}, + {"Scale", (Vector3D a, float b) => Vector3D.Multiply(a, b)}, {"Scale", (Vector4 a, float b) => a * b}, {"Sin", (float a) => (float) Math.Sin(a)}, {"SmoothStep", (float a1, float a2, float a3) => SmoothStep(a1, a2, a3)}, {"SmoothStep", (Vector2 a1, Vector2 a2, Vector2 a3) => SmoothStep(a1, a2, a3)}, + {"SmoothStep", (Vector a1, Vector a2, Vector a3) => SmoothStep(a1, a2, a3)}, {"SmoothStep", (Vector3 a1, Vector3 a2, Vector3 a3) => SmoothStep(a1, a2, a3)}, + {"SmoothStep", (Vector3D a1, Vector3D a2, Vector3D a3) => SmoothStep(a1, a2, a3)}, {"SmoothStep", (Vector4 a1, Vector4 a2, Vector4 a3) => SmoothStep(a1, a2, a3)}, // I have no idea how to do a spherical interpolation for a scalar value, so we are doing a linear one @@ -222,9 +273,13 @@ namespace Avalonia.Rendering.Composition.Expressions {"Transform", (Vector2 a, Matrix3x2 b) => Vector2.Transform(a, b)}, {"Transform", (Vector3 a, Matrix4x4 b) => Vector3.Transform(a, b)}, - {"Vector2", (float a, float b) => new Vector2(a, b)}, - {"Vector3", (float a, float b, float c) => new Vector3(a, b, c)}, + {"Vector2", (float a, float b) => new Vector(a, b)}, + {"Vector2", (double a, double b) => new Vector(a, b)}, + {"Vector3", (float a, float b, float c) => new Vector3D(a, b, c)}, + {"Vector3", (double a, double b, double c) => new Vector3D(a, b, c)}, {"Vector3", (Vector2 v2, float z) => new Vector3(v2, z)}, + {"Vector3", (Vector v2, float z) => new Vector3D(v2.X, v2.Y, z)}, + {"Vector3", (Vector v2, double z) => new Vector3D(v2.X, v2.Y, z)}, {"Vector4", (float a, float b, float c, float d) => new Vector4(a, b, c, d)}, {"Vector4", (Vector2 v2, float z, float w) => new Vector4(v2, z, w)}, {"Vector4", (Vector3 v3, float w) => new Vector4(v3, w)}, diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs index c15487065c..ad8d370478 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs @@ -49,6 +49,46 @@ namespace Avalonia.Rendering.Composition.Expressions } } + return CallWithCast(countGroup, arguments, out result, false); + } + + bool CallWithCast(List countGroup, IReadOnlyList arguments, out ExpressionVariant result, bool anyCast) + { + result = default; + foreach (var record in countGroup) + { + var match = true; + for (var c = 0; c < arguments.Count; c++) + { + var parameter = record.Types[c]; + var arg = arguments[c].Type; + if (parameter != arg) + { + var canCast = (parameter == VariantType.Double && arg == VariantType.Scalar) + || (parameter == VariantType.Vector3D && arg == VariantType.Vector3) + || (parameter == VariantType.Vector && arg == VariantType.Vector2) + || (anyCast && ( + (arg == VariantType.Double && parameter == VariantType.Scalar) + || (arg == VariantType.Vector3D && parameter == VariantType.Vector3) + || (arg == VariantType.Vector && parameter == VariantType.Vector2) + )); + if (!canCast) + { + match = false; + break; + } + } + } + + if (match) + { + result = record.Delegate(arguments); + return true; + } + } + + if (anyCast == false) + return CallWithCast(countGroup, arguments, out result, true); return false; } @@ -75,8 +115,11 @@ namespace Avalonia.Rendering.Composition.Expressions { [typeof(bool)] = VariantType.Boolean, [typeof(float)] = VariantType.Scalar, + [typeof(double)] = VariantType.Double, [typeof(Vector2)] = VariantType.Vector2, + [typeof(Vector)] = VariantType.Vector, [typeof(Vector3)] = VariantType.Vector3, + [typeof(Vector3D)] = VariantType.Vector3D, [typeof(Vector4)] = VariantType.Vector4, [typeof(Matrix3x2)] = VariantType.Matrix3x2, [typeof(Matrix4x4)] = VariantType.Matrix4x4, diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs index 21f14283b5..a97b959fe1 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs @@ -17,6 +17,8 @@ namespace Avalonia.Rendering.Composition.Expressions Vector2, Vector3, Vector4, + Vector, + Vector3D, AvaloniaMatrix, Matrix3x2, Matrix4x4, @@ -38,6 +40,8 @@ namespace Avalonia.Rendering.Composition.Expressions [FieldOffset(4)] public Vector2 Vector2; [FieldOffset(4)] public Vector3 Vector3; [FieldOffset(4)] public Vector4 Vector4; + [FieldOffset(4)] public Vector Vector; + [FieldOffset(4)] public Vector3D Vector3D; [FieldOffset(4)] public Matrix AvaloniaMatrix; [FieldOffset(4)] public Matrix3x2 Matrix3x2; [FieldOffset(4)] public Matrix4x4 Matrix4x4; @@ -55,6 +59,15 @@ namespace Avalonia.Rendering.Composition.Expressions return Vector2.Y; return default; } + + if (Type == VariantType.Vector) + { + if (ReferenceEquals(property, "X")) + return Vector.X; + if (ReferenceEquals(property, "Y")) + return Vector.Y; + return default; + } if (Type == VariantType.Vector3) { @@ -78,6 +91,29 @@ namespace Avalonia.Rendering.Composition.Expressions return new Vector2(Vector3.Z, Vector3.Y); return default; } + + if (Type == VariantType.Vector3D) + { + if (ReferenceEquals(property, "X")) + return Vector3D.X; + if (ReferenceEquals(property, "Y")) + return Vector3D.Y; + if (ReferenceEquals(property, "Z")) + return Vector3D.Z; + if(ReferenceEquals(property, "XY")) + return new Vector(Vector3D.X, Vector3D.Y); + if(ReferenceEquals(property, "YX")) + return new Vector(Vector3D.Y, Vector3D.X); + if(ReferenceEquals(property, "XZ")) + return new Vector(Vector3D.X, Vector3D.Z); + if(ReferenceEquals(property, "ZX")) + return new Vector(Vector3D.Z, Vector3D.X); + if(ReferenceEquals(property, "YZ")) + return new Vector(Vector3D.Y, Vector3D.Z); + if(ReferenceEquals(property, "ZY")) + return new Vector(Vector3D.Z, Vector3D.Y); + return default; + } if (Type == VariantType.Vector4) { @@ -115,14 +151,20 @@ namespace Avalonia.Rendering.Composition.Expressions return AvaloniaMatrix.M11; if (ReferenceEquals(property, "M12")) return AvaloniaMatrix.M12; + if (ReferenceEquals(property, "M13")) + return AvaloniaMatrix.M13; if (ReferenceEquals(property, "M21")) return AvaloniaMatrix.M21; if (ReferenceEquals(property, "M22")) return AvaloniaMatrix.M22; + if (ReferenceEquals(property, "M23")) + return AvaloniaMatrix.M23; if (ReferenceEquals(property, "M31")) return AvaloniaMatrix.M31; if (ReferenceEquals(property, "M32")) return AvaloniaMatrix.M32; + if (ReferenceEquals(property, "M33")) + return AvaloniaMatrix.M33; return default; } @@ -220,7 +262,13 @@ namespace Avalonia.Rendering.Composition.Expressions Type = VariantType.Vector2, Vector2 = value }; - + + public static implicit operator ExpressionVariant(Vector value) => + new ExpressionVariant + { + Type = VariantType.Vector, + Vector = value + }; public static implicit operator ExpressionVariant(Vector3 value) => new ExpressionVariant @@ -228,6 +276,13 @@ namespace Avalonia.Rendering.Composition.Expressions Type = VariantType.Vector3, Vector3 = value }; + + public static implicit operator ExpressionVariant(Vector3D value) => + new ExpressionVariant + { + Type = VariantType.Vector3D, + Vector3D = value + }; public static implicit operator ExpressionVariant(Vector4 value) => @@ -285,10 +340,16 @@ namespace Avalonia.Rendering.Composition.Expressions if (left.Type == VariantType.Vector2) return left.Vector2 + right.Vector2; + + if (left.Type == VariantType.Vector) + return left.Vector + right.Vector; if (left.Type == VariantType.Vector3) return left.Vector3 + right.Vector3; + if (left.Type == VariantType.Vector3D) + return Avalonia.Vector3D.Add(left.Vector3D, right.Vector3D); + if (left.Type == VariantType.Vector4) return left.Vector4 + right.Vector4; @@ -317,10 +378,16 @@ namespace Avalonia.Rendering.Composition.Expressions if (left.Type == VariantType.Vector2) return left.Vector2 - right.Vector2; + + if (left.Type == VariantType.Vector) + return left.Vector - right.Vector; if (left.Type == VariantType.Vector3) return left.Vector3 - right.Vector3; + if (left.Type == VariantType.Vector3D) + return Vector3D.Add(left.Vector3D, -right.Vector3D); + if (left.Type == VariantType.Vector4) return left.Vector4 - right.Vector4; @@ -347,9 +414,15 @@ namespace Avalonia.Rendering.Composition.Expressions if (left.Type == VariantType.Vector2) return -left.Vector2; + + if (left.Type == VariantType.Vector) + return -left.Vector; if (left.Type == VariantType.Vector3) return -left.Vector3; + + if (left.Type == VariantType.Vector3D) + return -left.Vector3D; if (left.Type == VariantType.Vector4) return -left.Vector4; @@ -383,14 +456,29 @@ namespace Avalonia.Rendering.Composition.Expressions if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2) return left.Vector2 * right.Vector2; + if (left.Type == VariantType.Vector && right.Type == VariantType.Vector) + return Vector.Multiply(left.Vector, right.Vector); + if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar) return left.Vector2 * right.Scalar; + + if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar) + return left.Vector * right.Scalar; + + if (left.Type == VariantType.Vector && right.Type == VariantType.Double) + return left.Vector * right.Double; if (left.Type == VariantType.Vector3 && right.Type == VariantType.Vector3) return left.Vector3 * right.Vector3; + + if (left.Type == VariantType.Vector3D && right.Type == VariantType.Vector3D) + return Vector3D.Multiply(left.Vector3D, right.Vector3D); if (left.Type == VariantType.Vector3 && right.Type == VariantType.Scalar) return left.Vector3 * right.Scalar; + + if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar) + return Vector3D.Multiply(left.Vector3D, right.Scalar); if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4) return left.Vector4 * right.Vector4; @@ -436,15 +524,33 @@ namespace Avalonia.Rendering.Composition.Expressions if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2) return left.Vector2 / right.Vector2; + if (left.Type == VariantType.Vector && right.Type == VariantType.Vector) + return Vector.Divide(left.Vector, right.Vector); + if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar) return left.Vector2 / right.Scalar; + + if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar) + return left.Vector / right.Scalar; + + if (left.Type == VariantType.Vector && right.Type == VariantType.Double) + return left.Vector / right.Scalar; if (left.Type == VariantType.Vector3 && right.Type == VariantType.Vector3) return left.Vector3 / right.Vector3; + if (left.Type == VariantType.Vector3D && right.Type == VariantType.Vector3D) + return Vector3D.Divide(left.Vector3D, right.Vector3D); + if (left.Type == VariantType.Vector3 && right.Type == VariantType.Scalar) return left.Vector3 / right.Scalar; + if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar) + return Avalonia.Vector3D.Divide(left.Vector3D, right.Scalar); + + if (left.Type == VariantType.Vector3D && right.Type == VariantType.Double) + return Avalonia.Vector3D.Divide(left.Vector3D, right.Double); + if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4) return left.Vector4 / right.Vector4; @@ -471,9 +577,15 @@ namespace Avalonia.Rendering.Composition.Expressions if (Type == VariantType.Vector2) return Vector2 == right.Vector2; - + + if (Type == VariantType.Vector) + return Vector == right.Vector; + if (Type == VariantType.Vector3) return Vector3 == right.Vector3; + + if (Type == VariantType.Vector3D) + return Vector3D == right.Vector3D; if (Type == VariantType.Vector4) return Vector4 == right.Vector4; @@ -571,6 +683,11 @@ namespace Avalonia.Rendering.Composition.Expressions res = (T) (object) Scalar; return true; } + if (Type == VariantType.Double) + { + res = (T)(object)Scalar; + return true; + } } if (typeof(T) == typeof(double)) @@ -580,6 +697,12 @@ namespace Avalonia.Rendering.Composition.Expressions res = (T) (object) Double; return true; } + + if (Type == VariantType.Scalar) + { + res = (T)(object)(float)Double; + return true; + } } if (typeof(T) == typeof(Vector2)) @@ -589,6 +712,27 @@ namespace Avalonia.Rendering.Composition.Expressions res = (T) (object) Vector2; return true; } + + if (Type == VariantType.Vector) + { + res = (T) (object) Vector.ToVector2(); + return true; + } + } + + if (typeof(T) == typeof(Vector)) + { + if (Type == VariantType.Vector) + { + res = (T) (object) Vector; + return true; + } + + if (Type == VariantType.Vector2) + { + res = (T)(object)new Vector(Vector2); + return true; + } } if (typeof(T) == typeof(Vector3)) @@ -598,6 +742,26 @@ namespace Avalonia.Rendering.Composition.Expressions res = (T) (object) Vector3; return true; } + if (Type == VariantType.Vector3D) + { + res = (T) (object) Vector3D.ToVector3(); + return true; + } + } + + if (typeof(T) == typeof(Vector3D)) + { + if (Type == VariantType.Vector3D) + { + res = (T) (object) Vector3D; + return true; + } + + if (Type == VariantType.Vector3) + { + res = (T)(object)new Vector3D(Vector3); + return true; + } } if (typeof(T) == typeof(Vector4)) @@ -668,9 +832,15 @@ namespace Avalonia.Rendering.Composition.Expressions if (typeof(T) == typeof(Vector2)) return (Vector2) (object) v; + + if (typeof(T) == typeof(Vector)) + return (Vector) (object) v; if (typeof(T) == typeof(Vector3)) return (Vector3) (object) v; + + if (typeof(T) == typeof(Vector3D)) + return (Vector3D) (object) v; if (typeof(T) == typeof(Vector4)) return (Vector4) (object) v; @@ -709,8 +879,12 @@ namespace Avalonia.Rendering.Composition.Expressions return Double.ToString(CultureInfo.InvariantCulture); if (Type == VariantType.Vector2) return Vector2.ToString(); + if (Type == VariantType.Vector) + return Vector.ToString(); if (Type == VariantType.Vector3) return Vector3.ToString(); + if (Type == VariantType.Vector3D) + return Vector3D.ToString(); if (Type == VariantType.Vector4) return Vector4.ToString(); if (Type == VariantType.Quaternion) diff --git a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs index 3d8d5bae78..714895cd71 100644 --- a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs +++ b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs @@ -6,41 +6,46 @@ namespace Avalonia.Rendering.Composition { static class MatrixUtils { - public static Matrix4x4 ComputeTransform(Vector2 size, Vector2 anchorPoint, Vector3 centerPoint, - Matrix4x4 transformMatrix, Vector3 scale, float rotationAngle, Quaternion orientation, Vector3 offset) + public static Matrix ComputeTransform(Vector size, Vector anchorPoint, Vector3D centerPoint, + Matrix transformMatrix, Vector3D scale, float rotationAngle, Quaternion orientation, Vector3D offset) { // The math here follows the *observed* UWP behavior since there are no docs on how it's supposed to work - - var anchor = size * anchorPoint; - var mat = Matrix4x4.CreateTranslation(-anchor.X, -anchor.Y, 0); - var center = new Vector3(centerPoint.X, centerPoint.Y, centerPoint.Z); + var anchor = Vector.Multiply(size, anchorPoint); + var mat = Matrix.CreateTranslation(-anchor.X, -anchor.Y); + + var center = new Vector3D(centerPoint.X, centerPoint.Y, centerPoint.Z); if (!transformMatrix.IsIdentity) mat = transformMatrix * mat; - if (scale != new Vector3(1, 1, 1)) - mat *= Matrix4x4.CreateScale(scale, center); + if (scale != new Vector3D(1, 1, 1)) + mat *= ToMatrix(Matrix4x4.CreateScale(scale.ToVector3(), center.ToVector3())); //TODO: RotationAxis support if (rotationAngle != 0) - mat *= Matrix4x4.CreateRotationZ(rotationAngle, center); + mat *= ToMatrix(Matrix4x4.CreateRotationZ(rotationAngle, center.ToVector3())); if (orientation != Quaternion.Identity) { if (centerPoint != default) { - mat *= Matrix4x4.CreateTranslation(-center) - * Matrix4x4.CreateFromQuaternion(orientation) - * Matrix4x4.CreateTranslation(center); + mat *= ToMatrix(Matrix4x4.CreateTranslation(-center.ToVector3()) + * Matrix4x4.CreateFromQuaternion(orientation) + * Matrix4x4.CreateTranslation(center.ToVector3())); } else - mat *= Matrix4x4.CreateFromQuaternion(orientation); + mat *= ToMatrix(Matrix4x4.CreateFromQuaternion(orientation)); } if (offset != default) - mat *= Matrix4x4.CreateTranslation(offset); + { + if (offset.Z == 0) + mat *= Matrix.CreateTranslation(offset.X, offset.Y); + else + mat *= ToMatrix(Matrix4x4.CreateTranslation(offset.ToVector3())); + } return mat; } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs index b9e6833d21..c63e7917b3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs @@ -81,10 +81,9 @@ namespace Avalonia.Rendering.Composition.Server // If we only have translation and scale, just scale the padding if (CombinedTransformMatrix is { - M12: 0, M13: 0, M14: 0, - M21: 0, M23: 0, M24: 0, - M31: 0, M32: 0, M34: 0, - M43: 0, M44: 1 + M12: 0, M13: 0, + M21: 0, M23: 0, + M31: 0, M32: 0 }) padding = new Thickness(padding.Left * CombinedTransformMatrix.M11, padding.Top * CombinedTransformMatrix.M22, diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 0eeffda123..3ff1bf2b98 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -51,7 +51,7 @@ namespace Avalonia.Rendering.Composition.Server canvas.PushClip(AdornedVisual._combinedTransformedClipBounds); } var transform = GlobalTransformMatrix; - canvas.PostTransform = MatrixUtils.ToMatrix(transform); + canvas.PostTransform = transform; canvas.Transform = Matrix.Identity; if (Effect != null) @@ -71,7 +71,7 @@ namespace Avalonia.Rendering.Composition.Server RenderCore(canvas, currentTransformedClip); // Hack to force invalidation of SKMatrix - canvas.PostTransform = MatrixUtils.ToMatrix(transform); + canvas.PostTransform = transform; canvas.Transform = Matrix.Identity; if (OpacityMaskBrush != null) @@ -106,8 +106,8 @@ namespace Avalonia.Rendering.Composition.Server return ref _readback2; } - public Matrix4x4 CombinedTransformMatrix { get; private set; } = Matrix4x4.Identity; - public Matrix4x4 GlobalTransformMatrix { get; private set; } + public Matrix CombinedTransformMatrix { get; private set; } = Matrix.Identity; + public Matrix GlobalTransformMatrix { get; private set; } public record struct UpdateResult(Rect? Bounds, bool InvalidatedOld, bool InvalidatedNew) { @@ -134,12 +134,12 @@ namespace Avalonia.Rendering.Composition.Server { CombinedTransformMatrix = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, // HACK: Ignore RenderTransform set by the adorner layer - AdornedVisual != null ? Matrix4x4.Identity : TransformMatrix, + AdornedVisual != null ? Matrix.Identity : TransformMatrix, Scale, RotationAngle, Orientation, Offset); _combinedTransformDirty = false; } - var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix4x4.Identity; + var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix.Identity; var newTransform = CombinedTransformMatrix * parentTransform; @@ -148,7 +148,7 @@ namespace Avalonia.Rendering.Composition.Server if (GlobalTransformMatrix != newTransform) { _isBackface = Vector3.Transform( - new Vector3(0, 0, float.PositiveInfinity), GlobalTransformMatrix).Z <= 0; + new Vector3(0, 0, float.PositiveInfinity), MatrixUtils.ToMatrix4x4(GlobalTransformMatrix)).Z <= 0; positionChanged = true; } @@ -179,14 +179,14 @@ namespace Avalonia.Rendering.Composition.Server TransformedOwnContentBounds = default; else TransformedOwnContentBounds = - ownBounds.TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix)); + ownBounds.TransformToAABB(GlobalTransformMatrix); } if (_clipSizeDirty || positionChanged) { _transformedClipBounds = ClipToBounds ? new Rect(new Size(Size.X, Size.Y)) - .TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix)) + .TransformToAABB(GlobalTransformMatrix) : null; _clipSizeDirty = false; @@ -249,7 +249,7 @@ namespace Avalonia.Rendering.Composition.Server /// public struct ReadbackData { - public Matrix4x4 Matrix; + public Matrix Matrix; public ulong Revision; public long TargetId; public bool Visible; diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs index 6d6818256a..f4f1f3e8d9 100644 --- a/src/Avalonia.Base/Rendering/Composition/Visual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs @@ -33,7 +33,7 @@ namespace Avalonia.Rendering.Composition } } - internal Matrix4x4? TryGetServerGlobalTransform() + internal Matrix? TryGetServerGlobalTransform() { if (Root == null) return null; diff --git a/src/Avalonia.Base/Vector.cs b/src/Avalonia.Base/Vector.cs index 166ae6b93b..15722901a6 100644 --- a/src/Avalonia.Base/Vector.cs +++ b/src/Avalonia.Base/Vector.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Numerics; #if !BUILDTASK using Avalonia.Animation.Animators; #endif @@ -353,5 +354,52 @@ namespace Avalonia x = this._x; y = this._y; } + + internal Vector2 ToVector2() => new Vector2((float)X, (float)Y); + + internal Vector(Vector2 v) : this(v.X, v.Y) + { + + } + + /// + /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. + /// + /// + public Vector Abs() => new(Math.Abs(X), Math.Abs(Y)); + + /// + /// Restricts a vector between a minimum and a maximum value. + /// + public static Vector Clamp(Vector value, Vector min, Vector max) => + Min(Max(value, min), max); + + /// + /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors + /// + public static Vector Max(Vector left, Vector right) => + new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y)); + + /// + /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors + /// + public static Vector Min(Vector left, Vector right) => + new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y)); + + /// + /// Computes the Euclidean distance between the two given points. + /// + public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2)); + + /// + /// Returns the Euclidean distance squared between two specified points + /// + public static double DistanceSquared(Vector value1, Vector value2) + { + var difference = value1 - value2; + return Dot(difference, difference); + } + + public static implicit operator Vector(Vector2 v) => new(v); } } diff --git a/src/Avalonia.Base/Vector3D.cs b/src/Avalonia.Base/Vector3D.cs new file mode 100644 index 0000000000..ec6463c987 --- /dev/null +++ b/src/Avalonia.Base/Vector3D.cs @@ -0,0 +1,145 @@ +using System; +using System.Globalization; +using System.Numerics; +using Avalonia.Rendering.Composition.Expressions; +using Avalonia.Utilities; + +namespace Avalonia; + +public readonly record struct Vector3D(double X, double Y, double Z) +{ + /// + /// Parses a string. + /// + /// The string. + /// The . + public static Vector3D Parse(string s) + { + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Vector.")) + { + return new Vector3D( + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble() + ); + } + } + + internal Vector3 ToVector3() => new Vector3((float)X, (float)Y, (float)Z); + + internal Vector3D(Vector3 v) : this(v.X, v.Y, v.Z) + { + + } + + public static implicit operator Vector3D(Vector3 vector) => new(vector); + + /// + /// Calculates the dot product of two vectors. + /// + public static double Dot(Vector3D vector1, Vector3D vector2) => + (vector1.X * vector2.X) + + (vector1.Y * vector2.Y) + + (vector1.Z * vector2.Z); + + /// + /// Adds the second to the first vector + /// + public static Vector3D Add(Vector3D left, Vector3D right) => + new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + + /// + /// Adds the second to the first vector + /// + public static Vector3D operator +(Vector3D left, Vector3D right) => Add(left, right); + + /// + /// Subtracts the second from the first vector + /// + public static Vector3D Substract(Vector3D left, Vector3D right) => + new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + + /// + /// Subtracts the second from the first vector + /// + public static Vector3D operator -(Vector3D left, Vector3D right) => Substract(left, right); + + /// + /// Negates the vector + /// + public static Vector3D operator -(Vector3D v) => new(-v.X, -v.Y, -v.Z); + + /// + /// Multiplies the first vector by the second. + /// + public static Vector3D Multiply(Vector3D left, Vector3D right) => + new(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + + /// + /// Multiplies the vector by the given scalar. + /// + public static Vector3D Multiply(Vector3D left, double right) => + new(left.X * right, left.Y * right, left.Z * right); + + /// + /// Multiplies the vector by the given scalar. + /// + public static Vector3D operator *(Vector3D left, double right) => Multiply(left, right); + + /// + /// Divides the first vector by the second. + /// + public static Vector3D Divide(Vector3D left, Vector3D right) => + new(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + + /// + /// Divides the vector by the given scalar. + /// + public static Vector3D Divide(Vector3D left, double right) => + new(left.X / right, left.Y / right, left.Z / right); + + /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. + public Vector3D Abs() => new(Math.Abs(X), Math.Abs(Y), Math.Abs(Z)); + + /// + /// Restricts a vector between a minimum and a maximum value. + /// + public static Vector3D Clamp(Vector3D value, Vector3D min, Vector3D max) => + Min(Max(value, min), max); + + /// + /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors + /// + public static Vector3D Max(Vector3D left, Vector3D right) => + new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y), Math.Max(left.Z, right.Z)); + + /// + /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors + /// + public static Vector3D Min(Vector3D left, Vector3D right) => + new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y), Math.Min(left.Z, right.Z)); + + /// + /// Length of the vector. + /// + public double Length => Math.Sqrt(Dot(this, this)); + + /// + /// Returns a normalized version of this vector. + /// + public static Vector3D Normalize(Vector3D value) => Divide(value, value.Length); + + /// + /// Computes the squared Euclidean distance between the two given points. + /// + public static double DistanceSquared(Vector3D value1, Vector3D value2) + { + var difference = Vector3D.Substract(value1, value2); + return Dot(difference, difference); + } + + /// + /// Computes the Euclidean distance between the two given points. + /// + public static double Distance(Vector3D value1, Vector3D value2) => Math.Sqrt(DistanceSquared(value1, value2)); +} \ No newline at end of file diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index 97b2686584..05aea3808e 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -21,14 +21,14 @@ - - - - + + + + - - + + @@ -57,10 +57,13 @@ + + + diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs index 39ff8e3a92..406b4443a6 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs +++ b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs @@ -223,7 +223,7 @@ namespace Avalonia.Controls var visualizerVisual = ElementComposition.GetElementVisual(this); if (visual != null && contentVisual != null && visualizerVisual != null) { - contentVisual.CenterPoint = new Vector3((float)(_content.Bounds.Width / 2), (float)(_content.Bounds.Height / 2), 0); + contentVisual.CenterPoint = new Vector3D((_content.Bounds.Width / 2), (_content.Bounds.Height / 2), 0); switch (RefreshVisualizerState) { case RefreshVisualizerState.Idle: @@ -236,43 +236,43 @@ namespace Avalonia.Controls contentVisual.Opacity = MinimumIndicatorOpacity; contentVisual.RotationAngle = _startingRotationAngle; visualizerVisual.Offset = IsPullDirectionVertical ? - new Vector3(visualizerVisual.Offset.X, 0, 0) : - new Vector3(0, visualizerVisual.Offset.Y, 0); + new Vector3D(visualizerVisual.Offset.X, 0, 0) : + new Vector3D(0, visualizerVisual.Offset.Y, 0); _content.InvalidateMeasure(); break; case RefreshVisualizerState.Interacting: _played = false; contentVisual.Opacity = MinimumIndicatorOpacity; contentVisual.RotationAngle = (float)(_startingRotationAngle + _interactionRatio * 2 * Math.PI); - Vector3 offset = default; + Vector3D offset = default; if (IsPullDirectionVertical) { - offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + offset = new Vector3D(0, (_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); } else { - offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + offset = new Vector3D((_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); } visual.Offset = offset; visualizerVisual.Offset = IsPullDirectionVertical ? - new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : - new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3D(offset.X, visualizerVisual.Offset.Y, 0); break; case RefreshVisualizerState.Pending: contentVisual.Opacity = 1; contentVisual.RotationAngle = _startingRotationAngle + (float)(2 * Math.PI); if (IsPullDirectionVertical) { - offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + offset = new Vector3D(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); } else { - offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + offset = new Vector3D((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); } visual.Offset = offset; visualizerVisual.Offset = IsPullDirectionVertical ? - new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : - new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3D(offset.X, visualizerVisual.Offset.Y, 0); if (!_played) { @@ -301,18 +301,18 @@ namespace Avalonia.Controls * (IsPullDirectionFar ? -1f : 1f); if (IsPullDirectionVertical) { - offset = new Vector3(0, (float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + offset = new Vector3D(0, (_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); } else { - offset = new Vector3((float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + offset = new Vector3D((_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); } visual.Offset = offset; - contentVisual.Offset += IsPullDirectionVertical ? new Vector3(0, (float)(translationRatio * root.Bounds.Height), 0) : - new Vector3((float)(translationRatio * root.Bounds.Width), 0, 0); + contentVisual.Offset += IsPullDirectionVertical ? new Vector3D(0, (translationRatio * root.Bounds.Height), 0) : + new Vector3D((translationRatio * root.Bounds.Width), 0, 0); visualizerVisual.Offset = IsPullDirectionVertical ? - new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : - new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3D(offset.X, visualizerVisual.Offset.Y, 0); break; case RefreshVisualizerState.Peeking: contentVisual.Opacity = 1; diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index 83bf795b03..cefc2a4c06 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs @@ -105,7 +105,7 @@ namespace Avalonia.OpenGL.Controls } _visual = _compositor.CreateSurfaceVisual(); - _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height); + _visual.Size = new Vector(Bounds.Width, Bounds.Height); _visual.Surface = _resources.Surface; ElementComposition.SetElementChildVisual(this, _visual); using (_resources.Context.MakeCurrent()) @@ -118,7 +118,7 @@ namespace Avalonia.OpenGL.Controls { if (_visual != null && change.Property == BoundsProperty) { - _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height); + _visual.Size = new Vector(Bounds.Width, Bounds.Height); RequestNextFrameRendering(); } From e7ea247c01cce61b60caadfff378ee2dd7311c3c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 13 Jun 2023 22:21:18 +0600 Subject: [PATCH 126/391] Remove some casts for RefreshVisualizer --- src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs index 406b4443a6..c49043ea46 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs +++ b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs @@ -263,11 +263,11 @@ namespace Avalonia.Controls contentVisual.RotationAngle = _startingRotationAngle + (float)(2 * Math.PI); if (IsPullDirectionVertical) { - offset = new Vector3D(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + offset = new Vector3D(0, (_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); } else { - offset = new Vector3D((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + offset = new Vector3D((_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); } visual.Offset = offset; visualizerVisual.Offset = IsPullDirectionVertical ? From d407764b84d4d71a2eb3e53c6cfe2ee0500e461c Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 13 Jun 2023 21:21:30 -0400 Subject: [PATCH 127/391] Remove some duplicate periods --- src/Avalonia.Base/AvaloniaObjectExtensions.cs | 2 +- src/Avalonia.Base/Platform/IPlatformRenderInterface.cs | 2 +- src/Avalonia.Base/Utilities/TypeUtilities.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 0c22213d33..af64e5646f 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -334,7 +334,7 @@ namespace Avalonia /// . /// /// The type of the property change sender. - /// /// The type of the property.. + /// /// The type of the property. /// The property changed observable. /// /// The method to call. The parameters are the sender and the event args. diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 6f62c3be1d..57fedb3d69 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -18,7 +18,7 @@ namespace Avalonia.Platform /// Creates an ellipse geometry implementation. /// /// The bounds of the ellipse. - /// An ellipse geometry.. + /// An ellipse geometry. IGeometryImpl CreateEllipseGeometry(Rect rect); /// diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 3a82bf02e0..7dbb0872f5 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -306,7 +306,7 @@ namespace Avalonia.Utilities /// if the value could not be converted. /// /// The value to convert. - /// The type to convert to.. + /// The type to convert to. /// The culture to use. /// A value of . [RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)] From 7548238388fce993e66b27d3542a4130875484e4 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 13 Jun 2023 21:21:56 -0400 Subject: [PATCH 128/391] Fix EllipseGeometry clone method --- src/Avalonia.Base/Media/EllipseGeometry.cs | 41 +++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs index 8211855324..84d74e888e 100644 --- a/src/Avalonia.Base/Media/EllipseGeometry.cs +++ b/src/Avalonia.Base/Media/EllipseGeometry.cs @@ -56,6 +56,10 @@ namespace Avalonia.Media /// /// Gets or sets a rect that defines the bounds of the ellipse. /// + /// + /// When set, this takes priority over the other properties that define an + /// ellipse using a center point and X/Y-axis radii. + /// public Rect Rect { get => GetValue(RectProperty); @@ -65,6 +69,10 @@ namespace Avalonia.Media /// /// Gets or sets a double that defines the radius in the X-axis of the ellipse. /// + /// + /// In order for this property to be used, must not be set + /// (equal to the default value). + /// public double RadiusX { get => GetValue(RadiusXProperty); @@ -74,6 +82,10 @@ namespace Avalonia.Media /// /// Gets or sets a double that defines the radius in the Y-axis of the ellipse. /// + /// + /// In order for this property to be used, must not be set + /// (equal to the default value). + /// public double RadiusY { get => GetValue(RadiusYProperty); @@ -83,6 +95,10 @@ namespace Avalonia.Media /// /// Gets or sets a point that defines the center of the ellipse. /// + /// + /// In order for this property to be used, must not be set + /// (equal to the default value). + /// public Point Center { get => GetValue(CenterProperty); @@ -92,7 +108,30 @@ namespace Avalonia.Media /// public override Geometry Clone() { - return new EllipseGeometry(Rect); + // Note that the ellipse properties are used in two modes: + // + // 1. Rect-only Mode: + // Directly set the rectangle bounds the ellipse will fill + // + // 2. Center + Radii Mode: + // Set a center-point and then X/Y-axis radii that are used to + // calculate the rectangle bounds the ellipse will fill. + // This is the only mode supported by WPF. + // + // Rendering the ellipse will only ever use one of these two modes + // based on if the Rect property is set (not equal to default). + // + // This means it would normally be fine to copy ONLY the Rect property + // when it is set. However, while it would render the same, it isn't + // a true clone. We want to include all the properties here regardless + // of the rendering mode that will eventually be used. + return new EllipseGeometry() + { + Rect = Rect, + RadiusX = RadiusX, + RadiusY = RadiusY, + Center = Center, + }; } /// From 36ea4a69c836a07b74df6970df656cdf67330385 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 13 Jun 2023 21:43:02 -0400 Subject: [PATCH 129/391] Comment syntax fixes --- src/Avalonia.Base/AvaloniaObjectExtensions.cs | 2 +- src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs | 2 +- .../Primitives/PopupPositioning/IPopupPositioner.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index af64e5646f..b3f41eb420 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -334,7 +334,7 @@ namespace Avalonia /// . /// /// The type of the property change sender. - /// /// The type of the property. + /// The type of the property. /// The property changed observable. /// /// The method to call. The parameters are the sender and the event args. diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs index 3406432ce7..f418d4e14a 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs @@ -687,7 +687,7 @@ namespace Avalonia.Media.TextFormatting.Unicode /// /// This method resolves the sos and eos values for the run /// and adds the run to the list - /// /// + /// /// The index of the start of the run (in x9 removed units) /// The length of the run (in x9 removed units) /// The level of the run diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 0c9bb89caa..4029782772 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -216,7 +216,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// /// If the adjusted position also ends up being constrained, the resulting position of the /// FlipX adjustment will be the one before the adjustment. - /// /// + /// FlipX = 4, /// From dd764e8c438c38c561200a9dee97c74c00add1a6 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 14 Jun 2023 06:19:31 +0200 Subject: [PATCH 130/391] Prevent infinite loop if the default FontFamily lookup fails --- src/Avalonia.Base/Media/FontManager.cs | 7 ++++++- .../Media/FontManagerTests.cs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index 4425147098..17d1984286 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -151,8 +151,13 @@ namespace Avalonia.Media } } + if(typeface.FontFamily == DefaultFontFamily) + { + return false; + } + //Nothing was found so use the default - return TryGetGlyphTypeface(new Typeface(DefaultFontFamily, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface); + return TryGetGlyphTypeface(new Typeface(FontFamily.DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface); } /// diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs index 62f1cd2032..f4add27a55 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs @@ -123,5 +123,21 @@ namespace Avalonia.Skia.UnitTests.Media } } } + + [Fact] + public void Should_Return_False_For_Invalid_DefaultFontFamily() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl()))) + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions { DefaultFamilyName = "ABC" }); + + var result = FontManager.Current.TryGetGlyphTypeface(Typeface.Default, out _); + + Assert.False(result); + } + } + } } } From 0ff67a4bb83ca4f19f3d8390d72d00c65d1f3e67 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 14 Jun 2023 07:30:38 +0200 Subject: [PATCH 131/391] Don't use system font lookup for the test --- tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs index f4add27a55..0818510bc2 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs @@ -131,7 +131,7 @@ namespace Avalonia.Skia.UnitTests.Media { using (AvaloniaLocator.EnterScope()) { - AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions { DefaultFamilyName = "ABC" }); + AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions { DefaultFamilyName = "avares://resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Unknown" }); var result = FontManager.Current.TryGetGlyphTypeface(Typeface.Default, out _); From e9f655212fb1d0fd828c9b6ac62e233cdebd4bba Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 14 Jun 2023 10:05:13 +0200 Subject: [PATCH 132/391] Use a blocking collection for SKTextBlob caching --- src/Skia/Avalonia.Skia/GlyphRunImpl.cs | 35 ++++++++++++-------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index f6d84f0b12..fdb9d0b031 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.Collections.Concurrent; using System.Collections.Generic; using Avalonia.Media; using Avalonia.Media.TextFormatting; @@ -14,7 +15,7 @@ namespace Avalonia.Skia private readonly ushort[] _glyphIndices; private readonly SKPoint[] _glyphPositions; - private readonly Dictionary _textBlobCache = new(1); + private readonly ConcurrentDictionary _textBlobCache = new(); public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin) @@ -100,32 +101,28 @@ namespace Avalonia.Skia break; } - if (_textBlobCache.TryGetValue(edging, out var textBlob)) + return _textBlobCache.GetOrAdd(edging, (_) => { - return textBlob; - } - - var font = _glyphTypefaceImpl.SKFont; + var font = _glyphTypefaceImpl.SKFont; - font.Hinting = SKFontHinting.Full; - font.Subpixel = edging == SKFontEdging.SubpixelAntialias; - font.Edging = edging; - font.Size = (float)FontRenderingEmSize; + font.Hinting = SKFontHinting.Full; + font.Subpixel = edging == SKFontEdging.SubpixelAntialias; + font.Edging = edging; + font.Size = (float)FontRenderingEmSize; - var builder = SKTextBlobBuilderCache.Shared.Get(); + var builder = SKTextBlobBuilderCache.Shared.Get(); - var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length); + var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length); - runBuffer.SetPositions(_glyphPositions); - runBuffer.SetGlyphs(_glyphIndices); + runBuffer.SetPositions(_glyphPositions); + runBuffer.SetGlyphs(_glyphIndices); - textBlob = builder.Build(); + var textBlob = builder.Build(); - SKTextBlobBuilderCache.Shared.Return(builder); + SKTextBlobBuilderCache.Shared.Return(builder); - _textBlobCache.Add(edging, textBlob); - - return textBlob; + return textBlob; + }); } public void Dispose() From c6ab46178c5360ae7acb0bbe9f427171baf9d811 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2023 15:35:24 +0200 Subject: [PATCH 133/391] Update ncrunch configuration. --- .ncrunch/MobileSandbox.Browser.v3.ncrunchproject | 5 +++++ .ncrunch/WindowsInteropTest.net461.v3.ncrunchproject | 5 +++++ .ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .ncrunch/MobileSandbox.Browser.v3.ncrunchproject create mode 100644 .ncrunch/WindowsInteropTest.net461.v3.ncrunchproject create mode 100644 .ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject diff --git a/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject b/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject b/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject b/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file From 5fb0cf7ddaf27efc805a997144b502a99e8569fd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2023 15:35:44 +0200 Subject: [PATCH 134/391] Added failing test for #11617. --- .../Selection/SelectionModelTests_Single.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Selection/SelectionModelTests_Single.cs b/tests/Avalonia.Controls.UnitTests/Selection/SelectionModelTests_Single.cs index c163298b40..4b652c68c5 100644 --- a/tests/Avalonia.Controls.UnitTests/Selection/SelectionModelTests_Single.cs +++ b/tests/Avalonia.Controls.UnitTests/Selection/SelectionModelTests_Single.cs @@ -300,6 +300,27 @@ namespace Avalonia.Controls.UnitTests.Selection target.Source = new[] { 1, 2, 3 }; } + + [Fact] + public void Can_Change_Source_In_SelectedItem_Change_Handler() + { + // Issue #11617 + var target = CreateTarget(); + var raised = 0; + + target.PropertyChanged += (s, e) => + { + if (e.PropertyName == nameof(target.SelectedItem) && raised == 0) + { + ++raised; + target.Source = new[] { "foo", "baz", "bar" }; + } + }; + + target.SelectedIndex = 1; + + Assert.Equal(-1, target.SelectedIndex); + } } public class SelectedIndex From 1dc19f16278d22530aa2b82f6f70ef6d2fb0cef6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2023 15:53:53 +0200 Subject: [PATCH 135/391] Checking _operation for null isn't enough. Instead check if `UpdateCount` is not 0 to allow recursive setting of `Source`. I have a vague feeling that this isn't actually the correct solution, and that more issues are going to arise around this scenario but for now I'm unable to create a failing test so let's go with the simple solution for now. Fixes #11617 --- src/Avalonia.Controls/Selection/SelectionModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs index 68bad598d0..69bed2550e 100644 --- a/src/Avalonia.Controls/Selection/SelectionModel.cs +++ b/src/Avalonia.Controls/Selection/SelectionModel.cs @@ -277,7 +277,7 @@ namespace Avalonia.Controls.Selection { if (base.Source != value) { - if (_operation is not null) + if (_operation?.UpdateCount > 0) { throw new InvalidOperationException("Cannot change source while update is in progress."); } From 18e0512d81cb22989bc6adde3b3465ac22eae0ec Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2023 16:43:26 +0200 Subject: [PATCH 136/391] Added failing test for #11679. --- .../VirtualizingStackPanelTests.cs | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 4be888f96d..ba4fb32067 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -367,6 +367,38 @@ namespace Avalonia.Controls.UnitTests Assert.False(originalFocused.IsVisible); } + [Fact] + public void Focused_Element_Losing_Focus_Does_Not_Reset_Selection() + { + using var app = App(); + var (target, scroll, listBox) = CreateTarget( + styles: new[] + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(ListBoxItem.TemplateProperty, ListBoxItemTemplate()), + } + } + }); + + listBox.SelectedIndex = 0; + + var selectedContainer = target.GetRealizedElements().First()!; + selectedContainer.Focusable = true; + selectedContainer.Focus(); + + scroll.Offset = new Vector(0, 500); + Layout(target); + + var newFocused = target.GetRealizedElements().First()!; + newFocused.Focusable = true; + newFocused.Focus(); + + Assert.Equal(0, listBox.SelectedIndex); + } + [Fact] public void Removing_Range_When_Scrolled_To_End_Updates_Viewport() { @@ -776,7 +808,19 @@ namespace Avalonia.Controls.UnitTests Optional itemTemplate = default, IEnumerable + + + + diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml index 3e1270256d..6e534de2f0 100644 --- a/samples/ControlCatalog.Android/Resources/values/styles.xml +++ b/samples/ControlCatalog.Android/Resources/values/styles.xml @@ -6,16 +6,7 @@ - - - - - diff --git a/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml b/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml new file mode 100644 index 0000000000..dde4b5a7dd --- /dev/null +++ b/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml b/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml new file mode 100644 index 0000000000..94f27d9e63 --- /dev/null +++ b/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/MobileSandbox.Android/Resources/values-night/colors.xml b/samples/MobileSandbox.Android/Resources/values-night/colors.xml new file mode 100644 index 0000000000..3d47b6fc58 --- /dev/null +++ b/samples/MobileSandbox.Android/Resources/values-night/colors.xml @@ -0,0 +1,4 @@ + + + #212121 + diff --git a/samples/MobileSandbox.Android/Resources/values-v31/styles.xml b/samples/MobileSandbox.Android/Resources/values-v31/styles.xml new file mode 100644 index 0000000000..7518f078e5 --- /dev/null +++ b/samples/MobileSandbox.Android/Resources/values-v31/styles.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/samples/MobileSandbox.Android/Resources/values/styles.xml b/samples/MobileSandbox.Android/Resources/values/styles.xml index 2759d2904a..22085806da 100644 --- a/samples/MobileSandbox.Android/Resources/values/styles.xml +++ b/samples/MobileSandbox.Android/Resources/values/styles.xml @@ -6,12 +6,7 @@ - - - From 10941d7686b3e5dbbb18bead44cfc315235513e4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 20 Jun 2023 05:20:46 -0400 Subject: [PATCH 171/391] Fix ColorPalette TryGetResource --- .../ColorPaletteResourcesCollection.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs index 261de5497d..f26f6d8660 100644 --- a/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs +++ b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs @@ -30,11 +30,22 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary Count > 0; public bool TryGetResource(object key, ThemeVariant? theme, out object? value) { - theme ??= ThemeVariant.Default; - if (base.TryGetValue(theme, out var paletteResources) - && paletteResources.TryGetResource(key, theme, out value)) + if (theme is not null) { - return true; + if (base.TryGetValue(theme, out var themePaletteResources) + && themePaletteResources.TryGetResource(key, theme, out value)) + { + return true; + } + } + + if (theme != ThemeVariant.Default) + { + if (base.TryGetValue(ThemeVariant.Default, out var defaultPaletteResources) + && defaultPaletteResources.TryGetResource(key, ThemeVariant.Default, out value)) + { + return true; + } } value = null; From 3c23f01839f27ad4d832281597550f41982d012e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 20 Jun 2023 15:38:03 +0200 Subject: [PATCH 172/391] fix(ListBox): Regression space handling --- src/Avalonia.Controls/ListBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 220903cc04..2f3ba33d8d 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -148,7 +148,7 @@ namespace Avalonia.Controls } else if (e.Key == Key.Space || e.Key == Key.Enter) { - e.Handled |= UpdateSelectionFromEventSource( + UpdateSelectionFromEventSource( e.Source, true, e.KeyModifiers.HasFlag(KeyModifiers.Shift), From c368048aefe8430c3b22cf228f481daffe2b5a24 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 20 Jun 2023 15:38:31 +0200 Subject: [PATCH 173/391] test(LIstBox): Space handling --- .../ListBoxTests.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 03497c335d..a6a2a02479 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -1247,6 +1247,51 @@ namespace Avalonia.Controls.UnitTests data.GetRealizedItems()); } + [Fact] + public void Should_Not_Handle_Space_When_TextBox_Inside_ListBoxItem() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var target = new TextBox + { + Focusable = true + }; + var listbox = new ListBox() + { + Template = ListBoxTemplate(), + Items = + { + new ListBoxItem() + { + Template = ListBoxItemTemplate(), + Content = target, + } + } + }; + + var nKeyDown = 0; + + var root = new TestRoot() + { + Width = 1000, + Height = 1000, + Child = listbox, + }; + + root.KeyDown += (s, e) => nKeyDown++; + + + listbox.ApplyTemplate(); + root.LayoutManager.ExecuteInitialLayoutPass(); + + target.Focus(); + + RaiseKeyEvent(target, Key.Space, KeyModifiers.None); + + Assert.Equal(1, nKeyDown); + } + } + private static void RaiseKeyEvent(Control target, Key key, KeyModifiers inputModifiers = 0) { target.RaiseEvent(new KeyEventArgs From 9d051a6a64c6039bd8b075c7997468741a78acc8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Jun 2023 09:39:02 +0200 Subject: [PATCH 174/391] Added failing test for #11484. --- .../AvaloniaObjectTests_Coercion.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs index 42720cbb4c..e664829677 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs @@ -243,6 +243,19 @@ namespace Avalonia.Base.UnitTests Assert.Equal(new[] { 11 }, target.CoerceFooInvocations); } + [Fact] + public void Second_Coerce_Of_Default_Value_Is_Passed_Uncoerced_Value() + { + var target = new Class1(); + + target.MinFoo = 20; + target.CoerceFooInvocations.Clear(); + target.CoerceValue(Class1.FooProperty); + target.CoerceValue(Class1.FooProperty); + + Assert.Equal(new[] { 11, 11 }, target.CoerceFooInvocations); + } + [Fact] public void ClearValue_Respects_Coerced_Default_Value() { From a2d572e006cfd30eb6825f0cf3e33b55cef0e91e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Jun 2023 09:43:28 +0200 Subject: [PATCH 175/391] Don't set uncoerced value to coerced default val. Fixes #11484 --- src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs index 4518289335..a69d7b7094 100644 --- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -216,7 +216,7 @@ namespace Avalonia.PropertyStore valueChanged = !EqualityComparer.Default.Equals(Value, v); Value = v; Priority = priority; - if (_uncommon is not null) + if (!isCoercedDefaultValue && _uncommon is not null) _uncommon._uncoercedValue = value; } @@ -225,7 +225,7 @@ namespace Avalonia.PropertyStore baseValueChanged = !EqualityComparer.Default.Equals(_baseValue, v); _baseValue = v; BasePriority = priority; - if (_uncommon is not null) + if (!isCoercedDefaultValue && _uncommon is not null) _uncommon._uncoercedBaseValue = value; } From 21647102bb620e544220fb116ddeea484f30285f Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 21 Jun 2023 12:30:49 +0200 Subject: [PATCH 176/391] Fix simple theme for TransitioningContentControl --- .../Controls/TransitioningContentControl.xaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml b/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml index ceccdf56c7..5f532df848 100644 --- a/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml +++ b/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml @@ -13,11 +13,10 @@ BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" ContentTemplate="{TemplateBinding ContentTemplate}" - Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" /> - Date: Wed, 21 Jun 2023 15:05:19 +0200 Subject: [PATCH 177/391] First draft --- src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml | 148 +++++++----------- .../AboutAvaloniaDialog.xaml.cs | 5 +- 2 files changed, 62 insertions(+), 91 deletions(-) diff --git a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml index 22362fa5f5..2bd33ac296 100644 --- a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml +++ b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml @@ -6,106 +6,74 @@ MinWidth="430" MinHeight="475" Title="About Avalonia" - Background="Purple" FontFamily="/Assets/Roboto-Light.ttf#Roboto" x:Class="Avalonia.Dialogs.AboutAvaloniaDialog" x:DataType="dialogs:AboutAvaloniaDialog"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// The gesture recognizer. - /// internal void CaptureGestureRecognizer(GestureRecognizer? gestureRecognizer) { if (CapturedGestureRecognizer != gestureRecognizer) diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index 84a4a8c873..0a290052c7 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/src/Avalonia.Base/Media/MediaContext.cs @@ -213,10 +213,10 @@ internal partial class MediaContext : ICompositorScheduler } /// - /// Executes the callback in the next iteration of the current UI-thread + /// Executes the callback in the next iteration of the current UI-thread /// render loop / layout pass that. /// - /// Code to execute. + /// public void BeginInvokeOnRender(Action callback) { if (_invokeOnRenderCallbacks == null) diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index a0cd2a8dcd..df3c9ebfc5 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -161,7 +161,7 @@ namespace Avalonia.Platform void PopGeometryClip(); /// - + /// Get Fetaure from type /// /// Attempts to get an optional feature from the drawing context implementation /// diff --git a/src/Avalonia.Base/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs index aaecf5deaf..80410ba181 100644 --- a/src/Avalonia.Base/Rendering/RenderLoop.cs +++ b/src/Avalonia.Base/Rendering/RenderLoop.cs @@ -42,7 +42,6 @@ namespace Avalonia.Rendering /// Initializes a new instance of the class. /// /// The render timer. - /// The UI thread dispatcher. public RenderLoop(IRenderTimer timer) { _timer = timer; diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs index bb1663eac0..7d586e1cbd 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs @@ -581,11 +581,6 @@ public partial class Dispatcher /// /// A Func<Task<TResult>> delegate to invoke through the dispatcher. /// - /// - /// The priority that determines in what order the specified - /// callback is invoked relative to the other pending operations - /// in the Dispatcher. - /// /// /// An task that completes after the task returned from callback finishes /// From 25a59718464ef3ec4972b9a7484b9ed02471a760 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 22 Jun 2023 12:44:42 +0200 Subject: [PATCH 185/391] fix(DevTools): Invalid Enum Editor --- .../Diagnostics/Views/PropertyValueEditorView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/PropertyValueEditorView.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/PropertyValueEditorView.cs index aec0cb37bc..f6e9842ac8 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/PropertyValueEditorView.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/PropertyValueEditorView.cs @@ -265,7 +265,7 @@ namespace Avalonia.Diagnostics.Views static bool IsValidNumeric(Type? type) { - if (type == null) + if (type == null || type.IsEnum == true) { return false; } From be9a26cbb1d62e5e4621c96db698c20a872d6a6f Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 10:22:07 +0200 Subject: [PATCH 186/391] Rework ITextInputMethodClient --- samples/MobileSandbox.Android/MainActivity.cs | 15 +- .../MobileSandbox.Android.csproj | 4 + .../Avalonia.Android/AndroidInputMethod.cs | 70 +++-- .../Avalonia.Android/Avalonia.Android.csproj | 3 + src/Android/Avalonia.Android/InputEditable.cs | 127 -------- .../Platform/SkiaPlatform/TopLevelImpl.cs | 172 ++++++++--- .../Input/TextInput/ITextEditable.cs | 23 -- .../Input/TextInput/ITextInputMethodClient.cs | 66 ---- .../Input/TextInput/ITextInputMethodImpl.cs | 2 +- .../Input/TextInput/InputMethodManager.cs | 4 +- .../Input/TextInput/TextInputMethodClient.cs | 136 ++++++++ ...TextInputMethodClientRequestedEventArgs.cs | 2 +- .../Presenters/TextPresenter.cs | 94 +++--- src/Avalonia.Controls/TextBox.cs | 4 +- .../TextBoxTextInputMethodClient.cs | 292 ++++++------------ .../DBusIme/DBusTextInputMethodBase.cs | 6 +- .../AvaloniaNativeTextInputMethod.cs | 18 +- src/Avalonia.X11/X11Window.Xim.cs | 6 +- src/Browser/Avalonia.Browser/AvaloniaView.cs | 18 +- .../Avalonia.Win32/Input/Imm32InputMethod.cs | 4 +- src/iOS/Avalonia.iOS/AvaloniaView.Text.cs | 2 +- src/iOS/Avalonia.iOS/AvaloniaView.cs | 2 +- src/iOS/Avalonia.iOS/TextInputResponder.cs | 36 ++- 23 files changed, 543 insertions(+), 563 deletions(-) delete mode 100644 src/Android/Avalonia.Android/InputEditable.cs delete mode 100644 src/Avalonia.Base/Input/TextInput/ITextEditable.cs delete mode 100644 src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs create mode 100644 src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs index 807afb0bd0..bc1be42f12 100644 --- a/samples/MobileSandbox.Android/MainActivity.cs +++ b/samples/MobileSandbox.Android/MainActivity.cs @@ -1,5 +1,7 @@ -using Android.App; +using System; +using Android.App; using Android.Content.PM; +using Avalonia; using Avalonia.Android; namespace MobileSandbox.Android @@ -7,5 +9,16 @@ namespace MobileSandbox.Android [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AvaloniaMainActivity { + protected override void OnCreate(Bundle savedInstanceState) + { + AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; + + base.OnCreate(savedInstanceState); + } + + private void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + System.Diagnostics.Debug.WriteLine(e.ToString()); + } } } diff --git a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj index 3b67d79f7b..54cfea83fa 100644 --- a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj +++ b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj @@ -38,6 +38,10 @@ True + + True + + diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 2bf3486ca5..fc00143a9a 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -1,10 +1,10 @@ using System; using Android.Content; using Android.Runtime; +using Android.Text; using Android.Views; using Android.Views.InputMethods; using Avalonia.Android.Platform.SkiaPlatform; -using Avalonia.Controls.Presenters; using Avalonia.Input.TextInput; namespace Avalonia.Android @@ -13,7 +13,7 @@ namespace Avalonia.Android { public View View { get; } - public ITextInputMethodClient Client { get; } + public TextInputMethodClient Client { get; } public bool IsActive { get; } @@ -36,7 +36,7 @@ namespace Avalonia.Android { private readonly TView _host; private readonly InputMethodManager _imm; - private ITextInputMethodClient _client; + private TextInputMethodClient _client; private AvaloniaInputConnection _inputConnection; public AndroidInputMethod(TView host) @@ -56,7 +56,7 @@ namespace Avalonia.Android public bool IsActive => Client != null; - public ITextInputMethodClient Client => _client; + public TextInputMethodClient Client => _client; public InputMethodManager IMM => _imm; @@ -65,7 +65,7 @@ namespace Avalonia.Android } - public void SetClient(ITextInputMethodClient client) + public void SetClient(TextInputMethodClient client) { _client = client; @@ -77,9 +77,24 @@ namespace Avalonia.Android _imm.ShowSoftInput(_host, ShowFlags.Implicit); - var surroundingText = Client.SurroundingText; + var selection = Client.Selection; - _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset); + _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + + var surroundingText = _client.SurroundingText ?? ""; + + var extractedText = new ExtractedText + { + Text = new Java.Lang.String(surroundingText), + SelectionStart = selection.Start, + SelectionEnd = selection.End, + PartialEndOffset = surroundingText.Length + }; + + _imm.UpdateExtractedText(_host, _inputConnection?.ExtractedTextToken ?? 0, extractedText); + + _client.SurroundingTextChanged += _client_SurroundingTextChanged; + _client.SelectionChanged += _client_SelectionChanged; } else { @@ -87,6 +102,30 @@ namespace Avalonia.Android } } + private void _client_SelectionChanged(object sender, EventArgs e) + { + var selection = Client.Selection; + + _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + } + + private void _client_SurroundingTextChanged(object sender, EventArgs e) + { + var surroundingText = _client.SurroundingText ?? ""; + + _inputConnection.EditableWrapper.IgnoreChange = true; + + _inputConnection.Editable.Replace(0, _inputConnection.Editable.Length(), surroundingText); + + _inputConnection.EditableWrapper.IgnoreChange = false; + + var selection = Client.Selection; + + _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + + //Debug.WriteLine($"SurroundingText: {surroundingText}, CaretIndex: {selection.Start}"); + } + public void SetCursorRect(Rect rect) { @@ -134,25 +173,8 @@ namespace Avalonia.Android outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi; - _client.TextEditable = _inputConnection.InputEditable; - return _inputConnection; }); } } - - internal readonly record struct ComposingRegion - { - private readonly int _start = -1; - private readonly int _end = -1; - - public ComposingRegion(int start, int end) - { - _start = start; - _end = end; - } - - public int Start => _start; - public int End => _end; - } } diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj index d8b0c3d534..e04a1acde1 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -7,6 +7,9 @@ portable Avalonia.Android.Internal + + True + diff --git a/src/Android/Avalonia.Android/InputEditable.cs b/src/Android/Avalonia.Android/InputEditable.cs deleted file mode 100644 index c5b68d2652..0000000000 --- a/src/Android/Avalonia.Android/InputEditable.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using Android.Runtime; -using Android.Text; -using Android.Views; -using Android.Views.InputMethods; -using Avalonia.Android.Platform.SkiaPlatform; -using Avalonia.Controls.Presenters; -using Avalonia.Input; -using Avalonia.Input.Raw; -using Avalonia.Input.TextInput; -using Java.Lang; -using static System.Net.Mime.MediaTypeNames; - -namespace Avalonia.Android -{ - internal class InputEditable : SpannableStringBuilder, ITextEditable - { - private readonly TopLevelImpl _topLevel; - private readonly IAndroidInputMethod _inputMethod; - private readonly AvaloniaInputConnection _avaloniaInputConnection; - private int _currentBatchLevel; - private string _previousText; - private int _previousSelectionStart; - private int _previousSelectionEnd; - - public event EventHandler TextChanged; - public event EventHandler SelectionChanged; - public event EventHandler CompositionChanged; - - public InputEditable(TopLevelImpl topLevel, IAndroidInputMethod inputMethod, AvaloniaInputConnection avaloniaInputConnection) - { - _topLevel = topLevel; - _inputMethod = inputMethod; - _avaloniaInputConnection = avaloniaInputConnection; - } - - public InputEditable(ICharSequence text) : base(text) - { - } - - public InputEditable(string text) : base(text) - { - } - - public InputEditable(ICharSequence text, int start, int end) : base(text, start, end) - { - } - - public InputEditable(string text, int start, int end) : base(text, start, end) - { - } - - protected InputEditable(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) - { - } - - public int SelectionStart - { - get => Selection.GetSelectionStart(this); set - { - var end = SelectionEnd < 0 ? 0 : SelectionEnd; - _avaloniaInputConnection.SetSelection(value, end); - _inputMethod.IMM.UpdateSelection(_topLevel.View, value, end, value, end); - } - } - public int SelectionEnd - { - get => Selection.GetSelectionEnd(this); set - { - var start = SelectionStart < 0 ? 0 : SelectionStart; - _avaloniaInputConnection.SetSelection(start, value); - _inputMethod.IMM.UpdateSelection(_topLevel.View, start, value, start, value); - } - } - - public string? Text - { - get => ToString(); set - { - if (Text != value) - { - Clear(); - Insert(0, value ?? ""); - } - } - } - - public int CompositionStart => BaseInputConnection.GetComposingSpanStart(this); - - public int CompositionEnd => BaseInputConnection.GetComposingSpanEnd(this); - - public void BeginBatchEdit() - { - _currentBatchLevel++; - - if (_currentBatchLevel == 1) - { - _previousText = ToString(); - _previousSelectionStart = SelectionStart; - _previousSelectionEnd = SelectionEnd; - } - } - - public void EndBatchEdit() - { - if (_currentBatchLevel == 1) - { - if(_previousText != Text) - { - TextChanged?.Invoke(this, EventArgs.Empty); - } - - if (_previousSelectionStart != SelectionStart || _previousSelectionEnd != SelectionEnd) - { - SelectionChanged?.Invoke(this, EventArgs.Empty); - } - } - - _currentBatchLevel--; - } - - public void RaiseCompositionChanged() - { - CompositionChanged?.Invoke(this, EventArgs.Empty); - } - } -} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index a29f06a106..f725240c22 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -11,6 +11,7 @@ using Android.Text; using Android.Views; using Android.Views.InputMethods; using AndroidX.AppCompat.App; +using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Android.Platform.Storage; @@ -429,78 +430,124 @@ namespace Avalonia.Android.Platform.SkiaPlatform activity.Window.Attributes = attr; } } + + internal void TextInput(string text) + { + if(Input != null) + { + var args = new RawTextInputEventArgs(AndroidKeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, InputRoot, text); + + Input(args); + } + } } - internal class AvaloniaInputConnection : BaseInputConnection + internal class EditableWrapper : SpannableStringBuilder { - private readonly TopLevelImpl _topLevel; - private readonly IAndroidInputMethod _inputMethod; - private readonly InputEditable _editable; + private readonly AvaloniaInputConnection _inputConnection; - public AvaloniaInputConnection(TopLevelImpl topLevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) + public EditableWrapper(AvaloniaInputConnection inputConnection) { - _topLevel = topLevel; - _inputMethod = inputMethod; - _editable = new InputEditable(_topLevel, _inputMethod, this); + _inputConnection = inputConnection; } - public override IEditable Editable => _editable; - - internal InputEditable InputEditable => _editable; + public bool IgnoreChange { get; set; } - public override bool SetComposingRegion(int start, int end) + public override IEditable Replace(int start, int end, ICharSequence tb) { - var ret = base.SetComposingRegion(start, end); + if (!IgnoreChange && !_inputConnection.IsComposing && start != end) + { + var text = tb.SubSequence(0, tb.Length()); - InputEditable.RaiseCompositionChanged(); + //System.Diagnostics.Debug.WriteLine($"Replace: start: {start}, end: {end}, text: {text}"); - return ret; + _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); + } + + return base.Replace(start, end, tb); } - public override bool SetComposingText(ICharSequence text, int newCursorPosition) + public override IEditable Replace(int start, int end, ICharSequence tb, int tbstart, int tbend) { - var composingText = text.ToString(); - - if (string.IsNullOrEmpty(composingText)) - { - return CommitText(text, newCursorPosition); - } - else + if (!IgnoreChange && !_inputConnection.IsComposing && start != end) { - var ret = base.SetComposingText(text, newCursorPosition); + var text = tb.SubSequence(tbstart, tbend); - InputEditable.RaiseCompositionChanged(); + //System.Diagnostics.Debug.WriteLine($"Replace: start: {start}, end: {end}, text: {text}"); - return ret; + _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); } + + return base.Replace(start, end, tb, tbstart, tbend); } + } - public override bool BeginBatchEdit() - { - _editable.BeginBatchEdit(); + internal class AvaloniaInputConnection : BaseInputConnection + { + private readonly TopLevelImpl _toplevel; + private readonly IAndroidInputMethod _inputMethod; + private readonly EditableWrapper _editable; + private string _compositionText; + private bool _commitInProgress; - return base.BeginBatchEdit(); + public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) + { + _toplevel = toplevel; + _inputMethod = inputMethod; + _editable = new EditableWrapper(this); } - public override bool EndBatchEdit() - { - var ret = base.EndBatchEdit(); - _editable.EndBatchEdit(); + public bool IsComposing => !string.IsNullOrEmpty(_compositionText); - return ret; - } + public int ExtractedTextToken { get; private set; } + + public override IEditable Editable => _editable; + + public EditableWrapper EditableWrapper => _editable; + + public IAndroidInputMethod InputMethod => _inputMethod; - public override bool FinishComposingText() + public override bool SetComposingText(ICharSequence text, int newCursorPosition) { - var ret = base.FinishComposingText(); - InputEditable.RaiseCompositionChanged(); - return ret; + _compositionText = text.SubSequence(0, text.Length()); + + System.Diagnostics.Debug.WriteLine($"Composition Changed: {_compositionText}"); + + if(_inputMethod.IsActive && !_commitInProgress) + { + _inputMethod.Client.SetPreeditText(_compositionText); + } + + return base.SetComposingText(text, newCursorPosition); } public override bool CommitText(ICharSequence text, int newCursorPosition) { + _commitInProgress = true; + var ret = base.CommitText(text, newCursorPosition); - InputEditable.RaiseCompositionChanged(); + + var committedText = text.SubSequence(0, text.Length()); + + if (string.IsNullOrEmpty(committedText)) + { + committedText = _compositionText; + } + + if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText)) + { + if (!string.IsNullOrEmpty(_compositionText)) + { + _inputMethod.Client.SetPreeditText(null); + } + + _toplevel.TextInput(committedText); + + _compositionText = null; + } + + _commitInProgress = false; + return ret; } @@ -517,5 +564,48 @@ namespace Avalonia.Android.Platform.SkiaPlatform return base.PerformEditorAction(actionCode); } + + public override ExtractedText GetExtractedText(ExtractedTextRequest request, [GeneratedEnum] GetTextFlags flags) + { + if (request == null) + return null; + + ExtractedTextToken = request.Token; + + var editable = Editable; + + if (editable == null) + { + return null; + } + + if (!_inputMethod.IsActive) + { + return null; + } + + var selection = _inputMethod.Client.Selection; + + ExtractedText extract = new ExtractedText + { + Flags = 0, + PartialStartOffset = -1, + PartialEndOffset = -1, + SelectionStart = selection.Start, + SelectionEnd = selection.End, + StartOffset = 0 + }; + + if ((request.Flags & GetTextFlags.WithStyles) != 0) + { + extract.Text = new SpannableString(editable); + } + else + { + extract.Text = editable; + } + + return extract; + } } } diff --git a/src/Avalonia.Base/Input/TextInput/ITextEditable.cs b/src/Avalonia.Base/Input/TextInput/ITextEditable.cs deleted file mode 100644 index f9f5ec8d40..0000000000 --- a/src/Avalonia.Base/Input/TextInput/ITextEditable.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Metadata; - -namespace Avalonia.Input.TextInput -{ - [NotClientImplementable] - public interface ITextEditable - { - event EventHandler TextChanged; - event EventHandler SelectionChanged; - event EventHandler CompositionChanged; - int SelectionStart { get; set; } - int SelectionEnd { get; set; } - int CompositionStart { get; } - int CompositionEnd { get; } - - string? Text { get; set; } - } -} diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs deleted file mode 100644 index 2cdcd33626..0000000000 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using Avalonia.Media.TextFormatting; -using Avalonia.VisualTree; - -namespace Avalonia.Input.TextInput -{ - public interface ITextInputMethodClient - { - /// - /// The cursor rectangle relative to the TextViewVisual - /// - Rect CursorRectangle { get; } - /// - /// Should be fired when cursor rectangle is changed inside the TextViewVisual - /// - event EventHandler? CursorRectangleChanged; - /// - /// The visual that's showing the text - /// - Visual TextViewVisual { get; } - /// - /// Should be fired when text-hosting visual is changed - /// - event EventHandler? TextViewVisualChanged; - /// - /// Indicates if TextViewVisual is capable of displaying non-committed input on the cursor position - /// - bool SupportsPreedit { get; } - /// - /// Sets the non-committed input string - /// - void SetPreeditText(string? text); - - /// - /// Sets the current composing region. This doesn't remove the composing text from the commited text. - /// - void SetComposingRegion(TextRange? region); - - /// - /// Indicates if text input client is capable of providing the text around the cursor - /// - bool SupportsSurroundingText { get; } - /// - /// Returns the text around the cursor, usually the current paragraph, the cursor position inside that text and selection start position - /// - TextInputMethodSurroundingText SurroundingText { get; } - /// - /// Should be fired when surrounding text changed - /// - event EventHandler? SurroundingTextChanged; - - /// - /// Gets or sets a platform editable. Text and selection changes made in the editable are forwarded to the IM client. - /// - ITextEditable? TextEditable { get; set; } - - void SelectInSurroundingText(int start, int end); - } - - public record struct TextInputMethodSurroundingText - { - public string Text { get; set; } - public int CursorOffset { get; set; } - public int AnchorOffset { get; set; } - } -} diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs index be7ad81f81..2969b2e60d 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs @@ -5,7 +5,7 @@ namespace Avalonia.Input.TextInput [Unstable] public interface ITextInputMethodImpl { - void SetClient(ITextInputMethodClient? client); + void SetClient(TextInputMethodClient? client); void SetCursorRect(Rect rect); void SetOptions(TextInputOptions options); void Reset(); diff --git a/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs b/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs index c7fca04ea9..387179b970 100644 --- a/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs @@ -7,7 +7,7 @@ namespace Avalonia.Input.TextInput { private ITextInputMethodImpl? _im; private IInputElement? _focusedElement; - private ITextInputMethodClient? _client; + private TextInputMethodClient? _client; private readonly TransformTrackingHelper _transformTracker = new TransformTrackingHelper(); public TextInputMethodManager() @@ -16,7 +16,7 @@ namespace Avalonia.Input.TextInput InputMethod.IsInputMethodEnabledProperty.Changed.Subscribe(OnIsInputMethodEnabledChanged); } - private ITextInputMethodClient? Client + private TextInputMethodClient? Client { get => _client; set diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs new file mode 100644 index 0000000000..87f8d7ba97 --- /dev/null +++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs @@ -0,0 +1,136 @@ +using System; + +namespace Avalonia.Input.TextInput +{ + public abstract class TextInputMethodClient + { + private Rect _cursorRectangle; + private string _surroundingText = ""; + private TextSelection _selection; + + /// + /// Fires when the text view visual has changed + /// + public event EventHandler? TextViewVisualChanged; + + /// + /// Fires when the cursor rectangle has changed + /// + public event EventHandler? CursorRectangleChanged; + + /// + /// Fires when the surrounding text has changed + /// + public event EventHandler? SurroundingTextChanged; + + /// + /// Fires when the selection has changed + /// + public event EventHandler? SelectionChanged; + + /// + /// The visual that's showing the text + /// + public abstract Visual TextViewVisual { get; } + + /// + /// Indicates if TextViewVisual is capable of displaying non-committed input on the cursor position + /// + public abstract bool SupportsPreedit { get; } + + /// + /// Indicates if text input client is capable of providing the text around the cursor + /// + public abstract bool SupportsSurroundingText { get; } + + /// + /// Returns the text around the cursor, usually the current paragraph + /// + public string SurroundingText + { + get => _surroundingText; + set + { + var oldValue = _surroundingText; + + if (oldValue == value) + { + return; + } + + _surroundingText = value; + + OnSurroundingTextChanged(oldValue, value); + } + } + + /// + /// Gets the cursor rectangle relative to the TextViewVisual + /// + public Rect CursorRectangle + { + get => _cursorRectangle; + protected set + { + var oldvalue = _cursorRectangle; + + if (oldvalue == value) + { + return; + } + + _cursorRectangle = value; + + OnCursorRectangleChanged(oldvalue, value); + } + } + + /// + /// Gets or sets the curent selection range within current surrounding text. + /// + public TextSelection Selection + { + get => _selection; + set + { + var oldValue = _selection; + + if (oldValue == value) + { + return; + } + + _selection = value; + + OnSelectionChanged(oldValue, value); + } + } + + /// + /// Sets the non-committed input string + /// + public virtual void SetPreeditText(string? preeditText) { } + + protected virtual void OnCursorRectangleChanged(Rect oldValue, Rect newValue) + { + CursorRectangleChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue) + { + TextViewVisualChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnSurroundingTextChanged(string? oldValue, string? newValue) + { + SurroundingTextChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnSelectionChanged(TextSelection oldValue, TextSelection newValue) + { + SelectionChanged?.Invoke(this, EventArgs.Empty); + } + } + + public record struct TextSelection(int Start, int End); +} diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs index bec43487d2..d90d4257fa 100644 --- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs +++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs @@ -7,6 +7,6 @@ namespace Avalonia.Input.TextInput /// /// Set this property to a valid text input client to enable input method interaction /// - public ITextInputMethodClient? Client { get; set; } + public TextInputMethodClient? Client { get; set; } } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index c5b4834ded..e3949a3255 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -50,15 +50,6 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty PreeditTextProperty = AvaloniaProperty.Register(nameof(PreeditText)); - /// - /// Defines the property. - /// - public static readonly DirectProperty CompositionRegionProperty = - AvaloniaProperty.RegisterDirect( - nameof(CompositionRegion), - o => o.CompositionRegion, - (o, v) => o.CompositionRegion = v); - /// /// Defines the property. /// @@ -97,7 +88,6 @@ namespace Avalonia.Controls.Presenters private CharacterHit _lastCharacterHit; private Rect _caretBounds; private Point _navigationPosition; - private TextRange? _compositionRegion; static TextPresenter() { @@ -137,12 +127,6 @@ namespace Avalonia.Controls.Presenters set => SetValue(PreeditTextProperty, value); } - public TextRange? CompositionRegion - { - get => _compositionRegion; - set => SetAndRaise(CompositionRegionProperty, ref _compositionRegion, value); - } - /// /// Gets or sets the font family. /// @@ -490,10 +474,10 @@ namespace Avalonia.Controls.Presenters { TextLayout result; - var text = Text; - + var caretIndex = CaretIndex; + var preeditText = PreeditText; + var text = GetCombinedText(Text, caretIndex, preeditText); var typeface = new Typeface(FontFamily, FontStyle, FontWeight); - var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; var start = Math.Min(selectionStart, selectionEnd); @@ -503,22 +487,9 @@ namespace Avalonia.Controls.Presenters var foreground = Foreground; - if (_compositionRegion != null) - { - var preeditHighlight = new ValueSpan(_compositionRegion?.Start ?? 0, _compositionRegion?.Length ?? 0, - new GenericTextRunProperties(typeface, FontSize, - foregroundBrush: foreground, - textDecorations: TextDecorations.Underline)); - - textStyleOverrides = new[] - { - preeditHighlight - }; - - } - else if (!string.IsNullOrEmpty(PreeditText)) + if (!string.IsNullOrEmpty(preeditText)) { - var preeditHighlight = new ValueSpan(CaretIndex, PreeditText.Length, + var preeditHighlight = new ValueSpan(caretIndex, preeditText.Length, new GenericTextRunProperties(typeface, FontSize, foregroundBrush: foreground, textDecorations: TextDecorations.Underline)); @@ -554,6 +525,27 @@ namespace Avalonia.Controls.Presenters return result; } + private static string? GetCombinedText(string? text, int caretIndex, string? preeditText) + { + if (string.IsNullOrEmpty(preeditText)) + { + return text; + } + + if (string.IsNullOrEmpty(text)) + { + return preeditText; + } + + var sb = StringBuilderCache.Acquire(text.Length + preeditText.Length); + + sb.Append(text.Substring(0, caretIndex)); + sb.Insert(caretIndex, preeditText); + sb.Append(text.Substring(caretIndex)); + + return StringBuilderCache.GetStringAndRelease(sb); + } + protected virtual void InvalidateTextLayout() { _textLayout?.Dispose(); @@ -831,6 +823,18 @@ namespace Avalonia.Controls.Presenters _caretTimer.Tick -= CaretTimerTick; } + private void OnPreeditTextChanged(string? preeditText) + { + if (string.IsNullOrEmpty(preeditText)) + { + UpdateCaret(new CharacterHit(CaretIndex), false); + } + else + { + UpdateCaret(new CharacterHit(CaretIndex + preeditText.Length), false); + } + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); @@ -840,10 +844,30 @@ namespace Avalonia.Controls.Presenters MoveCaretToTextPosition(change.GetNewValue()); } + if(change.Property == PreeditTextProperty) + { + OnPreeditTextChanged(change.NewValue as string); + } + + if(change.Property == TextProperty) + { + if (!string.IsNullOrEmpty(PreeditText)) + { + PreeditText = null; + } + } + + if(change.Property == CaretIndexProperty) + { + if (!string.IsNullOrEmpty(PreeditText)) + { + PreeditText = null; + } + } + switch (change.Property.Name) { case nameof(PreeditText): - case nameof(CompositionRegion): case nameof(Foreground): case nameof(FontSize): case nameof(FontStyle): diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 3016dc8239..f8eabf670c 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -975,7 +975,9 @@ namespace Avalonia.Controls textBuilder.Insert(caretIndex, input); - SetCurrentValue(TextProperty, StringBuilderCache.GetStringAndRelease(textBuilder)); + var text = StringBuilderCache.GetStringAndRelease(textBuilder); + + SetCurrentValue(TextProperty, text); ClearSelection(); diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index e999d0bb74..33a0482c68 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -1,132 +1,80 @@ using System; -using System.Diagnostics; using Avalonia.Controls.Presenters; using Avalonia.Input.TextInput; -using Avalonia.Media; using Avalonia.Media.TextFormatting; -using Avalonia.Threading; using Avalonia.Utilities; namespace Avalonia.Controls { - internal class TextBoxTextInputMethodClient : ITextInputMethodClient + internal class TextBoxTextInputMethodClient : TextInputMethodClient { private TextBox? _parent; private TextPresenter? _presenter; - private ITextEditable? _textEditable; + private bool _isPropertyChange; - public Visual TextViewVisual => _presenter!; + public override Visual TextViewVisual => _presenter!; - public bool SupportsPreedit => true; + public override bool SupportsPreedit => true; - public bool SupportsSurroundingText => true; + public override bool SupportsSurroundingText => true; - public Rect CursorRectangle + public void SetPresenter(TextPresenter? presenter, TextBox? parent) { - get + if (_parent != null) { - if (_parent == null || _presenter == null) - { - return default; - } - - var transform = _presenter.TransformToVisual(_parent); - - if (transform == null) - { - return default; - } - - var rect = _presenter.GetCursorRectangle().TransformToAABB(transform.Value); - - return rect; + _parent.PropertyChanged -= OnParentPropertyChanged; } - } - - public TextInputMethodSurroundingText SurroundingText - { - get - { - if (_presenter is null || _parent is null) - { - return default; - } - - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); - var textLine = _presenter.TextLayout.TextLines[lineIndex]; - - var lineStart = textLine.FirstTextSourceIndex; + _parent = parent; - var lineText = GetTextLineText(textLine); + if (_parent != null) + { + _parent.PropertyChanged += OnParentPropertyChanged; + } - var anchorOffset = Math.Max(0, _parent.SelectionStart - lineStart); + var oldPresenter = _presenter; - var cursorOffset = Math.Max(0, _presenter.SelectionEnd - lineStart); + if (oldPresenter != null) + { + oldPresenter.ClearValue(TextPresenter.PreeditTextProperty); - return new TextInputMethodSurroundingText - { - Text = lineText ?? "", - AnchorOffset = anchorOffset, - CursorOffset = cursorOffset - }; + oldPresenter.CaretBoundsChanged -= OnPresenterCursorRectangleChanged; } - } - public ITextEditable? TextEditable - { - get => _textEditable; set + _presenter = presenter; + + if (_presenter != null) { - if (_textEditable != null) - { - _textEditable.TextChanged -= TextEditable_TextChanged; - _textEditable.SelectionChanged -= TextEditable_SelectionChanged; - _textEditable.CompositionChanged -= TextEditable_CompositionChanged; - } + _presenter.CaretBoundsChanged += OnPresenterCursorRectangleChanged; + } - _textEditable = value; + OnTextViewVisualChanged(oldPresenter, presenter); - if (_textEditable != null) - { - _textEditable.TextChanged += TextEditable_TextChanged; - _textEditable.SelectionChanged += TextEditable_SelectionChanged; - _textEditable.CompositionChanged += TextEditable_CompositionChanged; - - if (_presenter != null) - { - _textEditable.Text = _presenter.Text; - _textEditable.SelectionStart = _presenter.SelectionStart; - _textEditable.SelectionEnd = _presenter.SelectionEnd; - } - } - } + OnPresenterCursorRectangleChanged(this, EventArgs.Empty); } - private void TextEditable_CompositionChanged(object? sender, EventArgs e) + public override void SetPreeditText(string? preeditText) { - if (_presenter != null && _textEditable != null) + if (_presenter == null || _parent == null) { - _presenter.SetCurrentValue(TextPresenter.CompositionRegionProperty, new TextRange(_textEditable.CompositionStart, _textEditable.CompositionEnd)); + return; } + + _presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText); } - private void TextEditable_SelectionChanged(object? sender, EventArgs e) + protected override void OnSelectionChanged(TextSelection oldValue, TextSelection newValue) { - if (_parent != null && _textEditable != null) + base.OnSelectionChanged(oldValue, newValue); + + if (_isPropertyChange) { - _parent.SelectionStart = _textEditable.SelectionStart; - _parent.SelectionEnd = _textEditable.SelectionEnd; + return; } - } - private void TextEditable_TextChanged(object? sender, EventArgs e) - { - if (_parent != null) + if (oldValue != newValue) { - if (_parent.Text != _textEditable?.Text) - { - _parent.Text = _textEditable?.Text; - } + SetParentSelection(newValue); } } @@ -153,166 +101,112 @@ namespace Avalonia.Controls return lineText; } - public event EventHandler? TextViewVisualChanged; - - public event EventHandler? CursorRectangleChanged; - - public event EventHandler? SurroundingTextChanged; - - private string? _presenterText; - private int _compositionStart; - - public void SetPreeditText(string? preeditText) + private void OnParentTextChanged() { - if (_presenter == null || _parent == null) + if (_presenter is null || _parent is null) { - return; - } + SurroundingText = ""; - if (_presenterText is null) - { - _presenterText = _parent.Text ?? ""; - _compositionStart = _parent.CaretIndex; + return; } - var text = GetText(preeditText); - - _presenter.SetCurrentValue(TextPresenter.TextProperty, text); - - _presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText); - - _presenter.UpdateCaret(new CharacterHit(_compositionStart + (preeditText != null ? preeditText.Length : 0)), false); - - if (string.IsNullOrEmpty(preeditText)) +#if DEBUG + if (_parent.CaretIndex != _presenter.CaretIndex) { - _presenterText = null; + throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); } - } - private string? GetText(string? preeditText) - { - if (string.IsNullOrEmpty(preeditText)) + if (_parent.Text != _presenter.Text) { - return _presenterText; + throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); } +#endif - if (string.IsNullOrEmpty(_presenterText)) - { - return preeditText; - } + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); - var sb = StringBuilderCache.Acquire(_presenterText.Length + preeditText.Length); + var textLine = _presenter.TextLayout.TextLines[lineIndex]; - sb.Append(_presenterText); - sb.Insert(_compositionStart, preeditText); + var lineText = GetTextLineText(textLine); - return StringBuilderCache.GetStringAndRelease(sb); + SurroundingText = lineText; } - public void SetComposingRegion(TextRange? region) + private void OnPresenterCursorRectangleChanged(object? sender, EventArgs e) { - if (_presenter == null) + if (_parent == null || _presenter == null) { + CursorRectangle = default; + return; } - _presenter.SetCurrentValue(TextPresenter.CompositionRegionProperty, region); - } + var transform = _presenter.TransformToVisual(_parent); - public void SelectInSurroundingText(int start, int end) - { - if (_parent is null || _presenter is null) + if (transform == null) { + CursorRectangle = default; + return; } - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); - - var textLine = _presenter.TextLayout.TextLines[lineIndex]; - - var lineStart = textLine.FirstTextSourceIndex; - - var selectionStart = lineStart + start; - var selectionEnd = lineStart + end; - - _parent.SelectionStart = selectionStart; - _parent.SelectionEnd = selectionEnd; + CursorRectangle = _presenter.GetCursorRectangle().TransformToAABB(transform.Value); } - public void SetPresenter(TextPresenter? presenter, TextBox? parent) + private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { - if (_parent != null) + _isPropertyChange = true; + + if (e.Property == TextBox.TextProperty) { - _parent.PropertyChanged -= OnParentPropertyChanged; + OnParentTextChanged(); } - _parent = parent; - - if (_parent != null) + if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) { - _parent.PropertyChanged += OnParentPropertyChanged; + Selection = GetParentSelection(); } - if (_presenter != null) + _isPropertyChange = false; + } + + private TextSelection GetParentSelection() + { + if (_presenter is null || _parent is null) { - _presenter.ClearValue(TextPresenter.PreeditTextProperty); + return default; + } - _presenter.ClearValue(TextPresenter.CompositionRegionProperty); + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); - _presenter.CaretBoundsChanged -= OnCaretBoundsChanged; - } + var textLine = _presenter.TextLayout.TextLines[lineIndex]; - _presenter = presenter; + var lineStart = textLine.FirstTextSourceIndex; - if (_presenter != null) - { - _presenter.CaretBoundsChanged += OnCaretBoundsChanged; - } + var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart); - TextViewVisualChanged?.Invoke(this, EventArgs.Empty); + var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart); - OnCaretBoundsChanged(this, EventArgs.Empty); + return new TextSelection(selectionStart, selectionEnd); } - private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + private void SetParentSelection(TextSelection selection) { - if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) + if (_parent is null || _presenter is null) { - if (SupportsSurroundingText) - { - SurroundingTextChanged?.Invoke(this, e); - } - if (_textEditable != null) - { - var value = (int)(e.NewValue ?? 0); - if (e.Property == TextBox.SelectionStartProperty) - { - _textEditable.SelectionStart = value; - } - - if (e.Property == TextBox.SelectionEndProperty) - { - _textEditable.SelectionEnd = value; - } - } + return; } - if (e.Property == TextBox.TextProperty) - { - if (_textEditable != null) - { - _textEditable.Text = (string?)e.NewValue; - } - } - } + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); - private void OnCaretBoundsChanged(object? sender, EventArgs e) - { - Dispatcher.UIThread.Post(() => - { - CursorRectangleChanged?.Invoke(this, e); + var textLine = _presenter.TextLayout.TextLines[lineIndex]; + + var lineStart = textLine.FirstTextSourceIndex; + + var selectionStart = lineStart + selection.Start; + var selectionEnd = lineStart + selection.End; - }, DispatcherPriority.Input); + _parent.SelectionStart = selectionStart; + _parent.SelectionEnd = selectionEnd; } } } diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs index 936e856cf0..9ce6604594 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs @@ -41,7 +41,7 @@ namespace Avalonia.FreeDesktop.DBusIme private PixelRect? _lastReportedRect; private double _scaling = 1; private PixelPoint _windowPosition; - private ITextInputMethodClient? _client; + private TextInputMethodClient? _client; protected bool IsConnected => _currentName != null; @@ -53,7 +53,7 @@ namespace Avalonia.FreeDesktop.DBusIme _ = WatchAsync(); } - public ITextInputMethodClient Client => _client; + public TextInputMethodClient Client => _client; public bool IsActive => _client is not null; @@ -210,7 +210,7 @@ namespace Avalonia.FreeDesktop.DBusIme UpdateActive(); } - void ITextInputMethodImpl.SetClient(ITextInputMethodClient? client) + void ITextInputMethodImpl.SetClient(TextInputMethodClient? client) { _client = client; UpdateActive(); diff --git a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs index 4c93c06ef3..0171ada7b9 100644 --- a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs +++ b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input.TextInput; +using Avalonia.Media.TextFormatting; using Avalonia.Native.Interop; #nullable enable @@ -8,7 +9,7 @@ namespace Avalonia.Native { internal class AvaloniaNativeTextInputMethod : ITextInputMethodImpl, IDisposable { - private ITextInputMethodClient? _client; + private TextInputMethodClient? _client; private IAvnTextInputMethodClient? _nativeClient; private readonly IAvnTextInputMethod _inputMethod; @@ -28,7 +29,7 @@ namespace Avalonia.Native _inputMethod.Reset(); } - public void SetClient(ITextInputMethodClient? client) + public void SetClient(TextInputMethodClient? client) { if (_client is { SupportsSurroundingText: true }) { @@ -96,11 +97,12 @@ namespace Avalonia.Native } var surroundingText = _client.SurroundingText; + var selection = _client.Selection; _inputMethod.SetSurroundingText( - surroundingText.Text ?? "", - surroundingText.AnchorOffset, - surroundingText.CursorOffset + surroundingText ?? "", + selection.Start, + selection.End ); } @@ -116,9 +118,9 @@ namespace Avalonia.Native private class AvnTextInputMethodClient : NativeCallbackBase, IAvnTextInputMethodClient { - private readonly ITextInputMethodClient _client; + private readonly TextInputMethodClient _client; - public AvnTextInputMethodClient(ITextInputMethodClient client) + public AvnTextInputMethodClient(TextInputMethodClient client) { _client = client; } @@ -135,7 +137,7 @@ namespace Avalonia.Native { if (_client.SupportsSurroundingText) { - _client.SelectInSurroundingText(start, end); + _client.Selection = new TextSelection(start, end); } } } diff --git a/src/Avalonia.X11/X11Window.Xim.cs b/src/Avalonia.X11/X11Window.Xim.cs index cc5ecad09f..3dc860d612 100644 --- a/src/Avalonia.X11/X11Window.Xim.cs +++ b/src/Avalonia.X11/X11Window.Xim.cs @@ -15,14 +15,14 @@ namespace Avalonia.X11 private readonly X11Window _parent; private bool _windowActive, _imeActive; private Rect? _queuedCursorRect; - private ITextInputMethodClient? _client; + private TextInputMethodClient? _client; public XimInputMethod(X11Window parent) { _parent = parent; } - public ITextInputMethodClient? Client => _client; + public TextInputMethodClient? Client => _client; public bool IsActive => _client != null; @@ -62,7 +62,7 @@ namespace Avalonia.X11 UpdateActive(); } - public void SetClient(ITextInputMethodClient client) + public void SetClient(TextInputMethodClient client) { _client = client; UpdateActive(); diff --git a/src/Browser/Avalonia.Browser/AvaloniaView.cs b/src/Browser/Avalonia.Browser/AvaloniaView.cs index b5dd08d854..0b88d9c3e9 100644 --- a/src/Browser/Avalonia.Browser/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser/AvaloniaView.cs @@ -42,7 +42,7 @@ namespace Avalonia.Browser private const SKColorType ColorType = SKColorType.Rgba8888; private bool _useGL; - private ITextInputMethodClient? _client; + private TextInputMethodClient? _client; /// ID of the html element where avalonia content should be rendered. public AvaloniaView(string divId) @@ -397,7 +397,7 @@ namespace Avalonia.Browser if(start != -1 && end != -1 && _client != null) { - _client.SelectInSurroundingText(start, end); + _client.Selection = new TextSelection(start, end); } return false; } @@ -513,7 +513,7 @@ namespace Avalonia.Browser InputHelper.FocusElement(_containerElement); } - void ITextInputMethodImpl.SetClient(ITextInputMethodClient? client) + void ITextInputMethodImpl.SetClient(TextInputMethodClient? client) { if (_client != null) { @@ -534,9 +534,10 @@ namespace Avalonia.Browser InputHelper.ShowElement(_inputElement); InputHelper.FocusElement(_inputElement); - var surroundingText = _client.SurroundingText; + var surroundingText = _client.SurroundingText ?? ""; + var selection = _client.Selection; - InputHelper.SetSurroundingText(_inputElement, surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); + InputHelper.SetSurroundingText(_inputElement, surroundingText, selection.Start, selection.End); } else { @@ -548,16 +549,17 @@ namespace Avalonia.Browser { if (_client != null) { - var surroundingText = _client.SurroundingText; + var surroundingText = _client.SurroundingText ?? ""; + var selection = _client.Selection; - InputHelper.SetSurroundingText(_inputElement, surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); + InputHelper.SetSurroundingText(_inputElement, surroundingText, selection.Start, selection.End); } } void ITextInputMethodImpl.SetCursorRect(Rect rect) { InputHelper.FocusElement(_inputElement); - InputHelper.SetBounds(_inputElement, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, _client?.SurroundingText.CursorOffset ?? 0); + InputHelper.SetBounds(_inputElement, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, _client?.Selection.End ?? 0); InputHelper.FocusElement(_inputElement); } diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 05074cc82e..f36fb29107 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -26,7 +26,7 @@ namespace Avalonia.Win32.Input private bool _ignoreComposition; - public ITextInputMethodClient? Client { get; private set; } + public TextInputMethodClient? Client { get; private set; } [MemberNotNullWhen(true, nameof(Client))] public bool IsActive => Client != null; @@ -145,7 +145,7 @@ namespace Avalonia.Win32.Input }); } - public void SetClient(ITextInputMethodClient? client) + public void SetClient(TextInputMethodClient? client) { if(Client != null) { diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs index a1836f3ce4..0b0e169e8a 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs @@ -29,7 +29,7 @@ public partial class AvaloniaView private bool IsDrivingText => CurrentAvaloniaResponder is TextInputResponder t && ReferenceEquals(t.NextResponder, this); - void ITextInputMethodImpl.SetClient(ITextInputMethodClient? client) + void ITextInputMethodImpl.SetClient(TextInputMethodClient? client) { _client = client; if (_client == null && IsDrivingText) diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index 74cabb294d..939df1a3a0 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -27,7 +27,7 @@ namespace Avalonia.iOS private TopLevelImpl _topLevelImpl; private EmbeddableControlRoot _topLevel; private TouchHandler _touches; - private ITextInputMethodClient _client; + private TextInputMethodClient _client; private IAvaloniaViewController _controller; public AvaloniaView() diff --git a/src/iOS/Avalonia.iOS/TextInputResponder.cs b/src/iOS/Avalonia.iOS/TextInputResponder.cs index e444a90506..b54969e5ff 100644 --- a/src/iOS/Avalonia.iOS/TextInputResponder.cs +++ b/src/iOS/Avalonia.iOS/TextInputResponder.cs @@ -78,7 +78,7 @@ partial class AvaloniaView public NSObject Copy(NSZone? zone) => this; } - public TextInputResponder(AvaloniaView view, ITextInputMethodClient client) + public TextInputResponder(AvaloniaView view, TextInputMethodClient client) { _view = view; NextResponder = view; @@ -88,12 +88,12 @@ partial class AvaloniaView public override UIResponder NextResponder { get; } - private readonly ITextInputMethodClient _client; + private readonly TextInputMethodClient _client; private int _inSurroundingTextUpdateEvent; private readonly UITextPosition _beginningOfDocument = new AvaloniaTextPosition(0); private readonly UITextInputStringTokenizer _tokenizer; - public ITextInputMethodClient? Client => _client; + public TextInputMethodClient? Client => _client; public override bool CanResignFirstResponder => true; @@ -201,17 +201,23 @@ partial class AvaloniaView string IUITextInput.TextInRange(UITextRange range) { var r = (AvaloniaTextRange)range; - var s = _client.SurroundingText; + var surroundingText = _client.SurroundingText; + + var currentSelection = _client.Selection; + Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "IUIKeyInput.TextInRange {start} {end}", r.StartIndex, r.EndIndex); string result = ""; if (string.IsNullOrEmpty(_markedText)) - result = s.Text[r.StartIndex..r.EndIndex]; + if(surroundingText != null && r.EndIndex < surroundingText.Length) + { + result = surroundingText[r.StartIndex..r.EndIndex]; + } else { - var span = new CombinedSpan3(s.Text.AsSpan().Slice(0, s.CursorOffset), + var span = new CombinedSpan3(surroundingText.AsSpan().Slice(0, currentSelection.Start), _markedText, - s.Text.AsSpan().Slice(s.CursorOffset)); + surroundingText.AsSpan().Slice(currentSelection.Start)); var buf = new char[r.EndIndex - r.StartIndex]; span.CopyTo(buf, r.StartIndex); result = new string(buf); @@ -226,7 +232,7 @@ partial class AvaloniaView var r = (AvaloniaTextRange)range; Logger.TryGet(LogEventLevel.Debug, ImeLog)? .Log(null, "IUIKeyInput.ReplaceText {start} {end} {text}", r.StartIndex, r.EndIndex, text); - _client.SelectInSurroundingText(r.StartIndex, r.EndIndex); + _client.Selection = new TextSelection(r.StartIndex, r.EndIndex); TextInput(text); } @@ -447,21 +453,19 @@ partial class AvaloniaView { get { - return new AvaloniaTextRange( - Math.Min(_client.SurroundingText.CursorOffset, _client.SurroundingText.AnchorOffset), - Math.Max(_client.SurroundingText.CursorOffset, _client.SurroundingText.AnchorOffset)); + return new AvaloniaTextRange(_client.Selection.Start, _client.Selection.End); } set { if (_inSurroundingTextUpdateEvent > 0) return; if (value == null) - _client.SelectInSurroundingText(_client.SurroundingText.CursorOffset, - _client.SurroundingText.CursorOffset); + _client.Selection = default; else { var r = (AvaloniaTextRange)value; - _client.SelectInSurroundingText(r.StartIndex, r.EndIndex); + + _client.Selection = new TextSelection(r.StartIndex, r.EndIndex); } } } @@ -474,7 +478,7 @@ partial class AvaloniaView UITextPosition IUITextInput.BeginningOfDocument => _beginningOfDocument; - private int DocumentLength => (_client.SurroundingText.Text?.Length ?? 0) + (_markedText?.Length ?? 0); + private int DocumentLength => (_client.SurroundingText?.Length ?? 0) + (_markedText?.Length ?? 0); UITextPosition IUITextInput.EndOfDocument => new AvaloniaTextPosition(DocumentLength); UITextRange IUITextInput.MarkedTextRange @@ -483,7 +487,7 @@ partial class AvaloniaView { if (string.IsNullOrWhiteSpace(_markedText)) return null!; - return new AvaloniaTextRange(_client.SurroundingText.CursorOffset, _client.SurroundingText.CursorOffset + _markedText.Length); + return new AvaloniaTextRange(_client.Selection.Start, _client.Selection.Start + _markedText.Length); } } From 037bbd3ca012b3289f00be03b37df1cb58f8ff1c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 10:55:23 +0200 Subject: [PATCH 187/391] Adjust TextInputMethodClient --- samples/MobileSandbox.Android/MainActivity.cs | 1 + .../Input/TextInput/TextInputMethodClient.cs | 70 +----- .../TextBoxTextInputMethodClient.cs | 216 ++++++++---------- 3 files changed, 110 insertions(+), 177 deletions(-) diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs index bc1be42f12..7a0de74757 100644 --- a/samples/MobileSandbox.Android/MainActivity.cs +++ b/samples/MobileSandbox.Android/MainActivity.cs @@ -1,6 +1,7 @@ using System; using Android.App; using Android.Content.PM; +using Android.OS; using Avalonia; using Avalonia.Android; diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs index 87f8d7ba97..4abd20f911 100644 --- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs @@ -4,10 +4,6 @@ namespace Avalonia.Input.TextInput { public abstract class TextInputMethodClient { - private Rect _cursorRectangle; - private string _surroundingText = ""; - private TextSelection _selection; - /// /// Fires when the text view visual has changed /// @@ -46,87 +42,39 @@ namespace Avalonia.Input.TextInput /// /// Returns the text around the cursor, usually the current paragraph /// - public string SurroundingText - { - get => _surroundingText; - set - { - var oldValue = _surroundingText; - - if (oldValue == value) - { - return; - } - - _surroundingText = value; - - OnSurroundingTextChanged(oldValue, value); - } - } + public abstract string SurroundingText { get; } /// /// Gets the cursor rectangle relative to the TextViewVisual /// - public Rect CursorRectangle - { - get => _cursorRectangle; - protected set - { - var oldvalue = _cursorRectangle; - - if (oldvalue == value) - { - return; - } - - _cursorRectangle = value; - - OnCursorRectangleChanged(oldvalue, value); - } - } + public abstract Rect CursorRectangle { get; } /// /// Gets or sets the curent selection range within current surrounding text. /// - public TextSelection Selection - { - get => _selection; - set - { - var oldValue = _selection; - - if (oldValue == value) - { - return; - } - - _selection = value; - - OnSelectionChanged(oldValue, value); - } - } + public abstract TextSelection Selection { get; set; } /// /// Sets the non-committed input string /// public virtual void SetPreeditText(string? preeditText) { } - protected virtual void OnCursorRectangleChanged(Rect oldValue, Rect newValue) + protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue) { - CursorRectangleChanged?.Invoke(this, EventArgs.Empty); + TextViewVisualChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue) + protected virtual void OnCursorRectangleChanged() { - TextViewVisualChanged?.Invoke(this, EventArgs.Empty); + CursorRectangleChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnSurroundingTextChanged(string? oldValue, string? newValue) + protected virtual void OnSurroundingTextChanged() { SurroundingTextChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnSelectionChanged(TextSelection oldValue, TextSelection newValue) + protected virtual void OnSelectionChanged() { SelectionChanged?.Invoke(this, EventArgs.Empty); } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 33a0482c68..de4a3c5cf9 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -10,10 +10,104 @@ namespace Avalonia.Controls { private TextBox? _parent; private TextPresenter? _presenter; - private bool _isPropertyChange; public override Visual TextViewVisual => _presenter!; + public override string SurroundingText + { + get + { + if (_presenter is null || _parent is null) + { + return ""; + } + +#if DEBUG + if (_parent.CaretIndex != _presenter.CaretIndex) + { + throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); + } + + if (_parent.Text != _presenter.Text) + { + throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); + } +#endif + + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); + + var textLine = _presenter.TextLayout.TextLines[lineIndex]; + + var lineText = GetTextLineText(textLine); + + return lineText; + } + } + + public override Rect CursorRectangle + { + get + { + if (_parent == null || _presenter == null) + { + return default; + } + + var transform = _presenter.TransformToVisual(_parent); + + if (transform == null) + { + return default; + } + + return _presenter.GetCursorRectangle().TransformToAABB(transform.Value); + } + } + + public override TextSelection Selection + { + get + { + if (_presenter is null || _parent is null) + { + return default; + } + + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); + + var textLine = _presenter.TextLayout.TextLines[lineIndex]; + + var lineStart = textLine.FirstTextSourceIndex; + + var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart); + + var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart); + + return new TextSelection(selectionStart, selectionEnd); + } + set + { + if (_parent is null || _presenter is null) + { + return; + } + + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); + + var textLine = _presenter.TextLayout.TextLines[lineIndex]; + + var lineStart = textLine.FirstTextSourceIndex; + + var selectionStart = lineStart + value.Start; + var selectionEnd = lineStart + value.End; + + _parent.SelectionStart = selectionStart; + _parent.SelectionEnd = selectionEnd; + + OnSelectionChanged(); + } + } + public override bool SupportsPreedit => true; public override bool SupportsSurroundingText => true; @@ -38,19 +132,19 @@ namespace Avalonia.Controls { oldPresenter.ClearValue(TextPresenter.PreeditTextProperty); - oldPresenter.CaretBoundsChanged -= OnPresenterCursorRectangleChanged; + oldPresenter.CaretBoundsChanged -= (s,e) => OnCursorRectangleChanged(); } _presenter = presenter; if (_presenter != null) { - _presenter.CaretBoundsChanged += OnPresenterCursorRectangleChanged; + _presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged(); } OnTextViewVisualChanged(oldPresenter, presenter); - OnPresenterCursorRectangleChanged(this, EventArgs.Empty); + OnCursorRectangleChanged(); } public override void SetPreeditText(string? preeditText) @@ -63,21 +157,6 @@ namespace Avalonia.Controls _presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText); } - protected override void OnSelectionChanged(TextSelection oldValue, TextSelection newValue) - { - base.OnSelectionChanged(oldValue, newValue); - - if (_isPropertyChange) - { - return; - } - - if (oldValue != newValue) - { - SetParentSelection(newValue); - } - } - private static string GetTextLineText(TextLine textLine) { var builder = StringBuilderCache.Acquire(textLine.Length); @@ -101,112 +180,17 @@ namespace Avalonia.Controls return lineText; } - private void OnParentTextChanged() - { - if (_presenter is null || _parent is null) - { - SurroundingText = ""; - - return; - } - -#if DEBUG - if (_parent.CaretIndex != _presenter.CaretIndex) - { - throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); - } - - if (_parent.Text != _presenter.Text) - { - throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); - } -#endif - - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); - - var textLine = _presenter.TextLayout.TextLines[lineIndex]; - - var lineText = GetTextLineText(textLine); - - SurroundingText = lineText; - } - - private void OnPresenterCursorRectangleChanged(object? sender, EventArgs e) - { - if (_parent == null || _presenter == null) - { - CursorRectangle = default; - - return; - } - - var transform = _presenter.TransformToVisual(_parent); - - if (transform == null) - { - CursorRectangle = default; - - return; - } - - CursorRectangle = _presenter.GetCursorRectangle().TransformToAABB(transform.Value); - } - private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { - _isPropertyChange = true; - if (e.Property == TextBox.TextProperty) { - OnParentTextChanged(); + OnSurroundingTextChanged(); } if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) { - Selection = GetParentSelection(); - } - - _isPropertyChange = false; - } - - private TextSelection GetParentSelection() - { - if (_presenter is null || _parent is null) - { - return default; - } - - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); - - var textLine = _presenter.TextLayout.TextLines[lineIndex]; - - var lineStart = textLine.FirstTextSourceIndex; - - var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart); - - var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart); - - return new TextSelection(selectionStart, selectionEnd); - } - - private void SetParentSelection(TextSelection selection) - { - if (_parent is null || _presenter is null) - { - return; + OnSelectionChanged(); } - - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false); - - var textLine = _presenter.TextLayout.TextLines[lineIndex]; - - var lineStart = textLine.FirstTextSourceIndex; - - var selectionStart = lineStart + selection.Start; - var selectionEnd = lineStart + selection.End; - - _parent.SelectionStart = selectionStart; - _parent.SelectionEnd = selectionEnd; } } } From 86b3b70e5f76bdc84165e0a7fb4cfd67d35b95ef Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 11:09:15 +0200 Subject: [PATCH 188/391] More adjustments --- src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs | 2 +- src/Avalonia.Controls/TextBoxTextInputMethodClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs index 4abd20f911..d633fb2e46 100644 --- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs @@ -59,7 +59,7 @@ namespace Avalonia.Input.TextInput /// public virtual void SetPreeditText(string? preeditText) { } - protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue) + protected virtual void OnTextViewVisualChanged() { TextViewVisualChanged?.Invoke(this, EventArgs.Empty); } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index de4a3c5cf9..9d0fbd1a8c 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -142,7 +142,7 @@ namespace Avalonia.Controls _presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged(); } - OnTextViewVisualChanged(oldPresenter, presenter); + OnTextViewVisualChanged(); OnCursorRectangleChanged(); } From a445f884f6cc59e57d92c6c864c9984388324463 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 11:15:56 +0200 Subject: [PATCH 189/391] Adjust naming --- .../Input/TextInput/TextInputMethodClient.cs | 8 ++++---- .../TextBoxTextInputMethodClient.cs | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs index d633fb2e46..7cf3752057 100644 --- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs @@ -59,22 +59,22 @@ namespace Avalonia.Input.TextInput /// public virtual void SetPreeditText(string? preeditText) { } - protected virtual void OnTextViewVisualChanged() + protected virtual void RaiseTextViewVisualChanged() { TextViewVisualChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnCursorRectangleChanged() + protected virtual void RaiseCursorRectangleChanged() { CursorRectangleChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnSurroundingTextChanged() + protected virtual void RaiseSurroundingTextChanged() { SurroundingTextChanged?.Invoke(this, EventArgs.Empty); } - protected virtual void OnSelectionChanged() + protected virtual void RaiseSelectionChanged() { SelectionChanged?.Invoke(this, EventArgs.Empty); } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 9d0fbd1a8c..3c86471fbb 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -104,7 +104,7 @@ namespace Avalonia.Controls _parent.SelectionStart = selectionStart; _parent.SelectionEnd = selectionEnd; - OnSelectionChanged(); + RaiseSelectionChanged(); } } @@ -132,19 +132,19 @@ namespace Avalonia.Controls { oldPresenter.ClearValue(TextPresenter.PreeditTextProperty); - oldPresenter.CaretBoundsChanged -= (s,e) => OnCursorRectangleChanged(); + oldPresenter.CaretBoundsChanged -= (s,e) => RaiseCursorRectangleChanged(); } _presenter = presenter; if (_presenter != null) { - _presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged(); + _presenter.CaretBoundsChanged += (s, e) => RaiseCursorRectangleChanged(); } - OnTextViewVisualChanged(); + RaiseTextViewVisualChanged(); - OnCursorRectangleChanged(); + RaiseCursorRectangleChanged(); } public override void SetPreeditText(string? preeditText) @@ -184,12 +184,12 @@ namespace Avalonia.Controls { if (e.Property == TextBox.TextProperty) { - OnSurroundingTextChanged(); + RaiseSurroundingTextChanged(); } if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) { - OnSelectionChanged(); + RaiseSelectionChanged(); } } } From cee239b14252a1805525748799c3352836a0652e Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 12:16:40 +0200 Subject: [PATCH 190/391] Android adjustments --- .../Avalonia.Android/AndroidInputMethod.cs | 2 ++ .../Platform/SkiaPlatform/TopLevelImpl.cs | 36 +++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index fc00143a9a..af82c7a791 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -107,6 +107,8 @@ namespace Avalonia.Android var selection = Client.Selection; _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + + _inputConnection.SetSelection(selection.Start, selection.End); } private void _client_SurroundingTextChanged(object sender, EventArgs e) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index f725240c22..e597c26753 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -29,6 +29,7 @@ using Avalonia.Platform.Storage; using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Java.Lang; +using static System.Net.Mime.MediaTypeNames; using ClipboardManager = Android.Content.ClipboardManager; namespace Avalonia.Android.Platform.SkiaPlatform @@ -459,9 +460,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform { var text = tb.SubSequence(0, tb.Length()); - //System.Diagnostics.Debug.WriteLine($"Replace: start: {start}, end: {end}, text: {text}"); - - _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); + SelectSurroundingTextForDeletion(start, end, text); } return base.Replace(start, end, tb); @@ -473,13 +472,18 @@ namespace Avalonia.Android.Platform.SkiaPlatform { var text = tb.SubSequence(tbstart, tbend); - //System.Diagnostics.Debug.WriteLine($"Replace: start: {start}, end: {end}, text: {text}"); - - _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); + SelectSurroundingTextForDeletion(start, end, text); } return base.Replace(start, end, tb, tbstart, tbend); } + + private void SelectSurroundingTextForDeletion(int start, int end, string text) + { + _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); + + //_inputConnection.Toplevel.TextInput(text); + } } internal class AvaloniaInputConnection : BaseInputConnection @@ -507,6 +511,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IAndroidInputMethod InputMethod => _inputMethod; + public TopLevelImpl Toplevel => _toplevel; + public override bool SetComposingText(ICharSequence text, int newCursorPosition) { _compositionText = text.SubSequence(0, text.Length()); @@ -551,6 +557,24 @@ namespace Avalonia.Android.Platform.SkiaPlatform return ret; } + public override bool DeleteSurroundingText(int beforeLength, int afterLength) + { + if (InputMethod.IsActive) + { + EditableWrapper.IgnoreChange = true; + + var selection = InputMethod.Client.Selection; + + InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.Start + afterLength); + + Toplevel.TextInput(""); + + EditableWrapper.IgnoreChange = true; + } + + return base.DeleteSurroundingText(beforeLength, afterLength); + } + public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) { switch (actionCode) From a2eb8942882e857ddd81b753e175b3ba3b4fbcce Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 23 Jun 2023 11:43:53 +0000 Subject: [PATCH 191/391] android - dispatch key event for delete --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index e597c26753..eeb709b22b 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -567,7 +567,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.Start + afterLength); - Toplevel.TextInput(""); + InputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); EditableWrapper.IgnoreChange = true; } From 73ec5a4fc55897fd1bac04e5d5a3362064586664 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 23 Jun 2023 15:48:00 +0000 Subject: [PATCH 192/391] android - set composing text as normal text input --- .../Avalonia.Android/AndroidInputMethod.cs | 2 - .../Platform/SkiaPlatform/TopLevelImpl.cs | 66 ++++++++++++------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index af82c7a791..ea1958efa6 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -124,8 +124,6 @@ namespace Avalonia.Android var selection = Client.Selection; _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); - - //Debug.WriteLine($"SurroundingText: {surroundingText}, CaretIndex: {selection.Start}"); } public void SetCursorRect(Rect rect) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index eeb709b22b..558470c026 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -456,11 +456,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override IEditable Replace(int start, int end, ICharSequence tb) { - if (!IgnoreChange && !_inputConnection.IsComposing && start != end) + if (!IgnoreChange && start != end) { var text = tb.SubSequence(0, tb.Length()); - SelectSurroundingTextForDeletion(start, end, text); + SelectSurroundingTextForDeletion(start, end); } return base.Replace(start, end, tb); @@ -468,21 +468,19 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override IEditable Replace(int start, int end, ICharSequence tb, int tbstart, int tbend) { - if (!IgnoreChange && !_inputConnection.IsComposing && start != end) + if (!IgnoreChange && start != end) { var text = tb.SubSequence(tbstart, tbend); - SelectSurroundingTextForDeletion(start, end, text); + SelectSurroundingTextForDeletion(start, end); } return base.Replace(start, end, tb, tbstart, tbend); } - private void SelectSurroundingTextForDeletion(int start, int end, string text) + private void SelectSurroundingTextForDeletion(int start, int end) { _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); - - //_inputConnection.Toplevel.TextInput(text); } } @@ -491,8 +489,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly TopLevelImpl _toplevel; private readonly IAndroidInputMethod _inputMethod; private readonly EditableWrapper _editable; - private string _compositionText; private bool _commitInProgress; + private (int Start, int End)? _composingRegion; public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) { @@ -501,8 +499,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform _editable = new EditableWrapper(this); } - public bool IsComposing => !string.IsNullOrEmpty(_compositionText); - public int ExtractedTextToken { get; private set; } public override IEditable Editable => _editable; @@ -513,18 +509,34 @@ namespace Avalonia.Android.Platform.SkiaPlatform public TopLevelImpl Toplevel => _toplevel; + public override bool SetComposingRegion(int start, int end) + { + _composingRegion = new(start, end); + + return base.SetComposingRegion(start, end); + } + public override bool SetComposingText(ICharSequence text, int newCursorPosition) { - _compositionText = text.SubSequence(0, text.Length()); + if(_composingRegion != null) + { + // Select the composing region. + InputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End); + } + var compositionText = text.SubSequence(0, text.Length()); - System.Diagnostics.Debug.WriteLine($"Composition Changed: {_compositionText}"); + System.Diagnostics.Debug.WriteLine($"Composition Changed: {compositionText}"); - if(_inputMethod.IsActive && !_commitInProgress) + if (_inputMethod.IsActive && !_commitInProgress) { - _inputMethod.Client.SetPreeditText(_compositionText); + if (string.IsNullOrEmpty(compositionText)) + _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); + + else + _toplevel.TextInput(compositionText); } - return base.SetComposingText(text, newCursorPosition); + return true; } public override bool CommitText(ICharSequence text, int newCursorPosition) @@ -535,21 +547,16 @@ namespace Avalonia.Android.Platform.SkiaPlatform var committedText = text.SubSequence(0, text.Length()); - if (string.IsNullOrEmpty(committedText)) - { - committedText = _compositionText; - } - if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText)) { - if (!string.IsNullOrEmpty(_compositionText)) + if(_composingRegion != null) { - _inputMethod.Client.SetPreeditText(null); + _inputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End); } _toplevel.TextInput(committedText); - _compositionText = null; + _composingRegion = null; } _commitInProgress = false; @@ -557,11 +564,22 @@ namespace Avalonia.Android.Platform.SkiaPlatform return ret; } + public override bool FinishComposingText() + { + _composingRegion = null; + return base.FinishComposingText(); + } + public override bool DeleteSurroundingText(int beforeLength, int afterLength) { if (InputMethod.IsActive) { EditableWrapper.IgnoreChange = true; + } + var result = base.DeleteSurroundingText(beforeLength, afterLength); + + if (InputMethod.IsActive) + { var selection = InputMethod.Client.Selection; @@ -572,7 +590,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform EditableWrapper.IgnoreChange = true; } - return base.DeleteSurroundingText(beforeLength, afterLength); + return result; } public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) From 349d7e9e6763d9d477fc57945f992675b5c480de Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 23 Jun 2023 19:38:08 -0400 Subject: [PATCH 193/391] Fix compat fluent theme not being applied --- .../DensityStyles/Compact.xaml | 13 +++---- src/Avalonia.Themes.Fluent/FluentTheme.xaml | 2 +- .../FluentTheme.xaml.cs | 35 +++++++++---------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml index d335e6f4b9..f6efefa05a 100644 --- a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml +++ b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml @@ -1,10 +1,3 @@ - - - 14 14 24 @@ -18,5 +11,7 @@ 24 12,1,0,3 32 - - + + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml index c84772aa61..f4467e40ce 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml @@ -18,7 +18,7 @@ - + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs index 5af22dbd1d..378041356a 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs @@ -16,9 +16,10 @@ namespace Avalonia.Themes.Fluent /// /// Includes the fluent theme in an application. /// - public class FluentTheme : Styles + public class FluentTheme : Styles, IResourceNode { - private readonly Styles _compactStyles; + private readonly ResourceDictionary _compactStyles; + private DensityStyle _densityStyle; /// /// Initializes a new instance of the class. @@ -28,9 +29,7 @@ namespace Avalonia.Themes.Fluent { AvaloniaXamlLoader.Load(sp, this); - _compactStyles = (Styles)GetAndRemove("CompactStyles"); - - EnsureCompactStyles(); + _compactStyles = (ResourceDictionary)GetAndRemove("CompactStyles"); Palettes = Resources.MergedDictionaries.OfType().FirstOrDefault() ?? throw new InvalidOperationException("FluentTheme was initialized with missing ColorPaletteResourcesCollection."); @@ -43,17 +42,17 @@ namespace Avalonia.Themes.Fluent return val; } } - - public static readonly StyledProperty DensityStyleProperty = - AvaloniaProperty.Register(nameof(DensityStyle)); + + public static readonly DirectProperty DensityStyleProperty = AvaloniaProperty.RegisterDirect( + nameof(DensityStyle), o => o.DensityStyle, (o, v) => o.DensityStyle = v); /// /// Gets or sets the density style of the fluent theme (normal, compact). /// public DensityStyle DensityStyle { - get => GetValue(DensityStyleProperty); - set => SetValue(DensityStyleProperty, value); + get => _densityStyle; + set => SetAndRaise(DensityStyleProperty, ref _densityStyle, value); } public IDictionary Palettes { get; } @@ -64,20 +63,20 @@ namespace Avalonia.Themes.Fluent if (change.Property == DensityStyleProperty) { - EnsureCompactStyles(); + Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); } } - private void EnsureCompactStyles() + bool IResourceNode.TryGetResource(object key, ThemeVariant? theme, out object? value) { - if (DensityStyle == DensityStyle.Compact) + // DensityStyle dictionary should be checked first + if (_densityStyle == DensityStyle.Compact + && _compactStyles.TryGetResource(key, theme, out value)) { - Add(_compactStyles); - } - else - { - Remove(_compactStyles); + return true; } + + return base.TryGetResource(key, theme, out value); } } } From dcd1b59d3101a3d28d47fe3e888371a83b12d6b1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 23 Jun 2023 19:38:55 -0400 Subject: [PATCH 194/391] Make compact resources better --- .../DensityStyles/Compact.xaml | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml index f6efefa05a..830198f6d1 100644 --- a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml +++ b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml @@ -1,17 +1,25 @@ - 14 - 14 - 24 - 2,2,6,1 - 32 - 24 - 0,1,0,2 - 0,1,0,2 - 9,0,0,1 - 10,0,30,0 - 24 - 12,1,0,3 - 32  + 14 + 14 + 24 + 32 + 4, 2 + 4,2 + 24 + 11,5,11,7 + 4 4 8 4 + 5, 2 + 24 + 0,1,0,2 + 0,1,0,2 + 9,0,0,1 + 10,0,30,0 + 24 + 12,1,0,3 + 32 + 28 + 6, 0 + 6,4 From 9b66943dd2d3cab3ecb30319d2ab26c75e778c7d Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Sat, 24 Jun 2023 12:55:29 +0200 Subject: [PATCH 195/391] Use widechar variant for win32 pinvokes --- .../NativeControls/Win/WinApi.cs | 2 +- .../Interop/UnmanagedMethods.cs | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs index 47d368f7a4..bd0c1bf98e 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs @@ -39,7 +39,7 @@ internal unsafe class WinApi [DllImport("user32.dll", SetLastError = true)] public static extern bool DestroyWindow(IntPtr hwnd); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true)] public static extern IntPtr LoadLibrary(string lib); diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 39363beae3..ce42227292 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1161,7 +1161,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW", ExactSpelling = true)] public static extern IntPtr CreateWindowEx( int dwExStyle, uint lpClassName, @@ -1215,16 +1215,16 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern int GetMessageTime(); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandleW", ExactSpelling = true)] public static extern IntPtr GetModuleHandle(string? lpModuleName); [DllImport("user32.dll")] public static extern int GetSystemMetrics(SystemMetric smIndex); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW", ExactSpelling = true)] public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex); - [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLong")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongW", ExactSpelling = true)] public static extern uint GetWindowLong32b(IntPtr hWnd, int nIndex); public static uint GetWindowLong(IntPtr hWnd, int nIndex) @@ -1239,10 +1239,10 @@ namespace Avalonia.Win32.Interop } } - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongW", ExactSpelling = true)] private static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value); - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)] private static extern IntPtr SetWindowLong64b(IntPtr hWnd, int nIndex, IntPtr value); public static uint SetWindowLong(IntPtr hWnd, int nIndex, uint value) @@ -1310,7 +1310,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent); - [DllImport("user32.dll")] + [DllImport("user32.dll", EntryPoint = "LoadCursorW", ExactSpelling = true)] public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName); [DllImport("user32.dll")] @@ -1319,7 +1319,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool DestroyIcon(IntPtr hIcon); - [DllImport("user32.dll")] + [DllImport("user32.dll", EntryPoint = "PeekMessageW", ExactSpelling = true)] public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg); [DllImport("user32")] @@ -1334,7 +1334,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool ReleaseCapture(); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterWindowMessageW", ExactSpelling = true)] public static extern uint RegisterWindowMessage(string lpString); [DllImport("user32.dll")] @@ -1415,7 +1415,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool TranslateMessage(ref MSG lpMsg); - [DllImport("user32.dll", CharSet = CharSet.Unicode)] + [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "UnregisterClassW", ExactSpelling = true)] public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")] @@ -1439,10 +1439,10 @@ namespace Avalonia.Win32.Interop [DllImport("shell32", CharSet = CharSet.Auto)] public static extern int Shell_NotifyIcon(NIM dwMessage, NOTIFYICONDATA lpData); - [DllImport("user32.dll", EntryPoint = "SetClassLongPtr")] + [DllImport("user32.dll", EntryPoint = "SetClassLongPtrW", ExactSpelling = true)] private static extern IntPtr SetClassLong64(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong); - [DllImport("user32.dll", EntryPoint = "SetClassLong")] + [DllImport("user32.dll", EntryPoint = "SetClassLongW", ExactSpelling = true)] private static extern IntPtr SetClassLong32(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong); public static IntPtr SetClassLong(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong) @@ -1463,10 +1463,10 @@ namespace Avalonia.Win32.Interop return new IntPtr(GetClassLongPtr32(hWnd, nIndex)); } - [DllImport("user32.dll", EntryPoint = "GetClassLong")] + [DllImport("user32.dll", EntryPoint = "GetClassLongW", ExactSpelling = true)] public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex); - [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + [DllImport("user32.dll", EntryPoint = "GetClassLongPtrW", ExactSpelling = true)] public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex); [DllImport("user32.dll", EntryPoint = "SetCursor")] @@ -1527,10 +1527,10 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalFree(IntPtr hMem); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true)] public static extern IntPtr LoadLibrary(string fileName); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryExW", ExactSpelling = true)] public static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, int flags); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] @@ -1645,7 +1645,7 @@ namespace Avalonia.Win32.Interop [DllImport("opengl32.dll", CharSet = CharSet.Ansi)] public static extern IntPtr wglGetProcAddress(string name); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateFileMappingW", ExactSpelling = true)] public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, @@ -1668,16 +1668,16 @@ namespace Avalonia.Win32.Interop [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] internal static extern void ReleaseStgMedium(ref STGMEDIUM medium); - [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "GetClipboardFormatNameW", ExactSpelling = true)] public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); - [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "RegisterClipboardFormatW", ExactSpelling = true)] public static extern int RegisterClipboardFormat(string format); [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)] public static extern IntPtr GlobalSize(IntPtr hGlobal); - [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)] + [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, EntryPoint = "DragQueryFileW", ExactSpelling = true)] public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder? lpszFile, int cch); [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)] @@ -1893,7 +1893,7 @@ namespace Avalonia.Win32.Interop [DllImport("imm32.dll")] public static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT lf); - [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode, EntryPoint = "ImmGetCompositionStringW", ExactSpelling = true)] public static extern int ImmGetCompositionString(IntPtr hIMC, GCS dwIndex, [Out, Optional] IntPtr lpBuf, uint dwBufLen); public static string? ImmGetCompositionString(IntPtr hIMC, GCS dwIndex) From 6a3925b91b67e2a3412a7d3e3e11ba24589dfeff Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Sat, 24 Jun 2023 13:49:26 +0200 Subject: [PATCH 196/391] Remove support for undocumented blur #11855 --- .../Interop/UnmanagedMethods.cs | 3 -- src/Windows/Avalonia.Win32/WindowImpl.cs | 53 ++----------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ce42227292..e58abcadf0 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,9 +1827,6 @@ namespace Avalonia.Win32.Interop return result; } - [DllImport("user32.dll")] - internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); - [Flags] public enum GCS : uint { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 057cdb2db0..3fc2751e43 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -371,7 +371,7 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) SetTransparencyTransparent(windowsVersion); else if (level == WindowTransparencyLevel.Blur) - SetTransparencyBlur(windowsVersion); + continue; // Unsupported on all versions (unreachable) else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); else if (level == WindowTransparencyLevel.Mica) @@ -409,9 +409,9 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) return windowsVersion >= PlatformConstants.Windows8; - // Blur only supported on Windows 8 and lower. + // Support for legacy, undocumented DWM blur has been removed. if (level == WindowTransparencyLevel.Blur) - return windowsVersion < PlatformConstants.Windows10; + return false; // Acrylic is supported on Windows >= 10.0.15063. if (level == WindowTransparencyLevel.AcrylicBlur) @@ -426,37 +426,14 @@ namespace Avalonia.Win32 private void SetTransparencyTransparent(Version windowsVersion) { - // Transparent only supported with composition on Windows 8+. - if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8) + // Transparent only supported with composition on Windows 10+. + if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows10) return; - if (windowsVersion < PlatformConstants.Windows10) - { - // Some of the AccentState Enum's values have different meanings on Windows 8.x than on - // Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯. - // Hey, I'm just porting what was here before. - SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND); - var blurInfo = new DWM_BLURBEHIND(false); - DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); - } - SetUseHostBackdropBrush(false); _blurHost?.SetBlur(BlurEffect.None); } - private void SetTransparencyBlur(Version windowsVersion) - { - // Blur only supported with composition on Windows 8 and lower. - if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10) - return; - - // Some of the AccentState Enum's values have different meanings on Windows 8.x than on - // Windows 10. - SetAccentState(AccentState.ACCENT_DISABLED); - var blurInfo = new DWM_BLURBEHIND(true); - DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); - } - private void SetTransparencyAcrylicBlur(Version windowsVersion) { // Acrylic blur only supported with composition on Windows >= 10.0.15063. @@ -477,26 +454,6 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Mica); } - private void SetAccentState(AccentState state) - { - var accent = new AccentPolicy(); - var accentStructSize = Marshal.SizeOf(accent); - - //Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10 - accent.AccentState = state; - - var accentPtr = Marshal.AllocHGlobal(accentStructSize); - Marshal.StructureToPtr(accent, accentPtr, false); - - var data = new WindowCompositionAttributeData(); - data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; - data.SizeOfData = accentStructSize; - data.Data = accentPtr; - - SetWindowCompositionAttribute(_hwnd, ref data); - Marshal.FreeHGlobal(accentPtr); - } - private void SetUseHostBackdropBrush(bool useHostBackdropBrush) { if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion) From 29d3c7670be216f704f001ee6e28efc0adbe2e83 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 25 Jun 2023 23:26:45 -0400 Subject: [PATCH 197/391] Restructure Win32PlatformOptions options --- samples/ControlCatalog.NetCore/Program.cs | 3 +- src/Windows/Avalonia.Win32/Win32GlManager.cs | 37 ++++-- src/Windows/Avalonia.Win32/Win32Platform.cs | 74 ------------ .../Avalonia.Win32/Win32PlatformOptions.cs | 114 ++++++++++++++++++ .../WinUiCompositedWindowSurface.cs | 2 +- .../Composition/WinUiCompositionShared.cs | 1 + .../Composition/WinUiCompositorConnection.cs | 31 ++--- 7 files changed, 160 insertions(+), 102 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/Win32PlatformOptions.cs diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 5249a4fb41..99d507c817 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -110,8 +110,7 @@ namespace ControlCatalog.NetCore { builder.With(new Win32PlatformOptions() { - UseLowLatencyDxgiSwapChain = true, - UseWindowsUIComposition = false + CompositionMode = Win32CompositionMode.LowLatencyDxgiSwapChain }); return builder.StartWithClassicDesktopLifetime(args); } diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index c26ce5fd45..49eb369149 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Win32.DirectX; @@ -23,15 +24,19 @@ namespace Avalonia.Win32 private static IPlatformGraphics? InitializeCore() { - var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); - if (opts.UseWgl) + + var winVersion = Win32Platform.WindowsVersion; + var renderingMode = opts.RenderingMode ?? + (winVersion > PlatformConstants.Windows7 ? Win32RenderingMode.AngleEgl : Win32RenderingMode.Software); + + if (renderingMode == Win32RenderingMode.Wgl) { var wgl = WglPlatformOpenGlInterface.TryCreate(); return wgl; } - if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7) + if (renderingMode == Win32RenderingMode.AngleEgl) { var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ?? new()); @@ -40,14 +45,26 @@ namespace Avalonia.Win32 { AvaloniaLocator.CurrentMutable.Bind() .ToConstant(egl); - - if (opts.UseWindowsUIComposition) - { - WinUiCompositorConnection.TryCreateAndRegister(); - } - else if (opts.UseLowLatencyDxgiSwapChain) + + var compositionMode = opts.CompositionMode ?? + (WinUiCompositorConnection.IsSupported() ? Win32CompositionMode.WinUIComposition + //: DirectCompositionConnection.IsSupported() ? Win32CompositionMode.DirectComposition + : Win32CompositionMode.RedirectionSurface); + + switch (compositionMode) { - DxgiConnection.TryCreateAndRegister(); + case Win32CompositionMode.WinUIComposition: + if (!WinUiCompositorConnection.TryCreateAndRegister()) + { + //goto case Win32CompositionMode.DirectComposition; + } + break; + //case Win32CompositionMode.DirectComposition: + //DirectCompositionConnection.TryCreateAndRegister(); + //break; + case Win32CompositionMode.LowLatencyDxgiSwapChain: + DxgiConnection.TryCreateAndRegister(); + break; } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index e9019803be..aaf6fed624 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -10,7 +9,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; -using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.Composition; @@ -31,78 +29,6 @@ namespace Avalonia "Win32"); } } - - /// - /// Platform-specific options which apply to Windows. - /// - public class Win32PlatformOptions - { - /// - /// Enables ANGLE for Windows. For every Windows version that is above Windows 7, the default is true otherwise it's false. - /// - /// - /// GPU rendering will not be enabled if this is set to false. - /// - public bool? AllowEglInitialization { get; set; } - - /// - /// Embeds popups to the window when set to true. The default value is false. - /// - public bool OverlayPopups { get; set; } - - /// - /// Avalonia would try to use native Widows OpenGL when set to true. The default value is false. - /// - public bool UseWgl { get; set; } - - public IList WglProfiles { get; set; } = new List - { - new GlVersion(GlProfileType.OpenGL, 4, 0), - new GlVersion(GlProfileType.OpenGL, 3, 2), - }; - - /// - /// Render Avalonia to a Texture inside the Windows.UI.Composition tree. - /// This setting is true by default. - /// - /// - /// Supported on Windows 10 build 16299 and above. Ignored on other versions. - /// This is recommended if you need to use AcrylicBlur or acrylic in your applications. - /// - public bool UseWindowsUIComposition { get; set; } = true; - - /// - /// When enabled, create rounded corner blur brushes - /// If set to null the brushes will be created using default settings (sharp corners) - /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app - /// - public float? CompositionBackdropCornerRadius { get; set; } - - /// - /// When is active, renders Avalonia through a low-latency Dxgi Swapchain. - /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion. - /// This is only recommended if low input latency is desirable, and there is no need for the transparency - /// and stylings / blurrings offered by
- /// This is mutually exclusive with - /// which if active will override this setting. - /// This setting is false by default. - ///
- public bool UseLowLatencyDxgiSwapChain { get; set; } - - /// - /// Render directly on the UI thread instead of using a dedicated render thread. - /// Only applicable if both and - /// are false. - /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF. - /// This setting is false by default. - /// - public bool ShouldRenderOnUIThread { get; set; } - - /// - /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu - /// - public IPlatformGraphics? CustomPlatformGraphics { get; set; } - } } namespace Avalonia.Win32 diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs new file mode 100644 index 0000000000..aaebedb8c5 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using Avalonia.OpenGL; +using Avalonia.Platform; + +namespace Avalonia; + +public enum Win32RenderingMode +{ + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 1, + + /// + /// Enables ANGLE EGL for Windows with GPU rendering. + /// + AngleEgl = 2, + + /// + /// Avalonia would try to use native Widows OpenGL with GPU rendering. + /// + Wgl +} + +public enum Win32CompositionMode +{ + /// + /// Render Avalonia to a texture inside the Windows.UI.Composition tree. + /// + /// + /// Supported on Windows 10 build 17134 and above. Ignored on other versions. + /// This is recommended option, as it allows window acrylic effects and high refresh rate rendering. + /// + WinUIComposition = 1, + + // /// + // /// Render Avalonia to a texture inside the DirectComposition tree. + // /// + // /// + // /// Supported on Windows 8 and above. Ignored on other versions. + // /// + // DirectComposition = 2, + + /// + /// When is active, renders Avalonia through a low-latency Dxgi Swapchain. + /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion. + /// This is only recommended if low input latency is desirable, and there is no need for the transparency + /// and styling / blurring offered by
. + ///
+ LowLatencyDxgiSwapChain = 3, + + /// + /// The window renders to a redirection surface. + /// + /// + /// This option is kept only for compatibility with older systems. Some Avalonia features might not work. + /// + RedirectionSurface, +} + +/// +/// Platform-specific options which apply to Windows. +/// +public class Win32PlatformOptions +{ + /// + /// Embeds popups to the window when set to true. The default value is false. + /// + public bool OverlayPopups { get; set; } + + /// + /// Avalonia window rendering mode. + /// On Windows 8 and newer default value is , + /// otherwise. + /// + public Win32RenderingMode? RenderingMode { get; set; } + + /// + /// Avalonia window composition mode. + /// On Windows 8 and newer default value is , + /// otherwise. + /// + public Win32CompositionMode? CompositionMode { get; set; } + + /// + /// When is set to , create rounded corner blur brushes + /// If set to null the brushes will be created using default settings (sharp corners) + /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app. + /// + public float? WinUICompositionBackdropCornerRadius { get; set; } + + /// + /// Render directly on the UI thread instead of using a dedicated render thread. + /// Only applicable if is set to . + /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF. + /// This setting is false by default. + /// + public bool ShouldRenderOnUIThread { get; set; } + + /// + /// Windows OpenGL profiles used when is set to . + /// This setting is 4.0 and 3.2 by default. + /// + public IList WglProfiles { get; set; } = new List + { + new(GlProfileType.OpenGL, 4, 0), new(GlProfileType.OpenGL, 3, 2) + }; + + /// + /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu. + /// When this property set and are completely ignored. + /// + public IPlatformGraphics? CustomPlatformGraphics { get; set; } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs index 95ef338f08..8da154dc83 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs @@ -23,7 +23,7 @@ namespace Avalonia.Win32.WinRT.Composition public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) { var cornerRadius = AvaloniaLocator.Current.GetService() - ?.CompositionBackdropCornerRadius; + ?.WinUICompositionBackdropCornerRadius; _window ??= new WinUiCompositedWindow(_info, _shared, cornerRadius); _window.SetBlur(_blurEffect); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs index f17805fba3..b3a328d097 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs @@ -12,6 +12,7 @@ internal class WinUiCompositionShared : IDisposable public ICompositionBrush? MicaBrush { get; } public object SyncRoot { get; } = new(); + public static readonly Version MinWinCompositionVersion = new(10, 0, 17134); public static readonly Version MinAcrylicVersion = new(10, 0, 15063); public static readonly Version MinHostBackdropVersion = new(10, 0, 22000); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs index 754af86c06..3e5384715c 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs @@ -112,35 +112,36 @@ internal class WinUiCompositorConnection : IRenderTimer } } - public static void TryCreateAndRegister() + public static bool IsSupported() { - const int majorRequired = 10; - const int buildRequired = 17134; - - var majorInstalled = Win32Platform.WindowsVersion.Major; - var buildInstalled = Win32Platform.WindowsVersion.Build; - - if (majorInstalled >= majorRequired && - buildInstalled >= buildRequired) + return Win32Platform.WindowsVersion >= WinUiCompositionShared.MinWinCompositionVersion; + } + + public static bool TryCreateAndRegister() + { + if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinWinCompositionVersion) { try { TryCreateAndRegisterCore(); - return; + return true; } catch (Exception e) { Logger.TryGet(LogEventLevel.Error, "WinUIComposition") ?.Log(null, "Unable to initialize WinUI compositor: {0}", e); - } } + else + { + var osVersionNotice = + $"Windows {WinUiCompositionShared.MinWinCompositionVersion} is required. Your machine has Windows {Win32Platform.WindowsVersion} installed."; - var osVersionNotice = - $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed."; + Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null, + $"Unable to initialize WinUI compositor: {osVersionNotice}"); + } - Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null, - $"Unable to initialize WinUI compositor: {osVersionNotice}"); + return false; } public WinUiCompositedWindowSurface CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new(_shared, info); From effb12f9e04343437f2e2ac172c9951881f2bbfa Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 25 Jun 2023 23:43:07 -0400 Subject: [PATCH 198/391] Throw an exception when CustomPlatformGraphics is used with unsupported Win32CompositionMode --- src/Windows/Avalonia.Win32/Win32Platform.cs | 21 ++++++++++++++++--- .../Avalonia.Win32/Win32PlatformOptions.cs | 5 +++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index aaf6fed624..5082c76d0c 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -99,9 +99,24 @@ namespace Avalonia.Win32 .Bind().ToConstant(NonPumpingWaitHelperImpl.Instance) .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()) .Bind().ToConstant(s_instance); - - var platformGraphics = options.CustomPlatformGraphics - ?? Win32GlManager.Initialize(); + + IPlatformGraphics? platformGraphics; + if (options.CustomPlatformGraphics is not null) + { + if (options.CompositionMode.HasValue + && options.CompositionMode != Win32CompositionMode.RedirectionSurface) + { + throw new InvalidOperationException( + $"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CustomPlatformGraphics)} is only " + + $"compatible with {nameof(Win32CompositionMode)}.{nameof(Win32CompositionMode.RedirectionSurface)}"); + } + + platformGraphics = options.CustomPlatformGraphics; + } + else + { + platformGraphics = Win32GlManager.Initialize(); + } if (OleContext.Current != null) AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs index aaebedb8c5..3c7e32f774 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -91,7 +91,7 @@ public class Win32PlatformOptions /// /// Render directly on the UI thread instead of using a dedicated render thread. - /// Only applicable if is set to . + /// Only applicable if is set to . /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF. /// This setting is false by default. /// @@ -108,7 +108,8 @@ public class Win32PlatformOptions /// /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu. - /// When this property set and are completely ignored. + /// When this property set is ignored + /// and only accepts null or . /// public IPlatformGraphics? CustomPlatformGraphics { get; set; } } From 33f70d9fca7912510ba0d6708cabc19e5c15f50a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 25 Jun 2023 21:18:55 -0700 Subject: [PATCH 199/391] Update src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs Co-authored-by: jp2masa --- .../WinRT/Composition/WinUiCompositorConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs index 3e5384715c..596c94d30b 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs @@ -119,7 +119,7 @@ internal class WinUiCompositorConnection : IRenderTimer public static bool TryCreateAndRegister() { - if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinWinCompositionVersion) + if (IsSupported()) { try { From 692fd5de60d963f98173b4b97501b745e4589b24 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 26 Jun 2023 08:35:41 +0000 Subject: [PATCH 200/391] use current ime selection for deletion --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 558470c026..ecf0fd1225 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -491,6 +491,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly EditableWrapper _editable; private bool _commitInProgress; private (int Start, int End)? _composingRegion; + private TextSelection _selection; public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) { @@ -580,10 +581,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform if (InputMethod.IsActive) { + var selection = _selection; - var selection = InputMethod.Client.Selection; - - InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.Start + afterLength); + InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.End + afterLength); InputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); @@ -593,6 +593,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform return result; } + public override bool SetSelection(int start, int end) + { + _selection = new TextSelection(start, end); + return base.SetSelection(start, end); + } + public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) { switch (actionCode) From c7d77b15452ac6566b26613a945578da7dd76b8b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 26 Jun 2023 11:03:59 +0200 Subject: [PATCH 201/391] Use SetCurrentValue --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index e3949a3255..43c4e76aba 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -853,7 +853,7 @@ namespace Avalonia.Controls.Presenters { if (!string.IsNullOrEmpty(PreeditText)) { - PreeditText = null; + SetCurrentValue(PreeditTextProperty, null); } } @@ -861,7 +861,7 @@ namespace Avalonia.Controls.Presenters { if (!string.IsNullOrEmpty(PreeditText)) { - PreeditText = null; + SetCurrentValue(PreeditTextProperty, null); } } From 5fb1cf9f829c437befd163ea9ab6b5657e03e1af Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Mon, 26 Jun 2023 11:43:44 +0200 Subject: [PATCH 202/391] Fix DataGridColumn.CellStyleClasses not settable from xaml --- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index d28c0969c4..a5695afeb7 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -38,7 +38,7 @@ namespace Avalonia.Controls private ICellEditBinding _editBinding; private IBinding _clipboardContentBinding; private ControlTheme _cellTheme; - private readonly Classes _cellStyleClasses = new Classes(); + private Classes _cellStyleClasses; /// /// Initializes a new instance of the class. @@ -393,17 +393,7 @@ namespace Avalonia.Controls } } - public Classes CellStyleClasses - { - get => _cellStyleClasses; - set - { - if(_cellStyleClasses != value) - { - _cellStyleClasses.Replace(value); - } - } - } + public Classes CellStyleClasses => _cellStyleClasses ??= new(); /// /// Backing field for CellTheme property. From 1acbc8c36e8dddd07ec42af87b4323c9f5292f8a Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 26 Jun 2023 11:31:55 +0000 Subject: [PATCH 203/391] use ime selection in get extracted text --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index ecf0fd1225..d6c2ab7314 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -632,7 +632,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform return null; } - var selection = _inputMethod.Client.Selection; + var selection = _selection; ExtractedText extract = new ExtractedText { From a390f9f50cbf1db5d670b23243ec6328c3e90658 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 26 Jun 2023 23:18:34 -0400 Subject: [PATCH 204/391] Make RenderingMode and CompositionMode lists with fallbacks --- samples/ControlCatalog.NetCore/Program.cs | 2 +- .../Avalonia.Win32/DirectX/DxgiConnection.cs | 4 +- src/Windows/Avalonia.Win32/Win32GlManager.cs | 112 ++++++++++-------- src/Windows/Avalonia.Win32/Win32Platform.cs | 4 +- .../Avalonia.Win32/Win32PlatformOptions.cs | 43 +++++-- 5 files changed, 102 insertions(+), 63 deletions(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 99d507c817..5e3e301461 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -110,7 +110,7 @@ namespace ControlCatalog.NetCore { builder.With(new Win32PlatformOptions() { - CompositionMode = Win32CompositionMode.LowLatencyDxgiSwapChain + CompositionMode = new [] { Win32CompositionMode.LowLatencyDxgiSwapChain } }); return builder.StartWithClassicDesktopLifetime(args); } diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs index 07fb3169cb..bac2a73dd9 100644 --- a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs +++ b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs @@ -28,16 +28,18 @@ namespace Avalonia.Win32.DirectX _syncLock = syncLock; } - public static void TryCreateAndRegister() + public static bool TryCreateAndRegister() { try { TryCreateAndRegisterCore(); + return true; } catch (Exception ex) { Logger.TryGet(LogEventLevel.Error, LogArea) ?.Log(null, "Unable to establish Dxgi: {0}", ex); + return false; } } diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index 49eb369149..b3760a37e1 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.Tracing; +using System.Linq; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Win32.DirectX; @@ -6,72 +8,88 @@ using Avalonia.Win32.OpenGl; using Avalonia.Win32.OpenGl.Angle; using Avalonia.Win32.WinRT.Composition; -namespace Avalonia.Win32 +namespace Avalonia.Win32; + +static class Win32GlManager { - static class Win32GlManager + public static IPlatformGraphics? Initialize() { - public static IPlatformGraphics? Initialize() - { - var gl = InitializeCore(); - - if (gl is not null) - { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); - } + var gl = InitializeCore(); - return gl; + if (gl is not null) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); } + + return gl; + } - private static IPlatformGraphics? InitializeCore() + private static IPlatformGraphics? InitializeCore() + { + var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); + if (opts.RenderingMode is null || !opts.RenderingMode.Any()) { - var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); + throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} must not be empty or null"); + } - var winVersion = Win32Platform.WindowsVersion; - var renderingMode = opts.RenderingMode ?? - (winVersion > PlatformConstants.Windows7 ? Win32RenderingMode.AngleEgl : Win32RenderingMode.Software); - - if (renderingMode == Win32RenderingMode.Wgl) + foreach (var renderingMode in opts.RenderingMode) + { + if (renderingMode == Win32RenderingMode.Software) { - var wgl = WglPlatformOpenGlInterface.TryCreate(); - return wgl; + return null; } - + if (renderingMode == Win32RenderingMode.AngleEgl) { - var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ?? - new()); + var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ?? new()); if (egl != null && egl.PlatformApi == AngleOptions.PlatformApi.DirectX11) { - AvaloniaLocator.CurrentMutable.Bind() - .ToConstant(egl); - - var compositionMode = opts.CompositionMode ?? - (WinUiCompositorConnection.IsSupported() ? Win32CompositionMode.WinUIComposition - //: DirectCompositionConnection.IsSupported() ? Win32CompositionMode.DirectComposition - : Win32CompositionMode.RedirectionSurface); + TryRegisterComposition(opts); + return egl; + } + } - switch (compositionMode) - { - case Win32CompositionMode.WinUIComposition: - if (!WinUiCompositorConnection.TryCreateAndRegister()) - { - //goto case Win32CompositionMode.DirectComposition; - } - break; - //case Win32CompositionMode.DirectComposition: - //DirectCompositionConnection.TryCreateAndRegister(); - //break; - case Win32CompositionMode.LowLatencyDxgiSwapChain: - DxgiConnection.TryCreateAndRegister(); - break; - } + if (renderingMode == Win32RenderingMode.Wgl) + { + if (WglPlatformOpenGlInterface.TryCreate() is { } wgl) + { + return wgl; } + } + } + + throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); + } + + private static void TryRegisterComposition(Win32PlatformOptions opts) + { + if (opts.CompositionMode is null || !opts.CompositionMode.Any()) + { + throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} must not be empty or null"); + } - return egl; + foreach (var compositionMode in opts.CompositionMode) + { + if (compositionMode == Win32CompositionMode.RedirectionSurface) + { + return; } - return null; + if (compositionMode == Win32CompositionMode.WinUIComposition + && WinUiCompositorConnection.IsSupported() + && WinUiCompositorConnection.TryCreateAndRegister()) + { + return; + } + + if (compositionMode == Win32CompositionMode.LowLatencyDxgiSwapChain + && DxgiConnection.TryCreateAndRegister()) + { + return; + } } + + throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} has a value of \"{string.Join(", ", opts.CompositionMode)}\", but no options were applied."); } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 5082c76d0c..6e727ba4d4 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using Avalonia.Reactive; using System.Runtime.InteropServices; using System.Threading; @@ -103,8 +104,7 @@ namespace Avalonia.Win32 IPlatformGraphics? platformGraphics; if (options.CustomPlatformGraphics is not null) { - if (options.CompositionMode.HasValue - && options.CompositionMode != Win32CompositionMode.RedirectionSurface) + if (options.CompositionMode?.Contains(Win32CompositionMode.RedirectionSurface) == false) { throw new InvalidOperationException( $"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CustomPlatformGraphics)} is only " + diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs index 3c7e32f774..9eeb36b7d1 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -29,7 +29,8 @@ public enum Win32CompositionMode /// /// /// Supported on Windows 10 build 17134 and above. Ignored on other versions. - /// This is recommended option, as it allows window acrylic effects and high refresh rate rendering. + /// This is recommended option, as it allows window acrylic effects and high refresh rate rendering.
+ /// Can only be applied with =. ///
WinUIComposition = 1, @@ -37,16 +38,20 @@ public enum Win32CompositionMode // /// Render Avalonia to a texture inside the DirectComposition tree. // ///
// /// - // /// Supported on Windows 8 and above. Ignored on other versions. + // /// Supported on Windows 8 and above. Ignored on other versions.
+ // /// Can only be applied with =. // ///
// DirectComposition = 2, /// /// When is active, renders Avalonia through a low-latency Dxgi Swapchain. + /// + /// /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion. /// This is only recommended if low input latency is desirable, and there is no need for the transparency - /// and styling / blurring offered by
. - ///
+ /// and styling / blurring offered by .
+ /// Can only be applied with =. + /// LowLatencyDxgiSwapChain = 3, /// @@ -69,18 +74,32 @@ public class Win32PlatformOptions public bool OverlayPopups { get; set; } /// - /// Avalonia window rendering mode. - /// On Windows 8 and newer default value is , - /// otherwise. + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . /// - public Win32RenderingMode? RenderingMode { get; set; } + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + Win32RenderingMode.AngleEgl, Win32RenderingMode.Software + }; /// - /// Avalonia window composition mode. - /// On Windows 8 and newer default value is , - /// otherwise. + /// Gets or sets Avalonia composition modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . /// - public Win32CompositionMode? CompositionMode { get; set; } + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList CompositionMode { get; set; } = new[] + { + Win32CompositionMode.WinUIComposition, Win32CompositionMode.RedirectionSurface + }; /// /// When is set to , create rounded corner blur brushes From 6a50fc5a92648c8975d307b76be0adca4b5981b8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 26 Jun 2023 23:43:33 -0400 Subject: [PATCH 205/391] Replace UseGpu and UseEgl parameters with consistent RenderingMode enum on every platform --- .../Avalonia.Android/AndroidPlatform.cs | 71 ++++++++++++++-- .../Egl/EglPlatformGraphics.cs | 7 -- src/Avalonia.X11/Glx/GlxPlatformFeature.cs | 12 --- src/Avalonia.X11/X11Platform.cs | 82 +++++++++++++++---- .../Avalonia.Win32/Win32PlatformOptions.cs | 2 +- 5 files changed, 130 insertions(+), 44 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index d5d5f211e9..6ea067d8b7 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Avalonia.Controls; using Avalonia.Android; using Avalonia.Android.Platform; @@ -22,6 +24,36 @@ namespace Avalonia .UseSkia(); } } + + public enum AndroidRenderingMode + { + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 1, + + /// + /// Enables android EGL rendering. + /// + Egl = 2 + } + + public sealed class AndroidPlatformOptions + { + /// + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . + /// + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + AndroidRenderingMode.Egl, AndroidRenderingMode.Software + }; + } } namespace Avalonia.Android @@ -47,18 +79,39 @@ namespace Avalonia.Android .Bind().ToConstant(new ChoreographerTimer()) .Bind().ToSingleton(); - if (Options.UseGpu) + var graphics = InitializeGraphics(Options); + if (graphics is not null) { - EglPlatformGraphics.TryInitialize(); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics); } - - Compositor = new Compositor(AvaloniaLocator.Current.GetService()); + + Compositor = new Compositor(graphics); } - } + + private static IPlatformGraphics InitializeGraphics(AndroidPlatformOptions opts) + { + if (opts.RenderingMode is null || !opts.RenderingMode.Any()) + { + throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} must not be empty or null"); + } - public sealed class AndroidPlatformOptions - { - public bool UseDeferredRendering { get; set; } = false; - public bool UseGpu { get; set; } = true; + foreach (var renderingMode in opts.RenderingMode) + { + if (renderingMode == AndroidRenderingMode.Software) + { + return null; + } + + if (renderingMode == AndroidRenderingMode.Egl) + { + if (EglPlatformGraphics.TryCreate() is { } egl) + { + return egl; + } + } + } + + throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); + } } } diff --git a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs index 07e304febe..cf81999095 100644 --- a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs +++ b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs @@ -16,13 +16,6 @@ namespace Avalonia.OpenGL.Egl _display = display; } - public static void TryInitialize() - { - var feature = TryCreate(); - if (feature != null) - AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); - } - public static EglPlatformGraphics? TryCreate() => TryCreate(() => new EglDisplay(new EglDisplayCreationOptions { Egl = new EglInterface(), diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs index 06766e0963..58336522c1 100644 --- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs +++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs @@ -15,18 +15,6 @@ namespace Avalonia.X11.Glx IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext(); public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); - - public static bool TryInitialize(X11Info x11, IList glProfiles) - { - var feature = TryCreate(x11, glProfiles); - if (feature != null) - { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); - return true; - } - - return false; - } public static GlxPlatformGraphics TryCreate(X11Info x11, IList glProfiles) { diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index a880e4ba1a..7324481aaa 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -93,17 +93,13 @@ namespace Avalonia.X11 XI2 = xi2; } - if (options.UseGpu) + var graphics = InitializeGraphics(options, Info); + if (graphics is not null) { - if (options.UseEGL) - EglPlatformGraphics.TryInitialize(); - else - GlxPlatformGraphics.TryInitialize(Info, Options.GlProfiles); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics); } - var gl = AvaloniaLocator.Current.GetService(); - - Compositor = new Compositor(gl); + Compositor = new Compositor(graphics); } public IntPtr DeferredDisplay { get; set; } @@ -185,25 +181,81 @@ namespace Avalonia.X11 return false; } + + private static IPlatformGraphics InitializeGraphics(X11PlatformOptions opts, X11Info info) + { + if (opts.RenderingMode is null || !opts.RenderingMode.Any()) + { + throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} must not be empty or null"); + } + + foreach (var renderingMode in opts.RenderingMode) + { + if (renderingMode == X11RenderingMode.Software) + { + return null; + } + + if (renderingMode == X11RenderingMode.Glx) + { + if (GlxPlatformGraphics.TryCreate(info, opts.GlProfiles) is { } glx) + { + return glx; + } + } + + if (renderingMode == X11RenderingMode.Egl) + { + if (EglPlatformGraphics.TryCreate() is { } egl) + { + return egl; + } + } + } + + throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); + } } } namespace Avalonia { + public enum X11RenderingMode + { + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 1, + + /// + /// Enables Glx rendering. + /// + Glx = 2, + + /// + /// Enables native Linux EGL rendering. + /// + Egl = 3 + } + /// /// Platform-specific options which apply to Linux. /// public class X11PlatformOptions { /// - /// Enables native Linux EGL when set to true. The default value is false. + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . /// - public bool UseEGL { get; set; } - - /// - /// Determines whether to use GPU for rendering in your project. The default value is true. - /// - public bool UseGpu { get; set; } = true; + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + X11RenderingMode.Glx, X11RenderingMode.Software + }; /// /// Embeds popups to the window when set to true. The default value is false. diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs index 9eeb36b7d1..f4765a558f 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -19,7 +19,7 @@ public enum Win32RenderingMode /// /// Avalonia would try to use native Widows OpenGL with GPU rendering. /// - Wgl + Wgl = 3 } public enum Win32CompositionMode From d42a40fda7d47d6f88e9819068626f8c2f54945c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 26 Jun 2023 23:43:50 -0400 Subject: [PATCH 206/391] Remove AvaloniaNativePlatformOptions.UseGpu as it's not supported in 11.0 (always uses GPU) --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 24 +++++++++---------- .../AvaloniaNativePlatformExtensions.cs | 6 +---- src/Avalonia.Native/WindowImpl.cs | 2 +- src/Avalonia.Native/WindowImplBase.cs | 5 ++-- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 5d5e17839f..03f0129200 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -123,20 +123,18 @@ namespace Avalonia.Native hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); AvaloniaLocator.CurrentMutable.Bind().ToConstant(hotkeys); - - if (_options.UseGpu) + + // TODO: add software and metal support via RenderingMode options param + try + { + _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(_platformGl); + + } + catch (Exception) { - try - { - _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(_platformGl); - - } - catch (Exception) - { - // ignored - } + // ignored } Compositor = new Compositor(_platformGl, true); diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 2b989ce733..7e01e03c9c 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Native; @@ -30,11 +31,6 @@ namespace Avalonia /// public class AvaloniaNativePlatformOptions { - /// - /// Determines whether to use GPU for rendering in your project. The default value is true. - /// - public bool UseGpu { get; set; } = true; - /// /// Embeds popups to the window when set to true. The default value is false. /// diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 817fe3d080..ba96dd401a 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -24,7 +24,7 @@ namespace Avalonia.Native private bool _canResize = true; internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, - AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature) + AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, glFeature) { _opts = opts; _glFeature = glFeature; diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 760816643e..053957f89f 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -67,11 +67,10 @@ namespace Avalonia.Native private PlatformBehaviorInhibition _platformBehaviorInhibition; private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; - internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, - AvaloniaNativeGlPlatformGraphics glFeature) + internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativeGlPlatformGraphics glFeature) { _factory = factory; - _gpu = opts.UseGpu && glFeature != null; + _gpu = glFeature != null; _keyboard = AvaloniaLocator.Current.GetService(); _mouse = new MouseDevice(); From 290fd5caf38b140c976bcc2cd20edf7cffb9a9d7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 26 Jun 2023 23:46:33 -0400 Subject: [PATCH 207/391] Add missing docs --- src/Android/Avalonia.Android/AndroidPlatform.cs | 3 +++ src/Avalonia.X11/X11Platform.cs | 3 +++ src/Windows/Avalonia.Win32/Win32PlatformOptions.cs | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 6ea067d8b7..144909db4b 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -25,6 +25,9 @@ namespace Avalonia } } + /// + /// Represents the rendering mode for platform graphics. + /// public enum AndroidRenderingMode { /// diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 7324481aaa..c70c17d523 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -220,6 +220,9 @@ namespace Avalonia.X11 namespace Avalonia { + /// + /// Represents the rendering mode for platform graphics. + /// public enum X11RenderingMode { /// diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs index f4765a558f..bbb4c37d7e 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -4,6 +4,9 @@ using Avalonia.Platform; namespace Avalonia; +/// +/// Represents the rendering mode for platform graphics. +/// public enum Win32RenderingMode { /// @@ -22,6 +25,9 @@ public enum Win32RenderingMode Wgl = 3 } +/// +/// Represents the Win32 window composition mode. +/// public enum Win32CompositionMode { /// From e5098cfd6211672cbc2841056bd1b50ea4136af5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 00:00:21 -0400 Subject: [PATCH 208/391] Fix build --- src/Avalonia.Native/PopupImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 6b7f7e8883..6cef7ea578 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -15,7 +15,7 @@ namespace Avalonia.Native public PopupImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, AvaloniaNativeGlPlatformGraphics glFeature, - IWindowBaseImpl parent) : base(factory, opts, glFeature) + IWindowBaseImpl parent) : base(factory, glFeature) { _opts = opts; _glFeature = glFeature; From 862c175725bfb3978a32d6d03e94b2a8b1f9308c Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 27 Jun 2023 12:27:30 +0200 Subject: [PATCH 209/391] Fix ScrollContentPresenter's child margin with layout rounding --- .../Presenters/ScrollContentPresenter.cs | 12 +++++-- .../Presenters/ScrollContentPresenterTests.cs | 31 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index bd694b200c..bd3d2b5171 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -7,7 +7,7 @@ using Avalonia.Input.GestureRecognizers; using Avalonia.Utilities; using Avalonia.VisualTree; using System.Linq; -using Avalonia.Interactivity; +using Avalonia.Layout; namespace Avalonia.Controls.Presenters { @@ -473,7 +473,15 @@ namespace Avalonia.Controls.Presenters } Viewport = finalSize; - Extent = Child!.Bounds.Size.Inflate(Child.Margin); + + var childMargin = Child!.Margin; + if (Child.UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(Child); + childMargin = LayoutHelper.RoundLayoutThickness(childMargin, scale, scale); + } + + Extent = Child!.Bounds.Size.Inflate(childMargin); _isAnchorElementDirty = true; return finalSize; diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs index f6130d49d6..c7ea5c1b69 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs @@ -4,6 +4,7 @@ using System.Reactive.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Layout; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests.Presenters @@ -244,6 +245,36 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal(new Size(110, 110), target.Extent); } + [Fact] + public void Extent_Should_Include_Content_Margin_Scaled_With_Layout_Rounding() + { + var root = new TestRoot + { + LayoutScaling = 1.25, + UseLayoutRounding = true + }; + + var target = new ScrollContentPresenter + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Content = new Border + { + Width = 200, + Height = 200, + Margin = new Thickness(2) + } + }; + + root.Child = target; + target.UpdateChild(); + target.Measure(new Size(1000, 1000)); + target.Arrange(new Rect(0, 0, 1000, 1000)); + + Assert.Equal(new Size(203.2, 203.2), target.Viewport); + Assert.Equal(new Size(203.2, 203.2), target.Extent); + } + [Fact] public void Extent_Width_Should_Be_Arrange_Width_When_CanScrollHorizontally_False() { From b488afaf799eb50bcc1de5f6728024557a3373e3 Mon Sep 17 00:00:00 2001 From: Valters Melnalksnis Date: Tue, 27 Jun 2023 13:49:20 +0300 Subject: [PATCH 210/391] Fix non-configurable minimum width for DataGridColumnHeader --- src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 082eac60be..0cc620dae9 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -42,9 +42,10 @@ - + 0.6 0.8 + 32 M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z @@ -174,7 +175,7 @@ VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> - + - Date: Wed, 28 Jun 2023 01:27:12 +0300 Subject: [PATCH 211/391] fix SendDataRequest re-run task --- src/Avalonia.X11/X11Clipboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index 04d1aae194..6e18151fae 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -224,7 +224,7 @@ namespace Avalonia.X11 private Task SendDataRequest(IntPtr format) { - if (_requestedDataTcs == null || _requestedFormatsTcs.Task.IsCompleted) + if (_requestedDataTcs == null || _requestedDataTcs.Task.IsCompleted) _requestedDataTcs = new TaskCompletionSource(); XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, format, format, _handle, IntPtr.Zero); return _requestedDataTcs.Task; From 02d198a09eb19e55ceab66a125c1aacd88021429 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 17:27:07 -0400 Subject: [PATCH 212/391] Replace public IDynamicLibraryLoader with internal NativeLibraryEx helper --- .../Compatibility/NativeLibrary.cs | 119 +++++++++++++ .../Platform/Internal/DynLoader.cs | 161 ------------------ .../Platform/Interop/IDynamicLibraryLoader.cs | 24 --- .../StandardRuntimePlatformServices.cs | 11 +- src/Avalonia.OpenGL/Egl/EglInterface.cs | 5 +- 5 files changed, 122 insertions(+), 198 deletions(-) create mode 100644 src/Avalonia.Base/Compatibility/NativeLibrary.cs delete mode 100644 src/Avalonia.Base/Platform/Internal/DynLoader.cs delete mode 100644 src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs diff --git a/src/Avalonia.Base/Compatibility/NativeLibrary.cs b/src/Avalonia.Base/Compatibility/NativeLibrary.cs new file mode 100644 index 0000000000..8c77ee928a --- /dev/null +++ b/src/Avalonia.Base/Compatibility/NativeLibrary.cs @@ -0,0 +1,119 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Avalonia.Compatibility; +using Avalonia.Platform.Interop; + +namespace Avalonia.Compatibility +{ + internal class NativeLibraryEx + { +#if NET6_0_OR_GREATER + public static IntPtr Load(string dll) => System.Runtime.InteropServices.NativeLibrary.Load(dll); + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) + => System.Runtime.InteropServices.NativeLibrary.TryGetExport(handle, name, out address); +#else + public static IntPtr Load(string dll) + { + var handle = DlOpen(dll); + if (handle != IntPtr.Zero) + return handle; + throw new InvalidOperationException("Unable to load " + dll, DlErrorString()); + } + + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) + { + try + { + address = DlSym(handle, name); + return address != default; + } + catch (Exception) + { + address = default; + return false; + } + } + + static NativeLibraryEx() + { + if (OperatingSystemEx.IsWindows()) + { + Win32Imports.Init(); + } + else if (OperatingSystemEx.IsLinux() || OperatingSystemEx.IsMacOS()) + { + var buffer = Marshal.AllocHGlobal(0x1000); + uname(buffer); + var unixName = Marshal.PtrToStringAnsi(buffer); + Marshal.FreeHGlobal(buffer); + if (unixName == "Darwin") + OsXImports.Init(); + else + LinuxImports.Init(); + } + } + + private static Func? DlOpen; + private static Func? DlSym; + private static Func? DlErrorString; + + [DllImport("libc")] + static extern int uname(IntPtr buf); + + static class Win32Imports + { + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibrary(string lpszLib); + + public static void Init() + { + DlOpen = LoadLibrary; + DlSym = GetProcAddress; + DlErrorString = () => new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + static class LinuxImports + { + [DllImport("libdl.so.2")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = s => dlopen(s, 1); + DlSym = dlsym; + DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + } + } + + static class OsXImports + { + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = s => dlopen(s, 1); + DlSym = dlsym; + DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + } + } +#endif + } +} diff --git a/src/Avalonia.Base/Platform/Internal/DynLoader.cs b/src/Avalonia.Base/Platform/Internal/DynLoader.cs deleted file mode 100644 index 07903669b1..0000000000 --- a/src/Avalonia.Base/Platform/Internal/DynLoader.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform.Interop; - -// ReSharper disable InconsistentNaming -namespace Avalonia.Platform.Internal -{ - class UnixLoader : IDynamicLibraryLoader - { - static class LinuxImports - { - [DllImport("libdl.so.2")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("libdl.so.2")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("libdl.so.2")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - } - - static class OsXImports - { - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("/usr/lib/libSystem.dylib")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - - } - - - [DllImport("libc")] - static extern int uname(IntPtr buf); - - static UnixLoader() - { - var buffer = Marshal.AllocHGlobal(0x1000); - uname(buffer); - var unixName = Marshal.PtrToStringAnsi(buffer); - Marshal.FreeHGlobal(buffer); - if (unixName == "Darwin") - OsXImports.Init(); - else - LinuxImports.Init(); - } - - private static Func? DlOpen; - private static Func? DlSym; - private static Func? DlError; - // ReSharper restore InconsistentNaming - - static string? DlErrorString() => Marshal.PtrToStringAnsi(DlError!.Invoke()); - - public IntPtr LoadLibrary(string dll) - { - var handle = DlOpen!.Invoke(dll, 1); - if (handle == IntPtr.Zero) - throw new DynamicLibraryLoaderException(DlErrorString()!); - return handle; - } - - public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) - { - var ptr = DlSym!.Invoke(dll, proc); - if (ptr == IntPtr.Zero && !optional) - throw new DynamicLibraryLoaderException(DlErrorString()!); - return ptr; - } - } - - internal class Win32Loader : IDynamicLibraryLoader - { - [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr LoadLibrary(string lpszLib); - - IntPtr IDynamicLibraryLoader.LoadLibrary(string dll) - { - var handle = LoadLibrary(dll); - if (handle != IntPtr.Zero) - return handle; - var err = Marshal.GetLastWin32Error(); - - throw new DynamicLibraryLoaderException("Error loading " + dll + " error " + err); - } - - IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional) - { - var ptr = GetProcAddress(dll, proc); - if (ptr == IntPtr.Zero && !optional) - throw new DynamicLibraryLoaderException("Error " + Marshal.GetLastWin32Error()); - return ptr; - } - } - -#if NET6_0_OR_GREATER - internal class Net6Loader : IDynamicLibraryLoader - { - public IntPtr LoadLibrary(string dll) - { - try - { - return NativeLibrary.Load(dll); - } - catch (Exception ex) - { - throw new DynamicLibraryLoaderException("Error loading " + dll, ex); - } - } - - public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) - { - try - { - if (optional) - { - return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default; - } - return NativeLibrary.GetExport(dll, proc); - } - catch (Exception ex) - { - throw new DynamicLibraryLoaderException("Error " + dll, ex); - } - } - } -#endif - - internal class NotSupportedLoader : IDynamicLibraryLoader - { - IntPtr IDynamicLibraryLoader.LoadLibrary(string dll) - { - throw new PlatformNotSupportedException(); - } - - IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional) - { - throw new PlatformNotSupportedException(); - } - } -} diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs deleted file mode 100644 index 56c50e340d..0000000000 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Avalonia.Metadata; - -namespace Avalonia.Platform.Interop -{ - public interface IDynamicLibraryLoader - { - IntPtr LoadLibrary(string dll); - IntPtr GetProcAddress(IntPtr dll, string proc, bool optional); - } - - public class DynamicLibraryLoaderException : Exception - { - public DynamicLibraryLoaderException(string message) : base(message) - { - - } - - public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException) - { - - } - } -} diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index 800d9b390f..fe8ddb7a9e 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -14,16 +14,7 @@ namespace Avalonia.Platform AssetLoader.RegisterResUriParsers(); AvaloniaLocator.CurrentMutable .Bind().ToConstant(standardPlatform) - .Bind().ToConstant(new StandardAssetLoader(assembly)) - .Bind().ToConstant( -#if NET6_0_OR_GREATER - new Net6Loader() -#else - OperatingSystemEx.IsWindows() ? (IDynamicLibraryLoader)new Win32Loader() - : OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsAndroid() ? new UnixLoader() - : new NotSupportedLoader() -#endif - ); + .Bind().ToConstant(new StandardAssetLoader(assembly)); } } } diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index 2235d9087c..4b766d0dda 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -35,9 +35,8 @@ namespace Avalonia.OpenGL.Egl static Func Load(string library) { - var dyn = AvaloniaLocator.Current.GetRequiredService(); - var lib = dyn.LoadLibrary(library); - return (s) => dyn.GetProcAddress(lib, s, true); + var lib = NativeLibraryEx.Load(library); + return (s) => NativeLibraryEx.TryGetExport(lib, s, out var address) ? address : default; } // ReSharper disable UnassignedGetOnlyAutoProperty From cb33529af4eeeb4cbbbb0fab71a0d7aab19bd0d0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 17:28:01 -0400 Subject: [PATCH 213/391] Make IRuntimePlatform private API and remove some members that are always have single implementation --- .../Platform/IRuntimePlatform.cs | 15 ++------- .../Platform/Internal/UnmanagedBlob.cs | 2 +- .../Platform/StandardRuntimePlatform.cs | 12 ++----- .../Rendering/DefaultRenderTimer.cs | 8 ++--- src/Avalonia.X11/X11CursorFactory.cs | 6 ++-- src/Avalonia.X11/X11Framebuffer.cs | 5 +-- .../Avalonia.Browser/WindowingPlatform.cs | 32 +++++++------------ src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 20 ++++-------- .../Avalonia.Win32/FramebufferManager.cs | 8 ++--- 9 files changed, 37 insertions(+), 71 deletions(-) diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 64b504c479..a2b8079476 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -3,24 +3,13 @@ using Avalonia.Metadata; namespace Avalonia.Platform { - [Unstable] + [PrivateApi] public interface IRuntimePlatform { - IDisposable StartSystemTimer(TimeSpan interval, Action tick); RuntimePlatformInfo GetRuntimeInfo(); - IUnmanagedBlob AllocBlob(int size); } - [Unstable] - public interface IUnmanagedBlob : IDisposable - { - IntPtr Address { get; } - int Size { get; } - bool IsDisposed { get; } - - } - - [Unstable] + [PrivateApi] public record struct RuntimePlatformInfo { public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop : diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index a56d9ffd1c..fc299dbcec 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -6,7 +6,7 @@ using System.Threading; namespace Avalonia.Platform.Internal; -internal class UnmanagedBlob : IUnmanagedBlob +internal class UnmanagedBlob { private IntPtr _address; private readonly object _lock = new object(); diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 8352d794d0..7a5c92c774 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -1,26 +1,20 @@ using System; using System.Threading; using Avalonia.Compatibility; +using Avalonia.Metadata; using Avalonia.Platform.Internal; namespace Avalonia.Platform { + [PrivateApi] public class StandardRuntimePlatform : IRuntimePlatform { - public IDisposable StartSystemTimer(TimeSpan interval, Action tick) - { - return new Timer(_ => tick(), null, interval, interval); - } - - public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); - private static readonly RuntimePlatformInfo s_info = new() { IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(), IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS() }; - - + public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info; } } diff --git a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs index 74c8bd107a..102cc30e87 100644 --- a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Avalonia.Metadata; using Avalonia.Platform; @@ -14,7 +15,6 @@ namespace Avalonia.Rendering [PrivateApi] public class DefaultRenderTimer : IRenderTimer { - private IRuntimePlatform? _runtime; private int _subscriberCount; private Action? _tick; private IDisposable? _subscription; @@ -80,11 +80,9 @@ namespace Avalonia.Rendering /// protected virtual IDisposable StartCore(Action tick) { - _runtime ??= AvaloniaLocator.Current.GetRequiredService(); + var interval = TimeSpan.FromSeconds(1.0 / FramesPerSecond); - return _runtime.StartSystemTimer( - TimeSpan.FromSeconds(1.0 / FramesPerSecond), - () => tick(TimeSpan.FromMilliseconds(Environment.TickCount))); + return new Timer(_ => tick(TimeSpan.FromMilliseconds(Environment.TickCount)), null, interval, interval); } /// diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 9818d3f86a..6fc9e27c81 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.SourceGenerator; using Avalonia.Utilities; @@ -92,17 +93,16 @@ namespace Avalonia.X11 private unsafe class XImageCursor : CursorImpl, IFramebufferPlatformSurface, IPlatformHandle { private readonly PixelSize _pixelSize; - private readonly IUnmanagedBlob _blob; + private readonly UnmanagedBlob _blob; public XImageCursor(IntPtr display, IBitmapImpl bitmap, PixelPoint hotSpot) { var size = Marshal.SizeOf() + (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4); - var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); _pixelSize = bitmap.PixelSize; - _blob = runtimePlatform.AllocBlob(size); + _blob = new UnmanagedBlob(size); var image = (XcursorImage*)_blob.Address; image->version = 1; diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 90e4b18571..02cfe4a517 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -1,6 +1,7 @@ using System; using System.IO; using Avalonia.Platform; +using Avalonia.Platform.Internal; using SkiaSharp; using static Avalonia.X11.XLib; namespace Avalonia.X11 @@ -10,7 +11,7 @@ namespace Avalonia.X11 private readonly IntPtr _display; private readonly IntPtr _xid; private readonly int _depth; - private IUnmanagedBlob _blob; + private UnmanagedBlob _blob; public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int height, double factor) { @@ -25,7 +26,7 @@ namespace Avalonia.X11 RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetRequiredService().AllocBlob(RowBytes * height); + _blob = new UnmanagedBlob(RowBytes * height); Address = _blob.Address; } diff --git a/src/Browser/Avalonia.Browser/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs index 3ad2620206..a23cd01910 100644 --- a/src/Browser/Avalonia.Browser/WindowingPlatform.cs +++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs @@ -57,12 +57,11 @@ namespace Avalonia.Browser public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { - return GetRuntimePlatform() - .StartSystemTimer(interval, () => - { - Dispatcher.UIThread.RunJobs(priority); - tick(); - }); + return new Timer(_ => + { + Dispatcher.UIThread.RunJobs(priority); + tick(); + }, null, interval, interval); } public void Signal(DispatcherPriority priority) @@ -71,18 +70,16 @@ namespace Avalonia.Browser return; _signaled = true; + var interval = TimeSpan.FromMilliseconds(1); IDisposable? disp = null; + disp = new Timer(_ => + { + _signaled = false; + disp?.Dispose(); - disp = GetRuntimePlatform() - .StartSystemTimer(TimeSpan.FromMilliseconds(1), - () => - { - _signaled = false; - disp?.Dispose(); - - Signaled?.Invoke(null); - }); + Signaled?.Invoke(null); + }, null, interval, interval); } public bool CurrentThreadIsLoopThread @@ -94,10 +91,5 @@ namespace Avalonia.Browser } public event Action? Signaled; - - private static IRuntimePlatform GetRuntimePlatform() - { - return AvaloniaLocator.Current.GetRequiredService(); - } } } diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 38df7b2933..581470fcfe 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Skia.Helpers; using SkiaSharp; @@ -97,21 +98,12 @@ namespace Avalonia.Skia SKColorType colorType = format.ToSkColorType(); SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); - var runtimePlatform = AvaloniaLocator.Current.GetService(); + _bitmap = new SKBitmap(); - if (runtimePlatform != null) - { - _bitmap = new SKBitmap(); - - var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType); - var blob = runtimePlatform.AllocBlob(nfo.BytesSize); + var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType); + var blob = new UnmanagedBlob(nfo.BytesSize); - _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob); - } - else - { - _bitmap = new SKBitmap(size.Width, size.Height, colorType, alphaType); - } + _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob); _bitmap.Erase(SKColor.Empty); } @@ -176,7 +168,7 @@ namespace Avalonia.Skia /// Blob. private static void ReleaseProc(IntPtr address, object ctx) { - ((IUnmanagedBlob)ctx).Dispose(); + ((UnmanagedBlob)ctx).Dispose(); } /// diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index cba4879f5b..b1b203a92d 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -2,6 +2,7 @@ using System.Threading; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Win32.Interop; namespace Avalonia.Win32 @@ -105,8 +106,7 @@ namespace Avalonia.Win32 private static FramebufferData AllocateFramebufferData(int width, int height) { - var service = AvaloniaLocator.Current.GetRequiredService(); - var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel); + var bitmapBlob = new UnmanagedBlob(width * height * _bytesPerPixel); return new FramebufferData(bitmapBlob, width, height); } @@ -155,7 +155,7 @@ namespace Avalonia.Win32 private readonly struct FramebufferData { - public IUnmanagedBlob Data { get; } + public UnmanagedBlob Data { get; } public PixelSize Size { get; } @@ -163,7 +163,7 @@ namespace Avalonia.Win32 public UnmanagedMethods.BITMAPINFOHEADER Header { get; } - public FramebufferData(IUnmanagedBlob data, int width, int height) + public FramebufferData(UnmanagedBlob data, int width, int height) { Data = data; Size = new PixelSize(width, height); From 605d10b02258b7aef2f181d6d15b9e569ac940c7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 18:42:53 -0400 Subject: [PATCH 214/391] Unify IRuntimePlatform registration across backends --- .../Avalonia.Android/AndroidPlatform.cs | 1 + .../Platform/IRuntimePlatform.cs | 1 - .../StandardRuntimePlatformServices.cs | 19 +++---- src/Avalonia.Controls/AppBuilder.cs | 54 +++++++++++-------- .../Remote/RemoteDesignerEntryPoint.cs | 1 + .../AvaloniaNativePlatformExtensions.cs | 4 +- src/Avalonia.X11/X11Platform.cs | 4 +- .../Avalonia.Browser/BrowserAppBuilder.cs | 1 + .../BrowserRuntimePlatform.cs | 18 +++++++ .../AvaloniaHeadlessPlatform.cs | 6 ++- .../LinuxFramebufferPlatform.cs | 5 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 4 +- src/iOS/Avalonia.iOS/Platform.cs | 1 + 13 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index d5d5f211e9..28f6d5349b 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -18,6 +18,7 @@ namespace Avalonia public static AppBuilder UseAndroid(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android") .UseSkia(); } diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index a2b8079476..c6efadc99a 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -18,7 +18,6 @@ namespace Avalonia.Platform public bool IsMobile { get; set; } } - [Unstable] public enum FormFactorType { Unknown, diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index fe8ddb7a9e..70919bc477 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -3,18 +3,15 @@ using Avalonia.Compatibility; using Avalonia.Platform.Internal; using Avalonia.Platform.Interop; -namespace Avalonia.Platform +namespace Avalonia.Platform; + +internal static class StandardRuntimePlatformServices { - public static class StandardRuntimePlatformServices + public static void Register(Assembly? assembly = null) { - public static void Register(Assembly? assembly = null) - { - var standardPlatform = new StandardRuntimePlatform(); - - AssetLoader.RegisterResUriParsers(); - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(standardPlatform) - .Bind().ToConstant(new StandardAssetLoader(assembly)); - } + AssetLoader.RegisterResUriParsers(); + AvaloniaLocator.CurrentMutable + .Bind().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); } } diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs index 77cc9d4dcb..3bc00bbc9c 100644 --- a/src/Avalonia.Controls/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -12,23 +12,23 @@ namespace Avalonia /// /// Initializes platform-specific services for an . /// - public class AppBuilder + public sealed class AppBuilder { private static bool s_setupWasAlreadyCalled; private Action? _optionsInitializers; private Func? _appFactory; private IApplicationLifetime? _lifetime; - - /// - /// Gets or sets the instance. - /// - public IRuntimePlatform RuntimePlatform { get; set; } /// /// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader) /// - public Action RuntimePlatformServicesInitializer { get; private set; } + public Action? RuntimePlatformServicesInitializer { get; private set; } + /// + /// Gets the name of the currently selected windowing subsystem. + /// + public string? RuntimePlatformServicesName { get; private set; } + /// /// Gets the instance being initialized. /// @@ -70,21 +70,10 @@ namespace Avalonia /// /// Initializes a new instance of the class. /// - public AppBuilder() - : this(new StandardRuntimePlatform(), - builder => StandardRuntimePlatformServices.Register(builder.ApplicationType?.Assembly)) + private AppBuilder() { } - /// - /// Initializes a new instance of the class. - /// - protected AppBuilder(IRuntimePlatform platform, Action platformServices) - { - RuntimePlatform = platform; - RuntimePlatformServicesInitializer = () => platformServices(this); - } - /// /// Begin configuring an . /// @@ -96,7 +85,6 @@ namespace Avalonia return new AppBuilder() { ApplicationType = typeof(TApp), - // Needed for CoreRT compatibility _appFactory = () => new TApp() }; } @@ -155,7 +143,7 @@ namespace Avalonia $"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type."); } - protected AppBuilder Self => this; + private AppBuilder Self => this; public AppBuilder AfterSetup(Action callback) { @@ -225,6 +213,30 @@ namespace Avalonia RenderingSubsystemName = name; return Self; } + + /// + /// Specifies a runtime platform subsystem to use. + /// + /// The method to call to initialize the runtime platform subsystem. + /// The name of the runtime platform subsystem. + /// An instance. + public AppBuilder UseRuntimePlatformSubsystem(Action initializer, string name = "") + { + RuntimePlatformServicesInitializer = initializer; + RuntimePlatformServicesName = name; + return Self; + } + + /// + /// Specifies a standard runtime platform subsystem to use. + /// + /// An instance. + public AppBuilder UseStandardRuntimePlatformSubsystem() + { + RuntimePlatformServicesInitializer = () => StandardRuntimePlatformServices.Register(ApplicationType?.Assembly); + RuntimePlatformServicesName = nameof(StandardRuntimePlatform); + return Self; + } /// /// Configures platform-specific options diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 6a6bc8c746..06d63a65ba 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -138,6 +138,7 @@ namespace Avalonia.DesignerSupport.Remote CommandLineArgs args, object obj) { var builder = (AppBuilder)obj; + builder = builder.UseStandardRuntimePlatformSubsystem(); if (args.Method == Methods.AvaloniaRemote) builder.UseWindowingSubsystem(() => PreviewerWindowingPlatform.Initialize(transport)); if (args.Method == Methods.Html) diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 2b989ce733..52f66d3b0c 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -8,7 +8,9 @@ namespace Avalonia { public static AppBuilder UseAvaloniaNative(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => { var platform = AvaloniaNativePlatform.Initialize( AvaloniaLocator.Current.GetService() ?? diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index a880e4ba1a..3a39a2a6b6 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -289,7 +289,9 @@ namespace Avalonia { public static AppBuilder UseX11(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService() ?? new X11PlatformOptions())); return builder; diff --git a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs index 38784871b1..dbc5f9f4a9 100644 --- a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs +++ b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs @@ -98,6 +98,7 @@ public static class BrowserAppBuilder this AppBuilder builder) { return builder + .UseBrowserRuntimePlatformSubsystem() .UseWindowingSubsystem(BrowserWindowingPlatform.Register) .UseSkia(); } diff --git a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs index 67bb040410..eee2b70c9c 100644 --- a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs +++ b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.JavaScript; using System.Text.RegularExpressions; @@ -7,6 +8,23 @@ using Avalonia.Platform; namespace Avalonia.Browser; +internal static class BrowserRuntimePlatformServices +{ + public static AppBuilder UseBrowserRuntimePlatformSubsystem(this AppBuilder builder) + { + builder.UseRuntimePlatformSubsystem(() => Register(builder.ApplicationType?.Assembly), nameof(BrowserRuntimePlatform)); + return builder; + } + + public static void Register(Assembly? assembly = null) + { + AssetLoader.RegisterResUriParsers(); + AvaloniaLocator.CurrentMutable + .Bind().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); + } +} + internal class BrowserRuntimePlatform : StandardRuntimePlatform { private static readonly Lazy Info = new(() => diff --git a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs index 2d8506f92b..9bb587c9b1 100644 --- a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs +++ b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs @@ -105,8 +105,10 @@ namespace Avalonia.Headless public static AppBuilder UseHeadless(this AppBuilder builder, AvaloniaHeadlessPlatformOptions opts) { if(opts.UseHeadlessDrawing) - builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless"); - return builder.UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless"); + builder = builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless"); + return builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless"); } } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index 10ec4372c7..e4a48c78fe 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -76,7 +76,10 @@ namespace Avalonia.LinuxFramebuffer internal static LinuxFramebufferLifetime Initialize(AppBuilder builder, IOutputBackend outputBackend, IInputBackend? inputBackend) { var platform = new LinuxFramebufferPlatform(outputBackend); - builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev"); + builder + .UseStandardRuntimePlatformSubsystem() + .UseSkia() + .UseWindowingSubsystem(platform.Initialize, "fbdev"); return new LinuxFramebufferLifetime(platform._fb, inputBackend); } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index e9019803be..8f74fb49e8 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -25,7 +25,9 @@ namespace Avalonia { public static AppBuilder UseWin32(this AppBuilder builder) { - return builder.UseWindowingSubsystem( + return builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem( () => Win32.Win32Platform.Initialize( AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions()), "Win32"); diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs index de664c93e0..34018560e2 100644 --- a/src/iOS/Avalonia.iOS/Platform.cs +++ b/src/iOS/Avalonia.iOS/Platform.cs @@ -16,6 +16,7 @@ namespace Avalonia public static AppBuilder UseiOS(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(iOS.Platform.Register, "iOS") .UseSkia(); } From 8b0085d99ceaa11a9e7888712ba3919fadecb5ba Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 19:04:10 -0400 Subject: [PATCH 215/391] Test NativeLibraryEx with non net6 --- .../Compatibility/NativeLibrary.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Compatibility/NativeLibrary.cs b/src/Avalonia.Base/Compatibility/NativeLibrary.cs index 8c77ee928a..7627c095bc 100644 --- a/src/Avalonia.Base/Compatibility/NativeLibrary.cs +++ b/src/Avalonia.Base/Compatibility/NativeLibrary.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Reflection; using System.Runtime.InteropServices; using Avalonia.Compatibility; using Avalonia.Platform.Interop; @@ -9,23 +10,25 @@ namespace Avalonia.Compatibility internal class NativeLibraryEx { #if NET6_0_OR_GREATER - public static IntPtr Load(string dll) => System.Runtime.InteropServices.NativeLibrary.Load(dll); - public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) - => System.Runtime.InteropServices.NativeLibrary.TryGetExport(handle, name, out address); + public static IntPtr Load(string dll, Assembly assembly) => NativeLibrary.Load(dll, assembly, null); + public static IntPtr Load(string dll) => NativeLibrary.Load(dll); + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) => + NativeLibrary.TryGetExport(handle, name, out address); #else + public static IntPtr Load(string dll, Assembly assembly) => Load(dll); public static IntPtr Load(string dll) { - var handle = DlOpen(dll); + var handle = DlOpen!(dll); if (handle != IntPtr.Zero) return handle; - throw new InvalidOperationException("Unable to load " + dll, DlErrorString()); + throw new InvalidOperationException("Unable to load " + dll, DlError!()); } public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) { try { - address = DlSym(handle, name); + address = DlSym!(handle, name); return address != default; } catch (Exception) @@ -56,7 +59,7 @@ namespace Avalonia.Compatibility private static Func? DlOpen; private static Func? DlSym; - private static Func? DlErrorString; + private static Func? DlError; [DllImport("libc")] static extern int uname(IntPtr buf); @@ -73,7 +76,7 @@ namespace Avalonia.Compatibility { DlOpen = LoadLibrary; DlSym = GetProcAddress; - DlErrorString = () => new Win32Exception(Marshal.GetLastWin32Error()); + DlError = () => new Win32Exception(Marshal.GetLastWin32Error()); } } @@ -92,7 +95,7 @@ namespace Avalonia.Compatibility { DlOpen = s => dlopen(s, 1); DlSym = dlsym; - DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); } } @@ -111,7 +114,7 @@ namespace Avalonia.Compatibility { DlOpen = s => dlopen(s, 1); DlSym = dlsym; - DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); } } #endif From 4c0873eb9a90e3fb12f8509cfa77b4a4f8ad07e6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Jun 2023 15:05:41 -0400 Subject: [PATCH 216/391] Fix build --- tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index ebd8a6474a..c0ec3f0687 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -20,7 +20,7 @@ namespace Avalonia.Benchmarks.Styling var services = new TestServices( assetLoader: new StandardAssetLoader(), globalClock: new MockGlobalClock(), - platform: new AppBuilder().RuntimePlatform, + platform: new StandardRuntimePlatform(), standardCursorFactory: Mock.Of(), theme: () => CreateTheme(), windowingPlatform: new MockWindowingPlatform()); From 7ac63a593b9abcaa5dc8c8833a78a285329c749f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Jun 2023 15:22:33 -0400 Subject: [PATCH 217/391] Seal geometry class --- src/Avalonia.Base/Media/Geometry.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Base/Media/Geometry.cs b/src/Avalonia.Base/Media/Geometry.cs index 0d2311eafc..3e0e6018de 100644 --- a/src/Avalonia.Base/Media/Geometry.cs +++ b/src/Avalonia.Base/Media/Geometry.cs @@ -28,6 +28,11 @@ namespace Avalonia.Media TransformProperty.Changed.AddClassHandler((x,e) => x.TransformChanged(e)); } + internal Geometry() + { + + } + /// /// Raised when the geometry changes. /// From fbca526f85b64cd9b26fe03213d3d0c96344a534 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Jun 2023 15:31:29 -0400 Subject: [PATCH 218/391] Seal CreateDefiningGeometry --- src/Avalonia.Base/CombinedGeometry.cs | 4 +--- src/Avalonia.Base/Media/EllipseGeometry.cs | 2 +- src/Avalonia.Base/Media/Geometry.cs | 2 +- src/Avalonia.Base/Media/GeometryGroup.cs | 4 +--- src/Avalonia.Base/Media/LineGeometry.cs | 2 +- src/Avalonia.Base/Media/PathGeometry.cs | 4 ++-- src/Avalonia.Base/Media/PlatformGeometry.cs | 4 ++-- src/Avalonia.Base/Media/PolylineGeometry.cs | 2 +- src/Avalonia.Base/Media/RectangleGeometry.cs | 2 +- src/Avalonia.Base/Media/StreamGeometry.cs | 4 ++-- tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs | 2 +- 11 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/CombinedGeometry.cs b/src/Avalonia.Base/CombinedGeometry.cs index 4b5866519b..a47e756c88 100644 --- a/src/Avalonia.Base/CombinedGeometry.cs +++ b/src/Avalonia.Base/CombinedGeometry.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Text; using Avalonia.Platform; -#nullable enable - namespace Avalonia.Media { public enum GeometryCombineMode @@ -147,7 +145,7 @@ namespace Avalonia.Media return new CombinedGeometry(GeometryCombineMode, Geometry1, Geometry2, Transform); } - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var g1 = Geometry1; var g2 = Geometry2; diff --git a/src/Avalonia.Base/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs index 84d74e888e..bb1263d8d5 100644 --- a/src/Avalonia.Base/Media/EllipseGeometry.cs +++ b/src/Avalonia.Base/Media/EllipseGeometry.cs @@ -135,7 +135,7 @@ namespace Avalonia.Media } /// - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var factory = AvaloniaLocator.Current.GetRequiredService(); diff --git a/src/Avalonia.Base/Media/Geometry.cs b/src/Avalonia.Base/Media/Geometry.cs index 3e0e6018de..a66cd616a3 100644 --- a/src/Avalonia.Base/Media/Geometry.cs +++ b/src/Avalonia.Base/Media/Geometry.cs @@ -139,7 +139,7 @@ namespace Avalonia.Media /// Creates the platform implementation of the geometry, without the transform applied. /// /// - protected abstract IGeometryImpl? CreateDefiningGeometry(); + private protected abstract IGeometryImpl? CreateDefiningGeometry(); /// /// Invalidates the platform implementation of the geometry. diff --git a/src/Avalonia.Base/Media/GeometryGroup.cs b/src/Avalonia.Base/Media/GeometryGroup.cs index 3e61413919..20bd297fc1 100644 --- a/src/Avalonia.Base/Media/GeometryGroup.cs +++ b/src/Avalonia.Base/Media/GeometryGroup.cs @@ -1,8 +1,6 @@ using Avalonia.Metadata; using Avalonia.Platform; -#nullable enable - namespace Avalonia.Media { /// @@ -72,7 +70,7 @@ namespace Avalonia.Media newChildren.Parent = this; } - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { if (_children.Count > 0) { diff --git a/src/Avalonia.Base/Media/LineGeometry.cs b/src/Avalonia.Base/Media/LineGeometry.cs index 6ac92ea33b..ced208e30b 100644 --- a/src/Avalonia.Base/Media/LineGeometry.cs +++ b/src/Avalonia.Base/Media/LineGeometry.cs @@ -68,7 +68,7 @@ namespace Avalonia.Media } /// - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var factory = AvaloniaLocator.Current.GetRequiredService(); diff --git a/src/Avalonia.Base/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs index 8292afde7e..bdfbfadce4 100644 --- a/src/Avalonia.Base/Media/PathGeometry.cs +++ b/src/Avalonia.Base/Media/PathGeometry.cs @@ -43,7 +43,7 @@ namespace Avalonia.Media /// /// The s. /// - public static new PathGeometry Parse(string pathData) + public new static PathGeometry Parse(string pathData) { var pathGeometry = new PathGeometry(); @@ -81,7 +81,7 @@ namespace Avalonia.Media set { SetValue(FillRuleProperty, value); } } - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var figures = Figures; diff --git a/src/Avalonia.Base/Media/PlatformGeometry.cs b/src/Avalonia.Base/Media/PlatformGeometry.cs index f25a14540f..e1488a8229 100644 --- a/src/Avalonia.Base/Media/PlatformGeometry.cs +++ b/src/Avalonia.Base/Media/PlatformGeometry.cs @@ -2,7 +2,7 @@ namespace Avalonia.Media { - internal class PlatformGeometry : Geometry + internal sealed class PlatformGeometry : Geometry { private readonly IGeometryImpl _geometryImpl; @@ -16,7 +16,7 @@ namespace Avalonia.Media return new PlatformGeometry(_geometryImpl); } - protected override IGeometryImpl? CreateDefiningGeometry() + private protected override IGeometryImpl? CreateDefiningGeometry() { return _geometryImpl; } diff --git a/src/Avalonia.Base/Media/PolylineGeometry.cs b/src/Avalonia.Base/Media/PolylineGeometry.cs index b0229b6455..47cf2f48a4 100644 --- a/src/Avalonia.Base/Media/PolylineGeometry.cs +++ b/src/Avalonia.Base/Media/PolylineGeometry.cs @@ -74,7 +74,7 @@ namespace Avalonia.Media return new PolylineGeometry(Points, IsFilled); } - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var factory = AvaloniaLocator.Current.GetRequiredService(); var geometry = factory.CreateStreamGeometry(); diff --git a/src/Avalonia.Base/Media/RectangleGeometry.cs b/src/Avalonia.Base/Media/RectangleGeometry.cs index 0bf9eb5664..01771242a7 100644 --- a/src/Avalonia.Base/Media/RectangleGeometry.cs +++ b/src/Avalonia.Base/Media/RectangleGeometry.cs @@ -47,7 +47,7 @@ namespace Avalonia.Media /// public override Geometry Clone() => new RectangleGeometry(Rect); - protected override IGeometryImpl? CreateDefiningGeometry() + private protected sealed override IGeometryImpl? CreateDefiningGeometry() { var factory = AvaloniaLocator.Current.GetRequiredService(); diff --git a/src/Avalonia.Base/Media/StreamGeometry.cs b/src/Avalonia.Base/Media/StreamGeometry.cs index fb79488e0f..9969376896 100644 --- a/src/Avalonia.Base/Media/StreamGeometry.cs +++ b/src/Avalonia.Base/Media/StreamGeometry.cs @@ -31,7 +31,7 @@ namespace Avalonia.Media /// /// The string. /// A . - public static new StreamGeometry Parse(string s) + public new static StreamGeometry Parse(string s) { var streamGeometry = new StreamGeometry(); @@ -62,7 +62,7 @@ namespace Avalonia.Media } /// - protected override IGeometryImpl? CreateDefiningGeometry() + private protected override IGeometryImpl? CreateDefiningGeometry() { if (_impl == null) { diff --git a/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs index ae8dc9dad2..6d724c86b5 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs @@ -103,7 +103,7 @@ namespace Avalonia.Base.UnitTests.Media throw new NotImplementedException(); } - protected override IGeometryImpl CreateDefiningGeometry() + private protected sealed override IGeometryImpl CreateDefiningGeometry() { return Mock.Of( x => x.WithTransform(It.IsAny()) == From 544cf0fdabd2c9f44219d79513caf690f4dd5135 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 28 Jun 2023 22:36:50 +0200 Subject: [PATCH 219/391] Revert "Remove support for undocumented blur #11855" This reverts commit 6a3925b91b67e2a3412a7d3e3e11ba24589dfeff. --- .../Interop/UnmanagedMethods.cs | 3 ++ src/Windows/Avalonia.Win32/WindowImpl.cs | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index e58abcadf0..ce42227292 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,6 +1827,9 @@ namespace Avalonia.Win32.Interop return result; } + [DllImport("user32.dll")] + internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + [Flags] public enum GCS : uint { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3fc2751e43..057cdb2db0 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -371,7 +371,7 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) SetTransparencyTransparent(windowsVersion); else if (level == WindowTransparencyLevel.Blur) - continue; // Unsupported on all versions (unreachable) + SetTransparencyBlur(windowsVersion); else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); else if (level == WindowTransparencyLevel.Mica) @@ -409,9 +409,9 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) return windowsVersion >= PlatformConstants.Windows8; - // Support for legacy, undocumented DWM blur has been removed. + // Blur only supported on Windows 8 and lower. if (level == WindowTransparencyLevel.Blur) - return false; + return windowsVersion < PlatformConstants.Windows10; // Acrylic is supported on Windows >= 10.0.15063. if (level == WindowTransparencyLevel.AcrylicBlur) @@ -426,14 +426,37 @@ namespace Avalonia.Win32 private void SetTransparencyTransparent(Version windowsVersion) { - // Transparent only supported with composition on Windows 10+. - if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows10) + // Transparent only supported with composition on Windows 8+. + if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8) return; + if (windowsVersion < PlatformConstants.Windows10) + { + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯. + // Hey, I'm just porting what was here before. + SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND); + var blurInfo = new DWM_BLURBEHIND(false); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); + } + SetUseHostBackdropBrush(false); _blurHost?.SetBlur(BlurEffect.None); } + private void SetTransparencyBlur(Version windowsVersion) + { + // Blur only supported with composition on Windows 8 and lower. + if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10) + return; + + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10. + SetAccentState(AccentState.ACCENT_DISABLED); + var blurInfo = new DWM_BLURBEHIND(true); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); + } + private void SetTransparencyAcrylicBlur(Version windowsVersion) { // Acrylic blur only supported with composition on Windows >= 10.0.15063. @@ -454,6 +477,26 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Mica); } + private void SetAccentState(AccentState state) + { + var accent = new AccentPolicy(); + var accentStructSize = Marshal.SizeOf(accent); + + //Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10 + accent.AccentState = state; + + var accentPtr = Marshal.AllocHGlobal(accentStructSize); + Marshal.StructureToPtr(accent, accentPtr, false); + + var data = new WindowCompositionAttributeData(); + data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; + data.SizeOfData = accentStructSize; + data.Data = accentPtr; + + SetWindowCompositionAttribute(_hwnd, ref data); + Marshal.FreeHGlobal(accentPtr); + } + private void SetUseHostBackdropBrush(bool useHostBackdropBrush) { if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion) From 8f7cf7cc4a8d8d5f937f3d20b61f026ec007696f Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 28 Jun 2023 22:47:11 +0200 Subject: [PATCH 220/391] Invoke SetWindowCompositionAttribute dynamically without exposing it to ILC --- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ce42227292..68f3e3c670 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,8 +1827,21 @@ namespace Avalonia.Win32.Interop return result; } - [DllImport("user32.dll")] - internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + internal static int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data) + { + var user32 = LoadLibrary("user32.dll"); + var pfnSetWindowCompositionAttribute = (delegate* unmanaged[Stdcall])GetProcAddress(user32, nameof(SetWindowCompositionAttribute)); + if (pfnSetWindowCompositionAttribute == null) + { + // This preserves the same behavior as using the DllImport attribute. + throw new EntryPointNotFoundException("The unsupported SetWindowCompositionAttribute-function has been removed from the operating system."); + } + + fixed (WindowCompositionAttributeData* pData = &data) + { + return pfnSetWindowCompositionAttribute(hwnd, pData); + } + } [Flags] public enum GCS : uint From d76c06d4c4e77bd29577b0671b69ba13d3dcc9d5 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 29 Jun 2023 10:55:29 +0200 Subject: [PATCH 221/391] Make TextInputEventArgs.Text settable --- src/Avalonia.Base/Input/TextInputEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/TextInputEventArgs.cs b/src/Avalonia.Base/Input/TextInputEventArgs.cs index f4acd694a0..305b4d81f8 100644 --- a/src/Avalonia.Base/Input/TextInputEventArgs.cs +++ b/src/Avalonia.Base/Input/TextInputEventArgs.cs @@ -4,6 +4,6 @@ namespace Avalonia.Input { public class TextInputEventArgs : RoutedEventArgs { - public string? Text { get; init; } + public string? Text { get; set; } } } From 3f56045885f6c17b03ba8e48071b8e01855617be Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 29 Jun 2023 20:40:03 +0000 Subject: [PATCH 222/391] remove debug code --- samples/MobileSandbox.Android/MainActivity.cs | 11 ----------- .../MobileSandbox.Android.csproj | 4 ---- src/Android/Avalonia.Android/Avalonia.Android.csproj | 3 --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 -- 4 files changed, 20 deletions(-) diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs index 7a0de74757..702e9be9e6 100644 --- a/samples/MobileSandbox.Android/MainActivity.cs +++ b/samples/MobileSandbox.Android/MainActivity.cs @@ -10,16 +10,5 @@ namespace MobileSandbox.Android [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AvaloniaMainActivity { - protected override void OnCreate(Bundle savedInstanceState) - { - AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; - - base.OnCreate(savedInstanceState); - } - - private void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) - { - System.Diagnostics.Debug.WriteLine(e.ToString()); - } } } diff --git a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj index 54cfea83fa..3b67d79f7b 100644 --- a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj +++ b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj @@ -38,10 +38,6 @@ True - - True - - diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj index e04a1acde1..d8b0c3d534 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -7,9 +7,6 @@ portable Avalonia.Android.Internal - - True - diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index d6c2ab7314..5d82d099c9 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -526,8 +526,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform } var compositionText = text.SubSequence(0, text.Length()); - System.Diagnostics.Debug.WriteLine($"Composition Changed: {compositionText}"); - if (_inputMethod.IsActive && !_commitInProgress) { if (string.IsNullOrEmpty(compositionText)) From 1381ec28be5202139fca91ac6c7f2539401171bc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 29 Jun 2023 17:04:24 -0400 Subject: [PATCH 223/391] Allow only Light and Dark in the ColorPaletteResourcesCollection --- samples/ControlCatalog/App.xaml | 3 +++ samples/ControlCatalog/App.xaml.cs | 4 +-- .../ColorPaletteResourcesCollection.cs | 25 +++++++++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 64bf3e53b3..02b1242471 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -28,6 +28,9 @@ + + + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 246fe4385f..605254c995 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -30,8 +30,8 @@ namespace ControlCatalog AvaloniaXamlLoader.Load(this); - _fluentTheme = new FluentTheme(); - _simpleTheme = new SimpleTheme(); + _fluentTheme = (FluentTheme)Resources["FluentTheme"]!; + _simpleTheme = (SimpleTheme)Resources["SimpleTheme"]!; _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!; _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!; _dataGridFluent = (IStyle)Resources["DataGridFluent"]!; diff --git a/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs index f26f6d8660..6eb465d399 100644 --- a/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs +++ b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs @@ -10,12 +10,18 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary + (key, x) => { if (Owner is not null) { x.PropertyChanged += Palette_PropertyChanged; } + + if (key != ThemeVariant.Dark && key != ThemeVariant.Light) + { + throw new InvalidOperationException( + $"{nameof(FluentTheme)}.{nameof(FluentTheme.Palettes)} only supports Light and Dark variants."); + } }, (_, x) => { @@ -30,22 +36,15 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary Count > 0; public bool TryGetResource(object key, ThemeVariant? theme, out object? value) { - if (theme is not null) + if (theme == null || theme == ThemeVariant.Default) { - if (base.TryGetValue(theme, out var themePaletteResources) - && themePaletteResources.TryGetResource(key, theme, out value)) - { - return true; - } + theme = ThemeVariant.Light; } - if (theme != ThemeVariant.Default) + if (base.TryGetValue(theme, out var themePaletteResources) + && themePaletteResources.TryGetResource(key, theme, out value)) { - if (base.TryGetValue(ThemeVariant.Default, out var defaultPaletteResources) - && defaultPaletteResources.TryGetResource(key, ThemeVariant.Default, out value)) - { - return true; - } + return true; } value = null; From d7ee01fbf5885d3e34f6d53e6249cf80e44ed3dc Mon Sep 17 00:00:00 2001 From: Petar Tasev Date: Thu, 29 Jun 2023 19:22:28 -0700 Subject: [PATCH 224/391] Fixed a small xml doc mistake --- src/Avalonia.Base/Platform/IDrawingContextImpl.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index df3c9ebfc5..b52378d3b3 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -140,7 +140,7 @@ namespace Avalonia.Platform void PopOpacity(); /// - /// Pushes an opacity mask + /// Pushes an opacity mask. /// void PushOpacityMask(IBrush mask, Rect bounds); @@ -161,9 +161,7 @@ namespace Avalonia.Platform void PopGeometryClip(); /// - /// Get Fetaure from type - /// - /// Attempts to get an optional feature from the drawing context implementation + /// Attempts to get an optional feature from the drawing context implementation. /// object? GetFeature(Type t); } @@ -177,7 +175,7 @@ namespace Avalonia.Platform public static class DrawingContextImplExtensions { /// - /// Attempts to get an optional feature from the drawing context implementation + /// Attempts to get an optional feature from the drawing context implementation. /// public static T? GetFeature(this IDrawingContextImpl context) where T : class => (T?)context.GetFeature(typeof(T)); @@ -186,13 +184,13 @@ namespace Avalonia.Platform public interface IDrawingContextLayerImpl : IRenderTargetBitmapImpl { /// - /// Does optimized blit with Src blend mode + /// Does optimized blit with Src blend mode. /// /// void Blit(IDrawingContextImpl context); /// - /// Returns true if layer supports optimized blit + /// Returns true if layer supports optimized blit. /// bool CanBlit { get; } } From 43e97d98959a54b54b9b748c31059a5ba3a3c214 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 30 Jun 2023 10:34:40 +0200 Subject: [PATCH 225/391] Reset TextBox :empty class when text is empty but preedit is set Delete selection when composition starts --- src/Avalonia.Controls/TextBox.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 3016dc8239..3b92baa42a 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -814,7 +814,12 @@ namespace Avalonia.Controls if (IsFocused) { - _presenter?.ShowCaret(); + if(_presenter != null) + { + _presenter.ShowCaret(); + + _presenter.PropertyChanged += PresenterPropertyChanged; + } } } @@ -822,9 +827,29 @@ namespace Avalonia.Controls { base.OnDetachedFromVisualTree(e); + if (_presenter != null) + { + _presenter.HideCaret(); + + _presenter.PropertyChanged -= PresenterPropertyChanged; + } + _imClient.SetPresenter(null, null); } + private void PresenterPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if(e.Property == TextPresenter.PreeditTextProperty) + { + if(string.IsNullOrEmpty(e.OldValue as string) && !string.IsNullOrEmpty(e.NewValue as string)) + { + PseudoClasses.Set(":empty", false); + + DeleteSelection(); + } + } + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); From 8680c612fd9ce358c5cb93f4159de12e2b904263 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 30 Jun 2023 15:42:12 +0600 Subject: [PATCH 226/391] OSX rendering refactoring --- Avalonia.Desktop.slnf | 1 + Avalonia.sln | 6 + build/CoreLibraries.props | 1 + native/Avalonia.Native/inc/com.h | 1 + native/Avalonia.Native/inc/comimpl.h | 19 ++ native/Avalonia.Native/inc/noarc.h | 11 + native/Avalonia.Native/inc/rendertarget.h | 16 +- .../project.pbxproj | 20 ++ native/Avalonia.Native/src/OSX/AvnView.h | 5 +- native/Avalonia.Native/src/OSX/AvnView.mm | 46 ++-- native/Avalonia.Native/src/OSX/PopupImpl.mm | 6 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 13 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 58 +++-- native/Avalonia.Native/src/OSX/WindowImpl.h | 2 +- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- native/Avalonia.Native/src/OSX/common.h | 31 ++- native/Avalonia.Native/src/OSX/main.mm | 36 ++- native/Avalonia.Native/src/OSX/metal.mm | 161 ++++++++++++ native/Avalonia.Native/src/OSX/noarc.mm | 11 + .../Avalonia.Native/src/OSX/rendertarget.mm | 245 ++++++++++++------ .../SkiaPlatform/FramebufferManager.cs | 2 + .../RenderTargetNotReadyException.cs | 25 ++ .../Server/ServerCompositionTarget.cs | 13 +- .../Surfaces/IFramebufferPlatformSurface.cs | 29 ++- .../Remote/Server/RemoteServerTopLevelImpl.cs | 6 +- src/Avalonia.Metal/Avalonia.Metal.csproj | 15 ++ src/Avalonia.Metal/IMetalDevice.cs | 34 +++ .../AvaloniaNativeGlPlatformGraphics.cs | 5 +- src/Avalonia.Native/AvaloniaNativePlatform.cs | 42 ++- .../AvaloniaNativePlatformExtensions.cs | 2 + src/Avalonia.Native/AvnDispatcher.cs | 18 ++ src/Avalonia.Native/DeferredFramebuffer.cs | 49 +--- src/Avalonia.Native/Metal.cs | 119 +++++++++ src/Avalonia.Native/PopupImpl.cs | 12 +- src/Avalonia.Native/WindowImpl.cs | 10 +- src/Avalonia.Native/WindowImplBase.cs | 67 +++-- src/Avalonia.Native/avn.idl | 53 +++- src/Avalonia.X11/X11CursorFactory.cs | 2 + src/Avalonia.X11/X11FramebufferSurface.cs | 2 + src/Avalonia.X11/X11IconLoader.cs | 2 + .../Skia/BrowserSkiaRasterSurface.cs | 2 + .../Avalonia.Headless/HeadlessWindowImpl.cs | 2 + .../Output/FbdevOutput.cs | 2 + .../Avalonia.Skia/FramebufferRenderTarget.cs | 11 +- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 80 ++++++ .../Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs | 119 +++++++++ .../Avalonia.Skia/PlatformRenderInterface.cs | 4 + .../Avalonia.Skia/RenderTargetBitmapImpl.cs | 2 + .../FramebufferShimRenderTarget.cs | 12 +- .../Avalonia.Win32/FramebufferManager.cs | 2 + .../Avalonia.RenderTests/Media/BitmapTests.cs | 6 +- tests/Avalonia.RenderTests/TestBase.cs | 5 +- .../CompositorTestServices.cs | 2 + 53 files changed, 1204 insertions(+), 243 deletions(-) create mode 100644 native/Avalonia.Native/inc/noarc.h create mode 100644 native/Avalonia.Native/src/OSX/metal.mm create mode 100644 native/Avalonia.Native/src/OSX/noarc.mm create mode 100644 src/Avalonia.Base/RenderTargetNotReadyException.cs create mode 100644 src/Avalonia.Metal/Avalonia.Metal.csproj create mode 100644 src/Avalonia.Metal/IMetalDevice.cs create mode 100644 src/Avalonia.Native/AvnDispatcher.cs create mode 100644 src/Avalonia.Native/Metal.cs create mode 100644 src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs create mode 100644 src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf index 5e770a0170..6089a06d4f 100644 --- a/Avalonia.Desktop.slnf +++ b/Avalonia.Desktop.slnf @@ -24,6 +24,7 @@ "src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj", "src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj", "src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj", + "src\\Avalonia.Metal\\Avalonia.Metal.csproj", "src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj", "src\\Avalonia.Native\\Avalonia.Native.csproj", "src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj", diff --git a/Avalonia.sln b/Avalonia.sln index 107c55aa6f..d5419365ac 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -274,6 +274,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit.Uni EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Browser", "samples\MobileSandbox.Browser\MobileSandbox.Browser.csproj", "{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -661,6 +663,10 @@ Global {43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Debug|Any CPU.Build.0 = Debug|Any CPU {43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Release|Any CPU.ActiveCfg = Release|Any CPU {43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Release|Any CPU.Build.0 = Release|Any CPU + {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 00a1e3094b..a9af587a45 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -4,6 +4,7 @@ + diff --git a/native/Avalonia.Native/inc/com.h b/native/Avalonia.Native/inc/com.h index df251514ef..42a989e050 100644 --- a/native/Avalonia.Native/inc/com.h +++ b/native/Avalonia.Native/inc/com.h @@ -28,6 +28,7 @@ typedef DWORD ULONG; #define E_UNEXPECTED 0x8000FFFFL #define E_HANDLE 0x80070006L #define E_INVALIDARG 0x80070057L +#define COR_E_INVALIDOPERATION 0x80131509L struct IUnknown { diff --git a/native/Avalonia.Native/inc/comimpl.h b/native/Avalonia.Native/inc/comimpl.h index 47b0a3c5f2..017de01ee9 100644 --- a/native/Avalonia.Native/inc/comimpl.h +++ b/native/Avalonia.Native/inc/comimpl.h @@ -43,6 +43,18 @@ public: _obj->AddRef(); } } + + ComPtr(TInterface* pObj, bool ownsHandle) + { + _obj = 0; + + if (pObj) + { + _obj = pObj; + if(!ownsHandle) + _obj->AddRef(); + } + } ComPtr(const ComPtr& ptr) { @@ -92,6 +104,13 @@ public: { return &_obj; } + + void setNoAddRef(TInterface* value) + { + if(_obj != nullptr) + _obj->Release(); + _obj = value; + } operator TInterface*() const { diff --git a/native/Avalonia.Native/inc/noarc.h b/native/Avalonia.Native/inc/noarc.h new file mode 100644 index 0000000000..c49d975ade --- /dev/null +++ b/native/Avalonia.Native/inc/noarc.h @@ -0,0 +1,11 @@ +#import + +class CppAutoreleasePool +{ + void* _pool; +public: + CppAutoreleasePool(); + ~CppAutoreleasePool(); +}; + +#define START_ARP_CALL CppAutoreleasePool __autoreleasePool \ No newline at end of file diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h index a59915777f..28572bc566 100644 --- a/native/Avalonia.Native/inc/rendertarget.h +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -5,13 +5,21 @@ #include "avalonia-native.h" @protocol IRenderTarget --(void) setNewLayer: (CALayer*) layer; --(HRESULT) setSwFrame: (AvnFramebuffer*) fb; + -(void) resize: (AvnPixelSize) size withScale: (float) scale; --(AvnPixelSize) pixelSize; --(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget; +-(CALayer*) layer; + @end @interface IOSurfaceRenderTarget : NSObject -(IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context; +-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget; +-(IAvnSoftwareRenderTarget*) createSoftwareRenderTarget; +-(HRESULT) setSwFrame: (AvnFramebuffer*) fb; +-(void)consumeSurfaces; @end + +@interface MetalRenderTarget : NSObject +-(MetalRenderTarget*) initWithDevice: (IAvnMetalDevice*) device; +-(void) getRenderTarget: (IAvnMetalRenderTarget**) ppv; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index eb0b528fd3..f11b027173 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -43,6 +43,11 @@ 523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */ = {isa = PBXBuildFile; fileRef = 523484C926EA688F00EA0C2C /* trayicon.mm */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; + 64B1EA48E308E574685AFB07 /* metal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64B1EBEECBE13D8616D7C934 /* metal.mm */; }; + 64B1ECA861163C0EFF0E502B /* noarc.h in Headers */ = {isa = PBXBuildFile; fileRef = 64B1E26F2B1B9C577BF52F06 /* noarc.h */; }; + 64B1ED7C5433AC3B815FFCC5 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */; }; + 64B1EE5DD4D882D587CE76CE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */; }; + 64B1EF3C757B71526FFAF436 /* noarc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; }; 8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */; }; 8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */; }; @@ -99,6 +104,11 @@ 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; }; 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; }; + 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + 64B1E26F2B1B9C577BF52F06 /* noarc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = noarc.h; path = ../../inc/noarc.h; sourceTree = ""; }; + 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = noarc.mm; sourceTree = ""; }; + 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 64B1EBEECBE13D8616D7C934 /* metal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = metal.mm; sourceTree = ""; }; 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = ""; }; 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethodDelegate.h; sourceTree = ""; }; 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethod.h; sourceTree = ""; }; @@ -126,6 +136,8 @@ AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, 522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */, AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */, + 64B1ED7C5433AC3B815FFCC5 /* Metal.framework in Frameworks */, + 64B1EE5DD4D882D587CE76CE /* CoreGraphics.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -141,6 +153,8 @@ 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, AB1E522B217613570091CD71 /* OpenGL.framework */, AB661C1D2148230F00291242 /* AppKit.framework */, + 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */, + 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */, ); name = Frameworks; sourceTree = ""; @@ -198,6 +212,9 @@ 18391DB45C7D892E61BF388C /* WindowInterfaces.h */, 18391BB698579F40F1783F31 /* PopupImpl.mm */, 183910513F396141938832B5 /* PopupImpl.h */, + 64B1EBEECBE13D8616D7C934 /* metal.mm */, + 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */, + 64B1E26F2B1B9C577BF52F06 /* noarc.h */, ); sourceTree = ""; }; @@ -230,6 +247,7 @@ 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */, 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */, + 64B1ECA861163C0EFF0E502B /* noarc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -319,6 +337,8 @@ 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */, 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */, EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */, + 64B1EA48E308E574685AFB07 /* metal.mm in Sources */, + 64B1EF3C757B71526FFAF436 /* noarc.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index 256caa70e9..e058656d3f 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -11,15 +11,16 @@ #include "KeyTransform.h" @class AvnAccessibilityElement; +@protocol IRenderTarget; -@interface AvnView : NSView +@interface AvnView : NSView -(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(NSEvent* _Nonnull) lastMouseDownEvent; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; --(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; -(void) onClosed; -(AvnPlatformResizeReason) getResizeReason; -(void) setResizeReason:(AvnPlatformResizeReason)reason; +-(void) setRenderTarget:(NSObject*)target; + (AvnPoint)toAvnPoint:(CGPoint)p; @end diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 86bacfb819..a4999b8df3 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -17,7 +17,7 @@ NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; AvnPixelSize _lastPixelSize; - NSObject* _renderTarget; + NSObject* _currentRenderTarget; AvnPlatformResizeReason _resizeReason; AvnAccessibilityElement* _accessibilityChild; NSRect _cursorRect; @@ -41,15 +41,39 @@ - (void) updateRenderTarget { - [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; - [self setNeedsDisplayInRect:[self frame]]; + if(_currentRenderTarget) { + [_currentRenderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; + [self setNeedsDisplayInRect:[self frame]]; + } +} + + +-(void) setRenderTarget:(NSObject*)target +{ + if([self layer]) + { + [self layer].delegate = nil; + } + _currentRenderTarget = target; + auto layer = [target layer]; + [self setLayer: layer]; + [layer setDelegate: self]; + layer.needsDisplayOnBoundsChange = YES; + [self updateRenderTarget]; +} + +-(void)displayLayer: (CALayer*)layer +{ + [self updateLayer]; } -(AvnView*) initWithParent: (WindowBaseImpl*) parent { self = [super init]; - _renderTarget = parent->renderTarget; [self setWantsLayer:YES]; + [self setLayerContentsPlacement: NSViewLayerContentsPlacementTopLeft]; + + [self setCanDrawSubviewsIntoLayer: NO]; [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; _parent = parent; @@ -77,12 +101,6 @@ return YES; } -- (void)setLayer:(CALayer *)layer -{ - [_renderTarget setNewLayer: layer]; - [super setLayer: layer]; -} - - (BOOL)isOpaque { return YES; @@ -164,14 +182,6 @@ return; } --(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose -{ - @autoreleasepool { - [_renderTarget setSwFrame:fb]; - dispose->Release(); - } -} - - (AvnPoint) translateLocalPoint:(AvnPoint)pt { pt.Y = [self bounds].size.height - pt.Y; diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index 972d03d08c..39aa568134 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -23,7 +23,7 @@ private: END_INTERFACE_MAP() virtual ~PopupImpl(){} ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) + PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) { WindowEvents = events; [Window setLevel:NSPopUpMenuWindowLevel]; @@ -47,11 +47,11 @@ public: }; -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events) { @autoreleasepool { - IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events)); return ptr; } } diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index bc35ca670f..83c7aed5d3 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -27,7 +27,7 @@ BEGIN_INTERFACE_MAP() virtual ~WindowBaseImpl(); - WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false); + WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel = false); virtual HRESULT ObtainNSWindowHandle(void **ret) override; @@ -81,13 +81,15 @@ BEGIN_INTERFACE_MAP() virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; - virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override; - virtual HRESULT SetCursor(IAvnCursor *cursor) override; virtual void UpdateCursor(); - virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override; + virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) override; + + virtual HRESULT CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) override; + + virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) override; virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; @@ -118,7 +120,6 @@ private: void CleanNSWindow (); NSCursor *cursor; - ComPtr _glContext; bool hasPosition; NSSize lastSize; NSSize lastMinSize; @@ -132,7 +133,7 @@ protected: bool _shown; public: - NSObject *renderTarget; + NSObject *currentRenderTarget; NSWindow * Window; ComPtr BaseEvents; ComPtr InputMethod; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index edf6bcf508..b954bdd4e3 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -16,6 +16,7 @@ #import "WindowInterfaces.h" #include "WindowBaseImpl.h" #include "AvnTextInputMethod.h" +#include "AvnView.h" WindowBaseImpl::~WindowBaseImpl() { @@ -23,12 +24,10 @@ WindowBaseImpl::~WindowBaseImpl() { Window = nullptr; } -WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) { +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) { _shown = false; _inResize = false; BaseEvents = events; - _glContext = gl; - renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; View = [[AvnView alloc] initWithParent:this]; InputMethod = new AvnTextInputMethod(View); StandardContainer = [[AutoFitContentView new] initWithContent:View]; @@ -448,13 +447,6 @@ HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { } } -HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) { - START_COM_CALL; - - [View setSwRenderedFrame:fb dispose:dispose]; - return S_OK; -} - HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) { START_COM_CALL; @@ -479,13 +471,49 @@ void WindowBaseImpl::UpdateCursor() { } } -HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { +HRESULT WindowBaseImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) { START_COM_CALL; + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + if (View == NULL) return E_FAIL; - *ppv = [renderTarget createSurfaceRenderTarget]; - return static_cast(*ppv == nil ? E_FAIL : S_OK); + + auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil]; + *ppv = [target createSoftwareRenderTarget]; + [View setRenderTarget: target]; + return S_OK; +} + +HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) { + START_COM_CALL; + + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + + if (View == NULL) + return E_FAIL; + + auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext]; + *ppv = [target createSurfaceRenderTarget]; + [View setRenderTarget: target]; + return S_OK; +} + +HRESULT WindowBaseImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) { + START_COM_CALL; + + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + + if (View == NULL) + return E_FAIL; + + auto target = [[MetalRenderTarget alloc] initWithDevice: device]; + [View setRenderTarget: target]; + [target getRenderTarget: ppv]; + return S_OK; } HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { @@ -615,11 +643,11 @@ HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) { return S_OK; } -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) { @autoreleasepool { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events); return ptr; } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 5140124a17..049ef755ff 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -39,7 +39,7 @@ BEGIN_INTERFACE_MAP() ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); + WindowImpl(IAvnWindowEvents* events); virtual HRESULT Show (bool activate, bool isDialog) override; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 925880534a..be761e0af7 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -9,7 +9,7 @@ #include "automation.h" #include "WindowProtocol.h" -WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { +WindowImpl::WindowImpl(IAvnWindowEvents *events) : WindowBaseImpl(events) { _isEnabled = true; _children = std::list(); _isClientAreaExtended = false; diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 4353737dc8..5cf1b94a2f 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -6,11 +6,13 @@ #import #import #include +#include "noarc.h" extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); extern void FreeAvnGCHandle(void* handle); -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl); -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl); +extern void PostDispatcherCallback(IAvnActionCallback* cb); +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); extern IAvnSystemDialogs* CreateSystemDialogs(); extern IAvnScreens* CreateScreens(); extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*); @@ -21,6 +23,7 @@ extern NSString* GetAvnCustomDataType(); extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop); extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnGlDisplay* GetGlDisplay(); +extern IAvnMetalDisplay* GetMetalDisplay(); extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events); extern IAvnTrayIcon* CreateTrayIcon(); extern IAvnMenuItem* CreateAppMenuItem(); @@ -56,6 +59,27 @@ template inline T* objc_cast(id from) { return nil; } +template class ObjCWrapper { +public: + T* Value; + ObjCWrapper(T* value) + { + Value = value; + } + operator T*() const + { + return Value; + } + T* operator->() const + { + return Value; + } + ~ObjCWrapper() + { + Value = nil; + } +}; + @interface ActionCallback : NSObject - (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback; - (void) action; @@ -79,5 +103,8 @@ public: virtual HRESULT ShowAll() override; virtual HRESULT HideOthers() override; }; +#define NSApp [NSApplication sharedApplication] + +#define START_COM_ARP_CALL START_ARP_CALL; START_COM_CALL #endif diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 4bfda4b531..1c7e2cf25a 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -191,16 +191,20 @@ public: @end static ComPtr _deallocator; +static ComPtr _dispatcher; class AvaloniaNative : public ComSingleObject { public: FORWARD_IUNKNOWN() - virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* events) override + virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, + IAvnApplicationEvents* events, + IAvnDispatcher* dispatcher) override { START_COM_CALL; _deallocator = deallocator; + _dispatcher = dispatcher; @autoreleasepool{ [[ThreadingInitializer new] do]; } @@ -213,7 +217,7 @@ public: return (IAvnMacOptions*)new MacOptions(); } - virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) override + virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) override { START_COM_CALL; @@ -221,12 +225,12 @@ public: { if(cb == nullptr || ppv == nullptr) return E_POINTER; - *ppv = CreateAvnWindow(cb, gl); + *ppv = CreateAvnWindow(cb); return S_OK; } }; - virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) override + virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv) override { START_COM_CALL; @@ -235,7 +239,7 @@ public: if(cb == nullptr || ppv == nullptr) return E_POINTER; - *ppv = CreateAvnPopup(cb, gl); + *ppv = CreateAvnPopup(cb); return S_OK; } } @@ -320,7 +324,22 @@ public: return S_OK; } } - + + virtual HRESULT ObtainMetalDisplay(IAvnMetalDisplay** ppv) override + { + START_COM_CALL; + @autoreleasepool + { + auto rv = ::GetMetalDisplay(); + if(rv == NULL) + return E_FAIL; + rv->AddRef(); + *ppv = rv; + return S_OK; + } + } + + virtual HRESULT CreateTrayIcon (IAvnTrayIcon** ppv) override { START_COM_CALL; @@ -432,6 +451,11 @@ extern void FreeAvnGCHandle(void* handle) _deallocator->FreeGCHandle(handle); } +extern void PostDispatcherCallback(IAvnActionCallback* cb) +{ + _dispatcher->Post(cb); +} + NSSize ToNSSize (AvnSize s) { NSSize result; diff --git a/native/Avalonia.Native/src/OSX/metal.mm b/native/Avalonia.Native/src/OSX/metal.mm new file mode 100644 index 0000000000..8a66592ec4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/metal.mm @@ -0,0 +1,161 @@ +#import +#import +#import +#include "common.h" +#include "rendertarget.h" + +class AvnMetalDevice : public ComSingleObject +{ +public: + id device; + id queue; + FORWARD_IUNKNOWN() + + void *GetDevice() override { + return (__bridge void*) device; + } + + void *GetQueue() override { + return (__bridge void*) queue; + } + + AvnMetalDevice(id device, id queue) : device(device), queue(queue) { + } + +}; + + +class AvnMetalRenderSession : public ComSingleObject +{ + id _drawable; + id _queue; + id _texture; + CAMetalLayer* _layer; + AvnPixelSize _size; + double _scaling; +public: + FORWARD_IUNKNOWN() + + AvnMetalRenderSession(AvnMetalDevice* device, CAMetalLayer* layer, id drawable, const AvnPixelSize &size, double scaling) + : _drawable(drawable), _size(size), _scaling(scaling), _queue(device->queue), + _texture([drawable texture]) { + _layer = layer; + } + + HRESULT GetPixelSize(AvnPixelSize *ret) override { + *ret = _size; + return 0; + } + + double GetScaling() override { + return _scaling; + } + + void *GetTexture() override { + return (__bridge void*) _texture; + } + + ~AvnMetalRenderSession() + { + auto buffer = [_queue commandBuffer]; + [buffer presentDrawable: _drawable]; + [buffer commit]; + } +}; + +class AvnMetalRenderTarget : public ComSingleObject +{ + CAMetalLayer* _layer; + double _scaling = 1; + AvnPixelSize _size = {1,1}; + ComPtr _device; +public: + double PendingScaling = 1; + AvnPixelSize PendingSize = {1,1}; + FORWARD_IUNKNOWN() + AvnMetalRenderTarget(CAMetalLayer* layer, ComPtr device) + { + _layer = layer; + _device = device; + } + + HRESULT BeginDrawing(IAvnMetalRenderingSession **ret) override { + if([NSThread isMainThread]) + { + // Flush all existing rendering + auto buffer = [_device->queue commandBuffer]; + [buffer commit]; + [buffer waitUntilCompleted]; + _size = PendingSize; + _scaling= PendingScaling; + CGSize layerSize = {(CGFloat)_size.Width, (CGFloat)_size.Height}; + + [_layer setDrawableSize: layerSize]; + } + auto drawable = [_layer nextDrawable]; + if(drawable == nil) + { + ret = nil; + return E_FAIL; + } + *ret = new AvnMetalRenderSession(_device, _layer, drawable, _size, _scaling); + return 0; + } +}; + +@implementation MetalRenderTarget +{ + ComPtr _device; + CAMetalLayer* _layer; + ComPtr _target; +} +- (MetalRenderTarget *)initWithDevice:(IAvnMetalDevice *)device { + _device = dynamic_cast(device); + _layer = [CAMetalLayer new]; + _layer.device = _device->device; + _target.setNoAddRef(new AvnMetalRenderTarget(_layer, _device)); + return self; +} + + +-(void) getRenderTarget: (IAvnMetalRenderTarget**) ppv +{ + *ppv = static_cast(_target.getRetainedReference()); +} + +- (void)resize:(AvnPixelSize)size withScale:(float)scale { + CGSize layerSize = {(CGFloat)size.Width, (CGFloat)size.Height}; + _target->PendingScaling = scale; + _target->PendingSize = size; + [_layer setNeedsDisplay]; +} + +- (CALayer *)layer { + return _layer; +} +@end + + +class AvnMetalDisplay : public ComSingleObject +{ +public: + FORWARD_IUNKNOWN() + HRESULT CreateDevice(IAvnMetalDevice **ret) override { + + auto device = MTLCreateSystemDefaultDevice(); + if(device == nil) { + ret = nil; + return E_FAIL; + } + auto queue = [device newCommandQueue]; + *ret = new AvnMetalDevice(device, queue); + return S_OK; + } +}; + +static AvnMetalDisplay* _display = new AvnMetalDisplay(); + +extern IAvnMetalDisplay* GetMetalDisplay() +{ + return _display; +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/noarc.mm b/native/Avalonia.Native/src/OSX/noarc.mm new file mode 100644 index 0000000000..82378ce84c --- /dev/null +++ b/native/Avalonia.Native/src/OSX/noarc.mm @@ -0,0 +1,11 @@ +#include "noarc.h" + +CppAutoreleasePool::CppAutoreleasePool() +{ + _pool = [[NSAutoreleasePool alloc] init]; +} + +CppAutoreleasePool::~CppAutoreleasePool() { + auto ptr = (NSAutoreleasePool*)_pool; + [ptr release]; +} diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 1c22c91207..b44a605803 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -5,15 +5,12 @@ #include #include +#include -@interface IOSurfaceHolder : NSObject -@end - -@implementation IOSurfaceHolder +@implementation IOSurfaceHolder : NSObject { @public IOSurfaceRef surface; @public AvnPixelSize size; - @public bool hasContent; @public float scale; ComPtr _context; GLuint _framebuffer, _texture, _renderbuffer; @@ -42,7 +39,6 @@ self->scale = scale; self->size = size; self->_context = context; - self->hasContent = false; return self; } @@ -76,7 +72,7 @@ } glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT, _texture, 0); } - + if(_renderbuffer == 0) { glGenRenderbuffers(1, &_renderbuffer); @@ -84,48 +80,72 @@ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderbuffer); } - + return S_OK; } -(void) finishDraw { - ComPtr release; - _context->MakeCurrent(release.getPPV()); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + if(_framebuffer != 0) { + glDeleteFramebuffers(1, &_framebuffer); + _framebuffer = 0; + } + if(_texture != 0) { + glDeleteTextures(1, &_texture); + _texture = 0; + } + if(_renderbuffer != 0) { + glDeleteRenderbuffers(1, &_renderbuffer); + _renderbuffer = 0; + } glFlush(); - self->hasContent = true; } -(void) dealloc { - - if(_framebuffer != 0) - { - ComPtr release; - _context->MakeCurrent(release.getPPV()); - glDeleteFramebuffers(1, &_framebuffer); - if(_texture != 0) - glDeleteTextures(1, &_texture); - if(_renderbuffer != 0) - glDeleteRenderbuffers(1, &_renderbuffer); - } - if(surface != nullptr) { CFRelease(surface); + surface = nil; } } @end + static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target); +static IAvnSoftwareRenderTarget* CreateSoftwareRenderTarget(IOSurfaceRenderTarget* target); + +static bool SizeEquals(AvnPixelSize& left, AvnPixelSize& right) +{ + return left.Width == right.Width && right.Height == left.Height; +} + +class ConsumeSurfacesCallback : public ComSingleObject +{ + IOSurfaceRenderTarget* _target; +public: + FORWARD_IUNKNOWN() + ConsumeSurfacesCallback(IOSurfaceRenderTarget* target) + { + _target = target; + } + + void Run() override { + [_target consumeSurfaces]; + } +}; @implementation IOSurfaceRenderTarget { CALayer* _layer; - @public IOSurfaceHolder* surface; @public NSObject* lock; ComPtr _glContext; + bool _consumeSurfacesScheduled; + std::queue> _surfaces; + IOSurfaceHolder* _activeSurface; + AvnPixelSize _size; + float _scale; } - (IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context; @@ -133,16 +153,12 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta self = [super init]; _glContext = context; lock = [NSObject new]; - surface = nil; + _layer = [CALayer new]; [self resize:{1,1} withScale: 1]; return self; } -- (AvnPixelSize) pixelSize { - return {1, 1}; -} - - (CALayer *)layer { return _layer; } @@ -155,57 +171,110 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta size.Width = 1; @synchronized (lock) { - if(surface == nil - || surface->size.Width != size.Width - || surface->size.Height != size.Height - || surface->scale != scale) - { - surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()]; - - [self updateLayer]; - } + _size = size; + _scale = scale; } } -- (void)updateLayer { - if ([NSThread isMainThread]) +- (void)setSurfaceInUiThreadContext: (IOSurfaceHolder*) surface +{ + [CATransaction begin]; + if(surface == _activeSurface) + [_layer setContents: nil]; + _activeSurface = surface; + [_layer setContentsScale: _activeSurface->scale]; + [_layer setContents: (__bridge IOSurface*) _activeSurface->surface]; + [CATransaction commit]; +} + +- (void)consumeSurfaces { + @synchronized (lock) { + _consumeSurfacesScheduled = false; + + while(_surfaces.size() > 1) + _surfaces.pop(); + + if(_surfaces.size() == 0) + return; + + auto targetSurface = _surfaces.front(); + _surfaces.pop(); + + [self setSurfaceInUiThreadContext: targetSurface]; + } + // This can trigger event processing on the main thread + // which might need to lock the renderer + // which can cause a deadlock. So flush call is outside of the lock + [CATransaction flush]; + +} + +- (IOSurfaceHolder*) getNextSurfaceInSafeContext +{ + IOSurfaceHolder* targetSurface = nil; + if([NSThread isMainThread]) { - @synchronized (lock) { - if(_layer == nil) - return; - if(!surface->hasContent) - return; - [CATransaction begin]; - [_layer setContents: nil]; - if(surface != nil) + // Drain the surface queue and try to find a surface usable for rendering + while(_surfaces.size() > 0) + { + auto front = _surfaces.front(); + _surfaces.pop(); + if(targetSurface == nil && SizeEquals(front.Value->size, _size)) { - [_layer setContentsScale: surface->scale]; - [_layer setContents: (__bridge IOSurface*) surface->surface]; + targetSurface = front; } - [CATransaction commit]; } - // This can trigger event processing on the main thread - // which might need to lock the renderer - // which can cause a deadlock. So flush call is outside of the lock - [CATransaction flush]; + if(targetSurface == nil && _activeSurface != nil && SizeEquals(_activeSurface->size, _size)) + targetSurface = _activeSurface; + } + else + { + // Try to reuse an outdated surface that is still not picked up by the UI thread + while(_surfaces.size() > 1) + { + auto front = _surfaces.front(); + _surfaces.pop(); + + // Simply discard the surface on size mismatch + if(SizeEquals(front.Value->size, _size)) + targetSurface = front; + } } + + if(targetSurface == nil) + targetSurface = [[IOSurfaceHolder alloc] initWithSize: _size withScale: _scale withOpenGlContext: _glContext]; + return targetSurface; +} + +- (void) presentSurfaceInSafeContext: (IOSurfaceHolder*) surface +{ + if([NSThread isMainThread]) + [self setSurfaceInUiThreadContext: surface]; else - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateLayer]; - }); + { + _surfaces.push(surface); + if(_consumeSurfacesScheduled) + return; + _consumeSurfacesScheduled = true; + __block auto strongSelf = self; + ComPtr cb(new ConsumeSurfacesCallback(self), true); + PostDispatcherCallback(cb); + } } -- (void) setNewLayer:(CALayer *)layer { - _layer = layer; - [self updateLayer]; +- (void) presentSurface: (IOSurfaceHolder*) surface +{ + @synchronized(lock) + { + [self presentSurfaceInSafeContext: surface]; + } } - (HRESULT)setSwFrame:(AvnFramebuffer *)fb { @synchronized (lock) { if(fb->PixelFormat == AvnPixelFormat::kAvnRgb565) return E_INVALIDARG; - if(surface == nil) - return E_FAIL; + auto surface = [self getNextSurfaceInSafeContext]; IOSurfaceRef surf = surface->surface; if(IOSurfaceLock(surf, 0, nil)) return E_FAIL; @@ -221,8 +290,7 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta memcpy(pSurface + y*sstride, pFb + y*fstride, wbytes); } IOSurfaceUnlock(surf, 0, nil); - surface->hasContent = true; - [self updateLayer]; + [self presentSurfaceInSafeContext: surface]; return S_OK; } } @@ -232,6 +300,11 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta return CreateGlRenderTarget(self); } +-(IAvnSoftwareRenderTarget*) createSoftwareRenderTarget +{ + return CreateSoftwareRenderTarget(self); +} + @end class AvnGlRenderingSession : public ComSingleObject @@ -241,12 +314,12 @@ class AvnGlRenderingSession : public ComSingleObject releaseContext) + AvnGlRenderingSession(IOSurfaceRenderTarget* target, IOSurfaceHolder* surface, ComPtr releaseContext) { _target = target; // This happens in a synchronized block set up by AvnRenderTarget, so we take the current surface for this // particular render session - _surface = _target->surface; + _surface = surface; _releaseContext = releaseContext; } @@ -272,8 +345,9 @@ public: virtual ~AvnGlRenderingSession() { + START_ARP_CALL; [_surface finishDraw]; - [_target updateLayer]; + [_target presentSurface: _surface]; _releaseContext = nil; } }; @@ -290,17 +364,15 @@ public: virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) override { - START_COM_CALL; - + START_COM_ARP_CALL; ComPtr releaseContext; @synchronized (_target->lock) { - if(_target->surface == nil) - return E_FAIL; + auto surface = [_target getNextSurfaceInSafeContext]; _target->_glContext->MakeCurrent(releaseContext.getPPV()); - HRESULT res = [_target->surface prepareForGlRender]; + HRESULT res = [surface prepareForGlRender]; if(res) return res; - *ret = new AvnGlRenderingSession(_target, releaseContext); + *ret = new AvnGlRenderingSession(_target, surface, releaseContext); return S_OK; } } @@ -310,4 +382,27 @@ public: static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target) { return new AvnGlRenderTarget(target); -} +}; + + +class AvnSoftwareRenderTarget : public ComSingleObject +{ + IOSurfaceRenderTarget* _target; +public: + FORWARD_IUNKNOWN() + + AvnSoftwareRenderTarget(IOSurfaceRenderTarget *target) { + _target = target; + } + + HRESULT SetFrame(AvnFramebuffer *fb) override { + START_COM_ARP_CALL; + [_target setSwFrame: fb]; + return 0; + } +}; + +static IAvnSoftwareRenderTarget* CreateSoftwareRenderTarget(IOSurfaceRenderTarget* target) +{ + return new AvnSoftwareRenderTarget(target); +}; diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs index 56a4eb22d4..365c9cdb42 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs @@ -13,5 +13,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform } public ILockedFramebuffer Lock() => new AndroidFramebuffer(_topLevel.InternalView.Holder.Surface, _topLevel.RenderScaling); + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } } diff --git a/src/Avalonia.Base/RenderTargetNotReadyException.cs b/src/Avalonia.Base/RenderTargetNotReadyException.cs new file mode 100644 index 0000000000..e34dd41709 --- /dev/null +++ b/src/Avalonia.Base/RenderTargetNotReadyException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Avalonia; + +public class RenderTargetNotReadyException : Exception +{ + public RenderTargetNotReadyException() + { + } + + public RenderTargetNotReadyException(string message) + : base(message) + { + } + + public RenderTargetNotReadyException(Exception innerException) + : base(null, innerException) + { + } + + public RenderTargetNotReadyException(string message, Exception innerException) + : base(message, innerException) + { + } +} diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 8f1aa1cb49..3883e4e574 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -135,7 +135,18 @@ namespace Avalonia.Rendering.Composition.Server _redrawRequested = true; } - _renderTarget ??= _compositor.CreateRenderTarget(_surfaces()); + try + { + _renderTarget ??= _compositor.CreateRenderTarget(_surfaces()); + } + catch (RenderTargetNotReadyException) + { + return; + } + catch (RenderTargetCorruptedException) + { + return; + } if ((_dirtyRect.Width == 0 && _dirtyRect.Height == 0) && !_redrawRequested) return; diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 0a7daeaa24..22ee322e36 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -1,10 +1,17 @@ -using Avalonia.Metadata; +using System; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { [Unstable] public interface IFramebufferPlatformSurface + { + IFramebufferRenderTarget CreateFramebufferRenderTarget(); + } + + [Unstable] + public interface IFramebufferRenderTarget : IDisposable { /// /// Provides a framebuffer descriptor for drawing. @@ -14,4 +21,24 @@ namespace Avalonia.Controls.Platform.Surfaces /// ILockedFramebuffer Lock(); } + + /// + /// For simple cases when framebuffer is always available + /// + public class FuncFramebufferRenderTarget : IFramebufferRenderTarget + { + private readonly Func _lockFramebuffer; + + public FuncFramebufferRenderTarget(Func lockFramebuffer) + { + _lockFramebuffer = lockFramebuffer; + } + + public void Dispose() + { + // No-op + } + + public ILockedFramebuffer Lock() => _lockFramebuffer(); + } } diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 74f12280bb..ac7e8ec179 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -282,9 +282,6 @@ namespace Avalonia.Controls.Remote.Server } } - public ILockedFramebuffer Lock() - => GetOrCreateFramebuffer().Lock(_sendLastFrameIfNeeded); - private void SendLastFrameIfNeeded() { if (IsDisposed) @@ -328,5 +325,8 @@ namespace Avalonia.Controls.Remote.Server public override IMouseDevice MouseDevice { get; } = new MouseDevice(); public IKeyboardDevice KeyboardDevice { get; } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => + new FuncFramebufferRenderTarget(() => GetOrCreateFramebuffer().Lock(_sendLastFrameIfNeeded)); } } diff --git a/src/Avalonia.Metal/Avalonia.Metal.csproj b/src/Avalonia.Metal/Avalonia.Metal.csproj new file mode 100644 index 0000000000..6b17caecbb --- /dev/null +++ b/src/Avalonia.Metal/Avalonia.Metal.csproj @@ -0,0 +1,15 @@ + + + net6.0;netstandard2.0 + true + + + + + + + + + + + diff --git a/src/Avalonia.Metal/IMetalDevice.cs b/src/Avalonia.Metal/IMetalDevice.cs new file mode 100644 index 0000000000..0910e6aa17 --- /dev/null +++ b/src/Avalonia.Metal/IMetalDevice.cs @@ -0,0 +1,34 @@ +using System; +using Avalonia.Metadata; +using Avalonia.Platform; + +namespace Avalonia.Metal; + + +[PrivateApi] +public interface IMetalDevice : IPlatformGraphicsContext +{ + IntPtr Device { get; } + IntPtr CommandQueue { get; } +} + +[PrivateApi] +public interface IMetalPlatformSurface +{ + IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device); +} + +[PrivateApi] +public interface IMetalPlatformSurfaceRenderTarget : IDisposable +{ + IMetalPlatformSurfaceRenderingSession BeginRendering(); +} + +[PrivateApi] +public interface IMetalPlatformSurfaceRenderingSession : IDisposable +{ + IntPtr Texture { get; } + PixelSize Size { get; } + double Scaling { get; } + bool IsYFlipped { get; } +} diff --git a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs index 8e40960af6..16280a90cd 100644 --- a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs +++ b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs @@ -192,10 +192,13 @@ namespace Avalonia.Native { _window = window; } + public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context) { + if (!Dispatcher.UIThread.CheckAccess()) + throw new RenderTargetNotReadyException(); var avnContext = (GlContext)context; - return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(), avnContext); + return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(avnContext.Context), avnContext); } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 5d5e17839f..7da15811db 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -19,7 +19,7 @@ namespace Avalonia.Native { private readonly IAvaloniaNativeFactory _factory; private AvaloniaNativePlatformOptions? _options; - private AvaloniaNativeGlPlatformGraphics? _platformGl; + private IPlatformGraphics? _platformGraphics; [DllImport("libAvaloniaNative")] static extern IntPtr CreateAvaloniaNative(); @@ -93,7 +93,7 @@ namespace Avalonia.Native if (_factory.MacOptions != null) _factory.MacOptions.SetDisableAppDelegate(macOpts.DisableAvaloniaAppDelegate ? 1 : 0); - _factory.Initialize(new GCHandleDeallocator(), applicationPlatform); + _factory.Initialize(new GCHandleDeallocator(), applicationPlatform, new AvnDispatcher()); if (_factory.MacOptions != null) { @@ -126,20 +126,38 @@ namespace Avalonia.Native if (_options.UseGpu) { - try + if (_options.UseMetal) { - _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(_platformGl); - + try + { + var metal = new MetalPlatformGraphics(_factory); + metal.CreateContext().Dispose(); + _platformGraphics = metal; + } + catch + { + // Ignored + } } - catch (Exception) + + if (_platformGraphics == null) { - // ignored + try + { + _platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); + } + catch (Exception) + { + // ignored + } } + + if(_platformGraphics != null) + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(_platformGraphics); } - - Compositor = new Compositor(_platformGl, true); + + Compositor = new Compositor(_platformGraphics, true); } public ITrayIconImpl CreateTrayIcon() @@ -149,7 +167,7 @@ namespace Avalonia.Native public IWindowImpl CreateWindow() { - return new WindowImpl(_factory, _options, _platformGl); + return new WindowImpl(_factory, _options); } public IWindowImpl CreateEmbeddableWindow() diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 2b989ce733..78b4b0fbf3 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -35,6 +35,8 @@ namespace Avalonia /// public bool UseGpu { get; set; } = true; + public bool UseMetal { get; set; } + /// /// Embeds popups to the window when set to true. The default value is false. /// diff --git a/src/Avalonia.Native/AvnDispatcher.cs b/src/Avalonia.Native/AvnDispatcher.cs new file mode 100644 index 0000000000..b3658d5e67 --- /dev/null +++ b/src/Avalonia.Native/AvnDispatcher.cs @@ -0,0 +1,18 @@ +using Avalonia.Native.Interop; +using Avalonia.Threading; +using MicroCom.Runtime; + +namespace Avalonia.Native; + +class AvnDispatcher : NativeCallbackBase, IAvnDispatcher +{ + public void Post(IAvnActionCallback cb) + { + var callback = cb.CloneReference(); + Dispatcher.UIThread.Post(() => + { + using (callback) + callback.Run(); + }, DispatcherPriority.Send); + } +} diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs index c390459286..0942bee508 100644 --- a/src/Avalonia.Native/DeferredFramebuffer.cs +++ b/src/Avalonia.Native/DeferredFramebuffer.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using Avalonia.Controls.Platform.Surfaces; using Avalonia.Native.Interop; using Avalonia.Platform; @@ -7,11 +8,13 @@ namespace Avalonia.Native { internal unsafe class DeferredFramebuffer : ILockedFramebuffer { - private readonly Func, bool> _lockWindow; - - public DeferredFramebuffer(Func, bool> lockWindow, + private readonly IAvnSoftwareRenderTarget _renderTarget; + private readonly Action> _lockWindow; + + public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action> lockWindow, int width, int height, Vector dpi) { + _renderTarget = renderTarget; _lockWindow = lockWindow; Address = Marshal.AllocHGlobal(width * height * 4); Size = new PixelSize(width, height); @@ -27,54 +30,28 @@ namespace Avalonia.Native public Vector Dpi { get; set; } public PixelFormat Format { get; set; } - class Disposer : NativeCallbackBase - { - private IntPtr _ptr; - - public Disposer(IntPtr ptr) - { - _ptr = ptr; - } - - protected override void Destroyed() - { - if(_ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(_ptr); - _ptr = IntPtr.Zero; - } - } - } - public void Dispose() { if (Address == IntPtr.Zero) return; - if (!_lockWindow(win => + _lockWindow(win => { var fb = new AvnFramebuffer { Data = Address.ToPointer(), - Dpi = new AvnVector - { - X = Dpi.X, - Y = Dpi.Y - }, + Dpi = new AvnVector { X = Dpi.X, Y = Dpi.Y }, Width = Size.Width, Height = Size.Height, PixelFormat = (AvnPixelFormat)Format.FormatEnum, Stride = RowBytes }; - using (var d = new Disposer(Address)) - { - win.ThreadSafeSetSwRenderedFrame(&fb, d); - } - })) - { - Marshal.FreeHGlobal(Address); - } + _renderTarget.SetFrame(&fb); + + }); + + Marshal.FreeHGlobal(Address); Address = IntPtr.Zero; } diff --git a/src/Avalonia.Native/Metal.cs b/src/Avalonia.Native/Metal.cs new file mode 100644 index 0000000000..8c8d293576 --- /dev/null +++ b/src/Avalonia.Native/Metal.cs @@ -0,0 +1,119 @@ +using System; +using Avalonia.Metal; +using Avalonia.Native.Interop; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.Utilities; + +namespace Avalonia.Native; + +class MetalPlatformGraphics : IPlatformGraphics +{ + private readonly IAvnMetalDisplay _display; + + public MetalPlatformGraphics(IAvaloniaNativeFactory factory) + { + _display = factory.ObtainMetalDisplay(); + } + public bool UsesSharedContext => false; + public IPlatformGraphicsContext CreateContext() => new MetalDevice(_display.CreateDevice()); + + public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); +} + +class MetalDevice : IMetalDevice +{ + public IAvnMetalDevice Native { get; private set; } + private DisposableLock _syncRoot = new(); + + + public MetalDevice(IAvnMetalDevice native) + { + Native = native; + } + + public void Dispose() + { + Native?.Dispose(); + Native = null; + } + + public object TryGetFeature(Type featureType) => null; + + public bool IsLost => false; + + public IDisposable EnsureCurrent() => _syncRoot.Lock(); + + public IntPtr Device => Native.Device; + public IntPtr CommandQueue => Native.Queue; +} + +class MetalPlatformSurface : IMetalPlatformSurface +{ + private readonly IAvnWindowBase _window; + + public MetalPlatformSurface(IAvnWindowBase window) + { + _window = window; + } + public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device) + { + if (!Dispatcher.UIThread.CheckAccess()) + throw new RenderTargetNotReadyException(); + + var dev = (MetalDevice)device; + var target = _window.CreateMetalRenderTarget(dev.Native); + return new MetalRenderTarget(target); + } +} + +internal class MetalRenderTarget : IMetalPlatformSurfaceRenderTarget +{ + private IAvnMetalRenderTarget _native; + + public MetalRenderTarget(IAvnMetalRenderTarget native) + { + _native = native; + } + + public void Dispose() + { + _native?.Dispose(); + _native = null; + } + + public IMetalPlatformSurfaceRenderingSession BeginRendering() + { + var session = _native.BeginDrawing(); + return new MetalDrawingSession(session); + } +} + +internal class MetalDrawingSession : IMetalPlatformSurfaceRenderingSession +{ + private IAvnMetalRenderingSession _session; + + public MetalDrawingSession(IAvnMetalRenderingSession session) + { + _session = session; + } + + public void Dispose() + { + _session?.Dispose(); + _session = null; + } + + public IntPtr Texture => _session.Texture; + public PixelSize Size + { + get + { + var size = _session.PixelSize; + return new(size.Width, size.Height); + } + } + + public double Scaling => _session.Scaling; + public bool IsYFlipped => false; +} diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 6b7f7e8883..2dad083767 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -8,21 +8,15 @@ namespace Avalonia.Native { class PopupImpl : WindowBaseImpl, IPopupImpl { - private readonly AvaloniaNativePlatformOptions _opts; - private readonly AvaloniaNativeGlPlatformGraphics _glFeature; private readonly IWindowBaseImpl _parent; public PopupImpl(IAvaloniaNativeFactory factory, - AvaloniaNativePlatformOptions opts, - AvaloniaNativeGlPlatformGraphics glFeature, - IWindowBaseImpl parent) : base(factory, opts, glFeature) + IWindowBaseImpl parent) : base(factory) { - _opts = opts; - _glFeature = glFeature; _parent = parent; using (var e = new PopupEvents(this)) { - Init(factory.CreatePopup(e, _glFeature.SharedContext.Context), factory.CreateScreens()); + Init(factory.CreatePopup(e), factory.CreateScreens()); } PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); } @@ -68,7 +62,7 @@ namespace Avalonia.Native base.Show(false, isDialog); } - public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this); + public override IPopupImpl CreatePopup() => new PopupImpl(_factory, this); public void SetWindowManagerAddShadowHint(bool enabled) { diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 817fe3d080..f1b7f6876c 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -15,7 +15,7 @@ namespace Avalonia.Native internal class WindowImpl : WindowBaseImpl, IWindowImpl { private readonly AvaloniaNativePlatformOptions _opts; - private readonly AvaloniaNativeGlPlatformGraphics _glFeature; + private readonly AvaloniaNativeGlPlatformGraphics _graphics; IAvnWindow _native; private double _extendTitleBarHeight = -1; private DoubleClickHelper _doubleClickHelper; @@ -23,16 +23,14 @@ namespace Avalonia.Native private readonly AvaloniaNativeTextInputMethod _inputMethod; private bool _canResize = true; - internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, - AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature) + internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(factory) { _opts = opts; - _glFeature = glFeature; _doubleClickHelper = new DoubleClickHelper(); using (var e = new WindowEvents(this)) { - Init(_native = factory.CreateWindow(e, glFeature.SharedContext.Context), factory.CreateScreens()); + Init(_native = factory.CreateWindow(e), factory.CreateScreens()); } _nativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory); @@ -215,7 +213,7 @@ namespace Avalonia.Native public void Move(PixelPoint point) => Position = point; public override IPopupImpl CreatePopup() => - _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, _glFeature, this); + _opts.OverlayPopups ? null : new PopupImpl(_factory, this); public Action GotInputWhenDisabled { get; set; } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 760816643e..3b4e457ba5 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -54,24 +54,20 @@ namespace Avalonia.Native protected IInputRoot _inputRoot; IAvnWindowBase _native; private object _syncRoot = new object(); - private bool _gpu = false; private readonly MouseDevice _mouse; private readonly IKeyboardDevice _keyboard; private readonly ICursorFactory _cursorFactory; private Size _savedLogicalSize; private Size _lastRenderedLogicalSize; private double _savedScaling; - private GlPlatformSurface _glSurface; private NativeControlHostImpl _nativeControlHost; private IStorageProvider _storageProvider; private PlatformBehaviorInhibition _platformBehaviorInhibition; private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; - internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, - AvaloniaNativeGlPlatformGraphics glFeature) + internal WindowBaseImpl(IAvaloniaNativeFactory factory) { _factory = factory; - _gpu = opts.UseGpu && glFeature != null; _keyboard = AvaloniaLocator.Current.GetService(); _mouse = new MouseDevice(); @@ -82,9 +78,8 @@ namespace Avalonia.Native { _native = window; + Surfaces = new object[] { new GlPlatformSurface(window), new MetalPlatformSurface(window), this }; Handle = new MacOSTopLevelWindowHandle(window); - if (_gpu) - _glSurface = new GlPlatformSurface(window); Screen = new ScreenImpl(screens); _savedLogicalSize = ClientSize; @@ -133,29 +128,55 @@ namespace Avalonia.Native } } - public IEnumerable Surfaces => new[] { - (_gpu ? _glSurface : (object)null), - this - }; + public IEnumerable Surfaces { get; private set; } public INativeControlHostImpl NativeControlHost => _nativeControlHost; - public ILockedFramebuffer Lock() + + IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget() { - var w = _savedLogicalSize.Width * _savedScaling; - var h = _savedLogicalSize.Height * _savedScaling; - var dpi = _savedScaling * 96; - return new DeferredFramebuffer(cb => + if (!Dispatcher.UIThread.CheckAccess()) + throw new RenderTargetNotReadyException(); + return new FramebufferRenderTarget(this, _native.CreateSoftwareRenderTarget()); + } + + class FramebufferRenderTarget : IFramebufferRenderTarget + { + private readonly WindowBaseImpl _parent; + private IAvnSoftwareRenderTarget? _target; + + public FramebufferRenderTarget(WindowBaseImpl parent, IAvnSoftwareRenderTarget target) { - lock (_syncRoot) + _parent = parent; + _target = target; + } + + public void Dispose() + { + lock (_parent._syncRoot) { - if (_native == null) - return false; - cb(_native); - _lastRenderedLogicalSize = _savedLogicalSize; - return true; + _target?.Dispose(); + _target = null; } - }, (int)w, (int)h, new Vector(dpi, dpi)); + } + + public ILockedFramebuffer Lock() + { + var w = _parent._savedLogicalSize.Width * _parent._savedScaling; + var h = _parent._savedLogicalSize.Height * _parent._savedScaling; + var dpi = _parent._savedScaling * 96; + return new DeferredFramebuffer(_target, cb => + { + lock (_parent._syncRoot) + { + if (_parent._native != null && _target != null) + { + cb(_parent._native); + _parent._lastRenderedLogicalSize = _parent._savedLogicalSize; + } + } + }, (int)w, (int)h, new Vector(dpi, dpi)); + } } public Action LostFocus { get; set; } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index a58a00d59d..bc372bbcb5 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -485,10 +485,11 @@ enum AvnPlatformThemeVariant [uuid(809c652e-7396-11d2-9771-00a0c9b4d50c)] interface IAvaloniaNativeFactory : IUnknown { - HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* appCb); + HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* appCb, + IAvnDispatcher* dispatcher); IAvnMacOptions* GetMacOptions(); - HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv); - HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv); + HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv); + HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv); HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv); HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv); HRESULT CreateScreens(IAvnScreens** ppv); @@ -496,6 +497,7 @@ interface IAvaloniaNativeFactory : IUnknown HRESULT CreateDndClipboard(IAvnClipboard** ppv); HRESULT CreateCursorFactory(IAvnCursorFactory** ppv); HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv); + HRESULT ObtainMetalDisplay(IAvnMetalDisplay** ppv); HRESULT SetAppMenu(IAvnMenu* menu); HRESULT SetServicesMenu(IAvnMenu* menu); HRESULT CreateMenu(IAvnMenuEvents* cb, IAvnMenu** ppv); @@ -533,10 +535,11 @@ interface IAvnWindowBase : IUnknown HRESULT SetPosition(AvnPoint point); HRESULT PointToClient(AvnPoint point, AvnPoint*ret); HRESULT PointToScreen(AvnPoint point, AvnPoint*ret); - HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose); HRESULT SetTopMost(bool value); HRESULT SetCursor(IAvnCursor* cursor); - HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret); + HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret); + HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret); + HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret); HRESULT SetMainMenu(IAvnMenu* menu); HRESULT ObtainNSWindowHandle([intptr]void** retOut); HRESULT ObtainNSWindowHandleRetained([intptr]void** retOut); @@ -734,6 +737,13 @@ interface IAvnCursorFactory : IUnknown HRESULT CreateCustomCursor (void* bitmapData, size_t length, AvnPixelSize hotPixel, IAvnCursor** retOut); } +[uuid(931062d2-5bc8-4062-8588-83dd8deb99c2)] +interface IAvnSoftwareRenderTarget : IUnknown +{ + HRESULT SetFrame(AvnFramebuffer* fb); +} + + [uuid(60452465-8616-40af-bc00-042e69828ce7)] interface IAvnGlDisplay : IUnknown { @@ -766,6 +776,33 @@ interface IAvnGlSurfaceRenderingSession : IUnknown HRESULT GetScaling(double* ret); } +[uuid(da291767-4db3-4598-893d-09ecaa23893f)] +interface IAvnMetalDisplay : IUnknown +{ + HRESULT CreateDevice(IAvnMetalDevice** ret); +} + +[uuid(969fa914-b74a-4c9f-8725-5160dc63579e)] +interface IAvnMetalDevice : IUnknown +{ + [intptr]void* GetDevice(); + [intptr]void* GetQueue(); +} + +[uuid(f1306b71-eca0-426e-8700-105192693b1a)] +interface IAvnMetalRenderTarget : IUnknown +{ + HRESULT BeginDrawing(IAvnMetalRenderingSession** ret); +} + +[uuid(e625b406-f04c-484e-946a-4abd2c6015ad)] +interface IAvnMetalRenderingSession : IUnknown +{ + HRESULT GetPixelSize(AvnPixelSize* ret); + double GetScaling(); + [intptr]void* GetTexture(); +} + [uuid(60992d19-38f0-4141-a0a9-76ac303801f3)] interface IAvnTrayIcon : IUnknown { @@ -828,6 +865,12 @@ interface IAvnGCHandleDeallocatorCallback : IUnknown void FreeGCHandle([intptr]void* handle); } +[uuid(96688589-5dc7-41ec-9ce3-d481942454ee)] +interface IAvnDispatcher : IUnknown +{ + void Post(IAvnActionCallback* cb); +} + [uuid(91c7f677-f26b-4ff3-93cc-cf15aa966ffa)] interface IAvnNativeControlHost : IUnknown { diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 9818d3f86a..4060869b19 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -139,6 +139,8 @@ namespace Avalonia.X11 _pixelSize, _pixelSize.Width * 4, new Vector(96, 96), PixelFormat.Bgra8888, null); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } } diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs index 05885d1a25..6daa040593 100644 --- a/src/Avalonia.X11/X11FramebufferSurface.cs +++ b/src/Avalonia.X11/X11FramebufferSurface.cs @@ -27,5 +27,7 @@ namespace Avalonia.X11 XUnlockDisplay(_display); return new X11Framebuffer(_display, _xid, _depth, width, height, _scaling()); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } } diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index bede875e89..cb09802d76 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -86,5 +86,7 @@ namespace Avalonia.X11 new Vector(96, 96), PixelFormat.Bgra8888, () => h.Free()); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } } diff --git a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs index 76b07a6827..777d44ea66 100644 --- a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs @@ -84,5 +84,7 @@ namespace Avalonia.Browser.Skia Marshal.FreeHGlobal(Address); } } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } } diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs index c7eb07ba10..964934d6cf 100644 --- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs @@ -218,6 +218,8 @@ namespace Avalonia.Headless }); } + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); + public WriteableBitmap? GetLastRenderedFrame() { lock (_sync) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs index 56c6bdb8c4..d85485e7b3 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs @@ -152,6 +152,8 @@ namespace Avalonia.LinuxFramebuffer new FbDevBackBuffer(_fd, _fixedInfo, _varInfo, _mappedAddress)) .Lock(new Vector(96, 96) * Scaling); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); private void ReleaseUnmanagedResources() diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index 64afaa6817..2cdc9a2d82 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs @@ -12,12 +12,12 @@ namespace Avalonia.Skia /// internal class FramebufferRenderTarget : IRenderTarget { - private readonly IFramebufferPlatformSurface _platformSurface; private SKImageInfo _currentImageInfo; private IntPtr _currentFramebufferAddress; private SKSurface? _framebufferSurface; private PixelFormatConversionShim? _conversionShim; private IDisposable? _preFramebufferCopyHandler; + private IFramebufferRenderTarget? _renderTarget; /// /// Create new framebuffer render target using a target surface. @@ -25,19 +25,24 @@ namespace Avalonia.Skia /// Target surface. public FramebufferRenderTarget(IFramebufferPlatformSurface platformSurface) { - _platformSurface = platformSurface; + _renderTarget = platformSurface.CreateFramebufferRenderTarget(); } /// public void Dispose() { + _renderTarget?.Dispose(); + _renderTarget = null; FreeSurface(); } /// public IDrawingContextImpl CreateDrawingContext() { - var framebuffer = _platformSurface.Lock(); + if (_renderTarget == null) + throw new ObjectDisposedException(nameof(FramebufferRenderTarget)); + + var framebuffer = _renderTarget.Lock(); var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height, framebuffer.Format.ToSkColorType(), framebuffer.Format == PixelFormat.Rgb565 ? SKAlphaType.Opaque : SKAlphaType.Premul); diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs new file mode 100644 index 0000000000..9475d96fc0 --- /dev/null +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -0,0 +1,80 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; +using Avalonia.Platform.Interop; +using SkiaSharp; +using BindingFlags = System.Reflection.BindingFlags; + +namespace Avalonia.Skia.Metal; + +internal unsafe class SkiaMetalApi +{ + delegate* unmanaged[Stdcall] _gr_direct_context_make_metal_with_options; + private delegate* unmanaged[Stdcall] + _gr_backendrendertarget_new_metal; + private readonly ConstructorInfo _contextCtor; + private readonly MethodInfo _contextOptionsToNative; + private readonly ConstructorInfo _renderTargetCtor; + + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(GRContext))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(GRBackendRenderTarget))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, typeof(GRContextOptions))] + public SkiaMetalApi() + { + // Make sure that skia is loaded + GC.KeepAlive(new SKPaint()); + + var loader = AvaloniaLocator.Current.GetRequiredService(); +#if NET6_0_OR_GREATER + var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); +#else + var dll = loader.LoadLibrary("libSkiaSharp"); +#endif + _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] ) + loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall]) + loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); + + _contextCtor = typeof(GRContext).GetConstructor( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, + new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); + + + _renderTargetCtor = typeof(GRBackendRenderTarget).GetConstructor( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, + new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); + + _contextOptionsToNative = typeof(GRContextOptions).GetMethod("ToNative", + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + ?? throw new MissingMemberException("GRContextOptions.ToNative()"); + } + + public GRContext CreateContext(IntPtr device, IntPtr queue, GRContextOptions? options) + { + options ??= new(); + var nativeOptions = _contextOptionsToNative.Invoke(options, null)!; + var pOptions = Marshal.AllocHGlobal(Marshal.SizeOf(nativeOptions)); + Marshal.StructureToPtr(nativeOptions, pOptions, false); + var context = _gr_direct_context_make_metal_with_options(device, queue, pOptions); + Marshal.FreeHGlobal(pOptions); + if (context == IntPtr.Zero) + throw new ArgumentException(); + return (GRContext)_contextCtor.Invoke(new object[] { context, true }); + } + + internal struct GRMtlTextureInfoNative + { + public IntPtr Texture; + } + + public GRBackendRenderTarget CreateBackendRenderTarget(int width, int height, int samples, IntPtr texture) + { + var info = new GRMtlTextureInfoNative() { Texture = texture }; + var target = _gr_backendrendertarget_new_metal(width, height, samples, &info); + if (target == IntPtr.Zero) + throw new InvalidOperationException("Unable to create GRBackendRenderTarget"); + return (GRBackendRenderTarget)_renderTargetCtor.Invoke(new object[] { target, true }); + } +} diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs new file mode 100644 index 0000000000..8965f4fc7a --- /dev/null +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Avalonia.Metal; +using SkiaSharp; + +namespace Avalonia.Skia.Metal; + +internal class SkiaMetalGpu : ISkiaGpu +{ + private SkiaMetalApi _api = new(); + private GRContext? _context; + private readonly IMetalDevice _device; + + public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes) + { + _context = _api.CreateContext(device.Device, device.CommandQueue, + new GRContextOptions() { AvoidStencilBuffers = true }); + _device = device; + if (maxResourceBytes.HasValue) + _context.SetResourceCacheLimit(maxResourceBytes.Value); + } + + public void Dispose() + { + _context?.Dispose(); + _context = null; + } + + public object? TryGetFeature(Type featureType) => null; + + public bool IsLost => false; + public IDisposable EnsureCurrent() => _device.EnsureCurrent(); + + public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable surfaces) + { + foreach (var surface in surfaces) + { + if (surface is IMetalPlatformSurface metalSurface) + { + var target = metalSurface.CreateMetalRenderTarget(_device); + return new SkiaMetalRenderTarget(this, target); + } + } + + return null; + } + + public class SkiaMetalRenderTarget : ISkiaGpuRenderTarget + { + private readonly SkiaMetalGpu _gpu; + private IMetalPlatformSurfaceRenderTarget? _target; + + public SkiaMetalRenderTarget(SkiaMetalGpu gpu, IMetalPlatformSurfaceRenderTarget target) + { + _gpu = gpu; + _target = target; + } + + public void Dispose() + { + _target?.Dispose(); + _target = null; + } + + public ISkiaGpuRenderSession BeginRenderingSession() + { + var session = (_target ?? throw new ObjectDisposedException(nameof(SkiaMetalRenderTarget))).BeginRendering(); + var backendTarget = _gpu._api.CreateBackendRenderTarget(session.Size.Width, session.Size.Height, + 1, session.Texture); + + var surface = SKSurface.Create(_gpu._context!, backendTarget, + session.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft, + SKColorType.Bgra8888); + + return new SkiaMetalRenderSession(_gpu, surface, session); + } + + public bool IsCorrupted => false; + } + + internal class SkiaMetalRenderSession : ISkiaGpuRenderSession + { + private readonly SkiaMetalGpu _gpu; + private SKSurface? _surface; + private IMetalPlatformSurfaceRenderingSession? _session; + + public SkiaMetalRenderSession(SkiaMetalGpu gpu, + SKSurface surface, + IMetalPlatformSurfaceRenderingSession session) + { + _gpu = gpu; + _surface = surface; + _session = session; + } + + public void Dispose() + { + _surface?.Canvas.Flush(); + _surface?.Flush(); + _gpu._context?.Flush(); + + _surface?.Dispose(); + _surface = null; + _session?.Dispose(); + _session = null; + } + + public GRContext GrContext => _gpu._context!; + public SKSurface SkSurface => _surface!; + public double ScaleFactor => _session!.Scaling; + + public GRSurfaceOrigin SurfaceOrigin => + _session!.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft; + } + + + public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session) => null; +} diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index f0a25cc1cb..fe74971432 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -7,6 +7,8 @@ using Avalonia.Platform; using Avalonia.Media.Imaging; using SkiaSharp; using Avalonia.Media.TextFormatting; +using Avalonia.Metal; +using Avalonia.Skia.Metal; namespace Avalonia.Skia { @@ -32,6 +34,8 @@ namespace Avalonia.Skia return new SkiaContext(skiaGpu); if (graphicsContext is IGlContext gl) return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes)); + if (graphicsContext is IMetalDevice metal) + return new SkiaContext(new SkiaMetalGpu(metal, _maxResourceBytes)); throw new ArgumentException("Graphics context of type is not supported"); } diff --git a/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs index e20755b4e2..454e137389 100644 --- a/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs @@ -28,4 +28,6 @@ internal class RenderTargetBitmapImpl : WriteableBitmapImpl, _renderTarget.Dispose(); base.Dispose(); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } \ No newline at end of file diff --git a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs index 0af326d6a8..1f20b3ed08 100644 --- a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs @@ -11,20 +11,24 @@ namespace Avalonia.Direct2D1 { class FramebufferShimRenderTarget : IRenderTarget { - private readonly IFramebufferPlatformSurface _surface; + private IFramebufferRenderTarget? _target; public FramebufferShimRenderTarget(IFramebufferPlatformSurface surface) { - _surface = surface; + _target = surface.CreateFramebufferRenderTarget(); } public void Dispose() - { + { + _target?.Dispose(); + _target = null; } public IDrawingContextImpl CreateDrawingContext() { - var locked = _surface.Lock(); + if (_target == null) + throw new ObjectDisposedException(nameof(FramebufferShimRenderTarget)); + var locked = _target.Lock(); if (locked.Format == PixelFormat.Rgb565) { locked.Dispose(); diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index cba4879f5b..47cab24aa2 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -60,6 +60,8 @@ namespace Avalonia.Win32 } } } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); public void Dispose() { diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index 6916d0c130..131f5ec18a 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -53,12 +53,8 @@ namespace Avalonia.Direct2D1.RenderTests.Media //no-op } - public ILockedFramebuffer Lock() - { - return this; - } - public void Deallocate() => Marshal.FreeHGlobal(Address); + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(() => this); } diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 3e0ba7d2f6..5c50b3cf11 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -135,7 +135,10 @@ namespace Avalonia.Direct2D1.RenderTests _bitmap = bitmap; } - public ILockedFramebuffer Lock() => _bitmap.Lock(); + public IFramebufferRenderTarget CreateFramebufferRenderTarget() + { + return new FuncFramebufferRenderTarget(() => _bitmap.Lock()); + } } protected void CompareImages([CallerMemberName] string testName = "", diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs index 0a85072af1..53fd610a17 100644 --- a/tests/Avalonia.UnitTests/CompositorTestServices.cs +++ b/tests/Avalonia.UnitTests/CompositorTestServices.cs @@ -161,6 +161,8 @@ public class CompositorTestServices : IDisposable return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96), PixelFormat.Rgba8888, () => Marshal.FreeHGlobal(ptr)); } + + public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock); } public Compositor Compositor => _compositor; From e44c5a869a49b799b1dab78339cd92f5dabc4105 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 30 Jun 2023 16:15:07 +0600 Subject: [PATCH 227/391] Use rendering mode --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 39 +++++++++++-------- .../AvaloniaNativePlatformExtensions.cs | 36 ++++++++++++++--- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 7da15811db..37e5e41faf 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -123,40 +123,45 @@ namespace Avalonia.Native hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); AvaloniaLocator.CurrentMutable.Bind().ToConstant(hotkeys); - - if (_options.UseGpu) + + foreach (var mode in _options.RenderingMode) { - if (_options.UseMetal) + if (mode == AvaloniaNativeRenderingMode.OpenGl) { try { - var metal = new MetalPlatformGraphics(_factory); - metal.CreateContext().Dispose(); - _platformGraphics = metal; + _platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); + break; } - catch + catch (Exception) { - // Ignored + // ignored } } - - if (_platformGraphics == null) +#pragma warning disable CS0618 + else if (mode == AvaloniaNativeRenderingMode.Metal) +#pragma warning restore CS0618 { try { - _platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay()); + var metal = new MetalPlatformGraphics(_factory); + metal.CreateContext().Dispose(); + _platformGraphics = metal; } - catch (Exception) + catch { - // ignored + // Ignored } } - - if(_platformGraphics != null) - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(_platformGraphics); + else if (mode == AvaloniaNativeRenderingMode.Software) + break; } + if (_platformGraphics != null) + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(_platformGraphics); + + Compositor = new Compositor(_platformGraphics, true); } diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 6193895b67..b670f302ac 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -26,18 +26,44 @@ namespace Avalonia } } + public enum AvaloniaNativeRenderingMode + { + /// + /// Avalonia would try to use native OpenGL with GPU rendering. + /// + OpenGl = 1, + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 2, + /// + /// Avalonia would try to use Metal with GPU rendering. + /// + [Obsolete("Experimental, unstable, not for production usage")] + Metal = 3 + } + /// /// OSX backend options. /// public class AvaloniaNativePlatformOptions { /// - /// Determines whether to use GPU for rendering in your project. The default value is true. + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . /// - public bool UseGpu { get; set; } = true; - - public bool UseMetal { get; set; } - + /// + /// If application should work on as wide range of devices as possible, + /// at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + AvaloniaNativeRenderingMode.OpenGl, + AvaloniaNativeRenderingMode.Software + }; + /// /// Embeds popups to the window when set to true. The default value is false. /// From fe7a118aa80ed60dc602cfdbdaef1cbbb3a491f8 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 30 Jun 2023 12:15:18 +0200 Subject: [PATCH 228/391] Fix Imm32InputMethod --- src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 6b283282bf..495e808a04 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -325,7 +325,7 @@ namespace Avalonia.Win32.Input { Client.SetPreeditText(null); - if (Client.SupportsSurroundingText && Client.SurroundingText.AnchorOffset != Client.SurroundingText.CursorOffset) + if (Client.SupportsSurroundingText && Client.Selection.Start != Client.Selection.End) { KeyPress(Key.Delete); } From a457dff79c1d1a197ae3076b15ae9e4bd966cc67 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 11:44:16 +0100 Subject: [PATCH 229/391] whitespace --- src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 495e808a04..1beccb5239 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -325,7 +325,7 @@ namespace Avalonia.Win32.Input { Client.SetPreeditText(null); - if (Client.SupportsSurroundingText && Client.Selection.Start != Client.Selection.End) + if (Client.SupportsSurroundingText && Client.Selection.Start != Client.Selection.End) { KeyPress(Key.Delete); } From 1b10cc2ef89aaca8582e49bb4314800160396a93 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 11:44:39 +0100 Subject: [PATCH 230/391] revert whitespace --- src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 1beccb5239..495e808a04 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -325,7 +325,7 @@ namespace Avalonia.Win32.Input { Client.SetPreeditText(null); - if (Client.SupportsSurroundingText && Client.Selection.Start != Client.Selection.End) + if (Client.SupportsSurroundingText && Client.Selection.Start != Client.Selection.End) { KeyPress(Key.Delete); } From 58f619290bbb86a292c2f3610b3c977bd79a5d12 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 12:16:08 +0100 Subject: [PATCH 231/391] attempt to make build services auto updated. --- packages/Avalonia/Avalonia.csproj | 1 - packages/Avalonia/Avalonia.props | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index b8373cf609..00f0c0577c 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,6 @@ - all diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index a2da228887..eaf11d805a 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -13,5 +13,6 @@ + From e7e37cfaa1cb9dd4ff35871e8588433c12188484 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 12:45:00 +0100 Subject: [PATCH 232/391] Revert "attempt to make build services auto updated." This reverts commit 58f619290bbb86a292c2f3610b3c977bd79a5d12. --- packages/Avalonia/Avalonia.csproj | 1 + packages/Avalonia/Avalonia.props | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 00f0c0577c..b8373cf609 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,6 +5,7 @@ + all diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index eaf11d805a..a2da228887 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -13,6 +13,5 @@ - From 628a54be63fb7c4ee9b7eee62b38f95ca3fd1595 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 12:46:13 +0100 Subject: [PATCH 233/391] fix build services version. --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index b8373cf609..50b7df15fa 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From 2aabc38895d5bf1a7446216d34020c3248ecf5db Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 13:50:50 +0100 Subject: [PATCH 234/391] fix build services. --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 50b7df15fa..b2d369f93b 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From 013e9dd8f071f2c42590f5a78e9e83a3c7f43548 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 30 Jun 2023 15:24:51 +0200 Subject: [PATCH 235/391] Fix PresenterPropertyChanged add handler --- src/Avalonia.Controls/TextBox.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 3b92baa42a..37e7b7beac 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -812,14 +812,14 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTree(e); - if (IsFocused) + if (_presenter != null) { - if(_presenter != null) + if (IsFocused) { _presenter.ShowCaret(); - - _presenter.PropertyChanged += PresenterPropertyChanged; } + + _presenter.PropertyChanged += PresenterPropertyChanged; } } From 125cbc42bb7a1f98fde22c30aebe18040b70ca7a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 15:32:57 +0100 Subject: [PATCH 236/391] Ensure render is started when a window opens. --- src/Avalonia.Controls/Window.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 42052b8d69..ce4fc8dd5c 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -715,8 +715,8 @@ namespace Avalonia.Controls SetWindowStartupLocation(owner); - PlatformImpl?.Show(ShowActivated, false); StartRendering(); + PlatformImpl?.Show(ShowActivated, false); OnOpened(EventArgs.Empty); } } @@ -791,9 +791,8 @@ namespace Avalonia.Controls SetWindowStartupLocation(owner); - PlatformImpl?.Show(ShowActivated, true); - StartRendering(); + PlatformImpl?.Show(ShowActivated, true); Observable.FromEventPattern( x => Closed += x, From b580d3f986ea81907246d9b45665713d160f8439 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 17:21:06 +0100 Subject: [PATCH 237/391] fix native api loading. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index 9475d96fc0..bfd486c60a 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; +using Avalonia.Compatibility; using Avalonia.Platform.Interop; using SkiaSharp; using BindingFlags = System.Reflection.BindingFlags; @@ -24,19 +25,23 @@ internal unsafe class SkiaMetalApi { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var loader = AvaloniaLocator.Current.GetRequiredService(); -#if NET6_0_OR_GREATER - var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); -#else - var dll = loader.LoadLibrary("libSkiaSharp"); -#endif - _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] ) - loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall]) - loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); - + + var dll = NativeLibraryEx.Load("libSkiaSharp"); + + IntPtr address; + + if (NativeLibraryEx.TryGetExport(dll, "gr_direct_context_make_metal_with_options", out address)) + { + _gr_direct_context_make_metal_with_options = + (delegate* unmanaged[Stdcall] )address; + } + + if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) + { + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall])address; + } + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From 3b23aa2f079b0d2db7a5e5e4fe248c9abc1bf9b6 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 30 Jun 2023 21:53:59 +0200 Subject: [PATCH 238/391] Enforce that TextBox and TextPresenter are in sync when SurroundingText is requested --- src/Avalonia.Controls/TextBoxTextInputMethodClient.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 3c86471fbb..66581df3be 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -25,15 +25,14 @@ namespace Avalonia.Controls #if DEBUG if (_parent.CaretIndex != _presenter.CaretIndex) { - throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); + _presenter.SetCurrentValue(TextPresenter.CaretIndexProperty, _parent.CaretIndex); } if (_parent.Text != _presenter.Text) { - throw new InvalidOperationException("TextBox and TextPresenter are out of sync"); + _presenter.SetCurrentValue(TextPresenter.TextProperty, _parent.Text); } #endif - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); var textLine = _presenter.TextLayout.TextLines[lineIndex]; From 792143d09d55a666eed15ce77625f70c266dfba6 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 30 Jun 2023 21:56:54 +0200 Subject: [PATCH 239/391] Remove compiler conditionals --- src/Avalonia.Controls/TextBoxTextInputMethodClient.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 66581df3be..28a230640a 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -21,8 +21,7 @@ namespace Avalonia.Controls { return ""; } - -#if DEBUG + if (_parent.CaretIndex != _presenter.CaretIndex) { _presenter.SetCurrentValue(TextPresenter.CaretIndexProperty, _parent.CaretIndex); @@ -32,7 +31,7 @@ namespace Avalonia.Controls { _presenter.SetCurrentValue(TextPresenter.TextProperty, _parent.Text); } -#endif + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); var textLine = _presenter.TextLayout.TextLines[lineIndex]; From ae2aee402d30712ba10c115585ac35f34f897ec4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 21:42:54 +0100 Subject: [PATCH 240/391] Revert "fix native api loading." This reverts commit b580d3f986ea81907246d9b45665713d160f8439. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index bfd486c60a..9475d96fc0 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,7 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; -using Avalonia.Compatibility; using Avalonia.Platform.Interop; using SkiaSharp; using BindingFlags = System.Reflection.BindingFlags; @@ -25,23 +24,19 @@ internal unsafe class SkiaMetalApi { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var dll = NativeLibraryEx.Load("libSkiaSharp"); - - IntPtr address; - - if (NativeLibraryEx.TryGetExport(dll, "gr_direct_context_make_metal_with_options", out address)) - { - _gr_direct_context_make_metal_with_options = - (delegate* unmanaged[Stdcall] )address; - } - - if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) - { - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall])address; - } - + + var loader = AvaloniaLocator.Current.GetRequiredService(); +#if NET6_0_OR_GREATER + var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); +#else + var dll = loader.LoadLibrary("libSkiaSharp"); +#endif + _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] ) + loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall]) + loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From ce4a8137ee60ec38ccc0aa220da4eec8d47dcf4c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 16:51:15 -0400 Subject: [PATCH 241/391] Revert "Revert "fix native api loading."" This reverts commit ae2aee402d30712ba10c115585ac35f34f897ec4. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index 9475d96fc0..bfd486c60a 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; +using Avalonia.Compatibility; using Avalonia.Platform.Interop; using SkiaSharp; using BindingFlags = System.Reflection.BindingFlags; @@ -24,19 +25,23 @@ internal unsafe class SkiaMetalApi { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var loader = AvaloniaLocator.Current.GetRequiredService(); -#if NET6_0_OR_GREATER - var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); -#else - var dll = loader.LoadLibrary("libSkiaSharp"); -#endif - _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] ) - loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall]) - loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); - + + var dll = NativeLibraryEx.Load("libSkiaSharp"); + + IntPtr address; + + if (NativeLibraryEx.TryGetExport(dll, "gr_direct_context_make_metal_with_options", out address)) + { + _gr_direct_context_make_metal_with_options = + (delegate* unmanaged[Stdcall] )address; + } + + if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) + { + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall])address; + } + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From 1489fcc62d743e4516e8b3ebac6aec6161e8cca3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 16:59:39 -0400 Subject: [PATCH 242/391] Pass typeof(SKPaint).Assembly as well --- src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index bfd486c60a..e133e3dcde 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -26,7 +26,7 @@ internal unsafe class SkiaMetalApi // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - var dll = NativeLibraryEx.Load("libSkiaSharp"); + var dll = NativeLibraryEx.Load("libSkiaSharp", typeof(SKPaint).Assembly); IntPtr address; From 6499bbd4dc11ef4046b861de3418b7a66ae5b87c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 17:06:07 -0400 Subject: [PATCH 243/391] Throw if methods were not found --- src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index e133e3dcde..3022f92ec5 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -35,12 +35,22 @@ internal unsafe class SkiaMetalApi _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] )address; } + else + { + throw new InvalidOperationException( + "Unable to export gr_direct_context_make_metal_with_options. Make sure SkiaSharp is up to date."); + } if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) { _gr_backendrendertarget_new_metal = (delegate* unmanaged[Stdcall])address; } + else + { + throw new InvalidOperationException( + "Unable to export gr_backendrendertarget_new_metal. Make sure SkiaSharp is up to date."); + } _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, From 26da71181c3c4bcce088cee4378c830ce8b41efa Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 1 Jul 2023 14:55:14 +0100 Subject: [PATCH 244/391] fix incompatibility with .net framework --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index b2d369f93b..4258572df7 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From 01a57b251978f0b8f9fe0699e7c518160a59ff12 Mon Sep 17 00:00:00 2001 From: Mike James Date: Sun, 2 Jul 2023 10:17:39 +0200 Subject: [PATCH 245/391] Removing compositional renderer license. Reverting to MIT --- src/Avalonia.Base/Rendering/Composition/License.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/Avalonia.Base/Rendering/Composition/License.md diff --git a/src/Avalonia.Base/Rendering/Composition/License.md b/src/Avalonia.Base/Rendering/Composition/License.md deleted file mode 100644 index d1b709859e..0000000000 --- a/src/Avalonia.Base/Rendering/Composition/License.md +++ /dev/null @@ -1,7 +0,0 @@ -Please note: Any code in this directory is excluded from the normal MIT license. - -This code is owned and copyright to Avalonia OU. - -This code may be used free of charge by any application that consumes Avalonia binary packages as a direct or indirect dependency. - -Explicit permission is required for any other use outside of Avalonia applications. \ No newline at end of file From 1f06e4af50ae9ff56d4af8869cf0cdf0298e98ba Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 13:31:33 +0300 Subject: [PATCH 246/391] use real data formats while storing atoms early --- src/Avalonia.X11/X11Clipboard.cs | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index 6e18151fae..bed0a8cebf 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -82,18 +82,9 @@ namespace Avalonia.X11 Encoding textEnc; if (target == _x11.Atoms.TARGETS) { - var atoms = new HashSet { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE }; - foreach (var fmt in _storedDataObject.GetDataFormats()) - { - if (fmt == DataFormats.Text) - foreach (var ta in _textAtoms) - atoms.Add(ta); - else - atoms.Add(_x11.Atoms.GetAtom(fmt)); - } - + var atoms = ConvertDataObject(_storedDataObject); XChangeProperty(_x11.Display, window, property, - _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms.ToArray(), atoms.Count); + _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); return property; } else if(target == _x11.Atoms.SAVE_TARGETS && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) @@ -252,13 +243,29 @@ namespace Avalonia.X11 return (string)await SendDataRequest(target); } - private void StoreAtomsInClipboardManager(IntPtr[] atoms) + + private IntPtr[] ConvertDataObject(IDataObject data) + { + var atoms = new HashSet { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE }; + foreach (var fmt in data.GetDataFormats()) + { + if (fmt == DataFormats.Text) + foreach (var ta in _textAtoms) + atoms.Add(ta); + else + atoms.Add(_x11.Atoms.GetAtom(fmt)); + } + return atoms.ToArray(); + } + + private void StoreAtomsInClipboardManager(IDataObject data) { if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) { var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); if (clipboardManager != IntPtr.Zero) - { + { + var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); @@ -283,8 +290,8 @@ namespace Avalonia.X11 public Task SetDataObjectAsync(IDataObject data) { _storedDataObject = data; - XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); - StoreAtomsInClipboardManager(_textAtoms); + XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); + StoreAtomsInClipboardManager(data); return Task.CompletedTask; } From 0d1f7fac56936312c2d4249eb182ea6ae137dd35 Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 14:19:10 +0300 Subject: [PATCH 247/391] await for completion when storing atoms early --- src/Avalonia.X11/X11Clipboard.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index bed0a8cebf..53190b0f3e 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -14,6 +14,7 @@ namespace Avalonia.X11 private readonly X11Info _x11; private IDataObject _storedDataObject; private IntPtr _handle; + private TaskCompletionSource _storeAtomTcs; private TaskCompletionSource _requestedFormatsTcs; private TaskCompletionSource _requestedDataTcs; private readonly IntPtr[] _textAtoms; @@ -52,6 +53,12 @@ namespace Avalonia.X11 private unsafe void OnEvent(ref XEvent ev) { + if (ev.type == XEventName.SelectionClear) + { + _storeAtomTcs?.TrySetResult(); + return; + } + if (ev.type == XEventName.SelectionRequest) { var sel = ev.SelectionRequestEvent; @@ -258,21 +265,26 @@ namespace Avalonia.X11 return atoms.ToArray(); } - private void StoreAtomsInClipboardManager(IDataObject data) + private Task StoreAtomsInClipboardManager(IDataObject data) { if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) { var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); if (clipboardManager != IntPtr.Zero) - { + { + if (_storeAtomTcs == null || _storeAtomTcs.Task.IsCompleted) + _storeAtomTcs = new TaskCompletionSource(); + var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER, _x11.Atoms.SAVE_TARGETS, _avaloniaSaveTargetsAtom, _handle, IntPtr.Zero); + return _storeAtomTcs.Task; } } + return Task.CompletedTask; } public Task SetTextAsync(string text) @@ -291,8 +303,7 @@ namespace Avalonia.X11 { _storedDataObject = data; XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); - StoreAtomsInClipboardManager(data); - return Task.CompletedTask; + return StoreAtomsInClipboardManager(data); } public async Task GetFormatsAsync() From 84d00f2aa338422f70e8f845ed710e981dd7a0a7 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 2 Jul 2023 09:08:06 -0400 Subject: [PATCH 248/391] Fix ImportCompleted spelling --- .../Rendering/Composition/CompositionExternalMemory.cs | 3 ++- .../Rendering/Composition/CompositionInterop.cs | 6 +++--- .../Composition/Server/ServerCompositionDrawingSurface.cs | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs index ce728f86a2..1021bc58b4 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs @@ -91,7 +91,8 @@ public interface ICompositionGpuImportedObject : IAsyncDisposable /// the user code is allowed to free the resource owner in case when a non-owning /// sharing handle was used /// - Task ImportCompeted { get; } + Task ImportCompleted { get; } + /// /// Indicates if the device context this instance is associated with is no longer available /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs index 1643ec6e8d..9eea228e84 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs @@ -67,18 +67,18 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject Context = context; Feature = feature; - ImportCompeted = Compositor.InvokeServerJobAsync(Import); + ImportCompleted = Compositor.InvokeServerJobAsync(Import); } protected abstract void Import(); public abstract void Dispose(); - public Task ImportCompeted { get; } + public Task ImportCompleted { get; } public bool IsLost => Context.IsLost; public ValueTask DisposeAsync() => new(Compositor.InvokeServerJobAsync(() => { - if (ImportCompeted.Status == TaskStatus.RanToCompletion) + if (ImportCompleted.Status == TaskStatus.RanToCompletion) Dispose(); })); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs index da24b3812a..f58403f0bc 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs @@ -31,12 +31,12 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp throw new PlatformGraphicsContextLostException(); // This should never happen, but check for it anyway to avoid a deadlock - if (!image.ImportCompeted.IsCompleted) + if (!image.ImportCompleted.IsCompleted) throw new InvalidOperationException("The import operation is not completed yet"); // Rethrow the import here exception - if (image.ImportCompeted.IsFaulted) - image.ImportCompeted.GetAwaiter().GetResult(); + if (image.ImportCompleted.IsFaulted) + image.ImportCompleted.GetAwaiter().GetResult(); } void Update(IBitmapImpl newImage, IPlatformRenderInterfaceContext context) From ad79bc6663daab7675089871fefd22bd1b872df8 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Sun, 2 Jul 2023 23:38:38 +0800 Subject: [PATCH 249/391] Delete readme.md --- src/Avalonia.Base/Rendering/Composition/readme.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/Avalonia.Base/Rendering/Composition/readme.md diff --git a/src/Avalonia.Base/Rendering/Composition/readme.md b/src/Avalonia.Base/Rendering/Composition/readme.md deleted file mode 100644 index 6c238db191..0000000000 --- a/src/Avalonia.Base/Rendering/Composition/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -Please note the composition renderer is not subject to the usual MIT license. - -Please contact us for more details and see [License.md](https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md) From 22f68362b6af22a1ebdb39373a2aa142dd468f1c Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 19:47:19 +0300 Subject: [PATCH 250/391] fix error CS0305: Using the generic type 'TaskCompletionSource' requires 1 type arguments --- src/Avalonia.X11/X11Clipboard.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index 53190b0f3e..637d44d617 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -14,7 +14,7 @@ namespace Avalonia.X11 private readonly X11Info _x11; private IDataObject _storedDataObject; private IntPtr _handle; - private TaskCompletionSource _storeAtomTcs; + private TaskCompletionSource _storeAtomTcs; private TaskCompletionSource _requestedFormatsTcs; private TaskCompletionSource _requestedDataTcs; private readonly IntPtr[] _textAtoms; @@ -55,7 +55,7 @@ namespace Avalonia.X11 { if (ev.type == XEventName.SelectionClear) { - _storeAtomTcs?.TrySetResult(); + _storeAtomTcs?.TrySetResult(true); return; } @@ -273,7 +273,7 @@ namespace Avalonia.X11 if (clipboardManager != IntPtr.Zero) { if (_storeAtomTcs == null || _storeAtomTcs.Task.IsCompleted) - _storeAtomTcs = new TaskCompletionSource(); + _storeAtomTcs = new TaskCompletionSource(); var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, From 8a8ecd9a10cfcfd28339e925fc68b418d1f7523a Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Mon, 3 Jul 2023 07:07:15 +0800 Subject: [PATCH 251/391] remove comment referencing removed license --- .../Rendering/Composition/Animations/AnimationInstanceBase.cs | 2 -- .../Rendering/Composition/Animations/CompositionAnimation.cs | 2 -- .../Composition/Animations/CompositionAnimationGroup.cs | 3 --- .../Rendering/Composition/Animations/ExpressionAnimation.cs | 2 -- .../Composition/Animations/ExpressionAnimationInstance.cs | 1 - .../Rendering/Composition/Animations/IAnimationInstance.cs | 2 -- .../Composition/Animations/ICompositionAnimationBase.cs | 2 -- .../Composition/Animations/ImplicitAnimationCollection.cs | 2 -- .../Rendering/Composition/Animations/Interpolators.cs | 2 -- .../Rendering/Composition/Animations/KeyFrameAnimation.cs | 2 -- .../Composition/Animations/KeyFrameAnimationInstance.cs | 2 -- .../Rendering/Composition/Animations/KeyFrames.cs | 2 -- .../Rendering/Composition/Animations/PropertySetSnapshot.cs | 2 -- src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs | 2 -- .../Rendering/Composition/CompositionDrawListVisual.cs | 3 --- src/Avalonia.Base/Rendering/Composition/CompositionObject.cs | 2 -- .../Rendering/Composition/CompositionPropertySet.cs | 2 -- src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Compositor.cs | 3 --- src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs | 2 -- .../Rendering/Composition/ElementCompositionPreview.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Enums.cs | 2 -- .../Rendering/Composition/Expressions/BuiltInExpressionFfi.cs | 2 -- .../Rendering/Composition/Expressions/DelegateExpressionFfi.cs | 2 -- .../Rendering/Composition/Expressions/Expression.cs | 2 -- .../Composition/Expressions/ExpressionEvaluationContext.cs | 2 -- .../Composition/Expressions/ExpressionParseException.cs | 2 -- .../Rendering/Composition/Expressions/ExpressionParser.cs | 2 -- .../Composition/Expressions/ExpressionTrackedValues.cs | 2 -- .../Rendering/Composition/Expressions/ExpressionVariant.cs | 2 -- .../Rendering/Composition/Expressions/TokenParser.cs | 2 -- .../Rendering/Composition/ICompositionTargetDebugEvents.cs | 2 -- src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs | 2 -- .../Rendering/Composition/Server/CompositionProperty.cs | 2 -- .../Rendering/Composition/Server/DrawingContextProxy.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs | 1 - .../Rendering/Composition/Server/ReadbackIndices.cs | 2 -- .../Composition/Server/ServerCompositionContainerVisual.cs | 2 -- .../Composition/Server/ServerCompositionDrawListVisual.cs | 2 -- .../Rendering/Composition/Server/ServerCompositionSurface.cs | 2 -- .../Rendering/Composition/Server/ServerCompositionTarget.cs | 2 -- .../Server/ServerCompositionVisual.DirtyProperties.cs | 2 -- .../Rendering/Composition/Server/ServerCompositionVisual.cs | 2 -- .../Rendering/Composition/Server/ServerCompositor.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs | 2 -- .../Rendering/Composition/Transport/BatchStream.cs | 2 -- .../Rendering/Composition/Transport/BatchStreamArrayPool.cs | 2 -- .../Rendering/Composition/Transport/BatchStreamDebugMarker.cs | 2 -- .../Rendering/Composition/Transport/ServerListProxyHelper.cs | 2 -- src/Avalonia.Base/Rendering/Composition/Visual.cs | 2 -- src/Avalonia.Base/Rendering/Composition/VisualCollection.cs | 2 -- 53 files changed, 107 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs b/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs index 80b6bb9d2a..64aa1736f8 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs index 455e9ebb5f..4dbeb7f152 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs @@ -5,8 +5,6 @@ using System.Numerics; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs index 1500e88abe..89d5a73612 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs @@ -2,9 +2,6 @@ using System; using System.Collections.Generic; using Avalonia.Rendering.Composition.Transport; - -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { public class CompositionAnimationGroup : CompositionObject, ICompositionAnimationBase diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs index ec2972044e..80263884bb 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs @@ -3,8 +3,6 @@ using System; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs index 6c7c556456..569a98e93e 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md namespace Avalonia.Rendering.Composition.Animations { diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs index b14d2c0b19..43bc642c73 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs @@ -2,8 +2,6 @@ using System; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { internal interface IAnimationInstance : IServerClockItem diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs index 91b37a81af..0fd976684e 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs @@ -2,8 +2,6 @@ using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs index d9adf261f8..ca8566f804 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Avalonia.Rendering.Composition.Transport; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs index 0df21c2c1d..33caef5e68 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs @@ -1,8 +1,6 @@ using System; using System.Numerics; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs index d21a4d06e3..6cfc279580 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs @@ -2,8 +2,6 @@ using System; using Avalonia.Animation; using Avalonia.Animation.Easings; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs index 986110b5e6..f29bb71ede 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs @@ -4,8 +4,6 @@ using Avalonia.Animation; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs index 79045ccff8..7c4109785d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using Avalonia.Animation.Easings; using Avalonia.Rendering.Composition.Expressions; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs b/src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs index af3c344999..3fb6fd6796 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using Avalonia.Rendering.Composition.Expressions; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Animations { /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 368743f1b5..7c5f5d75d5 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -10,8 +10,6 @@ using Avalonia.Media; using Avalonia.Rendering.Composition.Drawing; using Avalonia.VisualTree; -// Special license applies License.md - namespace Avalonia.Rendering.Composition; /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs index 4ce8a9f9ae..c4e2038450 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs @@ -2,11 +2,8 @@ using Avalonia.Rendering.Composition.Drawing; using Avalonia.Rendering.Composition.Server; using Avalonia.Rendering.Composition.Transport; -// Special license applies License.md - namespace Avalonia.Rendering.Composition; - /// /// A composition visual that holds a list of drawing commands issued by /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs index 942b27d84e..9392c6a617 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs @@ -7,8 +7,6 @@ using Avalonia.Rendering.Composition.Server; using Avalonia.Rendering.Composition.Transport; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs b/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs index 8052d9c32c..66f52c5f80 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs @@ -6,8 +6,6 @@ using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Server; using Avalonia.Rendering.Composition.Transport; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index 121d11bf2a..8ffdd649d5 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs @@ -4,8 +4,6 @@ using System.Numerics; using Avalonia.Collections.Pooled; using Avalonia.VisualTree; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs index 5838811e9e..902a195098 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs @@ -13,9 +13,6 @@ using Avalonia.Rendering.Composition.Transport; using Avalonia.Threading; using Avalonia.Utilities; - -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs index 9b9c44b95f..f79828c9b4 100644 --- a/src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs @@ -1,7 +1,5 @@ using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs index b01321edd8..6d9b9e23a8 100644 --- a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs +++ b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs @@ -1,5 +1,3 @@ -// Special license applies License.md - using System; using Avalonia.VisualTree; diff --git a/src/Avalonia.Base/Rendering/Composition/Enums.cs b/src/Avalonia.Base/Rendering/Composition/Enums.cs index cc37b25701..20b3c31206 100644 --- a/src/Avalonia.Base/Rendering/Composition/Enums.cs +++ b/src/Avalonia.Base/Rendering/Composition/Enums.cs @@ -1,7 +1,5 @@ using System; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { public enum CompositionBlendMode diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs index 2fdf163153..4164f96c30 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs @@ -4,8 +4,6 @@ using System.Numerics; using Avalonia.Rendering.Composition.Animations; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs index ad8d370478..c70056b5aa 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs @@ -5,8 +5,6 @@ using System.Linq; using System.Numerics; using Avalonia.Media; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs index b15da5d05d..11a0b9e227 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs @@ -4,8 +4,6 @@ using System.Globalization; using System.Reflection; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs index f268364b54..6d52dd7de0 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { internal struct ExpressionEvaluationContext diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs index c32a7832a6..d8b43b46e2 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs @@ -1,7 +1,5 @@ using System; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { internal class ExpressionParseException : Exception diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs index 3937d48187..ce3d89a670 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs @@ -5,8 +5,6 @@ using System.Linq; // ReSharper disable StringLiteralTypo -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { internal class ExpressionParser diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs index e386e058df..727a555ea9 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions; internal class ExpressionTrackedObjects : IEnumerable diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs index a97b959fe1..136ef69b55 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs @@ -4,8 +4,6 @@ using System.Numerics; using System.Runtime.InteropServices; using Avalonia.Media; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { internal enum VariantType diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs index 3991300fb5..22e1521887 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Expressions { /// diff --git a/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs index 131a51b5e6..c830ca2c49 100644 --- a/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs +++ b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs @@ -1,5 +1,3 @@ -// Special license applies License.md - namespace Avalonia.Rendering.Composition; internal interface ICompositionTargetDebugEvents diff --git a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs index 714895cd71..4ec1326103 100644 --- a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs +++ b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs @@ -1,7 +1,5 @@ using System.Numerics; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { static class MatrixUtils diff --git a/src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs b/src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs index 5d63c8a4b3..c6ff722778 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Threading; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server; internal class CompositionProperty diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index 14dd59b7e3..61f8c8451f 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -8,8 +8,6 @@ using Avalonia.Rendering.Composition.Drawing; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs index 03bd965fa8..00526eb4d1 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs @@ -4,7 +4,6 @@ using System.Globalization; using Avalonia.Media; using Avalonia.Platform; -// Special license applies License.md namespace Avalonia.Rendering.Composition.Server; diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs b/src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs index d9df176e66..9f0341826a 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs @@ -1,5 +1,3 @@ -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs index c63e7917b3..f2a777b4bd 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs @@ -3,8 +3,6 @@ using System.Numerics; using Avalonia.Media; using Avalonia.Platform; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs index 7d6e9442d1..5da8afd9cd 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs @@ -7,8 +7,6 @@ using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs index 88f10ba642..a20eaabd7b 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs @@ -1,5 +1,3 @@ -// Special license applies License.md - using System; using Avalonia.Platform; using Avalonia.Utilities; diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 3883e4e574..9dc5c27d79 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -9,8 +9,6 @@ using Avalonia.Platform; using Avalonia.Rendering.Composition.Transport; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs index 405c7879b7..c1037d5c67 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs @@ -1,5 +1,3 @@ -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server; partial class ServerCompositionVisual diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 3ff1bf2b98..45515a37e2 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -6,8 +6,6 @@ using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Transport; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs index 38969fa582..aa1f099cf6 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs @@ -8,8 +8,6 @@ using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Transport; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs index 0e52f256ee..0652e1731d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using Avalonia.Rendering.Composition.Transport; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs index c7fdd6dea6..34f46f5e68 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs @@ -7,8 +7,6 @@ using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Transport; using Avalonia.Utilities; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Server { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs b/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs index 992e5ba7b9..c21f1c9227 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Transport { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs index 5a88f0e91d..0231c29bb3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs @@ -6,8 +6,6 @@ using System.Runtime.InteropServices; using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Transport; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs index 222e9918d2..4cb5ec25e0 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs @@ -5,8 +5,6 @@ using System.Runtime.InteropServices; using Avalonia.Platform; using Avalonia.Threading; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Transport; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs index fed0f4d4aa..edb796f832 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs @@ -1,7 +1,5 @@ using System; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Transport; internal class BatchStreamDebugMarkers diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs b/src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs index 603c7582a1..f308a5476d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition.Transport { /// diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs index f4f1f3e8d9..4f6442bd77 100644 --- a/src/Avalonia.Base/Rendering/Composition/Visual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs @@ -3,8 +3,6 @@ using System.Numerics; using Avalonia.Media; using Avalonia.VisualTree; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// diff --git a/src/Avalonia.Base/Rendering/Composition/VisualCollection.cs b/src/Avalonia.Base/Rendering/Composition/VisualCollection.cs index 93e9f4d740..ebc84a2de7 100644 --- a/src/Avalonia.Base/Rendering/Composition/VisualCollection.cs +++ b/src/Avalonia.Base/Rendering/Composition/VisualCollection.cs @@ -1,8 +1,6 @@ using System; using Avalonia.Rendering.Composition.Server; -// Special license applies License.md - namespace Avalonia.Rendering.Composition { /// From e9f2cb34789017b0db57e33d02660c2bc70af4bf Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 3 Jul 2023 09:29:53 +0100 Subject: [PATCH 252/391] Fix datagrid not updating when not attached to tree see #9527 --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 7ade9f2437..de49d50637 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -2101,6 +2101,7 @@ namespace Avalonia.Controls if (DataConnection.DataSource != null && !DataConnection.EventsWired) { DataConnection.WireEvents(DataConnection.DataSource); + InitializeElements(false /*recycleRows*/); } } From 03ec924d6bfeff81a21b69014c669882e4547130 Mon Sep 17 00:00:00 2001 From: Mike James Date: Mon, 3 Jul 2023 12:29:00 +0200 Subject: [PATCH 253/391] Update readme.md Resolving #12013 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e9cd220714..24a3b7bd8c 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,7 @@ Considered by many to be the spiritual successor to WPF, Avalonia UI provides a For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform. #### Roadmap -To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). +To see the status of some of our features, look at our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). #### Breaking Changes You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. From b51341609857cb912716dfbb746295ef5b1c9244 Mon Sep 17 00:00:00 2001 From: yanlinlin Date: Tue, 4 Jul 2023 09:35:28 +0000 Subject: [PATCH 254/391] Add event trigger time in DevTools. #11338 --- .../Diagnostics/ViewModels/EventTreeNode.cs | 3 ++- .../Diagnostics/ViewModels/FiredEvent.cs | 5 ++++- .../Diagnostics/Views/EventsPageView.xaml | 10 ++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs index 785fd49983..0002107ee0 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs @@ -72,12 +72,13 @@ namespace Avalonia.Diagnostics.ViewModels var s = sender!; var handled = e.Handled; var route = e.Route; + var triggerTime = DateTime.Now; void handler() { if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e)) { - _currentEvent = new FiredEvent(e, new EventChainLink(s, handled, route)); + _currentEvent = new FiredEvent(e, new EventChainLink(s, handled, route), triggerTime); _parentViewModel.RecordedEvents.Add(_currentEvent); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs index a9f3182bef..1eab9afe8a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs @@ -10,11 +10,12 @@ namespace Avalonia.Diagnostics.ViewModels private readonly RoutedEventArgs _eventArgs; private EventChainLink? _handledBy; - public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator) + public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator, DateTime triggerTime) { _eventArgs = eventArgs ?? throw new ArgumentNullException(nameof(eventArgs)); Originator = originator ?? throw new ArgumentNullException(nameof(originator)); AddToChain(originator); + TriggerTime = triggerTime; } public bool IsPartOfSameEventChain(RoutedEventArgs e) @@ -22,6 +23,8 @@ namespace Avalonia.Diagnostics.ViewModels return e == _eventArgs; } + public DateTime TriggerTime { get; } + public RoutedEvent Event => _eventArgs.RoutedEvent!; public bool IsHandled => HandledBy?.Handled == true; diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml index 54c2f06709..c18b8cf003 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml @@ -77,21 +77,23 @@ - + - + + + - + - + From 7cd2350b682e7b503e370695a621e69f55d7eb20 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Tue, 4 Jul 2023 22:40:48 +0300 Subject: [PATCH 255/391] Removed redundant Source check --- src/Avalonia.Base/Interactivity/EventRoute.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Base/Interactivity/EventRoute.cs b/src/Avalonia.Base/Interactivity/EventRoute.cs index 37539a7153..d0d82b4884 100644 --- a/src/Avalonia.Base/Interactivity/EventRoute.cs +++ b/src/Avalonia.Base/Interactivity/EventRoute.cs @@ -120,11 +120,6 @@ namespace Avalonia.Interactivity return; } - if (e.Source is null) - { - throw new ArgumentException("Event source may not be null", nameof(e)); - } - Interactive? lastTarget = null; var start = 0; var end = _route.Count; From 48162b0ebc7992b3a28b66e6cdb03e947108de07 Mon Sep 17 00:00:00 2001 From: jankrib Date: Wed, 5 Jul 2023 14:06:47 +0200 Subject: [PATCH 256/391] Use cancellation token in name generator --- .../NameGenerator/AvaloniaNameGenerator.cs | 8 +++++--- .../NameGenerator/AvaloniaNameSourceGenerator.cs | 12 ++++++++++-- .../NameGenerator/INameGenerator.cs | 3 ++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs index 8d45afafa7..fa20be721c 100644 --- a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs +++ b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Avalonia.Generators.Common.Domain; using Microsoft.CodeAnalysis; @@ -31,7 +32,7 @@ internal class AvaloniaNameGenerator : INameGenerator _code = code; } - public IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles) + public IEnumerable GenerateNameReferences(IEnumerable additionalFiles, CancellationToken cancellationToken) { var resolveViews = from file in additionalFiles @@ -39,7 +40,8 @@ internal class AvaloniaNameGenerator : INameGenerator file.Path.EndsWith(".paml") || file.Path.EndsWith(".axaml")) && _pathPattern.Matches(file.Path) - let xaml = file.GetText()!.ToString() + let xaml = file.GetText(cancellationToken)?.ToString() + where xaml != null let view = _classes.ResolveView(xaml) where view != null && _namespacePattern.Matches(view.Namespace) select view; @@ -51,7 +53,7 @@ internal class AvaloniaNameGenerator : INameGenerator let fileName = ResolveViewFileName(view, _naming) select new GeneratedPartialClass(fileName, code); - return query.ToList(); + return query; } private static string ResolveViewFileName(ResolvedView view, ViewFileNamingStrategy strategy) => strategy switch diff --git a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs index fa1dcde064..c8a9825974 100644 --- a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs +++ b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs @@ -22,8 +22,16 @@ public class AvaloniaNameSourceGenerator : ISourceGenerator return; } - var partials = generator.GenerateNameReferences(context.AdditionalFiles); - foreach (var (fileName, content) in partials) context.AddSource(fileName, content); + var partials = generator.GenerateNameReferences(context.AdditionalFiles, context.CancellationToken); + foreach (var (fileName, content) in partials) + { + if(context.CancellationToken.IsCancellationRequested) + { + break; + } + + context.AddSource(fileName, content); + } } catch (Exception exception) { diff --git a/src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs b/src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs index 922d800332..5b44de43c1 100644 --- a/src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs +++ b/src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using System.Threading; using Microsoft.CodeAnalysis; namespace Avalonia.Generators.NameGenerator; internal interface INameGenerator { - IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles); + IEnumerable GenerateNameReferences(IEnumerable additionalFiles, CancellationToken cancellationToken); } internal record GeneratedPartialClass(string FileName, string Content); From 760ad47880b6585b8d0df51d441e547703f4129d Mon Sep 17 00:00:00 2001 From: Mike James Date: Wed, 5 Jul 2023 15:40:19 +0200 Subject: [PATCH 257/391] Update readme.md Removing the pause in PRs for v11. --- readme.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/readme.md b/readme.md index e9cd220714..c9539e1eb9 100644 --- a/readme.md +++ b/readme.md @@ -6,8 +6,6 @@
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) -⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)** - ## 📖 About [Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github). From 640fb5ec1bfcab76fe77bbd06e41ff656daaca9e Mon Sep 17 00:00:00 2001 From: AtomCrafty Date: Wed, 5 Jul 2023 20:12:59 +0200 Subject: [PATCH 258/391] add AutoCompleteBox with MinimumPrefixLength=0 to control gallery --- samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml index a492808f1d..b682ebf51d 100644 --- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml +++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml @@ -18,6 +18,10 @@ + + + + @@ -42,7 +46,6 @@ - From 4b5b6225f1124dfdaedb2b1cb18c997cfe0d8a4c Mon Sep 17 00:00:00 2001 From: AtomCrafty Date: Wed, 5 Jul 2023 20:13:54 +0200 Subject: [PATCH 259/391] fix AutoCompleteBox dropdown not opening when the text box is empty --- .../AutoCompleteBox/AutoCompleteBox.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index d0b894101f..ebf66164b7 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -1320,17 +1320,14 @@ namespace Avalonia.Controls // Evaluate the conditions needed for completion. // 1. Minimum prefix length // 2. If a delay timer is in use, use it - bool populateReady = newText.Length >= MinimumPrefixLength && MinimumPrefixLength >= 0; - if (populateReady && MinimumPrefixLength == 0 && String.IsNullOrEmpty(newText) && String.IsNullOrEmpty(SearchText)) - { - populateReady = false; - } - _userCalledPopulate = populateReady ? userInitiated : false; + bool minimumLengthReached = newText.Length >= MinimumPrefixLength && MinimumPrefixLength >= 0; + + _userCalledPopulate = minimumLengthReached && userInitiated; // Update the interface and values only as necessary UpdateTextValue(newText, userInitiated); - if (populateReady) + if (minimumLengthReached) { _ignoreTextSelectionChange = true; From 124ad88924b3863162e8fd5936edeb5d0b39855c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 5 Jul 2023 11:50:21 -0700 Subject: [PATCH 260/391] Fix egl opengl initialization on win32 --- src/Windows/Avalonia.Win32/Win32GlManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index b3760a37e1..ebb708f4c5 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -20,6 +20,10 @@ static class Win32GlManager { AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); } + if (gl is IPlatformGraphicsOpenGlContextFactory openGlFactory) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(openGlFactory); + } return gl; } From 21f4a2044bb3e1fe79ef9fc5c630c3ea10dc245b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 5 Jul 2023 11:50:33 -0700 Subject: [PATCH 261/391] Minor doc fix --- src/Avalonia.Base/Platform/Storage/PickerOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Platform/Storage/PickerOptions.cs b/src/Avalonia.Base/Platform/Storage/PickerOptions.cs index ed061aa2d5..07f99f32c8 100644 --- a/src/Avalonia.Base/Platform/Storage/PickerOptions.cs +++ b/src/Avalonia.Base/Platform/Storage/PickerOptions.cs @@ -6,7 +6,7 @@ public class PickerOptions { /// - /// Gets or sets the text that appears in the title bar of a folder dialog. + /// Gets or sets the text that appears in the title bar of a picker. /// public string? Title { get; set; } From 42ad2fd46e4409874db5235efc1e838c0475f6b1 Mon Sep 17 00:00:00 2001 From: AtomCrafty Date: Wed, 5 Jul 2023 20:59:18 +0200 Subject: [PATCH 262/391] add regression test for AutoCompleteBox with MinimumPrefixLegth=0 --- .../AutoCompleteBoxTests.cs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index acae583b5c..f31df71c0d 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -1,21 +1,15 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; -using Avalonia.Markup.Data; -using Avalonia.Platform; using Avalonia.Threading; using Avalonia.UnitTests; -using Moq; using Xunit; using System.Collections.ObjectModel; -using System.Reactive.Linq; using System.Reactive.Subjects; +using Avalonia.Input; namespace Avalonia.Controls.UnitTests { @@ -439,6 +433,29 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void Explicit_Dropdown_Open_Request_MinimumPrefixLength_0() + { + RunTest((control, textbox) => + { + control.Text = ""; + control.MinimumPrefixLength = 0; + Dispatcher.UIThread.RunJobs(); + + Assert.False(control.IsDropDownOpen); + + control.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down + }); + + Dispatcher.UIThread.RunJobs(); + + Assert.True(control.IsDropDownOpen); + }); + } + /// /// Retrieves a defined predicate filter through a new AutoCompleteBox /// control instance. @@ -1072,14 +1089,14 @@ namespace Avalonia.Controls.UnitTests private AutoCompleteBox CreateControl() { - var datePicker = + var autoCompleteBox = new AutoCompleteBox { Template = CreateTemplate() }; - datePicker.ApplyTemplate(); - return datePicker; + autoCompleteBox.ApplyTemplate(); + return autoCompleteBox; } private TextBox GetTextBox(AutoCompleteBox control) { From dfb3bfe3bae77ffba9a95266e51fd1e60ea0377c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 5 Jul 2023 15:31:46 -0400 Subject: [PATCH 263/391] Update build.md --- Documentation/build.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/build.md b/Documentation/build.md index fd6b26337c..62d6d72a05 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -43,6 +43,25 @@ Build and run `ControlCatalog.NetCore` project to see the sample application. >CSC : error CS0006: Metadata file 'C:\...\Avalonia\packages\Avalonia\bin\Debug\netcoreapp2.0\Avalonia.dll' could not be found ``` To correct this, right click on the `Avalonia.DesktopRuntime` project then press `Build` to build the project manually. Afterwards the solution should build normally and the ControlCatalog can be run. + * **Error MSB4062 GenerateAvaloniaResourcesTask** + + Same as previous one, you need to manually build `Avalonia.Build.Tasks` project at least once. + + Alternatively, you can build the solution once with Nuke. + +## Building packages with Nuke + +Install Nuke +`dotnet tool install --global Nuke.GlobalTool --version 6.2.1` + +Build project: +`nuke --target Compile --configuration Release` + +And run tests: +`nuke --target RunTests --configuration Release` + +Or if you need to create nuget packages as well (it will compile and run tests automatically): +`nuke --target Package --configuration Release` # Linux/macOS From 75e60d9c2d97be30367e12b0ba7d9072c41363b4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 5 Jul 2023 15:37:50 -0400 Subject: [PATCH 264/391] Update build.md --- Documentation/build.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/build.md b/Documentation/build.md index 62d6d72a05..3fcaae8963 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -14,6 +14,18 @@ git submodule update --init Go to https://dotnet.microsoft.com/download/visual-studio-sdks and install the latest version of the .NET Core SDK compatible with Avalonia UI. Make sure to download the SDK (not just the "runtime") package. The version compatible is indicated within the [global.json](https://github.com/AvaloniaUI/Avalonia/blob/master/global.json) file. Note that Avalonia UI does not always use the latest version and is hardcoded to use the last version known to be compatible (SDK releases may break the builds from time-to-time). +### Installing necessary .NET Workloads + +.NET SDK requires developers to install workloads for each platform they are targeting. +Since Avalonia targets pretty much every supported .NET platform, you need to install these workloads as well. +Running it from the command line: +``` +dotnet workload install android ios wasm-tools wasm-experimental +``` + +macOS workloads are not required to build Avalonia. +Note: on Unix OS you need to run this command from sudo. + ## Build and Run Avalonia ``` From 7dbe6f292a1159e5cc129c8a04dd68a92dbd093f Mon Sep 17 00:00:00 2001 From: jankrib Date: Wed, 5 Jul 2023 21:38:37 +0200 Subject: [PATCH 265/391] Handle OperationCanceledException --- .../NameGenerator/AvaloniaNameSourceGenerator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs index c8a9825974..407cf4f35f 100644 --- a/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs +++ b/src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs @@ -33,6 +33,9 @@ public class AvaloniaNameSourceGenerator : ISourceGenerator context.AddSource(fileName, content); } } + catch (OperationCanceledException) + { + } catch (Exception exception) { context.ReportNameGeneratorUnhandledError(exception); From c4178c8a4b0c6f561bdfca9dec275b07eccb44a5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 5 Jul 2023 13:14:48 -0700 Subject: [PATCH 266/391] Fix WGL crash in virtual machine --- src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs index e207bc23ab..85cab1d18e 100644 --- a/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs +++ b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs @@ -38,12 +38,10 @@ namespace Avalonia.Win32.OpenGl [MemberNotNullWhen(true, nameof(s_wglChoosePixelFormatArb))] [MemberNotNullWhen(true, nameof(s_wglCreateContextAttribsArb))] - [MemberNotNullWhen(true, nameof(s_glDebugMessageCallback))] private static bool Initialize() => _initialized ??= InitializeCore(); [MemberNotNullWhen(true, nameof(s_wglChoosePixelFormatArb))] [MemberNotNullWhen(true, nameof(s_wglCreateContextAttribsArb))] - [MemberNotNullWhen(true, nameof(s_glDebugMessageCallback))] private static bool InitializeCore() { Dispatcher.UIThread.VerifyAccess(); @@ -74,9 +72,9 @@ namespace Avalonia.Win32.OpenGl Marshal.GetDelegateForFunctionPointer( wglGetProcAddress("wglChoosePixelFormatARB")); - s_glDebugMessageCallback = - Marshal.GetDelegateForFunctionPointer( - wglGetProcAddress("glDebugMessageCallback")); + s_glDebugMessageCallback = wglGetProcAddress("glDebugMessageCallback") is { } setDebugCallback && setDebugCallback != default ? + Marshal.GetDelegateForFunctionPointer(setDebugCallback) : + null; var formats = new int[1]; @@ -144,8 +142,12 @@ namespace Avalonia.Win32.OpenGl }); } - using(new WglRestoreContext(dc, context, null)) - s_glDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero); + if (s_glDebugMessageCallback is not null) + { + using (new WglRestoreContext(dc, context, null)) + s_glDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero); + } + if (context != IntPtr.Zero) return new WglContext(shareContext, version, context, window, dc, _defaultPixelFormat, _defaultPfd); From 2b1c0e25ac8c93e80ae2689d75a2030019a1654a Mon Sep 17 00:00:00 2001 From: afunc Date: Thu, 6 Jul 2023 22:08:49 +0800 Subject: [PATCH 267/391] Country or Region --- samples/ControlCatalog/Pages/DataGridPage.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index fb097da71b..356834832d 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -34,7 +34,7 @@ RowBackground="#1000"> - + @@ -68,7 +68,7 @@ - + From 62fc555e093be28afddcc3b648f4ce88253fdb9c Mon Sep 17 00:00:00 2001 From: AtomCrafty Date: Fri, 7 Jul 2023 00:21:40 +0200 Subject: [PATCH 268/391] fix dead link in readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index c9539e1eb9..aa99360681 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ [Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github). -Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements. +Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/docs/next/get-started/wpf/), it isn't a 1:1 copy, and you'll find plenty of improvements. For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform. From 4405d7970c5408400fa1132d45e0a375b1bc66ab Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Thu, 6 Jul 2023 16:33:32 +0200 Subject: [PATCH 269/391] Drop original internal XAML resources after merge --- .../XamlCompilerTaskExecutor.cs | 39 ++++++++++++------- .../AvaloniaXamlIlRuntimeCompiler.cs | 23 +++++++---- .../AvaloniaXamlIlCompiler.cs | 6 ++- .../XamlMergeResourceGroupTransformer.cs | 20 +++++----- .../IXamlDocumentResource.cs | 4 +- .../XamlDocumentResource.cs | 34 ++++++++++------ .../XamlDocumentTypeBuilderProvider.cs | 23 +++++++++++ .../CompilerExtensions/XamlDocumentUsage.cs | 8 ++++ 8 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentUsage.cs diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 1b611db62b..80876f1819 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -304,7 +304,7 @@ namespace Avalonia.Build.Tasks || "Internal".Equals(classModifierText, StringComparison.OrdinalIgnoreCase)) classModifierPublic = false; else - throw new XamlParseException("Invalid value for x:ClassModifier. Expected value are: Public, NotPublic (internal).", precompileDirective); + throw new XamlParseException("Invalid value for x:ClassModifier. Expected value are: Public, NotPublic (internal).", classModifierDirective); } var classDirective = initialRoot.Children.OfType() @@ -353,10 +353,16 @@ namespace Avalonia.Build.Tasks ((List)parsedXamlDocuments).Add(new XamlDocumentResource( parsed, res.Uri, res, classType, classModifierPublic.Value, - populateBuilder, - compiler.DefinePopulateMethod(populateBuilder, parsed, populateName, - classTypeDefinition == null && classModifierPublic.Value), - buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, classModifierPublic.Value))); + () => new XamlDocumentTypeBuilderProvider( + populateBuilder, + compiler.DefinePopulateMethod( + populateBuilder, + parsed, + populateName, + classTypeDefinition == null && classModifierPublic.Value), + buildName == null ? + null : + compiler.DefineBuildMethod(builder, parsed, buildName, classModifierPublic.Value)))); } catch (Exception e) { @@ -387,10 +393,17 @@ namespace Avalonia.Build.Tasks foreach (var document in parsedXamlDocuments) { + var res = (IResource)document.FileSource!; + + if (document.Usage == XamlDocumentUsage.Merged && !document.IsPublic) + { + res.Remove(); + continue; + } + var parsed = document.XamlDocument; - var res = (IResource)document.FileSource; var classType = document.ClassType; - var populateBuilder = document.TypeBuilder; + var populateBuilder = document.TypeBuilderProvider.TypeBuilder; try { @@ -399,8 +412,8 @@ namespace Avalonia.Build.Tasks compiler.Compile(parsed, contextClass, - document.PopulateMethod, - document.BuildMethod, + document.TypeBuilderProvider.PopulateMethod, + document.TypeBuilderProvider.BuildMethod, builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name, true), (closureName, closureBaseType) => populateBuilder.DefineSubType(closureBaseType, closureName, false), @@ -412,7 +425,7 @@ namespace Avalonia.Build.Tasks if (classTypeDefinition != null) { var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve() - .Methods.First(m => m.Name == document.PopulateMethod.Name); + .Methods.First(m => m.Name == document.TypeBuilderProvider.PopulateMethod.Name); var designLoaderFieldType = typeSystem .GetType("System.Action`1") @@ -536,12 +549,12 @@ namespace Avalonia.Build.Tasks } if (document.IsPublic - && (document.BuildMethod != null || classTypeDefinition != null)) + && (document.TypeBuilderProvider.BuildMethod != null || classTypeDefinition != null)) { - var compiledBuildMethod = document.BuildMethod == null ? + var compiledBuildMethod = document.TypeBuilderProvider.BuildMethod is not { } buildMethod ? null : typeSystem.GetTypeReference(builder).Resolve() - .Methods.First(m => m.Name == document.BuildMethod?.Name); + .Methods.First(m => m.Name == buildMethod.Name); var parameterlessConstructor = compiledBuildMethod != null ? null : classTypeDefinition.GetConstructors().FirstOrDefault(c => diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index e2aab7d685..995ed91b8d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -231,10 +232,19 @@ namespace Avalonia.Markup.Xaml.XamlIl ?? ((IXamlAstValueNode)parsed.Root).Type.GetClrType().Name; var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + xamlName); var builder = _sreTypeSystem.CreateTypeBuilder(tb); - parsedDocuments.Add(new XamlDocumentResource(parsed, document.BaseUri?.ToString(), - null, null, true, builder, - compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, true), - document.RootInstance is null ? compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, true) : null)); + + parsedDocuments.Add(new XamlDocumentResource( + parsed, + document.BaseUri?.ToString(), + null, + null, + true, + () => new XamlDocumentTypeBuilderProvider( + builder, + compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, true), + document.RootInstance is null ? + compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, true) : + null))); originalDocuments.Add(document); } @@ -242,9 +252,8 @@ namespace Avalonia.Markup.Xaml.XamlIl var createdTypes = parsedDocuments.Select(document => { - compiler.Compile(document.XamlDocument, document.TypeBuilder, document.PopulateMethod, - document.BuildMethod, document.Uri, document.FileSource); - return _sreTypeSystem.GetType(document.TypeBuilder.CreateType()); + compiler.Compile(document.XamlDocument, document.TypeBuilderProvider, document.Uri, document.FileSource); + return _sreTypeSystem.GetType(document.TypeBuilderProvider.TypeBuilder.CreateType()); }).ToArray(); clrPropertyBuilder.CreateTypeInfo(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 23c67df810..af34d55a3d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -179,9 +179,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return parsed; } - public void Compile(XamlDocument document, IXamlTypeBuilder tb, IXamlMethodBuilder populateMethod, IXamlMethodBuilder buildMethod, string baseUri, IFileSource fileSource) + public void Compile(XamlDocument document, XamlDocumentTypeBuilderProvider typeBuilderProvider, string baseUri, IFileSource fileSource) { - Compile(document, _contextType, populateMethod, buildMethod, + var tb = typeBuilderProvider.TypeBuilder; + + Compile(document, _contextType, typeBuilderProvider.PopulateMethod, typeBuilderProvider.BuildMethod, _configuration.TypeMappings.XmlNamespaceInfoProvider == null ? null : tb.DefineSubType(_configuration.WellKnownTypes.Object, diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs index 8e04a7d467..f135ed5ffb 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using XamlX.Ast; -using XamlX.IL.Emitters; -using XamlX.Transform.Transformers; using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers; @@ -24,7 +22,6 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude; var mergeSourceNodes = new List(); - var mergedResourceWasAdded = false; foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray()) { void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode) @@ -39,7 +36,6 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer { parent.Children.Remove(assignmentNode); mergeSourceNodes.Add(sourceAssignmentNode); - mergedResourceWasAdded = true; } else { @@ -85,25 +81,29 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer return context.ParseError( $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node); } - + var targetDocument = context.Documents.FirstOrDefault(d => - string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase)) - ?.XamlDocument.Root as XamlValueWithManipulationNode; - if (targetDocument is null) + string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase)); + if (targetDocument?.XamlDocument.Root is not XamlValueWithManipulationNode targetDocumentRoot) { return context.ParseError( $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node); } - var singleRootObject = ((XamlManipulationGroupNode)targetDocument.Manipulation) + var singleRootObject = ((XamlManipulationGroupNode)targetDocumentRoot.Manipulation) .Children.OfType().Single(); if (singleRootObject.Type != resourceDictionaryType) { return context.ParseError( - $"MergeResourceInclude can only include another ResourceDictionary", propertyNode, node); + "MergeResourceInclude can only include another ResourceDictionary", propertyNode, node); } manipulationGroup.Add(singleRootObject.Manipulation); + + if (targetDocument.Usage == XamlDocumentUsage.Unknown) + { + targetDocument.Usage = XamlDocumentUsage.Merged; + } } // Order of resources is defined by ResourceDictionary.TryGetResource. diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs index b859b3ed59..9b5c59682f 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs @@ -1,5 +1,4 @@ -using System; -using XamlX.Ast; +using XamlX.Ast; using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; @@ -14,4 +13,5 @@ internal interface IXamlDocumentResource IXamlMethod PopulateMethod { get; } IFileSource? FileSource { get; } XamlDocument XamlDocument { get; } + XamlDocumentUsage Usage { get; set; } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs index 932fe067af..0dab80d743 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs @@ -1,6 +1,5 @@ using System; using XamlX.Ast; -using XamlX.IL; using XamlX.TypeSystem; #nullable enable @@ -8,24 +7,23 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; internal class XamlDocumentResource : IXamlDocumentResource { + private readonly Func _createTypeBuilderProvider; + private XamlDocumentTypeBuilderProvider? _typeBuilderProvider; + public XamlDocumentResource( XamlDocument xamlDocument, string? uri, IFileSource? fileSource, IXamlType? classType, bool isPublic, - IXamlTypeBuilder typeBuilder, - IXamlMethodBuilder populateMethod, - IXamlMethodBuilder? buildMethod) + Func createTypeBuilderProvider) { + _createTypeBuilderProvider = createTypeBuilderProvider; XamlDocument = xamlDocument; Uri = uri; FileSource = fileSource; ClassType = classType; IsPublic = isPublic; - TypeBuilder = typeBuilder; - PopulateMethod = populateMethod; - BuildMethod = buildMethod; } public XamlDocument XamlDocument { get; } @@ -34,10 +32,22 @@ internal class XamlDocumentResource : IXamlDocumentResource public IXamlType? ClassType { get; } public bool IsPublic { get; } - public IXamlTypeBuilder TypeBuilder { get; } - public IXamlMethodBuilder PopulateMethod { get; } - public IXamlMethodBuilder? BuildMethod { get; } + public XamlDocumentUsage Usage { get; set; } + + public XamlDocumentTypeBuilderProvider TypeBuilderProvider + { + get + { + if (_typeBuilderProvider is null) + { + _typeBuilderProvider = _createTypeBuilderProvider(); + Usage = XamlDocumentUsage.Used; + } + + return _typeBuilderProvider; + } + } - IXamlMethod? IXamlDocumentResource.BuildMethod => BuildMethod; - IXamlMethod IXamlDocumentResource.PopulateMethod => PopulateMethod; + IXamlMethod? IXamlDocumentResource.BuildMethod => TypeBuilderProvider.BuildMethod; + IXamlMethod IXamlDocumentResource.PopulateMethod => TypeBuilderProvider.PopulateMethod; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs new file mode 100644 index 0000000000..4701403fb9 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs @@ -0,0 +1,23 @@ +#nullable enable + +using XamlX.IL; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; + +internal sealed class XamlDocumentTypeBuilderProvider +{ + public XamlDocumentTypeBuilderProvider( + IXamlTypeBuilder typeBuilder, + IXamlMethodBuilder populateMethod, + IXamlMethodBuilder? buildMethod) + { + TypeBuilder = typeBuilder; + PopulateMethod = populateMethod; + BuildMethod = buildMethod; + } + + public IXamlTypeBuilder TypeBuilder { get; } + public IXamlMethodBuilder PopulateMethod { get; } + public IXamlMethodBuilder? BuildMethod { get; } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentUsage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentUsage.cs new file mode 100644 index 0000000000..ec5b931548 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentUsage.cs @@ -0,0 +1,8 @@ +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; + +internal enum XamlDocumentUsage +{ + Unknown, + Merged, + Used +} From 0e038492f7e5e20c2d7ceb3d1e69a564fe148e27 Mon Sep 17 00:00:00 2001 From: Daniil Pavliuchyk Date: Fri, 7 Jul 2023 19:03:18 +0300 Subject: [PATCH 270/391] Do not set Window Startup Location multiple times --- samples/Sandbox/MainWindow.axaml | 8 +---- src/Avalonia.Controls/Window.cs | 7 ++++ .../WindowTests.cs | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index f96abcac96..d8614983b7 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,11 +1,5 @@ - - - - - - - + diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ce4fc8dd5c..dc58947994 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -169,6 +169,7 @@ namespace Avalonia.Controls private readonly Size _maxPlatformClientSize; private bool _shown; private bool _showingAsDialog; + private bool _wasShownBefore; /// /// Initializes static members of the class. @@ -718,6 +719,7 @@ namespace Avalonia.Controls StartRendering(); PlatformImpl?.Show(ShowActivated, false); OnOpened(EventArgs.Empty); + _wasShownBefore = true; } } @@ -871,6 +873,11 @@ namespace Avalonia.Controls private void SetWindowStartupLocation(Window? owner = null) { + if (_wasShownBefore == true) + { + return; + } + var startupLocation = WindowStartupLocation; if (startupLocation == WindowStartupLocation.CenterOwner && diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index cd45a3d920..186b2f6836 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -513,6 +513,41 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Window_Should_Not_Be_Centered_When_WindowStartupLocation_Is_CenterScreen_And_Window_Is_Hidden_And_Shown() + { + var screen1 = new Mock(1.0, new PixelRect(new PixelSize(1920, 1080)), new PixelRect(new PixelSize(1920, 1040)), true); + + var screens = new Mock(); + screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object }); + screens.Setup(x => x.ScreenFromPoint(It.IsAny())).Returns(screen1.Object); + + + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480)); + windowImpl.Setup(x => x.DesktopScaling).Returns(1); + windowImpl.Setup(x => x.RenderScaling).Returns(1); + windowImpl.Setup(x => x.Screen).Returns(screens.Object); + + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(windowImpl.Object) + { + WindowStartupLocation = WindowStartupLocation.CenterScreen + }; + + window.Show(); + + var expected = new PixelPoint(150, 400); + window.Position = expected; + + window.IsVisible = false; + window.IsVisible = true; + + Assert.Equal(expected, window.Position); + } + } + [Fact] public void Window_Should_Be_Centered_When_WindowStartupLocation_Is_CenterScreen() { From c21e9ccbb4559caafceaf49795d002caf7907013 Mon Sep 17 00:00:00 2001 From: Daniil Pavliuchyk Date: Fri, 7 Jul 2023 19:03:47 +0300 Subject: [PATCH 271/391] Cleanup --- samples/Sandbox/MainWindow.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index d8614983b7..6929f192c7 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,5 +1,4 @@ - From 0013f444449b772920c275fb780655620cfac0f1 Mon Sep 17 00:00:00 2001 From: Vladimir Kulikov Date: Fri, 7 Jul 2023 21:57:39 +0300 Subject: [PATCH 272/391] Setting child of TestRoot once --- tests/Avalonia.UnitTests/TestRoot.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs index fcfd845602..f62afaf74d 100644 --- a/tests/Avalonia.UnitTests/TestRoot.cs +++ b/tests/Avalonia.UnitTests/TestRoot.cs @@ -35,7 +35,6 @@ namespace Avalonia.UnitTests public TestRoot(Control child) : this(false, child) { - Child = child; } public TestRoot(bool useGlobalStyles, Control child) From d4b5085ba3e7afaa859b87421c02f155b45e3d4d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 15:29:40 -0400 Subject: [PATCH 273/391] Revert "Fix ImportCompleted spelling" --- .../Rendering/Composition/CompositionExternalMemory.cs | 3 +-- .../Rendering/Composition/CompositionInterop.cs | 6 +++--- .../Composition/Server/ServerCompositionDrawingSurface.cs | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs index 1021bc58b4..ce728f86a2 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs @@ -91,8 +91,7 @@ public interface ICompositionGpuImportedObject : IAsyncDisposable /// the user code is allowed to free the resource owner in case when a non-owning /// sharing handle was used /// - Task ImportCompleted { get; } - + Task ImportCompeted { get; } /// /// Indicates if the device context this instance is associated with is no longer available /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs index 9eea228e84..1643ec6e8d 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs @@ -67,18 +67,18 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject Context = context; Feature = feature; - ImportCompleted = Compositor.InvokeServerJobAsync(Import); + ImportCompeted = Compositor.InvokeServerJobAsync(Import); } protected abstract void Import(); public abstract void Dispose(); - public Task ImportCompleted { get; } + public Task ImportCompeted { get; } public bool IsLost => Context.IsLost; public ValueTask DisposeAsync() => new(Compositor.InvokeServerJobAsync(() => { - if (ImportCompleted.Status == TaskStatus.RanToCompletion) + if (ImportCompeted.Status == TaskStatus.RanToCompletion) Dispose(); })); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs index f58403f0bc..da24b3812a 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs @@ -31,12 +31,12 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp throw new PlatformGraphicsContextLostException(); // This should never happen, but check for it anyway to avoid a deadlock - if (!image.ImportCompleted.IsCompleted) + if (!image.ImportCompeted.IsCompleted) throw new InvalidOperationException("The import operation is not completed yet"); // Rethrow the import here exception - if (image.ImportCompleted.IsFaulted) - image.ImportCompleted.GetAwaiter().GetResult(); + if (image.ImportCompeted.IsFaulted) + image.ImportCompeted.GetAwaiter().GetResult(); } void Update(IBitmapImpl newImage, IPlatformRenderInterfaceContext context) From 96f21b6cbf3cdc3d6eb96dc7a0e3eb22ea15c273 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:17:01 -0700 Subject: [PATCH 274/391] Create initial ApiDiffValidation implementation --- .nuke/build.schema.json | 10 +++ nukebuild/ApiDiffValidation.cs | 123 +++++++++++++++++++++++++++++++++ nukebuild/Build.cs | 21 +++++- nukebuild/BuildParameters.cs | 15 +++- nukebuild/Shims.cs | 4 +- nukebuild/_build.csproj | 2 + 6 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 nukebuild/ApiDiffValidation.cs diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index d2f2ee36d5..8bc812b0e2 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -6,6 +6,10 @@ "build": { "type": "object", "properties": { + "ApiValidationBaseline": { + "type": "string", + "description": "api-baseline" + }, "Configuration": { "type": "string", "description": "configuration" @@ -89,6 +93,7 @@ "RunRenderTests", "RunTests", "RunToolsTests", + "ValidateApiDiff", "ZipFiles" ] } @@ -124,10 +129,15 @@ "RunRenderTests", "RunTests", "RunToolsTests", + "ValidateApiDiff", "ZipFiles" ] } }, + "UpdateApiValidationSuppression": { + "type": "boolean", + "description": "update-api-suppression" + }, "Verbosity": { "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs new file mode 100644 index 0000000000..5112f87728 --- /dev/null +++ b/nukebuild/ApiDiffValidation.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; +using Nuke.Common.Tooling; + +public static class ApiDiffValidation +{ + public static void ValidatePackage( + Tool apiCompatTool, string packagePath, Version baselineVersion, + string suppressionFilesFolder, bool updateSuppressionFile) + { + if (baselineVersion is null) + { + throw new InvalidOperationException( + "Build \"api-baseline\" parameter must be set when running Nuke CreatePackages"); + } + + if (!Directory.Exists(suppressionFilesFolder)) + { + Directory.CreateDirectory(suppressionFilesFolder!); + } + + using (var baselineStream = DownloadBaselinePackage(packagePath, baselineVersion)) + using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read)) + using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read)) + using (Helpers.UseTempDir(out var tempFolder)) + { + var targetDlls = GetDlls(target); + var baselineDlls = GetDlls(baseline); + + var left = new List(); + var right = new List(); + + var suppressionFile = Path.Combine(suppressionFilesFolder, Path.GetFileName(packagePath) + ".xml"); + + foreach (var baselineDll in baselineDlls) + { + var baselineDllPath = Path.Combine("baseline", baselineDll.target, baselineDll.entry.Name); + var baselineDllRealPath = Path.Combine(tempFolder, baselineDllPath); + Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); + using (var baselineDllFile = File.Create(baselineDllRealPath)) + { + baselineDll.entry.Open().CopyTo(baselineDllFile); + } + + var targetDll = targetDlls.FirstOrDefault(e => + e.target == baselineDll.target && e.entry.Name == baselineDll.entry.Name); + if (targetDll.entry is null) + { + throw new InvalidOperationException($"Some assemblies are missing in the new package: {baselineDll.entry.Name} for {baselineDll.target}"); + } + + var targetDllPath = Path.Combine("target", targetDll.target, targetDll.entry.Name); + var targetDllRealPath = Path.Combine(tempFolder, targetDllPath); + Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); + using (var targetDllFile = File.Create(targetDllRealPath)) + { + targetDll.entry.Open().CopyTo(targetDllFile); + } + + left.Add(baselineDllPath); + right.Add(targetDllPath); + } + + var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; + updateSuppressionFile = true; + if (File.Exists(suppressionFile)) + { + args += $""" --suppression-file="{suppressionFile}" """; + } + if (updateSuppressionFile) + { + args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; + } + + apiCompatTool(args, tempFolder); + } + } + + private static IReadOnlyCollection<(string target, ZipArchiveEntry entry)> GetDlls(ZipArchive archive) + { + return archive.Entries + .Where(e => Path.GetExtension(e.FullName) == ".dll") + .Select(e => ( + entry: e, + isRef: e.FullName.Contains("ref/"), + target: Path.GetDirectoryName(e.FullName)!.Split('/').Last()) + ) + .GroupBy(e => (e.target, e.entry.Name)) + .Select(g => g.MaxBy(e => e.isRef)) + .Select(e => (e.target, e.entry)) + .ToArray(); + } + + static Stream DownloadBaselinePackage(string packagePath, Version baselineVersion) + { + Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); + + try + { + var packageId = Regex.Replace( + Path.GetFileNameWithoutExtension(packagePath), + """(\.\d+\.\d+\.\d+)$""", ""); + + using var httpClient = new HttpClient(); + using var response = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, + $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); + using var stream = response.Content.ReadAsStream(); + var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + return memoryStream; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Downloading baseline package for {packagePath} failed.\r" + ex.Message, ex); + } + } +} diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 524c9fa4e4..644c1267d6 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -36,6 +36,10 @@ using MicroCom.CodeGenerator; partial class Build : NukeBuild { BuildParameters Parameters { get; set; } + + [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll")] + Tool ApiCompatTool; + protected override void OnBuildInitialized() { Parameters = new BuildParameters(this); @@ -278,7 +282,19 @@ partial class Build : NukeBuild RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." + Parameters.Version + ".nupkg"); }); - + + Target ValidateApiDiff => _ => _ + .DependsOn(CreateNugetPackages) + .Executes(() => + { + foreach (var nugetPackage in Directory.GetFiles(Parameters.NugetRoot)) + { + ApiDiffValidation.ValidatePackage( + ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline, + Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression); + } + }); + Target RunTests => _ => _ .DependsOn(RunCoreLibsTests) .DependsOn(RunRenderTests) @@ -288,7 +304,8 @@ partial class Build : NukeBuild Target Package => _ => _ .DependsOn(RunTests) - .DependsOn(CreateNugetPackages); + .DependsOn(CreateNugetPackages) + .DependsOn(ValidateApiDiff); Target CiAzureLinux => _ => _ .DependsOn(RunTests); diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index dfa914d1db..67ed086e20 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -22,6 +22,12 @@ public partial class Build [Parameter("skip-previewer")] public bool SkipPreviewer { get; set; } + [Parameter("api-baseline")] + public string ApiValidationBaseline { get; set; } + + [Parameter("update-api-suppression")] + public bool UpdateApiValidationSuppression { get; set; } + public class BuildParameters { public string Configuration { get; } @@ -57,7 +63,9 @@ public partial class Build public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - + public Version ApiValidationBaseline { get; } + public bool UpdateApiValidationSuppression { get; } + public AbsolutePath ApiValidationSuppressionFiles { get; } public BuildParameters(Build b) { @@ -65,6 +73,10 @@ public partial class Build Configuration = b.Configuration ?? "Release"; SkipTests = b.SkipTests; SkipPreviewer = b.SkipPreviewer; + ApiValidationBaseline = b.ApiValidationBaseline is not null ? + new Version(b.ApiValidationBaseline) : + new Version(11, 0); + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; // CONFIGURATION MainRepo = "https://github.com/AvaloniaUI/Avalonia"; @@ -125,6 +137,7 @@ public partial class Build FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix); + ApiValidationSuppressionFiles = RootDirectory / "api"; } string GetVersion() diff --git a/nukebuild/Shims.cs b/nukebuild/Shims.cs index 6f79972ad6..eecfcf6da1 100644 --- a/nukebuild/Shims.cs +++ b/nukebuild/Shims.cs @@ -9,12 +9,12 @@ using Numerge; public partial class Build { - static void Information(string info) + internal static void Information(string info) { Logger.Info(info); } - static void Information(string info, params object[] args) + internal static void Information(string info, params object[] args) { Logger.Info(info, args); } diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 30e1200220..43453833d7 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -22,6 +22,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + From 6eaf0f9e4f12e32a6851de7a167f9440891a3f4c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:17:10 -0700 Subject: [PATCH 275/391] Break the API --- src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls.ColorPicker/HsvComponent.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs index b1d15d6b17..fb981db762 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs @@ -24,14 +24,14 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - OldColor = oldColor; + //OldColor = oldColor; NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - public Color OldColor { get; private set; } + //public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 1a7a13166a..998633a8e1 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls /// /// Also see: /// - Alpha = 0, + // Alpha = 0, /// /// The Hue component. From c72aa402a8e5fbc84bf47b2afbd862f537055682 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 6 Jul 2023 14:25:54 -0700 Subject: [PATCH 276/391] Run in parallel --- nukebuild/ApiDiffValidation.cs | 18 +++++++++--------- nukebuild/Build.cs | 10 ++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 5112f87728..0ebded3b5a 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -5,11 +5,12 @@ using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Nuke.Common.Tooling; public static class ApiDiffValidation { - public static void ValidatePackage( + public static async Task ValidatePackage( Tool apiCompatTool, string packagePath, Version baselineVersion, string suppressionFilesFolder, bool updateSuppressionFile) { @@ -24,7 +25,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(suppressionFilesFolder!); } - using (var baselineStream = DownloadBaselinePackage(packagePath, baselineVersion)) + using (var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion)) using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read)) using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read)) using (Helpers.UseTempDir(out var tempFolder)) @@ -44,7 +45,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); using (var baselineDllFile = File.Create(baselineDllRealPath)) { - baselineDll.entry.Open().CopyTo(baselineDllFile); + await baselineDll.entry.Open().CopyToAsync(baselineDllFile); } var targetDll = targetDlls.FirstOrDefault(e => @@ -59,7 +60,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); using (var targetDllFile = File.Create(targetDllRealPath)) { - targetDll.entry.Open().CopyTo(targetDllFile); + await targetDll.entry.Open().CopyToAsync(targetDllFile); } left.Add(baselineDllPath); @@ -67,7 +68,6 @@ public static class ApiDiffValidation } var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; - updateSuppressionFile = true; if (File.Exists(suppressionFile)) { args += $""" --suppression-file="{suppressionFile}" """; @@ -96,7 +96,7 @@ public static class ApiDiffValidation .ToArray(); } - static Stream DownloadBaselinePackage(string packagePath, Version baselineVersion) + static async Task DownloadBaselinePackage(string packagePath, Version baselineVersion) { Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); @@ -107,11 +107,11 @@ public static class ApiDiffValidation """(\.\d+\.\d+\.\d+)$""", ""); using var httpClient = new HttpClient(); - using var response = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, + using var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); - using var stream = response.Content.ReadAsStream(); + await using var stream = await response.Content.ReadAsStreamAsync(); var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); + await stream.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 644c1267d6..7c921cb630 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -285,14 +285,12 @@ partial class Build : NukeBuild Target ValidateApiDiff => _ => _ .DependsOn(CreateNugetPackages) - .Executes(() => + .Executes(async () => { - foreach (var nugetPackage in Directory.GetFiles(Parameters.NugetRoot)) - { - ApiDiffValidation.ValidatePackage( + await Task.WhenAll( + Directory.GetFiles(Parameters.NugetRoot).Select(nugetPackage => ApiDiffValidation.ValidatePackage( ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline, - Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression); - } + Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression))); }); Target RunTests => _ => _ From 65e29787dd5338a333f625b711e063c29a562dff Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 11:35:10 -0700 Subject: [PATCH 277/391] Parameters fixes --- .nuke/build.schema.json | 30 ++++++++++++------------------ nukebuild/ApiDiffValidation.cs | 19 +++++++++++-------- nukebuild/BuildParameters.cs | 21 ++++++++++----------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 8bc812b0e2..b802589fc7 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -6,21 +6,18 @@ "build": { "type": "object", "properties": { - "ApiValidationBaseline": { - "type": "string", - "description": "api-baseline" + "api-baseline": { + "type": "string" }, - "Configuration": { - "type": "string", - "description": "configuration" + "configuration": { + "type": "string" }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" }, - "ForceNugetVersion": { - "type": "string", - "description": "force-nuget-version" + "force-nuget-version": { + "type": "string" }, "Help": { "type": "boolean", @@ -98,13 +95,11 @@ ] } }, - "SkipPreviewer": { - "type": "boolean", - "description": "skip-previewer" + "skip-previewer": { + "type": "boolean" }, - "SkipTests": { - "type": "boolean", - "description": "skip-tests" + "skip-tests": { + "type": "boolean" }, "Target": { "type": "array", @@ -134,9 +129,8 @@ ] } }, - "UpdateApiValidationSuppression": { - "type": "boolean", - "description": "update-api-suppression" + "update-api-suppression": { + "type": "boolean" }, "Verbosity": { "type": "string", diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 0ebded3b5a..41259d9b2f 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -10,8 +10,10 @@ using Nuke.Common.Tooling; public static class ApiDiffValidation { + private static readonly HttpClient s_httpClient = new(); + public static async Task ValidatePackage( - Tool apiCompatTool, string packagePath, Version baselineVersion, + Tool apiCompatTool, string packagePath, string baselineVersion, string suppressionFilesFolder, bool updateSuppressionFile) { if (baselineVersion is null) @@ -25,7 +27,7 @@ public static class ApiDiffValidation Directory.CreateDirectory(suppressionFilesFolder!); } - using (var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion)) + await using (var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion)) using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read)) using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read)) using (Helpers.UseTempDir(out var tempFolder)) @@ -43,7 +45,7 @@ public static class ApiDiffValidation var baselineDllPath = Path.Combine("baseline", baselineDll.target, baselineDll.entry.Name); var baselineDllRealPath = Path.Combine(tempFolder, baselineDllPath); Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); - using (var baselineDllFile = File.Create(baselineDllRealPath)) + await using (var baselineDllFile = File.Create(baselineDllRealPath)) { await baselineDll.entry.Open().CopyToAsync(baselineDllFile); } @@ -58,7 +60,7 @@ public static class ApiDiffValidation var targetDllPath = Path.Combine("target", targetDll.target, targetDll.entry.Name); var targetDllRealPath = Path.Combine(tempFolder, targetDllPath); Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); - using (var targetDllFile = File.Create(targetDllRealPath)) + await using (var targetDllFile = File.Create(targetDllRealPath)) { await targetDll.entry.Open().CopyToAsync(targetDllFile); } @@ -96,7 +98,7 @@ public static class ApiDiffValidation .ToArray(); } - static async Task DownloadBaselinePackage(string packagePath, Version baselineVersion) + static async Task DownloadBaselinePackage(string packagePath, string baselineVersion) { Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); @@ -106,9 +108,10 @@ public static class ApiDiffValidation Path.GetFileNameWithoutExtension(packagePath), """(\.\d+\.\d+\.\d+)$""", ""); - using var httpClient = new HttpClient(); - using var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, - $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}")); + using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, + $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + await using var stream = await response.Content.ReadAsStreamAsync(); var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream); diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 67ed086e20..75c859f2e8 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -10,22 +10,22 @@ using static Nuke.Common.IO.PathConstruction; public partial class Build { - [Parameter("configuration")] + [Parameter(Name = "configuration")] public string Configuration { get; set; } - [Parameter("skip-tests")] + [Parameter(Name = "skip-tests")] public bool SkipTests { get; set; } - [Parameter("force-nuget-version")] + [Parameter(Name = "force-nuget-version")] public string ForceNugetVersion { get; set; } - [Parameter("skip-previewer")] + [Parameter(Name = "skip-previewer")] public bool SkipPreviewer { get; set; } - [Parameter("api-baseline")] + [Parameter(Name = "api-baseline")] public string ApiValidationBaseline { get; set; } - [Parameter("update-api-suppression")] + [Parameter(Name = "update-api-suppression")] public bool UpdateApiValidationSuppression { get; set; } public class BuildParameters @@ -63,7 +63,7 @@ public partial class Build public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - public Version ApiValidationBaseline { get; } + public string ApiValidationBaseline { get; } public bool UpdateApiValidationSuppression { get; } public AbsolutePath ApiValidationSuppressionFiles { get; } @@ -73,10 +73,6 @@ public partial class Build Configuration = b.Configuration ?? "Release"; SkipTests = b.SkipTests; SkipPreviewer = b.SkipPreviewer; - ApiValidationBaseline = b.ApiValidationBaseline is not null ? - new Version(b.ApiValidationBaseline) : - new Version(11, 0); - UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; // CONFIGURATION MainRepo = "https://github.com/AvaloniaUI/Avalonia"; @@ -115,6 +111,9 @@ public partial class Build // VERSION Version = b.ForceNugetVersion ?? GetVersion(); + ApiValidationBaseline = b.ApiValidationBaseline ?? new Version(new Version(Version).Major, 0).ToString(); + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; + if (IsRunningOnAzure) { if (!IsNuGetRelease) From 8097a1c9934d19373aee0062bc5ce57b5f42ab03 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:19:00 -0700 Subject: [PATCH 278/391] Throw on any output error --- nukebuild/ApiDiffValidation.cs | 29 ++++++++++++++++++++--------- nukebuild/Build.cs | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 41259d9b2f..91bd54fd89 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -69,17 +69,28 @@ public static class ApiDiffValidation right.Add(targetDllPath); } - var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; - if (File.Exists(suppressionFile)) + if (left.Any()) { - args += $""" --suppression-file="{suppressionFile}" """; - } - if (updateSuppressionFile) - { - args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; - } + var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """; + if (File.Exists(suppressionFile)) + { + args += $""" --suppression-file="{suppressionFile}" """; + } - apiCompatTool(args, tempFolder); + if (updateSuppressionFile) + { + args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """; + } + + var result = apiCompatTool(args, tempFolder) + .Where(t => t.Type == OutputType.Err).ToArray(); + if (result.Any()) + { + throw new AggregateException( + $"ApiDiffValidation task has failed for \"{Path.GetFileName(packagePath)}\" package", + result.Select(r => new Exception(r.Text))); + } + } } } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 7c921cb630..b82c446249 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -37,7 +37,7 @@ partial class Build : NukeBuild { BuildParameters Parameters { get; set; } - [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll")] + [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net6.0")] Tool ApiCompatTool; protected override void OnBuildInitialized() From 003bed5c80599dcb18997d64cfe3917d3adbd578 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:21:52 -0700 Subject: [PATCH 279/391] Update exclusions --- nukebuild/ApiDiffValidation.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 91bd54fd89..1bcb761cbb 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -97,7 +97,9 @@ public static class ApiDiffValidation private static IReadOnlyCollection<(string target, ZipArchiveEntry entry)> GetDlls(ZipArchive archive) { return archive.Entries - .Where(e => Path.GetExtension(e.FullName) == ".dll") + .Where(e => Path.GetExtension(e.FullName) == ".dll" + // Exclude analyzers and build task, as we don't care about breaking changes there + && !e.FullName.Contains("analyzers/") && !e.Name.Contains("Avalonia.Build.Tasks")) .Select(e => ( entry: e, isRef: e.FullName.Contains("ref/"), From 36b74afa17f587a3438bf677fdb0d1587754aac4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:24:38 -0700 Subject: [PATCH 280/391] Set UpdateApiValidationSuppression to true by default on local build --- nukebuild/BuildParameters.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 75c859f2e8..897fdc84f1 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -26,7 +26,7 @@ public partial class Build public string ApiValidationBaseline { get; set; } [Parameter(Name = "update-api-suppression")] - public bool UpdateApiValidationSuppression { get; set; } + public bool? UpdateApiValidationSuppression { get; set; } public class BuildParameters { @@ -112,7 +112,7 @@ public partial class Build Version = b.ForceNugetVersion ?? GetVersion(); ApiValidationBaseline = b.ApiValidationBaseline ?? new Version(new Version(Version).Major, 0).ToString(); - UpdateApiValidationSuppression = b.UpdateApiValidationSuppression; + UpdateApiValidationSuppression = b.UpdateApiValidationSuppression ?? IsLocalBuild; if (IsRunningOnAzure) { From 13c6fbe1dbd87e558cc9db0a54665bacb11a4349 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 12:31:44 -0700 Subject: [PATCH 281/391] Revert "Break the API" This reverts commit f043f6b1617c85706c7c33741024a09b5b8c7d89. --- src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls.ColorPicker/HsvComponent.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs index fb981db762..b1d15d6b17 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs @@ -24,14 +24,14 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - //OldColor = oldColor; + OldColor = oldColor; NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - //public Color OldColor { get; private set; } + public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 998633a8e1..1a7a13166a 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls /// /// Also see: /// - // Alpha = 0, + Alpha = 0, /// /// The Hue component. From c755ea628bf832fbdd9b8a575980d86564aedc0c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 7 Jul 2023 23:39:13 -0700 Subject: [PATCH 282/391] Fix regex --- nukebuild/ApiDiffValidation.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index 1bcb761cbb..a236178c83 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -117,9 +117,15 @@ public static class ApiDiffValidation try { + /* + Gets package name from versions like: + Avalonia.0.10.0-preview1 + Avalonia.11.0.999-cibuild0037534-beta + Avalonia.11.0.0 + */ var packageId = Regex.Replace( Path.GetFileNameWithoutExtension(packagePath), - """(\.\d+\.\d+\.\d+)$""", ""); + """(\.\d+\.\d+\.\d+(?:-.+)?)$""", ""); using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead); From 19bae967d30a00d2767921b1842087321dae624b Mon Sep 17 00:00:00 2001 From: jgcodes2020 Date: Sun, 9 Jul 2023 18:09:48 -0400 Subject: [PATCH 283/391] Rename ImportCompeted into ImportCompleted --- .../Composition/CompositionExternalMemory.cs | 11 +++++++++-- .../Rendering/Composition/CompositionInterop.cs | 6 +++--- .../Server/ServerCompositionDrawingSurface.cs | 6 +++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs index ce728f86a2..476e15c47f 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs @@ -84,14 +84,21 @@ public enum CompositionGpuImportedImageSynchronizationCapabilities /// /// An imported GPU object that's usable by composition APIs /// +[NotClientImplementable] public interface ICompositionGpuImportedObject : IAsyncDisposable { /// /// Tracks the import status of the object. Once the task is completed, /// the user code is allowed to free the resource owner in case when a non-owning - /// sharing handle was used + /// sharing handle was used. /// - Task ImportCompeted { get; } + Task ImportCompleted { get; } + + /// + /// ImportCompleted (recommended replacement) + [Obsolete("Please use ICompositionGpuImportedObject.ImportCompleted instead")] + Task ImportCompeted => ImportCompleted; + /// /// Indicates if the device context this instance is associated with is no longer available /// diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs index 1643ec6e8d..9eea228e84 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs @@ -67,18 +67,18 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject Context = context; Feature = feature; - ImportCompeted = Compositor.InvokeServerJobAsync(Import); + ImportCompleted = Compositor.InvokeServerJobAsync(Import); } protected abstract void Import(); public abstract void Dispose(); - public Task ImportCompeted { get; } + public Task ImportCompleted { get; } public bool IsLost => Context.IsLost; public ValueTask DisposeAsync() => new(Compositor.InvokeServerJobAsync(() => { - if (ImportCompeted.Status == TaskStatus.RanToCompletion) + if (ImportCompleted.Status == TaskStatus.RanToCompletion) Dispose(); })); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs index da24b3812a..f58403f0bc 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs @@ -31,12 +31,12 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp throw new PlatformGraphicsContextLostException(); // This should never happen, but check for it anyway to avoid a deadlock - if (!image.ImportCompeted.IsCompleted) + if (!image.ImportCompleted.IsCompleted) throw new InvalidOperationException("The import operation is not completed yet"); // Rethrow the import here exception - if (image.ImportCompeted.IsFaulted) - image.ImportCompeted.GetAwaiter().GetResult(); + if (image.ImportCompleted.IsFaulted) + image.ImportCompleted.GetAwaiter().GetResult(); } void Update(IBitmapImpl newImage, IPlatformRenderInterfaceContext context) From 1e5975d4ded724c362e11e28894fef1d834ac8d3 Mon Sep 17 00:00:00 2001 From: jgcodes2020 Date: Sun, 9 Jul 2023 22:04:58 -0400 Subject: [PATCH 284/391] Hide ImportCompeted from smart completion (to avoid confusion) --- .../Rendering/Composition/CompositionExternalMemory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs index 476e15c47f..236cb89fcd 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Threading.Tasks; using Avalonia.Metadata; using Avalonia.Platform; @@ -97,6 +98,7 @@ public interface ICompositionGpuImportedObject : IAsyncDisposable /// /// ImportCompleted (recommended replacement) [Obsolete("Please use ICompositionGpuImportedObject.ImportCompleted instead")] + [EditorBrowsable(EditorBrowsableState.Never)] Task ImportCompeted => ImportCompleted; /// From 4419ef7d4799af311dfc69169df04bd118cf9bce Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Mon, 10 Jul 2023 23:10:55 +0200 Subject: [PATCH 285/391] Add suppression for removed generated NamespaceInfo types --- ...ia.Controls.ColorPicker.11.0.999.nupkg.xml | 64 +++ api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml | 418 ++++++++++++++++++ api/Avalonia.Themes.Simple.11.0.999.nupkg.xml | 406 +++++++++++++++++ 3 files changed, 888 insertions(+) create mode 100644 api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml create mode 100644 api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml create mode 100644 api/Avalonia.Themes.Simple.11.0.999.nupkg.xml diff --git a/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml b/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml new file mode 100644 index 0000000000..978aafcc88 --- /dev/null +++ b/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml @@ -0,0 +1,64 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPicker.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPreviewer.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSlider.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSpectrum.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorView.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPicker.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPreviewer.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSlider.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSpectrum.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorView.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + \ No newline at end of file diff --git a/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml b/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml new file mode 100644 index 0000000000..5204e75fdd --- /dev/null +++ b/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml @@ -0,0 +1,418 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/BaseResources.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/FluentControlResources.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + \ No newline at end of file diff --git a/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml b/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml new file mode 100644 index 0000000000..199ccd76df --- /dev/null +++ b/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml @@ -0,0 +1,406 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/Base.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + \ No newline at end of file From 98e5f74f4e8c793c43a331a692b12ad77c3b5dac Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 10 Jul 2023 20:22:40 -0400 Subject: [PATCH 286/391] Fix xml files generation --- api/Avalonia.Controls.ColorPicker.nupkg.xml | 64 +++ api/Avalonia.Themes.Fluent.nupkg.xml | 418 ++++++++++++++++++++ api/Avalonia.Themes.Simple.nupkg.xml | 406 +++++++++++++++++++ nukebuild/ApiDiffValidation.cs | 30 +- 4 files changed, 905 insertions(+), 13 deletions(-) create mode 100644 api/Avalonia.Controls.ColorPicker.nupkg.xml create mode 100644 api/Avalonia.Themes.Fluent.nupkg.xml create mode 100644 api/Avalonia.Themes.Simple.nupkg.xml diff --git a/api/Avalonia.Controls.ColorPicker.nupkg.xml b/api/Avalonia.Controls.ColorPicker.nupkg.xml new file mode 100644 index 0000000000..978aafcc88 --- /dev/null +++ b/api/Avalonia.Controls.ColorPicker.nupkg.xml @@ -0,0 +1,64 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPicker.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPreviewer.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSlider.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSpectrum.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorView.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPicker.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPreviewer.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSlider.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSpectrum.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorView.xaml + baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + + \ No newline at end of file diff --git a/api/Avalonia.Themes.Fluent.nupkg.xml b/api/Avalonia.Themes.Fluent.nupkg.xml new file mode 100644 index 0000000000..5204e75fdd --- /dev/null +++ b/api/Avalonia.Themes.Fluent.nupkg.xml @@ -0,0 +1,418 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/BaseResources.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/FluentControlResources.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + + \ No newline at end of file diff --git a/api/Avalonia.Themes.Simple.nupkg.xml b/api/Avalonia.Themes.Simple.nupkg.xml new file mode 100644 index 0000000000..199ccd76df --- /dev/null +++ b/api/Avalonia.Themes.Simple.nupkg.xml @@ -0,0 +1,406 @@ + + + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/Base.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + + CP0001 + T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml + baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll + target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + + \ No newline at end of file diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index a236178c83..d0fc3d02e1 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -38,7 +38,7 @@ public static class ApiDiffValidation var left = new List(); var right = new List(); - var suppressionFile = Path.Combine(suppressionFilesFolder, Path.GetFileName(packagePath) + ".xml"); + var suppressionFile = Path.Combine(suppressionFilesFolder, GetPackageId(packagePath) + ".nupkg.xml"); foreach (var baselineDll in baselineDlls) { @@ -113,20 +113,17 @@ public static class ApiDiffValidation static async Task DownloadBaselinePackage(string packagePath, string baselineVersion) { - Build.Information("Downloading {0} baseline package for version {1}", Path.GetFileName(packagePath), baselineVersion); + /* + Gets package name from versions like: + Avalonia.0.10.0-preview1 + Avalonia.11.0.999-cibuild0037534-beta + Avalonia.11.0.0 + */ + var packageId = GetPackageId(packagePath); + Build.Information("Downloading {0} {1} baseline package", packageId, baselineVersion); try { - /* - Gets package name from versions like: - Avalonia.0.10.0-preview1 - Avalonia.11.0.999-cibuild0037534-beta - Avalonia.11.0.0 - */ - var packageId = Regex.Replace( - Path.GetFileNameWithoutExtension(packagePath), - """(\.\d+\.\d+\.\d+(?:-.+)?)$""", ""); - using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); @@ -139,7 +136,14 @@ public static class ApiDiffValidation } catch (Exception ex) { - throw new InvalidOperationException($"Downloading baseline package for {packagePath} failed.\r" + ex.Message, ex); + throw new InvalidOperationException($"Downloading baseline package for {packageId} {baselineVersion} failed.\r" + ex.Message, ex); } } + + static string GetPackageId(string packagePath) + { + return Regex.Replace( + Path.GetFileNameWithoutExtension(packagePath), + """(\.\d+\.\d+\.\d+(?:-.+)?)$""", ""); + } } From bd8628bfef9abd409aef8e37132c756a6814419b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 10 Jul 2023 20:22:47 -0400 Subject: [PATCH 287/391] Revert "Add suppression for removed generated NamespaceInfo types" This reverts commit 4419ef7d4799af311dfc69169df04bd118cf9bce. --- ...ia.Controls.ColorPicker.11.0.999.nupkg.xml | 64 --- api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml | 418 ------------------ api/Avalonia.Themes.Simple.11.0.999.nupkg.xml | 406 ----------------- 3 files changed, 888 deletions(-) delete mode 100644 api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml delete mode 100644 api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml delete mode 100644 api/Avalonia.Themes.Simple.11.0.999.nupkg.xml diff --git a/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml b/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml deleted file mode 100644 index 978aafcc88..0000000000 --- a/api/Avalonia.Controls.ColorPicker.11.0.999.nupkg.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPicker.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPreviewer.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSlider.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSpectrum.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorView.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPicker.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPreviewer.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSlider.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSpectrum.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorView.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - - \ No newline at end of file diff --git a/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml b/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml deleted file mode 100644 index 5204e75fdd..0000000000 --- a/api/Avalonia.Themes.Fluent.11.0.999.nupkg.xml +++ /dev/null @@ -1,418 +0,0 @@ - - - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/BaseResources.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/FluentControlResources.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - - \ No newline at end of file diff --git a/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml b/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml deleted file mode 100644 index 199ccd76df..0000000000 --- a/api/Avalonia.Themes.Simple.11.0.999.nupkg.xml +++ /dev/null @@ -1,406 +0,0 @@ - - - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/Base.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - - CP0001 - T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll - - \ No newline at end of file From edccb582615b91048e9e5bb995e53acf1138f25a Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 11 Jul 2023 18:13:54 +0200 Subject: [PATCH 288/391] Composition: collect IRenderDataItem resources recursively --- .../Drawing/ServerCompositionRenderData.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs index cb5e21be00..97083ccaf5 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs @@ -42,22 +42,31 @@ class ServerCompositionRenderData : SimpleServerRenderResource _items.Add(reader.ReadObject()); var collector = s_resourceHashSetPool.Get(); - foreach(var item in _items) - if (item is IRenderDataItemWithServerResources resourceItem) - resourceItem.Collect(collector); - + CollectResources(_items, collector); + foreach (var r in collector.Resources) { _referencedResources.Add(r); r.AddObserver(this); } - + collector.Resources.Clear(); s_resourceHashSetPool.ReturnAndSetNull(ref collector); base.DeserializeChangesCore(reader, committedAt); } + private static void CollectResources(PooledInlineList items, IRenderDataServerResourcesCollector collector) + { + foreach (var item in items) + { + if (item is IRenderDataItemWithServerResources resourceItem) + resourceItem.Collect(collector); + else if (item is RenderDataPushNode pushNode) + CollectResources(pushNode.Children, collector); + } + } + public Rect? Bounds { get @@ -133,4 +142,4 @@ class ServerCompositionRenderData : SimpleServerRenderResource Reset(); base.Dispose(); } -} \ No newline at end of file +} From 237e4cba69cf26e3ca8ca379c2326521dde29500 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 11 Jul 2023 18:35:38 +0200 Subject: [PATCH 289/391] Fix InlineDictionary enumeration when an array is used --- .../Utilities/SmallDictionary.cs | 16 +++--- .../Utilities/InlineDictionaryTests.cs | 53 +++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Utilities/InlineDictionaryTests.cs diff --git a/src/Avalonia.Base/Utilities/SmallDictionary.cs b/src/Avalonia.Base/Utilities/SmallDictionary.cs index b6e62411da..1e63d554b4 100644 --- a/src/Avalonia.Base/Utilities/SmallDictionary.cs +++ b/src/Avalonia.Base/Utilities/SmallDictionary.cs @@ -51,7 +51,7 @@ internal struct InlineDictionary : IEnumerable : IEnumerable(); + dic.Add("foo", 1); + dic.Add("bar", 2); + dic.Add("baz", 3); + + Assert.Equal( + new[] { + new KeyValuePair("foo", 1), + new KeyValuePair("bar", 2), + new KeyValuePair("baz", 3) + }, + dic); + } + + [Fact] + public void Enumeration_After_Remove_With_Internal_Array_Works() + { + var dic = new InlineDictionary(); + dic.Add("foo", 1); + dic.Add("bar", 2); + dic.Add("baz", 3); + + Assert.Equal( + new[] { + new KeyValuePair("foo", 1), + new KeyValuePair("bar", 2), + new KeyValuePair("baz", 3) + }, + dic); + + dic.Remove("bar"); + + Assert.Equal( + new[] { + new KeyValuePair("foo", 1), + new KeyValuePair("baz", 3) + }, + dic); + } +} From bd64a51eebbc36571b086d22d72a710d47f94309 Mon Sep 17 00:00:00 2001 From: john_doe Date: Tue, 11 Jul 2023 23:03:15 +0200 Subject: [PATCH 290/391] Changed null checks to make use of nameof() --- src/Avalonia.Base/Threading/Dispatcher.Invoke.cs | 8 ++++---- src/Avalonia.Base/Threading/DispatcherTimer.cs | 4 ++-- src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs | 2 +- .../DataGridColumnCollection.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridLength.cs | 2 +- .../Automation/Peers/ControlAutomationPeer.cs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs index 7d586e1cbd..25be5779b9 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs @@ -90,7 +90,7 @@ public partial class Dispatcher { if (callback == null) { - throw new ArgumentNullException("callback"); + throw new ArgumentNullException(nameof(callback)); } DispatcherPriority.Validate(priority, "priority"); @@ -212,7 +212,7 @@ public partial class Dispatcher { if (callback == null) { - throw new ArgumentNullException("callback"); + throw new ArgumentNullException(nameof(callback)); } DispatcherPriority.Validate(priority, "priority"); @@ -304,7 +304,7 @@ public partial class Dispatcher { if (callback == null) { - throw new ArgumentNullException("callback"); + throw new ArgumentNullException(nameof(callback)); } DispatcherPriority.Validate(priority, "priority"); @@ -379,7 +379,7 @@ public partial class Dispatcher { if (callback == null) { - throw new ArgumentNullException("callback"); + throw new ArgumentNullException(nameof(callback)); } DispatcherPriority.Validate(priority, "priority"); diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 879d9d8a5f..473b9eeefa 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -62,7 +62,7 @@ public partial class DispatcherTimer { if (callback == null) { - throw new ArgumentNullException("callback"); + throw new ArgumentNullException(nameof(callback)); } Tick += callback; @@ -253,7 +253,7 @@ public partial class DispatcherTimer { if (dispatcher == null) { - throw new ArgumentNullException("dispatcher"); + throw new ArgumentNullException(nameof(dispatcher)); } DispatcherPriority.Validate(priority, "priority"); diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs index 5d080f5ef6..b2deb1c26e 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs @@ -222,7 +222,7 @@ namespace Avalonia.Controls { if (element == null) { - throw new ArgumentNullException("element"); + throw new ArgumentNullException(nameof(element)); } if (element is CheckBox checkBox) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs index e7f9a9a6c4..798228c84c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs @@ -191,7 +191,7 @@ namespace Avalonia.Controls } if (dataGridColumn == null) { - throw new ArgumentNullException("dataGridColumn"); + throw new ArgumentNullException(nameof(dataGridColumn)); } int columnIndexWithFiller = columnIndex; diff --git a/src/Avalonia.Controls.DataGrid/DataGridLength.cs b/src/Avalonia.Controls.DataGrid/DataGridLength.cs index 4841ddd494..a193352f52 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridLength.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridLength.cs @@ -499,7 +499,7 @@ namespace Avalonia.Controls { if (destinationType == null) { - throw new ArgumentNullException("destinationType"); + throw new ArgumentNullException(nameof(destinationType)); } if (destinationType != typeof(string)) { diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index 756951667b..c19d887230 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -18,7 +18,7 @@ namespace Avalonia.Automation.Peers public ControlAutomationPeer(Control owner) { - Owner = owner ?? throw new ArgumentNullException("owner"); + Owner = owner ?? throw new ArgumentNullException(nameof(owner)); Initialize(); } From 54158b475efce777c567d9a325d13a99b1c689af Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 11 Jul 2023 23:09:49 -0400 Subject: [PATCH 291/391] Fix separators issue in ApiDiffValidatiton too --- api/Avalonia.Controls.ColorPicker.nupkg.xml | 40 +-- api/Avalonia.Themes.Fluent.nupkg.xml | 276 ++++++++++---------- api/Avalonia.Themes.Simple.nupkg.xml | 268 +++++++++---------- nukebuild/ApiDiffValidation.cs | 14 +- 4 files changed, 301 insertions(+), 297 deletions(-) diff --git a/api/Avalonia.Controls.ColorPicker.nupkg.xml b/api/Avalonia.Controls.ColorPicker.nupkg.xml index 978aafcc88..90271d6e98 100644 --- a/api/Avalonia.Controls.ColorPicker.nupkg.xml +++ b/api/Avalonia.Controls.ColorPicker.nupkg.xml @@ -4,61 +4,61 @@ CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPicker.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorPreviewer.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSlider.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorSpectrum.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Fluent/ColorView.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPicker.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorPreviewer.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSlider.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorSpectrum.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Themes/Simple/ColorView.xaml - baseline\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll - target\lib\netstandard2.0\Avalonia.Controls.ColorPicker.dll + baseline/netstandard2.0/Avalonia.Controls.ColorPicker.dll + target/netstandard2.0/Avalonia.Controls.ColorPicker.dll \ No newline at end of file diff --git a/api/Avalonia.Themes.Fluent.nupkg.xml b/api/Avalonia.Themes.Fluent.nupkg.xml index 5204e75fdd..f35bf5a629 100644 --- a/api/Avalonia.Themes.Fluent.nupkg.xml +++ b/api/Avalonia.Themes.Fluent.nupkg.xml @@ -4,415 +4,415 @@ CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/BaseResources.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/FluentControlResources.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Fluent.dll - target\lib\netstandard2.0\Avalonia.Themes.Fluent.dll + baseline/netstandard2.0/Avalonia.Themes.Fluent.dll + target/netstandard2.0/Avalonia.Themes.Fluent.dll \ No newline at end of file diff --git a/api/Avalonia.Themes.Simple.nupkg.xml b/api/Avalonia.Themes.Simple.nupkg.xml index 199ccd76df..78d8c4b332 100644 --- a/api/Avalonia.Themes.Simple.nupkg.xml +++ b/api/Avalonia.Themes.Simple.nupkg.xml @@ -4,403 +4,403 @@ CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Accents/Base.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AdornerLayer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/AutoCompleteBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Button.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ButtonSpinner.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Calendar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarDayButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CalendarItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CaptionButtons.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Carousel.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/CheckBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ComboBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ContextMenu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DataValidationErrors.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DatePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DateTimePickerShared.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/DropDownButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/EmbeddableControlRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Expander.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/FlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/GridSplitter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ItemsControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Label.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ListBoxItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ManagedFileChooser.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Menu.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuFlyoutPresenter.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/MenuItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NativeMenuBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NotificationCard.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/NumericUpDown.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/OverlayPopupHost.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PathIcon.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/PopupRoot.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ProgressBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RadioButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshContainer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RefreshVisualizer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/RepeatButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ScrollViewer.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SelectableTextBlock.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Separator.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Slider.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/SplitView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStrip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TabStripItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TextBox.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ThemeVariantScope.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TimePicker.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TitleBar.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleButton.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToggleSwitch.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/ToolTip.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TransitioningContentControl.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeView.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/TreeViewItem.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/Window.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll CP0001 T:CompiledAvaloniaXaml.!AvaloniaResources.NamespaceInfo:/Controls/WindowNotificationManager.xaml - baseline\lib\netstandard2.0\Avalonia.Themes.Simple.dll - target\lib\netstandard2.0\Avalonia.Themes.Simple.dll + baseline/netstandard2.0/Avalonia.Themes.Simple.dll + target/netstandard2.0/Avalonia.Themes.Simple.dll \ No newline at end of file diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index d0fc3d02e1..bd576250bd 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -40,9 +40,12 @@ public static class ApiDiffValidation var suppressionFile = Path.Combine(suppressionFilesFolder, GetPackageId(packagePath) + ".nupkg.xml"); + // Don't use Path.Combine with these left and right tool parameters. + // Microsoft.DotNet.ApiCompat.Tool is stupid and treats '/' and '\' as different assemblies in suppression files. + // So, always use Unix '/' foreach (var baselineDll in baselineDlls) { - var baselineDllPath = Path.Combine("baseline", baselineDll.target, baselineDll.entry.Name); + var baselineDllPath = $"baseline/{baselineDll.target}/{baselineDll.entry.Name}"; var baselineDllRealPath = Path.Combine(tempFolder, baselineDllPath); Directory.CreateDirectory(Path.GetDirectoryName(baselineDllRealPath)!); await using (var baselineDllFile = File.Create(baselineDllRealPath)) @@ -57,7 +60,7 @@ public static class ApiDiffValidation throw new InvalidOperationException($"Some assemblies are missing in the new package: {baselineDll.entry.Name} for {baselineDll.target}"); } - var targetDllPath = Path.Combine("target", targetDll.target, targetDll.entry.Name); + var targetDllPath = $"target/{targetDll.target}/{targetDll.entry.Name}"; var targetDllRealPath = Path.Combine(tempFolder, targetDllPath); Directory.CreateDirectory(Path.GetDirectoryName(targetDllRealPath)!); await using (var targetDllFile = File.Create(targetDllRealPath)) @@ -99,11 +102,12 @@ public static class ApiDiffValidation return archive.Entries .Where(e => Path.GetExtension(e.FullName) == ".dll" // Exclude analyzers and build task, as we don't care about breaking changes there - && !e.FullName.Contains("analyzers/") && !e.Name.Contains("Avalonia.Build.Tasks")) + && !e.FullName.Contains("analyzers/") && !e.FullName.Contains("analyzers\\") + && !e.Name.Contains("Avalonia.Build.Tasks")) .Select(e => ( entry: e, - isRef: e.FullName.Contains("ref/"), - target: Path.GetDirectoryName(e.FullName)!.Split('/').Last()) + isRef: e.FullName.Contains("ref/") || e.FullName.Contains("ref\\"), + target: Path.GetDirectoryName(e.FullName)!.Split(new [] { '/', '\\' }).Last()) ) .GroupBy(e => (e.target, e.entry.Name)) .Select(g => g.MaxBy(e => e.isRef)) From 157c6e91a000d57e38692b9fca8383529c769619 Mon Sep 17 00:00:00 2001 From: Tom Edwards Date: Fri, 9 Jun 2023 21:38:32 +0200 Subject: [PATCH 292/391] Add an overload for GetObservable that accepts an untyped AvaloniaProperty and a converter to a typed value Added missing docstrings for type params --- src/Avalonia.Base/AvaloniaObjectExtensions.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index b3f41eb420..833cd4b034 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -60,6 +60,8 @@ namespace Avalonia } /// + /// The type of the values held by the . + /// The type of the value returned by the . /// /// /// A method which is executed to convert each property value to . @@ -71,6 +73,15 @@ namespace Avalonia converter ?? throw new ArgumentNullException(nameof(converter))); } + /// + public static IObservable GetObservable(this AvaloniaObject o, AvaloniaProperty property, Func converter) + { + return new AvaloniaPropertyObservable( + o ?? throw new ArgumentNullException(nameof(o)), + property ?? throw new ArgumentNullException(nameof(property)), + converter ?? throw new ArgumentNullException(nameof(converter))); + } + /// /// Gets an observable for an . /// @@ -92,6 +103,15 @@ namespace Avalonia property ?? throw new ArgumentNullException(nameof(property))); } + /// + public static IObservable> GetBindingObservable(this AvaloniaObject o, AvaloniaProperty property, Func converter) + { + return new AvaloniaPropertyBindingObservable( + o ?? throw new ArgumentNullException(nameof(o)), + property ?? throw new ArgumentNullException(nameof(property)), + converter?? throw new ArgumentNullException(nameof(converter))); + } + /// /// Gets an observable for an . /// From aa7a2a27644b974b06da0f4108c5a30d3c7368e9 Mon Sep 17 00:00:00 2001 From: startewho Date: Thu, 13 Jul 2023 17:11:07 +0800 Subject: [PATCH 293/391] fix 12161 --- src/Avalonia.Controls/Utils/StringUtils.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs index 2ea47b442a..b664687650 100644 --- a/src/Avalonia.Controls/Utils/StringUtils.cs +++ b/src/Avalonia.Controls/Utils/StringUtils.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls.Utils } var codepoint = new Codepoint(text[index]); - + if (!codepoint.IsWhiteSpace) { return false; @@ -85,12 +85,16 @@ namespace Avalonia.Controls.Utils // preceeded by lwsp. if (index > 0) { - var nextCodePoint = new Codepoint(text[index + 1]); - - if (nextCodePoint.IsBreakChar) + if (index + 1 < text.Length) { - return true; + var nextCodePoint = new Codepoint(text[index + 1]); + + if (nextCodePoint.IsBreakChar) + { + return true; + } } + return true; } switch (codepoint.GeneralCategory) @@ -125,7 +129,7 @@ namespace Avalonia.Controls.Utils } cursor = Math.Min(cursor, text.Length); - + int begin; int i; int cr; @@ -181,7 +185,7 @@ namespace Avalonia.Controls.Utils { return cursor; } - + if (cr < text.Length && text[cr] == '\r' && cr + 1 < text.Length && text[cr + 1] == '\n') { lf = cr + 1; @@ -214,9 +218,9 @@ namespace Avalonia.Controls.Utils { return i; } - + var cc = GetCharClass(text[i]); - + // skip over the word, punctuation, or run of whitespace while (i < cr && GetCharClass(text[i]) == cc) { From add676efab0f904b6200824c567ac6d238462419 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 13 Jul 2023 13:08:47 +0300 Subject: [PATCH 294/391] Add failing tests --- .../TransitioningContentControlTests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs index 02dd4e6c03..0e817b1887 100644 --- a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -183,6 +183,31 @@ namespace Avalonia.Controls.UnitTests Assert.Equal("bar", presenter2.Content); } + [Fact] + public void Logical_Children_Dont_Duplicated() + { + using var app = Start(); + var (target, transition) = CreateTarget(""); + + var childControl = new Control(); + target.Content = childControl; + + // There should be two, One the initial content and one the new content. + Assert.Equal(2, target.LogicalChildren.Count); + } + + [Fact] + public void First_Presenter_Register_TCC_As_Host() + { + using var app = Start(); + var (target, transition) = CreateTarget(""); + + var childControl = new Control(); + target.Presenter!.Content = childControl; + + Assert.Contains(childControl, target.LogicalChildren); + } + private static IDisposable Start() { return UnitTestApplication.Start( From f7a584e86e0db552cd5509bd827361360ff8211c Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 13 Jul 2023 13:14:11 +0300 Subject: [PATCH 295/391] Return true when first presenter register and fix logical children duplicated --- src/Avalonia.Controls/ContentControl.cs | 16 ++++++++++++++++ .../TransitioningContentControl.cs | 11 +++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index 867dca2be1..176157001e 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -95,6 +95,17 @@ namespace Avalonia.Controls /// IAvaloniaList IContentPresenterHost.LogicalChildren => LogicalChildren; + /// + /// Determine whether manage his LogicalChildren + ///(default behavior) himself, or leaves the management to the inherited control. + /// + /// + /// The default value is false, So the manages itself, + /// if you want to bypass this behavior and manage LogicalChildren yourself, set + /// the to true. + /// + protected virtual bool BypassLogicalChildrenManangment => false; + /// bool IContentPresenterHost.RegisterContentPresenter(ContentPresenter presenter) { @@ -118,6 +129,11 @@ namespace Avalonia.Controls private void ContentChanged(AvaloniaPropertyChangedEventArgs e) { + if (BypassLogicalChildrenManangment) + { + return; + } + if (e.OldValue is ILogical oldChild) { LogicalChildren.Remove(oldChild); diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs index bf540698f1..75f95d1d68 100644 --- a/src/Avalonia.Controls/TransitioningContentControl.cs +++ b/src/Avalonia.Controls/TransitioningContentControl.cs @@ -36,6 +36,9 @@ public class TransitioningContentControl : ContentControl set => SetValue(PageTransitionProperty, value); } + /// + protected override bool BypassLogicalChildrenManangment => true; + protected override Size ArrangeOverride(Size finalSize) { var result = base.ArrangeOverride(finalSize); @@ -79,8 +82,12 @@ public class TransitioningContentControl : ContentControl protected override bool RegisterContentPresenter(ContentPresenter presenter) { - if (!base.RegisterContentPresenter(presenter) && - presenter is ContentPresenter p && + if (base.RegisterContentPresenter(presenter)) + { + return true; + } + + if (presenter is ContentPresenter p && p.Name == "PART_ContentPresenter2") { _presenter2 = p; From 22ef68e43960f57368c593ca31841fe90c9f4f3a Mon Sep 17 00:00:00 2001 From: Gundlack Florian Date: Thu, 13 Jul 2023 13:45:16 +0200 Subject: [PATCH 296/391] Fixed issue where RowDetailsTemplate was getting the wrong DataContext --- src/Avalonia.Controls.DataGrid/DataGridRows.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 44079d24d0..107a2a7353 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -2970,10 +2970,7 @@ namespace Avalonia.Controls if (detailsContent != null) { _rowsPresenter.Children.Add(detailsContent); - if (dataItem != null) - { - detailsContent.DataContext = dataItem; - } + detailsContent.DataContext = dataItem; detailsContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); RowDetailsHeightEstimate = detailsContent.DesiredSize.Height; _rowsPresenter.Children.Remove(detailsContent); From abc7eea3369642162617c9a20a91b27e0d7b87dc Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 13 Jul 2023 15:33:42 +0300 Subject: [PATCH 297/391] simplifies the first test --- .../TransitioningContentControlTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs index 0e817b1887..210b24b3ad 100644 --- a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -188,12 +188,12 @@ namespace Avalonia.Controls.UnitTests { using var app = Start(); var (target, transition) = CreateTarget(""); + target.PageTransition = null; var childControl = new Control(); target.Content = childControl; - // There should be two, One the initial content and one the new content. - Assert.Equal(2, target.LogicalChildren.Count); + Assert.Equal(1, target.LogicalChildren.Count); } [Fact] From 35be25ee2e99f03ca0dfa6f96f0c6dd47edf8d05 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 13 Jul 2023 16:38:11 +0300 Subject: [PATCH 298/391] typo --- src/Avalonia.Controls/ContentControl.cs | 4 ++-- src/Avalonia.Controls/TransitioningContentControl.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index 176157001e..362931eafa 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -102,9 +102,9 @@ namespace Avalonia.Controls /// /// The default value is false, So the manages itself, /// if you want to bypass this behavior and manage LogicalChildren yourself, set - /// the to true. + /// the to true. /// - protected virtual bool BypassLogicalChildrenManangment => false; + protected virtual bool BypassLogicalChildrenManagement => false; /// bool IContentPresenterHost.RegisterContentPresenter(ContentPresenter presenter) diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs index 75f95d1d68..c113afde17 100644 --- a/src/Avalonia.Controls/TransitioningContentControl.cs +++ b/src/Avalonia.Controls/TransitioningContentControl.cs @@ -37,7 +37,7 @@ public class TransitioningContentControl : ContentControl } /// - protected override bool BypassLogicalChildrenManangment => true; + protected override bool BypassLogicalChildrenManagement => true; protected override Size ArrangeOverride(Size finalSize) { From 3fc0d19ad017a819535a0610058652aa07d57348 Mon Sep 17 00:00:00 2001 From: Gundlack Florian Date: Thu, 13 Jul 2023 15:47:28 +0200 Subject: [PATCH 299/391] Moved detailsContent.DataContext assignation before Children.Add --- src/Avalonia.Controls.DataGrid/DataGridRows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 107a2a7353..c9a348d172 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -2969,8 +2969,8 @@ namespace Avalonia.Controls var detailsContent = RowDetailsTemplate.Build(dataItem); if (detailsContent != null) { - _rowsPresenter.Children.Add(detailsContent); detailsContent.DataContext = dataItem; + _rowsPresenter.Children.Add(detailsContent); detailsContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); RowDetailsHeightEstimate = detailsContent.DesiredSize.Height; _rowsPresenter.Children.Remove(detailsContent); From e25995f3c94e2f8a9781c8d93afe3e9ee5d229cf Mon Sep 17 00:00:00 2001 From: jgcodes2020 Date: Thu, 13 Jul 2023 10:25:58 -0400 Subject: [PATCH 300/391] Implement @maxkatz6's suggestions --- .../Rendering/Composition/CompositionExternalMemory.cs | 4 +++- src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs index 236cb89fcd..31cbaf2a29 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs @@ -6,6 +6,8 @@ using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Rendering.Composition; + +[NotClientImplementable] public interface ICompositionGpuInterop { /// @@ -99,7 +101,7 @@ public interface ICompositionGpuImportedObject : IAsyncDisposable /// ImportCompleted (recommended replacement) [Obsolete("Please use ICompositionGpuImportedObject.ImportCompleted instead")] [EditorBrowsable(EditorBrowsableState.Never)] - Task ImportCompeted => ImportCompleted; + Task ImportCompeted { get; } /// /// Indicates if the device context this instance is associated with is no longer available diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs index 9eea228e84..12a252ed96 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs @@ -74,6 +74,8 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject public abstract void Dispose(); public Task ImportCompleted { get; } + + public Task ImportCompeted => ImportCompleted; public bool IsLost => Context.IsLost; public ValueTask DisposeAsync() => new(Compositor.InvokeServerJobAsync(() => From 007800357865a37db308512ff4731b42ee9bcf15 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 13 Jul 2023 21:02:45 +0300 Subject: [PATCH 301/391] typo --- src/Avalonia.Controls/ContentControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index 362931eafa..47f7598900 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -129,7 +129,7 @@ namespace Avalonia.Controls private void ContentChanged(AvaloniaPropertyChangedEventArgs e) { - if (BypassLogicalChildrenManangment) + if (BypassLogicalChildrenManagement) { return; } From 081803b292ce66c179441bfc9017019871271c98 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 13 Jul 2023 17:20:23 -0400 Subject: [PATCH 302/391] Add api change suppression for ImportCompleted --- api/Avalonia.nupkg.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 api/Avalonia.nupkg.xml diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml new file mode 100644 index 0000000000..6a7d53544e --- /dev/null +++ b/api/Avalonia.nupkg.xml @@ -0,0 +1,10 @@ + + + + + CP0006 + P:Avalonia.Rendering.Composition.ICompositionGpuImportedObject.ImportCompleted + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + + \ No newline at end of file From 4c1d7b4a204e437ac0f1e648361522753d650800 Mon Sep 17 00:00:00 2001 From: startewho Date: Fri, 14 Jul 2023 08:38:00 +0800 Subject: [PATCH 303/391] Update StringUtils.cs --- src/Avalonia.Controls/Utils/StringUtils.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs index b664687650..0ae1ef0b97 100644 --- a/src/Avalonia.Controls/Utils/StringUtils.cs +++ b/src/Avalonia.Controls/Utils/StringUtils.cs @@ -94,7 +94,11 @@ namespace Avalonia.Controls.Utils return true; } } - return true; + else + { + return true; + } + } switch (codepoint.GeneralCategory) From 4e8e78886640ed84c89b7debb5c78957fb10829c Mon Sep 17 00:00:00 2001 From: startewho Date: Fri, 14 Jul 2023 09:33:28 +0800 Subject: [PATCH 304/391] handled --- src/Avalonia.Controls/ComboBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index bce1a3685f..fb27b3da22 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -270,6 +270,7 @@ namespace Avalonia.Controls { if (_popup?.IsInsidePopup(source) == true) { + e.Handled = true; return; } } From f89a0b2ac9323b38cd9c376f8bf2ed30814a1bd5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 14 Jul 2023 10:57:45 +0200 Subject: [PATCH 305/391] Added failing test for #11220. --- .../SelectingItemsControlTests_SelectedValue.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs index e9b8895174..9ea12c62fb 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_SelectedValue.cs @@ -268,6 +268,22 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.True(called); } + [Fact] + public void Handles_Null_SelectedItem_When_SelectedValueBinding_Assigned() + { + // Issue #11220 + var items = new object[] { null }; + var sic = new SelectingItemsControl + { + ItemsSource = items, + SelectedIndex = 0, + SelectedValueBinding = new Binding("Name"), + Template = Template() + }; + + Assert.Null(sic.SelectedValue); + } + private static FuncControlTemplate Template() { return new FuncControlTemplate((control, scope) => From c7da9c4bb86cc3583e796a45106ebb9fb8e6ad0d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 14 Jul 2023 11:01:38 +0200 Subject: [PATCH 306/391] Allow null item in BindingHelper.Evaluate. Fixes #11220. --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index e22d03273a..66ed1a27e2 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -1395,10 +1395,8 @@ namespace Avalonia.Controls.Primitives public object Evaluate(object? dataContext) { - dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext)); - // Only update the DataContext if necessary - if (!dataContext.Equals(DataContext)) + if (!Equals(dataContext, DataContext)) DataContext = dataContext; return GetValue(ValueProperty); From 43e1d99bdd70315aa069f867928a053b0b22eb23 Mon Sep 17 00:00:00 2001 From: Lighto Date: Fri, 14 Jul 2023 15:44:14 +0300 Subject: [PATCH 307/391] Added support for Mica Light & Dark based on Win11 --- samples/ControlCatalog/MainView.xaml | 3 +- .../WindowTransparencyLevel.cs | 9 +- .../WinRT/Composition/D2DEffects.cs | 3 + .../WinRT/Composition/WinUIEffectBase.cs | 104 ++++++++++++++++++ .../Composition/WinUiCompositedWindow.cs | 27 +++-- .../Composition/WinUiCompositionShared.cs | 8 +- .../Composition/WinUiCompositionUtils.cs | 75 ++++++++++++- src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs | 3 +- .../WinRT/WinRTPropertyValue.cs | 23 +++- src/Windows/Avalonia.Win32/WindowImpl.cs | 22 +++- 10 files changed, 253 insertions(+), 24 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 7ed2d67379..0a1621d02a 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -226,7 +226,8 @@ Transparent Blur AcrylicBlur - Mica + MicaLight + MicaDark - /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 + /// The window background is based on desktop wallpaper tint with a light blur. This will only work on Windows 11 /// - public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica)); + public static WindowTransparencyLevel MicaLight { get; } = new(nameof(MicaLight)); + + /// + /// The window background is based on desktop wallpaper tint with a dark blur. This will only work on Windows 11 + /// + public static WindowTransparencyLevel MicaDark { get; } = new(nameof(MicaDark)); public override string ToString() { diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs index bef5a55b06..98e1077885 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs @@ -28,6 +28,9 @@ namespace Avalonia.Win32.WinRT.Composition public static readonly Guid CLSID_D2D1Border = new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27); + public static readonly Guid CLSID_D2D1Opacity = + new Guid("811d79a4-de28-4454-8094-c64685f8bd4c"); + public static readonly Guid CLSID_D2D1Brightness = new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs index 1a922b4acd..10dbcfcc6b 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs @@ -52,6 +52,110 @@ namespace Avalonia.Win32.WinRT.Composition _sources = null; } } + + class BorderEffect : WinUIEffectBase + { + private readonly int _x; + private readonly int _y; + public override Guid EffectId => D2DEffects.CLSID_D2D1Border; + public override uint PropertyCount => 2; + + public BorderEffect(int x, int y, params IGraphicsEffectSource[] _sources):base(_sources) + { + _x = x; + _y = y; + } + + public override IPropertyValue? GetProperty(uint index) + { + if (index == 0) + return new WinRTPropertyValue((uint)_x); + if (index == 1) + return new WinRTPropertyValue((uint)_y); + return null; + } + } + + class BlendEffect : WinUIEffectBase + { + private readonly int _mode; + + public BlendEffect(int mode, params IGraphicsEffectSource[] _sources) : base(_sources) + { + _mode = mode; + } + + public override Guid EffectId => D2DEffects.CLSID_D2D1Blend; + public override uint PropertyCount => 1; + + public override IPropertyValue? GetProperty(uint index) + { + if (index == 0) + return new WinRTPropertyValue((uint)_mode); + return null; + } + } + + class CompositeStepEffect : WinUIEffectBase + { + private readonly float _mode; + + public CompositeStepEffect(int mode, params IGraphicsEffectSource[] _sources) : base(_sources) + { + _mode = mode; + } + + public override Guid EffectId => D2DEffects.CLSID_D2D1Composite; + public override uint PropertyCount => 1; + + public override IPropertyValue? GetProperty(uint index) + { + if (index == 0) + return new WinRTPropertyValue((uint)_mode); + return null; + } + } + + class OpacityEffect : WinUIEffectBase + { + private readonly float _opacity; + + public OpacityEffect(float opacity, params IGraphicsEffectSource[] _sources) : base(_sources) + { + _opacity = opacity; + } + + public override Guid EffectId => D2DEffects.CLSID_D2D1Opacity; + public override uint PropertyCount => 1; + + public override IPropertyValue? GetProperty(uint index) + { + if (index == 0) + return new WinRTPropertyValue(_opacity); + return null; + } + } + + class ColorSourceEffect : WinUIEffectBase + { + private readonly float[] _color; + + public ColorSourceEffect(float[] color) + { + _color = color; + } + + public override Guid EffectId => D2DEffects.CLSID_D2D1Flood; + public override uint PropertyCount => 1; + + public override IPropertyValue? GetProperty(uint index) + { + if (index == 0) + return new WinRTPropertyValue(_color); + return null; + } + } + internal class WinUIGaussianBlurEffect : WinUIEffectBase { diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs index 2f22ba99f9..d87c8d1361 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs @@ -12,7 +12,8 @@ internal class WinUiCompositedWindow : IDisposable public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; } private readonly WinUiCompositionShared _shared; private readonly ICompositionRoundedRectangleGeometry? _compositionRoundedRectangleGeometry; - private readonly IVisual? _mica; + private readonly IVisual? _micaLight; + private readonly IVisual? _micaDark; private readonly IVisual _blur; private readonly IVisual _visual; private PixelSize _size; @@ -25,7 +26,8 @@ internal class WinUiCompositedWindow : IDisposable { _compositionRoundedRectangleGeometry?.Dispose(); _blur.Dispose(); - _mica?.Dispose(); + _micaLight?.Dispose(); + _micaDark?.Dispose(); _visual.Dispose(); _surfaceBrush.Dispose(); _target.Dispose(); @@ -50,14 +52,20 @@ internal class WinUiCompositedWindow : IDisposable _target.SetRoot(containerVisual); _blur = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.BlurBrush); - if (shared.MicaBrush != null) + if (shared.MicaBrushLight != null) { - _mica = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrush); - containerChildren.InsertAtTop(_mica); + _micaLight = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrushLight); + containerChildren.InsertAtTop(_micaLight); + } + + if (shared.MicaBrushDark != null) + { + _micaDark = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrushDark); + containerChildren.InsertAtTop(_micaDark); } _compositionRoundedRectangleGeometry = - WinUiCompositionUtils.ClipVisual(shared.Compositor, backdropCornerRadius, _blur, _mica); + WinUiCompositionUtils.ClipVisual(shared.Compositor, backdropCornerRadius, _blur, _micaLight, _micaDark); containerChildren.InsertAtTop(_blur); using var spriteVisual = shared.Compositor.CreateSpriteVisual(); @@ -79,12 +87,13 @@ internal class WinUiCompositedWindow : IDisposable { lock (_shared.SyncRoot) { - _blur.SetIsVisible(blurEffect == BlurEffect.Acrylic - || blurEffect == BlurEffect.Mica && _mica == null ? + || (blurEffect == BlurEffect.MicaLight && _micaLight == null) || + (blurEffect == BlurEffect.MicaDark && _micaDark == null) ? 1 : 0); - _mica?.SetIsVisible(blurEffect == BlurEffect.Mica ? 1 : 0); + _micaLight?.SetIsVisible(blurEffect == BlurEffect.MicaLight ? 1 : 0); + _micaDark?.SetIsVisible(blurEffect == BlurEffect.MicaDark ? 1 : 0); } } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs index b3a328d097..5c7f490710 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs @@ -9,6 +9,8 @@ internal class WinUiCompositionShared : IDisposable public ICompositor5 Compositor5 { get; } public ICompositorDesktopInterop DesktopInterop { get; } public ICompositionBrush BlurBrush { get; } + public ICompositionBrush? MicaBrushLight { get; } + public ICompositionBrush? MicaBrushDark { get; } public ICompositionBrush? MicaBrush { get; } public object SyncRoot { get; } = new(); @@ -21,14 +23,16 @@ internal class WinUiCompositionShared : IDisposable Compositor = compositor.CloneReference(); Compositor5 = compositor.QueryInterface(); BlurBrush = WinUiCompositionUtils.CreateAcrylicBlurBackdropBrush(compositor); - MicaBrush = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor); + MicaBrushLight = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor, 242, 0.6f); + MicaBrushDark = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor, 32, 0.8f); DesktopInterop = compositor.QueryInterface(); } public void Dispose() { BlurBrush.Dispose(); - MicaBrush?.Dispose(); + MicaBrushLight?.Dispose(); + MicaBrushDark?.Dispose(); DesktopInterop.Dispose(); Compositor.Dispose(); Compositor5.Dispose(); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs index 7b970868df..1a130698ee 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs @@ -1,3 +1,4 @@ +using System; using System.Numerics; using MicroCom.Runtime; @@ -5,16 +6,75 @@ namespace Avalonia.Win32.WinRT.Composition; internal static class WinUiCompositionUtils { - public static ICompositionBrush? CreateMicaBackdropBrush(ICompositor compositor) + public static ICompositionBrush? CreateMicaBackdropBrush(ICompositor compositor, float color, float opacity) { if (Win32Platform.WindowsVersion.Build < 22000) return null; + using var backDropParameterFactory = + NativeWinRTMethods.CreateActivationFactory( + "Windows.UI.Composition.CompositionEffectSourceParameter"); + + + var tint = new[] { color / 255f, color / 255f, color / 255f, 255f / 255f }; + + using var tintColorEffect = new ColorSourceEffect(tint); + + + using var tintOpacityEffect = new OpacityEffect(1.0f, tintColorEffect); + using var tintOpacityEffectFactory = compositor.CreateEffectFactory(tintOpacityEffect); + using var tintOpacityEffectBrushEffect = tintOpacityEffectFactory.CreateBrush(); + using var tintOpacityEffectBrush = tintOpacityEffectBrushEffect.QueryInterface(); + + using var luminosityColorEffect = new ColorSourceEffect(tint); + + using var luminosityOpacityEffect = new OpacityEffect(opacity, luminosityColorEffect); + using var luminosityOpacityEffectFactory = compositor.CreateEffectFactory(luminosityOpacityEffect); + using var luminosityOpacityEffectBrushEffect = luminosityOpacityEffectFactory.CreateBrush(); + using var luminosityOpacityEffectBrush = + luminosityOpacityEffectBrushEffect.QueryInterface(); + + + // using var backDropParameterAsSource = GetParameterSource("BlurredWallpaperBackdrop", backDropParameterFactory, out var backdropHandle); + // using var backdropCompositionBrsuh = backDropParameterAsSource.QueryInterface(); using var compositorWithBlurredWallpaperBackdropBrush = compositor.QueryInterface(); using var blurredWallpaperBackdropBrush = compositorWithBlurredWallpaperBackdropBrush?.TryCreateBlurredWallpaperBackdropBrush(); - return blurredWallpaperBackdropBrush?.QueryInterface(); + using var micaBackdropBrush = blurredWallpaperBackdropBrush?.QueryInterface(); + + + using var backgroundParameterAsSource = + GetParameterSource("Background", backDropParameterFactory, out var backgroundHandle); + using var foregroundParameterAsSource = + GetParameterSource("Foreground", backDropParameterFactory, out var foregroundHandle); + + using var luminosityBlendEffect = + new BlendEffect(23, backgroundParameterAsSource, foregroundParameterAsSource); + using var luminosityBlendEffectFactory = compositor.CreateEffectFactory(luminosityBlendEffect); + using var luminosityBlendEffectBrush = luminosityBlendEffectFactory.CreateBrush(); + using var luminosityBlendEffectBrush1 = luminosityBlendEffectBrush.QueryInterface(); + luminosityBlendEffectBrush.SetSourceParameter(backgroundHandle, micaBackdropBrush); + luminosityBlendEffectBrush.SetSourceParameter(foregroundHandle, luminosityOpacityEffectBrush); + + + using var backgroundParameterAsSource1 = + GetParameterSource("Background", backDropParameterFactory, out var backgroundHandle1); + using var foregroundParameterAsSource1 = + GetParameterSource("Foreground", backDropParameterFactory, out var foregroundHandle1); + + using var colorBlendEffect = + new BlendEffect(22, backgroundParameterAsSource1, foregroundParameterAsSource1); + using var colorBlendEffectFactory = compositor.CreateEffectFactory(colorBlendEffect); + using var colorBlendEffectBrush = colorBlendEffectFactory.CreateBrush(); + colorBlendEffectBrush.SetSourceParameter(backgroundHandle1, luminosityBlendEffectBrush1); + colorBlendEffectBrush.SetSourceParameter(foregroundHandle1, tintOpacityEffectBrush); + + + // colorBlendEffectBrush.SetSourceParameter(backgroundHandle, micaBackdropBrush); + + using var micaBackdropBrush1 = colorBlendEffectBrush.QueryInterface(); + return micaBackdropBrush1.CloneReference(); } public static ICompositionBrush CreateAcrylicBlurBackdropBrush(ICompositor compositor) @@ -97,4 +157,15 @@ internal static class WinUiCompositionUtils brush?.Dispose(); } } + + private static IGraphicsEffectSource GetParameterSource(string name, + ICompositionEffectSourceParameterFactory backDropParameterFactory, out IntPtr handle) + { + var backdropString = new HStringInterop(name); + var backDropParameter = + backDropParameterFactory.Create(backdropString.Handle); + var backDropParameterAsSource = backDropParameter.QueryInterface(); + handle = backdropString.Handle; + return backDropParameterAsSource; + } } diff --git a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs index a3918d9ae6..5f2e752af8 100644 --- a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs +++ b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs @@ -4,7 +4,8 @@ { None, Acrylic, - Mica + MicaLight, + MicaDark } internal interface IBlurHost diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs index 684e7ff7b5..8874902d4e 100644 --- a/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs +++ b/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Avalonia.Win32.WinRT @@ -16,7 +17,15 @@ namespace Avalonia.Win32.WinRT UInt32 = u; Type = PropertyType.UInt32; } - + + public WinRTPropertyValue(float[] uiColor) + { + Type = PropertyType.SingleArray; + _singleArray = uiColor; + } + + private readonly float[]? _singleArray; + public PropertyType Type { get; } public int IsNumericScalar { get; } public byte UInt8 { get; } @@ -62,7 +71,17 @@ namespace Avalonia.Win32.WinRT public unsafe ulong* GetUInt64Array(uint* __valueSize) => throw NotImplemented; - public unsafe float* GetSingleArray(uint* __valueSize) => throw NotImplemented; + public unsafe float* GetSingleArray(uint* __valueSize) + { + if (_singleArray == null) + throw NotImplemented; + *__valueSize = (uint)_singleArray.Length; + var allocCoTaskMem = Marshal.AllocCoTaskMem(_singleArray.Length * Unsafe.SizeOf()); + Marshal.Copy(_singleArray, 0, allocCoTaskMem, _singleArray.Length); + float* s = (float*)allocCoTaskMem; + + return s; + } public unsafe double* GetDoubleArray(uint* __valueSize) => throw NotImplemented; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 057cdb2db0..2b16bbe275 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -374,8 +374,10 @@ namespace Avalonia.Win32 SetTransparencyBlur(windowsVersion); else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); - else if (level == WindowTransparencyLevel.Mica) - SetTransparencyMica(windowsVersion); + else if (level == WindowTransparencyLevel.MicaLight) + SetTransparencyMicaLight(windowsVersion); + else if (level == WindowTransparencyLevel.MicaDark) + SetTransparencyMicaDark(windowsVersion); TransparencyLevel = level; return; @@ -418,7 +420,7 @@ namespace Avalonia.Win32 return windowsVersion >= WinUiCompositionShared.MinAcrylicVersion; // Mica is supported on Windows >= 10.0.22000. - if (level == WindowTransparencyLevel.Mica) + if (level == WindowTransparencyLevel.MicaLight || level == WindowTransparencyLevel.MicaDark) return windowsVersion >= WinUiCompositionShared.MinHostBackdropVersion; return false; @@ -467,14 +469,24 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Acrylic); } - private void SetTransparencyMica(Version windowsVersion) + private void SetTransparencyMicaLight(Version windowsVersion) { // Mica only supported with composition on Windows >= 10.0.22000. if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion) return; SetUseHostBackdropBrush(false); - _blurHost?.SetBlur(BlurEffect.Mica); + _blurHost?.SetBlur(BlurEffect.MicaLight); + } + + private void SetTransparencyMicaDark(Version windowsVersion) + { + // Mica only supported with composition on Windows >= 10.0.22000. + if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion) + return; + + SetUseHostBackdropBrush(false); + _blurHost?.SetBlur(BlurEffect.MicaDark); } private void SetAccentState(AccentState state) From 432dafac91dc4cb61ffd338f7d8afcf5d4ab6540 Mon Sep 17 00:00:00 2001 From: Lighto Date: Fri, 14 Jul 2023 18:38:35 +0300 Subject: [PATCH 308/391] Removed unused old property --- .../Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs index 5c7f490710..7a775afb3a 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs @@ -11,7 +11,6 @@ internal class WinUiCompositionShared : IDisposable public ICompositionBrush BlurBrush { get; } public ICompositionBrush? MicaBrushLight { get; } public ICompositionBrush? MicaBrushDark { get; } - public ICompositionBrush? MicaBrush { get; } public object SyncRoot { get; } = new(); public static readonly Version MinWinCompositionVersion = new(10, 0, 17134); From 217a3e0624d0b116f5d279ca026e926107a6b0a0 Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Fri, 14 Jul 2023 22:31:01 +0300 Subject: [PATCH 309/391] remove duplicates in InternalsVisibleTo --- src/Avalonia.Base/Avalonia.Base.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index ddc2b7effb..16eb09de65 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -43,21 +43,15 @@ - - - - - - From c126db5b554349893e45e43a0cf289a601dedc20 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 15 Jul 2023 18:29:40 +0600 Subject: [PATCH 310/391] Added AvaloniaFilePreview target --- packages/Avalonia/AvaloniaBuildTasks.targets | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 33f22f4d02..6af35ff0d2 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -144,4 +144,30 @@ + + + Build + + + + + http://127.0.0.1:6001 + $(OutputPath)/$(AssemblyName).dll + MainWindow.axaml + $(APreviewExecutable) + $([System.IO.Path]::ChangeExtension('$(APreviewExecutable)', '.deps.json')) + $([System.IO.Path]::ChangeExtension('$(APreviewExecutable)', '.runtimeconfig.json')) + $([System.IO.Path]::GetFullPath('$(APreviewFile)')) + + + + + + + + + + + + From 55c03069b1e9cf681cde74a476038118296c340f Mon Sep 17 00:00:00 2001 From: Lighto Date: Sat, 15 Jul 2023 22:18:59 +0300 Subject: [PATCH 311/391] Changed Mica dark/light based on theme --- samples/ControlCatalog/MainView.xaml | 3 +- .../WindowTransparencyLevel.cs | 9 ++--- .../Composition/WinUiCompositedWindow.cs | 3 -- src/Windows/Avalonia.Win32/WindowImpl.cs | 33 +++++++++---------- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 0a1621d02a..7ed2d67379 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -226,8 +226,7 @@ Transparent Blur AcrylicBlur - MicaLight - MicaDark + Mica - /// The window background is based on desktop wallpaper tint with a light blur. This will only work on Windows 11 + /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 /// - public static WindowTransparencyLevel MicaLight { get; } = new(nameof(MicaLight)); + public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica)); - /// - /// The window background is based on desktop wallpaper tint with a dark blur. This will only work on Windows 11 - /// - public static WindowTransparencyLevel MicaDark { get; } = new(nameof(MicaDark)); - public override string ToString() { return _value; diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs index d87c8d1361..d720e525d3 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs @@ -76,9 +76,6 @@ internal class WinUiCompositedWindow : IDisposable using var compositionBrush = _surfaceBrush.QueryInterface(); spriteVisual.SetBrush(compositionBrush); _target.SetRoot(containerVisual); - - - } public void SetSurface(ICompositionSurface surface) => _surfaceBrush.SetSurface(surface); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2b16bbe275..044c2cad67 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -106,6 +106,7 @@ namespace Avalonia.Win32 private static POINTER_PEN_INFO[]? s_historyPenInfos; private static POINTER_INFO[]? s_historyInfos; private static MOUSEMOVEPOINT[]? s_mouseHistoryInfos; + private PlatformThemeVariant _currentThemeVariant; public WindowImpl() { @@ -374,10 +375,8 @@ namespace Avalonia.Win32 SetTransparencyBlur(windowsVersion); else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); - else if (level == WindowTransparencyLevel.MicaLight) - SetTransparencyMicaLight(windowsVersion); - else if (level == WindowTransparencyLevel.MicaDark) - SetTransparencyMicaDark(windowsVersion); + else if (level == WindowTransparencyLevel.Mica) + SetTransparencyMica(windowsVersion); TransparencyLevel = level; return; @@ -420,7 +419,7 @@ namespace Avalonia.Win32 return windowsVersion >= WinUiCompositionShared.MinAcrylicVersion; // Mica is supported on Windows >= 10.0.22000. - if (level == WindowTransparencyLevel.MicaLight || level == WindowTransparencyLevel.MicaDark) + if (level == WindowTransparencyLevel.Mica) return windowsVersion >= WinUiCompositionShared.MinHostBackdropVersion; return false; @@ -469,24 +468,19 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Acrylic); } - private void SetTransparencyMicaLight(Version windowsVersion) + private void SetTransparencyMica(Version windowsVersion) { // Mica only supported with composition on Windows >= 10.0.22000. if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion) return; SetUseHostBackdropBrush(false); - _blurHost?.SetBlur(BlurEffect.MicaLight); - } - - private void SetTransparencyMicaDark(Version windowsVersion) - { - // Mica only supported with composition on Windows >= 10.0.22000. - if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion) - return; - - SetUseHostBackdropBrush(false); - _blurHost?.SetBlur(BlurEffect.MicaDark); + _blurHost?.SetBlur(_currentThemeVariant switch + { + PlatformThemeVariant.Light => BlurEffect.MicaLight, + PlatformThemeVariant.Dark => BlurEffect.MicaDark, + _ => throw new ArgumentOutOfRangeException() + }); } private void SetAccentState(AccentState state) @@ -790,6 +784,7 @@ namespace Avalonia.Win32 public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { + _currentThemeVariant = themeVariant; if (Win32Platform.WindowsVersion.Build >= 22000) { var pvUseBackdropBrush = themeVariant == PlatformThemeVariant.Dark ? 1 : 0; @@ -798,6 +793,10 @@ namespace Avalonia.Win32 (int)DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, &pvUseBackdropBrush, sizeof(int)); + if (TransparencyLevel == WindowTransparencyLevel.Mica) + { + SetTransparencyMica(Win32Platform.WindowsVersion); + } } } From 7a0e0c41a40baf60cce214ee6756f95ca75fd7a8 Mon Sep 17 00:00:00 2001 From: Lighto Date: Sat, 15 Jul 2023 22:20:50 +0300 Subject: [PATCH 312/391] Removed unnecessary spaces --- src/Avalonia.Controls/WindowTransparencyLevel.cs | 2 +- .../WinRT/Composition/WinUiCompositionUtils.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/WindowTransparencyLevel.cs b/src/Avalonia.Controls/WindowTransparencyLevel.cs index 9604a7ef5c..bce6e21e68 100644 --- a/src/Avalonia.Controls/WindowTransparencyLevel.cs +++ b/src/Avalonia.Controls/WindowTransparencyLevel.cs @@ -36,7 +36,7 @@ public readonly record struct WindowTransparencyLevel /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 /// public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica)); - + public override string ToString() { return _value; diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs index 1a130698ee..29c1e1fe27 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs @@ -33,10 +33,7 @@ internal static class WinUiCompositionUtils using var luminosityOpacityEffectBrushEffect = luminosityOpacityEffectFactory.CreateBrush(); using var luminosityOpacityEffectBrush = luminosityOpacityEffectBrushEffect.QueryInterface(); - - - // using var backDropParameterAsSource = GetParameterSource("BlurredWallpaperBackdrop", backDropParameterFactory, out var backdropHandle); - // using var backdropCompositionBrsuh = backDropParameterAsSource.QueryInterface(); + using var compositorWithBlurredWallpaperBackdropBrush = compositor.QueryInterface(); using var blurredWallpaperBackdropBrush = From 746a027bd80e2f1b8f837abad11a851c17125077 Mon Sep 17 00:00:00 2001 From: Sorien Date: Sat, 15 Jul 2023 22:19:12 +0200 Subject: [PATCH 313/391] Fix typo --- src/Avalonia.Native/NativePlatformSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/NativePlatformSettings.cs b/src/Avalonia.Native/NativePlatformSettings.cs index 53d3da0378..42d05d831c 100644 --- a/src/Avalonia.Native/NativePlatformSettings.cs +++ b/src/Avalonia.Native/NativePlatformSettings.cs @@ -23,7 +23,7 @@ internal class NativePlatformSettings : DefaultPlatformSettings AvnPlatformThemeVariant.Dark => (PlatformThemeVariant.Dark, ColorContrastPreference.NoPreference), AvnPlatformThemeVariant.Light => (PlatformThemeVariant.Light, ColorContrastPreference.NoPreference), AvnPlatformThemeVariant.HighContrastDark => (PlatformThemeVariant.Dark, ColorContrastPreference.High), - AvnPlatformThemeVariant.HighContrastLight => (PlatformThemeVariant.Dark, ColorContrastPreference.High), + AvnPlatformThemeVariant.HighContrastLight => (PlatformThemeVariant.Light, ColorContrastPreference.High), _ => throw new ArgumentOutOfRangeException() }; var color = _platformSettings.AccentColor; From cbfa327aee6564a0a42ead2c68c15d33cb47fa46 Mon Sep 17 00:00:00 2001 From: Zachary Weldon Date: Sat, 15 Jul 2023 20:22:54 -0700 Subject: [PATCH 314/391] added clear function to combobox --- src/Avalonia.Controls/ComboBox.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index fb27b3da22..bd05d4947c 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -517,5 +517,14 @@ namespace Avalonia.Controls } } } + + /// + /// Clears the selection + /// + public void Clear() + { + SelectedItem = null; + SelectedIndex = -1; + } } } From 6eff41172fc88b0472fd3c9b04dd6c6b4eb19548 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sun, 16 Jul 2023 00:21:01 +0200 Subject: [PATCH 315/391] Generate symbol packages --- build/SourceLink.props | 8 ++------ nukebuild/Numerge | 2 +- nukebuild/_build.csproj | 12 +++++------- .../Avalonia.Analyzers/Avalonia.Analyzers.csproj | 1 + .../Avalonia.Generators/Avalonia.Generators.csproj | 1 + 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/build/SourceLink.props b/build/SourceLink.props index dd7ecc8d2a..b0f1f2c2cc 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -1,9 +1,9 @@ true - false + true + snupkg true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb @@ -14,10 +14,6 @@ true - - embedded - - diff --git a/nukebuild/Numerge b/nukebuild/Numerge index aef10ae67d..7908ee8e86 160000 --- a/nukebuild/Numerge +++ b/nukebuild/Numerge @@ -1 +1 @@ -Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5 +Subproject commit 7908ee8e8619180d04a4de3b0210c009db400e78 diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 43453833d7..64eccbbb3f 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -8,8 +8,8 @@ 1 net7.0 - - + + @@ -32,9 +32,9 @@ - - - + + + @@ -43,7 +43,5 @@ dirs.proj - - diff --git a/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj b/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj index c27801db61..05d72618a9 100644 --- a/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj +++ b/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj @@ -5,6 +5,7 @@ Avalonia.Analyzers true true + false true diff --git a/src/tools/Avalonia.Generators/Avalonia.Generators.csproj b/src/tools/Avalonia.Generators/Avalonia.Generators.csproj index cd1e2fcaba..c6992f9438 100644 --- a/src/tools/Avalonia.Generators/Avalonia.Generators.csproj +++ b/src/tools/Avalonia.Generators/Avalonia.Generators.csproj @@ -5,6 +5,7 @@ Avalonia.Generators $(DefineConstants);XAMLX_INTERNAL true + false true true From 12e1d5ef104bb46731511e5ed3245517be822537 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sun, 16 Jul 2023 01:58:50 +0200 Subject: [PATCH 316/391] Avoid api validation for symbol packages --- nukebuild/ApiDiffValidation.cs | 5 +++-- nukebuild/Build.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nukebuild/ApiDiffValidation.cs b/nukebuild/ApiDiffValidation.cs index bd576250bd..483bfd336a 100644 --- a/nukebuild/ApiDiffValidation.cs +++ b/nukebuild/ApiDiffValidation.cs @@ -38,7 +38,8 @@ public static class ApiDiffValidation var left = new List(); var right = new List(); - var suppressionFile = Path.Combine(suppressionFilesFolder, GetPackageId(packagePath) + ".nupkg.xml"); + var packageId = GetPackageId(packagePath); + var suppressionFile = Path.Combine(suppressionFilesFolder, packageId + ".nupkg.xml"); // Don't use Path.Combine with these left and right tool parameters. // Microsoft.DotNet.ApiCompat.Tool is stupid and treats '/' and '\' as different assemblies in suppression files. @@ -57,7 +58,7 @@ public static class ApiDiffValidation e.target == baselineDll.target && e.entry.Name == baselineDll.entry.Name); if (targetDll.entry is null) { - throw new InvalidOperationException($"Some assemblies are missing in the new package: {baselineDll.entry.Name} for {baselineDll.target}"); + throw new InvalidOperationException($"Some assemblies are missing in the new package {packageId}: {baselineDll.entry.Name} for {baselineDll.target}"); } var targetDllPath = $"target/{targetDll.target}/{targetDll.entry.Name}"; diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index b82c446249..bbfc28aa9f 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -288,7 +288,7 @@ partial class Build : NukeBuild .Executes(async () => { await Task.WhenAll( - Directory.GetFiles(Parameters.NugetRoot).Select(nugetPackage => ApiDiffValidation.ValidatePackage( + Directory.GetFiles(Parameters.NugetRoot, "*.nupkg").Select(nugetPackage => ApiDiffValidation.ValidatePackage( ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline, Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression))); }); From 97d4e7d9a1a57086173bc279996348aceff597bf Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sun, 16 Jul 2023 11:38:59 +0200 Subject: [PATCH 317/391] Update Numerge --- nukebuild/Numerge | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nukebuild/Numerge b/nukebuild/Numerge index 7908ee8e86..9738c6121f 160000 --- a/nukebuild/Numerge +++ b/nukebuild/Numerge @@ -1 +1 @@ -Subproject commit 7908ee8e8619180d04a4de3b0210c009db400e78 +Subproject commit 9738c6121fdd143c78d3e25686a7e4e13c00f586 From 6a48527a52df21ea1a6f014e55952bddaa54c097 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 16 Jul 2023 23:16:34 +0200 Subject: [PATCH 318/391] Remove unnecessary cast. --- src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index ff8ff69d5e..739f0ac251 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -33,8 +33,7 @@ namespace Avalonia.Win32.Automation return null; var p = WindowImpl.PointToClient(new PixelPoint((int)x, (int)y)); - var peer = (WindowBaseAutomationPeer)Peer; - var found = InvokeSync(() => peer.GetPeerFromPoint(p)); + var found = InvokeSync(() => Peer.GetPeerFromPoint(p)); var result = GetOrCreate(found) as IRawElementProviderFragment; return result; } From 7f4f01b11a2863d326a48e8ce8c42233a51b1edc Mon Sep 17 00:00:00 2001 From: John Doe Date: Mon, 17 Jul 2023 02:40:47 +0200 Subject: [PATCH 319/391] Modernized argument check syntax --- src/Avalonia.Base/Threading/Dispatcher.Invoke.cs | 4 ++-- src/Avalonia.Base/Threading/DispatcherTimer.cs | 12 ++++++------ .../Collections/DataGridCollectionView.cs | 4 ++-- .../Automation/Peers/ScrollViewerAutomationPeer.cs | 4 ++-- .../DateTimePickers/DateTimePickerPanel.cs | 4 ++-- .../ScrollViewerIRefreshInfoProviderAdapter.cs | 10 +++++----- .../Utils/VirtualizingSnapPointsList.cs | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs index 25be5779b9..5995a03758 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs @@ -98,7 +98,7 @@ public partial class Dispatcher if (timeout.TotalMilliseconds < 0 && timeout != TimeSpan.FromMilliseconds(-1)) { - throw new ArgumentOutOfRangeException("timeout"); + throw new ArgumentOutOfRangeException(nameof(timeout)); } // Fast-Path: if on the same thread, and invoking at Send priority, @@ -220,7 +220,7 @@ public partial class Dispatcher if (timeout.TotalMilliseconds < 0 && timeout != TimeSpan.FromMilliseconds(-1)) { - throw new ArgumentOutOfRangeException("timeout"); + throw new ArgumentOutOfRangeException(nameof(timeout)); } // Fast-Path: if on the same thread, and invoking at Send priority, diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 473b9eeefa..fbdeed4edc 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -112,11 +112,11 @@ public partial class DispatcherTimer bool updateOSTimer = false; if (value.TotalMilliseconds < 0) - throw new ArgumentOutOfRangeException("value", + throw new ArgumentOutOfRangeException(nameof(value), "TimeSpan period must be greater than or equal to zero."); if (value.TotalMilliseconds > Int32.MaxValue) - throw new ArgumentOutOfRangeException("value", + throw new ArgumentOutOfRangeException(nameof(value), "TimeSpan period must be less than or equal to Int32.MaxValue."); lock (_instanceLock) @@ -259,14 +259,14 @@ public partial class DispatcherTimer DispatcherPriority.Validate(priority, "priority"); if (priority == DispatcherPriority.Inactive) { - throw new ArgumentException("Specified priority is not valid.", "priority"); + throw new ArgumentException("Specified priority is not valid.", nameof(priority)); } if (interval.TotalMilliseconds < 0) - throw new ArgumentOutOfRangeException("interval", "TimeSpan period must be greater than or equal to zero."); + throw new ArgumentOutOfRangeException(nameof(interval), "TimeSpan period must be greater than or equal to zero."); if (interval.TotalMilliseconds > Int32.MaxValue) - throw new ArgumentOutOfRangeException("interval", + throw new ArgumentOutOfRangeException(nameof(interval), "TimeSpan period must be less than or equal to Int32.MaxValue."); @@ -349,4 +349,4 @@ public partial class DispatcherTimer // used by Dispatcher internal long DueTimeInMs { get; private set; } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs index 30517f4b00..0026c9e074 100644 --- a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs +++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs @@ -807,7 +807,7 @@ namespace Avalonia.Collections { if (value < 0) { - throw new ArgumentOutOfRangeException("PageSize cannot have a negative value."); + throw new ArgumentOutOfRangeException(nameof(value), "PageSize cannot have a negative value."); } // if the Refresh is currently deferred, cache the desired PageSize @@ -1954,7 +1954,7 @@ namespace Avalonia.Collections // for indices larger than the count if (index >= Count || index < 0) { - throw new ArgumentOutOfRangeException("index"); + throw new ArgumentOutOfRangeException(nameof(index)); } if (IsGrouping) diff --git a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs index 835ed1c4af..674eb9a241 100644 --- a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs @@ -146,12 +146,12 @@ namespace Avalonia.Automation.Peers if (scrollHorizontally && (horizontalPercent < 0.0) || (horizontalPercent > 100.0)) { - throw new ArgumentOutOfRangeException("horizontalPercent"); + throw new ArgumentOutOfRangeException(nameof(horizontalPercent)); } if (scrollVertically && (verticalPercent < 0.0) || (verticalPercent > 100.0)) { - throw new ArgumentOutOfRangeException("verticalPercent"); + throw new ArgumentOutOfRangeException(nameof(verticalPercent)); } var offset = Owner.Offset; diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index 69b0ffe9a6..5a5c3fdf1d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -165,7 +165,7 @@ namespace Avalonia.Controls.Primitives set { if (value > MaximumValue || value < MinimumValue) - throw new ArgumentOutOfRangeException("SelectedValue"); + throw new ArgumentOutOfRangeException(nameof(value)); var sel = CoerceSelected(value); _selectedValue = sel; @@ -195,7 +195,7 @@ namespace Avalonia.Controls.Primitives set { if (value <= 0 || value > _range) - throw new ArgumentOutOfRangeException("Increment"); + throw new ArgumentOutOfRangeException(nameof(value)); _increment = value; UpdateHelperInfo(); var sel = CoerceSelected(SelectedValue); diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs index 4c1e0c2565..7ff02711d6 100644 --- a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs +++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs @@ -98,14 +98,14 @@ namespace Avalonia.Controls.PullToRefresh if (_scrollViewer.Content == null) { - throw new ArgumentException(nameof(adaptee), "Adaptee's content property cannot be null."); + throw new ArgumentException("Adaptee's content property cannot be null.", nameof(adaptee)); } var content = adaptee.Content as Visual; if (content == null) { - throw new ArgumentException(nameof(adaptee), "Adaptee's content property must be a Visual"); + throw new ArgumentException("Adaptee's content property must be a Visual", nameof(adaptee)); } if (content.GetVisualParent() == null) @@ -118,7 +118,7 @@ namespace Avalonia.Controls.PullToRefresh if (content.Parent is not InputElement) { - throw new ArgumentException(nameof(adaptee), "Adaptee's content's parent must be a InputElement"); + throw new ArgumentException("Adaptee's content's parent must be a InputElement", nameof(adaptee)); } } @@ -194,12 +194,12 @@ namespace Avalonia.Controls.PullToRefresh var content = _scrollViewer?.Content as Visual; if (content == null) { - throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content property must be a Visual"); + throw new ArgumentException("Adaptee's content property must be a Visual", nameof(_scrollViewer)); } if (content.Parent is not InputElement parent) { - throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content parent must be an InputElement"); + throw new ArgumentException("Adaptee's content parent must be an InputElement", nameof(_scrollViewer)); } MakeInteractionSource(parent); diff --git a/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs index f92756c59b..eaf501b45f 100644 --- a/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs +++ b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs @@ -39,7 +39,7 @@ namespace Avalonia.Controls.Utils get { if(index < 0 || index >= Count) - throw new ArgumentOutOfRangeException("index"); + throw new ArgumentOutOfRangeException(nameof(index)); index += _start; From 15b42836b13f67623efc9d5b47db8a6b9f11bc1d Mon Sep 17 00:00:00 2001 From: John Doe Date: Mon, 17 Jul 2023 02:50:25 +0200 Subject: [PATCH 320/391] string.Format converted to interpolated string --- src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs | 4 ++-- src/Avalonia.Remote.Protocol/MetsysBson.cs | 8 ++++---- src/Avalonia.X11/X11Structs.cs | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index b5be12fa54..30f9d8f380 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -1113,11 +1113,11 @@ namespace Avalonia.Controls } if (value < Minimum) { - throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum)); + throw new ArgumentOutOfRangeException(nameof(value), $"Value must be greater than Minimum value of {Minimum}"); } else if (value > Maximum) { - throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be less than Maximum value of {0}", Maximum)); + throw new ArgumentOutOfRangeException(nameof(value), $"Value must be less than Maximum value of {Maximum}"); } } diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs index 8966dd4206..9c5f92b6ac 100644 --- a/src/Avalonia.Remote.Protocol/MetsysBson.cs +++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs @@ -752,7 +752,7 @@ namespace Metsys.Bson if (memberExpression.Expression.NodeType != ExpressionType.Parameter && memberExpression.Expression.NodeType != ExpressionType.Convert) { - throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member.", lambdaExpression), nameof(lambdaExpression)); + throw new ArgumentException($"Expression '{lambdaExpression}' must resolve to top-level member.", nameof(lambdaExpression)); } return memberExpression.Member.Name; default: @@ -942,7 +942,7 @@ namespace Metsys.Bson return new ListWrapper(); } } - throw new BsonException(string.Format("Collection of type {0} cannot be deserialized", type.FullName)); + throw new BsonException($"Collection of type {type.FullName} cannot be deserialized"); } public abstract void Add(object value); @@ -1514,7 +1514,7 @@ namespace Metsys.Bson.Configuration result = Visit((MemberExpression)expression.Left); } var index = Expression.Lambda(expression.Right).Compile().DynamicInvoke(); - return result + string.Format("[{0}]", index); + return result + $"[{index}]"; } private string Visit(MemberExpression expression) @@ -1540,7 +1540,7 @@ namespace Metsys.Bson.Configuration if (expression.Method.Name == "get_Item" && expression.Arguments.Count == 1) { var index = Expression.Lambda(expression.Arguments[0]).Compile().DynamicInvoke(); - name += string.Format("[{0}]", index); + name += $"[{index}]"; } return name; } diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs index 18f860a1a8..86ef7879a5 100644 --- a/src/Avalonia.X11/X11Structs.cs +++ b/src/Avalonia.X11/X11Structs.cs @@ -1109,7 +1109,7 @@ namespace Avalonia.X11 { public override string ToString () { - return string.Format("MotifWmHints Date: Mon, 17 Jul 2023 17:26:19 +0600 Subject: [PATCH 321/391] Special handling for macos dispatcher quirks --- .../Threading/Dispatcher.Queue.cs | 20 +++++++++++++------ .../Threading/Dispatcher.Timers.cs | 2 +- src/Avalonia.Base/Threading/Dispatcher.cs | 3 +++ .../Threading/DispatcherFrame.cs | 8 ++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.Queue.cs b/src/Avalonia.Base/Threading/Dispatcher.Queue.cs index 829ab4cf87..1644332aea 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Queue.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Queue.cs @@ -9,7 +9,9 @@ public partial class Dispatcher private readonly DispatcherPriorityQueue _queue = new(); private bool _signaled; private bool _explicitBackgroundProcessingRequested; - private const int MaximumTimeProcessingBackgroundJobs = 50; + private const int MaximumInputStarvationTimeInFallbackMode = 50; + private const int MaximumInputStarvationTimeInExplicitProcessingExplicitMode = 50; + private int _maximumInputStarvationTime; void RequestBackgroundProcessing() { @@ -35,8 +37,8 @@ public partial class Dispatcher lock (InstanceLock) { _explicitBackgroundProcessingRequested = false; - ExecuteJobsCore(); } + ExecuteJobsCore(true); } /// @@ -130,10 +132,10 @@ public partial class Dispatcher lock (InstanceLock) _signaled = false; - ExecuteJobsCore(); + ExecuteJobsCore(false); } - void ExecuteJobsCore() + void ExecuteJobsCore(bool fromExplicitBackgroundProcessingCallback) { long? backgroundJobExecutionStartedAt = null; while (true) @@ -151,7 +153,6 @@ public partial class Dispatcher if (job.Priority > DispatcherPriority.Input) { ExecuteJob(job); - backgroundJobExecutionStartedAt = null; } // If platform supports pending input query, ask the platform if we can continue running low priority jobs else if (_pendingInputImpl?.CanQueryPendingInput == true) @@ -164,6 +165,13 @@ public partial class Dispatcher return; } } + // We can't ask if the implementation has pending input, so we should let it to call us back + // Once it thinks that input is handled + else if (_backgroundProcessingImpl != null && !fromExplicitBackgroundProcessingCallback) + { + RequestBackgroundProcessing(); + return; + } // We can't check if there is pending input, but still need to enforce interactivity // so we stop processing background jobs after some timeout and start a timer to continue later else @@ -171,7 +179,7 @@ public partial class Dispatcher if (backgroundJobExecutionStartedAt == null) backgroundJobExecutionStartedAt = Now; - if (Now - backgroundJobExecutionStartedAt.Value > MaximumTimeProcessingBackgroundJobs) + if (Now - backgroundJobExecutionStartedAt.Value > _maximumInputStarvationTime) { _signaled = true; RequestBackgroundProcessing(); diff --git a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs index bb252b7f55..51408daad1 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs @@ -127,7 +127,7 @@ public partial class Dispatcher if (needToPromoteTimers) PromoteTimers(); if (needToProcessQueue) - ExecuteJobsCore(); + ExecuteJobsCore(false); UpdateOSTimer(); } diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index f257072dc8..46c6699b5a 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -34,6 +34,9 @@ public partial class Dispatcher : IDispatcher _controlledImpl = _impl as IControlledDispatcherImpl; _pendingInputImpl = _impl as IDispatcherImplWithPendingInput; _backgroundProcessingImpl = _impl as IDispatcherImplWithExplicitBackgroundProcessing; + _maximumInputStarvationTime = _backgroundProcessingImpl == null ? + MaximumInputStarvationTimeInFallbackMode : + MaximumInputStarvationTimeInExplicitProcessingExplicitMode; if (_backgroundProcessingImpl != null) _backgroundProcessingImpl.ReadyForBackgroundProcessing += OnReadyForExplicitBackgroundProcessing; } diff --git a/src/Avalonia.Base/Threading/DispatcherFrame.cs b/src/Avalonia.Base/Threading/DispatcherFrame.cs index e826432475..1a80740420 100644 --- a/src/Avalonia.Base/Threading/DispatcherFrame.cs +++ b/src/Avalonia.Base/Threading/DispatcherFrame.cs @@ -38,10 +38,14 @@ public class DispatcherFrame /// for their important criteria to be met. These frames /// should have a timeout associated with them. /// - public DispatcherFrame(bool exitWhenRequested) + public DispatcherFrame(bool exitWhenRequested) : this(Dispatcher.UIThread, exitWhenRequested) { - Dispatcher = Dispatcher.UIThread; Dispatcher.VerifyAccess(); + } + + internal DispatcherFrame(Dispatcher dispatcher, bool exitWhenRequested) + { + Dispatcher = dispatcher; _exitWhenRequested = exitWhenRequested; _continue = true; } From 368de7e79f5bb8d5870b4aea530633a8534a70d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 17 Jul 2023 13:18:24 +0000 Subject: [PATCH 322/391] clean up; apply only changed text to editable --- .../Avalonia.Android/AndroidInputMethod.cs | 32 +++++++++++++++++-- .../Platform/SkiaPlatform/TopLevelImpl.cs | 16 ++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index ea1958efa6..dea029aaf2 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -114,16 +114,42 @@ namespace Avalonia.Android private void _client_SurroundingTextChanged(object sender, EventArgs e) { var surroundingText = _client.SurroundingText ?? ""; + var editableText = _inputConnection.EditableWrapper.ToString(); - _inputConnection.EditableWrapper.IgnoreChange = true; + if (editableText != surroundingText) + { + _inputConnection.EditableWrapper.IgnoreChange = true; + + var diff = GetDiff(); - _inputConnection.Editable.Replace(0, _inputConnection.Editable.Length(), surroundingText); + _inputConnection.Editable.Replace(diff.index, editableText.Length, diff.diff); - _inputConnection.EditableWrapper.IgnoreChange = false; + _inputConnection.EditableWrapper.IgnoreChange = false; + } var selection = Client.Selection; _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); + + (int index, string diff) GetDiff() + { + int index = 0; + + var longerLength = Math.Max(surroundingText.Length, editableText.Length); + + for(int i = 0; i < longerLength; i++) + { + if (surroundingText.Length == i || editableText.Length == i || surroundingText[i] != editableText[i]) + { + index = i; + break; + } + } + + var diffString = surroundingText.Substring(index, surroundingText.Length - index); + + return (index, diffString); + } } public void SetCursorRect(Rect rect) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 5d82d099c9..5492742a4c 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -447,6 +447,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform { private readonly AvaloniaInputConnection _inputConnection; + public event EventHandler SelectionChanged; + public EditableWrapper(AvaloniaInputConnection inputConnection) { _inputConnection = inputConnection; @@ -458,8 +460,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { if (!IgnoreChange && start != end) { - var text = tb.SubSequence(0, tb.Length()); - SelectSurroundingTextForDeletion(start, end); } @@ -470,8 +470,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { if (!IgnoreChange && start != end) { - var text = tb.SubSequence(tbstart, tbend); - SelectSurroundingTextForDeletion(start, end); } @@ -482,6 +480,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); } + + public override void SetSpan(Java.Lang.Object what, int start, int end, [GeneratedEnum] SpanTypes flags) + { + base.SetSpan(what, start, end, flags); + } } internal class AvaloniaInputConnection : BaseInputConnection @@ -548,11 +551,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText)) { - if(_composingRegion != null) - { - _inputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End); - } - _toplevel.TextInput(committedText); _composingRegion = null; From 117ea16f0ba8d0099cd96875336f82a9e1082030 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 17 Jul 2023 17:24:19 +0000 Subject: [PATCH 323/391] remove redundant selection update --- src/Android/Avalonia.Android/AndroidInputMethod.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index dea029aaf2..1404adbae0 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -127,10 +127,6 @@ namespace Avalonia.Android _inputConnection.EditableWrapper.IgnoreChange = false; } - var selection = Client.Selection; - - _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); - (int index, string diff) GetDiff() { int index = 0; From 250743d786147434b36c1f584d7741964f3e4975 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 18 Jul 2023 09:40:03 +0200 Subject: [PATCH 324/391] Rework GetNext/PreviousCharacterHit --- src/Avalonia.Base/Media/GlyphRun.cs | 6 +- .../Media/TextFormatting/TextLineImpl.cs | 321 ++++++------------ .../Media/GlyphRunTests.cs | 2 +- .../Media/TextFormatting/TextLineTests.cs | 116 ++++++- 4 files changed, 222 insertions(+), 223 deletions(-) diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 0f70386424..fcb2cec733 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -424,13 +424,13 @@ namespace Avalonia.Media /// public CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit) { + var previousCharacterHit = FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); + if (characterHit.TrailingLength != 0) { - return new CharacterHit(characterHit.FirstCharacterIndex); + return previousCharacterHit; } - var previousCharacterHit = FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); - return new CharacterHit(previousCharacterHit.FirstCharacterIndex); } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index ca31d9a6d0..44f53420de 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -9,7 +9,7 @@ namespace Avalonia.Media.TextFormatting internal static Comparer TextBoundsComparer { get; } = Comparer.Create((x, y) => x.Rectangle.Left.CompareTo(y.Rectangle.Left)); - private IReadOnlyList? _indexedTextRuns; + internal IReadOnlyList? _indexedTextRuns; private readonly TextRun[] _textRuns; private readonly double _paragraphWidth; private readonly TextParagraphProperties _paragraphProperties; @@ -512,38 +512,45 @@ namespace Avalonia.Media.TextFormatting /// public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit) { - if (_textRuns.Length == 0) + if (_textRuns.Length == 0 || _indexedTextRuns is null) { return new CharacterHit(); } - if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit)) - { - return nextCharacterHit; - } - - var lastTextPosition = FirstTextSourceIndex + Length; + var currentCharacterrHit = characterHit; + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - // Can't move, we're after the last character - var runIndex = GetRunIndexAtCharacterIndex(lastTextPosition, LogicalDirection.Forward, out var currentPosition); + var currentRun = GetRunAtCharacterIndex(characterIndex, LogicalDirection.Forward, out var currentPosition); - var currentRun = _textRuns[runIndex]; + var nextCharacterHit = characterHit; switch (currentRun) { case ShapedTextRun shapedRun: { - nextCharacterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(characterHit); + var offset = Math.Max(0, currentPosition - shapedRun.GlyphRun.Metrics.FirstCluster - characterHit.TrailingLength); + + if (offset > 0) + { + currentCharacterrHit = new CharacterHit(Math.Max(0, characterHit.FirstCharacterIndex - offset), characterHit.TrailingLength); + } + + nextCharacterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(currentCharacterrHit); + + if (offset > 0) + { + nextCharacterHit = new CharacterHit(nextCharacterHit.FirstCharacterIndex + offset, nextCharacterHit.TrailingLength); + } break; } - default: + case TextRun: { nextCharacterHit = new CharacterHit(currentPosition + currentRun.Length); break; } } - if (characterHit.FirstCharacterIndex + characterHit.TrailingLength == nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength) + if (characterIndex == nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength) { return characterHit; } @@ -554,17 +561,75 @@ namespace Avalonia.Media.TextFormatting /// public override CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit) { - if (TryFindPreviousCharacterHit(characterHit, out var previousCharacterHit)) + if (_textRuns.Length == 0 || _indexedTextRuns is null) + { + return new CharacterHit(); + } + + if (characterHit.TrailingLength > 0 && characterHit.FirstCharacterIndex <= FirstTextSourceIndex) + { + return new CharacterHit(FirstTextSourceIndex); + } + + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + + if (characterIndex <= FirstTextSourceIndex) { - return previousCharacterHit; + return new CharacterHit(FirstTextSourceIndex); + } + + var currentCharacterrHit = characterHit; + + var currentRun = GetRunAtCharacterIndex(characterIndex, LogicalDirection.Backward, out var currentPosition); + + if (currentPosition == characterHit.FirstCharacterIndex) + { + currentRun = GetRunAtCharacterIndex(characterHit.FirstCharacterIndex, LogicalDirection.Backward, out currentPosition); + } + + var previousCharacterHit = characterHit; + + switch (currentRun) + { + case ShapedTextRun shapedRun: + { + var offset = Math.Max(0, currentPosition - shapedRun.GlyphRun.Metrics.FirstCluster); + + if (offset > 0) + { + currentCharacterrHit = new CharacterHit(Math.Max(0, characterHit.FirstCharacterIndex - offset), characterHit.TrailingLength); + } + + previousCharacterHit = shapedRun.GlyphRun.GetPreviousCaretCharacterHit(currentCharacterrHit); + + if (offset > 0) + { + previousCharacterHit = new CharacterHit(previousCharacterHit.FirstCharacterIndex + offset, previousCharacterHit.TrailingLength); + } + break; + } + case TextRun: + { + if (characterHit.TrailingLength > 0) + { + previousCharacterHit = new CharacterHit(currentPosition, currentRun.Length); + + } + else + { + previousCharacterHit = new CharacterHit(currentPosition + currentRun.Length); + } + + break; + } } - if (characterHit.FirstCharacterIndex <= FirstTextSourceIndex) + if (characterIndex == previousCharacterHit.FirstCharacterIndex + previousCharacterHit.TrailingLength) { - characterHit = new CharacterHit(FirstTextSourceIndex); + return characterHit; } - return characterHit; // Can't move, we're before the first character + return previousCharacterHit; } /// @@ -1009,161 +1074,7 @@ namespace Avalonia.Media.TextFormatting if (_textLineBreak is null && _textRuns.Length > 1 && _textRuns[_textRuns.Length - 1] is TextEndOfLine textEndOfLine) { _textLineBreak = new TextLineBreak(textEndOfLine); - } - } - - /// - /// Tries to find the next character hit. - /// - /// The current character hit. - /// The next character hit. - /// - private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit) - { - nextCharacterHit = characterHit; - - var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - var lastCodepointIndex = FirstTextSourceIndex + Length; - - if (codepointIndex >= lastCodepointIndex) - { - return false; // Cannot go forward anymore - } - - if (codepointIndex < FirstTextSourceIndex) - { - codepointIndex = FirstTextSourceIndex; - } - - var runIndex = GetRunIndexAtCharacterIndex(codepointIndex, LogicalDirection.Forward, out var currentPosition); - - while (runIndex < _textRuns.Length) - { - var currentRun = _textRuns[runIndex]; - - switch (currentRun) - { - case ShapedTextRun shapedRun: - { - var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); - - var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == FirstTextSourceIndex + Length; - - if (isAtEnd && !shapedRun.GlyphRun.IsLeftToRight) - { - nextCharacterHit = foundCharacterHit; - - return true; - } - - nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ? - foundCharacterHit : - new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength); - - if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex) - { - return true; - } - - break; - } - default: - { - var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - - if (textPosition == currentPosition) - { - nextCharacterHit = new CharacterHit(currentPosition + currentRun.Length); - - return true; - } - - break; - } - } - - currentPosition += currentRun.Length; - runIndex++; - } - - return false; - } - - /// - /// Tries to find the previous character hit. - /// - /// The current character hit. - /// The previous character hit. - /// - private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit) - { - var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - - if (characterIndex == FirstTextSourceIndex) - { - previousCharacterHit = new CharacterHit(FirstTextSourceIndex); - - return true; - } - - previousCharacterHit = characterHit; - - if (characterIndex < FirstTextSourceIndex) - { - return false; // Cannot go backward anymore. - } - - var runIndex = GetRunIndexAtCharacterIndex(characterIndex, LogicalDirection.Backward, out var currentPosition); - - while (runIndex >= 0) - { - var currentRun = _textRuns[runIndex]; - - switch (currentRun) - { - case ShapedTextRun shapedRun: - { - var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); - - if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex) - { - previousCharacterHit = foundCharacterHit; - - return true; - } - - var previousPosition = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength; - - if (foundCharacterHit.TrailingLength > 0 && previousPosition == characterIndex) - { - previousCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex); - } - - if (previousCharacterHit != characterHit) - { - return true; - } - - break; - } - default: - { - if (characterIndex == currentPosition + currentRun.Length) - { - previousCharacterHit = new CharacterHit(currentPosition); - - return true; - } - - break; - } - } - - currentPosition -= currentRun.Length; - runIndex--; } - - return false; } /// @@ -1173,15 +1084,23 @@ namespace Avalonia.Media.TextFormatting /// The logical direction. /// The text position of the found run index. /// The text run index. - private int GetRunIndexAtCharacterIndex(int codepointIndex, LogicalDirection direction, out int textPosition) + private TextRun? GetRunAtCharacterIndex(int codepointIndex, LogicalDirection direction, out int textPosition) { var runIndex = 0; textPosition = FirstTextSourceIndex; + + if (_indexedTextRuns is null) + { + return null; + } + + TextRun? currentRun = null; TextRun? previousRun = null; - while (runIndex < _textRuns.Length) + while (runIndex < _indexedTextRuns.Count) { - var currentRun = _textRuns[runIndex]; + var indexedRun = _indexedTextRuns[runIndex]; + currentRun = indexedRun.TextRun; switch (currentRun) { @@ -1189,64 +1108,49 @@ namespace Avalonia.Media.TextFormatting { var firstCluster = shapedRun.GlyphRun.Metrics.FirstCluster; - if (firstCluster > codepointIndex) - { - break; - } - - if (previousRun is ShapedTextRun previousShaped && !previousShaped.ShapedBuffer.IsLeftToRight) - { - if (shapedRun.ShapedBuffer.IsLeftToRight) - { - if (firstCluster >= codepointIndex) - { - return --runIndex; - } - } - else - { - if (codepointIndex > firstCluster + currentRun.Length) - { - return --runIndex; - } - } - } + firstCluster += Math.Max(0, indexedRun.TextSourceCharacterIndex - firstCluster); if (direction == LogicalDirection.Forward) { - if (codepointIndex >= firstCluster && codepointIndex <= firstCluster + currentRun.Length) + if (codepointIndex >= firstCluster && codepointIndex < firstCluster + currentRun.Length) { - return runIndex; + return currentRun; } } else { - if (codepointIndex > firstCluster && - codepointIndex <= firstCluster + currentRun.Length) + if (previousRun is not null && previousRun is not ShapedTextRun && codepointIndex == textPosition + firstCluster) + { + textPosition -= previousRun.Length; + + return previousRun; + } + + if (codepointIndex > firstCluster && codepointIndex <= firstCluster + currentRun.Length) { - return runIndex; + return currentRun; } } if (runIndex + 1 >= _textRuns.Length) { - return runIndex; + return currentRun; } textPosition += currentRun.Length; break; } - default: + case TextRun: { if (codepointIndex == textPosition) { - return runIndex; + return currentRun; } if (runIndex + 1 >= _textRuns.Length) { - return runIndex; + return currentRun; } textPosition += currentRun.Length; @@ -1257,10 +1161,11 @@ namespace Avalonia.Media.TextFormatting } runIndex++; + previousRun = currentRun; } - return runIndex; + return currentRun; } private TextLineMetrics CreateLineMetrics() diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index c273cc6489..69d7fc4916 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -111,7 +111,7 @@ namespace Avalonia.Base.UnitTests.Media using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel)) { - var characterHit = glyphRun.GetPreviousCaretCharacterHit(new CharacterHit(currentIndex, currentLength)); + var characterHit = glyphRun.GetPreviousCaretCharacterHit(new CharacterHit(currentIndex + currentLength)); Assert.Equal(previousIndex, characterHit.FirstCharacterIndex); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 12427e1f9e..d576a64523 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -194,7 +194,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting for (var i = 0; i < clusters.Count; i++) { var expectedCluster = clusters[i]; - var actualCluster = nextCharacterHit.FirstCharacterIndex; + var actualCluster = nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength; Assert.Equal(expectedCluster, actualCluster); @@ -278,16 +278,6 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(clusters[i], previousCharacterHit.FirstCharacterIndex + previousCharacterHit.TrailingLength); } - - firstCharacterHit = previousCharacterHit; - - firstCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit); - - previousCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit); - - Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex); - - Assert.Equal(0, previousCharacterHit.TrailingLength); } } @@ -728,6 +718,110 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_GetNextCaretCharacterHit_From_Mixed_TextBuffer() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new MixedTextBufferTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(9, 1)); + + Assert.Equal(10, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetNextCaretCharacterHit(characterHit); + + Assert.Equal(11, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(19, 1)); + + Assert.Equal(20, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(10)); + + Assert.Equal(11, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + + characterHit = textLine.GetNextCaretCharacterHit(characterHit); + + Assert.Equal(12, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + + characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(20)); + + Assert.Equal(21, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + } + } + + [Fact] + public void Should_GetPreviousCaretCharacterHit_From_Mixed_TextBuffer() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new MixedTextBufferTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(20, 1)); + + Assert.Equal(19, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(10, 1)); + + Assert.Equal(9, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetPreviousCaretCharacterHit(characterHit); + + Assert.Equal(8, characterHit.FirstCharacterIndex); + + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(21)); + + Assert.Equal(20, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + + characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(11)); + + Assert.Equal(10, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + + characterHit = textLine.GetPreviousCaretCharacterHit(characterHit); + + Assert.Equal(9, characterHit.FirstCharacterIndex); + + Assert.Equal(0, characterHit.TrailingLength); + } + } + private class MixedTextBufferTextSource : ITextSource { public TextRun? GetTextRun(int textSourceIndex) From 7c4ce4bca3ef66859f91d5b4c0dea9e5b72b5e6a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 18 Jul 2023 14:45:33 +0600 Subject: [PATCH 325/391] Make dispatcher more usable on non-ui threads --- .../Threading/AvaloniaSynchronizationContext.cs | 7 ++++--- src/Avalonia.Base/Threading/Dispatcher.Invoke.cs | 4 ++-- src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs | 2 +- src/Avalonia.Base/Threading/DispatcherOperation.cs | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index e0563876bf..1efaa01442 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -99,14 +99,15 @@ namespace Avalonia.Threading } } - public static RestoreContext Ensure(DispatcherPriority priority) + public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.UIThread, priority); + public static RestoreContext Ensure(Dispatcher dispatcher, DispatcherPriority priority) { if (Current is AvaloniaSynchronizationContext avaloniaContext && avaloniaContext.Priority == priority) return default; var oldContext = Current; - Dispatcher.UIThread.VerifyAccess(); - SetSynchronizationContext(Dispatcher.UIThread.GetContextWithPriority(priority)); + dispatcher.VerifyAccess(); + SetSynchronizationContext(dispatcher.GetContextWithPriority(priority)); return new RestoreContext(oldContext); } } diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs index 5995a03758..add990bd57 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs @@ -106,7 +106,7 @@ public partial class Dispatcher // call the callback directly. if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) { - using (AvaloniaSynchronizationContext.Ensure(priority)) + using (AvaloniaSynchronizationContext.Ensure(this, priority)) callback(); return; } @@ -228,7 +228,7 @@ public partial class Dispatcher // call the callback directly. if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) { - using (AvaloniaSynchronizationContext.Ensure(priority)) + using (AvaloniaSynchronizationContext.Ensure(this, priority)) return callback(); } diff --git a/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs b/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs index e1833fef2b..4b60ee7479 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs @@ -49,7 +49,7 @@ public partial class Dispatcher try { _frames.Push(frame); - using (AvaloniaSynchronizationContext.Ensure(DispatcherPriority.Normal)) + using (AvaloniaSynchronizationContext.Ensure(this, DispatcherPriority.Normal)) frame.Run(_controlledImpl); } finally diff --git a/src/Avalonia.Base/Threading/DispatcherOperation.cs b/src/Avalonia.Base/Threading/DispatcherOperation.cs index 8bd6d3bc01..0008d771c6 100644 --- a/src/Avalonia.Base/Threading/DispatcherOperation.cs +++ b/src/Avalonia.Base/Threading/DispatcherOperation.cs @@ -258,7 +258,7 @@ public class DispatcherOperation try { - using (AvaloniaSynchronizationContext.Ensure(Priority)) + using (AvaloniaSynchronizationContext.Ensure(Dispatcher, Priority)) InvokeCore(); } finally From 2dd92675e3920f666cf8f67fc908b013ddb07669 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 18 Jul 2023 19:27:19 +0200 Subject: [PATCH 326/391] macOS native: fix destructors accessing freed .NET objects --- native/Avalonia.Native/src/OSX/app.mm | 18 ++++++++++++++++++ native/Avalonia.Native/src/OSX/common.h | 1 + native/Avalonia.Native/src/OSX/main.mm | 7 +++++++ src/Avalonia.Native/AvaloniaNativePlatform.cs | 10 ++++++++-- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 9cc9fc9523..88cdf4d9de 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -2,6 +2,7 @@ #include "AvnString.h" @interface AvnAppDelegate : NSObject -(AvnAppDelegate* _Nonnull) initWithEvents: (IAvnApplicationEvents* _Nonnull) events; +-(void) releaseEvents; @end NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; @@ -15,6 +16,11 @@ ComPtr _events; return self; } +- (void)releaseEvents +{ + _events = nil; +} + - (void)applicationWillFinishLaunching:(NSNotification *)notification { if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy) @@ -105,6 +111,18 @@ extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDeleg } } +extern void ReleaseAvnAppEvents() +{ + NSApplication* app = [AvnApplication sharedApplication]; + id delegate = [app delegate]; + if ([delegate isMemberOfClass:[AvnAppDelegate class]]) + { + AvnAppDelegate* avnDelegate = delegate; + [avnDelegate releaseEvents]; + [app setDelegate:nil]; + } +} + HRESULT AvnApplicationCommands::HideApp() { START_COM_CALL; diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 5cf1b94a2f..672525c64a 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -38,6 +38,7 @@ extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDelegate); +extern void ReleaseAvnAppEvents(); extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSPoint ToNSPoint (AvnPoint p); extern NSRect ToNSRect (AvnRect r); diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 1c7e2cf25a..37fd0758e5 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -197,6 +197,13 @@ class AvaloniaNative : public ComSingleObject Date: Wed, 19 Jul 2023 08:04:22 +0000 Subject: [PATCH 327/391] android - use batch edits to defer updates to input manager --- .../Avalonia.Android/AndroidInputMethod.cs | 29 ++++++- .../Platform/SkiaPlatform/TopLevelImpl.cs | 85 +++++++++++-------- 2 files changed, 76 insertions(+), 38 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 1404adbae0..60f56805d1 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -18,6 +18,8 @@ namespace Avalonia.Android public bool IsActive { get; } public InputMethodManager IMM { get; } + + void OnBatchEditedEnded(); } enum CustomImeFlags @@ -103,6 +105,13 @@ namespace Avalonia.Android } private void _client_SelectionChanged(object sender, EventArgs e) + { + if (_inputConnection.IsInBatchEdit) + return; + OnSelectionChanged(); + } + + private void OnSelectionChanged() { var selection = Client.Selection; @@ -112,6 +121,22 @@ namespace Avalonia.Android } private void _client_SurroundingTextChanged(object sender, EventArgs e) + { + if (_inputConnection.IsInBatchEdit) + return; + OnSurroundingTextChanged(); + } + + public void OnBatchEditedEnded() + { + if (_inputConnection.IsInBatchEdit) + return; + + OnSurroundingTextChanged(); + OnSelectionChanged(); + } + + private void OnSurroundingTextChanged() { var surroundingText = _client.SurroundingText ?? ""; var editableText = _inputConnection.EditableWrapper.ToString(); @@ -133,7 +158,7 @@ namespace Avalonia.Android var longerLength = Math.Max(surroundingText.Length, editableText.Length); - for(int i = 0; i < longerLength; i++) + for (int i = 0; i < longerLength; i++) { if (surroundingText.Length == i || editableText.Length == i || surroundingText[i] != editableText[i]) { @@ -142,7 +167,7 @@ namespace Avalonia.Android } } - var diffString = surroundingText.Substring(index, surroundingText.Length - index); + var diffString = surroundingText.Substring(index, surroundingText.Length - index); return (index, diffString); } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 5492742a4c..26b8837f76 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.Versioning; +using System.Threading; using Android.App; using Android.Content; using Android.Graphics; @@ -454,6 +455,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform _inputConnection = inputConnection; } + public TextSelection CurrentSelection => new TextSelection(Selection.GetSelectionStart(this), Selection.GetSelectionEnd(this)); + public TextSelection CurrentComposition => new TextSelection(BaseInputConnection.GetComposingSpanStart(this), BaseInputConnection.GetComposingSpanEnd(this)); + public bool IgnoreChange { get; set; } public override IEditable Replace(int start, int end, ICharSequence tb) @@ -480,11 +484,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _inputConnection.InputMethod.Client.Selection = new TextSelection(start, end); } - - public override void SetSpan(Java.Lang.Object what, int start, int end, [GeneratedEnum] SpanTypes flags) - { - base.SetSpan(what, start, end, flags); - } } internal class AvaloniaInputConnection : BaseInputConnection @@ -493,8 +492,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly IAndroidInputMethod _inputMethod; private readonly EditableWrapper _editable; private bool _commitInProgress; - private (int Start, int End)? _composingRegion; - private TextSelection _selection; + private int _batchLevel = 0; public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) { @@ -513,36 +511,64 @@ namespace Avalonia.Android.Platform.SkiaPlatform public TopLevelImpl Toplevel => _toplevel; + public bool IsInBatchEdit => _batchLevel > 0; + public override bool SetComposingRegion(int start, int end) { - _composingRegion = new(start, end); - return base.SetComposingRegion(start, end); } public override bool SetComposingText(ICharSequence text, int newCursorPosition) { - if(_composingRegion != null) + BeginBatchEdit(); + _editable.IgnoreChange = true; + + try { - // Select the composing region. - InputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End); - } - var compositionText = text.SubSequence(0, text.Length()); + if (_editable.CurrentComposition.Start > -1) + { + // Select the composing region. + InputMethod.Client.Selection = new TextSelection(_editable.CurrentComposition.Start, _editable.CurrentComposition.End); + } + var compositionText = text.SubSequence(0, text.Length()); + + if (_inputMethod.IsActive && !_commitInProgress) + { + if (string.IsNullOrEmpty(compositionText)) + _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); - if (_inputMethod.IsActive && !_commitInProgress) + else + _toplevel.TextInput(compositionText); + } + base.SetComposingText(text, newCursorPosition); + } + finally { - if (string.IsNullOrEmpty(compositionText)) - _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); + _editable.IgnoreChange = false; - else - _toplevel.TextInput(compositionText); + EndBatchEdit(); } return true; } + public override bool BeginBatchEdit() + { + _batchLevel = Interlocked.Increment(ref _batchLevel); + return base.BeginBatchEdit(); + } + + public override bool EndBatchEdit() + { + _batchLevel = Interlocked.Decrement(ref _batchLevel); + + _inputMethod.OnBatchEditedEnded(); + return base.EndBatchEdit(); + } + public override bool CommitText(ICharSequence text, int newCursorPosition) { + BeginBatchEdit(); _commitInProgress = true; var ret = base.CommitText(text, newCursorPosition); @@ -551,22 +577,15 @@ namespace Avalonia.Android.Platform.SkiaPlatform if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText)) { - _toplevel.TextInput(committedText); - - _composingRegion = null; + _toplevel.TextInput(committedText); } _commitInProgress = false; + EndBatchEdit(); return ret; } - public override bool FinishComposingText() - { - _composingRegion = null; - return base.FinishComposingText(); - } - public override bool DeleteSurroundingText(int beforeLength, int afterLength) { if (InputMethod.IsActive) @@ -577,7 +596,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform if (InputMethod.IsActive) { - var selection = _selection; + var selection = _editable.CurrentSelection; InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.End + afterLength); @@ -589,12 +608,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform return result; } - public override bool SetSelection(int start, int end) - { - _selection = new TextSelection(start, end); - return base.SetSelection(start, end); - } - public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) { switch (actionCode) @@ -628,7 +641,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform return null; } - var selection = _selection; + var selection = _editable.CurrentSelection; ExtractedText extract = new ExtractedText { From c5cabca78f18170ea8bd1dfc5fa4891fb4e4c37b Mon Sep 17 00:00:00 2001 From: yankun Date: Wed, 19 Jul 2023 11:22:12 +0200 Subject: [PATCH 328/391] Update Rotate3DTransition.cs Fix flickering after transition has finished. This was originally contained in my previous PR, but was lost. --- src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs index b4a1f02f09..fb8e9488a7 100644 --- a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs @@ -89,6 +89,7 @@ public class Rotate3DTransition: PageSlide { Easing = SlideInEasing, Duration = Duration, + FillMode = FillMode.Forward, Children = { CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), From e16deecd6a2ee1b521e5528afa0257fa28cfa903 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 19 Jul 2023 12:05:49 +0200 Subject: [PATCH 329/391] Use ProcessExit to cleanup the native resources --- native/Avalonia.Native/src/OSX/main.mm | 3 ++- src/Avalonia.Native/AvaloniaNativePlatform.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 37fd0758e5..3fddb72529 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -198,7 +198,8 @@ class AvaloniaNative : public ComSingleObject Date: Wed, 19 Jul 2023 12:39:01 +0000 Subject: [PATCH 330/391] only handle deletion in surrounding text on the client side --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 26b8837f76..fa01cb83e3 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -583,7 +583,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _commitInProgress = false; EndBatchEdit(); - return ret; + return true; } public override bool DeleteSurroundingText(int beforeLength, int afterLength) @@ -592,7 +592,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { EditableWrapper.IgnoreChange = true; } - var result = base.DeleteSurroundingText(beforeLength, afterLength); if (InputMethod.IsActive) { @@ -605,7 +604,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform EditableWrapper.IgnoreChange = true; } - return result; + return true; } public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) From 5ee76faabdc59f6b3d5e7fc460d5924e204c4915 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 19 Jul 2023 13:24:55 +0000 Subject: [PATCH 331/391] fix no border resize issue --- src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 9a7f4b62e3..bdeda9236f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -59,7 +59,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_NCCALCSIZE: { - if (ToInt32(wParam) == 1 && !HasFullDecorations || _isClientAreaExtended) + if (ToInt32(wParam) == 1 && _isClientAreaExtended) { return IntPtr.Zero; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 044c2cad67..154b20ef84 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -1340,14 +1340,12 @@ namespace Avalonia.Win32 if (!_isFullScreenActive) { - var margin = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0; - var margins = new MARGINS { - cyBottomHeight = margin, - cxRightWidth = margin, - cxLeftWidth = margin, - cyTopHeight = margin + cyBottomHeight = 0, + cxRightWidth = 0, + cxLeftWidth = 0, + cyTopHeight = 0 }; DwmExtendFrameIntoClientArea(_hwnd, ref margins); From 1206996c83d7e89f8ac650a85ebd2751c7575bdb Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 19 Jul 2023 13:47:05 +0000 Subject: [PATCH 332/391] disable resize for None decorations --- src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index bdeda9236f..f62c99ec4a 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -59,7 +59,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_NCCALCSIZE: { - if (ToInt32(wParam) == 1 && _isClientAreaExtended) + if (ToInt32(wParam) == 1 && _windowProperties.Decorations == SystemDecorations.None || _isClientAreaExtended) { return IntPtr.Zero; } From fde8744175ee25ba5e231fc58a3064e94f03face Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 19 Jul 2023 15:09:53 +0000 Subject: [PATCH 333/391] delete selection when committext has null text --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index fa01cb83e3..3b2a3657b3 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -575,10 +575,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform var committedText = text.SubSequence(0, text.Length()); - if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText)) - { - _toplevel.TextInput(committedText); - } + if (_inputMethod.IsActive) + if (string.IsNullOrEmpty(committedText)) + _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); + else + _toplevel.TextInput(committedText); _commitInProgress = false; EndBatchEdit(); From 515e0843dec9544aa3c6964c4a580aee2ea28b59 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 19 Jul 2023 14:31:48 +0200 Subject: [PATCH 334/391] Handle ScrollContentPresenter extent rounding errors --- samples/Sandbox/MainWindow.axaml | 7 ----- .../Presenters/ScrollContentPresenter.cs | 19 ++++++++++-- .../Presenters/ScrollContentPresenterTests.cs | 31 +++++++++++++++++++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index f96abcac96..6929f192c7 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,11 +1,4 @@ - - - - - - - diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index bd3d2b5171..4ff8e7cfef 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -473,18 +473,31 @@ namespace Avalonia.Controls.Presenters } Viewport = finalSize; + Extent = ComputeExtent(finalSize); + _isAnchorElementDirty = true; + return finalSize; + } + + private Size ComputeExtent(Size viewportSize) + { var childMargin = Child!.Margin; + if (Child.UseLayoutRounding) { var scale = LayoutHelper.GetLayoutScale(Child); childMargin = LayoutHelper.RoundLayoutThickness(childMargin, scale, scale); } - Extent = Child!.Bounds.Size.Inflate(childMargin); - _isAnchorElementDirty = true; + var extent = Child!.Bounds.Size.Inflate(childMargin); - return finalSize; + if (MathUtilities.AreClose(extent.Width, viewportSize.Width, LayoutHelper.LayoutEpsilon)) + extent = extent.WithWidth(viewportSize.Width); + + if (MathUtilities.AreClose(extent.Height, viewportSize.Height, LayoutHelper.LayoutEpsilon)) + extent = extent.WithHeight(viewportSize.Height); + + return extent; } private void OnScrollGesture(object? sender, ScrollGestureEventArgs e) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs index c7ea5c1b69..30628b1af8 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs @@ -275,6 +275,37 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal(new Size(203.2, 203.2), target.Extent); } + [Fact] + public void Extent_Should_Be_Rounded_To_Viewport_When_Close() + { + var root = new TestRoot + { + LayoutScaling = 1.75, + UseLayoutRounding = true + }; + + var target = new ScrollContentPresenter + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Content = new Border + { + Width = 164.57142857142858, + Height = 164.57142857142858, + Margin = new Thickness(6) + } + }; + + root.Child = target; + target.UpdateChild(); + target.Measure(new Size(1000, 1000)); + target.Arrange(new Rect(0, 0, 1000, 1000)); + + Assert.Equal(new Size(176.00000000000003, 176.00000000000003), target.Child!.DesiredSize); + Assert.Equal(new Size(176, 176), target.Viewport); + Assert.Equal(new Size(176, 176), target.Extent); + } + [Fact] public void Extent_Width_Should_Be_Arrange_Width_When_CanScrollHorizontally_False() { From 571f69fbb5d1f403ebb64d316681392a126b5a6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:50:35 +0000 Subject: [PATCH 335/391] Bump word-wrap in /src/Browser/Avalonia.Browser/webapp Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap 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 12757fd7a0..fd4c6a9539 100644 --- a/src/Browser/Avalonia.Browser/webapp/package-lock.json +++ b/src/Browser/Avalonia.Browser/webapp/package-lock.json @@ -3296,9 +3296,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5560,9 +5560,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wrappy": { From a54163f1c10fcb5dbaabd8a9b3eae379f5f53a81 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 20 Jul 2023 13:10:50 +0000 Subject: [PATCH 336/391] select current composing region if active when commiting and ensure selection is set when surrounding text changes --- src/Android/Avalonia.Android/AndroidInputMethod.cs | 7 +++++++ .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 60f56805d1..b6c1154455 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -150,6 +150,13 @@ namespace Avalonia.Android _inputConnection.Editable.Replace(diff.index, editableText.Length, diff.diff); _inputConnection.EditableWrapper.IgnoreChange = false; + + if(diff.index == 0) + { + var selection = _client.Selection; + _client.Selection = new TextSelection(selection.Start, 0); + _client.Selection = selection; + } } (int index, string diff) GetDiff() diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 3b2a3657b3..a6e8d10777 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -571,8 +571,15 @@ namespace Avalonia.Android.Platform.SkiaPlatform BeginBatchEdit(); _commitInProgress = true; + var composingRegion = _editable.CurrentComposition; + var ret = base.CommitText(text, newCursorPosition); + if(composingRegion.Start != -1) + { + InputMethod.Client.Selection = composingRegion; + } + var committedText = text.SubSequence(0, text.Length()); if (_inputMethod.IsActive) From 99f1225f57372094353870ceb60837da7cabf73b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 20 Jul 2023 18:28:41 +0200 Subject: [PATCH 337/391] Prevent NRE --- src/Android/Avalonia.Android/AndroidInputMethod.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index b6c1154455..7d5130cf5d 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -138,6 +138,11 @@ namespace Avalonia.Android private void OnSurroundingTextChanged() { + if(_client is null) + { + return; + } + var surroundingText = _client.SurroundingText ?? ""; var editableText = _inputConnection.EditableWrapper.ToString(); From ed6331cd3dfde3036da361cdddc75e5031228e30 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 3 Nov 2021 15:41:53 +0100 Subject: [PATCH 338/391] feat(DataGrid): Allow binding DataGridColumn Width --- .../DataGridColumn.cs | 159 ++++++++++-------- .../DataGridColumnHeader.cs | 4 +- .../DataGridColumns.cs | 17 +- .../DataGridLength.cs | 1 - 4 files changed, 106 insertions(+), 75 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index a5695afeb7..a77ac985ac 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -7,7 +7,6 @@ using Avalonia.Data; using Avalonia.Interactivity; using Avalonia.VisualTree; using Avalonia.Collections; -using Avalonia.Utilities; using System; using System.ComponentModel; using System.Linq; @@ -24,8 +23,6 @@ namespace Avalonia.Controls { internal const int DATAGRIDCOLUMN_maximumWidth = 65536; private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false; - - private DataGridLength? _width; // Null by default, null means inherit the Width from the DataGrid private bool? _isReadOnly; private double? _maxWidth; private double? _minWidth; @@ -39,6 +36,7 @@ namespace Avalonia.Controls private IBinding _clipboardContentBinding; private ControlTheme _cellTheme; private Classes _cellStyleClasses; + private bool _setWidthInternalNoCallback; /// /// Initializes a new instance of the class. @@ -214,6 +212,36 @@ namespace Avalonia.Controls OwningGrid?.OnColumnVisibleStateChanged(this); NotifyPropertyChanged(change.Property.Name); } + else if (change.Property == WidthProperty) + { + if (!_settingWidthInternally) + { + InheritsWidth = false; + } + if (_setWidthInternalNoCallback == false) + { + var grid = OwningGrid; + var width = (change as AvaloniaPropertyChangedEventArgs).NewValue.Value; + if (grid != null) + { + var oldWidth = (change as AvaloniaPropertyChangedEventArgs).OldValue.Value; + if (width.IsStar != oldWidth.IsStar) + { + SetWidthInternalNoCallback(width); + IsInitialDesiredWidthDetermined = false; + grid.OnColumnWidthChanged(this); + } + else + { + Resize(oldWidth, width, false); + } + } + else + { + SetWidthInternalNoCallback(width); + } + } + } } @@ -549,48 +577,15 @@ namespace Avalonia.Controls } } + public static readonly StyledProperty WidthProperty = AvaloniaProperty + .Register(nameof(Width) + , coerce: CoerceWidth + ); + public DataGridLength Width { - get - { - return - _width ?? - OwningGrid?.ColumnWidth ?? - // We don't have a good choice here because we don't want to make this property nullable, see DevDiv Bugs 196581 - DataGridLength.Auto; - } - set - { - if (!_width.HasValue || _width.Value != value) - { - if (!_settingWidthInternally) - { - InheritsWidth = false; - } - - if (OwningGrid != null) - { - DataGridLength width = CoerceWidth(value); - if (width.IsStar != Width.IsStar) - { - // If a column has changed either from or to a star value, we want to recalculate all - // star column widths. They are recalculated during Measure based off what the value we set here. - SetWidthInternalNoCallback(width); - IsInitialDesiredWidthDetermined = false; - OwningGrid.OnColumnWidthChanged(this); - } - else - { - // If a column width's value is simply changing, we resize it (to the right only). - Resize(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue, false); - } - } - else - { - SetWidthInternalNoCallback(value); - } - } - } + get => this.GetValue(WidthProperty); + set => SetValue(WidthProperty, value); } /// @@ -812,19 +807,34 @@ namespace Avalonia.Controls /// on the rest of the star columns. For pixel widths, the desired value is based on the pixel value. /// For auto widths, the desired value is initialized as the column's minimum width. /// + /// /// The DataGridLength to coerce. /// The resultant (coerced) DataGridLength. - internal DataGridLength CoerceWidth(DataGridLength width) + static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width) { + var target = (DataGridColumn)source; + + if (target._setWidthInternalNoCallback) + { + return width; + } + + if (!target.IsSet(WidthProperty)) + { + + return target.OwningGrid?.ColumnWidth ?? + DataGridLength.Auto; + } + double desiredValue = width.DesiredValue; if (double.IsNaN(desiredValue)) { - if (width.IsStar && OwningGrid != null && OwningGrid.ColumnsInternal != null) + if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null) { double totalStarValues = 0; double totalStarDesiredValues = 0; double totalNonStarDisplayWidths = 0; - foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != this && !double.IsNaN(c.Width.DesiredValue))) + foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue))) { if (column.Width.IsStar) { @@ -839,7 +849,7 @@ namespace Avalonia.Controls if (totalStarValues == 0) { // Compute the new star column's desired value based on the available space if there are no other visible star columns - desiredValue = Math.Max(ActualMinWidth, OwningGrid.CellsWidth - totalNonStarDisplayWidths); + desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths); } else { @@ -853,7 +863,7 @@ namespace Avalonia.Controls } else { - desiredValue = ActualMinWidth; + desiredValue = target.ActualMinWidth; } } @@ -862,7 +872,7 @@ namespace Avalonia.Controls { displayValue = desiredValue; } - displayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue)); + displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue)); return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue); } @@ -896,7 +906,7 @@ namespace Avalonia.Controls }; result[!ContentControl.ContentProperty] = this[!HeaderProperty]; result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty]; - if (OwningGrid.ColumnHeaderTheme is {} columnTheme) + if (OwningGrid.ColumnHeaderTheme is { } columnTheme) { result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template); } @@ -909,7 +919,7 @@ namespace Avalonia.Controls /// internal void EnsureWidth() { - SetWidthInternalNoCallback(CoerceWidth(Width)); + SetWidthInternalNoCallback(CoerceWidth(this, Width)); } internal Control GenerateElementInternal(DataGridCell cell, object dataItem) @@ -931,17 +941,17 @@ namespace Avalonia.Controls /// can only decrease in size by the amount that the columns after it can increase in size. /// Likewise, the column can only increase in size if other columns can spare the width. /// - /// The new Value. - /// The new UnitType. - /// The new DesiredValue. - /// The new DisplayValue. + /// with before resize. + /// with after resize. /// Whether or not this resize was initiated by a user action. - internal void Resize(double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue, bool userInitiated) + + // double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue + internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated) { - double newValue = value; - double newDesiredValue = desiredValue; - double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue)); - DataGridLengthUnitType newUnitType = unitType; + double newValue = newWidth.Value; + double newDesiredValue = newWidth.DesiredValue; + double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue)); + DataGridLengthUnitType newUnitType = newWidth.UnitType; int starColumnsCount = 0; double totalDisplayWidth = 0; @@ -955,11 +965,11 @@ namespace Avalonia.Controls // If we're using star sizing, we can only resize the column as much as the columns to the // right will allow (i.e. until they hit their max or min widths). - if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (unitType == DataGridLengthUnitType.Star && Width.IsStar && userInitiated))) + if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated))) { - double limitedDisplayValue = Width.DisplayValue; + double limitedDisplayValue = oldWidth.DisplayValue; double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth); - double desiredChange = newDisplayValue - Width.DisplayValue; + double desiredChange = newDisplayValue - oldWidth.DisplayValue; if (desiredChange > availableIncrease) { // The desired change is greater than the amount of available space, @@ -979,7 +989,7 @@ namespace Avalonia.Controls // The desired change is negative, so we need to increase the widths of columns to the right. limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated); } - if (ActualCanUserResize || (Width.IsStar && !userInitiated)) + if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated)) { newDisplayValue = limitedDisplayValue; } @@ -1002,9 +1012,10 @@ namespace Avalonia.Controls } } - DataGridLength oldWidth = Width; - SetWidthInternalNoCallback(new DataGridLength(Math.Min(double.MaxValue, newValue), newUnitType, newDesiredValue, newDisplayValue)); - if (Width != oldWidth) + newDisplayValue = Math.Min(double.MaxValue, newValue); + newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue); + SetWidthInternalNoCallback(newWidth); + if (newWidth != oldWidth) { OwningGrid.OnColumnWidthChanged(this); } @@ -1052,7 +1063,17 @@ namespace Avalonia.Controls /// The new Width. internal void SetWidthInternalNoCallback(DataGridLength width) { - _width = width; + var originalValue = _setWidthInternalNoCallback; + _setWidthInternalNoCallback = true; + try + { + Width = width; + } + finally + { + _setWidthInternalNoCallback = originalValue; + } + } /// @@ -1122,7 +1143,7 @@ namespace Avalonia.Controls && OwningGrid.DataConnection != null && OwningGrid.DataConnection.SortDescriptions != null) { - if(CustomSortComparer != null) + if (CustomSortComparer != null) { return OwningGrid.DataConnection.SortDescriptions diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 0755864c25..28e8a0ed5e 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -785,7 +785,9 @@ namespace Avalonia.Controls double desiredWidth = _originalWidth + mouseDelta; desiredWidth = Math.Max(_dragColumn.ActualMinWidth, Math.Min(_dragColumn.ActualMaxWidth, desiredWidth)); - _dragColumn.Resize(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth, true); + _dragColumn.Resize(_dragColumn.Width, + new(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth), + true); OwningGrid.UpdateHorizontalOffset(_originalHorizontalOffset); diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index 4056b78bfe..8dd5bd262b 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -91,7 +91,10 @@ namespace Avalonia.Controls // this column is newly added, we'll just set its display value directly. if (UsesStarSizing && column.IsInitialDesiredWidthDetermined) { - column.Resize(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth, false); + var oldWidth = column.Width; + column.Resize(oldWidth, + new(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth), + false); } else { @@ -142,7 +145,7 @@ namespace Avalonia.Controls { Debug.Assert(dataGridColumn != null); - if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn && + if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn && dataGridBoundColumn.Binding is BindingBase binding) { var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); @@ -359,6 +362,7 @@ namespace Avalonia.Controls if (column.IsVisible && oldValue != column.ActualMaxWidth) { + DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue); if (column.ActualMaxWidth < column.Width.DisplayValue) { // If the maximum width has caused the column to decrease in size, try first to resize @@ -371,7 +375,9 @@ namespace Avalonia.Controls { // If the column was previously limited by its maximum value but has more room now, // attempt to resize the column to its desired width. - column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false); + column.Resize(oldWitdh, + new (column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue), + false); } OnColumnWidthChanged(column); } @@ -388,6 +394,7 @@ namespace Avalonia.Controls if (column.IsVisible && oldValue != column.ActualMinWidth) { + DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue); if (column.ActualMinWidth > column.Width.DisplayValue) { // If the minimum width has caused the column to increase in size, try first to resize @@ -400,7 +407,9 @@ namespace Avalonia.Controls { // If the column was previously limited by its minimum value but but can be smaller now, // attempt to resize the column to its desired width. - column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false); + column.Resize(oldWitdh, + new(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue), + false); } OnColumnWidthChanged(column); } diff --git a/src/Avalonia.Controls.DataGrid/DataGridLength.cs b/src/Avalonia.Controls.DataGrid/DataGridLength.cs index a193352f52..9bff8b2cf5 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridLength.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridLength.cs @@ -7,7 +7,6 @@ using Avalonia.Utilities; using System; using System.ComponentModel; using System.Globalization; -using Avalonia.Controls.Utils; namespace Avalonia.Controls { From a5f615d783aab4765769cf45f7b9ad94bce06875 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 7 Jul 2023 11:19:08 +0200 Subject: [PATCH 339/391] feat(ControlCatalog): Add sample how binding DataGridColumn Width --- .../Models/GDPdLengthConverter.cs | 31 +++++++++++++++++++ .../ControlCatalog/Pages/DataGridPage.xaml | 21 +++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 samples/ControlCatalog/Models/GDPdLengthConverter.cs diff --git a/samples/ControlCatalog/Models/GDPdLengthConverter.cs b/samples/ControlCatalog/Models/GDPdLengthConverter.cs new file mode 100644 index 0000000000..034e664305 --- /dev/null +++ b/samples/ControlCatalog/Models/GDPdLengthConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Models; + +internal class GDPdLengthConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double d) + { + return new Avalonia.Controls.DataGridLength(d,Avalonia.Controls.DataGridLengthUnitType.Pixel,d,d); + } + else if (value is decimal d2) + { + var dv =System.Convert.ToDouble(d2); + return new Avalonia.Controls.DataGridLength(dv, Avalonia.Controls.DataGridLengthUnitType.Pixel, dv, dv); + } + return value; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is Avalonia.Controls.DataGridLength width) + { + return System.Convert.ToDecimal(width.DisplayValue); + } + return value; + } +} diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index 356834832d..88252091c4 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -1,11 +1,14 @@ + + @@ -28,8 +31,18 @@ - + + + + + @@ -38,9 +51,11 @@ - From 0ce9a6ed87559506d401a0c0d041438080a34409 Mon Sep 17 00:00:00 2001 From: mrxx99 Date: Sun, 23 Jul 2023 19:49:05 +0200 Subject: [PATCH 340/391] Fix creating fields for attached properties called name. closes #12283 --- src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs | 6 ++++++ tests/Avalonia.Generators.Tests/Views/NamedControl.xml | 3 ++- tests/Avalonia.Generators.Tests/Views/xNamedControl.xml | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs b/src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs index 7ed19eb84c..955df90ddd 100644 --- a/src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs +++ b/src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs @@ -38,6 +38,7 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor { if (child is XamlAstXamlPropertyValueNode propertyValueNode && propertyValueNode.Property is XamlAstNamePropertyReference namedProperty && + !IsAttachedProperty(namedProperty) && namedProperty.Name == "Name" && propertyValueNode.Values.Count > 0 && propertyValueNode.Values[0] is XamlAstTextNode text) @@ -89,4 +90,9 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor _ => _defaultFieldModifier }; } + + private static bool IsAttachedProperty(XamlAstNamePropertyReference namedProperty) + { + return !namedProperty.DeclaringType.Equals(namedProperty.TargetType); + } } diff --git a/tests/Avalonia.Generators.Tests/Views/NamedControl.xml b/tests/Avalonia.Generators.Tests/Views/NamedControl.xml index ba1efb247f..0a3c3d3111 100644 --- a/tests/Avalonia.Generators.Tests/Views/NamedControl.xml +++ b/tests/Avalonia.Generators.Tests/Views/NamedControl.xml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sample.App.NamedControl"> - \ No newline at end of file + diff --git a/tests/Avalonia.Generators.Tests/Views/xNamedControl.xml b/tests/Avalonia.Generators.Tests/Views/xNamedControl.xml index 7eab86411c..3dc6df2c7d 100644 --- a/tests/Avalonia.Generators.Tests/Views/xNamedControl.xml +++ b/tests/Avalonia.Generators.Tests/Views/xNamedControl.xml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sample.App.xNamedControl"> - \ No newline at end of file + From c834b044966d2536fdc648468f9ce84c94f94c34 Mon Sep 17 00:00:00 2001 From: Luc Date: Mon, 24 Jul 2023 19:03:48 +0300 Subject: [PATCH 341/391] Fix segfault on Linux when typing in a TexBox See https://github.com/AvaloniaUI/Avalonia/issues/12049 --- src/Avalonia.X11/X11Info.cs | 20 +++++++++++++++++--- src/Avalonia.X11/X11Platform.cs | 10 +++------- src/Avalonia.X11/X11Window.Ime.cs | 15 +++++++++++---- src/Avalonia.X11/XLib.cs | 2 +- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index 17cd6b12c6..890ac0d5ac 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -34,7 +34,10 @@ namespace Avalonia.X11 public bool HasXim { get; set; } public bool HasXSync { get; set; } public IntPtr DefaultFontSet { get; set; } - + + [DllImport("libc")] + private static extern void setlocale(int type, string s); + public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim) { Display = display; @@ -48,7 +51,10 @@ namespace Avalonia.X11 DefaultFontSet = XCreateFontSet(Display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*", out var _, out var _, IntPtr.Zero); - + + // We have problems with text input otherwise + setlocale(0, ""); + if (useXim) { XSetLocaleModifiers(""); @@ -59,7 +65,15 @@ namespace Avalonia.X11 if (Xim == IntPtr.Zero) { - XSetLocaleModifiers("@im=none"); + if (XSetLocaleModifiers("@im=none") == IntPtr.Zero) + { + setlocale(0, "en_US.UTF-8"); + if (XSetLocaleModifiers("@im=none") == IntPtr.Zero) + { + setlocale(0, "C.UTF-8"); + XSetLocaleModifiers("@im=none"); + } + } Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 12e4e09158..91c0190e8b 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -36,12 +36,11 @@ namespace Avalonia.X11 public IntPtr OrphanedWindow { get; private set; } public X11Globals Globals { get; private set; } public ManualRawEventGrouperDispatchQueue EventGrouperDispatchQueue { get; } = new(); - [DllImport("libc")] - private static extern void setlocale(int type, string s); + public void Initialize(X11PlatformOptions options) { Options = options; - + bool useXim = false; if (EnableIme(options)) { @@ -50,9 +49,6 @@ namespace Avalonia.X11 useXim = true; } - // We have problems with text input otherwise - setlocale(0, ""); - XInitThreads(); Display = XOpenDisplay(IntPtr.Zero); if (Display == IntPtr.Zero) @@ -64,7 +60,7 @@ namespace Avalonia.X11 OrphanedWindow = XCreateSimpleWindow(Display, XDefaultRootWindow(Display), 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero); XError.Init(); - + Info = new X11Info(Display, DeferredDisplay, useXim); Globals = new X11Globals(this); //TODO: log diff --git a/src/Avalonia.X11/X11Window.Ime.cs b/src/Avalonia.X11/X11Window.Ime.cs index 4ee418fd0a..1264a9dc88 100644 --- a/src/Avalonia.X11/X11Window.Ime.cs +++ b/src/Avalonia.X11/X11Window.Ime.cs @@ -133,14 +133,21 @@ namespace Avalonia.X11 { if (ImeBuffer == IntPtr.Zero) ImeBuffer = Marshal.AllocHGlobal(ImeBufferSize); - - var len = Xutf8LookupString(_xic, ref ev, ImeBuffer.ToPointer(), ImeBufferSize, - out _, out var istatus); - var status = (XLookupStatus)istatus; + + IntPtr istatus; + int len; + if(_xic != IntPtr.Zero) + len = Xutf8LookupString(_xic, ref ev, ImeBuffer.ToPointer(), + ImeBufferSize, out _, out istatus); + else + len = XLookupString(ref ev, ImeBuffer.ToPointer(), ImeBufferSize, + out _, out istatus); if (len == 0) return null; + var status = (XLookupStatus)istatus; + string text; if (status == XLookupStatus.XBufferOverflow) return null; diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 641adde7e2..d129e4b025 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -468,7 +468,7 @@ namespace Avalonia.X11 public static extern unsafe int XLookupString(ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status); [DllImport (libX11)] - public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out UIntPtr status); + public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status); [DllImport (libX11)] public static extern unsafe int Xutf8LookupString(IntPtr xic, XEvent* xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status); From 9c94158ae7da372eedc75c96001d056989ba1c8b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 25 Jul 2023 08:38:52 +0200 Subject: [PATCH 342/391] Make sure TextLine.GetCharacterHitFromDistance for mixed buffers --- .../Media/TextFormatting/TextLineImpl.cs | 2 +- .../Media/TextFormatting/TextLineTests.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 44f53420de..86c29941e7 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -216,7 +216,7 @@ namespace Avalonia.Media.TextFormatting if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight) { - currentPosition = lineLength - lastRun.Length; + currentPosition = FirstTextSourceIndex + Length - lastRun.Length; } return GetRunCharacterHit(lastRun, currentPosition, distance); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index d576a64523..96c592702b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -822,6 +822,26 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_GetCharacterHitFromDistance_From_Mixed_TextBuffer() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new MixedTextBufferTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 20, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var characterHit = textLine.GetCharacterHitFromDistance(double.PositiveInfinity); + + Assert.Equal(40, characterHit.FirstCharacterIndex + characterHit.TrailingLength); + } + } + private class MixedTextBufferTextSource : ITextSource { public TextRun? GetTextRun(int textSourceIndex) From 1e64c51879118e24caeb967400f8d8d7f0ba8493 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 25 Jul 2023 09:29:58 +0200 Subject: [PATCH 343/391] Adjust test --- .../Media/TextFormatting/TextLineTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 96c592702b..a3bbdc9a7c 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -713,7 +713,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var characterHit = textLine.GetCharacterHitFromDistance(1000); - Assert.Equal(10, characterHit.FirstCharacterIndex); + Assert.Equal(11, characterHit.FirstCharacterIndex); Assert.Equal(1, characterHit.TrailingLength); } } From bd3f097895c840445647090e64413d848a090641 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 25 Jul 2023 13:26:15 +0200 Subject: [PATCH 344/391] Adjust behavior to match what WPF expects --- src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs | 4 ++-- .../Media/TextFormatting/TextLineTests.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 86c29941e7..a2936f1857 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -204,7 +204,7 @@ namespace Avalonia.Media.TextFormatting if (_paragraphProperties.FlowDirection == FlowDirection.RightToLeft) { - currentPosition = Length - firstRun.Length; + currentPosition += lineLength - firstRun.Length; } return GetRunCharacterHit(firstRun, currentPosition, 0); @@ -216,7 +216,7 @@ namespace Avalonia.Media.TextFormatting if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight) { - currentPosition = FirstTextSourceIndex + Length - lastRun.Length; + currentPosition += lineLength - lastRun.Length; } return GetRunCharacterHit(lastRun, currentPosition, distance); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index a3bbdc9a7c..96c592702b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -713,7 +713,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var characterHit = textLine.GetCharacterHitFromDistance(1000); - Assert.Equal(11, characterHit.FirstCharacterIndex); + Assert.Equal(10, characterHit.FirstCharacterIndex); Assert.Equal(1, characterHit.TrailingLength); } } From 5d2b9edec35c1a8f159b85ac547979a349855a94 Mon Sep 17 00:00:00 2001 From: daniilpavliuchyk Date: Tue, 25 Jul 2023 19:27:16 +0300 Subject: [PATCH 345/391] Fix window incorrect positioning with window startup location CenterScreen on MacOS --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index b954bdd4e3..1a4f979ce7 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -151,6 +151,14 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { + auto frame = [Window frame]; + + AvnPoint point; + point.X = frame.origin.x; + point.Y = frame.origin.y + frame.size.height; + + lastPositionSet = ConvertPointY(point); + hasPosition = true; [Window orderOut:Window]; } From 0e7b8f6f4589195ae35e933c64f89f9478832fb1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 25 Jul 2023 23:17:48 +0200 Subject: [PATCH 346/391] Allow embedded root automation peers. --- .../Avalonia.Win32/Automation/RootAutomationNode.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 739f0ac251..0a73c8bc7b 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -33,7 +33,7 @@ namespace Avalonia.Win32.Automation return null; var p = WindowImpl.PointToClient(new PixelPoint((int)x, (int)y)); - var found = InvokeSync(() => Peer.GetPeerFromPoint(p)); + var found = InvokeSync(() => GetPeerFromPoint(p)); var result = GetOrCreate(found) as IRawElementProviderFragment; return result; } @@ -101,5 +101,15 @@ namespace Avalonia.Win32.Automation return result; } } + + private AutomationPeer? GetPeerFromPoint(Point p) + { + var hit = Peer.GetPeerFromPoint(p); + + while (hit != Peer && hit?.GetProvider() is { } embeddedRoot) + hit = embeddedRoot.GetPeerFromPoint(p); + + return hit; + } } } From 1fe2c29901e63340c0faf6a83b223d5bf24c6b59 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 26 Jul 2023 15:48:45 +0600 Subject: [PATCH 347/391] Started to move composition sync logic to the visual itself --- .../Composition/CompositingRenderer.cs | 122 +------------ src/Avalonia.Base/Visual.Composition.cs | 165 ++++++++++++++++++ src/Avalonia.Base/Visual.cs | 28 +-- 3 files changed, 170 insertions(+), 145 deletions(-) create mode 100644 src/Avalonia.Base/Visual.Composition.cs diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 7c5f5d75d5..4db14ba183 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -142,93 +142,6 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester QueueUpdate(); } - private static void SyncChildren(Visual v) - { - //TODO: Optimize by moving that logic to Visual itself - if(v.CompositionVisual == null) - return; - var compositionChildren = v.CompositionVisual.Children; - var visualChildren = (AvaloniaList)v.GetVisualChildren(); - - PooledList<(Visual visual, int index)>? sortedChildren = null; - if (v.HasNonUniformZIndexChildren && visualChildren.Count > 1) - { - sortedChildren = new (visualChildren.Count); - for (var c = 0; c < visualChildren.Count; c++) - sortedChildren.Add((visualChildren[c], c)); - - // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements. - sortedChildren.Sort(static (lhs, rhs) => - { - var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex); - return result == 0 ? lhs.index.CompareTo(rhs.index) : result; - }); - } - - var childVisual = v.ChildCompositionVisual; - - // Check if the current visual somehow got migrated to another compositor - if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor) - childVisual = null; - - var expectedCount = visualChildren.Count; - if (childVisual != null) - expectedCount++; - - if (compositionChildren.Count == expectedCount) - { - bool mismatch = false; - if (sortedChildren != null) - for (var c = 0; c < visualChildren.Count; c++) - { - if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) - { - mismatch = true; - break; - } - } - else - for (var c = 0; c < visualChildren.Count; c++) - if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) - { - mismatch = true; - break; - } - - if (childVisual != null && - !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) - mismatch = true; - - if (!mismatch) - { - sortedChildren?.Dispose(); - return; - } - } - - compositionChildren.Clear(); - if (sortedChildren != null) - { - foreach (var ch in sortedChildren) - { - var compositionChild = ch.visual.CompositionVisual; - if (compositionChild != null) - compositionChildren.Add(compositionChild); - } - sortedChildren.Dispose(); - } - else - foreach (var ch in visualChildren) - { - var compositionChild = ch.CompositionVisual; - if (compositionChild != null) - compositionChildren.Add(compositionChild); - } - - if (childVisual != null) - compositionChildren.Add(childVisual); - } - private void UpdateCore() { _queuedUpdate = false; @@ -238,36 +151,7 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester if(comp == null) continue; - // TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time - comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0); - comp.Size = new (visual.Bounds.Width, visual.Bounds.Height); - comp.Visible = visual.IsVisible; - comp.Opacity = (float)visual.Opacity; - comp.ClipToBounds = visual.ClipToBounds; - comp.Clip = visual.Clip?.PlatformImpl; - - - if (!Equals(comp.OpacityMask, visual.OpacityMask)) - comp.OpacityMask = visual.OpacityMask?.ToImmutable(); - - if (!comp.Effect.EffectEquals(visual.Effect)) - comp.Effect = visual.Effect?.ToImmutable(); - - comp.RenderOptions = visual.RenderOptions; - - var renderTransform = Matrix.Identity; - - if (visual.HasMirrorTransform) - renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); - - if (visual.RenderTransform != null) - { - var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); - var offset = Matrix.CreateTranslation(origin); - renderTransform *= (-offset) * visual.RenderTransform.Value * (offset); - } - - comp.TransformMatrix = renderTransform; + visual.SynchronizeCompositionProperties(); try { @@ -279,11 +163,11 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester _recorder.Reset(); } - SyncChildren(visual); + visual.SynchronizeCompositionChildVisuals(); } foreach(var v in _recalculateChildren) if (!_dirty.Contains(v)) - SyncChildren(v); + v.SynchronizeCompositionChildVisuals(); _dirty.Clear(); _recalculateChildren.Clear(); CompositionTarget.Size = _root.ClientSize; diff --git a/src/Avalonia.Base/Visual.Composition.cs b/src/Avalonia.Base/Visual.Composition.cs new file mode 100644 index 0000000000..d6a4e3e9be --- /dev/null +++ b/src/Avalonia.Base/Visual.Composition.cs @@ -0,0 +1,165 @@ +using Avalonia.Collections; +using Avalonia.Collections.Pooled; +using Avalonia.Media; +using Avalonia.Rendering.Composition; +using Avalonia.Rendering.Composition.Server; +using Avalonia.VisualTree; + +namespace Avalonia; + +public partial class Visual +{ + internal CompositionDrawListVisual? CompositionVisual { get; private set; } + internal CompositionVisual? ChildCompositionVisual { get; set; } + + + private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor) + => new CompositionDrawListVisual(compositor, + new ServerCompositionDrawListVisual(compositor.Server, this), this); + + internal CompositionVisual AttachToCompositor(Compositor compositor) + { + if (CompositionVisual == null || CompositionVisual.Compositor != compositor) + { + CompositionVisual = CreateCompositionVisual(compositor); + } + + return CompositionVisual; + } + + internal virtual void DetachFromCompositor() + { + if (CompositionVisual != null) + { + if (ChildCompositionVisual != null) + CompositionVisual.Children.Remove(ChildCompositionVisual); + + CompositionVisual.DrawList = null; + CompositionVisual = null; + } + } + + internal virtual void SynchronizeCompositionChildVisuals() + { + if(CompositionVisual == null) + return; + var compositionChildren = CompositionVisual.Children; + var visualChildren = (AvaloniaList)VisualChildren; + + PooledList<(Visual visual, int index)>? sortedChildren = null; + if (HasNonUniformZIndexChildren && visualChildren.Count > 1) + { + sortedChildren = new (visualChildren.Count); + for (var c = 0; c < visualChildren.Count; c++) + sortedChildren.Add((visualChildren[c], c)); + + // Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements. + sortedChildren.Sort(static (lhs, rhs) => + { + var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex); + return result == 0 ? lhs.index.CompareTo(rhs.index) : result; + }); + } + + var childVisual = ChildCompositionVisual; + + // Check if the current visual somehow got migrated to another compositor + if (childVisual != null && childVisual.Compositor != CompositionVisual.Compositor) + childVisual = null; + + var expectedCount = visualChildren.Count; + if (childVisual != null) + expectedCount++; + + if (compositionChildren.Count == expectedCount) + { + bool mismatch = false; + if (sortedChildren != null) + for (var c = 0; c < visualChildren.Count; c++) + { + if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) + { + mismatch = true; + break; + } + } + else + for (var c = 0; c < visualChildren.Count; c++) + if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) + { + mismatch = true; + break; + } + + if (childVisual != null && + !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) + mismatch = true; + + if (!mismatch) + { + sortedChildren?.Dispose(); + return; + } + } + + compositionChildren.Clear(); + if (sortedChildren != null) + { + foreach (var ch in sortedChildren) + { + var compositionChild = ch.visual.CompositionVisual; + if (compositionChild != null) + compositionChildren.Add(compositionChild); + } + sortedChildren.Dispose(); + } + else + foreach (var ch in visualChildren) + { + var compositionChild = ch.CompositionVisual; + if (compositionChild != null) + compositionChildren.Add(compositionChild); + } + + if (childVisual != null) + compositionChildren.Add(childVisual); + } + + internal virtual void SynchronizeCompositionProperties() + { + if(CompositionVisual == null) + return; + var comp = CompositionVisual; + + // TODO: Introduce a dirty mask like WPF has, so we don't overwrite properties every time + + comp.Offset = new (Bounds.Left, Bounds.Top, 0); + comp.Size = new (Bounds.Width, Bounds.Height); + comp.Visible = IsVisible; + comp.Opacity = (float)Opacity; + comp.ClipToBounds = ClipToBounds; + comp.Clip = Clip?.PlatformImpl; + + if (!Equals(comp.OpacityMask, OpacityMask)) + comp.OpacityMask = OpacityMask?.ToImmutable(); + + if (!comp.Effect.EffectEquals(Effect)) + comp.Effect = Effect?.ToImmutable(); + + comp.RenderOptions = RenderOptions; + + var renderTransform = Matrix.Identity; + + if (HasMirrorTransform) + renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, Bounds.Width, 0); + + if (RenderTransform != null) + { + var origin = RenderTransformOrigin.ToPixels(new Size(Bounds.Width, Bounds.Height)); + var offset = Matrix.CreateTranslation(origin); + renderTransform *= (-offset) * RenderTransform.Value * (offset); + } + + comp.TransformMatrix = renderTransform; + } +} \ No newline at end of file diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 1b7975c154..9774468e16 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -29,7 +29,7 @@ namespace Avalonia /// extension methods defined in . /// [UsableDuringInitialization] - public class Visual : StyledElement + public partial class Visual : StyledElement { /// /// Defines the property. @@ -316,9 +316,6 @@ namespace Avalonia /// protected internal IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot); - internal CompositionDrawListVisual? CompositionVisual { get; private set; } - internal CompositionVisual? ChildCompositionVisual { get; set; } - internal RenderOptions RenderOptions { get; set; } internal bool HasNonUniformZIndexChildren { get; private set; } @@ -515,20 +512,6 @@ namespace Avalonia } } - private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor) - => new CompositionDrawListVisual(compositor, - new ServerCompositionDrawListVisual(compositor.Server, this), this); - - internal CompositionVisual AttachToCompositor(Compositor compositor) - { - if (CompositionVisual == null || CompositionVisual.Compositor != compositor) - { - CompositionVisual = CreateCompositionVisual(compositor); - } - - return CompositionVisual; - } - /// /// Calls the method /// for this control and all of its visual descendants. @@ -547,14 +530,7 @@ namespace Avalonia DisableTransitions(); OnDetachedFromVisualTree(e); - if (CompositionVisual != null) - { - if (ChildCompositionVisual != null) - CompositionVisual.Children.Remove(ChildCompositionVisual); - - CompositionVisual.DrawList = null; - CompositionVisual = null; - } + DetachFromCompositor(); DetachedFromVisualTree?.Invoke(this, e); e.Root.Renderer.AddDirty(this); From a99fb216cc12376a99c1d5918a8a813ede2e9373 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 26 Jul 2023 12:33:58 +0200 Subject: [PATCH 348/391] Use embedded pdb for analyzers and build tasks --- .../Avalonia.Build.Tasks.csproj | 16 +++++++++------- .../Avalonia.Analyzers/Avalonia.Analyzers.csproj | 1 + .../Avalonia.Generators.csproj | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 4f9c7416db..bc36c610c7 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -1,12 +1,14 @@  - netstandard2.0 - exe - false - tools - $(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL - true - $(NoWarn);NU1605;CS8632 + netstandard2.0 + exe + false + tools + $(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL + true + $(NoWarn);NU1605;CS8632 + embedded + false diff --git a/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj b/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj index 05d72618a9..d6538fba13 100644 --- a/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj +++ b/src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj @@ -4,6 +4,7 @@ false Avalonia.Analyzers true + embedded true false true diff --git a/src/tools/Avalonia.Generators/Avalonia.Generators.csproj b/src/tools/Avalonia.Generators/Avalonia.Generators.csproj index c6992f9438..a7653562e8 100644 --- a/src/tools/Avalonia.Generators/Avalonia.Generators.csproj +++ b/src/tools/Avalonia.Generators/Avalonia.Generators.csproj @@ -4,6 +4,7 @@ false Avalonia.Generators $(DefineConstants);XAMLX_INTERNAL + embedded true false true From 2c91d7f89323c736dd4afeaf6e13ad5efc247b06 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 12:45:58 +0200 Subject: [PATCH 349/391] Handle null from RootProvider_GetWindow. --- native/Avalonia.Native/src/OSX/automation.mm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index d0c8d7a9db..4b325a092d 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -73,6 +73,13 @@ private: if (peer->IsRootProvider()) { auto window = peer->RootProvider_GetWindow(); + + if (window == nullptr) + { + NSLog(@"IRootProvider.PlatformImpl returned null or a non-WindowBaseImpl."); + return nil; + } + auto holder = dynamic_cast(window); auto view = holder->GetNSView(); return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view]; From 8d5ef676f5098257889925419501799596aa1bf7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 12:46:25 +0200 Subject: [PATCH 350/391] Allow non-ControlAutomationPeer IRootProviders. --- src/Avalonia.Native/AvnAutomationPeer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 6c4e96b31b..038b62a7f6 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -74,10 +74,11 @@ namespace Avalonia.Native public int IsRootProvider() => (_inner is IRootProvider).AsComBool(); - public IAvnWindowBase RootProvider_GetWindow() + public IAvnWindowBase? RootProvider_GetWindow() { - var window = (WindowBase)((ControlAutomationPeer)_inner).Owner; - return ((WindowBaseImpl)window.PlatformImpl!).Native; + if (((IRootProvider)_inner).PlatformImpl is WindowBaseImpl impl) + return impl.Native; + return null; } public IAvnAutomationPeer? RootProvider_GetFocus() => Wrap(((IRootProvider)_inner).GetFocus()); From da993425e7a18c545d591259eb1a19a8bc27f5a9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 13:46:35 +0200 Subject: [PATCH 351/391] Fix determining if a peer supports a provider. We should use `GetProvider` instead of a plain cast as a peer may decide to dynamically support a provider, or delegate its implementation. --- src/Avalonia.Native/AvnAutomationPeer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 038b62a7f6..6b1b4a7a03 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -72,7 +72,7 @@ namespace Avalonia.Native Node = node; } - public int IsRootProvider() => (_inner is IRootProvider).AsComBool(); + public int IsRootProvider() => (_inner.GetProvider() is not null).AsComBool(); public IAvnWindowBase? RootProvider_GetWindow() { @@ -104,7 +104,7 @@ namespace Avalonia.Native return Wrap(result); } - public int IsExpandCollapseProvider() => (_inner is IExpandCollapseProvider).AsComBool(); + public int IsExpandCollapseProvider() => (_inner.GetProvider() is not null).AsComBool(); public int ExpandCollapseProvider_GetIsExpanded() => ((IExpandCollapseProvider)_inner).ExpandCollapseState switch { @@ -128,14 +128,14 @@ namespace Avalonia.Native public double RangeValueProvider_GetLargeChange() => ((IRangeValueProvider)_inner).LargeChange; public void RangeValueProvider_SetValue(double value) => ((IRangeValueProvider)_inner).SetValue(value); - public int IsSelectionItemProvider() => (_inner is ISelectionItemProvider).AsComBool(); + public int IsSelectionItemProvider() => (_inner.GetProvider() is not null).AsComBool(); public int SelectionItemProvider_IsSelected() => ((ISelectionItemProvider)_inner).IsSelected.AsComBool(); - public int IsToggleProvider() => (_inner is IToggleProvider).AsComBool(); + public int IsToggleProvider() => (_inner.GetProvider() is not null).AsComBool(); public int ToggleProvider_GetToggleState() => (int)((IToggleProvider)_inner).ToggleState; public void ToggleProvider_Toggle() => ((IToggleProvider)_inner).Toggle(); - public int IsValueProvider() => (_inner is IValueProvider).AsComBool(); + public int IsValueProvider() => (_inner.GetProvider() is not null).AsComBool(); public IAvnString ValueProvider_GetValue() => ((IValueProvider)_inner).Value.ToAvnString(); public void ValueProvider_SetValue(string value) => ((IValueProvider)_inner).SetValue(value); From c9dfda42ebd524e19205d756ba6c625496e9361d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 17:31:43 +0200 Subject: [PATCH 352/391] More fixing of provider resolution. The previous commit missed some providers, and we also need to call `GetProvider` when calling members on the provider. --- src/Avalonia.Native/AvnAutomationPeer.cs | 88 +++++++++++++----------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 6b1b4a7a03..8069ac76bf 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -48,6 +48,13 @@ namespace Avalonia.Native public void SetFocus() => _inner.SetFocus(); public int ShowContextMenu() => _inner.ShowContextMenu().AsComBool(); + public void SetNode(IAvnAutomationNode node) + { + if (Node is not null) + throw new InvalidOperationException("The AvnAutomationPeer already has a node."); + Node = node; + } + public IAvnAutomationPeer? RootPeer { get @@ -65,27 +72,22 @@ namespace Avalonia.Native } } - public void SetNode(IAvnAutomationNode node) - { - if (Node is not null) - throw new InvalidOperationException("The AvnAutomationPeer already has a node."); - Node = node; - } - - public int IsRootProvider() => (_inner.GetProvider() is not null).AsComBool(); + private IRootProvider RootProvider => GetProvider(); + private IExpandCollapseProvider ExpandCollapseProvider => GetProvider(); + private IInvokeProvider InvokeProvider => GetProvider(); + private IRangeValueProvider RangeValueProvider => GetProvider(); + private ISelectionItemProvider SelectionItemProvider => GetProvider(); + private IToggleProvider ToggleProvider => GetProvider(); + private IValueProvider ValueProvider => GetProvider(); - public IAvnWindowBase? RootProvider_GetWindow() - { - if (((IRootProvider)_inner).PlatformImpl is WindowBaseImpl impl) - return impl.Native; - return null; - } - - public IAvnAutomationPeer? RootProvider_GetFocus() => Wrap(((IRootProvider)_inner).GetFocus()); + public int IsRootProvider() => IsProvider(); + + public IAvnWindowBase? RootProvider_GetWindow() => (RootProvider.PlatformImpl as WindowBaseImpl)?.Native; + public IAvnAutomationPeer? RootProvider_GetFocus() => Wrap(RootProvider.GetFocus()); public IAvnAutomationPeer? RootProvider_GetPeerFromPoint(AvnPoint point) { - var result = ((IRootProvider)_inner).GetPeerFromPoint(point.ToAvaloniaPoint()); + var result = RootProvider.GetPeerFromPoint(point.ToAvaloniaPoint()); if (result is null) return null; @@ -104,46 +106,54 @@ namespace Avalonia.Native return Wrap(result); } - public int IsExpandCollapseProvider() => (_inner.GetProvider() is not null).AsComBool(); + public int IsExpandCollapseProvider() => IsProvider(); - public int ExpandCollapseProvider_GetIsExpanded() => ((IExpandCollapseProvider)_inner).ExpandCollapseState switch + public int ExpandCollapseProvider_GetIsExpanded() => ExpandCollapseProvider.ExpandCollapseState switch { ExpandCollapseState.Expanded => 1, ExpandCollapseState.PartiallyExpanded => 1, _ => 0, }; - public int ExpandCollapseProvider_GetShowsMenu() => ((IExpandCollapseProvider)_inner).ShowsMenu.AsComBool(); - public void ExpandCollapseProvider_Expand() => ((IExpandCollapseProvider)_inner).Expand(); - public void ExpandCollapseProvider_Collapse() => ((IExpandCollapseProvider)_inner).Collapse(); + public int ExpandCollapseProvider_GetShowsMenu() => ExpandCollapseProvider.ShowsMenu.AsComBool(); + public void ExpandCollapseProvider_Expand() => ExpandCollapseProvider.Expand(); + public void ExpandCollapseProvider_Collapse() => ExpandCollapseProvider.Collapse(); - public int IsInvokeProvider() => (_inner is IInvokeProvider).AsComBool(); - public void InvokeProvider_Invoke() => ((IInvokeProvider)_inner).Invoke(); + public int IsInvokeProvider() => IsProvider(); + public void InvokeProvider_Invoke() => InvokeProvider.Invoke(); - public int IsRangeValueProvider() => (_inner is IRangeValueProvider).AsComBool(); - public double RangeValueProvider_GetValue() => ((IRangeValueProvider)_inner).Value; - public double RangeValueProvider_GetMinimum() => ((IRangeValueProvider)_inner).Minimum; - public double RangeValueProvider_GetMaximum() => ((IRangeValueProvider)_inner).Maximum; - public double RangeValueProvider_GetSmallChange() => ((IRangeValueProvider)_inner).SmallChange; - public double RangeValueProvider_GetLargeChange() => ((IRangeValueProvider)_inner).LargeChange; - public void RangeValueProvider_SetValue(double value) => ((IRangeValueProvider)_inner).SetValue(value); + public int IsRangeValueProvider() => IsProvider(); + public double RangeValueProvider_GetValue() => RangeValueProvider.Value; + public double RangeValueProvider_GetMinimum() => RangeValueProvider.Minimum; + public double RangeValueProvider_GetMaximum() => RangeValueProvider.Maximum; + public double RangeValueProvider_GetSmallChange() => RangeValueProvider.SmallChange; + public double RangeValueProvider_GetLargeChange() => RangeValueProvider.LargeChange; + public void RangeValueProvider_SetValue(double value) => RangeValueProvider.SetValue(value); - public int IsSelectionItemProvider() => (_inner.GetProvider() is not null).AsComBool(); - public int SelectionItemProvider_IsSelected() => ((ISelectionItemProvider)_inner).IsSelected.AsComBool(); + public int IsSelectionItemProvider() => IsProvider(); + public int SelectionItemProvider_IsSelected() => SelectionItemProvider.IsSelected.AsComBool(); - public int IsToggleProvider() => (_inner.GetProvider() is not null).AsComBool(); - public int ToggleProvider_GetToggleState() => (int)((IToggleProvider)_inner).ToggleState; - public void ToggleProvider_Toggle() => ((IToggleProvider)_inner).Toggle(); + public int IsToggleProvider() => IsProvider(); + public int ToggleProvider_GetToggleState() => (int)ToggleProvider.ToggleState; + public void ToggleProvider_Toggle() => ToggleProvider.Toggle(); - public int IsValueProvider() => (_inner.GetProvider() is not null).AsComBool(); - public IAvnString ValueProvider_GetValue() => ((IValueProvider)_inner).Value.ToAvnString(); - public void ValueProvider_SetValue(string value) => ((IValueProvider)_inner).SetValue(value); + public int IsValueProvider() => IsProvider(); + public IAvnString ValueProvider_GetValue() => ValueProvider.Value.ToAvnString(); + public void ValueProvider_SetValue(string value) => ValueProvider.SetValue(value); [return: NotNullIfNotNull("peer")] public static AvnAutomationPeer? Wrap(AutomationPeer? peer) { return peer is null ? null : s_wrappers.GetValue(peer, x => new(peer)); } + + private T GetProvider() + { + return _inner.GetProvider() ?? throw new InvalidOperationException( + $"The peer {_inner} does not implement {typeof(T)}."); + } + + private int IsProvider() => (_inner.GetProvider() is not null).AsComBool(); } internal class AvnAutomationPeerArray : NativeCallbackBase, IAvnAutomationPeerArray From 6b68a8e5c6480edb095c33d0fd744e01ef2c4637 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 19:09:33 +0200 Subject: [PATCH 353/391] More fixing of provider resolution. Found a few more places that were doing casts instead of calling `GetProvider()`. --- .../Automation/Peers/ItemsControlAutomationPeer.cs | 2 +- .../Automation/Peers/ListItemAutomationPeer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs index db16bf0a53..64727c43c5 100644 --- a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs @@ -28,7 +28,7 @@ namespace Avalonia.Automation.Peers if (!_searchedForScrollable) { if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable) - _scroller = GetOrCreate(scrollable) as IScrollProvider; + _scroller = GetOrCreate(scrollable).GetProvider(); _searchedForScrollable = true; } diff --git a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs index aea91b5e26..dab8c45567 100644 --- a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs @@ -22,7 +22,7 @@ namespace Avalonia.Automation.Peers if (Owner.Parent is Control parent) { var parentPeer = GetOrCreate(parent); - return parentPeer as ISelectionProvider; + return parentPeer.GetProvider(); } return null; From 8ffbb2a214150830c278aff0c60d45d196594548 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Jul 2023 19:52:36 +0200 Subject: [PATCH 354/391] More fixing of provider resolution. Arrgh! Forgot to save the file. --- src/Avalonia.Native/AvnAutomationPeer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 8069ac76bf..76cae2684f 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -62,7 +62,7 @@ namespace Avalonia.Native var peer = _inner; var parent = peer.GetParent(); - while (peer is not IRootProvider && parent is not null) + while (peer.GetProvider() is null && parent is not null) { peer = parent; parent = peer.GetParent(); From 02789d2d48e442997d70a5eba024d02094fcb554 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 00:19:37 +0200 Subject: [PATCH 355/391] Revert "Allow embedded root automation peers." This reverts commit 0e7b8f6f4589195ae35e933c64f89f9478832fb1. The code is in the wrong place. --- .../Avalonia.Win32/Automation/RootAutomationNode.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 0a73c8bc7b..739f0ac251 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -33,7 +33,7 @@ namespace Avalonia.Win32.Automation return null; var p = WindowImpl.PointToClient(new PixelPoint((int)x, (int)y)); - var found = InvokeSync(() => GetPeerFromPoint(p)); + var found = InvokeSync(() => Peer.GetPeerFromPoint(p)); var result = GetOrCreate(found) as IRawElementProviderFragment; return result; } @@ -101,15 +101,5 @@ namespace Avalonia.Win32.Automation return result; } } - - private AutomationPeer? GetPeerFromPoint(Point p) - { - var hit = Peer.GetPeerFromPoint(p); - - while (hit != Peer && hit?.GetProvider() is { } embeddedRoot) - hit = embeddedRoot.GetPeerFromPoint(p); - - return hit; - } } } From 0c7c315a10c114427e7a6514bf37949aeaf34122 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 00:23:19 +0200 Subject: [PATCH 356/391] Hit-test embedded root automation peers. --- .../Peers/WindowBaseAutomationPeer.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs index 9ec65592fa..3786ba32c7 100644 --- a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Globalization; using Avalonia.Automation.Provider; using Avalonia.Controls; using Avalonia.Input; @@ -32,7 +33,21 @@ namespace Avalonia.Automation.Peers public AutomationPeer? GetPeerFromPoint(Point p) { var hit = Owner.GetVisualAt(p)?.FindAncestorOfType(includeSelf: true); - return hit is object ? GetOrCreate(hit) : null; + + if (hit is null) + return null; + + var peer = GetOrCreate(hit); + + while (peer != this && peer.GetProvider() is { } embedded) + { + var embeddedHit = embedded.GetPeerFromPoint(p); + if (embeddedHit is null) + break; + peer = embeddedHit; + } + + return peer; } protected void StartTrackingFocus() From 47988db9c310e4790187d0a2b6d8061ad2195884 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 26 Jul 2023 16:52:18 -0700 Subject: [PATCH 357/391] Update ExternalConsumers.props --- build/ExternalConsumers.props | 1 + 1 file changed, 1 insertion(+) diff --git a/build/ExternalConsumers.props b/build/ExternalConsumers.props index d79e951330..12c747f3f0 100644 --- a/build/ExternalConsumers.props +++ b/build/ExternalConsumers.props @@ -1,6 +1,7 @@ + From b7fcb141420001364d7a06efced40dfe2d357e93 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 12:53:44 +0200 Subject: [PATCH 358/391] Added IEmbeddedRootProvider. For some reason, on win32 embedded `IRawElementProviderFragmentRoot`s just don't show up, so we need an interface to distinguish between "actual" root peers and "embedded" root peers. Ideally `IRootProvider` and `IEmbeddedRootProvider` would share a common interface but that would be a breaking change. --- .../Peers/WindowBaseAutomationPeer.cs | 2 +- .../Provider/IEmbeddedRootProvider.cs | 33 +++++++++ .../Automation/Provider/IRootProvider.cs | 25 +++++++ .../Automation/AutomationNode.cs | 69 +++++++++++-------- .../Automation/RootAutomationNode.cs | 48 ++----------- 5 files changed, 108 insertions(+), 69 deletions(-) create mode 100644 src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs diff --git a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs index 3786ba32c7..ceb695422d 100644 --- a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs @@ -39,7 +39,7 @@ namespace Avalonia.Automation.Peers var peer = GetOrCreate(hit); - while (peer != this && peer.GetProvider() is { } embedded) + while (peer != this && peer.GetProvider() is { } embedded) { var embeddedHit = embedded.GetPeerFromPoint(p); if (embeddedHit is null) diff --git a/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs new file mode 100644 index 0000000000..1b1caef182 --- /dev/null +++ b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs @@ -0,0 +1,33 @@ +using System; +using Avalonia.Automation.Peers; + +namespace Avalonia.Automation.Provider +{ + /// + /// Exposure methods and properties to support UI Automation client access to the root of an + /// automation tree hosted by another UI framework. + /// + /// + /// This interface is implemented by the class, and can be used + /// to embed an automation tree from a 3rd party UI framework that wishes to use Avalonia's + /// automation support. + /// + public interface IEmbeddedRootProvider + { + /// + /// Gets the currently focused element. + /// + AutomationPeer? GetFocus(); + + /// + /// Gets the element at the specified point, expressed in top-level coordinates. + /// + /// The point. + AutomationPeer? GetPeerFromPoint(Point p); + + /// + /// Raised by the automation peer when the focus changes. + /// + event EventHandler? FocusChanged; + } +} diff --git a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs index ce38059559..6a266da5c5 100644 --- a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs +++ b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs @@ -4,11 +4,36 @@ using Avalonia.Platform; namespace Avalonia.Automation.Provider { + /// + /// Exposes methods and properties to support UI Automation client access to the root of an + /// automation tree. + /// + /// + /// This interface is implemented by the class, and should only + /// be implemented on true root elements, such as Windows. To embed an automation tree, use + /// instead. + /// public interface IRootProvider { + /// + /// Gets the platform implementation of the TopLevel for the element. + /// ITopLevelImpl? PlatformImpl { get; } + + /// + /// Gets the currently focused element. + /// AutomationPeer? GetFocus(); + + /// + /// Gets the element at the specified point, expressed in top-level coordinates. + /// + /// The point. AutomationPeer? GetPeerFromPoint(Point p); + + /// + /// Raised by the automation peer when the focus changes. + /// event EventHandler? FocusChanged; } } diff --git a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs index 3eeedc4b5d..e835c6a57a 100644 --- a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs @@ -54,25 +54,11 @@ namespace Avalonia.Win32.Automation _runtimeId = new int[] { 3, GetHashCode() }; Peer = peer; s_nodes.Add(peer, this); - peer.ChildrenChanged += Peer_ChildrenChanged; - peer.PropertyChanged += Peer_PropertyChanged; - } - - private void Peer_ChildrenChanged(object? sender, EventArgs e) - { - ChildrenChanged(); - } + peer.ChildrenChanged += OnPeerChildrenChanged; + peer.PropertyChanged += OnPeerPropertyChanged; - private void Peer_PropertyChanged(object? sender, AutomationPropertyChangedEventArgs e) - { - if (s_propertyMap.TryGetValue(e.Property, out var id)) - { - UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent( - this, - (int)id, - e.OldValue as IConvertible, - e.NewValue as IConvertible); - } + if (Peer.GetProvider() is { } embeddedRoot) + embeddedRoot.FocusChanged += OnEmbeddedRootFocusChanged; } public AutomationPeer Peer { get; protected set; } @@ -95,15 +81,6 @@ namespace Avalonia.Win32.Automation public virtual IRawElementProviderSimple? HostRawElementProvider => null; public ProviderOptions ProviderOptions => ProviderOptions.ServerSideProvider; - public void ChildrenChanged() - { - UiaCoreProviderApi.UiaRaiseStructureChangedEvent( - this, - StructureChangeType.ChildrenInvalidated, - null, - 0); - } - [return: MarshalAs(UnmanagedType.IUnknown)] public virtual object? GetPatternProvider(int patternId) { @@ -250,6 +227,21 @@ namespace Avalonia.Win32.Automation throw new NotSupportedException(); } + protected void RaiseChildrenChanged() + { + UiaCoreProviderApi.UiaRaiseStructureChangedEvent( + this, + StructureChangeType.ChildrenInvalidated, + null, + 0); + } + + protected void RaiseFocusChanged(AutomationNode? focused) + { + UiaCoreProviderApi.UiaRaiseAutomationEvent( + focused, + (int)UiaEventId.AutomationFocusChanged); + } private AutomationNode? GetRoot() { @@ -267,6 +259,29 @@ namespace Avalonia.Win32.Automation return peer is object ? GetOrCreate(peer) : null; } + private void OnPeerChildrenChanged(object? sender, EventArgs e) + { + RaiseChildrenChanged(); + } + + private void OnPeerPropertyChanged(object? sender, AutomationPropertyChangedEventArgs e) + { + if (s_propertyMap.TryGetValue(e.Property, out var id)) + { + UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent( + this, + (int)id, + e.OldValue as IConvertible, + e.NewValue as IConvertible); + } + } + + private void OnEmbeddedRootFocusChanged(object? sender, EventArgs e) + { + if (Peer.GetProvider() is { } embeddedRoot) + RaiseFocusChanged(GetOrCreate(embeddedRoot.GetFocus())); + } + private static AutomationNode Create(AutomationPeer peer) { return peer.GetProvider() is object ? diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 739f0ac251..7334186c80 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -9,18 +9,14 @@ using Avalonia.Win32.Interop.Automation; namespace Avalonia.Win32.Automation { [RequiresUnreferencedCode("Requires .NET COM interop")] - internal class RootAutomationNode : AutomationNode, - IRawElementProviderFragmentRoot, - IRawElementProviderAdviseEvents + internal class RootAutomationNode : AutomationNode, IRawElementProviderFragmentRoot { - private int _raiseFocusChanged; - public RootAutomationNode(AutomationPeer peer) : base(peer) { Peer = base.Peer.GetProvider() ?? throw new AvaloniaInternalException( "Attempt to create RootAutomationNode from peer which does not implement IRootProvider."); - Peer.FocusChanged += FocusChanged; + Peer.FocusChanged += OnRootFocusChanged; } public override IRawElementProviderFragmentRoot? FragmentRoot => this; @@ -44,41 +40,6 @@ namespace Avalonia.Win32.Automation return GetOrCreate(focus); } - void IRawElementProviderAdviseEvents.AdviseEventAdded(int eventId, int[] properties) - { - switch ((UiaEventId)eventId) - { - case UiaEventId.AutomationFocusChanged: - ++_raiseFocusChanged; - break; - } - } - - void IRawElementProviderAdviseEvents.AdviseEventRemoved(int eventId, int[] properties) - { - switch ((UiaEventId)eventId) - { - case UiaEventId.AutomationFocusChanged: - --_raiseFocusChanged; - break; - } - } - - protected void RaiseFocusChanged(AutomationNode? focused) - { - if (_raiseFocusChanged > 0) - { - UiaCoreProviderApi.UiaRaiseAutomationEvent( - focused, - (int)UiaEventId.AutomationFocusChanged); - } - } - - public void FocusChanged(object? sender, EventArgs e) - { - RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); - } - public Rect ToScreen(Rect rect) { if (WindowImpl is null) @@ -101,5 +62,10 @@ namespace Avalonia.Win32.Automation return result; } } + + private void OnRootFocusChanged(object? sender, EventArgs e) + { + RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); + } } } From 4fc2a0dfe799c7d944ad7bd114185cca362cd7a6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 17:04:45 +0200 Subject: [PATCH 359/391] Add IEmbeddedRootProvider to AvnAutomationPeer. --- src/Avalonia.Native/AvnAutomationPeer.cs | 29 +++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 76cae2684f..5933dc6c92 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -72,10 +72,11 @@ namespace Avalonia.Native } } - private IRootProvider RootProvider => GetProvider(); + private IEmbeddedRootProvider EmbeddedRootProvider => GetProvider(); private IExpandCollapseProvider ExpandCollapseProvider => GetProvider(); private IInvokeProvider InvokeProvider => GetProvider(); private IRangeValueProvider RangeValueProvider => GetProvider(); + private IRootProvider RootProvider => GetProvider(); private ISelectionItemProvider SelectionItemProvider => GetProvider(); private IToggleProvider ToggleProvider => GetProvider(); private IValueProvider ValueProvider => GetProvider(); @@ -106,6 +107,32 @@ namespace Avalonia.Native return Wrap(result); } + + public int IsEmbeddedRootProvider() => IsProvider(); + + public IAvnAutomationPeer? EmbeddedRootProvider_GetFocus() => Wrap(EmbeddedRootProvider.GetFocus()); + + public IAvnAutomationPeer? EmbeddedRootProvider_GetPeerFromPoint(AvnPoint point) + { + var result = EmbeddedRootProvider.GetPeerFromPoint(point.ToAvaloniaPoint()); + + if (result is null) + return null; + + // The OSX accessibility APIs expect non-ignored elements when hit-testing. + while (!result.IsControlElement()) + { + var parent = result.GetParent(); + + if (parent is not null) + result = parent; + else + break; + } + + return Wrap(result); + } + public int IsExpandCollapseProvider() => IsProvider(); public int ExpandCollapseProvider_GetIsExpanded() => ExpandCollapseProvider.ExpandCollapseState switch From c1645ca31ae595a1bed83413aba59020d011d24c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 19:37:37 +0200 Subject: [PATCH 360/391] Allow an AutomationPeer to override its visual root. This is needed for example when a UI framework hosts a peer in the automation tree of a main window whose control is actually hosted in a popup. It allows the bounding rectangle to be calculated correctly in that case. s --- .../Automation/Peers/AutomationPeer.cs | 23 ++++++++++++++++++- .../Automation/AutomationNode.cs | 16 +++---------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index 3d3fe35d29..a264909ba6 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -115,9 +115,14 @@ namespace Avalonia.Automation.Peers /// /// Gets the that is the parent of this . /// - /// public AutomationPeer? GetParent() => GetParentCore(); + /// + /// Gets the that is the root of this 's + /// visual tree. + /// + public AutomationPeer? GetVisualRoot() => GetVisualRootCore(); + /// /// Gets a value that indicates whether the element that is associated with this automation /// peer currently has keyboard focus. @@ -247,6 +252,22 @@ namespace Avalonia.Automation.Peers return GetAutomationControlTypeCore(); } + protected virtual AutomationPeer? GetVisualRootCore() + { + var parent = GetParent(); + + while (parent != null) + { + var nextParent = parent.GetParent(); + if (nextParent == null) + return parent; + parent = nextParent; + } + + return null; + } + + protected virtual bool IsContentElementOverrideCore() { return IsControlElement() && IsContentElementCore(); diff --git a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs index e835c6a57a..569f7da738 100644 --- a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs @@ -75,7 +75,7 @@ namespace Avalonia.Win32.Automation public virtual IRawElementProviderFragmentRoot? FragmentRoot { - get => InvokeSync(() => GetRoot()) as IRawElementProviderFragmentRoot; + get => InvokeSync(() => GetRoot()); } public virtual IRawElementProviderSimple? HostRawElementProvider => null; @@ -243,20 +243,10 @@ namespace Avalonia.Win32.Automation (int)UiaEventId.AutomationFocusChanged); } - private AutomationNode? GetRoot() + private RootAutomationNode? GetRoot() { Dispatcher.UIThread.VerifyAccess(); - - var peer = Peer; - var parent = peer.GetParent(); - - while (peer.GetProvider() is null && parent is object) - { - peer = parent; - parent = peer.GetParent(); - } - - return peer is object ? GetOrCreate(peer) : null; + return GetOrCreate(Peer.GetVisualRoot()) as RootAutomationNode; } private void OnPeerChildrenChanged(object? sender, EventArgs e) From 651f558b67578971a981cdcba479ecb5783cd74b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jul 2023 19:58:16 +0200 Subject: [PATCH 361/391] Added new members to IAvnAutomationPeer. --- src/Avalonia.Native/AvnAutomationPeer.cs | 1 + src/Avalonia.Native/avn.idl | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 5933dc6c92..d2d93b69a9 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -39,6 +39,7 @@ namespace Avalonia.Native public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy()); public IAvnString Name => _inner.GetName().ToAvnString(); public IAvnAutomationPeer? Parent => Wrap(_inner.GetParent()); + public IAvnAutomationPeer? VisualRoot => Wrap(_inner.GetVisualRoot()); public int HasKeyboardFocus() => _inner.HasKeyboardFocus().AsComBool(); public int IsContentElement() => _inner.IsContentElement().AsComBool(); diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index bc372bbcb5..0911e5ffff 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -921,6 +921,7 @@ interface IAvnAutomationPeer : IUnknown IAvnAutomationPeer* GetLabeledBy(); IAvnString* GetName(); IAvnAutomationPeer* GetParent(); + IAvnAutomationPeer* GetVisualRoot(); bool HasKeyboardFocus(); bool IsContentElement(); bool IsControlElement(); @@ -935,7 +936,11 @@ interface IAvnAutomationPeer : IUnknown IAvnWindowBase* RootProvider_GetWindow(); IAvnAutomationPeer* RootProvider_GetFocus(); IAvnAutomationPeer* RootProvider_GetPeerFromPoint(AvnPoint point); - + + bool IsEmbeddedRootProvider(); + IAvnAutomationPeer* EmbeddedRootProvider_GetFocus(); + IAvnAutomationPeer* EmbeddedRootProvider_GetPeerFromPoint(AvnPoint point); + bool IsExpandCollapseProvider(); bool ExpandCollapseProvider_GetIsExpanded(); bool ExpandCollapseProvider_GetShowsMenu(); From 1b2d3948d07da0c6569b90b7634d58fcba2d3bd2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Jul 2023 00:41:16 +0200 Subject: [PATCH 362/391] Fix accessibilityWindow. Seems it was broken before and always would have returned null. --- native/Avalonia.Native/src/OSX/automation.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 4b325a092d..9fe0ff3c60 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -291,8 +291,8 @@ private: - (id)accessibilityWindow { - id topLevel = [self accessibilityTopLevelUIElement]; - return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil; + auto rootPeer = _peer->GetVisualRoot(); + return [AvnAccessibilityElement acquire:rootPeer]; } - (BOOL)isAccessibilityExpanded From 953ec9b3ef595336dfbd7ba397c8ef7305635218 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 28 Jul 2023 07:38:17 +0300 Subject: [PATCH 363/391] Remove BypassLogicalChildrenManagement and use OnPropertyChanged --- src/Avalonia.Controls/ContentControl.cs | 30 ++++++------------- .../TransitioningContentControl.cs | 8 ++--- .../TransitioningContentControlTests.cs | 9 ++++-- 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index 47f7598900..b278942f84 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -40,11 +40,6 @@ namespace Avalonia.Controls public static readonly StyledProperty VerticalContentAlignmentProperty = AvaloniaProperty.Register(nameof(VerticalContentAlignment)); - static ContentControl() - { - ContentProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e)); - } - /// /// Gets or sets the content to display. /// @@ -95,22 +90,20 @@ namespace Avalonia.Controls /// IAvaloniaList IContentPresenterHost.LogicalChildren => LogicalChildren; - /// - /// Determine whether manage his LogicalChildren - ///(default behavior) himself, or leaves the management to the inherited control. - /// - /// - /// The default value is false, So the manages itself, - /// if you want to bypass this behavior and manage LogicalChildren yourself, set - /// the to true. - /// - protected virtual bool BypassLogicalChildrenManagement => false; - /// bool IContentPresenterHost.RegisterContentPresenter(ContentPresenter presenter) { return RegisterContentPresenter(presenter); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ContentProperty) + { + ContentChanged(change); + } + } /// /// Called when an is registered with the control. @@ -129,11 +122,6 @@ namespace Avalonia.Controls private void ContentChanged(AvaloniaPropertyChangedEventArgs e) { - if (BypassLogicalChildrenManagement) - { - return; - } - if (e.OldValue is ILogical oldChild) { LogicalChildren.Remove(oldChild); diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs index c113afde17..0b04866d64 100644 --- a/src/Avalonia.Controls/TransitioningContentControl.cs +++ b/src/Avalonia.Controls/TransitioningContentControl.cs @@ -36,9 +36,6 @@ public class TransitioningContentControl : ContentControl set => SetValue(PageTransitionProperty, value); } - /// - protected override bool BypassLogicalChildrenManagement => true; - protected override Size ArrangeOverride(Size finalSize) { var result = base.ArrangeOverride(finalSize); @@ -101,12 +98,13 @@ public class TransitioningContentControl : ContentControl protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - base.OnPropertyChanged(change); - if (change.Property == ContentProperty) { UpdateContent(true); + return; } + + base.OnPropertyChanged(change); } private void UpdateContent(bool withTransition) diff --git a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs index 210b24b3ad..47e53676d8 100644 --- a/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs @@ -184,7 +184,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Logical_Children_Dont_Duplicated() + public void Logical_Children_Should_Not_Be_Duplicated() { using var app = Start(); var (target, transition) = CreateTarget(""); @@ -194,18 +194,21 @@ namespace Avalonia.Controls.UnitTests target.Content = childControl; Assert.Equal(1, target.LogicalChildren.Count); + Assert.Equal(target.LogicalChildren[0], childControl); } [Fact] - public void First_Presenter_Register_TCC_As_Host() + public void First_Presenter_Should_Register_TCC_As_His_Host() { using var app = Start(); var (target, transition) = CreateTarget(""); + target.PageTransition = null; var childControl = new Control(); target.Presenter!.Content = childControl; - Assert.Contains(childControl, target.LogicalChildren); + Assert.Equal(1, target.LogicalChildren.Count); + Assert.Equal(target.LogicalChildren[0], childControl); } private static IDisposable Start() From b780c77260fa806bfc288e41ad46070785f758f9 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 28 Jul 2023 07:39:28 +0300 Subject: [PATCH 364/391] typo --- src/Avalonia.Controls/ContentControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index b278942f84..3564ce1faa 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -95,6 +95,7 @@ namespace Avalonia.Controls { return RegisterContentPresenter(presenter); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); From 608c251fb27bd37b5ceb1224b62b53954cce67d7 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Jul 2023 06:32:34 +0000 Subject: [PATCH 365/391] Make the animation display complete --- src/Avalonia.Base/Animation/CrossFade.cs | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Avalonia.Base/Animation/CrossFade.cs b/src/Avalonia.Base/Animation/CrossFade.cs index 640d6456a3..d598db870f 100644 --- a/src/Avalonia.Base/Animation/CrossFade.cs +++ b/src/Avalonia.Base/Animation/CrossFade.cs @@ -35,6 +35,18 @@ namespace Avalonia.Animation { Children = { + new KeyFrame() + { + Setters = + { + new Setter + { + Property = Visual.OpacityProperty, + Value = 1d + } + }, + Cue = new Cue(0d) + }, new KeyFrame() { Setters = @@ -54,6 +66,18 @@ namespace Avalonia.Animation { Children = { + new KeyFrame() + { + Setters = + { + new Setter + { + Property = Visual.OpacityProperty, + Value = 0d + } + }, + Cue = new Cue(0d) + }, new KeyFrame() { Setters = @@ -117,11 +141,13 @@ namespace Avalonia.Animation if (from != null) { + from.Opacity = 0f; tasks.Add(_fadeOutAnimation.RunAsync(from, null, cancellationToken)); } if (to != null) { + to.Opacity = 1f; to.IsVisible = true; tasks.Add(_fadeInAnimation.RunAsync(to, null, cancellationToken)); } From 90e3760c0f8e1c346a4b9d2d0f5a17f0fadbef9d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Jul 2023 09:19:42 +0200 Subject: [PATCH 366/391] Use interface instead of concrete class. --- src/Avalonia.Native/AvnAutomationPeer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index d2d93b69a9..af4958b02f 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -22,8 +22,8 @@ namespace Avalonia.Native { _inner = inner; _inner.ChildrenChanged += (_, _) => Node?.ChildrenChanged(); - if (inner is WindowBaseAutomationPeer window) - window.FocusChanged += (_, _) => Node?.FocusChanged(); + if (inner is IRootProvider root) + root.FocusChanged += (_, _) => Node?.FocusChanged(); } ~AvnAutomationPeer() => Node?.Dispose(); From ae82bc1b8f39362dbfec7412c2a5381a92998359 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Jul 2023 11:19:57 +0200 Subject: [PATCH 367/391] Fix GetVisualRootCore. Needs to check for `IRootProvider`. Fixes integration tests on Windows. --- .../Automation/Peers/AutomationPeer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index a264909ba6..fb7cdd87ed 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Automation.Provider; namespace Avalonia.Automation.Peers { @@ -254,17 +255,16 @@ namespace Avalonia.Automation.Peers protected virtual AutomationPeer? GetVisualRootCore() { - var parent = GetParent(); + var peer = this; + var parent = peer.GetParent(); - while (parent != null) + while (peer.GetProvider() is null && parent is not null) { - var nextParent = parent.GetParent(); - if (nextParent == null) - return parent; - parent = nextParent; + peer = parent; + parent = peer.GetParent(); } - return null; + return peer; } From 6b3db2a3f56231075042983d01272605974a6dee Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Jul 2023 13:15:30 +0200 Subject: [PATCH 368/391] Shortcut finding the visual root for controls. --- .../Automation/Peers/ControlAutomationPeer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index c19d887230..69f267a605 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -120,6 +120,13 @@ namespace Avalonia.Automation.Peers return _parent; } + protected override AutomationPeer? GetVisualRootCore() + { + if (Owner.GetVisualRoot() is Control c) + return CreatePeerForElement(c); + return null; + } + /// /// Invalidates the peer's children and causes a re-read from . /// From 55c9da56ebc569e061b7a34b5ae02ea748978ebb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 28 Jul 2023 16:10:13 +0200 Subject: [PATCH 369/391] feat(NumericUpDown): TextAlignment --- .../NumericUpDown/NumericUpDown.cs | 15 +++++++++++++++ .../Controls/NumericUpDown.xaml | 1 + .../Controls/NumericUpDown.xaml | 1 + 3 files changed, 17 insertions(+) diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 30f9d8f380..84772e7789 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -126,6 +126,12 @@ namespace Avalonia.Controls public static readonly StyledProperty VerticalContentAlignmentProperty = ContentControl.VerticalContentAlignmentProperty.AddOwner(); + /// + /// Defines the property + /// + public static readonly StyledProperty TextAlignmentProperty = + TextBox.TextAlignmentProperty.AddOwner(); + private IDisposable? _textBoxTextChangedSubscription; private bool _internalValueSet; @@ -299,6 +305,15 @@ namespace Avalonia.Controls set => SetValue(VerticalContentAlignmentProperty, value); } + /// + /// Gets or sets the of the + /// + public Media.TextAlignment TextAlignment + { + get => GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); + } + /// /// Initializes new instance of class. /// diff --git a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml index 1f84ee664c..a470eb1d3b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml @@ -57,6 +57,7 @@ VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Text="{TemplateBinding Text}" + TextAlignment="{TemplateBinding TextAlignment}" AcceptsReturn="False" TextWrapping="NoWrap" /> diff --git a/src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml b/src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml index 4ce6a20dc6..ff4be12fda 100644 --- a/src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml +++ b/src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml @@ -31,6 +31,7 @@ DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" IsReadOnly="{TemplateBinding IsReadOnly}" Text="{TemplateBinding Text}" + TextAlignment="{TemplateBinding TextAlignment}" TextWrapping="NoWrap" Watermark="{TemplateBinding Watermark}" /> From e1ac9be7f1ae659a44c449e692946977c330af29 Mon Sep 17 00:00:00 2001 From: Will Kennedy Date: Sat, 29 Jul 2023 15:14:50 -0400 Subject: [PATCH 370/391] Catch errors so that for loop can register all names --- .../DBusIme/DBusTextInputMethodBase.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs index 9ce6604594..b897d52204 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs @@ -62,9 +62,15 @@ namespace Avalonia.FreeDesktop.DBusIme foreach (var name in _knownNames) { var dbus = new OrgFreedesktopDBus(Connection, "org.freedesktop.DBus", "/org/freedesktop/DBus"); - _disposables.Add(await dbus.WatchNameOwnerChangedAsync(OnNameChange)); - var nameOwner = await dbus.GetNameOwnerAsync(name); - OnNameChange(null, (name, null, nameOwner)); + try + { + _disposables.Add(await dbus.WatchNameOwnerChangedAsync(OnNameChange)); + var nameOwner = await dbus.GetNameOwnerAsync(name); + OnNameChange(null, (name, null, nameOwner)); + } + catch (DBusException) + { + } } } From 9ba9414c223216e0d36ceef25ae34c12e252bb81 Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Thu, 27 Jul 2023 03:24:57 +0300 Subject: [PATCH 371/391] unit test for issue added --- .../HotKeyedControlsTests.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/Avalonia.Controls.UnitTests/HotKeyedControlsTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/HotKeyedControlsTests.cs b/tests/Avalonia.Controls.UnitTests/HotKeyedControlsTests.cs new file mode 100644 index 0000000000..55a3f0d5d4 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/HotKeyedControlsTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Windows.Input; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.LogicalTree; +using Avalonia.Platform; +using Avalonia.UnitTests; +using Moq; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + internal class HotKeyedTextBox : TextBox, ICommandSource + { + private class DelegateCommand : ICommand + { + private readonly Action _action; + public DelegateCommand(Action action) => _action = action; + public event EventHandler CanExecuteChanged { add { } remove { } } + public bool CanExecute(object parameter) => true; + public void Execute(object parameter) => _action(); + } + + public static readonly StyledProperty HotKeyProperty = + HotKeyManager.HotKeyProperty.AddOwner(); + + private KeyGesture _hotkey; + + public KeyGesture HotKey + { + get => GetValue(HotKeyProperty); + set => SetValue(HotKeyProperty, value); + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (_hotkey != null) + { + this.SetValue(HotKeyProperty, _hotkey); + } + + base.OnAttachedToLogicalTree(e); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (this.HotKey != null) + { + _hotkey = this.HotKey; + this.SetValue(HotKeyProperty, null); + } + + base.OnDetachedFromLogicalTree(e); + } + + public void CanExecuteChanged(object sender, EventArgs e) + { + } + + protected override Type StyleKeyOverride => typeof(TextBox); + + public ICommand Command => _command; + + public object CommandParameter => null; + + private readonly DelegateCommand _command; + + public HotKeyedTextBox() + { + _command = new DelegateCommand(() => Focus()); + } + } + + public class HotKeyedControlsTests + { + private static Window PreparedWindow(object content = null) + { + var platform = AvaloniaLocator.Current.GetRequiredService(); + var windowImpl = Mock.Get(platform.CreateWindow()); + windowImpl.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor()); + var w = new Window(windowImpl.Object) { Content = content }; + w.ApplyTemplate(); + return w; + } + + private static IDisposable CreateServicesWithFocus() + { + return UnitTestApplication.Start( + TestServices.StyledWindow.With( + windowingPlatform: new MockWindowingPlatform( + null, + window => MockWindowingPlatform.CreatePopupMock(window).Object), + focusManager: new FocusManager(), + keyboardDevice: () => new KeyboardDevice())); + } + + [Fact] + public void HotKeyedTextBox_Focus_Performed_On_Hotkey() + { + using var _ = CreateServicesWithFocus(); + + var keyboardDevice = new KeyboardDevice(); + var hotKeyedTextBox = new HotKeyedTextBox { HotKey = new KeyGesture(Key.F, KeyModifiers.Control) }; + var root = PreparedWindow(); + root.Content = hotKeyedTextBox; + root.Show(); + + Assert.False(hotKeyedTextBox.IsFocused); + + keyboardDevice.ProcessRawEvent( + new RawKeyEventArgs( + keyboardDevice, + 0, + root, + RawKeyEventType.KeyDown, + Key.F, + RawInputModifiers.Control)); + + Assert.True(hotKeyedTextBox.IsFocused); + } + } +} From a5c7b1d35842b40592bc04e5f0565243f1ce6e05 Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Thu, 27 Jul 2023 03:29:27 +0300 Subject: [PATCH 372/391] issue fix --- src/Avalonia.Controls/HotkeyManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index de753f0bd0..6ad4a8cc76 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -149,10 +149,10 @@ namespace Avalonia.Controls return; var control = args.Sender as Control; - if (control is not IClickableControl) + if (control is not IClickableControl and not ICommandSource) { Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?.Log(control, - $"The element {args.Sender.GetType().Name} does not implement IClickableControl and does not support binding a HotKey ({args.NewValue})."); + $"The element {args.Sender.GetType().Name} does not implement IClickableControl nor ICommandSource and does not support binding a HotKey ({args.NewValue})."); return; } From d0b1389ee888d3ae2bac4a12dc04386d703e9062 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 2 Aug 2023 12:51:48 +0600 Subject: [PATCH 373/391] override_redirect and Handle fixes for X11Window --- src/Avalonia.X11/X11Window.cs | 36 +++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index be2d754819..a2019c276b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -17,7 +17,6 @@ using Avalonia.OpenGL; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Platform.Storage; -using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Avalonia.Threading; using Avalonia.X11.Glx; @@ -36,6 +35,7 @@ namespace Avalonia.X11 { private readonly AvaloniaX11Platform _platform; private readonly bool _popup; + private readonly bool _overrideRedirect; private readonly X11Info _x11; private XConfigureEvent? _configure; private PixelPoint? _configurePoint; @@ -72,10 +72,11 @@ namespace Avalonia.X11 WaitPaint } - public X11Window(AvaloniaX11Platform platform, IWindowImpl? popupParent) + public X11Window(AvaloniaX11Platform platform, IWindowImpl? popupParent, bool overrideRedirect = false) { _platform = platform; _popup = popupParent != null; + _overrideRedirect = _popup || overrideRedirect; _x11 = platform.Info; _mouse = new MouseDevice(); _touch = new TouchDevice(); @@ -92,7 +93,7 @@ namespace Avalonia.X11 | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; - if (_popup) + if (_overrideRedirect) { attr.override_redirect = 1; valueMask |= SetWindowValuemask.OverrideRedirect; @@ -155,7 +156,7 @@ namespace Avalonia.X11 else _renderHandle = _handle; - Handle = new SurfacePlatformHandle(this); + Handle = new PlatformHandle(_handle, "XID"); _realSize = new PixelSize(defaultWidth, defaultHeight); platform.Windows[_handle] = OnEvent; XEventMask ignoredMask = XEventMask.SubstructureRedirectMask @@ -165,15 +166,18 @@ namespace Avalonia.X11 ignoredMask |= platform.XI2.AddWindow(_handle, this); var mask = new IntPtr(0xffffff ^ (int)ignoredMask); XSelectInput(_x11.Display, _handle, mask); - var protocols = new[] + if (!_overrideRedirect) { - _x11.Atoms.WM_DELETE_WINDOW - }; - XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length); - XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM, - 32, PropertyMode.Replace, new[] {_x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL}, 1); + var protocols = new[] + { + _x11.Atoms.WM_DELETE_WINDOW + }; + XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length); + XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM, + 32, PropertyMode.Replace, new[] { _x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL }, 1); - SetWmClass(_platform.Options.WmClass); + SetWmClass(_platform.Options.WmClass); + } var surfaces = new List { @@ -187,7 +191,7 @@ namespace Avalonia.X11 if (glx != null) surfaces.Insert(0, new GlxGlPlatformSurface(new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle))); - surfaces.Add(Handle); + surfaces.Add(new SurfacePlatformHandle(this)); Surfaces = surfaces.ToArray(); UpdateMotifHints(); @@ -257,6 +261,8 @@ namespace Avalonia.X11 private void UpdateMotifHints() { + if(_overrideRedirect) + return; var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize; var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border | @@ -286,6 +292,8 @@ namespace Avalonia.X11 private void UpdateSizeHints(PixelSize? preResize) { + if(_overrideRedirect) + return; var min = _minMaxSize.minSize; var max = _minMaxSize.maxSize; @@ -507,7 +515,7 @@ namespace Avalonia.X11 } UpdateImePosition(); - if (changedSize && !updatedSizeViaScaling && !_popup) + if (changedSize && !updatedSizeViaScaling && !_overrideRedirect) Resized?.Invoke(ClientSize, WindowResizeReason.Unspecified); }, DispatcherPriority.AsyncRenderTargetResize); @@ -984,7 +992,7 @@ namespace Avalonia.X11 XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize); XFlush(_x11.Display); - if (force || !_wasMappedAtLeastOnce || (_popup && needImmediatePopupResize)) + if (force || !_wasMappedAtLeastOnce || (_overrideRedirect && needImmediatePopupResize)) { _realSize = pixelSize; Resized?.Invoke(ClientSize, reason); From ec21caf7fe0419b2284e34d63cd13da6de9460dc Mon Sep 17 00:00:00 2001 From: 3dfxuser <3dfxuser@gmail.com> Date: Wed, 2 Aug 2023 11:43:53 +0300 Subject: [PATCH 374/391] Add null check for TextInputMethodClient in OnSelectionChanged() method --- src/Android/Avalonia.Android/AndroidInputMethod.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 7d5130cf5d..f708d6936c 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -113,6 +113,11 @@ namespace Avalonia.Android private void OnSelectionChanged() { + if (Client is null) + { + return; + } + var selection = Client.Selection; _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End); From f253b614b3dcf3c8acedf0614cbbfd6b6287c105 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 1 Aug 2023 18:12:53 +0200 Subject: [PATCH 375/391] Generate pdb for ref assemblies --- nukebuild/Build.cs | 5 +- nukebuild/RefAssemblyGenerator.cs | 109 ++++++++++++++++++------------ 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index bbfc28aa9f..f8fbf64e83 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -279,8 +279,9 @@ partial class Build : NukeBuild if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, new NumergeNukeLogger())) throw new Exception("Package merge failed"); - RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." + - Parameters.Version + ".nupkg"); + RefAssemblyGenerator.GenerateRefAsmsInPackage( + Parameters.NugetRoot / $"Avalonia.{Parameters.Version}.nupkg", + Parameters.NugetRoot / $"Avalonia.{Parameters.Version}.snupkg"); }); Target ValidateApiDiff => _ => _ diff --git a/nukebuild/RefAssemblyGenerator.cs b/nukebuild/RefAssemblyGenerator.cs index 54e428c442..e93070e2f0 100644 --- a/nukebuild/RefAssemblyGenerator.cs +++ b/nukebuild/RefAssemblyGenerator.cs @@ -1,8 +1,10 @@ +#nullable enable + +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; -using ILRepacking; using Mono.Cecil; using Mono.Cecil.Cil; @@ -10,8 +12,8 @@ public class RefAssemblyGenerator { class Resolver : DefaultAssemblyResolver, IAssemblyResolver { - private readonly string _dir; - Dictionary _cache = new(); + readonly string _dir; + readonly Dictionary _cache = new(); public Resolver(string dir) { @@ -31,17 +33,17 @@ public class RefAssemblyGenerator public static void PatchRefAssembly(string file) { - var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk"); + var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk")!; var snk = new byte[reader.Length]; - reader.Read(snk, 0, snk.Length); + reader.ReadExactly(snk, 0, snk.Length); var def = AssemblyDefinition.ReadAssembly(file, new ReaderParameters { ReadWrite = true, InMemory = true, ReadSymbols = true, - SymbolReaderProvider = new DefaultSymbolReaderProvider(false), - AssemblyResolver = new Resolver(Path.GetDirectoryName(file)) + SymbolReaderProvider = new DefaultSymbolReaderProvider(throwIfNoSymbol: true), + AssemblyResolver = new Resolver(Path.GetDirectoryName(file)!) }); var obsoleteAttribute = def.MainModule.ImportReference(new TypeReference("System", "ObsoleteAttribute", def.MainModule, @@ -58,7 +60,7 @@ public class RefAssemblyGenerator { StrongNameKeyBlob = snk, WriteSymbols = def.MainModule.HasSymbols, - SymbolWriterProvider = new EmbeddedPortablePdbWriterProvider(), + SymbolWriterProvider = new PortablePdbWriterProvider(), DeterministicMvid = def.MainModule.HasSymbols }); } @@ -146,7 +148,7 @@ public class RefAssemblyGenerator m.Attributes = ((m.Attributes | dflags) ^ dflags) | MethodAttributes.Assembly; } - static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute unstableAttribute) + static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute? unstableAttribute) { if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute")) return; @@ -172,43 +174,66 @@ public class RefAssemblyGenerator }); } - public static void GenerateRefAsmsInPackage(string packagePath) + public static void GenerateRefAsmsInPackage(string mainPackagePath, string symbolsPackagePath) { - using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite), - ZipArchiveMode.Update)) + using var mainArchive = OpenPackage(mainPackagePath); + using var symbolsArchive = OpenPackage(symbolsPackagePath); + + foreach (var entry in mainArchive.Entries + .Where(e => e.FullName.StartsWith("ref/", StringComparison.Ordinal)) + .ToArray()) { - foreach (var entry in archive.Entries.ToList()) - { - if (entry.FullName.StartsWith("ref/")) - entry.Delete(); - } - - foreach (var entry in archive.Entries.ToList()) + entry.Delete(); + } + + foreach (var libEntry in GetLibEntries(mainArchive, ".xml")) + { + var refEntry = mainArchive.CreateEntry("ref/" + libEntry.FullName.Substring(4), CompressionLevel.Optimal); + using var src = libEntry.Open(); + using var dst = refEntry.Open(); + src.CopyTo(dst); + } + + var pdbEntries = GetLibEntries(symbolsArchive, ".pdb").ToDictionary(e => e.FullName); + + var libs = GetLibEntries(mainArchive, ".dll") + .Select(e => (NameParts: e.FullName.Split('/'), Entry: e)) + .Select(e => ( + Tfm: e.NameParts[1], + DllName: e.NameParts[2], + DllEntry: e.Entry, + PdbName: Path.ChangeExtension(e.NameParts[2], ".pdb"), + PdbEntry: pdbEntries.TryGetValue(Path.ChangeExtension(e.Entry.FullName, ".pdb"), out var pdbEntry) ? + pdbEntry : + throw new InvalidOperationException($"Missing symbols for {e.Entry.FullName}"))) + .GroupBy(e => e.Tfm); + + foreach (var tfm in libs) + { + using var _ = Helpers.UseTempDir(out var temp); + + foreach (var lib in tfm) { - if (entry.FullName.StartsWith("lib/") && entry.Name.EndsWith(".xml")) - { - var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4), - CompressionLevel.Optimal); - using (var src = entry.Open()) - using (var dst = newEntry.Open()) - src.CopyTo(dst); - } - } + var extractedDllPath = Path.Combine(temp, lib.DllName); + var extractedPdbPath = Path.Combine(temp, lib.PdbName); + + lib.DllEntry.ExtractToFile(extractedDllPath); + lib.PdbEntry.ExtractToFile(extractedPdbPath); - var libs = archive.Entries.Where(e => e.FullName.StartsWith("lib/") && e.FullName.EndsWith(".dll")) - .Select((e => new { s = e.FullName.Split('/'), e = e })) - .Select(e => new { Tfm = e.s[1], Name = e.s[2], Entry = e.e }) - .GroupBy(x => x.Tfm); - foreach(var tfm in libs) - using (Helpers.UseTempDir(out var temp)) - { - foreach (var l in tfm) - l.Entry.ExtractToFile(Path.Combine(temp, l.Name)); - foreach (var l in tfm) - PatchRefAssembly(Path.Combine(temp, l.Name)); - foreach (var l in tfm) - archive.CreateEntryFromFile(Path.Combine(temp, l.Name), $"ref/{l.Tfm}/{l.Name}"); - } + PatchRefAssembly(extractedDllPath); + + mainArchive.CreateEntryFromFile(extractedDllPath, $"ref/{lib.Tfm}/{lib.DllName}"); + symbolsArchive.CreateEntryFromFile(extractedPdbPath, $"ref/{lib.Tfm}/{lib.PdbName}"); + } } + + static ZipArchive OpenPackage(string packagePath) + => new(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite), ZipArchiveMode.Update); + + static ZipArchiveEntry[] GetLibEntries(ZipArchive archive, string extension) + => archive.Entries + .Where(e => e.FullName.StartsWith("lib/", StringComparison.Ordinal) + && e.FullName.EndsWith(extension, StringComparison.Ordinal)) + .ToArray(); } } From adcaf6a317038033391f3d6a22e4b702118eea1b Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Thu, 3 Aug 2023 22:38:52 +0300 Subject: [PATCH 376/391] fix: try 1 --- src/Avalonia.Controls/TopLevel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 04a5a0e6aa..66e402d642 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -591,6 +591,7 @@ namespace Avalonia.Controls Renderer.SceneInvalidated -= SceneInvalidated; // We need to wait for the renderer to complete any in-flight operations Renderer.Dispose(); + StopRendering(); Debug.Assert(PlatformImpl != null); // The PlatformImpl is completely invalid at this point From b0f6d17c558fd7ece0c3d56d090433087de66871 Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Sat, 5 Aug 2023 00:31:33 +0300 Subject: [PATCH 377/391] unit tests added --- .../WindowDataContextTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/Avalonia.LeakTests/WindowDataContextTests.cs diff --git a/tests/Avalonia.LeakTests/WindowDataContextTests.cs b/tests/Avalonia.LeakTests/WindowDataContextTests.cs new file mode 100644 index 0000000000..239b090515 --- /dev/null +++ b/tests/Avalonia.LeakTests/WindowDataContextTests.cs @@ -0,0 +1,70 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Reactive; +using Avalonia.Threading; +using Avalonia.UnitTests; +using JetBrains.dotMemoryUnit; +using Xunit; +using Xunit.Abstractions; + +namespace Avalonia.LeakTests; + +internal class ViewModelForDisposingTest +{ + ~ViewModelForDisposingTest() { ; } +} + +[DotMemoryUnit(FailIfRunWithoutSupport = false)] +public class WindowDataContextTests +{ + public WindowDataContextTests(ITestOutputHelper atr) + { + DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine); + } + + [Fact] + public void Window_DataContext_Disposed_After_Window_Close_With_Lifetime() + { + static IDisposable Run() + { + var unitTestApp = UnitTestApplication.Start(TestServices.StyledWindow); + var lifetime = new ClassicDesktopStyleApplicationLifetime(); + lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; + var window = new Window { DataContext = new ViewModelForDisposingTest() }; + window.Show(); + window.Close(); + + return Disposable.Create(lifetime, lt => lt.Shutdown()) + .DisposeWith(new CompositeDisposable(lifetime, unitTestApp)); + } + + using var _ = Run(); + // Process all Loaded events to free control reference(s) + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + GC.Collect(); + + dotMemory.Check(m => Assert.Equal(0, + m.GetObjects(o => o.Type.Is()).ObjectsCount)); + } + + [Fact] + public void Window_DataContext_Disposed_After_Window_Close_Without_Lifetime() + { + static void Run() + { + using var _ = UnitTestApplication.Start(TestServices.StyledWindow); + var window = new Window { DataContext = new ViewModelForDisposingTest() }; + window.Show(); + window.Close(); + } + + Run(); + // Process all Loaded events to free control reference(s) + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + GC.Collect(); + + dotMemory.Check(m => Assert.Equal(0, + m.GetObjects(o => o.Type.Is()).ObjectsCount)); + } +} From c418442d1345ee8ea5ed113ae9453bd740088c08 Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Sat, 5 Aug 2023 17:17:57 +0300 Subject: [PATCH 378/391] Revert "unit tests added" This reverts commit b0f6d17c558fd7ece0c3d56d090433087de66871. --- .../WindowDataContextTests.cs | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 tests/Avalonia.LeakTests/WindowDataContextTests.cs diff --git a/tests/Avalonia.LeakTests/WindowDataContextTests.cs b/tests/Avalonia.LeakTests/WindowDataContextTests.cs deleted file mode 100644 index 239b090515..0000000000 --- a/tests/Avalonia.LeakTests/WindowDataContextTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Reactive; -using Avalonia.Threading; -using Avalonia.UnitTests; -using JetBrains.dotMemoryUnit; -using Xunit; -using Xunit.Abstractions; - -namespace Avalonia.LeakTests; - -internal class ViewModelForDisposingTest -{ - ~ViewModelForDisposingTest() { ; } -} - -[DotMemoryUnit(FailIfRunWithoutSupport = false)] -public class WindowDataContextTests -{ - public WindowDataContextTests(ITestOutputHelper atr) - { - DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine); - } - - [Fact] - public void Window_DataContext_Disposed_After_Window_Close_With_Lifetime() - { - static IDisposable Run() - { - var unitTestApp = UnitTestApplication.Start(TestServices.StyledWindow); - var lifetime = new ClassicDesktopStyleApplicationLifetime(); - lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; - var window = new Window { DataContext = new ViewModelForDisposingTest() }; - window.Show(); - window.Close(); - - return Disposable.Create(lifetime, lt => lt.Shutdown()) - .DisposeWith(new CompositeDisposable(lifetime, unitTestApp)); - } - - using var _ = Run(); - // Process all Loaded events to free control reference(s) - Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); - GC.Collect(); - - dotMemory.Check(m => Assert.Equal(0, - m.GetObjects(o => o.Type.Is()).ObjectsCount)); - } - - [Fact] - public void Window_DataContext_Disposed_After_Window_Close_Without_Lifetime() - { - static void Run() - { - using var _ = UnitTestApplication.Start(TestServices.StyledWindow); - var window = new Window { DataContext = new ViewModelForDisposingTest() }; - window.Show(); - window.Close(); - } - - Run(); - // Process all Loaded events to free control reference(s) - Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); - GC.Collect(); - - dotMemory.Check(m => Assert.Equal(0, - m.GetObjects(o => o.Type.Is()).ObjectsCount)); - } -} From ca2ca4ee9fae2e20155da6bd3148dc3e999fe57c Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Sat, 5 Aug 2023 17:20:34 +0300 Subject: [PATCH 379/391] Revert "fix: try 1" This reverts commit adcaf6a317038033391f3d6a22e4b702118eea1b. --- src/Avalonia.Controls/TopLevel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 66e402d642..04a5a0e6aa 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -591,7 +591,6 @@ namespace Avalonia.Controls Renderer.SceneInvalidated -= SceneInvalidated; // We need to wait for the renderer to complete any in-flight operations Renderer.Dispose(); - StopRendering(); Debug.Assert(PlatformImpl != null); // The PlatformImpl is completely invalid at this point From 5b182890f3c3dece03e81c79ae1bdf889e85961c Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Sat, 5 Aug 2023 17:27:31 +0300 Subject: [PATCH 380/391] Added unit tests for non-disposable DataContext issue (#12123) --- tests/Avalonia.LeakTests/DataContextTests.cs | 70 ++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/Avalonia.LeakTests/DataContextTests.cs diff --git a/tests/Avalonia.LeakTests/DataContextTests.cs b/tests/Avalonia.LeakTests/DataContextTests.cs new file mode 100644 index 0000000000..d16f8c1f57 --- /dev/null +++ b/tests/Avalonia.LeakTests/DataContextTests.cs @@ -0,0 +1,70 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Reactive; +using Avalonia.Threading; +using Avalonia.UnitTests; +using JetBrains.dotMemoryUnit; +using Xunit; +using Xunit.Abstractions; + +namespace Avalonia.LeakTests; + +internal class ViewModelForDisposingTest +{ + ~ViewModelForDisposingTest() { ; } +} + +[DotMemoryUnit(FailIfRunWithoutSupport = false)] +public class DataContextTests +{ + public DataContextTests(ITestOutputHelper atr) + { + DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine); + } + + [Fact] + public void Window_DataContext_Disposed_After_Window_Close_With_Lifetime() + { + static IDisposable Run() + { + var unitTestApp = UnitTestApplication.Start(TestServices.StyledWindow); + var lifetime = new ClassicDesktopStyleApplicationLifetime(); + lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; + var window = new Window { DataContext = new ViewModelForDisposingTest() }; + window.Show(); + window.Close(); + + return Disposable.Create(lifetime, lt => lt.Shutdown()) + .DisposeWith(new CompositeDisposable(lifetime, unitTestApp)); + } + + using var _ = Run(); + // Process all Loaded events to free control reference(s) + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + GC.Collect(); + + dotMemory.Check(m => Assert.Equal(0, + m.GetObjects(o => o.Type.Is()).ObjectsCount)); + } + + [Fact] + public void Window_DataContext_Disposed_After_Window_Close_Without_Lifetime() + { + static void Run() + { + using var _ = UnitTestApplication.Start(TestServices.StyledWindow); + var window = new Window { DataContext = new ViewModelForDisposingTest() }; + window.Show(); + window.Close(); + } + + Run(); + // Process all Loaded events to free control reference(s) + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + GC.Collect(); + + dotMemory.Check(m => Assert.Equal(0, + m.GetObjects(o => o.Type.Is()).ObjectsCount)); + } +} From 628455ec3d3291083d76216de9d9b7934d1fcfcd Mon Sep 17 00:00:00 2001 From: flexxxxer Date: Sat, 5 Aug 2023 17:29:17 +0300 Subject: [PATCH 381/391] To Avalonia.Controls.TopLevel.HandleClosed method body was added Avalonia.Controls.TopLevel.StopRendering method call --- src/Avalonia.Controls/TopLevel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 04a5a0e6aa..66e402d642 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -591,6 +591,7 @@ namespace Avalonia.Controls Renderer.SceneInvalidated -= SceneInvalidated; // We need to wait for the renderer to complete any in-flight operations Renderer.Dispose(); + StopRendering(); Debug.Assert(PlatformImpl != null); // The PlatformImpl is completely invalid at this point From 8328cc79e2bbe0d1b038d689a47df1ddea907665 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 8 Aug 2023 15:25:00 +0600 Subject: [PATCH 382/391] Unwrap win32 data object --- src/Windows/Avalonia.Win32/DataObject.cs | 1 + src/Windows/Avalonia.Win32/OleDropTarget.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index a215a0a322..df19cdc329 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -103,6 +103,7 @@ namespace Avalonia.Win32 private IDataObject _wrapped; + public IDataObject Wrapped => _wrapped; public DataObject(IDataObject wrapped) { diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index a81652ffc2..94d744301a 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -217,7 +217,7 @@ namespace Avalonia.Win32 if (MicroComRuntime.TryUnwrapManagedObject(pDataObj) is DataObject dataObject) { - return dataObject; + return dataObject.Wrapped; } return new OleDataObject(pDataObj); } From 2c889d558acf18b6e30714a56dc4158d4d9fe647 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Aug 2023 11:28:10 +0100 Subject: [PATCH 383/391] Fixes #YOUTRACK-HDSW-24 --- packages/Avalonia/Avalonia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 4258572df7..ebfa325067 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all From 11971a0431bdda95581c8f4914c164f87e8acab1 Mon Sep 17 00:00:00 2001 From: adirh Date: Sat, 12 Aug 2023 16:42:50 +0300 Subject: [PATCH 384/391] Fixed memory leaks in ContextMenu.cs --- src/Avalonia.Controls/ContextMenu.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index e3a419d7b3..7fab36d931 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -114,7 +114,6 @@ namespace Avalonia.Controls /// static ContextMenu() { - ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); PlacementProperty.OverrideDefaultValue(PlacementMode.Pointer); ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue(AccessibilityView.Control); @@ -216,16 +215,16 @@ namespace Avalonia.Controls if (e.OldValue is ContextMenu oldMenu) { control.ContextRequested -= ControlContextRequested; + control.AttachedToVisualTree -= ControlOnAttachedToVisualTree; control.DetachedFromVisualTree -= ControlDetachedFromVisualTree; oldMenu._attachedControls?.Remove(control); ((ISetLogicalParent?)oldMenu._popup)?.SetParent(null); } - if (e.NewValue is ContextMenu newMenu) + if (e.NewValue is ContextMenu) { - newMenu._attachedControls ??= new List(); - newMenu._attachedControls.Add(control); control.ContextRequested += ControlContextRequested; + control.AttachedToVisualTree += ControlOnAttachedToVisualTree; control.DetachedFromVisualTree += ControlDetachedFromVisualTree; } } @@ -428,11 +427,20 @@ namespace Avalonia.Controls e.Handled = true; } } + + + private static void ControlOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e) + { + if (sender is Control { ContextMenu: {} contextMenu } control) + { + contextMenu._attachedControls ??= new List(); + contextMenu._attachedControls.Add(control); + } + } private static void ControlDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is Control control - && control.ContextMenu is ContextMenu contextMenu) + if (sender is Control { ContextMenu: { } contextMenu } control) { if (contextMenu._popup?.Parent == control) { @@ -440,6 +448,7 @@ namespace Avalonia.Controls } contextMenu.Close(); + contextMenu._attachedControls?.Remove(control); } } From 5f7d823b59c96473d048f5c66ab098c36bd055bb Mon Sep 17 00:00:00 2001 From: adirh Date: Sat, 12 Aug 2023 17:26:02 +0300 Subject: [PATCH 385/391] Added unit test --- tests/Avalonia.LeakTests/ControlTests.cs | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index ab8e4377b6..9a4bcb1808 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -583,6 +583,48 @@ namespace Avalonia.LeakTests Assert.Equal(initialMenuItemCount, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); } } + + [Fact] + public void Attached_Control_From_ContextMenu_Is_Freed() + { + using (Start()) + { + var contextMenu = new ContextMenu(); + Func run = () => + { + var window = new Window + { + Content = new TextBlock + { + ContextMenu = contextMenu + } + }; + + window.Show(); + + // Do a layout and make sure that TextBlock gets added to visual tree with + // its render transform. + window.LayoutManager.ExecuteInitialLayoutPass(); + var textBlock = Assert.IsType(window.Presenter.Child); + Assert.IsType(textBlock.RenderTransform); + + // Clear the content and ensure the TextBlock is removed. + window.Content = null; + window.LayoutManager.ExecuteLayoutPass(); + Assert.Null(window.Presenter.Child); + + return window; + }; + + var result = run(); + + // Process all Loaded events to free control reference(s) + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + + dotMemory.Check(memory => + Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); + } + } [Fact] public void Standalone_ContextMenu_Is_Freed() From 1d484685370d7ddb60259b74f1f24630a975f17b Mon Sep 17 00:00:00 2001 From: adirh Date: Sat, 12 Aug 2023 17:30:51 +0300 Subject: [PATCH 386/391] Added check for when control already attached to visual tree --- src/Avalonia.Controls/ContextMenu.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 7fab36d931..6300239e12 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -227,6 +227,11 @@ namespace Avalonia.Controls control.AttachedToVisualTree += ControlOnAttachedToVisualTree; control.DetachedFromVisualTree += ControlDetachedFromVisualTree; } + + if (control.IsAttachedToVisualTree) + { + AttachControlToContextMenu(control); + } } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) @@ -431,7 +436,12 @@ namespace Avalonia.Controls private static void ControlOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is Control { ContextMenu: {} contextMenu } control) + AttachControlToContextMenu(sender); + } + + private static void AttachControlToContextMenu(object? sender) + { + if (sender is Control { ContextMenu: { } contextMenu } control) { contextMenu._attachedControls ??= new List(); contextMenu._attachedControls.Add(control); From 038c96375037bc9fe5e72382c5697bac73aa18f2 Mon Sep 17 00:00:00 2001 From: adirh Date: Sat, 12 Aug 2023 21:39:45 +0300 Subject: [PATCH 387/391] Fixed unit test --- tests/Avalonia.LeakTests/ControlTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 9a4bcb1808..c6f9c5eae0 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -602,11 +602,9 @@ namespace Avalonia.LeakTests window.Show(); - // Do a layout and make sure that TextBlock gets added to visual tree with - // its render transform. + // Do a layout and make sure that TextBlock gets added to visual tree. window.LayoutManager.ExecuteInitialLayoutPass(); - var textBlock = Assert.IsType(window.Presenter.Child); - Assert.IsType(textBlock.RenderTransform); + Assert.IsType(window.Presenter.Child); // Clear the content and ensure the TextBlock is removed. window.Content = null; From ea946b2b7ceb67737fa432d39b5e9af33329a01b Mon Sep 17 00:00:00 2001 From: Flithor <23412916+Flithor@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:22:48 +0800 Subject: [PATCH 388/391] Update Fluent.xaml --- .../Themes/Fluent.xaml | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 0cc620dae9..e6831faf87 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -170,26 +170,28 @@ CornerRadius="{TemplateBinding CornerRadius}"> - - - - - - - - - - + + + + + + + + + + + Date: Tue, 15 Aug 2023 09:27:28 +0800 Subject: [PATCH 389/391] Update Fluent.xaml --- src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index e6831faf87..2d40721bbf 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -170,7 +170,7 @@ CornerRadius="{TemplateBinding CornerRadius}"> - @@ -191,7 +191,7 @@ Fill="{TemplateBinding Foreground}" Stretch="Uniform" /> - + Date: Tue, 15 Aug 2023 18:37:33 +0300 Subject: [PATCH 390/391] Set PreserveSig to true for OleGetClipboard --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 68f3e3c670..226ed2a406 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1509,7 +1509,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern IntPtr SetClipboardData(ClipboardFormat uFormat, IntPtr hMem); - [DllImport("ole32.dll", PreserveSig = false)] + [DllImport("ole32.dll", PreserveSig = true)] public static extern int OleGetClipboard(out IntPtr dataObject); [DllImport("ole32.dll", PreserveSig = true)] From 74a5e13d2e20bf3d31238e050c4d5775c7c5b137 Mon Sep 17 00:00:00 2001 From: adirh Date: Tue, 15 Aug 2023 21:23:42 +0300 Subject: [PATCH 391/391] Trigger devops build again --- src/Avalonia.Controls/ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 6300239e12..78f6c5f77d 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -230,7 +230,7 @@ namespace Avalonia.Controls if (control.IsAttachedToVisualTree) { - AttachControlToContextMenu(control); + AttachControlToContextMenu(control); } }