From 011e728c42781c97fcc52633e16ba4b0338ba537 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 26 Jan 2020 21:07:15 +0100 Subject: [PATCH 1/7] Allow for preallocating inner list size when `InsertRange` is called with non ICollection argument. --- src/Avalonia.Base/Collections/AvaloniaList.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 3c8d4ca7e6..ca51822eed 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -327,6 +327,8 @@ namespace Avalonia.Collections } else { + EnsureCapacity(_inner.Count + list.Count); + using (IEnumerator en = items.GetEnumerator()) { int insertIndex = index; @@ -550,6 +552,24 @@ namespace Avalonia.Collections /// Delegate[] INotifyCollectionChangedDebug.GetCollectionChangedSubscribers() => _collectionChanged?.GetInvocationList(); + private void EnsureCapacity(int capacity) + { + // Adapted from List implementation. + var currentCapacity = _inner.Capacity; + + if (currentCapacity < capacity) + { + var newCapacity = currentCapacity == 0 ? 4 : currentCapacity * 2; + + if (newCapacity < capacity) + { + newCapacity = capacity; + } + + _inner.Capacity = newCapacity; + } + } + /// /// Raises the event with an add action. /// From 14b13ac8f5c0d781fa8218d652e212efff51ea3c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 13:26:04 +0100 Subject: [PATCH 2/7] Added failing test for #3459. --- .../Platform/DefaultMenuInteractionHandlerTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs index 989bd744a6..97b6bf9d24 100644 --- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs @@ -199,6 +199,18 @@ namespace Avalonia.Controls.UnitTests.Platform menu.VerifySet(x => x.SelectedItem = null, Times.Never); Assert.False(e.Handled); } + + [Fact] + public void Doesnt_Throw_On_Menu_Keypress() + { + // Issue #3459 + var target = new DefaultMenuInteractionHandler(false); + var menu = Mock.Of(); + var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu); + var e = new KeyEventArgs { Key = Key.Tab, Source = menu }; + + target.KeyDown(menu, e); + } } public class NonTopLevel From a4a162fc78258be26ed63f44c4a04b1928d536cc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 13:39:39 +0100 Subject: [PATCH 3/7] Fix NRE in DefaultMenuInteractionHandler. Fixes #3459 --- src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 97aeead608..813fbf60cb 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -218,7 +218,7 @@ namespace Avalonia.Controls.Platform e.Handled = true; } } - else if (item.Parent?.MoveSelection(direction.Value, true) == true) + else if (item?.Parent?.MoveSelection(direction.Value, true) == true) { // If the the parent is an IMenu which successfully moved its selection, // and the current menu is open then close the current menu and open the From cfeb06473f92cd6c8bdafc12405011a565c70155 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 13:40:19 +0100 Subject: [PATCH 4/7] Enable null checking in DefaultMenuInteractionHandler. --- .../Platform/DefaultMenuInteractionHandler.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 813fbf60cb..bf811ad008 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -6,6 +6,8 @@ using Avalonia.LogicalTree; using Avalonia.Rendering; using Avalonia.Threading; +#nullable enable + namespace Avalonia.Controls.Platform { /// @@ -14,8 +16,8 @@ namespace Avalonia.Controls.Platform public class DefaultMenuInteractionHandler : IMenuInteractionHandler { private readonly bool _isContextMenu; - private IDisposable _inputManagerSubscription; - private IRenderRoot _root; + private IDisposable? _inputManagerSubscription; + private IRenderRoot? _root; public DefaultMenuInteractionHandler(bool isContextMenu) : this(isContextMenu, Input.InputManager.Instance, DefaultDelayRun) @@ -24,9 +26,11 @@ namespace Avalonia.Controls.Platform public DefaultMenuInteractionHandler( bool isContextMenu, - IInputManager inputManager, + IInputManager? inputManager, Action delayRun) { + delayRun = delayRun ?? throw new ArgumentNullException(nameof(delayRun)); + _isContextMenu = isContextMenu; InputManager = inputManager; DelayRun = delayRun; @@ -92,7 +96,7 @@ namespace Avalonia.Controls.Platform root.Deactivated -= WindowDeactivated; } - _inputManagerSubscription.Dispose(); + _inputManagerSubscription!.Dispose(); Menu = null; _root = null; @@ -100,9 +104,9 @@ namespace Avalonia.Controls.Platform protected Action DelayRun { get; } - protected IInputManager InputManager { get; } + protected IInputManager? InputManager { get; } - protected IMenu Menu { get; private set; } + protected IMenu? Menu { get; private set; } protected static TimeSpan MenuShowDelay { get; } = TimeSpan.FromMilliseconds(400); @@ -131,7 +135,7 @@ namespace Avalonia.Controls.Platform KeyDown(GetMenuItem(e.Source as IControl), e); } - protected internal virtual void KeyDown(IMenuItem item, KeyEventArgs e) + protected internal virtual void KeyDown(IMenuItem? item, KeyEventArgs e) { switch (e.Key) { @@ -200,7 +204,7 @@ namespace Avalonia.Controls.Platform } else { - Menu.Close(); + Menu!.Close(); } e.Handled = true; @@ -213,7 +217,7 @@ namespace Avalonia.Controls.Platform { if (item == null && _isContextMenu) { - if (Menu.MoveSelection(direction.Value, true) == true) + if (Menu!.MoveSelection(direction.Value, true) == true) { e.Handled = true; } @@ -408,7 +412,7 @@ namespace Avalonia.Controls.Platform protected void CloseMenu(IMenuItem item) { - var current = (IMenuElement)item; + var current = (IMenuElement?)item; while (current != null && !(current is IMenu)) { @@ -456,7 +460,7 @@ namespace Avalonia.Controls.Platform protected void SelectItemAndAncestors(IMenuItem item) { - var current = item; + var current = (IMenuItem?)item; while (current?.Parent != null) { @@ -465,7 +469,7 @@ namespace Avalonia.Controls.Platform } } - protected static IMenuItem GetMenuItem(IControl item) + protected static IMenuItem? GetMenuItem(IControl? item) { while (true) { From 89b62370a57fbd5504bccebe7580c244ad2ae117 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 14:21:35 +0100 Subject: [PATCH 5/7] Respect background/border in ItemsControl. Fixes #3431 --- src/Avalonia.Themes.Default/ItemsControl.xaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Default/ItemsControl.xaml b/src/Avalonia.Themes.Default/ItemsControl.xaml index f3def542fc..8bb0fc297c 100644 --- a/src/Avalonia.Themes.Default/ItemsControl.xaml +++ b/src/Avalonia.Themes.Default/ItemsControl.xaml @@ -1,10 +1,15 @@ From c8c96f57a36d63502e6b78191060eec480d60b70 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 14:41:34 +0100 Subject: [PATCH 6/7] Make test fail for #3325 --- tests/Avalonia.Input.UnitTests/GesturesTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Input.UnitTests/GesturesTests.cs b/tests/Avalonia.Input.UnitTests/GesturesTests.cs index 39c219a773..ffa03f9e8e 100644 --- a/tests/Avalonia.Input.UnitTests/GesturesTests.cs +++ b/tests/Avalonia.Input.UnitTests/GesturesTests.cs @@ -135,7 +135,7 @@ namespace Avalonia.Interactivity.UnitTests } [Fact] - public void DoubleTapped_Should_Be_Raised_For_Middle_Button() + public void DoubleTapped_Should_Not_Be_Raised_For_Middle_Button() { Border border = new Border(); var decorator = new Decorator @@ -149,7 +149,7 @@ namespace Avalonia.Interactivity.UnitTests _mouse.Click(border, MouseButton.Middle); _mouse.Down(border, MouseButton.Middle, clickCount: 2); - Assert.True(raised); + Assert.False(raised); } [Fact] From 3cc59007b0007dcf0d9a48656fe555265d3cb65c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 28 Jan 2020 14:42:06 +0100 Subject: [PATCH 7/7] Only fire DoubleTapped on left button press. Fixes #3325 --- src/Avalonia.Input/Gestures.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index a5bd4feb64..a656d08c16 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -79,7 +79,7 @@ namespace Avalonia.Input { s_lastPress = new WeakReference(e.Source); } - else if (s_lastPress != null && e.ClickCount == 2 && e.MouseButton != MouseButton.Right) + else if (s_lastPress != null && e.ClickCount == 2 && e.MouseButton == MouseButton.Left) { if (s_lastPress.TryGetTarget(out var target) && target == e.Source) {