using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Rendering;
namespace Avalonia.Controls
{
///
/// Base class for menu controls.
///
public abstract class MenuBase : SelectingItemsControl, IFocusScope, IMenu
{
///
/// Defines the property.
///
public static readonly DirectProperty IsOpenProperty =
AvaloniaProperty.RegisterDirect(
nameof(IsOpen),
o => o.IsOpen);
///
/// Defines the event.
///
public static readonly RoutedEvent MenuOpenedEvent =
RoutedEvent.Register(nameof(MenuOpened), RoutingStrategies.Bubble);
///
/// Defines the event.
///
public static readonly RoutedEvent MenuClosedEvent =
RoutedEvent.Register(nameof(MenuClosed), RoutingStrategies.Bubble);
private bool _isOpen;
///
/// Initializes a new instance of the class.
///
protected MenuBase()
{
InteractionHandler = new DefaultMenuInteractionHandler(false);
}
///
/// Initializes a new instance of the class.
///
/// The menu interaction handler.
protected MenuBase(IMenuInteractionHandler interactionHandler)
{
InteractionHandler = interactionHandler ?? throw new ArgumentNullException(nameof(interactionHandler));
}
///
/// Initializes static members of the class.
///
static MenuBase()
{
MenuItem.SubmenuOpenedEvent.AddClassHandler((x, e) => x.OnSubmenuOpened(e));
}
///
/// Gets a value indicating whether the menu is open.
///
public bool IsOpen
{
get { return _isOpen; }
protected set { SetAndRaise(IsOpenProperty, ref _isOpen, value); }
}
///
IMenuInteractionHandler IMenu.InteractionHandler => InteractionHandler;
IRenderRoot? IMenu.VisualRoot => VisualRoot;
///
IMenuItem? IMenuElement.SelectedItem
{
get
{
var index = SelectedIndex;
return (index != -1) ?
(IMenuItem?)ContainerFromIndex(index) :
null;
}
set
{
SelectedIndex = value is Control c ?
IndexFromContainer(c) : -1;
}
}
///
IEnumerable IMenuElement.SubItems => GetRealizedContainers().OfType();
///
/// Gets the interaction handler for the menu.
///
protected IMenuInteractionHandler InteractionHandler { get; }
///
/// Occurs when a is opened.
///
public event EventHandler? MenuOpened
{
add { AddHandler(MenuOpenedEvent, value); }
remove { RemoveHandler(MenuOpenedEvent, value); }
}
///
/// Occurs when a is closed.
///
public event EventHandler? MenuClosed
{
add { AddHandler(MenuClosedEvent, value); }
remove { RemoveHandler(MenuClosedEvent, value); }
}
///
/// Closes the menu.
///
public abstract void Close();
///
/// Opens the menu.
///
public abstract void Open();
///
bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap);
protected internal override Control CreateContainerForItemOverride() => new MenuItem();
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is MenuItem or Separator;
///
protected override void OnKeyDown(KeyEventArgs e)
{
// Don't handle here: let the interaction handler handle it.
}
///
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
InteractionHandler.Attach(this);
}
///
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
InteractionHandler.Detach(this);
}
///
/// Called when a submenu opens somewhere in the menu.
///
/// The event args.
protected virtual void OnSubmenuOpened(RoutedEventArgs e)
{
if (e.Source is MenuItem menuItem && menuItem.Parent == this)
{
foreach (var child in this.GetLogicalChildren().OfType