diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
index 932f3292ff..8f147638b5 100644
--- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml
+++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
@@ -10,7 +10,8 @@
HorizontalAlignment="Center"
Spacing="16">
+ Margin="16"
+ Padding="48,48,48,48">
@@ -31,7 +32,24 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
index dc73bef07a..96e8b49f89 100644
--- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
@@ -8,6 +9,7 @@ namespace ControlCatalog.Pages
public ContextMenuPage()
{
this.InitializeComponent();
+ DataContext = new ContextMenuPageViewModel();
}
private void InitializeComponent()
diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs
index 5a07cae89f..0a77607719 100644
--- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
using ReactiveUI;
namespace ControlCatalog.Pages
@@ -13,51 +14,7 @@ namespace ControlCatalog.Pages
public MenuPage()
{
this.InitializeComponent();
- var vm = new MenuPageViewModel();
-
- vm.MenuItems = new[]
- {
- new MenuItemViewModel
- {
- Header = "_File",
- Items = new[]
- {
- new MenuItemViewModel { Header = "_Open...", Command = vm.OpenCommand },
- new MenuItemViewModel { Header = "Save", Command = vm.SaveCommand },
- new MenuItemViewModel { Header = "-" },
- new MenuItemViewModel
- {
- Header = "Recent",
- Items = new[]
- {
- new MenuItemViewModel
- {
- Header = "File1.txt",
- Command = vm.OpenRecentCommand,
- CommandParameter = @"c:\foo\File1.txt"
- },
- new MenuItemViewModel
- {
- Header = "File2.txt",
- Command = vm.OpenRecentCommand,
- CommandParameter = @"c:\foo\File2.txt"
- },
- }
- },
- }
- },
- new MenuItemViewModel
- {
- Header = "_Edit",
- Items = new[]
- {
- new MenuItemViewModel { Header = "_Copy" },
- new MenuItemViewModel { Header = "_Paste" },
- }
- }
- };
-
- DataContext = vm;
+ DataContext = new MenuPageViewModel();
}
private void InitializeComponent()
@@ -65,51 +22,4 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
}
-
- public class MenuPageViewModel
- {
- public MenuPageViewModel()
- {
- OpenCommand = ReactiveCommand.CreateFromTask(Open);
- SaveCommand = ReactiveCommand.Create(Save);
- OpenRecentCommand = ReactiveCommand.Create(OpenRecent);
- }
-
- public IReadOnlyList MenuItems { get; set; }
- public ReactiveCommand OpenCommand { get; }
- public ReactiveCommand SaveCommand { get; }
- public ReactiveCommand OpenRecentCommand { get; }
-
- public async Task Open()
- {
- var dialog = new OpenFileDialog();
- var result = await dialog.ShowAsync(App.Current.MainWindow);
-
- if (result != null)
- {
- foreach (var path in result)
- {
- System.Diagnostics.Debug.WriteLine($"Opened: {path}");
- }
- }
- }
-
- public void Save()
- {
- System.Diagnostics.Debug.WriteLine("Save");
- }
-
- public void OpenRecent(string path)
- {
- System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
- }
- }
-
- public class MenuItemViewModel
- {
- public string Header { get; set; }
- public ICommand Command { get; set; }
- public object CommandParameter { get; set; }
- public IList Items { get; set; }
- }
}
diff --git a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs
new file mode 100644
index 0000000000..d34e9af017
--- /dev/null
+++ b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs
@@ -0,0 +1,73 @@
+using System.Collections.Generic;
+using System.Reactive;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using ReactiveUI;
+
+namespace ControlCatalog.ViewModels
+{
+ public class ContextMenuPageViewModel
+ {
+ public ContextMenuPageViewModel()
+ {
+ OpenCommand = ReactiveCommand.CreateFromTask(Open);
+ SaveCommand = ReactiveCommand.Create(Save);
+ OpenRecentCommand = ReactiveCommand.Create(OpenRecent);
+
+ MenuItems = new[]
+ {
+ new MenuItemViewModel { Header = "_Open...", Command = OpenCommand },
+ new MenuItemViewModel { Header = "Save", Command = SaveCommand },
+ new MenuItemViewModel { Header = "-" },
+ new MenuItemViewModel
+ {
+ Header = "Recent",
+ Items = new[]
+ {
+ new MenuItemViewModel
+ {
+ Header = "File1.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File1.txt"
+ },
+ new MenuItemViewModel
+ {
+ Header = "File2.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File2.txt"
+ },
+ }
+ },
+ };
+ }
+
+ public IReadOnlyList MenuItems { get; set; }
+ public ReactiveCommand OpenCommand { get; }
+ public ReactiveCommand SaveCommand { get; }
+ public ReactiveCommand OpenRecentCommand { get; }
+
+ public async Task Open()
+ {
+ var dialog = new OpenFileDialog();
+ var result = await dialog.ShowAsync(App.Current.MainWindow);
+
+ if (result != null)
+ {
+ foreach (var path in result)
+ {
+ System.Diagnostics.Debug.WriteLine($"Opened: {path}");
+ }
+ }
+ }
+
+ public void Save()
+ {
+ System.Diagnostics.Debug.WriteLine("Save");
+ }
+
+ public void OpenRecent(string path)
+ {
+ System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
+ }
+ }
+}
diff --git a/samples/ControlCatalog/ViewModels/MenuItemViewModel.cs b/samples/ControlCatalog/ViewModels/MenuItemViewModel.cs
new file mode 100644
index 0000000000..1e4a764efc
--- /dev/null
+++ b/samples/ControlCatalog/ViewModels/MenuItemViewModel.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Windows.Input;
+
+namespace ControlCatalog.ViewModels
+{
+ public class MenuItemViewModel
+ {
+ public string Header { get; set; }
+ public ICommand Command { get; set; }
+ public object CommandParameter { get; set; }
+ public IList Items { get; set; }
+ }
+}
diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
new file mode 100644
index 0000000000..038f3574cc
--- /dev/null
+++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+using System.Reactive;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using ReactiveUI;
+
+namespace ControlCatalog.ViewModels
+{
+ public class MenuPageViewModel
+ {
+ public MenuPageViewModel()
+ {
+ OpenCommand = ReactiveCommand.CreateFromTask(Open);
+ SaveCommand = ReactiveCommand.Create(Save);
+ OpenRecentCommand = ReactiveCommand.Create(OpenRecent);
+
+ MenuItems = new[]
+ {
+ new MenuItemViewModel
+ {
+ Header = "_File",
+ Items = new[]
+ {
+ new MenuItemViewModel { Header = "_Open...", Command = OpenCommand },
+ new MenuItemViewModel { Header = "Save", Command = SaveCommand },
+ new MenuItemViewModel { Header = "-" },
+ new MenuItemViewModel
+ {
+ Header = "Recent",
+ Items = new[]
+ {
+ new MenuItemViewModel
+ {
+ Header = "File1.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File1.txt"
+ },
+ new MenuItemViewModel
+ {
+ Header = "File2.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File2.txt"
+ },
+ }
+ },
+ }
+ },
+ new MenuItemViewModel
+ {
+ Header = "_Edit",
+ Items = new[]
+ {
+ new MenuItemViewModel { Header = "_Copy" },
+ new MenuItemViewModel { Header = "_Paste" },
+ }
+ }
+ };
+ }
+
+ public IReadOnlyList MenuItems { get; set; }
+ public ReactiveCommand OpenCommand { get; }
+ public ReactiveCommand SaveCommand { get; }
+ public ReactiveCommand OpenRecentCommand { get; }
+
+ public async Task Open()
+ {
+ var dialog = new OpenFileDialog();
+ var result = await dialog.ShowAsync(App.Current.MainWindow);
+
+ if (result != null)
+ {
+ foreach (var path in result)
+ {
+ System.Diagnostics.Debug.WriteLine($"Opened: {path}");
+ }
+ }
+ }
+
+ public void Save()
+ {
+ System.Diagnostics.Debug.WriteLine("Save");
+ }
+
+ public void OpenRecent(string path)
+ {
+ System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index a69152c42b..92293a32d6 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -1,34 +1,31 @@
using System;
-using System.Reactive.Linq;
-using System.Linq;
using System.ComponentModel;
+using System.Linq;
+using System.Reactive.Linq;
+using Avalonia.Controls.Generators;
using Avalonia.Controls.Platform;
-using System.Collections.Generic;
+using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
-using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
{
- public class ContextMenu : SelectingItemsControl, IMenu
+ ///
+ /// A control context menu.
+ ///
+ public class ContextMenu : MenuBase
{
- private readonly IMenuInteractionHandler _interaction;
- private bool _isOpen;
+ private static readonly ITemplate DefaultPanel =
+ new FuncTemplate(() => new StackPanel { Orientation = Orientation.Vertical });
private Popup _popup;
- ///
- /// Defines the property.
- ///
- public static readonly DirectProperty IsOpenProperty =
- AvaloniaProperty.RegisterDirect(nameof(IsOpen), o => o.IsOpen);
-
///
/// Initializes a new instance of the class.
///
public ContextMenu()
+ : this(new DefaultMenuInteractionHandler(true))
{
- _interaction = AvaloniaLocator.Current.GetService() ??
- new DefaultMenuInteractionHandler();
}
///
@@ -36,10 +33,8 @@ namespace Avalonia.Controls
///
/// The menu interaction handler.
public ContextMenu(IMenuInteractionHandler interactionHandler)
+ : base(interactionHandler)
{
- Contract.Requires(interactionHandler != null);
-
- _interaction = interactionHandler;
}
///
@@ -47,44 +42,10 @@ namespace Avalonia.Controls
///
static ContextMenu()
{
+ ItemsPanelProperty.OverrideDefaultValue(typeof(ContextMenu), DefaultPanel);
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
}
- ///
- /// Gets a value indicating whether the popup is open
- ///
- public bool IsOpen => _isOpen;
-
- ///
- IMenuInteractionHandler IMenu.InteractionHandler => _interaction;
-
- ///
- IMenuItem IMenuElement.SelectedItem
- {
- get
- {
- var index = SelectedIndex;
- return (index != -1) ?
- (IMenuItem)ItemContainerGenerator.ContainerFromIndex(index) :
- null;
- }
- set
- {
- SelectedIndex = ItemContainerGenerator.IndexFromContainer(value);
- }
- }
-
- ///
- IEnumerable IMenuElement.SubItems
- {
- get
- {
- return ItemContainerGenerator.Containers
- .Select(x => x.ContainerControl)
- .OfType();
- }
- }
-
///
/// Occurs when the value of the
///
@@ -121,7 +82,7 @@ namespace Avalonia.Controls
///
/// Opens the menu.
///
- public void Open() => Open(null);
+ public override void Open() => Open(null);
///
/// Opens a context menu on the specified control.
@@ -139,21 +100,20 @@ namespace Avalonia.Controls
ObeyScreenEdges = true
};
+ _popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
- _interaction.Attach(this);
}
((ISetLogicalParent)_popup).SetParent(control);
_popup.Child = this;
_popup.IsOpen = true;
-
- SetAndRaise(IsOpenProperty, ref _isOpen, true);
+ IsOpen = true;
}
///
/// Closes the menu.
///
- public void Close()
+ public override void Close()
{
if (_popup != null && _popup.IsVisible)
{
@@ -161,8 +121,17 @@ namespace Avalonia.Controls
}
SelectedIndex = -1;
+ IsOpen = false;
+ }
+
+ protected override IItemContainerGenerator CreateItemContainerGenerator()
+ {
+ return new MenuItemContainerGenerator(this);
+ }
- SetAndRaise(IsOpenProperty, ref _isOpen, false);
+ private void PopupOpened(object sender, EventArgs e)
+ {
+ Focus();
}
private void PopupClosed(object sender, EventArgs e)
@@ -176,7 +145,7 @@ namespace Avalonia.Controls
i.IsSubMenuOpen = false;
}
- contextMenu._isOpen = false;
+ contextMenu.IsOpen = false;
contextMenu.SelectedIndex = -1;
}
}
@@ -186,7 +155,7 @@ namespace Avalonia.Controls
var control = (Control)sender;
var contextMenu = control.ContextMenu;
- if (control.ContextMenu._isOpen)
+ if (control.ContextMenu.IsOpen)
{
if (contextMenu.CancelClosing())
return;
@@ -218,10 +187,5 @@ namespace Avalonia.Controls
ContextMenuOpening?.Invoke(this, eventArgs);
return eventArgs.Cancel;
}
-
- bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap)
- {
- throw new NotImplementedException();
- }
}
}
diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs
index 00fca385a0..b0fb3f2b3b 100644
--- a/src/Avalonia.Controls/Menu.cs
+++ b/src/Avalonia.Controls/Menu.cs
@@ -1,56 +1,26 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Controls.Generators;
using Avalonia.Controls.Platform;
-using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
///
/// A top-level menu control.
///
- public class Menu : SelectingItemsControl, IFocusScope, IMainMenu, IMenu
+ public class Menu : MenuBase, IMainMenu
{
- ///
- /// Defines the property.
- ///
- public static readonly DirectProperty
public class DefaultMenuInteractionHandler : IMenuInteractionHandler
{
+ private readonly bool _isContextMenu;
private IDisposable _inputManagerSubscription;
private IRenderRoot _root;
- public DefaultMenuInteractionHandler()
- : this(Input.InputManager.Instance, DefaultDelayRun)
+ public DefaultMenuInteractionHandler(bool isContextMenu)
+ : this(isContextMenu, Input.InputManager.Instance, DefaultDelayRun)
{
}
public DefaultMenuInteractionHandler(
+ bool isContextMenu,
IInputManager inputManager,
Action delayRun)
{
+ _isContextMenu = isContextMenu;
InputManager = inputManager;
DelayRun = delayRun;
}
@@ -59,7 +62,7 @@ namespace Avalonia.Controls.Platform
window.Deactivated += WindowDeactivated;
}
- _inputManagerSubscription = InputManager.Process.Subscribe(RawInput);
+ _inputManagerSubscription = InputManager?.Process.Subscribe(RawInput);
}
public virtual void Detach(IMenu menu)
@@ -125,23 +128,16 @@ namespace Avalonia.Controls.Platform
protected internal virtual void KeyDown(object sender, KeyEventArgs e)
{
- var item = GetMenuItem(e.Source as IControl);
-
- if (item != null)
- {
- KeyDown(item, e);
- }
+ KeyDown(GetMenuItem(e.Source as IControl), e);
}
protected internal virtual void KeyDown(IMenuItem item, KeyEventArgs e)
{
- Contract.Requires(item != null);
-
switch (e.Key)
{
case Key.Up:
case Key.Down:
- if (item.IsTopLevel)
+ if (item?.IsTopLevel == true)
{
if (item.HasSubMenu && !item.IsSubMenuOpen)
{
@@ -156,7 +152,7 @@ namespace Avalonia.Controls.Platform
break;
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.Focus();
@@ -169,7 +165,7 @@ namespace Avalonia.Controls.Platform
break;
case Key.Right:
- if (!item.IsTopLevel && item.HasSubMenu)
+ if (item != null && !item.IsTopLevel && item.HasSubMenu)
{
Open(item, true);
e.Handled = true;
@@ -181,47 +177,65 @@ namespace Avalonia.Controls.Platform
break;
case Key.Enter:
- if (!item.HasSubMenu)
+ if (item != null)
{
- Click(item);
- }
- else
- {
- Open(item, true);
- }
+ if (!item.HasSubMenu)
+ {
+ Click(item);
+ }
+ else
+ {
+ Open(item, true);
+ }
- e.Handled = true;
+ e.Handled = true;
+ }
break;
case Key.Escape:
- if (item.Parent != null)
+ if (item?.Parent != null)
{
item.Parent.Close();
item.Parent.Focus();
- e.Handled = true;
}
+ else
+ {
+ Menu.Close();
+ }
+
+ e.Handled = true;
break;
default:
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,
- // and the current menu is open then close the current menu and open the
- // new menu.
- if (item.IsSubMenuOpen && item.Parent is IMenu)
+ if (item == null && _isContextMenu)
{
- item.Close();
- Open(item.Parent.SelectedItem, true);
+ if (Menu.MoveSelection(direction.Value, true) == 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;
}
- if (!e.Handled && item.Parent is IMenuItem parentItem)
+ if (!e.Handled && item?.Parent is IMenuItem parentItem)
{
KeyDown(parentItem, e);
}
diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs
index e7fea94d3d..07e04486ec 100644
--- a/src/Avalonia.Input/InputElement.cs
+++ b/src/Avalonia.Input/InputElement.cs
@@ -375,7 +375,7 @@ namespace Avalonia.Input
///
public void Focus()
{
- FocusManager.Instance.Focus(this);
+ FocusManager.Instance?.Focus(this);
}
///
diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/ContextMenu.xaml
index bfa26d3528..53d7c5abb4 100644
--- a/src/Avalonia.Themes.Default/ContextMenu.xaml
+++ b/src/Avalonia.Themes.Default/ContextMenu.xaml
@@ -10,12 +10,22 @@
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
-
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index e17279013d..87b235dce7 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Up_Opens_MenuItem_With_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Up, Source = item };
@@ -27,7 +27,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Down_Opens_MenuItem_With_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Down, Source = item };
@@ -41,7 +41,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_Selects_Next_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of(x => x.MoveSelection(NavigationDirection.Right, true) == true);
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Right, Source = item };
@@ -55,7 +55,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Left_Selects_Previous_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of(x => x.MoveSelection(NavigationDirection.Left, true) == true);
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Left, Source = item };
@@ -69,7 +69,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ 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.Enter, Source = item };
@@ -84,7 +84,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Escape_Closes_Parent_Menu()
{
- var target = new DefaultMenuInteractionHandler();
+ 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.Escape, Source = item };
@@ -113,7 +113,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerEnter_Opens_Item_When_Old_Item_Is_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x =>
x.IsSubMenuOpen == true &&
@@ -141,7 +141,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Deselects_Item_When_Menu_Not_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@@ -175,7 +175,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Up_Selects_Previous_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Up, Source = item };
@@ -189,7 +189,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Down_Selects_Next_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Down, Source = item };
@@ -203,7 +203,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Left_Closes_Parent_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.HasSubMenu == true && x.IsSubMenuOpen == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Left, Source = item };
@@ -218,7 +218,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_With_SubMenu_Items_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Right, Source = item };
@@ -233,7 +233,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_On_TopLevel_Child_Navigates_TopLevel_Selection()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var parentItem = Mock.Of(x =>
x.IsSubMenuOpen == true &&
@@ -263,7 +263,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -279,7 +279,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@@ -294,7 +294,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Escape_Closes_Parent_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Escape, Source = item };
@@ -309,7 +309,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerEnter_Selects_Item()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -325,7 +325,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Opens_Submenu_After_Delay()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
@@ -344,7 +344,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Closes_Sibling_Submenu_After_Delay()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -365,7 +365,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Deselects_Item()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -381,7 +381,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Sibling()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -398,7 +398,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true);
@@ -413,7 +413,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerReleased_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(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()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
@@ -467,7 +467,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerPressed_On_Item_With_SubMenu_Causes_Opens_Submenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(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(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 Action _action;