Browse Source

Fix keyboard interaction for ContextMenu.

pull/2332/head
Steven Kirk 7 years ago
parent
commit
4a5e11f6aa
  1. 7
      src/Avalonia.Controls/ContextMenu.cs
  2. 2
      src/Avalonia.Controls/Menu.cs
  3. 5
      src/Avalonia.Controls/MenuBase.cs
  4. 80
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  5. 2
      src/Avalonia.Input/InputElement.cs
  6. 71
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

7
src/Avalonia.Controls/ContextMenu.cs

@ -24,6 +24,7 @@ namespace Avalonia.Controls
/// Initializes a new instance of the <see cref="ContextMenu"/> class. /// Initializes a new instance of the <see cref="ContextMenu"/> class.
/// </summary> /// </summary>
public ContextMenu() public ContextMenu()
: this(new DefaultMenuInteractionHandler(true))
{ {
} }
@ -99,6 +100,7 @@ namespace Avalonia.Controls
ObeyScreenEdges = true ObeyScreenEdges = true
}; };
_popup.Opened += PopupOpened;
_popup.Closed += PopupClosed; _popup.Closed += PopupClosed;
} }
@ -127,6 +129,11 @@ namespace Avalonia.Controls
return new MenuItemContainerGenerator(this); return new MenuItemContainerGenerator(this);
} }
private void PopupOpened(object sender, EventArgs e)
{
Focus();
}
private void PopupClosed(object sender, EventArgs e) private void PopupClosed(object sender, EventArgs e)
{ {
var contextMenu = (sender as Popup)?.Child as ContextMenu; var contextMenu = (sender as Popup)?.Child as ContextMenu;

2
src/Avalonia.Controls/Menu.cs

@ -11,7 +11,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A top-level menu control. /// A top-level menu control.
/// </summary> /// </summary>
public class Menu : MenuBase, IFocusScope, IMainMenu public class Menu : MenuBase, IMainMenu
{ {
private static readonly ITemplate<IPanel> DefaultPanel = private static readonly ITemplate<IPanel> DefaultPanel =
new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Horizontal }); new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Horizontal });

5
src/Avalonia.Controls/MenuBase.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Base class for menu controls. /// Base class for menu controls.
/// </summary> /// </summary>
public abstract class MenuBase : SelectingItemsControl, IMenu public abstract class MenuBase : SelectingItemsControl, IFocusScope, IMenu
{ {
/// <summary> /// <summary>
/// Defines the <see cref="IsOpen"/> property. /// Defines the <see cref="IsOpen"/> property.
@ -46,8 +46,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public MenuBase() public MenuBase()
{ {
InteractionHandler = AvaloniaLocator.Current.GetService<IMenuInteractionHandler>() ?? InteractionHandler = new DefaultMenuInteractionHandler(false);
new DefaultMenuInteractionHandler();
} }
/// <summary> /// <summary>

80
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -13,18 +13,21 @@ namespace Avalonia.Controls.Platform
/// </summary> /// </summary>
public class DefaultMenuInteractionHandler : IMenuInteractionHandler public class DefaultMenuInteractionHandler : IMenuInteractionHandler
{ {
private readonly bool _isContextMenu;
private IDisposable _inputManagerSubscription; private IDisposable _inputManagerSubscription;
private IRenderRoot _root; private IRenderRoot _root;
public DefaultMenuInteractionHandler() public DefaultMenuInteractionHandler(bool isContextMenu)
: this(Input.InputManager.Instance, DefaultDelayRun) : this(isContextMenu, Input.InputManager.Instance, DefaultDelayRun)
{ {
} }
public DefaultMenuInteractionHandler( public DefaultMenuInteractionHandler(
bool isContextMenu,
IInputManager inputManager, IInputManager inputManager,
Action<Action, TimeSpan> delayRun) Action<Action, TimeSpan> delayRun)
{ {
_isContextMenu = isContextMenu;
InputManager = inputManager; InputManager = inputManager;
DelayRun = delayRun; DelayRun = delayRun;
} }
@ -59,7 +62,7 @@ namespace Avalonia.Controls.Platform
window.Deactivated += WindowDeactivated; window.Deactivated += WindowDeactivated;
} }
_inputManagerSubscription = InputManager.Process.Subscribe(RawInput); _inputManagerSubscription = InputManager?.Process.Subscribe(RawInput);
} }
public virtual void Detach(IMenu menu) public virtual void Detach(IMenu menu)
@ -125,23 +128,16 @@ namespace Avalonia.Controls.Platform
protected internal virtual void KeyDown(object sender, KeyEventArgs e) protected internal virtual void KeyDown(object sender, KeyEventArgs e)
{ {
var item = GetMenuItem(e.Source as IControl); KeyDown(GetMenuItem(e.Source as IControl), e);
if (item != null)
{
KeyDown(item, e);
}
} }
protected internal virtual void KeyDown(IMenuItem item, KeyEventArgs e) protected internal virtual void KeyDown(IMenuItem item, KeyEventArgs e)
{ {
Contract.Requires<ArgumentNullException>(item != null);
switch (e.Key) switch (e.Key)
{ {
case Key.Up: case Key.Up:
case Key.Down: case Key.Down:
if (item.IsTopLevel) if (item?.IsTopLevel == true)
{ {
if (item.HasSubMenu && !item.IsSubMenuOpen) if (item.HasSubMenu && !item.IsSubMenuOpen)
{ {
@ -156,7 +152,7 @@ namespace Avalonia.Controls.Platform
break; break;
case Key.Left: case Key.Left:
if (item.Parent is IMenuItem parent && !parent.IsTopLevel && parent.IsSubMenuOpen) if (item?.Parent is IMenuItem parent && !parent.IsTopLevel && parent.IsSubMenuOpen)
{ {
parent.Close(); parent.Close();
parent.Focus(); parent.Focus();
@ -169,7 +165,7 @@ namespace Avalonia.Controls.Platform
break; break;
case Key.Right: case Key.Right:
if (!item.IsTopLevel && item.HasSubMenu) if (item != null && !item.IsTopLevel && item.HasSubMenu)
{ {
Open(item, true); Open(item, true);
e.Handled = true; e.Handled = true;
@ -181,47 +177,65 @@ namespace Avalonia.Controls.Platform
break; break;
case Key.Enter: case Key.Enter:
if (!item.HasSubMenu) if (item != null)
{ {
Click(item); if (!item.HasSubMenu)
} {
else Click(item);
{ }
Open(item, true); else
} {
Open(item, true);
}
e.Handled = true; e.Handled = true;
}
break; break;
case Key.Escape: case Key.Escape:
if (item.Parent != null) if (item?.Parent != null)
{ {
item.Parent.Close(); item.Parent.Close();
item.Parent.Focus(); item.Parent.Focus();
e.Handled = true;
} }
else
{
Menu.Close();
}
e.Handled = true;
break; break;
default: default:
var direction = e.Key.ToNavigationDirection(); var direction = e.Key.ToNavigationDirection();
if (direction.HasValue && item.Parent?.MoveSelection(direction.Value, true) == true) if (direction.HasValue)
{ {
// If the the parent is an IMenu which successfully moved its selection, if (item == null && _isContextMenu)
// and the current menu is open then close the current menu and open the
// new menu.
if (item.IsSubMenuOpen && item.Parent is IMenu)
{ {
item.Close(); if (Menu.MoveSelection(direction.Value, true) == true)
Open(item.Parent.SelectedItem, true); {
e.Handled = 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
// new menu.
if (item.IsSubMenuOpen && item.Parent is IMenu)
{
item.Close();
Open(item.Parent.SelectedItem, true);
}
e.Handled = true;
} }
e.Handled = true;
} }
break; break;
} }
if (!e.Handled && item.Parent is IMenuItem parentItem) if (!e.Handled && item?.Parent is IMenuItem parentItem)
{ {
KeyDown(parentItem, e); KeyDown(parentItem, e);
} }

2
src/Avalonia.Input/InputElement.cs

@ -375,7 +375,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public void Focus() public void Focus()
{ {
FocusManager.Instance.Focus(this); FocusManager.Instance?.Focus(this);
} }
/// <inheritdoc/> /// <inheritdoc/>

71
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Up_Opens_MenuItem_With_SubMenu() public void Up_Opens_MenuItem_With_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Up, Source = item }; var e = new KeyEventArgs { Key = Key.Up, Source = item };
@ -27,7 +27,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Down_Opens_MenuItem_With_SubMenu() public void Down_Opens_MenuItem_With_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Down, Source = item }; var e = new KeyEventArgs { Key = Key.Down, Source = item };
@ -41,7 +41,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Right_Selects_Next_MenuItem() public void Right_Selects_Next_MenuItem()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Right, true) == true); var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Right, true) == true);
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Right, Source = item }; var e = new KeyEventArgs { Key = Key.Right, Source = item };
@ -55,7 +55,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Left_Selects_Previous_MenuItem() public void Left_Selects_Previous_MenuItem()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Left, true) == true); var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Left, true) == true);
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Left, Source = item }; var e = new KeyEventArgs { Key = Key.Left, Source = item };
@ -69,7 +69,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click() public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Enter, Source = item }; var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@ -84,7 +84,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu() public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Enter, Source = item }; var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Escape_Closes_Parent_Menu() public void Escape_Closes_Parent_Menu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Escape, Source = item }; var e = new KeyEventArgs { Key = Key.Escape, Source = item };
@ -113,7 +113,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerEnter_Opens_Item_When_Old_Item_Is_Open() public void PointerEnter_Opens_Item_When_Old_Item_Is_Open()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var item = Mock.Of<IMenuItem>(x => var item = Mock.Of<IMenuItem>(x =>
x.IsSubMenuOpen == true && x.IsSubMenuOpen == true &&
@ -141,7 +141,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerLeave_Deselects_Item_When_Menu_Not_Open() public void PointerLeave_Deselects_Item_When_Menu_Not_Open()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item }; var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open() public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item }; var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@ -175,7 +175,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Up_Selects_Previous_MenuItem() public void Up_Selects_Previous_MenuItem()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Up, Source = item }; var e = new KeyEventArgs { Key = Key.Up, Source = item };
@ -189,7 +189,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Down_Selects_Next_MenuItem() public void Down_Selects_Next_MenuItem()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Down, Source = item }; var e = new KeyEventArgs { Key = Key.Down, Source = item };
@ -203,7 +203,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Left_Closes_Parent_SubMenu() public void Left_Closes_Parent_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.HasSubMenu == true && x.IsSubMenuOpen == true); var parentItem = Mock.Of<IMenuItem>(x => x.HasSubMenu == true && x.IsSubMenuOpen == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Left, Source = item }; var e = new KeyEventArgs { Key = Key.Left, Source = item };
@ -218,7 +218,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Right_With_SubMenu_Items_Opens_SubMenu() public void Right_With_SubMenu_Items_Opens_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Right, Source = item }; var e = new KeyEventArgs { Key = Key.Right, Source = item };
@ -233,7 +233,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Right_On_TopLevel_Child_Navigates_TopLevel_Selection() public void Right_On_TopLevel_Child_Navigates_TopLevel_Selection()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => var parentItem = Mock.Of<IMenuItem>(x =>
x.IsSubMenuOpen == true && x.IsSubMenuOpen == true &&
@ -263,7 +263,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click() public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -279,7 +279,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu() public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Enter, Source = item }; var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@ -294,7 +294,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void Escape_Closes_Parent_MenuItem() public void Escape_Closes_Parent_MenuItem()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Escape, Source = item }; var e = new KeyEventArgs { Key = Key.Escape, Source = item };
@ -309,7 +309,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerEnter_Selects_Item() public void PointerEnter_Selects_Item()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -325,7 +325,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Opens_Submenu_After_Delay() public void PointerEnter_Opens_Submenu_After_Delay()
{ {
var timer = new TestTimer(); var timer = new TestTimer();
var target = new DefaultMenuInteractionHandler(null, timer.RunOnce); var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
@ -344,7 +344,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Closes_Sibling_Submenu_After_Delay() public void PointerEnter_Closes_Sibling_Submenu_After_Delay()
{ {
var timer = new TestTimer(); var timer = new TestTimer();
var target = new DefaultMenuInteractionHandler(null, timer.RunOnce); var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -365,7 +365,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerLeave_Deselects_Item() public void PointerLeave_Deselects_Item()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -381,7 +381,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Sibling() public void PointerLeave_Doesnt_Deselect_Sibling()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -398,7 +398,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu() public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true);
@ -413,7 +413,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerReleased_On_Item_With_No_SubMenu_Causes_Click() public void PointerReleased_On_Item_With_No_SubMenu_Causes_Click()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
@ -430,7 +430,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void Selection_Is_Correct_When_Pointer_Temporarily_Exits_Item_To_Select_SubItem() public void Selection_Is_Correct_When_Pointer_Temporarily_Exits_Item_To_Select_SubItem()
{ {
var timer = new TestTimer(); var timer = new TestTimer();
var target = new DefaultMenuInteractionHandler(null, timer.RunOnce); var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
@ -467,7 +467,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact] [Fact]
public void PointerPressed_On_Item_With_SubMenu_Causes_Opens_Submenu() public void PointerPressed_On_Item_With_SubMenu_Causes_Opens_Submenu()
{ {
var target = new DefaultMenuInteractionHandler(); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
@ -481,6 +481,23 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
} }
public class ContextMenu
{
[Fact]
public void Down_Selects_Selects_First_MenuItem_When_No_Selection()
{
var target = new DefaultMenuInteractionHandler(true);
var contextMenu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Down, true) == true);
var e = new KeyEventArgs { Key = Key.Down, Source = contextMenu };
target.Attach(contextMenu);
target.KeyDown(contextMenu, e);
Mock.Get(contextMenu).Verify(x => x.MoveSelection(NavigationDirection.Down, true));
Assert.True(e.Handled);
}
}
private class TestTimer private class TestTimer
{ {
private Action _action; private Action _action;

Loading…
Cancel
Save