From e1bfdf03246a8fe2df2fb443eb6226e17ef1940d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 10 Oct 2019 23:01:30 +0200 Subject: [PATCH 01/51] Added failing test for #3094. --- .../Primitives/SelectingItemsControlTests.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index d5237e2aca..17f0e609a5 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1000,6 +1000,26 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { "Bar" }, selectedItems); } + [Fact] + public void MoveSelection_Wrap_Does_Not_Hang_With_No_Focusable_Controls() + { + // Issue #3094. + var target = new TestSelector + { + Template = Template(), + Items = new[] + { + new ListBoxItem { Focusable = false }, + new ListBoxItem { Focusable = false }, + }, + SelectedIndex = 0, + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + target.MoveSelection(NavigationDirection.Next, true); + } + private FuncControlTemplate Template() { return new FuncControlTemplate((control, scope) => @@ -1044,5 +1064,13 @@ namespace Avalonia.Controls.UnitTests.Primitives public List Items { get; set; } = new List() { "a", "b", "c", "d", "e" }; public string Selected { get; set; } = "b"; } + + private class TestSelector : SelectingItemsControl + { + public new bool MoveSelection(NavigationDirection direction, bool wrap) + { + return base.MoveSelection(direction, wrap); + } + } } } From 60c9015a7fd42a5cc13d156fd70c0c3f5444ceda Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 10 Oct 2019 23:03:12 +0200 Subject: [PATCH 02/51] Prevent hang in ItemsControl with no focusable controls. --- src/Avalonia.Controls/ItemsControl.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0fe7291835..6b81237e6f 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -489,18 +489,20 @@ namespace Avalonia.Controls bool wrap) { IInputElement result; + var c = from; do { - result = container.GetControl(direction, from, wrap); + result = container.GetControl(direction, c, wrap); + from ??= result; if (result?.Focusable == true) { return result; } - from = result; - } while (from != null); + c = result; + } while (c != null && c != from); return null; } From adb65f14b1e0fdb86889d2038c5f4dcdf18a312c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 11 Oct 2019 15:13:48 +0200 Subject: [PATCH 03/51] Don't use C#8 features yet. --- src/Avalonia.Controls/ItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 6b81237e6f..11fdd0457d 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -494,7 +494,7 @@ namespace Avalonia.Controls do { result = container.GetControl(direction, c, wrap); - from ??= result; + from = from ?? result; if (result?.Focusable == true) { From 5e9f3067ce9babca797cdd8e99bec665001be9e0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 10:32:21 +0200 Subject: [PATCH 04/51] Fix off-by-N error in VisualNode.TrimChildren. And added unit test. --- .../Rendering/SceneGraph/VisualNode.cs | 2 +- .../Rendering/SceneGraph/VisualNodeTests.cs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index f579bf0a62..d342f2eb2e 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -218,7 +218,7 @@ namespace Avalonia.Rendering.SceneGraph if (first < _children?.Count) { EnsureChildrenCreated(); - for (int i = first; i < _children.Count - first; i++) + for (int i = first; i < _children.Count; i++) { _children[i].Dispose(); } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 24ba2d1c48..3211c3397b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -101,5 +101,24 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph node.SortChildren(scene); } + + [Fact] + public void TrimChildren_Should_Work_Correctly() + { + var parent = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), null); + var child2 = new VisualNode(Mock.Of(), null); + var child3 = new VisualNode(Mock.Of(), null); + + parent.AddChild(child1); + parent.AddChild(child2); + parent.AddChild(child3); + parent.TrimChildren(2); + + Assert.Equal(2, parent.Children.Count); + Assert.False(child1.Disposed); + Assert.False(child2.Disposed); + Assert.True(child3.Disposed); + } } } From f6525de3c81b4754e9bc426684dae6647decdb94 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 10:18:24 +0200 Subject: [PATCH 05/51] Added failing test for #3115. --- .../Rendering/SceneGraph/SceneBuilderTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 13bcd27240..e226a61659 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -577,6 +577,58 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph } } + [Fact] + public void Should_Not_Dispose_Active_VisualNode_When_Control_Reparented_And_Child_Made_Invisible() + { + // Issue #3115 + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + StackPanel panel; + Border border1; + Border border2; + var tree = new TestRoot + { + Width = 100, + Height = 100, + Child = panel = new StackPanel + { + Children = + { + (border1 = new Border + { + Background = Brushes.Red, + }), + (border2 = new Border + { + Background = Brushes.Green, + }), + } + } + }; + + tree.Measure(Size.Infinity); + tree.Arrange(new Rect(tree.DesiredSize)); + + var scene = new Scene(tree); + var sceneBuilder = new SceneBuilder(); + sceneBuilder.UpdateAll(scene); + + var decorator = new Decorator(); + tree.Child = null; + decorator.Child = panel; + tree.Child = decorator; + border1.IsVisible = false; + + scene = scene.CloneScene(); + + var panelNode = (VisualNode)scene.FindNode(panel); + sceneBuilder.Update(scene, decorator); + + Assert.Equal(1, panelNode.Children.Count); + Assert.False(panelNode.Children[0].Disposed); + } + } + [Fact] public void Should_Update_ClipBounds_For_Negative_Margin() { From 534852af75bc83c3a4c1681f51cb13cba8ea7f70 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 10:53:24 +0200 Subject: [PATCH 06/51] Create VisualNode for invisible controls. If we don't do this, then a `VisualNode` can appear twice in its parent `ChildrenCollection` when an earlier sibling is made invisible. Fixes #3115 --- src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs | 4 ++++ .../Rendering/SceneGraph/SceneBuilderTests.cs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 161cbc099e..87c9ed0bae 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -240,6 +240,10 @@ namespace Avalonia.Rendering.SceneGraph } } } + else + { + contextImpl.BeginUpdate(node).Dispose(); + } } private void UpdateSize(Scene scene) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index e226a61659..5fd14e9ea9 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -624,8 +624,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var panelNode = (VisualNode)scene.FindNode(panel); sceneBuilder.Update(scene, decorator); - Assert.Equal(1, panelNode.Children.Count); + Assert.Equal(2, panelNode.Children.Count); Assert.False(panelNode.Children[0].Disposed); + Assert.False(panelNode.Children[1].Disposed); } } From e6790fe1d108eac4cf542c17dcf7ca8d0d09719b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 13:03:37 +0200 Subject: [PATCH 07/51] Add check for correct VisualNode.Parent. Check that `VisualNode.Parent` is correct when adding a child. Causes a failing test for #3095. --- .../Rendering/SceneGraph/VisualNode.cs | 10 ++++++++++ .../SceneGraph/DeferredDrawingContextImplTests.cs | 12 ++++++------ .../Rendering/SceneGraph/VisualNodeTests.cs | 8 ++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index d342f2eb2e..d2a9e0a673 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -119,6 +119,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (child.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children.Add(child); } @@ -155,6 +160,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (node.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children[index] = node; } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 4d23c57eed..f57c73c45c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Add_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); var target = new DeferredDrawingContextImpl(null, layers); @@ -32,7 +32,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Not_Replace_Identical_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child); @@ -50,8 +50,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Replace_Different_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child1); @@ -78,8 +78,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var layers = new SceneLayers(root); var target = new DeferredDrawingContextImpl(null, layers); - var child1 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; - var child2 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; + var child1 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; + var child2 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; target.BeginUpdate(node); using (target.BeginUpdate(child1)) { } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 3211c3397b..4ec3630053 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -24,7 +24,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(Mock.Of(), null); var collection = node.Children; - node.AddChild(Mock.Of()); + node.AddChild(Mock.Of(x => x.Parent == node)); Assert.NotSame(collection, node.Children); } @@ -106,9 +106,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void TrimChildren_Should_Work_Correctly() { var parent = new VisualNode(Mock.Of(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); - var child3 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); + var child3 = new VisualNode(Mock.Of(), parent); parent.AddChild(child1); parent.AddChild(child2); From f2c4bf4d6a27992bb89b450b2322a846d77a21ba Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 13:17:48 +0200 Subject: [PATCH 08/51] Update test to ensure we're looking at the correct node. --- .../Rendering/SceneGraph/SceneBuilderTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 5fd14e9ea9..327ef98c4d 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -620,10 +620,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph border1.IsVisible = false; scene = scene.CloneScene(); - - var panelNode = (VisualNode)scene.FindNode(panel); sceneBuilder.Update(scene, decorator); + var panelNode = (VisualNode)scene.FindNode(panel); Assert.Equal(2, panelNode.Children.Count); Assert.False(panelNode.Children[0].Disposed); Assert.False(panelNode.Children[1].Disposed); From d1ad46d07ed6ef6a96d7eaa8b9360d9196a2b833 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Oct 2019 13:23:23 +0200 Subject: [PATCH 09/51] Ensure child node has correct parent. When updating the scene, if a control has been reparented then a new `VisualNode` needs to be created to prevent #3095. Check that an existing node has the correct parent and if it doesn't, deindex it and create a new node. Fixes #3095 --- .../Rendering/SceneGraph/SceneBuilder.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 87c9ed0bae..558e96e132 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -148,6 +148,19 @@ namespace Avalonia.Rendering.SceneGraph return (VisualNode)node; } + private static object GetOrCreateChildNode(Scene scene, IVisual child, VisualNode parent) + { + var result = (VisualNode)scene.FindNode(child); + + if (result != null && result.Parent != parent) + { + Deindex(scene, result); + result = null; + } + + return result ?? CreateNode(scene, child, parent); + } + private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) { var visual = node.Visual; @@ -231,7 +244,7 @@ namespace Avalonia.Rendering.SceneGraph { foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { - var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); + var childNode = GetOrCreateChildNode(scene, child, node); Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } From 40d90378370eb3181e3e046113567fba2002f94d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 16 Oct 2019 15:28:58 +0100 Subject: [PATCH 10/51] fix compiler services unsafe reference. --- build/Base.props | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Base.props b/build/Base.props index a60373ebb3..100c9088cd 100644 --- a/build/Base.props +++ b/build/Base.props @@ -1,5 +1,6 @@  + From 9c62cc9a416a2a1287f4d01e7ba5f41db4113cad Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 17 Oct 2019 02:35:28 +0800 Subject: [PATCH 11/51] Fix #3069 --- .../Interop/UnmanagedMethods.cs | 9 ++++++++ src/Windows/Avalonia.Win32/ScreenImpl.cs | 22 ++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index d37bec3334..2c6425e26c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1031,6 +1031,9 @@ namespace Avalonia.Win32.Interop [DllImport("shcore.dll")] public static extern long GetDpiForMonitor(IntPtr hmonitor, MONITOR_DPI_TYPE dpiType, out uint dpiX, out uint dpiY); + [DllImport("gdi32.dll")] + public static extern int GetDeviceCaps(IntPtr hdc, DEVICECAP nIndex); + [DllImport("shcore.dll")] public static extern void GetScaleFactorForMonitor(IntPtr hMon, out uint pScale); @@ -1147,6 +1150,12 @@ namespace Avalonia.Win32.Interop } } + public enum DEVICECAP + { + HORZRES = 8, + DESKTOPHORZRES = 118 + } + public enum PROCESS_DPI_AWARENESS { PROCESS_DPI_UNAWARE = 0, diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index e77aa07bcd..6a89c9bf24 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -28,9 +28,25 @@ namespace Avalonia.Win32 (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { MONITORINFO monitorInfo = MONITORINFO.Create(); - if (GetMonitorInfo(monitor,ref monitorInfo)) + if (GetMonitorInfo(monitor, ref monitorInfo)) { - GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); + var dpi = 1.0; + try + { + GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); + dpi = (double)x; + } + catch (DllNotFoundException) + { + var hdc = GetDC(IntPtr.Zero); + + double virtW = GetDeviceCaps(hdc, DEVICECAP.HORZRES); + double physW = GetDeviceCaps(hdc, DEVICECAP.DESKTOPHORZRES); + + dpi = (96d * physW / virtW); + + ReleaseDC(IntPtr.Zero, hdc); + } RECT bounds = monitorInfo.rcMonitor; RECT workingArea = monitorInfo.rcWork; @@ -40,7 +56,7 @@ namespace Avalonia.Win32 new PixelRect(workingArea.left, workingArea.top, workingArea.right - workingArea.left, workingArea.bottom - workingArea.top); screens[index] = - new WinScreen((double)x / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1, + new WinScreen(dpi / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1, monitor); index++; } From bbdf0592c1c1cd021d96314bdefd386cfdcf4fb0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 16 Oct 2019 21:34:59 +0100 Subject: [PATCH 12/51] check for shcore rather than exception. --- src/Windows/Avalonia.Win32/ScreenImpl.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 6a89c9bf24..fda3b77727 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Avalonia.Platform; +using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 @@ -31,12 +32,13 @@ namespace Avalonia.Win32 if (GetMonitorInfo(monitor, ref monitorInfo)) { var dpi = 1.0; - try - { + + if (UnmanagedMethods.ShCoreAvailable) + { GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); dpi = (double)x; } - catch (DllNotFoundException) + else { var hdc = GetDC(IntPtr.Zero); From 1c9e158a4c2d655cfd1319b3af46203364467608 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 16 Oct 2019 21:58:39 +0100 Subject: [PATCH 13/51] use getproc address to check for GetDpiForMonitor. --- src/Windows/Avalonia.Win32/ScreenImpl.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index fda3b77727..df4cf7fa19 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -32,8 +32,10 @@ namespace Avalonia.Win32 if (GetMonitorInfo(monitor, ref monitorInfo)) { var dpi = 1.0; - - if (UnmanagedMethods.ShCoreAvailable) + + var shcore = LoadLibrary("shcore.dll"); + var method = GetProcAddress(shcore, nameof(GetDpiForMonitor)); + if (method != IntPtr.Zero) { GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); dpi = (double)x; From 85ec916af3258496b2edea5ca525981076a2dde3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 17 Oct 2019 15:51:26 +0200 Subject: [PATCH 14/51] Add some tests for KeyboardDevice. Including failing tests for #3127. --- .../KeyboardDeviceTests.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs diff --git a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs new file mode 100644 index 0000000000..3c8e800fca --- /dev/null +++ b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs @@ -0,0 +1,90 @@ +using Avalonia.Input.Raw; +using Avalonia.Interactivity; +using Moq; +using Xunit; + +namespace Avalonia.Input.UnitTests +{ + public class KeyboardDeviceTests + { + [Fact] + public void Keypresses_Should_Be_Sent_To_Root_If_No_Focused_Element() + { + var target = new KeyboardDevice(); + var root = new Mock(); + + target.ProcessRawEvent( + new RawKeyEventArgs( + target, + 0, + root.Object, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.None)); + + root.Verify(x => x.RaiseEvent(It.IsAny())); + } + + [Fact] + public void Keypresses_Should_Be_Sent_To_Focused_Element() + { + var target = new KeyboardDevice(); + var focused = new Mock(); + var root = Mock.Of(); + + target.SetFocusedElement( + focused.Object, + NavigationMethod.Unspecified, + InputModifiers.None); + + target.ProcessRawEvent( + new RawKeyEventArgs( + target, + 0, + root, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.None)); + + focused.Verify(x => x.RaiseEvent(It.IsAny())); + } + + [Fact] + public void TextInput_Should_Be_Sent_To_Root_If_No_Focused_Element() + { + var target = new KeyboardDevice(); + var root = new Mock(); + + target.ProcessRawEvent( + new RawTextInputEventArgs( + target, + 0, + root.Object, + "Foo")); + + root.Verify(x => x.RaiseEvent(It.IsAny())); + } + + [Fact] + public void TextInput_Should_Be_Sent_To_Focused_Element() + { + var target = new KeyboardDevice(); + var focused = new Mock(); + var root = Mock.Of(); + + target.SetFocusedElement( + focused.Object, + NavigationMethod.Unspecified, + InputModifiers.None); + + target.ProcessRawEvent( + new RawTextInputEventArgs( + target, + 0, + root, + "Foo")); + + focused.Verify(x => x.RaiseEvent(It.IsAny())); + } + } +} From fb173b30e2f44f7db15cb34c4dadab7ba9a5d67f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 17 Oct 2019 15:55:01 +0200 Subject: [PATCH 15/51] Send keypresses to toplevel if no focused control. - Added `IInputRoot Root` property to `RawInputEventArgs` - Removed `InputRoot` and `Root` properties from `RawDragEvent` and `RawPointerEvent` as its now in the base class - `InputRoot` in `RawDragEvent` was incorrectly using the type `IInputElement` instead of `IInputRoot`, fix callers that were using the wrong type - Pass the input root to all raw input event constructors - If no control is focused in `KeyboardDevice.ProcessRawEvent` then raise the event on the root --- .../Platform/InProcessDragSource.cs | 6 +- .../Remote/Server/RemoteServerTopLevelImpl.cs | 2 + src/Avalonia.Input/DragDropDevice.cs | 18 ++-- src/Avalonia.Input/KeyboardDevice.cs | 102 +++++++++--------- src/Avalonia.Input/Raw/RawDragEvent.cs | 6 +- src/Avalonia.Input/Raw/RawInputEventArgs.cs | 9 +- src/Avalonia.Input/Raw/RawKeyEventArgs.cs | 3 +- src/Avalonia.Input/Raw/RawPointerEventArgs.cs | 8 +- .../Raw/RawTextInputEventArgs.cs | 11 +- src/Avalonia.Native/WindowImplBase.cs | 4 +- src/Avalonia.X11/X11Window.cs | 4 +- .../Wpf/WpfTopLevelImpl.cs | 6 +- src/Windows/Avalonia.Win32/OleDropTarget.cs | 4 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 4 +- .../TopLevelTests.cs | 1 + 15 files changed, 96 insertions(+), 92 deletions(-) diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs index 6eb056bb32..9d644aaa00 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -21,7 +21,7 @@ namespace Avalonia.Platform private DragDropEffects _allowedEffects; private IDataObject _draggedData; - private IInputElement _lastRoot; + private IInputRoot _lastRoot; private Point _lastPosition; private StandardCursorType _lastCursorType; private object _originalCursor; @@ -56,7 +56,7 @@ namespace Avalonia.Platform return DragDropEffects.None; } - private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, RawInputModifiers modifiers) + private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputRoot root, Point pt, RawInputModifiers modifiers) { _lastPosition = pt; @@ -91,7 +91,7 @@ namespace Avalonia.Platform return StandardCursorType.No; } - private void UpdateCursor(IInputElement root, DragDropEffects effect) + private void UpdateCursor(IInputRoot root, DragDropEffects effect) { if (_lastRoot != root) { diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index be24f22e20..9d70fbcf31 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -227,6 +227,7 @@ namespace Avalonia.Controls.Remote.Server Input?.Invoke(new RawKeyEventArgs( KeyboardDevice, 0, + InputRoot, key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, (Key)key.Key, GetAvaloniaRawInputModifiers(key.Modifiers))); @@ -241,6 +242,7 @@ namespace Avalonia.Controls.Remote.Server Input?.Invoke(new RawTextInputEventArgs( KeyboardDevice, 0, + InputRoot, text.Text)); }, DispatcherPriority.Input); } diff --git a/src/Avalonia.Input/DragDropDevice.cs b/src/Avalonia.Input/DragDropDevice.cs index 0b9f09b224..25d3b6887f 100644 --- a/src/Avalonia.Input/DragDropDevice.cs +++ b/src/Avalonia.Input/DragDropDevice.cs @@ -11,7 +11,7 @@ namespace Avalonia.Input private Interactive _lastTarget = null; - private Interactive GetTarget(IInputElement root, Point local) + private Interactive GetTarget(IInputRoot root, Point local) { var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); if (target != null && DragDrop.GetAllowDrop(target)) @@ -19,7 +19,7 @@ namespace Avalonia.Input return null; } - private DragDropEffects RaiseDragEvent(Interactive target, IInputElement inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, InputModifiers modifiers) + private DragDropEffects RaiseDragEvent(Interactive target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, InputModifiers modifiers) { if (target == null) return DragDropEffects.None; @@ -38,13 +38,13 @@ namespace Avalonia.Input return args.DragEffects; } - private DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) + private DragDropEffects DragEnter(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) { _lastTarget = GetTarget(inputRoot, point); return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } - private DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) + private DragDropEffects DragOver(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) { var target = GetTarget(inputRoot, point); @@ -77,7 +77,7 @@ namespace Avalonia.Input } } - private DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) + private DragDropEffects Drop(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) { try { @@ -100,16 +100,16 @@ namespace Avalonia.Input switch (e.Type) { case RawDragEventType.DragEnter: - e.Effects = DragEnter(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); + e.Effects = DragEnter(e.Root, e.Location, e.Data, e.Effects, e.Modifiers); break; case RawDragEventType.DragOver: - e.Effects = DragOver(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); + e.Effects = DragOver(e.Root, e.Location, e.Data, e.Effects, e.Modifiers); break; case RawDragEventType.DragLeave: - DragLeave(e.InputRoot); + DragLeave(e.Root); break; case RawDragEventType.Drop: - e.Effects = Drop(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); + e.Effects = Drop(e.Root, e.Location, e.Data, e.Effects, e.Modifiers); break; } } diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs index a02c580ad2..9db8525591 100644 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ b/src/Avalonia.Input/KeyboardDevice.cs @@ -70,66 +70,60 @@ namespace Avalonia.Input { if(e.Handled) return; - IInputElement element = FocusedElement; - if (element != null) - { - var keyInput = e as RawKeyEventArgs; + var element = FocusedElement ?? e.Root; - if (keyInput != null) + if (e is RawKeyEventArgs keyInput) + { + switch (keyInput.Type) { - switch (keyInput.Type) - { - case RawKeyEventType.KeyDown: - case RawKeyEventType.KeyUp: - var routedEvent = keyInput.Type == RawKeyEventType.KeyDown - ? InputElement.KeyDownEvent - : InputElement.KeyUpEvent; - - KeyEventArgs ev = new KeyEventArgs - { - RoutedEvent = routedEvent, - Device = this, - Key = keyInput.Key, - KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), - Source = element, - }; - - IVisual currentHandler = element; - while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown) - { - var bindings = (currentHandler as IInputElement)?.KeyBindings; - if(bindings!=null) - foreach (var binding in bindings) - { - if(ev.Handled) - break; - binding.TryHandle(ev); - } - currentHandler = currentHandler.VisualParent; - } - - element.RaiseEvent(ev); - e.Handled = ev.Handled; - break; - } + case RawKeyEventType.KeyDown: + case RawKeyEventType.KeyUp: + var routedEvent = keyInput.Type == RawKeyEventType.KeyDown + ? InputElement.KeyDownEvent + : InputElement.KeyUpEvent; + + KeyEventArgs ev = new KeyEventArgs + { + RoutedEvent = routedEvent, + Device = this, + Key = keyInput.Key, + KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), + Source = element, + }; + + IVisual currentHandler = element; + while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown) + { + var bindings = (currentHandler as IInputElement)?.KeyBindings; + if (bindings != null) + foreach (var binding in bindings) + { + if (ev.Handled) + break; + binding.TryHandle(ev); + } + currentHandler = currentHandler.VisualParent; + } + + element.RaiseEvent(ev); + e.Handled = ev.Handled; + break; } + } - var text = e as RawTextInputEventArgs; - - if (text != null) + if (e is RawTextInputEventArgs text) + { + var ev = new TextInputEventArgs() { - var ev = new TextInputEventArgs() - { - Device = this, - Text = text.Text, - Source = element, - RoutedEvent = InputElement.TextInputEvent - }; - - element.RaiseEvent(ev); - e.Handled = ev.Handled; - } + Device = this, + Text = text.Text, + Source = element, + RoutedEvent = InputElement.TextInputEvent + }; + + element.RaiseEvent(ev); + e.Handled = ev.Handled; } } } diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Input/Raw/RawDragEvent.cs index 1480f92f03..5722e17593 100644 --- a/src/Avalonia.Input/Raw/RawDragEvent.cs +++ b/src/Avalonia.Input/Raw/RawDragEvent.cs @@ -2,7 +2,6 @@ { public class RawDragEvent : RawInputEventArgs { - public IInputElement InputRoot { get; } public Point Location { get; set; } public IDataObject Data { get; } public DragDropEffects Effects { get; set; } @@ -10,11 +9,10 @@ public InputModifiers Modifiers { get; } public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, - IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers) - :base(inputDevice, 0) + IInputRoot root, Point location, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers) + :base(inputDevice, 0, root) { Type = type; - InputRoot = inputRoot; Location = location; Data = data; Effects = effects; diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Input/Raw/RawInputEventArgs.cs index 78c1b58624..6a488c4cc7 100644 --- a/src/Avalonia.Input/Raw/RawInputEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawInputEventArgs.cs @@ -21,12 +21,14 @@ namespace Avalonia.Input.Raw /// /// The associated device. /// The event timestamp. - public RawInputEventArgs(IInputDevice device, ulong timestamp) + /// The root from which the event originates. + public RawInputEventArgs(IInputDevice device, ulong timestamp, IInputRoot root) { Contract.Requires(device != null); Device = device; Timestamp = timestamp; + Root = root; } /// @@ -34,6 +36,11 @@ namespace Avalonia.Input.Raw /// public IInputDevice Device { get; } + /// + /// Gets the root from which the event originates. + /// + public IInputRoot Root { get; } + /// /// Gets or sets a value indicating whether the event was handled. /// diff --git a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs b/src/Avalonia.Input/Raw/RawKeyEventArgs.cs index cd8b2eacf7..c720cf11f8 100644 --- a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawKeyEventArgs.cs @@ -14,9 +14,10 @@ namespace Avalonia.Input.Raw public RawKeyEventArgs( IKeyboardDevice device, ulong timestamp, + IInputRoot root, RawKeyEventType type, Key key, RawInputModifiers modifiers) - : base(device, timestamp) + : base(device, timestamp, root) { Key = key; Type = type; diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index 88f6daf11f..56854c7d29 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -44,22 +44,16 @@ namespace Avalonia.Input.Raw RawPointerEventType type, Point position, RawInputModifiers inputModifiers) - : base(device, timestamp) + : base(device, timestamp, root) { Contract.Requires(device != null); Contract.Requires(root != null); - Root = root; Position = position; Type = type; InputModifiers = inputModifiers; } - /// - /// Gets the root from which the event originates. - /// - public IInputRoot Root { get; } - /// /// Gets the mouse position, in client DIPs. /// diff --git a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs index 0d1e5d2422..010342da15 100644 --- a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs @@ -5,11 +5,16 @@ namespace Avalonia.Input.Raw { public class RawTextInputEventArgs : RawInputEventArgs { - public string Text { get; set; } - - public RawTextInputEventArgs(IKeyboardDevice device, ulong timestamp, string text) : base(device, timestamp) + public RawTextInputEventArgs( + IKeyboardDevice device, + ulong timestamp, + IInputRoot root, + string text) + : base(device, timestamp, root) { Text = text; } + + public string Text { get; set; } } } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 209e1bc7ea..fe7458d583 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -204,7 +204,7 @@ namespace Avalonia.Native { Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - var args = new RawTextInputEventArgs(_keyboard, timeStamp, text); + var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text); Input?.Invoke(args); @@ -215,7 +215,7 @@ namespace Avalonia.Native { Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - var args = new RawKeyEventArgs(_keyboard, timeStamp, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers); + var args = new RawKeyEventArgs(_keyboard, timeStamp, _inputRoot, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers); Input?.Invoke(args); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 2630f9cf96..17471fad10 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -455,7 +455,7 @@ namespace Avalonia.X11 key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32(); - ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), + ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev); @@ -470,7 +470,7 @@ namespace Avalonia.X11 if (text[0] < ' ' || text[0] == 0x7f) //Control codes or DEL return; } - ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), text), + ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, text), ref ev); } } diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 71c398481b..2887468046 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -212,17 +212,17 @@ namespace Avalonia.Win32.Interop.Wpf 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, 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, 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, e.Text)); + => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text)); void ITopLevelImpl.SetCursor(IPlatformHandle cursor) { diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 8ac0f25598..b17e0d6c09 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -8,13 +8,13 @@ namespace Avalonia.Win32 { class OleDropTarget : IDropTarget { - private readonly IInputElement _target; + private readonly IInputRoot _target; private readonly ITopLevelImpl _tl; private readonly IDragDropDevice _dragDevice; private IDataObject _currentDrag = null; - public OleDropTarget(ITopLevelImpl tl, IInputElement target) + public OleDropTarget(ITopLevelImpl tl, IInputRoot target) { _dragDevice = AvaloniaLocator.Current.GetService(); _tl = tl; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 4f4c9852e8..04a9303d53 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -508,6 +508,7 @@ namespace Avalonia.Win32 e = new RawKeyEventArgs( WindowsKeyboardDevice.Instance, timestamp, + _owner, RawKeyEventType.KeyDown, KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers); break; @@ -521,6 +522,7 @@ namespace Avalonia.Win32 e = new RawKeyEventArgs( WindowsKeyboardDevice.Instance, timestamp, + _owner, RawKeyEventType.KeyUp, KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers); break; @@ -528,7 +530,7 @@ namespace Avalonia.Win32 // Ignore control chars if (ToInt32(wParam) >= 32) { - e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, + e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner, new string((char)ToInt32(wParam), 1)); } diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 645f87163a..72c81659c3 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -182,6 +182,7 @@ namespace Avalonia.Controls.UnitTests var input = new RawKeyEventArgs( new Mock().Object, 0, + target, RawKeyEventType.KeyDown, Key.A, RawInputModifiers.None); impl.Object.Input(input); From 91662d7020cbb8ab3a370c7f67a02faf4da44d31 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 17 Oct 2019 23:29:00 +0200 Subject: [PATCH 16/51] Fix Grid not reacting to cell structure changes during runtime. --- src/Avalonia.Controls/Grid.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 4f41b0bf1e..8ecfe349f8 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -24,13 +24,14 @@ namespace Avalonia.Controls { static Grid() { - IsSharedSizeScopeProperty.Changed.AddClassHandler(DefinitionBase.OnIsSharedSizeScopePropertyChanged); ShowGridLinesProperty.Changed.AddClassHandler(OnShowGridLinesPropertyChanged); - ColumnProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); - ColumnSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); - RowProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); - RowSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); + IsSharedSizeScopeProperty.Changed.AddClassHandler(DefinitionBase.OnIsSharedSizeScopePropertyChanged); + ColumnProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); + ColumnSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); + RowProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); + RowSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged); + AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty); } From b7e509f04637c9fccec17f38caadd5e68196c055 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 15:35:08 +0100 Subject: [PATCH 17/51] thumb adds :pressed pseudo class. --- src/Avalonia.Controls/Primitives/Thumb.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs index 7e9680dc9f..9f5ddb666c 100644 --- a/src/Avalonia.Controls/Primitives/Thumb.cs +++ b/src/Avalonia.Controls/Primitives/Thumb.cs @@ -83,6 +83,8 @@ namespace Avalonia.Controls.Primitives Vector = (Vector)_lastPoint, }; + PseudoClasses.Add(":pressed"); + RaiseEvent(ev); } @@ -102,6 +104,8 @@ namespace Avalonia.Controls.Primitives RaiseEvent(ev); } + + PseudoClasses.Remove(":pressed"); } } } From 5b1a552127e240b16fb8d34fe9dc0698c79f35ab Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 15:35:51 +0100 Subject: [PATCH 18/51] fill in the bottom right corner of scrollviewer area where the horizontal and vertical scrollbars meet. --- src/Avalonia.Themes.Default/ScrollViewer.xaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Themes.Default/ScrollViewer.xaml b/src/Avalonia.Themes.Default/ScrollViewer.xaml index 3e130cad67..38f4eef964 100644 --- a/src/Avalonia.Themes.Default/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Default/ScrollViewer.xaml @@ -36,6 +36,7 @@ Visibility="{TemplateBinding VerticalScrollBarVisibility}" Grid.Column="1" Focusable="False"/> + From 1bc56dc777cd1e41eaa72306877add569dd88023 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 15:36:29 +0100 Subject: [PATCH 19/51] overhaul scrollbar templates, to support pointer over, pointer pressed, and nice arrows. basically matches visual studio now. --- src/Avalonia.Themes.Default/RepeatButton.xaml | 6 +- src/Avalonia.Themes.Default/ScrollBar.xaml | 264 +++++++++--------- 2 files changed, 139 insertions(+), 131 deletions(-) diff --git a/src/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/RepeatButton.xaml index f555209471..702e4e6ebd 100644 --- a/src/Avalonia.Themes.Default/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/RepeatButton.xaml @@ -33,12 +33,8 @@ - - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml index c9552a607c..05689def0a 100644 --- a/src/Avalonia.Themes.Default/ScrollBar.xaml +++ b/src/Avalonia.Themes.Default/ScrollBar.xaml @@ -1,128 +1,140 @@ - - - - - - - - + + + + + + + + + + + + + + From 45028cd2eaeb4d56e3d4348fcaead360739438a7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 15:37:06 +0100 Subject: [PATCH 20/51] add 2 extra control colour levels like UWP, will need to rename these in 0.10, right now will be a breaking change. --- .../Accents/BaseDark.xaml | 7 ++++++- .../Accents/BaseLight.xaml | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml index 0ed17fae76..fbe74df1b2 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml @@ -14,7 +14,9 @@ #FFA0A0A0 #FF282828 #FF505050 + #FF686868 #FF808080 + #FFEFEBEF #FFA8A8A8 #FF828282 #FF505050 @@ -32,7 +34,9 @@ + + @@ -61,6 +65,7 @@ 12 16 - 10 + 20 + 9 diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index 3a8a8ec446..2643aae7b2 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -12,10 +12,15 @@ #FFAAAAAA #FF888888 #FF333333 - #FFFFFFFF - #FFAAAAAA - #FF888888 - #FFF0F0F0 + + + #FF868999 + #FFF5F5F5 + #FFC2C3C9 + #FF686868 + #FF5B5B5B + + #FFF0F0F0 #FFD0D0D0 #FF808080 #FF000000 @@ -32,7 +37,9 @@ + + @@ -61,6 +68,7 @@ 12 16 - 10 + 20 + 9 From 7658791ab9e49c416b3934c60047862a3d1d5b9a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 16:52:46 +0100 Subject: [PATCH 21/51] fix centralization of buttons. --- src/Avalonia.Themes.Default/ScrollBar.xaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml index 05689def0a..856fba00c0 100644 --- a/src/Avalonia.Themes.Default/ScrollBar.xaml +++ b/src/Avalonia.Themes.Default/ScrollBar.xaml @@ -5,12 +5,12 @@ - - + - - + From ab54a9ced42c7c40c5aa9556e201b4071ccde0b1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 16:57:48 +0100 Subject: [PATCH 22/51] fix indentation --- src/Avalonia.Themes.Default/Accents/BaseDark.xaml | 8 ++++---- src/Avalonia.Themes.Default/Accents/BaseLight.xaml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml index fbe74df1b2..e040ed233e 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml @@ -34,9 +34,9 @@ - + - + @@ -65,7 +65,7 @@ 12 16 - 20 - 9 + 20 + 9 diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index 2643aae7b2..b56bd3eaf6 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -20,7 +20,7 @@ #FF686868 #FF5B5B5B - #FFF0F0F0 + #FFF0F0F0 #FFD0D0D0 #FF808080 #FF000000 @@ -37,9 +37,9 @@ - + - + @@ -68,7 +68,7 @@ 12 16 - 20 - 9 + 20 + 9 From 31e56bc155ed5fb9fb27b5ba2c56ec059248925f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 17:11:57 +0100 Subject: [PATCH 23/51] fix centralization of scroll buttons --- src/Avalonia.Themes.Default/Accents/BaseDark.xaml | 4 ++-- src/Avalonia.Themes.Default/Accents/BaseLight.xaml | 4 ++-- src/Avalonia.Themes.Default/ScrollBar.xaml | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml index e040ed233e..ffe3e92202 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml @@ -65,7 +65,7 @@ 12 16 - 20 - 9 + 18 + 8 diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index b56bd3eaf6..c0e5f47eed 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -68,7 +68,7 @@ 12 16 - 20 - 9 + 18 + 8 diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml index 856fba00c0..64a4399d16 100644 --- a/src/Avalonia.Themes.Default/ScrollBar.xaml +++ b/src/Avalonia.Themes.Default/ScrollBar.xaml @@ -10,7 +10,7 @@ Grid.Row="0" Focusable="False" MinHeight="{DynamicResource ScrollBarThickness}"> - + - + @@ -57,7 +57,7 @@ Grid.Column="0" Focusable="False" MinWidth="{DynamicResource ScrollBarThickness}"> - + - + From c1ce0d6b99745e6c21d3767149bfbf2cad99e4df Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Oct 2019 17:37:21 +0100 Subject: [PATCH 24/51] [OSX] fix scroll wheel speed. --- native/Avalonia.Native/src/OSX/window.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 9c7e9323e0..2a5acaea91 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -855,8 +855,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent if(type == Wheel) { - delta.X = [event scrollingDeltaX] / 5; - delta.Y = [event scrollingDeltaY] / 5; + delta.X = [event scrollingDeltaX] / 50; + delta.Y = [event scrollingDeltaY] / 50; if(delta.X == 0 && delta.Y == 0) { From 8f736db8fc71aa8d1545192559d603dd68245365 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 19 Oct 2019 18:32:45 +0300 Subject: [PATCH 25/51] Updated XamlIl --- src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index c7155c5f6c..6a6a28513e 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit c7155c5f6c1a5153ee2d8cd78e5d1524dd6744cf +Subproject commit 6a6a28513e5abddacaf52e088635ff52d98f079d From 61340acf4b86e1e9dd5e7e769c6b7438cb9104dd Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 19 Oct 2019 19:11:21 +0300 Subject: [PATCH 26/51] Disable IL verification for nuget version of compiler by default --- build/BuildTargets.targets | 1 + packages/Avalonia/AvaloniaBuildTasks.targets | 2 ++ src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs | 4 +++- src/Avalonia.Build.Tasks/Program.cs | 3 ++- src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs | 4 ++-- .../XamlIl/AvaloniaXamlIlRuntimeCompiler.cs | 6 +++--- src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/build/BuildTargets.targets b/build/BuildTargets.targets index 08ec039ec7..a5543cd050 100644 --- a/build/BuildTargets.targets +++ b/build/BuildTargets.targets @@ -2,6 +2,7 @@ $(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll true + true diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index a455e2b3fc..552713f94b 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -53,6 +53,7 @@ $(IntermediateOutputPath)/Avalonia/references $(IntermediateOutputPath)/Avalonia/original.dll + false !string.IsNullOrWhiteSpace(l)).ToArray(), - ProjectDirectory, OutputPath); + ProjectDirectory, OutputPath, VerifyIl); if (!res.Success) return false; if (!res.WrittenFile) @@ -66,6 +66,8 @@ namespace Avalonia.Build.Tasks public string ProjectDirectory { get; set; } public string OutputPath { get; set; } + + public bool VerifyIl { get; set; } public IBuildEngine BuildEngine { get; set; } public ITaskHost HostObject { get; set; } diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs index d356b15408..1909c4c6ec 100644 --- a/src/Avalonia.Build.Tasks/Program.cs +++ b/src/Avalonia.Build.Tasks/Program.cs @@ -28,7 +28,8 @@ namespace Avalonia.Build.Tasks ReferencesFilePath = args[1], OutputPath = args[2], BuildEngine = new ConsoleBuildEngine(), - ProjectDirectory = Directory.GetCurrentDirectory() + ProjectDirectory = Directory.GetCurrentDirectory(), + VerifyIl = true }.Execute() ? 0 : 2; diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 0dd52c9b48..e348eb0fbc 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -40,7 +40,7 @@ namespace Avalonia.Build.Tasks } public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output) + string output, bool verifyIl) { var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input); var asm = typeSystem.TargetAssemblyDefinition; @@ -65,7 +65,7 @@ namespace Avalonia.Build.Tasks var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem, xamlLanguage); - var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass); + var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass) { EnableIlVerification = verifyIl }; var editorBrowsableAttribute = typeSystem .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute")) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs index b91d679fba..5a5da518d0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs @@ -114,10 +114,10 @@ namespace Avalonia.Markup.Xaml.XamlIl InitializeSre(); var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly); - + var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_sreTypeSystem, asm, - _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter), - _sreContextType); + _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter), + _sreContextType) { EnableIlVerification = true }; var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri); IXamlIlType overrideType = null; diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 6a6a28513e..88468ebe90 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit 6a6a28513e5abddacaf52e088635ff52d98f079d +Subproject commit 88468ebe90112dc23b04147deba0d9a0dcc99157 From 842958e551aca5e616caf7f70dc3b691eb9c2e90 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 19 Oct 2019 21:26:19 +0300 Subject: [PATCH 27/51] Updated XamlIl --- src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 88468ebe90..ad9915e193 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit 88468ebe90112dc23b04147deba0d9a0dcc99157 +Subproject commit ad9915e19398a49c5a11b66000c361659ca692b3 From 919f6fc5186c31c52c95b4b76c07239d421786b9 Mon Sep 17 00:00:00 2001 From: nishanth2143 Date: Mon, 21 Oct 2019 20:07:33 +0530 Subject: [PATCH 28/51] Updated README.md file Corrected grammatical errors --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index ee44a0cc3f..512b35a454 100644 --- a/readme.md +++ b/readme.md @@ -8,9 +8,9 @@ ## About -**Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS. +**Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), macOS and with experimental support for Android and iOS. -**Avalonia** is ready for **General-Purpose Desktop App Development**. However there may be some bugs and breaking changes as we continue along into this project's development. To see the status for some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239). +**Avalonia** is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. To see the status of some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239). | Control catalog | Desktop platforms | Mobile platforms | |---|---|---| @@ -20,11 +20,11 @@ Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (screenshot). Now you can write code and markup that will work on multiple platforms! -For those without Visual Studio, starter guide for .NET Core CLI can be found [here](http://avaloniaui.net/docs/quickstart/create-new-project#net-core). +For those without Visual Studio, a starter guide for .NET Core CLI can be found [here](http://avaloniaui.net/docs/quickstart/create-new-project#net-core). Avalonia is delivered via NuGet package manager. You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed)) -Use these commands in Package Manager console to install Avalonia manually: +Use these commands in the Package Manager console to install Avalonia manually: ``` Install-Package Avalonia Install-Package Avalonia.Desktop From 47c9f53e9a4aadf9d626541082326259e5684b6d Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 22 Oct 2019 00:38:39 +0800 Subject: [PATCH 29/51] Don't reopen an already active submenu on hover. --- .../Platform/DefaultMenuInteractionHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index b5dbd1e668..97aeead608 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -273,7 +273,8 @@ namespace Avalonia.Controls.Platform if (item.IsTopLevel) { - if (item.Parent.SelectedItem?.IsSubMenuOpen == true) + if (item != item.Parent.SelectedItem && + item.Parent.SelectedItem?.IsSubMenuOpen == true) { item.Parent.SelectedItem.Close(); SelectItemAndAncestors(item); From 1f2a37bcb77a98c66cdbf566dc71f24a38b94daf Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 21 Oct 2019 22:17:26 +0100 Subject: [PATCH 30/51] remove vertical seperator on menus between icon and menu header, polished spacing, now closely matches Windows and Visual Studio menus --- src/Avalonia.Themes.Default/ContextMenu.xaml | 8 ------ src/Avalonia.Themes.Default/MenuItem.xaml | 29 +++++--------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/ContextMenu.xaml index 53d7c5abb4..75f8f7c23d 100644 --- a/src/Avalonia.Themes.Default/ContextMenu.xaml +++ b/src/Avalonia.Themes.Default/ContextMenu.xaml @@ -11,19 +11,11 @@ BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> - - - diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index a794d15577..93989d3782 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -4,14 +4,14 @@