diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml
index fe1487a7f2..19ac68b15b 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml
+++ b/samples/IntegrationTestApp/MainWindow.axaml
@@ -82,12 +82,16 @@
- None
+
+ None
+
+
+
diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs
index b9e631a312..9a612aa94d 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml.cs
+++ b/samples/IntegrationTestApp/MainWindow.axaml.cs
@@ -62,6 +62,8 @@ namespace IntegrationTestApp
this.FindControl("BasicComboBox").SelectedIndex = 0;
if (source?.Name == "ListBoxSelectionClear")
this.FindControl("BasicListBox").SelectedIndex = -1;
+ if (source?.Name == "MenuClickedMenuItemReset")
+ this.FindControl("ClickedMenuItem").Text = "None";
}
}
}
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index c03c6000d8..955af8888b 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -644,7 +644,9 @@ namespace Avalonia.Controls
/// The property change event.
private void IsSelectedChanged(AvaloniaPropertyChangedEventArgs e)
{
- if ((bool)e.NewValue!)
+ var parentMenu = Parent as Menu;
+
+ if ((bool)e.NewValue! && (parentMenu is null || parentMenu.IsOpen))
{
Focus();
}
diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index 23ecdb2e7b..6e9ac537f1 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -149,13 +149,18 @@ namespace Avalonia.Controls.Platform
case Key.Up:
case Key.Down:
{
- if (item?.IsTopLevel == true)
+ if (item?.IsTopLevel == true && item.HasSubMenu)
{
- if (item.HasSubMenu && !item.IsSubMenuOpen)
+ if (!item.IsSubMenuOpen)
{
Open(item, true);
- e.Handled = true;
}
+ else
+ {
+ item.MoveSelection(NavigationDirection.First, true);
+ }
+
+ e.Handled = true;
}
else
{
@@ -247,7 +252,8 @@ namespace Avalonia.Controls.Platform
// new menu.
if (item.IsSubMenuOpen &&
item.Parent is IMenu &&
- item.Parent.SelectedItem is object)
+ item.Parent.SelectedItem is object &&
+ item.Parent.SelectedItem != item)
{
item.Close();
Open(item.Parent.SelectedItem, true);
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index 9eedd17716..d22a67f389 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -53,6 +53,19 @@ namespace Avalonia.Controls.UnitTests.Platform
Assert.True(e.Handled);
}
+ [Fact]
+ public void Down_Selects_First_Item_Of_Already_Opened_Submenu()
+ {
+ var target = new DefaultMenuInteractionHandler(false);
+ var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.IsSubMenuOpen);
+ var e = new KeyEventArgs { Key = Key.Down, Source = item };
+
+ target.KeyDown(item, e);
+
+ Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
+ Assert.True(e.Handled);
+ }
+
[Fact]
public void Right_Selects_Next_MenuItem()
{
diff --git a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
index 15e22f4424..3eb8646835 100644
--- a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
@@ -29,6 +29,20 @@ namespace Avalonia.IntegrationTests.Appium
_ => throw new ArgumentOutOfRangeException($"Unexpected IsChecked value.")
};
+ public static bool GetIsFocused(this AppiumWebElement element)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ var active = element.WrappedDriver.SwitchTo().ActiveElement() as AppiumWebElement;
+ return element.Id == active?.Id;
+ }
+ else
+ {
+ // https://stackoverflow.com/questions/71807788/check-if-element-is-focused-in-appium
+ throw new NotSupportedException("Couldn't work out how to check if an element is focused on mac.");
+ }
+ }
+
public static void SendClick(this AppiumWebElement element)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -44,6 +58,11 @@ namespace Avalonia.IntegrationTests.Appium
}
}
+ public static void MovePointerOver(this AppiumWebElement element)
+ {
+ new Actions(element.WrappedDriver).MoveToElement(element).Perform();
+ }
+
public static string GetAttribute(AppiumWebElement element, string windows, string macOS)
{
return element.GetAttribute(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windows : macOS);
diff --git a/tests/Avalonia.IntegrationTests.Appium/MenuTests.cs b/tests/Avalonia.IntegrationTests.Appium/MenuTests.cs
index e9a433b975..98fb335061 100644
--- a/tests/Avalonia.IntegrationTests.Appium/MenuTests.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/MenuTests.cs
@@ -1,4 +1,7 @@
-using OpenQA.Selenium.Appium;
+using System.Threading;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Appium;
+using OpenQA.Selenium.Interactions;
using Xunit;
namespace Avalonia.IntegrationTests.Appium
@@ -15,6 +18,12 @@ namespace Avalonia.IntegrationTests.Appium
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Menu");
tab.Click();
+
+ var reset = _session.FindElementByAccessibilityId("MenuClickedMenuItemReset");
+ reset.Click();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("None", clickedMenuItem.Text);
}
[Fact]
@@ -48,16 +57,107 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Grandchild", clickedMenuItem.Text);
}
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Child_With_Alt_Arrow_Keys()
+ {
+ new Actions(_session)
+ .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
+ .SendKeys(Keys.Down + Keys.Enter)
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Child 1", clickedMenuItem.Text);
+ }
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Grandchild_With_Alt_Arrow_Keys()
+ {
+ new Actions(_session)
+ .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
+ .SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter)
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Grandchild", clickedMenuItem.Text);
+ }
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Child_With_Alt_Access_Keys()
+ {
+ new Actions(_session)
+ .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
+ .SendKeys("rc")
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Child 1", clickedMenuItem.Text);
+ }
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Grandchild_With_Alt_Access_Keys()
+ {
+ new Actions(_session)
+ .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
+ .SendKeys("rhg")
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Grandchild", clickedMenuItem.Text);
+ }
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Child_With_Click_Arrow_Keys()
+ {
+ var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
+ rootMenuItem.SendClick();
+
+ new Actions(_session)
+ .SendKeys(Keys.Down + Keys.Enter)
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Child 1", clickedMenuItem.Text);
+ }
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void Select_Grandchild_With_Click_Arrow_Keys()
+ {
+ var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
+ rootMenuItem.SendClick();
+
+ new Actions(_session)
+ .SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter)
+ .Perform();
+
+ var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem");
+ Assert.Equal("_Grandchild", clickedMenuItem.Text);
+ }
+
[PlatformFact(SkipOnOSX = true)]
public void Child_AcceleratorKey()
{
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
-
+
rootMenuItem.SendClick();
var childMenuItem = _session.FindElementByAccessibilityId("Child1MenuItem");
Assert.Equal("Ctrl+O", childMenuItem.GetAttribute("AcceleratorKey"));
}
+
+ [PlatformFact(SkipOnOSX = true)]
+ public void PointerOver_Does_Not_Steal_Focus()
+ {
+ // Issue #7906
+ var textBox = _session.FindElementByAccessibilityId("MenuFocusTest");
+ textBox.Click();
+
+ Assert.True(textBox.GetIsFocused());
+
+ var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
+ rootMenuItem.MovePointerOver();
+
+ Assert.True(textBox.GetIsFocused());
+ }
}
}