|
|
|
@ -1,16 +1,18 @@ |
|
|
|
using System; |
|
|
|
using System.Reactive.Linq; |
|
|
|
using System.Linq; |
|
|
|
using System.ComponentModel; |
|
|
|
using Avalonia.Controls.Platform; |
|
|
|
using System.Collections.Generic; |
|
|
|
using Avalonia.Input; |
|
|
|
using Avalonia.LogicalTree; |
|
|
|
using Avalonia.Controls.Primitives; |
|
|
|
|
|
|
|
namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
using Input; |
|
|
|
using Interactivity; |
|
|
|
using LogicalTree; |
|
|
|
using Primitives; |
|
|
|
using System; |
|
|
|
using System.Reactive.Linq; |
|
|
|
using System.Linq; |
|
|
|
using System.ComponentModel; |
|
|
|
|
|
|
|
public class ContextMenu : SelectingItemsControl |
|
|
|
public class ContextMenu : SelectingItemsControl, IMenu |
|
|
|
{ |
|
|
|
private readonly IMenuInteractionHandler _interaction; |
|
|
|
private bool _isOpen; |
|
|
|
private Popup _popup; |
|
|
|
|
|
|
|
@ -20,6 +22,25 @@ namespace Avalonia.Controls |
|
|
|
public static readonly DirectProperty<ContextMenu, bool> IsOpenProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<ContextMenu, bool>(nameof(IsOpen), o => o.IsOpen); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="ContextMenu"/> class.
|
|
|
|
/// </summary>
|
|
|
|
public ContextMenu() |
|
|
|
{ |
|
|
|
_interaction = AvaloniaLocator.Current.GetService<IMenuInteractionHandler>() ?? |
|
|
|
new DefaultMenuInteractionHandler(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="ContextMenu"/> class.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="interactionHandler">The menu iteraction handler.</param>
|
|
|
|
public ContextMenu(IMenuInteractionHandler interactionHandler) |
|
|
|
{ |
|
|
|
Contract.Requires<ArgumentNullException>(interactionHandler != null); |
|
|
|
|
|
|
|
_interaction = interactionHandler; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes static members of the <see cref="ContextMenu"/> class.
|
|
|
|
@ -27,8 +48,6 @@ namespace Avalonia.Controls |
|
|
|
static ContextMenu() |
|
|
|
{ |
|
|
|
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); |
|
|
|
|
|
|
|
MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick, handledEventsToo: true); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -36,6 +55,36 @@ namespace Avalonia.Controls |
|
|
|
/// </summary>
|
|
|
|
public bool IsOpen => _isOpen; |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
IMenuInteractionHandler IMenu.InteractionHandler => _interaction; |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
IMenuItem IMenuElement.SelectedItem |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
var index = SelectedIndex; |
|
|
|
return (index != -1) ? |
|
|
|
(IMenuItem)ItemContainerGenerator.ContainerFromIndex(index) : |
|
|
|
null; |
|
|
|
} |
|
|
|
set |
|
|
|
{ |
|
|
|
SelectedIndex = ItemContainerGenerator.IndexFromContainer(value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
IEnumerable<IMenuItem> IMenuElement.SubItems |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return ItemContainerGenerator.Containers |
|
|
|
.Select(x => x.ContainerControl) |
|
|
|
.OfType<IMenuItem>(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Occurs when the value of the
|
|
|
|
/// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" />
|
|
|
|
@ -50,7 +99,6 @@ namespace Avalonia.Controls |
|
|
|
/// </summary>
|
|
|
|
public event CancelEventHandler ContextMenuClosing; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Called when the <see cref="Control.ContextMenu"/> property changes on a control.
|
|
|
|
/// </summary>
|
|
|
|
@ -71,62 +119,53 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Called when a submenu is clicked somewhere in the menu.
|
|
|
|
/// Opens the menu.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="e">The event args.</param>
|
|
|
|
private void OnContextMenuClick(RoutedEventArgs e) |
|
|
|
{ |
|
|
|
Hide(); |
|
|
|
FocusManager.Instance.Focus(null); |
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
public void Open() => Open(null); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Closes the menu.
|
|
|
|
/// Opens a context menu on the specified control.
|
|
|
|
/// </summary>
|
|
|
|
public void Hide() |
|
|
|
/// <param name="control">The control.</param>
|
|
|
|
public void Open(Control control) |
|
|
|
{ |
|
|
|
if (_popup != null && _popup.IsVisible) |
|
|
|
if (_popup == null) |
|
|
|
{ |
|
|
|
_popup.IsOpen = false; |
|
|
|
_popup = new Popup() |
|
|
|
{ |
|
|
|
PlacementMode = PlacementMode.Pointer, |
|
|
|
PlacementTarget = control, |
|
|
|
StaysOpen = false, |
|
|
|
ObeyScreenEdges = true |
|
|
|
}; |
|
|
|
|
|
|
|
_popup.Closed += PopupClosed; |
|
|
|
_interaction.Attach(this); |
|
|
|
} |
|
|
|
|
|
|
|
SelectedIndex = -1; |
|
|
|
((ISetLogicalParent)_popup).SetParent(control); |
|
|
|
_popup.Child = this; |
|
|
|
_popup.IsOpen = true; |
|
|
|
|
|
|
|
SetAndRaise(IsOpenProperty, ref _isOpen, false); |
|
|
|
SetAndRaise(IsOpenProperty, ref _isOpen, true); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Shows a context menu for the specified control.
|
|
|
|
/// Closes the menu.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="control">The control.</param>
|
|
|
|
private void Show(Control control) |
|
|
|
public void Close() |
|
|
|
{ |
|
|
|
if (control != null) |
|
|
|
if (_popup != null && _popup.IsVisible) |
|
|
|
{ |
|
|
|
if (_popup == null) |
|
|
|
{ |
|
|
|
_popup = new Popup() |
|
|
|
{ |
|
|
|
PlacementMode = PlacementMode.Pointer, |
|
|
|
PlacementTarget = control, |
|
|
|
StaysOpen = false, |
|
|
|
ObeyScreenEdges = true |
|
|
|
}; |
|
|
|
|
|
|
|
_popup.Closed += PopupClosed; |
|
|
|
} |
|
|
|
|
|
|
|
((ISetLogicalParent)_popup).SetParent(control); |
|
|
|
_popup.Child = this; |
|
|
|
_popup.IsOpen = false; |
|
|
|
} |
|
|
|
|
|
|
|
_popup.IsOpen = true; |
|
|
|
SelectedIndex = -1; |
|
|
|
|
|
|
|
SetAndRaise(IsOpenProperty, ref _isOpen, true); |
|
|
|
} |
|
|
|
SetAndRaise(IsOpenProperty, ref _isOpen, false); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PopupClosed(object sender, EventArgs e) |
|
|
|
private void PopupClosed(object sender, EventArgs e) |
|
|
|
{ |
|
|
|
var contextMenu = (sender as Popup)?.Child as ContextMenu; |
|
|
|
|
|
|
|
@ -152,7 +191,7 @@ namespace Avalonia.Controls |
|
|
|
if (contextMenu.CancelClosing()) |
|
|
|
return; |
|
|
|
|
|
|
|
control.ContextMenu.Hide(); |
|
|
|
control.ContextMenu.Close(); |
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
|
|
|
|
@ -161,7 +200,7 @@ namespace Avalonia.Controls |
|
|
|
if (contextMenu.CancelOpening()) |
|
|
|
return; |
|
|
|
|
|
|
|
contextMenu.Show(control); |
|
|
|
contextMenu.Open(control); |
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -179,5 +218,10 @@ namespace Avalonia.Controls |
|
|
|
ContextMenuOpening?.Invoke(this, eventArgs); |
|
|
|
return eventArgs.Cancel; |
|
|
|
} |
|
|
|
|
|
|
|
bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|