Browse Source

Partial impl using key binding context

Tig 1 year ago
parent
commit
1b7cd9d9a3

+ 8 - 1
Terminal.Gui/Input/CommandContext.cs

@@ -16,10 +16,12 @@ public record struct CommandContext
     /// </summary>
     /// </summary>
     /// <param name="command"></param>
     /// <param name="command"></param>
     /// <param name="key"></param>
     /// <param name="key"></param>
-    public CommandContext (Command command, Key? key)
+    /// <param name="keyBinding"></param>
+    public CommandContext (Command command, Key? key, KeyBinding? keyBinding = null)
     {
     {
         Command = command;
         Command = command;
         Key = key;
         Key = key;
+        KeyBinding = keyBinding;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -31,4 +33,9 @@ public record struct CommandContext
     ///     The <see cref="Key"/> that is being invoked. This is the key that was pressed to invoke the <see cref="Command"/>.
     ///     The <see cref="Key"/> that is being invoked. This is the key that was pressed to invoke the <see cref="Command"/>.
     /// </summary>
     /// </summary>
     public Key? Key { get; set; }
     public Key? Key { get; set; }
+
+    /// <summary>
+    /// The KeyBinding that was used to invoke the <see cref="Command"/>, if any.
+    /// </summary>
+    public KeyBinding? KeyBinding { get; set; }
 }
 }

+ 17 - 7
Terminal.Gui/Input/KeyBinding.cs

@@ -1,24 +1,34 @@
 #nullable enable
 #nullable enable
+
 // These classes use a key binding system based on the design implemented in Scintilla.Net which is an
 // These classes use a key binding system based on the design implemented in Scintilla.Net which is an
 // MIT licensed open source project https://github.com/jacobslusser/ScintillaNET/blob/master/src/ScintillaNET/Command.cs
 // MIT licensed open source project https://github.com/jacobslusser/ScintillaNET/blob/master/src/ScintillaNET/Command.cs
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Provides a collection of <see cref="Command"/> objects that are scoped to <see cref="KeyBindingScope"/>.</summary>
+/// <summary>
+/// Provides a collection of <see cref="Command"/> objects that are scoped to <see cref="KeyBindingScope"/>.
+/// </summary>
 public record struct KeyBinding
 public record struct KeyBinding
 {
 {
     /// <summary>Initializes a new instance.</summary>
     /// <summary>Initializes a new instance.</summary>
-    /// <param name="commands"></param>
-    /// <param name="scope"></param>
-    public KeyBinding (Command [] commands, KeyBindingScope scope)
+    /// <param name="commands">The commands this key binding will invoke.</param>
+    /// <param name="scope">The scope of the <see cref="Commands"/>.</param>
+    /// <param name="context">Arbitrary context that can be associated with this key binding.</param>
+    public KeyBinding (Command [] commands, KeyBindingScope scope, object? context = null)
     {
     {
         Commands = commands;
         Commands = commands;
         Scope = scope;
         Scope = scope;
+        Context = context;
     }
     }
 
 
-    /// <summary>The actions which can be performed by the application or bound to keys in a <see cref="View"/> control.</summary>
+    /// <summary>The commands this key binding will invoke.</summary>
     public Command [] Commands { get; set; }
     public Command [] Commands { get; set; }
 
 
-    /// <summary>The scope of the <see cref="Commands"/> bound to a key.</summary>
+    /// <summary>The scope of the <see cref="Commands"/>.</summary>
     public KeyBindingScope Scope { get; set; }
     public KeyBindingScope Scope { get; set; }
-}
+
+    /// <summary>
+    ///     Arbitrary context that can be associated with this key binding.
+    /// </summary>
+    public object? Context { get; set; }
+}

+ 3 - 1
Terminal.Gui/Input/KeyBindings.cs

@@ -2,7 +2,9 @@
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Provides a collection of <see cref="KeyBinding"/> objects bound to a <see cref="Key"/>.</summary>
+/// <summary>
+/// Provides a collection of <see cref="KeyBinding"/> objects bound to a <see cref="Key"/>.
+/// </summary>
 public class KeyBindings
 public class KeyBindings
 {
 {
     /// <summary>
     /// <summary>

+ 6 - 5
Terminal.Gui/View/ViewKeyboard.cs

@@ -755,7 +755,7 @@ public partial class View
             }
             }
 
 
             // each command has its own return value
             // each command has its own return value
-            bool? thisReturn = InvokeCommand (command, key);
+            bool? thisReturn = InvokeCommand (command, key, binding);
 
 
             // if we haven't got anything yet, the current command result should be used
             // if we haven't got anything yet, the current command result should be used
             toReturn ??= thisReturn;
             toReturn ??= thisReturn;
@@ -780,7 +780,7 @@ public partial class View
     ///     <see langword="true"/> if the command was invoked and it handled the command.
     ///     <see langword="true"/> if the command was invoked and it handled the command.
     ///     <see langword="false"/> if the command was invoked and it did not handle the command.
     ///     <see langword="false"/> if the command was invoked and it did not handle the command.
     /// </returns>
     /// </returns>
-    public bool? InvokeCommands (Command [] commands, [CanBeNull] Key key = null)
+    public bool? InvokeCommands (Command [] commands, [CanBeNull] Key key = null, [CanBeNull] KeyBinding? keyBinding = null)
     {
     {
         bool? toReturn = null;
         bool? toReturn = null;
 
 
@@ -792,7 +792,7 @@ public partial class View
             }
             }
 
 
             // each command has its own return value
             // each command has its own return value
-            bool? thisReturn = InvokeCommand (command, key);
+            bool? thisReturn = InvokeCommand (command, key, keyBinding);
 
 
             // if we haven't got anything yet, the current command result should be used
             // if we haven't got anything yet, the current command result should be used
             toReturn ??= thisReturn;
             toReturn ??= thisReturn;
@@ -810,15 +810,16 @@ public partial class View
     /// <summary>Invokes the specified command.</summary>
     /// <summary>Invokes the specified command.</summary>
     /// <param name="command">The command to invoke.</param>
     /// <param name="command">The command to invoke.</param>
     /// <param name="key">The key that caused the command to be invoked, if any.</param>
     /// <param name="key">The key that caused the command to be invoked, if any.</param>
+    /// <param name="keyBinding"></param>
     /// <returns>
     /// <returns>
     ///     <see langword="null"/> if no command was found. <see langword="true"/> if the command was invoked, and it
     ///     <see langword="null"/> if no command was found. <see langword="true"/> if the command was invoked, and it
     ///     handled the command. <see langword="false"/> if the command was invoked, and it did not handle the command.
     ///     handled the command. <see langword="false"/> if the command was invoked, and it did not handle the command.
     /// </returns>
     /// </returns>
-    public bool? InvokeCommand (Command command, [CanBeNull] Key key = null)
+    public bool? InvokeCommand (Command command, [CanBeNull] Key key = null, [CanBeNull] KeyBinding? keyBinding = null)
     {
     {
         if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?> implementation))
         if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?> implementation))
         {
         {
-            var context = new CommandContext (command, key); // Create the context here
+            var context = new CommandContext (command, key, keyBinding); // Create the context here
             return implementation (context);
             return implementation (context);
         }
         }
 
 

+ 75 - 71
Terminal.Gui/Views/Menu/Menu.cs

@@ -52,7 +52,7 @@ public class MenuItem
         Action = action;
         Action = action;
         CanExecute = canExecute;
         CanExecute = canExecute;
         Parent = parent;
         Parent = parent;
-        _shortcutHelper = new ShortcutHelper ();
+        _shortcutHelper = new ();
 
 
         if (shortcut != KeyCode.Null)
         if (shortcut != KeyCode.Null)
         {
         {
@@ -463,8 +463,8 @@ internal sealed class Menu : View
                     }
                     }
                    );
                    );
         AddCommand (Command.Select, () => _host?.SelectItem (_menuItemToSelect));
         AddCommand (Command.Select, () => _host?.SelectItem (_menuItemToSelect));
-        AddCommand (Command.ToggleExpandCollapse, () => SelectOrRun ());
-        AddCommand (Command.HotKey, () => _host?.SelectItem (_menuItemToSelect));
+        AddCommand (Command.ToggleExpandCollapse, (ctx) => SelectOrRun (ctx.KeyBinding?.Context));
+        AddCommand (Command.HotKey, (ctx) => _host?.SelectItem (ctx.KeyBinding?.Context as MenuItem));//_menuItemToSelect));
 
 
         // Default key bindings for this view
         // Default key bindings for this view
         KeyBindings.Add (Key.CursorUp, Command.LineUp);
         KeyBindings.Add (Key.CursorUp, Command.LineUp);
@@ -503,16 +503,15 @@ internal sealed class Menu : View
 
 
         foreach (MenuItem menuItem in menuBarItem.Children.Where (m => m is { }))
         foreach (MenuItem menuItem in menuBarItem.Children.Where (m => m is { }))
         {
         {
-            KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, Command.ToggleExpandCollapse);
+            KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuItem);
+            KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, keyBinding);
 
 
-            KeyBindings.Add (
-                             (KeyCode)menuItem.HotKey.Value | KeyCode.AltMask,
-                             Command.ToggleExpandCollapse
-                            );
+            KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, keyBinding);
 
 
             if (menuItem.Shortcut != KeyCode.Null)
             if (menuItem.Shortcut != KeyCode.Null)
             {
             {
-                KeyBindings.Add (menuItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
+                keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
+                KeyBindings.Add (menuItem.Shortcut, keyBinding);
             }
             }
 
 
             MenuBarItem subMenu = menuBarItem.SubMenu (menuItem);
             MenuBarItem subMenu = menuBarItem.SubMenu (menuItem);
@@ -525,13 +524,18 @@ internal sealed class Menu : View
 
 
     /// <summary>Called when a key bound to Command.Select is pressed. This means a hot key was pressed.</summary>
     /// <summary>Called when a key bound to Command.Select is pressed. This means a hot key was pressed.</summary>
     /// <returns></returns>
     /// <returns></returns>
-    private bool SelectOrRun ()
+    private bool SelectOrRun ([CanBeNull] object context)
     {
     {
         if (!IsInitialized || !Visible)
         if (!IsInitialized || !Visible)
         {
         {
             return true;
             return true;
         }
         }
 
 
+        if (context is MenuItem menuItem)
+        {
+            _menuItemToSelect = menuItem;
+        }
+
         if (_menuBarItemToActivate != -1)
         if (_menuBarItemToActivate != -1)
         {
         {
             _host.Activate (1, _menuBarItemToActivate);
             _host.Activate (1, _menuBarItemToActivate);
@@ -591,68 +595,69 @@ internal sealed class Menu : View
 
 
         KeyCode key = keyEvent.KeyCode;
         KeyCode key = keyEvent.KeyCode;
 
 
-        if (KeyBindings.TryGet (key, out _))
-        {
-            _menuBarItemToActivate = -1;
-            _menuItemToSelect = null;
+        //if (KeyBindings.TryGet (key, out _))
+        //{
+        //    _menuBarItemToActivate = -1;
+        //    _menuItemToSelect = null;
 
 
-            MenuItem [] children = _barItems.Children;
+        //    MenuItem [] children = _barItems.Children;
 
 
-            if (children is null)
-            {
-                return base.OnInvokingKeyBindings (keyEvent);
-            }
+        //    if (children is null)
+        //    {
+        //        return base.OnInvokingKeyBindings (keyEvent);
+        //    }
 
 
-            // Search for shortcuts first. If there's a shortcut, we don't want to activate the menu item.
-            foreach (MenuItem c in children)
-            {
-                if (key == c?.Shortcut)
-                {
-                    _menuBarItemToActivate = -1;
-                    _menuItemToSelect = c;
-                    //keyEvent.Scope = KeyBindingScope.HotKey;
+        //    // Search for shortcuts first. If there's a shortcut, we don't want to activate the menu item.
+        //    foreach (MenuItem c in children)
+        //    {
+        //        if (key == c?.Shortcut)
+        //        {
+        //            _menuBarItemToActivate = -1;
+        //            _menuItemToSelect = c;
 
 
-                    return base.OnInvokingKeyBindings (keyEvent);
-                }
+        //            //keyEvent.Scope = KeyBindingScope.HotKey;
 
 
-                MenuBarItem subMenu = _barItems.SubMenu (c);
+        //            return base.OnInvokingKeyBindings (keyEvent);
+        //        }
 
 
-                if (FindShortcutInChildMenu (key, subMenu))
-                {
-                    //keyEvent.Scope = KeyBindingScope.HotKey;
+        //        MenuBarItem subMenu = _barItems.SubMenu (c);
 
 
-                    return base.OnInvokingKeyBindings (keyEvent);
-                }
-            }
+        //        if (FindShortcutInChildMenu (key, subMenu))
+        //        {
+        //            //keyEvent.Scope = KeyBindingScope.HotKey;
 
 
-            // Search for hot keys next.
-            for (var c = 0; c < children.Length; c++)
-            {
-                int hotKeyValue = children [c]?.HotKey.Value ?? default (int);
-                var hotKey = (KeyCode)hotKeyValue;
+        //            return base.OnInvokingKeyBindings (keyEvent);
+        //        }
+        //    }
 
 
-                if (hotKey == KeyCode.Null)
-                {
-                    continue;
-                }
+        //    // Search for hot keys next.
+        //    for (var c = 0; c < children.Length; c++)
+        //    {
+        //        int hotKeyValue = children [c]?.HotKey.Value ?? default (int);
+        //        var hotKey = (KeyCode)hotKeyValue;
 
 
-                bool matches = key == hotKey || key == (hotKey | KeyCode.AltMask);
+        //        if (hotKey == KeyCode.Null)
+        //        {
+        //            continue;
+        //        }
 
 
-                if (!_host.IsMenuOpen)
-                {
-                    // If the menu is open, only match if Alt is not pressed.
-                    matches = key == hotKey;
-                }
+        //        bool matches = key == hotKey || key == (hotKey | KeyCode.AltMask);
 
 
-                if (matches)
-                {
-                    _menuItemToSelect = children [c];
-                    _currentChild = c;
+        //        if (!_host.IsMenuOpen)
+        //        {
+        //            // If the menu is open, only match if Alt is not pressed.
+        //            matches = key == hotKey;
+        //        }
 
 
-                    return base.OnInvokingKeyBindings (keyEvent);
-                }
-            }
-        }
+        //        if (matches)
+        //        {
+        //            _menuItemToSelect = children [c];
+        //            _currentChild = c;
+
+        //            return base.OnInvokingKeyBindings (keyEvent);
+        //        }
+        //    }
+        //}
 
 
         bool? handled = base.OnInvokingKeyBindings (keyEvent);
         bool? handled = base.OnInvokingKeyBindings (keyEvent);
 
 
@@ -727,6 +732,7 @@ internal sealed class Menu : View
         View view = a.View ?? this;
         View view = a.View ?? this;
 
 
         Point boundsPoint = view.ScreenToViewport (new (a.Position.X, a.Position.Y));
         Point boundsPoint = view.ScreenToViewport (new (a.Position.X, a.Position.Y));
+
         var me = new MouseEvent
         var me = new MouseEvent
         {
         {
             Position = boundsPoint,
             Position = boundsPoint,
@@ -786,12 +792,12 @@ internal sealed class Menu : View
 
 
             Driver.SetAttribute (
             Driver.SetAttribute (
                                  item is null ? GetNormalColor () :
                                  item is null ? GetNormalColor () :
-                                 i == _currentChild ? GetFocusColor() : GetNormalColor ()
+                                 i == _currentChild ? GetFocusColor () : GetNormalColor ()
                                 );
                                 );
 
 
             if (item is null && BorderStyle != LineStyle.None)
             if (item is null && BorderStyle != LineStyle.None)
             {
             {
-                var s = ViewportToScreen (new Point (-1, i));
+                Point s = ViewportToScreen (new Point (-1, i));
                 Driver.Move (s.X, s.Y);
                 Driver.Move (s.X, s.Y);
                 Driver.AddRune (Glyphs.LeftTee);
                 Driver.AddRune (Glyphs.LeftTee);
             }
             }
@@ -839,7 +845,7 @@ internal sealed class Menu : View
             {
             {
                 if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width)
                 if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width)
                 {
                 {
-                    var s = ViewportToScreen (new Point (Frame.Width - 2, i));
+                    Point s = ViewportToScreen (new Point (Frame.Width - 2, i));
                     Driver.Move (s.X, s.Y);
                     Driver.Move (s.X, s.Y);
                     Driver.AddRune (Glyphs.RightTee);
                     Driver.AddRune (Glyphs.RightTee);
                 }
                 }
@@ -876,7 +882,8 @@ internal sealed class Menu : View
                 textToDraw = item.Title;
                 textToDraw = item.Title;
             }
             }
 
 
-            var screen = ViewportToScreen (new Point(0  , i));
+            Point screen = ViewportToScreen (new Point (0, i));
+
             if (screen.X < Driver.Cols)
             if (screen.X < Driver.Cols)
             {
             {
                 Driver.Move (screen.X + 1, screen.Y);
                 Driver.Move (screen.X + 1, screen.Y);
@@ -895,7 +902,7 @@ internal sealed class Menu : View
 
 
                     // The -3 is left/right border + one space (not sure what for)
                     // The -3 is left/right border + one space (not sure what for)
                     tf.Draw (
                     tf.Draw (
-                             ViewportToScreen (new Rectangle(1, i, Frame.Width - 3, 1)),
+                             ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)),
                              i == _currentChild ? GetFocusColor () : GetNormalColor (),
                              i == _currentChild ? GetFocusColor () : GetNormalColor (),
                              i == _currentChild ? ColorScheme.HotFocus : ColorScheme.HotNormal,
                              i == _currentChild ? ColorScheme.HotFocus : ColorScheme.HotNormal,
                              SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
                              SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
@@ -934,7 +941,7 @@ internal sealed class Menu : View
 
 
         Driver.Clip = savedClip;
         Driver.Clip = savedClip;
 
 
-       // PositionCursor ();
+        // PositionCursor ();
     }
     }
 
 
     private void Current_DrawContentComplete (object sender, DrawEventArgs e)
     private void Current_DrawContentComplete (object sender, DrawEventArgs e)
@@ -953,13 +960,10 @@ internal sealed class Menu : View
             {
             {
                 return _host?.PositionCursor ();
                 return _host?.PositionCursor ();
             }
             }
-            else
-            {
-                Move (2, 1 + _currentChild);
 
 
-                return null; // Don't show the cursor
+            Move (2, 1 + _currentChild);
 
 
-            }
+            return null; // Don't show the cursor
         }
         }
 
 
         return _host?.PositionCursor ();
         return _host?.PositionCursor ();
@@ -1176,7 +1180,7 @@ internal sealed class Menu : View
         _host?.SetNeedsDisplay ();
         _host?.SetNeedsDisplay ();
     }
     }
 
 
-    protected internal override bool OnMouseEvent  (MouseEvent me)
+    protected internal override bool OnMouseEvent (MouseEvent me)
     {
     {
         if (!_host._handled && !_host.HandleGrabView (me, this))
         if (!_host._handled && !_host.HandleGrabView (me, this))
         {
         {

+ 106 - 101
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -106,18 +106,16 @@ public class MenuBarItem : MenuItem
         {
         {
             if (menuItem.HotKey != default (Rune))
             if (menuItem.HotKey != default (Rune))
             {
             {
-                menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, Command.ToggleExpandCollapse);
+                KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuItem);
 
 
-                menuBar.KeyBindings.Add (
-                                         (KeyCode)menuItem.HotKey.Value | KeyCode.AltMask,
-                                         KeyBindingScope.HotKey,
-                                         Command.ToggleExpandCollapse
-                                        );
+                menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, keyBinding);
+                menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, keyBinding);
             }
             }
 
 
             if (menuItem.Shortcut != KeyCode.Null)
             if (menuItem.Shortcut != KeyCode.Null)
             {
             {
-                menuBar.KeyBindings.Add (menuItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
+                KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
+                menuBar.KeyBindings.Add (menuItem.Shortcut, keyBinding);
             }
             }
 
 
             SubMenu (menuItem)?.AddKeyBindings (menuBar);
             SubMenu (menuItem)?.AddKeyBindings (menuBar);
@@ -308,8 +306,7 @@ public class MenuBar : View
                         return true;
                         return true;
                     }
                     }
                    );
                    );
-
-        AddCommand (Command.ToggleExpandCollapse, () => SelectOrRun ());
+        AddCommand (Command.ToggleExpandCollapse, (ctx) => SelectOrRun (ctx.KeyBinding?.Context));
         AddCommand (Command.Select, () => Run (_menuItemToSelect?.Action));
         AddCommand (Command.Select, () => Run (_menuItemToSelect?.Action));
 
 
         // Default key bindings for this view
         // Default key bindings for this view
@@ -353,29 +350,25 @@ public class MenuBar : View
             // TODO: Bindings (esp for hotkey) should be added across and then down. This currently does down then across. 
             // 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: 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!
             // TODO: Also: Hotkeys should not work for sub-menus if they are not visible!
-            foreach (MenuBarItem menuBarItem in Menus?.Where (m => m is { })!)
+            for (int i = 0; i < Menus.Length; i++)
+//            foreach (MenuBarItem menuBarItem in Menus?.Where (m => m is { })!)
             {
             {
-                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
                     // 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?.AddKeyBindings (this);
             }
             }
 #if SUPPORT_ALT_TO_ACTIVATE_MENU
 #if SUPPORT_ALT_TO_ACTIVATE_MENU
             // Enable the Alt key as a menu activator
             // Enable the Alt key as a menu activator
@@ -1491,7 +1484,7 @@ public class MenuBar : View
     public new static Rune HotKeySpecifier => (Rune)'_';
     public new static Rune HotKeySpecifier => (Rune)'_';
 
 
     // Set in OnInvokingKeyBindings. -1 means no menu item is selected for activation.
     // Set in OnInvokingKeyBindings. -1 means no menu item is selected for activation.
-    private int _menuBarItemToActivate;
+    private int _menuBarItemToActivate = -1;
 
 
     // Set in OnInvokingKeyBindings. null means no sub-menu is selected for activation.
     // Set in OnInvokingKeyBindings. null means no sub-menu is selected for activation.
     private MenuItem _menuItemToSelect;
     private MenuItem _menuItemToSelect;
@@ -1503,18 +1496,30 @@ public class MenuBar : View
     ///     whether it has a sub-menu. If the menu is open, it will close the menu bar.
     ///     whether it has a sub-menu. If the menu is open, it will close the menu bar.
     /// </summary>
     /// </summary>
     /// <returns></returns>
     /// <returns></returns>
-    private bool SelectOrRun ()
+    private bool SelectOrRun ([CanBeNull] object context)
     {
     {
         if (!IsInitialized || !Visible)
         if (!IsInitialized || !Visible)
         {
         {
             return true;
             return true;
         }
         }
 
 
+        if (context is MenuBarItem menuBarItem)
+
+        {
+            _menuItemToSelect = menuBarItem;
+        }
+
+        if (context is int index)
+        {
+            _menuBarItemToActivate = index;
+        }
+
         _openedByHotKey = true;
         _openedByHotKey = true;
 
 
         if (_menuBarItemToActivate != -1)
         if (_menuBarItemToActivate != -1)
         {
         {
             Activate (_menuBarItemToActivate);
             Activate (_menuBarItemToActivate);
+            _menuBarItemToActivate = -1;
         }
         }
         else if (_menuItemToSelect is { })
         else if (_menuItemToSelect is { })
         {
         {
@@ -1535,81 +1540,81 @@ public class MenuBar : View
         return true;
         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");
-                }
-            }
-
-            // Search for hot keys next.
-            for (var i = 0; i < Menus.Length; i++)
-            {
-                if (IsMenuOpen)
-                {
-                    // We don't need to do anything because `Menu` will handle the key binding.
-                    //break;
-                }
-
-                // 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)
-                {
-                    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;
-                    }
-                }
-            }
-        }
-
-        return base.OnInvokingKeyBindings (key);
-    }
+    ///// <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");
+    //    //        }
+    //    //    }
+
+    //    //    // Search for hot keys next.
+    //    //    for (var i = 0; i < Menus.Length; i++)
+    //    //    {
+    //    //        if (IsMenuOpen)
+    //    //        {
+    //    //            // We don't need to do anything because `Menu` will handle the key binding.
+    //    //            //break;
+    //    //        }
+
+    //    //        // 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)
+    //    //        {
+    //    //            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;
+    //    //            }
+    //    //        }
+    //    //    }
+    //    //}
+
+    //    return base.OnInvokingKeyBindings (key);
+    //}
 
 
     // TODO: Update to use Key instead of KeyCode
     // TODO: Update to use Key instead of KeyCode
     // Recurse the child menus looking for a shortcut that matches the key
     // Recurse the child menus looking for a shortcut that matches the key