|
@@ -1,191 +1,5 @@
|
|
|
namespace Terminal.Gui;
|
|
|
|
|
|
-/// <summary>
|
|
|
-/// <see cref="MenuBarItem"/> is a menu item on <see cref="MenuBar"/>. MenuBarItems do not support
|
|
|
-/// <see cref="MenuItem.Shortcut"/>.
|
|
|
-/// </summary>
|
|
|
-public class MenuBarItem : MenuItem
|
|
|
-{
|
|
|
- /// <summary>Initializes a new <see cref="MenuBarItem"/> as a <see cref="MenuItem"/>.</summary>
|
|
|
- /// <param name="title">Title for the menu item.</param>
|
|
|
- /// <param name="help">Help text to display. Will be displayed next to the Title surrounded by parentheses.</param>
|
|
|
- /// <param name="action">Action to invoke when the menu item is activated.</param>
|
|
|
- /// <param name="canExecute">Function to determine if the action can currently be executed.</param>
|
|
|
- /// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
|
|
|
- public MenuBarItem (
|
|
|
- string title,
|
|
|
- string help,
|
|
|
- Action action,
|
|
|
- Func<bool> canExecute = null,
|
|
|
- MenuItem parent = null
|
|
|
- ) : base (title, help, action, canExecute, parent)
|
|
|
- {
|
|
|
- SetInitialProperties (title, null, null, true);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>Initializes a new <see cref="MenuBarItem"/>.</summary>
|
|
|
- /// <param name="title">Title for the menu item.</param>
|
|
|
- /// <param name="children">The items in the current menu.</param>
|
|
|
- /// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
|
|
|
- public MenuBarItem (string title, MenuItem [] children, MenuItem parent = null) { SetInitialProperties (title, children, parent); }
|
|
|
-
|
|
|
- /// <summary>Initializes a new <see cref="MenuBarItem"/> with separate list of items.</summary>
|
|
|
- /// <param name="title">Title for the menu item.</param>
|
|
|
- /// <param name="children">The list of items in the current menu.</param>
|
|
|
- /// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
|
|
|
- public MenuBarItem (string title, List<MenuItem []> children, MenuItem parent = null) { SetInitialProperties (title, children, parent); }
|
|
|
-
|
|
|
- /// <summary>Initializes a new <see cref="MenuBarItem"/>.</summary>
|
|
|
- /// <param name="children">The items in the current menu.</param>
|
|
|
- public MenuBarItem (MenuItem [] children) : this ("", children) { }
|
|
|
-
|
|
|
- /// <summary>Initializes a new <see cref="MenuBarItem"/>.</summary>
|
|
|
- public MenuBarItem () : this (new MenuItem [] { }) { }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets or sets an array of <see cref="MenuItem"/> objects that are the children of this
|
|
|
- /// <see cref="MenuBarItem"/>
|
|
|
- /// </summary>
|
|
|
- /// <value>The children.</value>
|
|
|
- public MenuItem [] Children { get; set; }
|
|
|
-
|
|
|
- internal bool IsTopLevel => Parent is null && (Children is null || Children.Length == 0) && Action != null;
|
|
|
-
|
|
|
- /// <summary>Get the index of a child <see cref="MenuItem"/>.</summary>
|
|
|
- /// <param name="children"></param>
|
|
|
- /// <returns>Returns a greater than -1 if the <see cref="MenuItem"/> is a child.</returns>
|
|
|
- public int GetChildrenIndex (MenuItem children)
|
|
|
- {
|
|
|
- var i = 0;
|
|
|
-
|
|
|
- if (Children is { })
|
|
|
- {
|
|
|
- foreach (MenuItem child in Children)
|
|
|
- {
|
|
|
- if (child == children)
|
|
|
- {
|
|
|
- return i;
|
|
|
- }
|
|
|
-
|
|
|
- i++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>Check if a <see cref="MenuItem"/> is a submenu of this MenuBar.</summary>
|
|
|
- /// <param name="menuItem"></param>
|
|
|
- /// <returns>Returns <c>true</c> if it is a submenu. <c>false</c> otherwise.</returns>
|
|
|
- public bool IsSubMenuOf (MenuItem menuItem)
|
|
|
- {
|
|
|
- foreach (MenuItem child in Children)
|
|
|
- {
|
|
|
- if (child == menuItem && child.Parent == menuItem.Parent)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>Check if a <see cref="MenuItem"/> is a <see cref="MenuBarItem"/>.</summary>
|
|
|
- /// <param name="menuItem"></param>
|
|
|
- /// <returns>Returns a <see cref="MenuBarItem"/> or null otherwise.</returns>
|
|
|
- public MenuBarItem SubMenu (MenuItem menuItem) { return menuItem as MenuBarItem; }
|
|
|
-
|
|
|
- internal void AddKeyBindings (MenuBar menuBar)
|
|
|
- {
|
|
|
- if (Children is null)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- foreach (MenuItem menuItem in Children.Where (m => m is { }))
|
|
|
- {
|
|
|
- if (menuItem.HotKey != default (Rune))
|
|
|
- {
|
|
|
- menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, Command.ToggleExpandCollapse);
|
|
|
-
|
|
|
- menuBar.KeyBindings.Add (
|
|
|
- (KeyCode)menuItem.HotKey.Value | KeyCode.AltMask,
|
|
|
- KeyBindingScope.HotKey,
|
|
|
- Command.ToggleExpandCollapse
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (menuItem.Shortcut != KeyCode.Null)
|
|
|
- {
|
|
|
- menuBar.KeyBindings.Add (menuItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
|
|
|
- }
|
|
|
-
|
|
|
- SubMenu (menuItem)?.AddKeyBindings (menuBar);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void SetInitialProperties (string title, object children, MenuItem parent = null, bool isTopLevel = false)
|
|
|
- {
|
|
|
- if (!isTopLevel && children is null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException (
|
|
|
- nameof (children),
|
|
|
- "The parameter cannot be null. Use an empty array instead."
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- SetTitle (title ?? "");
|
|
|
-
|
|
|
- if (parent is { })
|
|
|
- {
|
|
|
- Parent = parent;
|
|
|
- }
|
|
|
-
|
|
|
- if (children is List<MenuItem []> childrenList)
|
|
|
- {
|
|
|
- MenuItem [] newChildren = { };
|
|
|
-
|
|
|
- foreach (MenuItem [] grandChild in childrenList)
|
|
|
- {
|
|
|
- foreach (MenuItem child in grandChild)
|
|
|
- {
|
|
|
- SetParent (grandChild);
|
|
|
- Array.Resize (ref newChildren, newChildren.Length + 1);
|
|
|
- newChildren [newChildren.Length - 1] = child;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Children = newChildren;
|
|
|
- }
|
|
|
- else if (children is MenuItem [] items)
|
|
|
- {
|
|
|
- SetParent (items);
|
|
|
- Children = items;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Children = null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void SetParent (MenuItem [] children)
|
|
|
- {
|
|
|
- foreach (MenuItem child in children)
|
|
|
- {
|
|
|
- if (child is { Parent: null })
|
|
|
- {
|
|
|
- child.Parent = this;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void SetTitle (string title)
|
|
|
- {
|
|
|
- title ??= string.Empty;
|
|
|
- Title = title;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// <para>Provides a menu bar that spans the top of a <see cref="Toplevel"/> View with drop-down and cascading menus.</para>
|
|
|
/// <para>
|
|
@@ -236,7 +50,6 @@ public class MenuBar : View
|
|
|
internal bool _isMenuClosing;
|
|
|
internal bool _isMenuOpening;
|
|
|
|
|
|
- // BUGBUG: Hack
|
|
|
internal Menu _openMenu;
|
|
|
internal List<Menu> _openSubMenu;
|
|
|
internal int _selected;
|
|
@@ -308,9 +121,8 @@ public class MenuBar : View
|
|
|
return true;
|
|
|
}
|
|
|
);
|
|
|
-
|
|
|
- AddCommand (Command.ToggleExpandCollapse, () => SelectOrRun ());
|
|
|
- AddCommand (Command.Select, () => Run (_menuItemToSelect?.Action));
|
|
|
+ AddCommand (Command.ToggleExpandCollapse, ctx => Select ((int)ctx.KeyBinding?.Context!));
|
|
|
+ AddCommand (Command.Select, ctx => Run ((ctx.KeyBinding?.Context as MenuItem)?.Action));
|
|
|
|
|
|
// Default key bindings for this view
|
|
|
KeyBindings.Add (Key.CursorLeft, Command.Left);
|
|
@@ -318,13 +130,15 @@ public class MenuBar : View
|
|
|
KeyBindings.Add (Key.Esc, Command.Cancel);
|
|
|
KeyBindings.Add (Key.CursorDown, Command.Accept);
|
|
|
KeyBindings.Add (Key.Enter, Command.Accept);
|
|
|
- KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.ToggleExpandCollapse);
|
|
|
|
|
|
- KeyBindings.Add (
|
|
|
- KeyCode.CtrlMask | KeyCode.Space,
|
|
|
- KeyBindingScope.HotKey,
|
|
|
- Command.ToggleExpandCollapse
|
|
|
- );
|
|
|
+ KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
|
|
|
+ KeyBindings.Add (Key, keyBinding);
|
|
|
+
|
|
|
+ // TODO: Why do we have two keybindings for opening the menu? Ctrl-Space and Key?
|
|
|
+ KeyBindings.Add (Key.Space.WithCtrl, keyBinding);
|
|
|
+
|
|
|
+ // TODO: Figure out how to make Alt work (on Windows)
|
|
|
+ //KeyBindings.Add (Key.WithAlt, keyBinding);
|
|
|
}
|
|
|
|
|
|
/// <summary><see langword="true"/> if the menu is open; otherwise <see langword="true"/>.</summary>
|
|
@@ -350,43 +164,27 @@ public class MenuBar : View
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // TODO: Bindings (esp for hotkey) should be added across and then down. This currently does down then across.
|
|
|
- // TODO: As a result, _File._Save will have precedence over in "_File _Edit _ScrollbarView"
|
|
|
- // TODO: Also: Hotkeys should not work for sub-menus if they are not visible!
|
|
|
- foreach (MenuBarItem menuBarItem in Menus?.Where (m => m is { })!)
|
|
|
+ // TODO: Hotkeys should not work for sub-menus if they are not visible!
|
|
|
+ for (var i = 0; i < Menus.Length; i++)
|
|
|
{
|
|
|
- if (menuBarItem.HotKey != default (Rune))
|
|
|
+ MenuBarItem menuBarItem = Menus [i];
|
|
|
+
|
|
|
+ if (menuBarItem?.HotKey != default (Rune))
|
|
|
{
|
|
|
- KeyBindings.Add (
|
|
|
- (KeyCode)menuBarItem.HotKey.Value,
|
|
|
- Command.ToggleExpandCollapse
|
|
|
- );
|
|
|
-
|
|
|
- KeyBindings.Add (
|
|
|
- (KeyCode)menuBarItem.HotKey.Value | KeyCode.AltMask,
|
|
|
- KeyBindingScope.HotKey,
|
|
|
- Command.ToggleExpandCollapse
|
|
|
- );
|
|
|
+ KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, i);
|
|
|
+ KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value, keyBinding);
|
|
|
+ KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value | KeyCode.AltMask, keyBinding);
|
|
|
}
|
|
|
|
|
|
- if (menuBarItem.Shortcut != KeyCode.Null)
|
|
|
+ if (menuBarItem?.Shortcut != KeyCode.Null)
|
|
|
{
|
|
|
// Technically this will never run because MenuBarItems don't have shortcuts
|
|
|
- KeyBindings.Add (menuBarItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
|
|
|
+ KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, i);
|
|
|
+ KeyBindings.Add (menuBarItem.Shortcut, keyBinding);
|
|
|
}
|
|
|
|
|
|
- menuBarItem.AddKeyBindings (this);
|
|
|
+ menuBarItem?.AddShortcutKeyBindings (this);
|
|
|
}
|
|
|
-#if SUPPORT_ALT_TO_ACTIVATE_MENU
|
|
|
- // Enable the Alt key as a menu activator
|
|
|
- Initialized += (s, e) =>
|
|
|
- {
|
|
|
- if (SuperView is { })
|
|
|
- {
|
|
|
- SuperView.KeyUp += SuperView_KeyUp;
|
|
|
- }
|
|
|
- };
|
|
|
-#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -399,7 +197,7 @@ public class MenuBar : View
|
|
|
/// <summary>
|
|
|
/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
|
|
|
/// <para>
|
|
|
- /// By default any sub-sub-menus (sub-menus of the main <see cref="MenuItem"/>s) are displayed in a cascading
|
|
|
+ /// By default, any sub-sub-menus (sub-menus of the main <see cref="MenuItem"/>s) are displayed in a cascading
|
|
|
/// manner, where each sub-sub-menu pops out of the sub-menu frame (either to the right or left, depending on where
|
|
|
/// the sub-menu is relative to the edge of the screen). By setting <see cref="UseSubMenusSingleFrame"/> to
|
|
|
/// <see langword="true"/>, this behavior can be changed such that all sub-sub-menus are drawn within a single
|
|
@@ -436,7 +234,7 @@ public class MenuBar : View
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- internal Menu openCurrentMenu
|
|
|
+ internal Menu OpenCurrentMenu
|
|
|
{
|
|
|
get => _ocm;
|
|
|
set
|
|
@@ -486,7 +284,7 @@ public class MenuBar : View
|
|
|
if (i == _selected && IsMenuOpen)
|
|
|
{
|
|
|
hotColor = i == _selected ? ColorScheme.HotFocus : GetHotNormalColor ();
|
|
|
- normalColor = i == _selected ? GetFocusColor() : GetNormalColor ();
|
|
|
+ normalColor = i == _selected ? GetFocusColor () : GetNormalColor ();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -533,17 +331,17 @@ public class MenuBar : View
|
|
|
MenuItem mi = null;
|
|
|
MenuBarItem parent;
|
|
|
|
|
|
- if (openCurrentMenu.BarItems.Children != null
|
|
|
- && openCurrentMenu.BarItems!.Children.Length > 0
|
|
|
- && openCurrentMenu?._currentChild > -1)
|
|
|
+ if (OpenCurrentMenu.BarItems.Children != null
|
|
|
+ && OpenCurrentMenu.BarItems!.Children.Length > 0
|
|
|
+ && OpenCurrentMenu?._currentChild > -1)
|
|
|
{
|
|
|
- parent = openCurrentMenu.BarItems;
|
|
|
- mi = parent.Children [openCurrentMenu._currentChild];
|
|
|
+ parent = OpenCurrentMenu.BarItems;
|
|
|
+ mi = parent.Children [OpenCurrentMenu._currentChild];
|
|
|
}
|
|
|
- else if (openCurrentMenu!.BarItems.IsTopLevel)
|
|
|
+ else if (OpenCurrentMenu!.BarItems.IsTopLevel)
|
|
|
{
|
|
|
parent = null;
|
|
|
- mi = openCurrentMenu.BarItems;
|
|
|
+ mi = OpenCurrentMenu.BarItems;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -587,16 +385,16 @@ public class MenuBar : View
|
|
|
OpenMenu (_selected);
|
|
|
|
|
|
if (!SelectEnabledItem (
|
|
|
- openCurrentMenu.BarItems.Children,
|
|
|
- openCurrentMenu._currentChild,
|
|
|
- out openCurrentMenu._currentChild
|
|
|
+ OpenCurrentMenu.BarItems.Children,
|
|
|
+ OpenCurrentMenu._currentChild,
|
|
|
+ out OpenCurrentMenu._currentChild
|
|
|
)
|
|
|
&& !CloseMenu (false))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!openCurrentMenu.CheckSubMenu ())
|
|
|
+ if (!OpenCurrentMenu.CheckSubMenu ())
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
@@ -631,6 +429,7 @@ public class MenuBar : View
|
|
|
: 0)
|
|
|
+ _rightPadding;
|
|
|
}
|
|
|
+
|
|
|
return null; // Don't show the cursor
|
|
|
}
|
|
|
|
|
@@ -660,7 +459,6 @@ public class MenuBar : View
|
|
|
}
|
|
|
|
|
|
_openedByAltKey = false;
|
|
|
- _openedByHotKey = false;
|
|
|
IsMenuOpen = false;
|
|
|
_selected = -1;
|
|
|
CanFocus = _initialCanFocus;
|
|
@@ -697,20 +495,19 @@ public class MenuBar : View
|
|
|
Application.UngrabMouse ();
|
|
|
}
|
|
|
|
|
|
- if (openCurrentMenu is { })
|
|
|
+ if (OpenCurrentMenu is { })
|
|
|
{
|
|
|
- openCurrentMenu = null;
|
|
|
+ OpenCurrentMenu = null;
|
|
|
}
|
|
|
|
|
|
IsMenuOpen = false;
|
|
|
_openedByAltKey = false;
|
|
|
- _openedByHotKey = false;
|
|
|
OnMenuAllClosed ();
|
|
|
}
|
|
|
|
|
|
internal bool CloseMenu (bool reopen = false, bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
|
|
|
{
|
|
|
- MenuBarItem mbi = isSubMenu ? openCurrentMenu.BarItems : _openMenu?.BarItems;
|
|
|
+ MenuBarItem mbi = isSubMenu ? OpenCurrentMenu.BarItems : _openMenu?.BarItems;
|
|
|
|
|
|
if (UseSubMenusSingleFrame && mbi is { } && !ignoreUseSubMenusSingleFrame && mbi.Parent is { })
|
|
|
{
|
|
@@ -725,7 +522,7 @@ public class MenuBar : View
|
|
|
{
|
|
|
_isMenuClosing = false;
|
|
|
|
|
|
- if (args.CurrentMenu.Parent is { })
|
|
|
+ if (args.CurrentMenu.Parent is { } && _openMenu is { })
|
|
|
{
|
|
|
_openMenu._currentChild =
|
|
|
((MenuBarItem)args.CurrentMenu.Parent).Children.IndexOf (args.CurrentMenu);
|
|
@@ -744,7 +541,7 @@ public class MenuBar : View
|
|
|
|
|
|
SetNeedsDisplay ();
|
|
|
|
|
|
- if (_previousFocused is { } && _previousFocused is Menu && _openMenu is { } && _previousFocused.ToString () != openCurrentMenu.ToString ())
|
|
|
+ if (_previousFocused is Menu && _openMenu is { } && _previousFocused.ToString () != OpenCurrentMenu.ToString ())
|
|
|
{
|
|
|
_previousFocused.SetFocus ();
|
|
|
}
|
|
@@ -752,7 +549,7 @@ public class MenuBar : View
|
|
|
_openMenu?.Dispose ();
|
|
|
_openMenu = null;
|
|
|
|
|
|
- if (_lastFocused is Menu || _lastFocused is MenuBar)
|
|
|
+ if (_lastFocused is Menu or MenuBar)
|
|
|
{
|
|
|
_lastFocused = null;
|
|
|
}
|
|
@@ -760,7 +557,7 @@ public class MenuBar : View
|
|
|
LastFocused = _lastFocused;
|
|
|
_lastFocused = null;
|
|
|
|
|
|
- if (LastFocused is { } && LastFocused.CanFocus)
|
|
|
+ if (LastFocused is { CanFocus: true })
|
|
|
{
|
|
|
if (!reopen)
|
|
|
{
|
|
@@ -772,11 +569,11 @@ public class MenuBar : View
|
|
|
_openSubMenu = null;
|
|
|
}
|
|
|
|
|
|
- if (openCurrentMenu is { })
|
|
|
+ if (OpenCurrentMenu is { })
|
|
|
{
|
|
|
- Application.Current.Remove (openCurrentMenu);
|
|
|
- openCurrentMenu.Dispose ();
|
|
|
- openCurrentMenu = null;
|
|
|
+ Application.Current?.Remove (OpenCurrentMenu);
|
|
|
+ OpenCurrentMenu.Dispose ();
|
|
|
+ OpenCurrentMenu = null;
|
|
|
}
|
|
|
|
|
|
LastFocused.SetFocus ();
|
|
@@ -788,7 +585,6 @@ public class MenuBar : View
|
|
|
else
|
|
|
{
|
|
|
SetFocus ();
|
|
|
- //PositionCursor ();
|
|
|
}
|
|
|
|
|
|
IsMenuOpen = false;
|
|
@@ -799,7 +595,7 @@ public class MenuBar : View
|
|
|
_selectedSub = -1;
|
|
|
SetNeedsDisplay ();
|
|
|
RemoveAllOpensSubMenus ();
|
|
|
- openCurrentMenu._previousSubFocused.SetFocus ();
|
|
|
+ OpenCurrentMenu._previousSubFocused.SetFocus ();
|
|
|
_openSubMenu = null;
|
|
|
IsMenuOpen = true;
|
|
|
|
|
@@ -823,11 +619,13 @@ public class MenuBar : View
|
|
|
|
|
|
Rectangle superViewFrame = SuperView is null ? Driver.Screen : SuperView.Frame;
|
|
|
View sv = SuperView is null ? Application.Current : SuperView;
|
|
|
+
|
|
|
if (sv is null)
|
|
|
{
|
|
|
// Support Unit Tests
|
|
|
return Point.Empty;
|
|
|
}
|
|
|
+
|
|
|
Point viewportOffset = sv?.GetViewportOffsetFromFrame () ?? Point.Empty;
|
|
|
|
|
|
return new (
|
|
@@ -836,20 +634,6 @@ public class MenuBar : View
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the <see cref="Application.Current"/> location offset relative to the <see cref="ConsoleDriver"/>
|
|
|
- /// location.
|
|
|
- /// </summary>
|
|
|
- /// <returns>The location offset.</returns>
|
|
|
- internal Point GetScreenOffsetFromCurrent ()
|
|
|
- {
|
|
|
- Rectangle screen = Driver.Screen;
|
|
|
- Rectangle currentFrame = Application.Current.Frame;
|
|
|
- Point viewportOffset = Application.Top.GetViewportOffsetFromFrame ();
|
|
|
-
|
|
|
- return new (screen.X - currentFrame.X - viewportOffset.X, screen.Y - currentFrame.Y - viewportOffset.Y);
|
|
|
- }
|
|
|
-
|
|
|
internal void NextMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
|
|
|
{
|
|
|
switch (isSubMenu)
|
|
@@ -876,9 +660,9 @@ public class MenuBar : View
|
|
|
OpenMenu (_selected);
|
|
|
|
|
|
SelectEnabledItem (
|
|
|
- openCurrentMenu.BarItems.Children,
|
|
|
- openCurrentMenu._currentChild,
|
|
|
- out openCurrentMenu._currentChild
|
|
|
+ OpenCurrentMenu.BarItems.Children,
|
|
|
+ OpenCurrentMenu._currentChild,
|
|
|
+ out OpenCurrentMenu._currentChild
|
|
|
);
|
|
|
|
|
|
break;
|
|
@@ -892,9 +676,9 @@ public class MenuBar : View
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- MenuBarItem subMenu = openCurrentMenu._currentChild > -1 && openCurrentMenu.BarItems.Children.Length > 0
|
|
|
- ? openCurrentMenu.BarItems.SubMenu (
|
|
|
- openCurrentMenu.BarItems.Children [openCurrentMenu._currentChild]
|
|
|
+ MenuBarItem subMenu = OpenCurrentMenu._currentChild > -1 && OpenCurrentMenu.BarItems.Children.Length > 0
|
|
|
+ ? OpenCurrentMenu.BarItems.SubMenu (
|
|
|
+ OpenCurrentMenu.BarItems.Children [OpenCurrentMenu._currentChild]
|
|
|
)
|
|
|
: null;
|
|
|
|
|
@@ -908,13 +692,13 @@ public class MenuBar : View
|
|
|
NextMenu (false, ignoreUseSubMenusSingleFrame);
|
|
|
}
|
|
|
else if (subMenu != null
|
|
|
- || (openCurrentMenu._currentChild > -1
|
|
|
- && !openCurrentMenu.BarItems
|
|
|
- .Children [openCurrentMenu._currentChild]
|
|
|
+ || (OpenCurrentMenu._currentChild > -1
|
|
|
+ && !OpenCurrentMenu.BarItems
|
|
|
+ .Children [OpenCurrentMenu._currentChild]
|
|
|
.IsFromSubMenu))
|
|
|
{
|
|
|
_selectedSub++;
|
|
|
- openCurrentMenu.CheckSubMenu ();
|
|
|
+ OpenCurrentMenu.CheckSubMenu ();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -930,7 +714,7 @@ public class MenuBar : View
|
|
|
|
|
|
if (UseKeysUpDownAsKeysLeftRight)
|
|
|
{
|
|
|
- openCurrentMenu.CheckSubMenu ();
|
|
|
+ OpenCurrentMenu.CheckSubMenu ();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -996,7 +780,7 @@ public class MenuBar : View
|
|
|
locationOffset.Y += SuperView.Border.Thickness.Top;
|
|
|
}
|
|
|
|
|
|
- _openMenu = new()
|
|
|
+ _openMenu = new ()
|
|
|
{
|
|
|
Host = this,
|
|
|
X = Frame.X + pos + locationOffset.X,
|
|
@@ -1004,8 +788,8 @@ public class MenuBar : View
|
|
|
BarItems = Menus [index],
|
|
|
Parent = null
|
|
|
};
|
|
|
- openCurrentMenu = _openMenu;
|
|
|
- openCurrentMenu._previousSubFocused = _openMenu;
|
|
|
+ OpenCurrentMenu = _openMenu;
|
|
|
+ OpenCurrentMenu._previousSubFocused = _openMenu;
|
|
|
|
|
|
if (Application.Current is { })
|
|
|
{
|
|
@@ -1013,9 +797,10 @@ public class MenuBar : View
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _openMenu.BeginInit();
|
|
|
- _openMenu.EndInit();
|
|
|
+ _openMenu.BeginInit ();
|
|
|
+ _openMenu.EndInit ();
|
|
|
}
|
|
|
+
|
|
|
_openMenu.SetFocus ();
|
|
|
|
|
|
break;
|
|
@@ -1038,7 +823,7 @@ public class MenuBar : View
|
|
|
{
|
|
|
locationOffset = GetLocationOffset ();
|
|
|
|
|
|
- openCurrentMenu = new()
|
|
|
+ OpenCurrentMenu = new ()
|
|
|
{
|
|
|
Host = this,
|
|
|
X = last.Frame.Left + last.Frame.Width + locationOffset.X,
|
|
@@ -1053,7 +838,7 @@ public class MenuBar : View
|
|
|
|
|
|
// 2 is for the parent and the separator
|
|
|
MenuItem [] mbi = new MenuItem [2 + subMenu.Children.Length];
|
|
|
- mbi [0] = new() { Title = subMenu.Title, Parent = subMenu };
|
|
|
+ mbi [0] = new () { Title = subMenu.Title, Parent = subMenu };
|
|
|
mbi [1] = null;
|
|
|
|
|
|
for (var j = 0; j < subMenu.Children.Length; j++)
|
|
@@ -1063,23 +848,23 @@ public class MenuBar : View
|
|
|
|
|
|
var newSubMenu = new MenuBarItem (mbi) { Parent = subMenu };
|
|
|
|
|
|
- openCurrentMenu = new()
|
|
|
+ OpenCurrentMenu = new ()
|
|
|
{
|
|
|
Host = this, X = first.Frame.Left, Y = first.Frame.Top, BarItems = newSubMenu
|
|
|
};
|
|
|
last.Visible = false;
|
|
|
- Application.GrabMouse (openCurrentMenu);
|
|
|
+ Application.GrabMouse (OpenCurrentMenu);
|
|
|
}
|
|
|
|
|
|
- openCurrentMenu._previousSubFocused = last._previousSubFocused;
|
|
|
- _openSubMenu.Add (openCurrentMenu);
|
|
|
- Application.Current?.Add (openCurrentMenu);
|
|
|
+ OpenCurrentMenu._previousSubFocused = last._previousSubFocused;
|
|
|
+ _openSubMenu.Add (OpenCurrentMenu);
|
|
|
+ Application.Current?.Add (OpenCurrentMenu);
|
|
|
|
|
|
- if (!openCurrentMenu.IsInitialized)
|
|
|
+ if (!OpenCurrentMenu.IsInitialized)
|
|
|
{
|
|
|
// Supports unit tests
|
|
|
- openCurrentMenu.BeginInit ();
|
|
|
- openCurrentMenu.EndInit ();
|
|
|
+ OpenCurrentMenu.BeginInit ();
|
|
|
+ OpenCurrentMenu.EndInit ();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1087,12 +872,12 @@ public class MenuBar : View
|
|
|
|
|
|
if (_selectedSub > -1
|
|
|
&& SelectEnabledItem (
|
|
|
- openCurrentMenu.BarItems.Children,
|
|
|
- openCurrentMenu._currentChild,
|
|
|
- out openCurrentMenu._currentChild
|
|
|
+ OpenCurrentMenu.BarItems.Children,
|
|
|
+ OpenCurrentMenu._currentChild,
|
|
|
+ out OpenCurrentMenu._currentChild
|
|
|
))
|
|
|
{
|
|
|
- openCurrentMenu.SetFocus ();
|
|
|
+ OpenCurrentMenu.SetFocus ();
|
|
|
}
|
|
|
|
|
|
break;
|
|
@@ -1124,13 +909,13 @@ public class MenuBar : View
|
|
|
OpenMenu (_selected);
|
|
|
|
|
|
if (!SelectEnabledItem (
|
|
|
- openCurrentMenu.BarItems.Children,
|
|
|
- openCurrentMenu._currentChild,
|
|
|
- out openCurrentMenu._currentChild,
|
|
|
+ OpenCurrentMenu.BarItems.Children,
|
|
|
+ OpenCurrentMenu._currentChild,
|
|
|
+ out OpenCurrentMenu._currentChild,
|
|
|
false
|
|
|
))
|
|
|
{
|
|
|
- openCurrentMenu._currentChild = 0;
|
|
|
+ OpenCurrentMenu._currentChild = 0;
|
|
|
}
|
|
|
|
|
|
break;
|
|
@@ -1182,42 +967,35 @@ public class MenuBar : View
|
|
|
}
|
|
|
|
|
|
internal bool SelectEnabledItem (
|
|
|
- IEnumerable<MenuItem> chldren,
|
|
|
+ IEnumerable<MenuItem> children,
|
|
|
int current,
|
|
|
out int newCurrent,
|
|
|
bool forward = true
|
|
|
)
|
|
|
{
|
|
|
- if (chldren is null)
|
|
|
+ if (children is null)
|
|
|
{
|
|
|
newCurrent = -1;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- IEnumerable<MenuItem> childrens;
|
|
|
-
|
|
|
- if (forward)
|
|
|
- {
|
|
|
- childrens = chldren;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- childrens = chldren.Reverse ();
|
|
|
- }
|
|
|
+ IEnumerable<MenuItem> childMenuItems = forward ? children : children.Reverse ();
|
|
|
|
|
|
int count;
|
|
|
|
|
|
+ IEnumerable<MenuItem> menuItems = childMenuItems as MenuItem [] ?? childMenuItems.ToArray ();
|
|
|
+
|
|
|
if (forward)
|
|
|
{
|
|
|
count = -1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- count = childrens.Count ();
|
|
|
+ count = menuItems.Count ();
|
|
|
}
|
|
|
|
|
|
- foreach (MenuItem child in childrens)
|
|
|
+ foreach (MenuItem child in menuItems)
|
|
|
{
|
|
|
if (forward)
|
|
|
{
|
|
@@ -1336,7 +1114,7 @@ public class MenuBar : View
|
|
|
|
|
|
if (mi.IsTopLevel)
|
|
|
{
|
|
|
- var screen = ViewportToScreen (new Point (0 , i));
|
|
|
+ Point screen = ViewportToScreen (new Point (0, i));
|
|
|
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
|
|
|
menu.Run (mi.Action);
|
|
|
menu.Dispose ();
|
|
@@ -1348,16 +1126,16 @@ public class MenuBar : View
|
|
|
OpenMenu (i);
|
|
|
|
|
|
if (!SelectEnabledItem (
|
|
|
- openCurrentMenu.BarItems.Children,
|
|
|
- openCurrentMenu._currentChild,
|
|
|
- out openCurrentMenu._currentChild
|
|
|
+ OpenCurrentMenu.BarItems.Children,
|
|
|
+ OpenCurrentMenu._currentChild,
|
|
|
+ out OpenCurrentMenu._currentChild
|
|
|
)
|
|
|
&& !CloseMenu (false))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!openCurrentMenu.CheckSubMenu ())
|
|
|
+ if (!OpenCurrentMenu.CheckSubMenu ())
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
@@ -1395,8 +1173,8 @@ public class MenuBar : View
|
|
|
menu.Visible = true;
|
|
|
}
|
|
|
|
|
|
- openCurrentMenu = menu;
|
|
|
- openCurrentMenu.SetFocus ();
|
|
|
+ OpenCurrentMenu = menu;
|
|
|
+ OpenCurrentMenu.SetFocus ();
|
|
|
|
|
|
if (_openSubMenu is { })
|
|
|
{
|
|
@@ -1417,7 +1195,7 @@ public class MenuBar : View
|
|
|
|
|
|
if (_openSubMenu.Count > 0)
|
|
|
{
|
|
|
- openCurrentMenu = _openSubMenu.Last ();
|
|
|
+ OpenCurrentMenu = _openSubMenu.Last ();
|
|
|
}
|
|
|
|
|
|
_isMenuClosing = false;
|
|
@@ -1449,7 +1227,8 @@ public class MenuBar : View
|
|
|
}
|
|
|
|
|
|
KeyBindings.Remove (_key);
|
|
|
- KeyBindings.Add (value, KeyBindingScope.HotKey, Command.ToggleExpandCollapse);
|
|
|
+ KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
|
|
|
+ KeyBindings.Add (value, keyBinding);
|
|
|
_key = value;
|
|
|
}
|
|
|
}
|
|
@@ -1490,168 +1269,66 @@ public class MenuBar : View
|
|
|
/// <summary>The specifier character for the hot keys.</summary>
|
|
|
public new static Rune HotKeySpecifier => (Rune)'_';
|
|
|
|
|
|
- // Set in OnInvokingKeyBindings. -1 means no menu item is selected for activation.
|
|
|
- private int _menuBarItemToActivate;
|
|
|
-
|
|
|
- // Set in OnInvokingKeyBindings. null means no sub-menu is selected for activation.
|
|
|
- private MenuItem _menuItemToSelect;
|
|
|
+ // TODO: This doesn't actually work. Figure out why.
|
|
|
private bool _openedByAltKey;
|
|
|
- private bool _openedByHotKey;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Called when a key bound to Command.Select is pressed. Either activates the menu item or runs it, depending on
|
|
|
/// whether it has a sub-menu. If the menu is open, it will close the menu bar.
|
|
|
/// </summary>
|
|
|
+ /// <param name="index">The index of the menu bar item to select. -1 if the selection was via <see cref="Key"/>.</param>
|
|
|
/// <returns></returns>
|
|
|
- private bool SelectOrRun ()
|
|
|
+ private bool Select (int index)
|
|
|
{
|
|
|
if (!IsInitialized || !Visible)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- _openedByHotKey = true;
|
|
|
-
|
|
|
- if (_menuBarItemToActivate != -1)
|
|
|
+ // If the menubar is open and the menu that's open is 'index' then close it. Otherwise activate it.
|
|
|
+ if (IsMenuOpen)
|
|
|
{
|
|
|
- Activate (_menuBarItemToActivate);
|
|
|
- }
|
|
|
- else if (_menuItemToSelect is { })
|
|
|
- {
|
|
|
- Run (_menuItemToSelect.Action);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (IsMenuOpen && _openMenu is { })
|
|
|
+ if (index == -1)
|
|
|
{
|
|
|
CloseAllMenus ();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- OpenMenu ();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public override bool? OnInvokingKeyBindings (Key key)
|
|
|
- {
|
|
|
- // This is a bit of a hack. We want to handle the key bindings for menu bar but
|
|
|
- // InvokeKeyBindings doesn't pass any context so we can't tell which item it is for.
|
|
|
- // So before we call the base class we set SelectedItem appropriately.
|
|
|
- // TODO: Figure out if there's a way to have KeyBindings pass context instead. Maybe a KeyBindingContext property?
|
|
|
-
|
|
|
- if (KeyBindings.TryGet (key, out _))
|
|
|
- {
|
|
|
- _menuBarItemToActivate = -1;
|
|
|
- _menuItemToSelect = null;
|
|
|
-
|
|
|
- // Search for shortcuts first. If there's a shortcut, we don't want to activate the menu item.
|
|
|
- for (var i = 0; i < Menus.Length; i++)
|
|
|
- {
|
|
|
- // Recurse through the menu to find one with the shortcut.
|
|
|
- if (FindShortcutInChildMenu (key.KeyCode, Menus [i], out _menuItemToSelect))
|
|
|
- {
|
|
|
- _menuBarItemToActivate = i;
|
|
|
|
|
|
- //keyEvent.Scope = KeyBindingScope.HotKey;
|
|
|
-
|
|
|
- return base.OnInvokingKeyBindings (key);
|
|
|
- }
|
|
|
-
|
|
|
- // Now see if any of the menu bar items have a hot key that matches
|
|
|
- // Technically this is not possible because menu bar items don't have
|
|
|
- // shortcuts or Actions. But it's here for completeness.
|
|
|
- KeyCode? shortcut = Menus [i]?.Shortcut;
|
|
|
-
|
|
|
- if (key == shortcut)
|
|
|
- {
|
|
|
- throw new InvalidOperationException ("Menu bar items cannot have shortcuts");
|
|
|
- }
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- // Search for hot keys next.
|
|
|
+ // Find the index of the open submenu and close the menu if it matches
|
|
|
for (var i = 0; i < Menus.Length; i++)
|
|
|
{
|
|
|
- if (IsMenuOpen)
|
|
|
+ MenuBarItem open = Menus [i];
|
|
|
+
|
|
|
+ if (open is null)
|
|
|
{
|
|
|
- // We don't need to do anything because `Menu` will handle the key binding.
|
|
|
- //break;
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- // No submenu item matched (or the menu is closed)
|
|
|
-
|
|
|
- // Check if one of the menu bar item has a hot key that matches
|
|
|
- var hotKey = new Key ((char)Menus [i]?.HotKey.Value);
|
|
|
-
|
|
|
- if (hotKey != Key.Empty)
|
|
|
+ if (open == OpenCurrentMenu.BarItems && i == index)
|
|
|
{
|
|
|
- bool matches = key == hotKey || key == hotKey.WithAlt || key == hotKey.NoShift.WithAlt;
|
|
|
-
|
|
|
- if (IsMenuOpen)
|
|
|
- {
|
|
|
- // If the menu is open, only match if Alt is not pressed.
|
|
|
- matches = key == hotKey;
|
|
|
- }
|
|
|
-
|
|
|
- if (matches)
|
|
|
- {
|
|
|
- _menuBarItemToActivate = i;
|
|
|
-
|
|
|
- //keyEvent.Scope = KeyBindingScope.HotKey;
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
+ CloseAllMenus ();
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return base.OnInvokingKeyBindings (key);
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: Update to use Key instead of KeyCode
|
|
|
- // Recurse the child menus looking for a shortcut that matches the key
|
|
|
- private bool FindShortcutInChildMenu (KeyCode key, MenuBarItem menuBarItem, out MenuItem menuItemToSelect)
|
|
|
- {
|
|
|
- menuItemToSelect = null;
|
|
|
-
|
|
|
- if (key == KeyCode.Null || menuBarItem?.Children is null)
|
|
|
+ if (index == -1)
|
|
|
{
|
|
|
- return false;
|
|
|
+ OpenMenu ();
|
|
|
}
|
|
|
-
|
|
|
- for (var c = 0; c < menuBarItem.Children.Length; c++)
|
|
|
+ else
|
|
|
{
|
|
|
- MenuItem menuItem = menuBarItem.Children [c];
|
|
|
-
|
|
|
- if (key == menuItem?.Shortcut)
|
|
|
- {
|
|
|
- menuItemToSelect = menuItem;
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- MenuBarItem subMenu = menuBarItem.SubMenu (menuItem);
|
|
|
-
|
|
|
- if (subMenu is { })
|
|
|
- {
|
|
|
- if (FindShortcutInChildMenu (key, subMenu, out menuItemToSelect))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
+ Activate (index);
|
|
|
}
|
|
|
|
|
|
- return false;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
#endregion Keyboard handling
|
|
|
|
|
|
#region Mouse Handling
|
|
|
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
public override bool OnLeave (View view)
|
|
|
{
|
|
@@ -1699,7 +1376,7 @@ public class MenuBar : View
|
|
|
{
|
|
|
if (Menus [i].IsTopLevel)
|
|
|
{
|
|
|
- var screen = ViewportToScreen (new Point(0 , i));
|
|
|
+ Point screen = ViewportToScreen (new Point (0, i));
|
|
|
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
|
|
|
menu.Run (Menus [i].Action);
|
|
|
menu.Dispose ();
|
|
@@ -1741,9 +1418,9 @@ public class MenuBar : View
|
|
|
{
|
|
|
if (!UseSubMenusSingleFrame
|
|
|
|| (UseSubMenusSingleFrame
|
|
|
- && openCurrentMenu != null
|
|
|
- && openCurrentMenu.BarItems.Parent != null
|
|
|
- && openCurrentMenu.BarItems.Parent.Parent != Menus [i]))
|
|
|
+ && OpenCurrentMenu != null
|
|
|
+ && OpenCurrentMenu.BarItems.Parent != null
|
|
|
+ && OpenCurrentMenu.BarItems.Parent.Parent != Menus [i]))
|
|
|
{
|
|
|
Activate (i);
|
|
|
}
|