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; #nullable enable 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. /// public MenuBase() { InteractionHandler = new DefaultMenuInteractionHandler(false); } /// /// Initializes a new instance of the class. /// /// The menu interaction handler. public 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; /// 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(); } } /// /// 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 override IItemContainerGenerator CreateItemContainerGenerator() { return new ItemContainerGenerator(this, MenuItem.HeaderProperty, null); } /// 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()) { if (child != menuItem && child.IsSubMenuOpen) { child.IsSubMenuOpen = false; } } } IsOpen = true; } } }