diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 97aeead608..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,12 +217,12 @@ 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; } } - 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 @@ -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) { 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) { 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 @@ 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 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]