From 3518f19b2aa5d688781a06217e7090bb2efb575c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 16 Nov 2020 11:22:05 +0100 Subject: [PATCH 1/9] Revert "ItemsControl+ItemVirtualizerSimple don not recreate containers when Items or ItemTemplate are replaced" --- src/Avalonia.Controls/ItemsControl.cs | 6 +- .../Presenters/ItemVirtualizerSimple.cs | 4 - .../Presenters/ItemsPresenterBase.cs | 4 +- .../ItemsControlTests.cs | 92 -------------- .../ListBoxTests.cs | 112 ------------------ 5 files changed, 2 insertions(+), 216 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index f955df5f21..4dc8aec6f3 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -449,11 +449,7 @@ namespace Avalonia.Controls if (_itemContainerGenerator != null) { _itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue; - - if (e.OldValue != null && Presenter != null) - { - Presenter.ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } + // TODO: Rebuild the item containers. } } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 51dbc969a8..7d50ef7d33 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -200,10 +200,6 @@ namespace Avalonia.Controls.Presenters break; case NotifyCollectionChangedAction.Reset: - Owner.ItemContainerGenerator.Clear(); - VirtualizingPanel.Children.Clear(); - FirstIndex = NextIndex = 0; - RecycleContainersOnRemove(); CreateAndRemoveContainers(); panel.ForceInvalidateMeasure(); diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index 6c408bbed9..52f173fc71 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -57,8 +57,6 @@ namespace Avalonia.Controls.Presenters set { - var itemsReplaced = (_items != value); - _itemsSubscription?.Dispose(); _itemsSubscription = null; @@ -69,7 +67,7 @@ namespace Avalonia.Controls.Presenters SetAndRaise(ItemsProperty, ref _items, value); - if (_createdPanel && itemsReplaced) + if (_createdPanel) { ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 157eefb84a..684486cbae 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -596,98 +596,6 @@ namespace Avalonia.Controls.UnitTests root.Child = target; } - [Fact] - public void Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() - { - var target = new ItemsControl - { - Template = GetTemplate(), - Items = new[] - { - new Item("Item1") - } - }; - - var root = new TestRoot { Child = target }; - var otherPanel = new StackPanel(); - - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); - materializedEventCallCount++; - }; - - target.Items = new[] - { - new Item("Item2") - }; - - //Ensure that events are called one time only - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - - [Fact] - public void Presenter_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() - { - var target = new ItemsControl - { - Template = GetTemplate(), - Items = new[] - { - new Item("Item1") - }, - ItemTemplate = new FuncDataTemplate((x, ns) => new TextBlock()) - }; - - var root = new TestRoot { Child = target }; - var otherPanel = new StackPanel(); - - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); - contentPresenter.UpdateChild(); - Assert.IsType(contentPresenter.Child); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); - contentPresenter.UpdateChild(); - Assert.IsType(contentPresenter.Child); - materializedEventCallCount++; - }; - - target.ItemTemplate = - new FuncDataTemplate((x, ns) => new Canvas()); - - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - private class Item { public Item(string value) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 364cb01c65..145fce4fed 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -454,118 +454,6 @@ namespace Avalonia.Controls.UnitTests } } - - [Fact] - public void ListBox_Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; - - var target = new ListBox() - { - VerticalAlignment = Layout.VerticalAlignment.Top, - AutoScrollToSelectedItem = true, - Width = 50, - VirtualizationMode = ItemVirtualizationMode.Simple, - Items = new[] - { - new Item("Item1") - }, - }; - wnd.Content = target; - - var lm = wnd.LayoutManager; - - lm.ExecuteInitialLayoutPass(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); - materializedEventCallCount++; - }; - - target.Items = new[] - { - new Item("Item2") - }; - - //assert that materialize/dematerialize events are called exactly one time - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - } - - [Fact] - public void ListBox_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; - - var target = new ListBox() - { - VerticalAlignment = Layout.VerticalAlignment.Top, - AutoScrollToSelectedItem = true, - Width = 50, - VirtualizationMode = ItemVirtualizationMode.Simple, - Items = new[] - { - new Item("Item1") - }, - ItemTemplate = - new FuncDataTemplate((x, ns) => new Canvas()) - }; - - wnd.Content = target; - - var lm = wnd.LayoutManager; - - lm.ExecuteInitialLayoutPass(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - Assert.IsType(((ListBoxItem)e.Containers[0].ContainerControl).Presenter.Child); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - ListBoxItem materializedListBoxItem = null; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - materializedListBoxItem = ((ListBoxItem)e.Containers[0].ContainerControl); - materializedEventCallCount++; - }; - - target.ItemTemplate = - new FuncDataTemplate((x, ns) => new TextBlock()); - - //ensure events are called only one time - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - - wnd.LayoutManager.ExecuteLayoutPass(); - - //ensure that new template has been applied - Assert.IsType(materializedListBoxItem.Presenter.Child); - } - } - private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) => From cc1051efa35342ba4ef095b987b3f583cfe550d8 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 16 Nov 2020 14:03:13 +0200 Subject: [PATCH 2/9] add failing test for set of base class property --- .../Xaml/XamlIlTests.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 67e46d25c3..77a4932ccc 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -295,8 +295,27 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal("Test", templated.Text); } } + + [Fact] + public void Should_Work_With_Base_Property() + { + var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Load(@" + + + + + + +"); + + Assert.NotNull(parsed.ItemTemplate); + } } - + public class XamlIlBugTestsEventHandlerCodeBehind : Window { public object SavedContext; From db96b4d39ee3733dcd55ead409c6df9a7314342f Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 16 Nov 2020 14:04:09 +0200 Subject: [PATCH 3/9] fix set of base class property in xaml fixes #1926 --- .../AvaloniaXamlIlTransformInstanceAttachedProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index d78ceeb918..f87e73a783 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject"); if (avaloniaObject.IsAssignableFrom(targetRef.Type) && avaloniaObject.IsAssignableFrom(declaringRef.Type) - && !targetRef.Type.IsAssignableFrom(declaringRef.Type)) + && !declaringRef.Type.IsAssignableFrom(targetRef.Type)) { // Instance property var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name); From 1dfb76d993fed57ac3ac64da4bcc1af475639983 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 16 Nov 2020 19:43:24 +0300 Subject: [PATCH 4/9] Fixed COM calling convention for 32 bit windows --- src/Avalonia.MicroCom/MicroComVtblBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.MicroCom/MicroComVtblBase.cs b/src/Avalonia.MicroCom/MicroComVtblBase.cs index e06e2d4934..2f0607c0a8 100644 --- a/src/Avalonia.MicroCom/MicroComVtblBase.cs +++ b/src/Avalonia.MicroCom/MicroComVtblBase.cs @@ -7,10 +7,10 @@ namespace Avalonia.MicroCom public unsafe class MicroComVtblBase { private List _methods = new List(); - [UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] private delegate int AddRefDelegate(Ccw* ccw); - [UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv); public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable(); From be8a3e83fa9fd50c29e12c33af4951c05105f231 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 16 Nov 2020 21:12:46 +0000 Subject: [PATCH 5/9] fix OSX NRE. --- src/Avalonia.Native/WindowImplBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 20b6b8ecc5..150ab2703e 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -174,7 +174,7 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Resized(AvnSize* size) { - if (_parent._native != null) + if (_parent?._native != null) { var s = new Size(size->Width, size->Height); _parent._savedLogicalSize = s; From c24f65a5f78cbe0dd11d926d28ea8d8a198d90a0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 16 Nov 2020 22:51:18 +0100 Subject: [PATCH 6/9] Fix even more broken cases for closing child/dialog windows on win32. --- .../ControlCatalog/Pages/DialogsPage.xaml.cs | 29 +++++++++++++--- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 19 ++--------- src/Windows/Avalonia.Win32/WindowImpl.cs | 34 +++++++++++++++++++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index cf6c771e34..49921fb7f6 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Dialogs; +using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 @@ -112,11 +113,29 @@ namespace ControlCatalog.Pages private Window CreateSampleWindow() { - var window = new Window(); - window.Height = 200; - window.Width = 200; - window.Content = new TextBlock { Text = "Hello world!" }; - window.WindowStartupLocation = WindowStartupLocation.CenterOwner; + Button button; + + var window = new Window + { + Height = 200, + Width = 200, + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock { Text = "Hello world!" }, + (button = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Click to close" + }) + } + }, + WindowStartupLocation = WindowStartupLocation.CenterOwner + }; + + button.Click += (_, __) => window.Close(); return window; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index d770f4b211..78de681403 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -65,23 +65,10 @@ namespace Avalonia.Win32 return IntPtr.Zero; } - // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 - // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us. - // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks - // Our window closed callback will set enabled state to a correct value after child window gets destroyed. - // We need to verify if parent is still alive (perhaps it got destroyed somehow). - if (_parent != null && IsWindow(_parent._hwnd)) - { - var wasActive = GetActiveWindow() == _hwnd; - - _parent.SetEnabled(true); + BeforeCloseCleanup(false); - // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. - if (wasActive) - { - SetActiveWindow(_parent._hwnd); - } - } + // Used to distinguish between programmatic and regular close requests. + _isCloseRequested = true; break; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index c603128a18..2483356e9a 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -83,6 +83,7 @@ namespace Avalonia.Win32 private POINT _maxTrackSize; private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; + private bool _isCloseRequested; public WindowImpl() { @@ -506,6 +507,13 @@ namespace Avalonia.Win32 if (_hwnd != IntPtr.Zero) { + // Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called + // and we didn't prepare this window for destruction. + if (!_isCloseRequested) + { + BeforeCloseCleanup(true); + } + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -948,6 +956,32 @@ namespace Avalonia.Win32 SetFocus(_hwnd); } } + + private void BeforeCloseCleanup(bool isDisposing) + { + // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 + // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us (or last active window). + // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks + // We need to verify if parent is still alive (perhaps it got destroyed somehow). + if (_parent != null && IsWindow(_parent._hwnd)) + { + var wasActive = GetActiveWindow() == _hwnd; + + // We can only set enabled state if we are not disposing - generally Dispose happens after enabled state has been set. + // Ignoring this would cause us to enable a window that might be disabled. + if (!isDisposing) + { + // Our window closed callback will set enabled state to a correct value after child window gets destroyed. + _parent.SetEnabled(true); + } + + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. + if (wasActive) + { + SetActiveWindow(_parent._hwnd); + } + } + } private void MaximizeWithoutCoveringTaskbar() { From 1428badc6b17c1922151d31af42d34d9d875ae97 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 17 Nov 2020 01:46:22 +0300 Subject: [PATCH 7/9] Fixed CPP header codegen --- src/tools/MicroComGenerator/CppGen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/MicroComGenerator/CppGen.cs b/src/tools/MicroComGenerator/CppGen.cs index 68192ebffe..b053088ca9 100644 --- a/src/tools/MicroComGenerator/CppGen.cs +++ b/src/tools/MicroComGenerator/CppGen.cs @@ -14,7 +14,10 @@ namespace MicroComGenerator name = "unsigned char"; else if(name == "uint") name = "unsigned int"; - return name + new string('*', type.PointerLevel); + + type = type.Clone(); + type.Name = name; + return type.Format(); } public static string GenerateCpp(AstIdlNode idl) From 2b9e092b32757f32b028288aac2934027b5ce924 Mon Sep 17 00:00:00 2001 From: capdj <42602325+capdj@users.noreply.github.com> Date: Tue, 17 Nov 2020 15:33:45 +0000 Subject: [PATCH 8/9] Fix X11 dropping modifier keys Fixes #4988 --- src/Avalonia.X11/XI2Manager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index b3a24e6c37..8cdf24cc7b 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -351,7 +351,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); + Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); From 053537721a65632346ccd55441fdabdcb2e30ef1 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 17 Nov 2020 16:49:55 +0100 Subject: [PATCH 9/9] Parse Color and GridLength during compilation. Improve error handling for failed parsing. --- .../Avalonia.Build.Tasks.csproj | 11 +- src/Avalonia.Build.Tasks/SpanCompat.cs | 4 + src/Avalonia.Controls/GridLength.cs | 10 +- src/Avalonia.Visuals/Media/Color.cs | 14 +- src/Avalonia.Visuals/Media/KnownColors.cs | 9 ++ .../AvaloniaXamlIlGridLengthAstNode.cs | 34 +++++ .../AvaloniaXamlIlLanguage.cs | 135 ++++++++++++++---- .../AvaloniaXamlIlWellKnownTypes.cs | 9 ++ 8 files changed, 192 insertions(+), 34 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index bfac1ac1e1..90f6abc873 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -1,7 +1,7 @@  netstandard2.0 - netstandard2.0;netcoreapp2.0 + netstandard2.0;netcoreapp3.1 exe false tools @@ -81,6 +81,15 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index d5c406293e..f8960f56ec 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -1,3 +1,4 @@ +#if !NETCOREAPP3_1 namespace System { // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory @@ -63,6 +64,8 @@ namespace System } public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); + + public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr)); } static class SpanCompatExtensions @@ -71,3 +74,4 @@ namespace System } } +#endif diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index 57f308d59f..b8418949d9 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -8,7 +8,10 @@ namespace Avalonia.Controls /// /// Defines the valid units for a . /// - public enum GridUnitType +#if !BUILDTASK + public +#endif + enum GridUnitType { /// /// The row or column is auto-sized to fit its content. @@ -29,7 +32,10 @@ namespace Avalonia.Controls /// /// Holds the width or height of a 's column and row definitions. /// - public struct GridLength : IEquatable +#if !BUILDTASK + public +#endif + struct GridLength : IEquatable { private readonly GridUnitType _type; diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 16b4f90d57..a57a962db4 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -1,17 +1,24 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif namespace Avalonia.Media { /// /// An ARGB color. /// - public readonly struct Color : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Color : IEquatable { static Color() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)); +#endif } /// @@ -223,7 +230,12 @@ namespace Avalonia.Media if (input.Length == 3 || input.Length == 4) { var extendedLength = 2 * input.Length; + +#if !BUILDTASK Span extended = stackalloc char[extendedLength]; +#else + char[] extended = new char[extendedLength]; +#endif for (int i = 0; i < input.Length; i++) { diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Visuals/Media/KnownColors.cs index 0887d2c913..fe09f5f538 100644 --- a/src/Avalonia.Visuals/Media/KnownColors.cs +++ b/src/Avalonia.Visuals/Media/KnownColors.cs @@ -8,7 +8,9 @@ namespace Avalonia.Media { private static readonly IReadOnlyDictionary _knownColorNames; private static readonly IReadOnlyDictionary _knownColors; +#if !BUILDTASK private static readonly Dictionary _knownBrushes; +#endif static KnownColors() { @@ -32,14 +34,19 @@ namespace Avalonia.Media _knownColorNames = knownColorNames; _knownColors = knownColors; + +#if !BUILDTASK _knownBrushes = new Dictionary(); +#endif } +#if !BUILDTASK public static ISolidColorBrush GetKnownBrush(string s) { var color = GetKnownColor(s); return color != KnownColor.None ? color.ToBrush() : null; } +#endif public static KnownColor GetKnownColor(string s) { @@ -61,6 +68,7 @@ namespace Avalonia.Media return Color.FromUInt32((uint)color); } +#if !BUILDTASK public static ISolidColorBrush ToBrush(this KnownColor color) { lock (_knownBrushes) @@ -74,6 +82,7 @@ namespace Avalonia.Media return brush; } } +#endif } internal enum KnownColor : uint diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs new file mode 100644 index 0000000000..218c49512c --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs @@ -0,0 +1,34 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly AvaloniaXamlIlWellKnownTypes _types; + private readonly GridLength _gridLength; + + public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo) + { + _types = types; + _gridLength = gridLength; + + Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen + .Ldc_R8(_gridLength.Value) + .Ldc_I4((int)_gridLength.GridUnitType) + .Newobj(_types.GridLengthConstructorValueType); + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index c3d9534828..87b82c112e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Avalonia.Controls; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using Avalonia.Media; using XamlX; using XamlX.Ast; using XamlX.Emit; @@ -205,67 +207,140 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return true; } - + if (type.Equals(types.Thickness)) { - var thickness = Thickness.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, - new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); + try + { + var thickness = Thickness.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, + new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); + } } if (type.Equals(types.Point)) { - var point = Point.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, - new[] { point.X, point.Y }); + try + { + var point = Point.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, + new[] { point.X, point.Y }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); + } } if (type.Equals(types.Vector)) { - var vector = Vector.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, - new[] { vector.X, vector.Y }); + try + { + var vector = Vector.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, + new[] { vector.X, vector.Y }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); + } } if (type.Equals(types.Size)) { - var size = Size.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, - new[] { size.Width, size.Height }); + try + { + var size = Size.Parse(text); - return true; + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, + new[] { size.Width, size.Height }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); + } } if (type.Equals(types.Matrix)) { - var matrix = Matrix.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, - new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); + try + { + var matrix = Matrix.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, + new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); + } } if (type.Equals(types.CornerRadius)) { - var cornerRadius = CornerRadius.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, - new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + try + { + var cornerRadius = CornerRadius.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, + new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); + } + } + + if (type.Equals(types.Color)) + { + if (!Color.TryParse(text, out Color color)) + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.GetMethod( + new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), + new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); + return true; } + if (type.Equals(types.GridLength)) + { + try + { + var gridLength = GridLength.Parse(text); + + result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); + } + } + if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 05b13b61d3..2a7e10d42e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ReflectionBindingExtension { get; } public IXamlType RelativeSource { get; } + public IXamlType UInt { get; } public IXamlType Long { get; } public IXamlType Uri { get; } public IXamlType FontFamily { get; } @@ -65,6 +66,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlConstructor MatrixFullConstructor { get; } public IXamlType CornerRadius { get; } public IXamlConstructor CornerRadiusFullConstructor { get; } + public IXamlType GridLength { get; } + public IXamlConstructor GridLengthConstructorValueType { get; } + public IXamlType Color { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -122,6 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater"); ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource"); + UInt = cfg.TypeSystem.GetType("System.UInt32"); Long = cfg.TypeSystem.GetType("System.Int64"); Uri = cfg.TypeSystem.GetType("System.Uri"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); @@ -141,6 +146,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers (Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + + GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); + GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); + Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); } }