瀏覽代碼

Unit tests pass. Most things actually work, but not all

Tig 7 月之前
父節點
當前提交
53d7449c85

+ 1 - 1
Terminal.Gui/Application/Application.Keyboard.cs

@@ -321,7 +321,7 @@ public static partial class Application // Keyboard handling
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///         This version of AddCommand is for commands that do not require a <see cref="ICommandContext"/>.
     ///     </para>
     /// </remarks>
     /// <param name="command">The command.</param>

+ 6 - 16
Terminal.Gui/Input/CommandContext.cs

@@ -3,14 +3,9 @@ namespace Terminal.Gui;
 
 #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
 /// <summary>
-///     Provides context for a <see cref="Command"/> that is being invoked.
+///     Provides context for a <see cref="Command"/> invocation.
 /// </summary>
-/// <remarks>
-///     <para>
-///         To define a <see cref="Command"/> that is invoked with context,
-///         use <see cref="View.AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>.
-///     </para>
-/// </remarks>
+/// <seealso cref="View.Invoke(Command)"/>.
 #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
 public record struct CommandContext<TBindingType> : ICommandContext
 {
@@ -19,22 +14,17 @@ public record struct CommandContext<TBindingType> : ICommandContext
     /// </summary>
     /// <param name="command"></param>
     /// <param name="binding"></param>
-    /// <param name="data"></param>
-    public CommandContext (Command command, TBindingType? binding, object? data = null)
+    public CommandContext (Command command, TBindingType? binding)
     {
         Command = command;
         Binding = binding;
-        Data = data;
     }
 
+    /// <inheritdoc />
+    public Command Command { get; set; }
+
     /// <summary>
     /// The keyboard or mouse minding that was used to invoke the <see cref="Command"/>, if any.
     /// </summary>
     public TBindingType? Binding { get; set; }
-
-    /// <inheritdoc />
-    public Command Command { get; set; }
-
-    /// <inheritdoc />
-    public object? Data { get; set; }
 }

+ 5 - 2
Terminal.Gui/Input/CommandEventArgs.cs

@@ -9,7 +9,10 @@ namespace Terminal.Gui;
 public class CommandEventArgs : CancelEventArgs
 {
     /// <summary>
-    ///     The context for the command.
+    ///     The context for the command, if any.
     /// </summary>
-    public required ICommandContext Context { get; init; }
+    /// <remarks>
+    ///     If <see langword="null"/> the command was invoked without context.
+    /// </remarks>
+    public required ICommandContext? Context { get; init; }
 }

+ 5 - 5
Terminal.Gui/Input/ICommandContext.cs

@@ -11,9 +11,9 @@ public interface ICommandContext
     /// </summary>
     public Command Command { get; set; }
 
-    // TODO: Remove this property. With CommandContext<TBindingType> being a generic type, there should be no need for arbitrary data.
-    /// <summary>
-    ///     Arbitrary data.
-    /// </summary>
-    public object? Data { get; set; }
+    //// TODO: Remove this property. With CommandContext<TBindingType> being a generic type, there should be no need for arbitrary data.
+    ///// <summary>
+    /////     Arbitrary data.
+    ///// </summary>
+    //public object? Data { get; set; }
 }

+ 6 - 6
Terminal.Gui/Input/Keyboard/KeyBinding.cs

@@ -21,20 +21,20 @@ public record struct KeyBinding
     {
         Commands = commands;
         Scope = scope;
-        Context = context;
+        Data = context;
     }
 
     /// <summary>Initializes a new instance.</summary>
     /// <param name="commands">The commands this key binding will invoke.</param>
     /// <param name="scope">The scope of the <see cref="Commands"/>.</param>
     /// <param name="boundView">The view the key binding is bound to.</param>
-    /// <param name="context">Arbitrary context that can be associated with this key binding.</param>
-    public KeyBinding (Command [] commands, KeyBindingScope scope, View? boundView, object? context = null)
+    /// <param name="data">Arbitrary data that can be associated with this key binding.</param>
+    public KeyBinding (Command [] commands, KeyBindingScope scope, View? boundView, object? data = null)
     {
         Commands = commands;
         Scope = scope;
         BoundView = boundView;
-        Context = context;
+        Data = data;
     }
 
     /// <summary>The commands this key binding will invoke.</summary>
@@ -46,7 +46,7 @@ public record struct KeyBinding
     /// <summary>
     ///     The Key that is bound to the <see cref="Commands"/>.
     /// </summary>
-    public Key Key { get; set; }
+    public Key? Key { get; set; }
 
     /// <summary>The view the key binding is bound to.</summary>
     public View? BoundView { get; set; }
@@ -54,5 +54,5 @@ public record struct KeyBinding
     /// <summary>
     ///     Arbitrary context that can be associated with this key binding.
     /// </summary>
-    public object? Context { get; set; }
+    public object? Data { get; set; }
 }

+ 1 - 0
Terminal.Gui/Input/Keyboard/KeyBindings.cs

@@ -431,6 +431,7 @@ public class KeyBindings
         {
             if (scope.HasFlag (binding.Scope))
             {
+                binding.Key = key;
                 return true;
             }
         }

+ 58 - 60
Terminal.Gui/Input/Mouse/MouseBindings.cs

@@ -2,7 +2,7 @@
 namespace Terminal.Gui;
 
 /// <summary>
-///     Provides a collection of <see cref="MouseBinding"/> objects bound to a combination of <see cref="MouseFlags"/>.
+///     Provides a collection of <see cref="MouseBinding"/> objects bound to a combination of <see cref="MouseEventArgs"/>.
 /// </summary>
 /// <seealso cref="View.MouseBindings"/>
 /// <seealso cref="Command"/>
@@ -15,15 +15,13 @@ public class MouseBindings
     public MouseBindings () { }
 
     /// <summary>Adds a <see cref="MouseBinding"/> to the collection.</summary>
-    /// <param name="mouseFlag"></param>
+    /// <param name="mouseEvent"></param>
     /// <param name="binding"></param>
-    public void Add (MouseFlags mouseFlag, MouseBinding binding)
+    public void Add (MouseEventArgs mouseEvent, MouseBinding binding)
     {
-        if (TryGet (mouseFlag, out MouseBinding _))
+        if (TryGet (mouseEvent, out MouseBinding _))
         {
-            throw new InvalidOperationException (@$"A binding for {mouseFlag} exists ({binding}).");
-
-            //Bindings [key] = binding;
+            throw new InvalidOperationException (@$"A binding for {mouseEvent} exists ({binding}).");
         }
 
 
@@ -31,7 +29,7 @@ public class MouseBindings
         // IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus 
         // IMPORTANT: Apply will update the Dictionary with the new key, but the old key will still be in the dictionary.
         // IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details.
-        Bindings.Add (mouseFlag, binding);
+        Bindings.Add (mouseEvent, binding);
     }
 
     /// <summary>
@@ -45,15 +43,15 @@ public class MouseBindings
     ///     Commands are only ever applied to the current <see cref="View"/> (i.e. this feature cannot be used to switch
     ///     focus to another view and perform multiple commands there).
     /// </remarks>
-    /// <param name="mouseFlags">The mouse flags to check.</param>
+    /// <param name="mouseEvents">The mouse flags to check.</param>
     /// <param name="commands">
-    ///     The command to invoked on the <see cref="View"/> when <paramref name="mouseFlags"/> is received. When
-    ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="mouseFlags"/> event will be
+    ///     The command to invoked on the <see cref="View"/> when <paramref name="mouseEvents"/> is received. When
+    ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="mouseEvents"/> event will be
     ///     consumed if any took effect.
     /// </param>
-    public void Add (MouseFlags mouseFlags, params Command [] commands)
+    public void Add (MouseEventArgs mouseEvents, params Command [] commands)
     {
-        if (mouseFlags == MouseFlags.None)
+        if (mouseEvents.Flags == MouseFlags.None)
         {
             throw new ArgumentException (@"Invalid MouseFlag", nameof (commands));
         }
@@ -63,24 +61,24 @@ public class MouseBindings
             throw new ArgumentException (@"At least one command must be specified", nameof (commands));
         }
 
-        if (TryGet (mouseFlags, out MouseBinding binding))
+        if (TryGet (mouseEvents, out MouseBinding binding))
         {
-            throw new InvalidOperationException (@$"A binding for {mouseFlags} exists ({binding}).");
+            throw new InvalidOperationException (@$"A binding for {mouseEvents} exists ({binding}).");
         }
 
-        Add (mouseFlags, new MouseBinding (commands));
+        Add (mouseEvents, new MouseBinding (commands));
     }
 
     // TODO: Add a dictionary comparer that ignores Scope
     // TODO: This should not be public!
     /// <summary>The collection of <see cref="MouseBinding"/> objects.</summary>
-    public Dictionary<MouseFlags, MouseBinding> Bindings { get; } = new ();
+    public Dictionary<MouseEventArgs, MouseBinding> Bindings { get; } = new ();
 
     /// <summary>
-    ///     Gets the <see cref="MouseFlags"/> that are bound.
+    ///     Gets the <see cref="MouseEventArgs"/> that are bound.
     /// </summary>
     /// <returns></returns>
-    public IEnumerable<MouseFlags> GetBoundMouseFlags ()
+    public IEnumerable<MouseEventArgs> GetBoundMouseEventArgs ()
     {
         return Bindings.Keys;
     }
@@ -95,38 +93,38 @@ public class MouseBindings
     /// <param name="command"></param>
     public void Clear (params Command [] command)
     {
-        KeyValuePair<MouseFlags, MouseBinding> [] kvps = Bindings
+        KeyValuePair<MouseEventArgs, MouseBinding> [] kvps = Bindings
                                                 .Where (kvp => kvp.Value.Commands.SequenceEqual (command))
                                                 .ToArray ();
 
-        foreach (KeyValuePair<MouseFlags, MouseBinding> kvp in kvps)
+        foreach (KeyValuePair<MouseEventArgs, MouseBinding> kvp in kvps)
         {
             Remove (kvp.Key);
         }
     }
 
-    /// <summary>Gets the <see cref="MouseBinding"/> for the specified combination of <see cref="MouseFlags"/>.</summary>
-    /// <param name="mouseFlags"></param>
+    /// <summary>Gets the <see cref="MouseBinding"/> for the specified combination of <see cref="MouseEventArgs"/>.</summary>
+    /// <param name="mouseEvents"></param>
     /// <returns></returns>
-    public MouseBinding Get (MouseFlags mouseFlags)
+    public MouseBinding Get (MouseEventArgs mouseEvents)
     {
-        if (TryGet (mouseFlags, out MouseBinding binding))
+        if (TryGet (mouseEvents, out MouseBinding binding))
         {
             return binding;
         }
 
-        throw new InvalidOperationException ($"{mouseFlags} is not bound.");
+        throw new InvalidOperationException ($"{mouseEvents} is not bound.");
     }
 
-    /// <summary>Gets the array of <see cref="Command"/>s bound to <paramref name="mouseFlags"/> if it exists.</summary>
-    /// <param name="mouseFlags">The key to check.</param>
+    /// <summary>Gets the array of <see cref="Command"/>s bound to <paramref name="mouseEvents"/> if it exists.</summary>
+    /// <param name="mouseEvents">The key to check.</param>
     /// <returns>
-    ///     The array of <see cref="Command"/>s if <paramref name="mouseFlags"/> is bound. An empty <see cref="Command"/> array
+    ///     The array of <see cref="Command"/>s if <paramref name="mouseEvents"/> is bound. An empty <see cref="Command"/> array
     ///     if not.
     /// </returns>
-    public Command [] GetCommands (MouseFlags mouseFlags)
+    public Command [] GetCommands (MouseEventArgs mouseEvents)
     {
-        if (TryGet (mouseFlags, out MouseBinding bindings))
+        if (TryGet (mouseEvents, out MouseBinding bindings))
         {
             return bindings.Commands;
         }
@@ -134,83 +132,83 @@ public class MouseBindings
         return [];
     }
 
-    /// <summary>Gets the first combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <summary>Gets the first combination of <see cref="MouseEventArgs"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
     /// <param name="commands">The set of commands to search.</param>
-    /// <returns>The first combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>. <see langword="null"/> if the set of caommands was not found.</returns>
-    public MouseFlags? GetMouseFlagsFromCommands (params Command [] commands)
+    /// <returns>The first combination of <see cref="MouseEventArgs"/> bound to the set of commands specified by <paramref name="commands"/>. <see langword="null"/> if the set of caommands was not found.</returns>
+    public MouseEventArgs? GetMouseEventArgsFromCommands (params Command [] commands)
     {
         return Bindings.FirstOrDefault (a => a.Value.Commands.SequenceEqual (commands)).Key;
     }
 
-    /// <summary>Gets combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <summary>Gets combination of <see cref="MouseEventArgs"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
     /// <param name="commands">The set of commands to search.</param>
-    /// <returns>The combination of <see cref="MouseFlags"/> bound to the set of commands specified by <paramref name="commands"/>. An empty list if the set of caommands was not found.</returns>
-    public IEnumerable<MouseFlags> GetAllMouseFlagsFromCommands (params Command [] commands)
+    /// <returns>The combination of <see cref="MouseEventArgs"/> bound to the set of commands specified by <paramref name="commands"/>. An empty list if the set of caommands was not found.</returns>
+    public IEnumerable<MouseEventArgs> GetAllMouseEventArgsFromCommands (params Command [] commands)
     {
         return Bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key);
     }
 
     /// <summary>Removes a <see cref="MouseBinding"/> from the collection.</summary>
-    /// <param name="mouseFlags"></param>
-    public void Remove (MouseFlags mouseFlags)
+    /// <param name="mouseEvents"></param>
+    public void Remove (MouseEventArgs mouseEvents)
     {
-        if (!TryGet (mouseFlags, out MouseBinding _))
+        if (!TryGet (mouseEvents, out MouseBinding _))
         {
             return;
         }
 
-        Bindings.Remove (mouseFlags);
+        Bindings.Remove (mouseEvents);
     }
 
-    /// <summary>Replaces the commands already bound to a combination of <see cref="MouseFlags"/>.</summary>
+    /// <summary>Replaces the commands already bound to a combination of <see cref="MouseEventArgs"/>.</summary>
     /// <remarks>
     ///     <para>
-    ///         If the combination of <see cref="MouseFlags"/> is not already bound, it will be added.
+    ///         If the combination of <see cref="MouseEventArgs"/> is not already bound, it will be added.
     ///     </para>
     /// </remarks>
-    /// <param name="mouseFlags">The combination of <see cref="MouseFlags"/> bound to the command to be replaced.</param>
+    /// <param name="mouseEvents">The combination of <see cref="MouseEventArgs"/> bound to the command to be replaced.</param>
     /// <param name="commands">The set of commands to replace the old ones with.</param>
-    public void ReplaceCommands (MouseFlags mouseFlags, params Command [] commands)
+    public void ReplaceCommands (MouseEventArgs mouseEvents, params Command [] commands)
     {
-        if (TryGet (mouseFlags, out MouseBinding binding))
+        if (TryGet (mouseEvents, out MouseBinding binding))
         {
             binding.Commands = commands;
         }
         else
         {
-            Add (mouseFlags, commands);
+            Add (mouseEvents, commands);
         }
     }
 
-    /// <summary>Replaces a <see cref="MouseFlags"/> combination already bound to a set of <see cref="Command"/>s.</summary>
+    /// <summary>Replaces a <see cref="MouseEventArgs"/> combination already bound to a set of <see cref="Command"/>s.</summary>
     /// <remarks></remarks>
-    /// <param name="oldMouseFlags">The <see cref="MouseFlags"/> to be replaced.</param>
-    /// <param name="newMouseFlags">The new <see cref="MouseFlags"/> to be used. If <see cref="Key.Empty"/> no action will be taken.</param>
-    public void ReplaceKey (MouseFlags oldMouseFlags, MouseFlags newMouseFlags)
+    /// <param name="oldMouseEventArgs">The <see cref="MouseEventArgs"/> to be replaced.</param>
+    /// <param name="newMouseEventArgs">The new <see cref="MouseEventArgs"/> to be used. If <see cref="Key.Empty"/> no action will be taken.</param>
+    public void ReplaceKey (MouseEventArgs oldMouseEventArgs, MouseEventArgs newMouseEventArgs)
     {
-        if (!TryGet (oldMouseFlags, out MouseBinding _))
+        if (!TryGet (oldMouseEventArgs, out MouseBinding _))
         {
-            throw new InvalidOperationException ($"Key {oldMouseFlags} is not bound.");
+            throw new InvalidOperationException ($"Key {oldMouseEventArgs} is not bound.");
         }
 
-        MouseBinding value = Bindings [oldMouseFlags];
-        Remove (oldMouseFlags);
-        Add (newMouseFlags, value);
+        MouseBinding value = Bindings [oldMouseEventArgs];
+        Remove (oldMouseEventArgs);
+        Add (newMouseEventArgs, value);
     }
 
-    /// <summary>Gets the commands bound with the specified <see cref="MouseFlags"/>.</summary>
+    /// <summary>Gets the commands bound with the specified <see cref="MouseEventArgs"/>.</summary>
     /// <remarks></remarks>
-    /// <param name="mouseFlags">The key to check.</param>
+    /// <param name="mouseEvents">The key to check.</param>
     /// <param name="binding">
     ///     When this method returns, contains the commands bound with the specified mouse flags, if the mouse flags are
     ///     found; otherwise, null. This parameter is passed uninitialized.
     /// </param>
     /// <returns><see langword="true"/> if the mouse flags are bound; otherwise <see langword="false"/>.</returns>
-    public bool TryGet (MouseFlags mouseFlags, out MouseBinding binding)
+    public bool TryGet (MouseEventArgs mouseEvents, out MouseBinding binding)
     {
 
         binding = new ([]);
 
-        return Bindings.TryGetValue (mouseFlags, out binding);
+        return Bindings.TryGetValue (mouseEvents, out binding);
     }
 }

+ 37 - 34
Terminal.Gui/View/View.Command.cs

@@ -30,7 +30,7 @@ public partial class View // Command APIs
                     });
 
         // Space or single-click - Raise Selecting
-        AddCommand (Command.Select, (ctx) =>
+        AddCommand (Command.Select, ctx =>
                                     {
                                         if (RaiseSelecting (ctx) is true)
                                         {
@@ -54,7 +54,7 @@ public partial class View // Command APIs
     /// </summary>
     /// <remarks>
     /// <para>
-    ///     The <see cref="Accepting"/> event should raised after the state of the View has changed (after <see cref="Selecting"/> is raised).
+    ///     The <see cref="Accepting"/> event should be raised after the state of the View has changed (after <see cref="Selecting"/> is raised).
     /// </para>
     /// <para>
     ///    If the Accepting event is not handled, <see cref="Command.Accept"/> will be invoked on the SuperView, enabling default Accept behavior.
@@ -65,11 +65,11 @@ public partial class View // Command APIs
     /// </para>
     /// </remarks>
     /// <returns>
-    ///     <see langword="null"/> if no event was raised; input proessing should continue.
-    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no event was raised; input processing should continue.
+    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
     /// </returns>
-    protected bool? RaiseAccepting (ICommandContext ctx)
+    protected bool? RaiseAccepting (ICommandContext? ctx)
     {
         CommandEventArgs args = new () { Context = ctx };
 
@@ -135,14 +135,14 @@ public partial class View // Command APIs
     ///     event. The default <see cref="Command.Select"/> handler calls this method.
     /// </summary>
     /// <remarks>
-    ///     The <see cref="Selecting"/> event should raised after the state of the View has been changed and before see <see cref="Accepting"/>.
+    ///     The <see cref="Selecting"/> event should be raised after the state of the View has been changed and before see <see cref="Accepting"/>.
     /// </remarks>
     /// <returns>
-    ///     <see langword="null"/> if no event was raised; input proessing should continue.
-    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no event was raised; input processing should continue.
+    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
     /// </returns>
-    protected bool? RaiseSelecting (ICommandContext ctx)
+    protected bool? RaiseSelecting (ICommandContext? ctx)
     {
         CommandEventArgs args = new () { Context = ctx };
 
@@ -179,9 +179,9 @@ public partial class View // Command APIs
     ///     event. The default <see cref="Command.HotKey"/> handler calls this method.
     /// </summary>
     /// <returns>
-    ///     <see langword="null"/> if no event was raised; input proessing should continue.
-    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no event was raised; input processing should continue.
+    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
     /// </returns>
     protected bool? RaiseHandlingHotKey ()
     {
@@ -219,11 +219,11 @@ public partial class View // Command APIs
     /// <summary>
     /// Function signature commands.
     /// </summary>
-    /// <param name="ctx">Provides information about the circumstances of invoking the command (e.g. <see cref="CommandContext.Key"/>)</param>
+    /// <param name="ctx">Provides context about the circumstances of invoking the command.</param>
     /// <returns>
-    ///     <see langword="null"/> if no command was found; input proessing should continue.
-    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no event was raised; input processing should continue.
+    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
     /// </returns>
     public delegate bool? CommandImplementation (ICommandContext? ctx);
 
@@ -239,7 +239,7 @@ public partial class View // Command APIs
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         This version of AddCommand is for commands that require <see cref="CommandContext"/>.
+    ///         This version of AddCommand is for commands that require <see cref="ICommandContext"/>.
     ///     </para>
     /// </remarks>
     /// <param name="command">The command.</param>
@@ -258,7 +258,7 @@ public partial class View // Command APIs
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///         This version of AddCommand is for commands that do not require context.
     ///         If the command requires context, use
     ///         <see cref="AddCommand(Command,CommandImplementation)"/>
     ///     </para>
@@ -275,12 +275,11 @@ public partial class View // Command APIs
     ///     Invokes the specified commands.
     /// </summary>
     /// <param name="commands">The set of commands to invoke.</param>
-    /// <param name="key">The key that caused the command to be invoked, if any. This will be passed as context with the command.</param>
-    /// <param name="keyBinding">The key binding that was bound to the key and caused the invocation, if any. This will be passed as context with the command.</param>
+    /// <param name="binding">The binding that caused the invocation, if any. This will be passed as context with the command.</param>
     /// <returns>
-    ///     <see langword="null"/> if no command was found; input proessing should continue.
-    ///     <see langword="false"/> if at least one command was invoked and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if at least one command was invoked the command was handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no command was found; input processing should continue.
+    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input processing should stop.
     /// </returns>
     public bool? InvokeCommands<TBindingType> (Command [] commands, TBindingType binding)
     {
@@ -315,30 +314,34 @@ public partial class View // Command APIs
     /// Invokes the specified command.
     /// </summary>
     /// <param name="command">The command to invoke.</param>
-    /// <param name="binding"></param>
+    /// <param name="binding">The binding that caused the invocation, if any. This will be passed as context with the command.</param>
     /// <returns>
-    ///     <see langword="null"/> if no command was found; input proessing should continue.
-    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no command was found; input processing should continue.
+    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input processing should stop.
     /// </returns>
     public bool? InvokeCommand<TBindingType> (Command command, TBindingType binding)
     {
         if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation))
         {
-            return implementation (binding as ICommandContext);
+            return implementation (new CommandContext<TBindingType> ()
+            {
+                Command = command,
+                Binding = binding,
+            });
         }
 
         return null;
     }
 
     /// <summary>
-    /// Invokes the specified command.
+    /// Invokes the specified command without context.
     /// </summary>
     /// <param name="command">The command to invoke.</param>
     /// <returns>
-    ///     <see langword="null"/> if no command was found; input proessing should continue.
-    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input proessing should continue.
-    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input proessing should stop.
+    ///     <see langword="null"/> if no command was found; input processing should continue.
+    ///     <see langword="false"/> if the command was invoked and was not handled (or cancelled); input processing should continue.
+    ///     <see langword="true"/> if the command was invoked the command was handled (or cancelled); input processing should stop.
     /// </returns>
     public bool? InvokeCommand (Command command)
     {

+ 3 - 2
Terminal.Gui/Views/ColorPicker.16.cs

@@ -205,9 +205,10 @@ public class ColorPicker16 : View
         AddCommand (Command.Select, (ctx) =>
                                     {
                                         bool set = false;
-                                        if (ctx.Data is MouseEventArgs me)
+
+                                        if (ctx is CommandContext<MouseEventArgs> { Binding: { } } mouseCommandContext)
                                         {
-                                            Cursor = new (me.Position.X / _boxWidth, me.Position.Y / _boxHeight);
+                                            Cursor = new (mouseCommandContext.Binding.Position.X / _boxWidth, mouseCommandContext.Binding.Position.Y / _boxHeight);
                                             set = true;
                                         }
                                         return RaiseAccepting (ctx) == true || set;

+ 5 - 1
Terminal.Gui/Views/ComboBox.cs

@@ -80,7 +80,11 @@ public class ComboBox : View, IDesignable
         // Things this view knows how to do
         AddCommand (Command.Accept, (ctx) =>
                                     {
-                                        if (ctx.Data == _search)
+                                        if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                                        {
+                                            return false;
+                                        }
+                                        if (keyCommandContext.Binding.Data == _search)
                                         {
                                             return null;
                                         }

+ 6 - 2
Terminal.Gui/Views/ListView.cs

@@ -143,8 +143,12 @@ public class ListView : View, IDesignable
 
         AddCommand (Command.SelectAll, (ctx) =>
                                        {
-                                           // BUGBUG: This probably isn't right
-                                           return MarkAll ((bool)ctx!.Data);
+                                           if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                                           {
+                                               return false;
+                                           }
+
+                                           return keyCommandContext.Binding.Data is { } && MarkAll ((bool)keyCommandContext.Binding.Data);
                                        });
 
         // Default keybindings for all ListViews

+ 541 - 507
Terminal.Gui/Views/Menu/Menu.cs

@@ -1,7 +1,5 @@
 #nullable enable
 
-using static System.Formats.Asn1.AsnWriter;
-
 namespace Terminal.Gui;
 
 /// <summary>
@@ -10,76 +8,115 @@ namespace Terminal.Gui;
 /// </summary>
 internal sealed class Menu : View
 {
-    private readonly MenuBarItem? _barItems;
-    private readonly MenuBar _host;
-    internal int _currentChild;
-    internal View? _previousSubFocused;
-
-    internal static Rectangle MakeFrame (int x, int y, MenuItem? []? items, Menu? parent = null)
+    public Menu ()
     {
-        if (items is null || items.Length == 0)
+        if (Application.Top is { })
         {
-            return Rectangle.Empty;
+            Application.Top.DrawComplete += Top_DrawComplete;
+            Application.Top.SizeChanging += Current_TerminalResized;
         }
 
-        int minX = x;
-        int minY = y;
-        const int borderOffset = 2; // This 2 is for the space around
-        int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset;
-        int maxH = items.Length + borderOffset;
+        Application.MouseEvent += Application_RootMouseEvent;
 
-        if (parent is { } && x + maxW > Driver.Cols)
-        {
-            minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
-        }
+        // Things this view knows how to do
+        AddCommand (Command.Up, () => MoveUp ());
+        AddCommand (Command.Down, () => MoveDown ());
 
-        if (y + maxH > Driver.Rows)
-        {
-            minY = Math.Max (Driver.Rows - maxH, 0);
-        }
+        AddCommand (
+                    Command.Left,
+                    () =>
+                    {
+                        _host!.PreviousMenu (true);
 
-        return new (minX, minY, maxW, maxH);
-    }
+                        return true;
+                    }
+                   );
 
-    internal required MenuBar Host
-    {
-        get => _host;
-        init
-        {
-            ArgumentNullException.ThrowIfNull (value);
-            _host = value;
-        }
-    }
+        AddCommand (
+                    Command.Cancel,
+                    () =>
+                    {
+                        CloseAllMenus ();
 
-    internal required MenuBarItem? BarItems
-    {
-        get => _barItems!;
-        init
-        {
-            ArgumentNullException.ThrowIfNull (value);
-            _barItems = value;
+                        return true;
+                    }
+                   );
 
-            // Debugging aid so ToString() is helpful
-            Text = _barItems.Title;
-        }
+        AddCommand (
+                    Command.Accept,
+                    () =>
+                    {
+                        RunSelected ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.Select,
+                    ctx =>
+                    {
+                        if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                        {
+                            return false;
+                        }
+
+                        return _host?.SelectItem ((keyCommandContext.Binding.Data as MenuItem)!);
+                    });
+
+        AddCommand (
+                    Command.Toggle,
+                    ctx =>
+                    {
+                        if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                        {
+                            return false;
+                        }
+
+                        return ExpandCollapse ((keyCommandContext.Binding.Data as MenuItem)!);
+                    });
+
+        AddCommand (
+                    Command.HotKey,
+                    ctx =>
+                    {
+                        if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                        {
+                            return false;
+                        }
+
+                        return _host?.SelectItem ((keyCommandContext.Binding.Data as MenuItem)!);
+                    });
+
+        // Default key bindings for this view
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
+        KeyBindings.Add (Key.CursorLeft, Command.Left);
+        KeyBindings.Add (Key.CursorRight, Command.Right);
+        KeyBindings.Add (Key.Esc, Command.Cancel);
     }
 
-    internal Menu? Parent { get; init; }
+    internal int _currentChild;
+    internal View? _previousSubFocused;
+    private readonly MenuBarItem? _barItems;
+    private readonly MenuBar _host;
 
     public override void BeginInit ()
     {
         base.BeginInit ();
 
-        var frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
+        Rectangle frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
 
         if (Frame.X != frame.X)
         {
             X = frame.X;
         }
+
         if (Frame.Y != frame.Y)
         {
             Y = frame.Y;
         }
+
         Width = frame.Width;
         Height = frame.Height;
 
@@ -94,6 +131,7 @@ internal sealed class Menu : View
                     if (menuItem.ShortcutKey != Key.Empty)
                     {
                         KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
+
                         // Remove an existent ShortcutKey
                         menuItem._menuBar.KeyBindings.Remove (menuItem.ShortcutKey!);
                         menuItem._menuBar.KeyBindings.Add (menuItem.ShortcutKey!, keyBinding);
@@ -155,196 +193,301 @@ internal sealed class Menu : View
         AddKeyBindingsHotKey (_barItems);
     }
 
-    public Menu ()
+    public override Point? PositionCursor ()
     {
-        if (Application.Top is { })
+        if (_host.IsMenuOpen)
         {
-            Application.Top.DrawComplete += Top_DrawComplete;
-            Application.Top.SizeChanging += Current_TerminalResized;
-        }
-
-        Application.MouseEvent += Application_RootMouseEvent;
-
-        // Things this view knows how to do
-        AddCommand (Command.Up, () => MoveUp ());
-        AddCommand (Command.Down, () => MoveDown ());
-
-        AddCommand (
-                    Command.Left,
-                    () =>
-                    {
-                        _host!.PreviousMenu (true);
-
-                        return true;
-                    }
-                   );
-
-        AddCommand (
-                    Command.Cancel,
-                    () =>
-                    {
-                        CloseAllMenus ();
-
-                        return true;
-                    }
-                   );
+            if (_barItems!.IsTopLevel)
+            {
+                return _host.PositionCursor ();
+            }
 
-        AddCommand (
-                    Command.Accept,
-                    () =>
-                    {
-                        RunSelected ();
+            Move (2, 1 + _currentChild);
 
-                        return true;
-                    }
-                   );
-        AddCommand (Command.Select, ctx => _host?.SelectItem ((ctx.Data as MenuItem)!));
-        AddCommand (Command.Toggle, ctx => ExpandCollapse ((ctx.Data as MenuItem)!));
-        AddCommand (Command.HotKey, ctx => _host?.SelectItem ((ctx.Data as MenuItem)!));
+            return null; // Don't show the cursor
+        }
 
-        // Default key bindings for this view
-        KeyBindings.Add (Key.CursorUp, Command.Up);
-        KeyBindings.Add (Key.CursorDown, Command.Down);
-        KeyBindings.Add (Key.CursorLeft, Command.Left);
-        KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.Esc, Command.Cancel);
+        return _host.PositionCursor ();
     }
 
-    private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem)
+    public void Run (Action? action)
     {
-        if (menuBarItem is null || menuBarItem.Children is null)
+        if (action is null)
         {
             return;
         }
 
-        IEnumerable<MenuItem> menuItems = menuBarItem.Children.Where (m => m is { })!;
-
-        foreach (MenuItem menuItem in menuItems)
-        {
-            KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, menuItem);
+        Application.UngrabMouse ();
+        _host.CloseAllMenus ();
+        Application.LayoutAndDraw (true);
 
-            if (menuItem.HotKey != Key.Empty)
-            {
-                KeyBindings.Remove (menuItem.HotKey!);
-                KeyBindings.Add (menuItem.HotKey!, keyBinding);
-                KeyBindings.Remove (menuItem.HotKey!.WithAlt);
-                KeyBindings.Add (menuItem.HotKey.WithAlt, keyBinding);
-            }
-        }
+        _host.Run (action);
     }
 
-    private void RemoveKeyBindingsHotKey (MenuBarItem? menuBarItem)
+    protected override void Dispose (bool disposing)
     {
-        if (menuBarItem is null || menuBarItem.Children is null)
+        RemoveKeyBindingsHotKey (_barItems);
+
+        if (Application.Top is { })
         {
-            return;
+            Application.Top.DrawComplete -= Top_DrawComplete;
+            Application.Top.SizeChanging -= Current_TerminalResized;
         }
 
-        IEnumerable<MenuItem> menuItems = menuBarItem.Children.Where (m => m is { })!;
+        Application.MouseEvent -= Application_RootMouseEvent;
+        base.Dispose (disposing);
+    }
 
-        foreach (MenuItem menuItem in menuItems)
+    protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
+    {
+        if (!newHasFocus)
         {
-            if (menuItem.HotKey != Key.Empty)
-            {
-                KeyBindings.Remove (menuItem.HotKey!);
-                KeyBindings.Remove (menuItem.HotKey!.WithAlt);
-            }
+            _host.LostFocus (previousFocusedView!);
         }
     }
 
-    /// <summary>Called when a key bound to Command.ToggleExpandCollapse is pressed. This means a hot key was pressed.</summary>
-    /// <returns></returns>
-    private bool ExpandCollapse (MenuItem? menuItem)
+    /// <inheritdoc/>
+    protected override bool OnKeyDownNotHandled (Key keyEvent)
     {
-        if (!IsInitialized || !Visible)
+        // We didn't handle the key, pass it on to host
+        return _host.InvokeCommandsBoundToKey (keyEvent) == true;
+    }
+
+    protected override bool OnMouseEvent (MouseEventArgs me)
+    {
+        if (!_host._handled && !_host.HandleGrabView (me, this))
         {
-            return true;
+            return false;
         }
 
+        _host._handled = false;
+        bool disabled;
 
-        for (var c = 0; c < _barItems!.Children!.Length; c++)
+        if (me.Flags == MouseFlags.Button1Clicked)
         {
-            if (_barItems.Children [c] == menuItem)
+            disabled = false;
+
+            if (me.Position.Y < 0)
             {
-                _currentChild = c;
+                return me.Handled = true;
+            }
 
-                break;
+            if (me.Position.Y >= _barItems!.Children!.Length)
+            {
+                return me.Handled = true;
             }
-        }
 
-        if (menuItem is { })
-        {
-            var m = menuItem as MenuBarItem;
+            MenuItem item = _barItems.Children [me.Position.Y]!;
 
-            if (m?.Children?.Length > 0)
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+            if (item is null || !item.IsEnabled ())
             {
-                MenuItem? item = _barItems.Children [_currentChild];
+                disabled = true;
+            }
 
-                if (item is null)
-                {
-                    return true;
-                }
+            if (disabled)
+            {
+                return me.Handled = true;
+            }
 
-                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
-                bool disabled = item is null || !item.IsEnabled ();
+            _currentChild = me.Position.Y;
+            RunSelected ();
 
-                if (!disabled && (_host.UseSubMenusSingleFrame || !CheckSubMenu ()))
-                {
-                    SetNeedsDraw ();
-                    SetParentSetNeedsDisplay ();
+            return me.Handled = true;
+        }
 
-                    return true;
-                }
+        if (me.Flags != MouseFlags.Button1Pressed
+            && me.Flags != MouseFlags.Button1DoubleClicked
+            && me.Flags != MouseFlags.Button1TripleClicked
+            && me.Flags != MouseFlags.ReportMousePosition
+            && !me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
+        {
+            return false;
+        }
 
-                if (!disabled)
-                {
-                    _host.OnMenuOpened ();
-                }
+        {
+            disabled = false;
+
+            if (me.Position.Y < 0 || me.Position.Y >= _barItems!.Children!.Length)
+            {
+                return me.Handled = true;
             }
-            else
+
+            MenuItem item = _barItems.Children [me.Position.Y]!;
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+            if (item is null)
             {
-                _host.SelectItem (menuItem);
+                return me.Handled = true;
+            }
+
+            if (item.IsEnabled () != true)
+            {
+                disabled = true;
+            }
+
+            if (!disabled)
+            {
+                _currentChild = me.Position.Y;
+            }
+
+            if (_host.UseSubMenusSingleFrame || !CheckSubMenu ())
+            {
+                SetNeedsDraw ();
+                SetParentSetNeedsDisplay ();
+
+                return me.Handled = true;
             }
+
+            _host.OnMenuOpened ();
+
+            return me.Handled = true;
         }
-        else if (_host.IsMenuOpen)
+    }
+
+    /// <inheritdoc/>
+    protected override void OnVisibleChanged ()
+    {
+        base.OnVisibleChanged ();
+
+        if (Visible)
         {
-            _host.CloseAllMenus ();
+            Application.MouseEvent += Application_RootMouseEvent;
         }
         else
         {
-            _host.OpenMenu ();
+            Application.MouseEvent -= Application_RootMouseEvent;
+        }
+    }
+
+    internal required MenuBarItem? BarItems
+    {
+        get => _barItems!;
+        init
+        {
+            ArgumentNullException.ThrowIfNull (value);
+            _barItems = value;
+
+            // Debugging aid so ToString() is helpful
+            Text = _barItems.Title;
+        }
+    }
+
+    internal bool CheckSubMenu ()
+    {
+        if (_currentChild == -1 || _barItems?.Children? [_currentChild] is null)
+        {
+            return true;
+        }
+
+        MenuBarItem? subMenu = _barItems.SubMenu (_barItems.Children [_currentChild]!);
+
+        // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+        if (subMenu is { })
+        {
+            int pos = -1;
+
+            if (_host._openSubMenu is { })
+            {
+                pos = _host._openSubMenu.FindIndex (o => o._barItems == subMenu);
+            }
+
+            if (pos == -1
+                && this != _host.OpenCurrentMenu
+                && subMenu.Children != _host.OpenCurrentMenu!._barItems!.Children
+                && !_host.CloseMenu (false, true))
+            {
+                return false;
+            }
+
+            _host.Activate (_host._selected, pos, subMenu);
+        }
+        else if (_host._openSubMenu?.Count == 0 || _host._openSubMenu?.Last ()._barItems!.IsSubMenuOf (_barItems.Children [_currentChild]!) == false)
+        {
+            return _host.CloseMenu (false, true);
+        }
+        else
+        {
+            SetNeedsDraw ();
+            SetParentSetNeedsDisplay ();
         }
 
         return true;
     }
 
-    /// <inheritdoc />
-    protected override bool OnKeyDownNotHandled (Key keyEvent)
+    internal Attribute DetermineColorSchemeFor (MenuItem? item, int index)
     {
-        // We didn't handle the key, pass it on to host
-        return _host.InvokeCommandsBoundToKey (keyEvent) == true;
+        if (item is null)
+        {
+            return GetNormalColor ();
+        }
+
+        if (index == _currentChild)
+        {
+            return GetFocusColor ();
+        }
+
+        return !item.IsEnabled () ? ColorScheme!.Disabled : GetNormalColor ();
     }
 
-    private void Current_TerminalResized (object? sender, SizeChangedEventArgs e)
+    internal required MenuBar Host
     {
-        if (_host.IsMenuOpen)
+        get => _host;
+        init
         {
-            _host.CloseAllMenus ();
+            ArgumentNullException.ThrowIfNull (value);
+            _host = value;
         }
     }
 
-    /// <inheritdoc/>
-    protected override void OnVisibleChanged ()
+    internal static Rectangle MakeFrame (int x, int y, MenuItem? []? items, Menu? parent = null)
     {
-        base.OnVisibleChanged ();
+        if (items is null || items.Length == 0)
+        {
+            return Rectangle.Empty;
+        }
 
-        if (Visible)
+        int minX = x;
+        int minY = y;
+        const int borderOffset = 2; // This 2 is for the space around
+        int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset;
+        int maxH = items.Length + borderOffset;
+
+        if (parent is { } && x + maxW > Driver.Cols)
         {
-            Application.MouseEvent += Application_RootMouseEvent;
+            minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
         }
-        else
+
+        if (y + maxH > Driver.Rows)
         {
-            Application.MouseEvent -= Application_RootMouseEvent;
+            minY = Math.Max (Driver.Rows - maxH, 0);
+        }
+
+        return new (minX, minY, maxW, maxH);
+    }
+
+    internal Menu? Parent { get; init; }
+
+    private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem)
+    {
+        if (menuBarItem is null || menuBarItem.Children is null)
+        {
+            return;
+        }
+
+        IEnumerable<MenuItem> menuItems = menuBarItem.Children.Where (m => m is { })!;
+
+        foreach (MenuItem menuItem in menuItems)
+        {
+            KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, menuItem);
+
+            if (menuItem.HotKey != Key.Empty)
+            {
+                KeyBindings.Remove (menuItem.HotKey!);
+                KeyBindings.Add (menuItem.HotKey!, keyBinding);
+                KeyBindings.Remove (menuItem.HotKey!.WithAlt);
+                KeyBindings.Add (menuItem.HotKey.WithAlt, keyBinding);
+            }
         }
     }
 
@@ -378,279 +521,83 @@ internal sealed class Menu : View
         }
     }
 
-    internal Attribute DetermineColorSchemeFor (MenuItem? item, int index)
+    private void CloseAllMenus ()
     {
-        if (item is null)
-        {
-            return GetNormalColor ();
-        }
+        Application.UngrabMouse ();
+        _host.CloseAllMenus ();
+    }
 
-        if (index == _currentChild)
+    private void Current_TerminalResized (object? sender, SizeChangedEventArgs e)
+    {
+        if (_host.IsMenuOpen)
         {
-            return GetFocusColor ();
+            _host.CloseAllMenus ();
         }
-
-        return !item.IsEnabled () ? ColorScheme!.Disabled : GetNormalColor ();
     }
 
-    // By doing this we draw last, over everything else.
-    private void Top_DrawComplete (object? sender, DrawEventArgs e)
+    /// <summary>Called when a key bound to Command.ToggleExpandCollapse is pressed. This means a hot key was pressed.</summary>
+    /// <returns></returns>
+    private bool ExpandCollapse (MenuItem? menuItem)
     {
-        if (!Visible)
+        if (!IsInitialized || !Visible)
         {
-            return;
+            return true;
         }
 
-        if (_barItems!.Children is null)
+        for (var c = 0; c < _barItems!.Children!.Length; c++)
         {
-            return;
-        }
-
-        DrawBorderAndPadding ();
-        RenderLineCanvas ();
-
-        // BUGBUG: Views should not change the clip. Doing so is an indcation of poor design or a bug in the framework.
-        Region? savedClip = View.SetClipToScreen ();
+            if (_barItems.Children [c] == menuItem)
+            {
+                _currentChild = c;
 
-        SetAttribute (GetNormalColor ());
+                break;
+            }
+        }
 
-        for (int i = Viewport.Y; i < _barItems!.Children.Length; i++)
+        if (menuItem is { })
         {
-            if (i < 0)
-            {
-                continue;
-            }
+            var m = menuItem as MenuBarItem;
 
-            if (ViewportToScreen (Viewport).Y + i >= Driver.Rows)
+            if (m?.Children?.Length > 0)
             {
-                break;
-            }
+                MenuItem? item = _barItems.Children [_currentChild];
 
-            MenuItem? item = _barItems.Children [i];
+                if (item is null)
+                {
+                    return true;
+                }
 
-            SetAttribute (
-                                 // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
-                                 item is null ? GetNormalColor () :
-                                 i == _currentChild ? GetFocusColor () : GetNormalColor ()
-                                );
+                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+                bool disabled = item is null || !item.IsEnabled ();
 
-            if (item is null && BorderStyle != LineStyle.None)
-            {
-                Point s = ViewportToScreen (new Point (-1, i));
-                Driver.Move (s.X, s.Y);
-                Driver.AddRune (Glyphs.LeftTee);
-            }
-            else if (Frame.X < Driver.Cols)
-            {
-                Move (0, i);
-            }
-
-            SetAttribute (DetermineColorSchemeFor (item, i));
-
-            for (int p = Viewport.X; p < Frame.Width - 2; p++)
-            {
-                // This - 2 is for the border
-                if (p < 0)
-                {
-                    continue;
-                }
-
-                if (ViewportToScreen (Viewport).X + p >= Driver.Cols)
-                {
-                    break;
-                }
-
-                if (item is null)
-                {
-                    Driver.AddRune (Glyphs.HLine);
-                }
-                else if (i == 0 && p == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { })
+                if (!disabled && (_host.UseSubMenusSingleFrame || !CheckSubMenu ()))
                 {
-                    Driver.AddRune (Glyphs.LeftArrow);
-                }
+                    SetNeedsDraw ();
+                    SetParentSetNeedsDisplay ();
 
-                // This `- 3` is left border + right border + one row in from right
-                else if (p == Frame.Width - 3 && _barItems?.SubMenu (_barItems.Children [i]!) is { })
-                {
-                    Driver.AddRune (Glyphs.RightArrow);
-                }
-                else
-                {
-                    Driver.AddRune ((Rune)' ');
+                    return true;
                 }
-            }
 
-            if (item is null)
-            {
-                if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width)
+                if (!disabled)
                 {
-                    Point s = ViewportToScreen (new Point (Frame.Width - 2, i));
-                    Driver.Move (s.X, s.Y);
-                    Driver.AddRune (Glyphs.RightTee);
+                    _host.OnMenuOpened ();
                 }
-
-                continue;
-            }
-
-            string? textToDraw;
-            Rune nullCheckedChar = Glyphs.CheckStateNone;
-            Rune checkChar = Glyphs.Selected;
-            Rune uncheckedChar = Glyphs.UnSelected;
-
-            if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked))
-            {
-                checkChar = Glyphs.CheckStateChecked;
-                uncheckedChar = Glyphs.CheckStateUnChecked;
-            }
-
-            // Support Checked even though CheckType wasn't set
-            if (item is { CheckType: MenuItemCheckStyle.Checked, Checked: null })
-            {
-                textToDraw = $"{nullCheckedChar} {item.Title}";
-            }
-            else if (item.Checked == true)
-            {
-                textToDraw = $"{checkChar} {item.Title}";
-            }
-            else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio))
-            {
-                textToDraw = $"{uncheckedChar} {item.Title}";
             }
             else
             {
-                textToDraw = item.Title;
-            }
-
-            Point screen = ViewportToScreen (new Point (0, i));
-
-            if (screen.X < Driver.Cols)
-            {
-                Driver.Move (screen.X + 1, screen.Y);
-
-                if (!item.IsEnabled ())
-                {
-                    DrawHotString (textToDraw, ColorScheme!.Disabled, ColorScheme.Disabled);
-                }
-                else if (i == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { })
-                {
-                    var tf = new TextFormatter
-                    {
-                        ConstrainToWidth = Frame.Width - 3,
-                        ConstrainToHeight = 1,
-                        Alignment = Alignment.Center, HotKeySpecifier = MenuBar.HotKeySpecifier, Text = textToDraw
-                    };
-
-                    // The -3 is left/right border + one space (not sure what for)
-                    tf.Draw (
-                             ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)),
-                             i == _currentChild ? GetFocusColor () : GetNormalColor (),
-                             i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
-                             SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
-                            );
-                }
-                else
-                {
-                    DrawHotString (
-                                   textToDraw,
-                                   i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
-                                   i == _currentChild ? GetFocusColor () : GetNormalColor ()
-                                  );
-                }
-
-                // The help string
-                int l = item.ShortcutTag.GetColumns () == 0
-                            ? item.Help.GetColumns ()
-                            : item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2;
-                int col = Frame.Width - l - 3;
-                screen = ViewportToScreen (new Point (col, i));
-
-                if (screen.X < Driver.Cols)
-                {
-                    Driver.Move (screen.X, screen.Y);
-                    Driver.AddStr (item.Help);
-
-                    // The shortcut tag string
-                    if (!string.IsNullOrEmpty (item.ShortcutTag))
-                    {
-                        Driver.Move (screen.X + l - item.ShortcutTag.GetColumns (), screen.Y);
-                        Driver.AddStr (item.ShortcutTag);
-                    }
-                }
-            }
-        }
-
-        View.SetClip (savedClip);
-    }
-
-    public override Point? PositionCursor ()
-    {
-        if (_host.IsMenuOpen)
-        {
-            if (_barItems!.IsTopLevel)
-            {
-                return _host.PositionCursor ();
+                _host.SelectItem (menuItem);
             }
-
-            Move (2, 1 + _currentChild);
-
-            return null; // Don't show the cursor
-        }
-
-        return _host.PositionCursor ();
-    }
-
-    public void Run (Action? action)
-    {
-        if (action is null)
-        {
-            return;
         }
-
-        Application.UngrabMouse ();
-        _host.CloseAllMenus ();
-        Application.LayoutAndDraw (true);
-
-        _host.Run (action);
-    }
-
-    protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
-    {
-        if (!newHasFocus)
-        {
-            _host.LostFocus (previousFocusedView!);
-        }
-    }
-
-    private void RunSelected ()
-    {
-        if (_barItems!.IsTopLevel)
+        else if (_host.IsMenuOpen)
         {
-            Run (_barItems.Action);
+            _host.CloseAllMenus ();
         }
         else
         {
-            switch (_currentChild)
-            {
-                case > -1 when _barItems.Children! [_currentChild]!.Action != null!:
-                    Run (_barItems.Children [_currentChild]?.Action);
-
-                    break;
-                case 0 when _host.UseSubMenusSingleFrame && _barItems.Children [_currentChild]?.Parent!.Parent != null:
-                    _host.PreviousMenu (_barItems.Children [_currentChild]!.Parent!.IsFromSubMenu, true);
-
-                    break;
-                case > -1 when _barItems.SubMenu (_barItems.Children [_currentChild]) != null!:
-                    CheckSubMenu ();
-
-                    break;
-            }
+            _host.OpenMenu ();
         }
-    }
 
-    private void CloseAllMenus ()
-    {
-        Application.UngrabMouse ();
-        _host.CloseAllMenus ();
+        return true;
     }
 
     private bool MoveDown ()
@@ -801,6 +748,51 @@ internal sealed class Menu : View
         return true;
     }
 
+    private void RemoveKeyBindingsHotKey (MenuBarItem? menuBarItem)
+    {
+        if (menuBarItem is null || menuBarItem.Children is null)
+        {
+            return;
+        }
+
+        IEnumerable<MenuItem> menuItems = menuBarItem.Children.Where (m => m is { })!;
+
+        foreach (MenuItem menuItem in menuItems)
+        {
+            if (menuItem.HotKey != Key.Empty)
+            {
+                KeyBindings.Remove (menuItem.HotKey!);
+                KeyBindings.Remove (menuItem.HotKey!.WithAlt);
+            }
+        }
+    }
+
+    private void RunSelected ()
+    {
+        if (_barItems!.IsTopLevel)
+        {
+            Run (_barItems.Action);
+        }
+        else
+        {
+            switch (_currentChild)
+            {
+                case > -1 when _barItems.Children! [_currentChild]!.Action != null!:
+                    Run (_barItems.Children [_currentChild]?.Action);
+
+                    break;
+                case 0 when _host.UseSubMenusSingleFrame && _barItems.Children [_currentChild]?.Parent!.Parent != null:
+                    _host.PreviousMenu (_barItems.Children [_currentChild]!.Parent!.IsFromSubMenu, true);
+
+                    break;
+                case > -1 when _barItems.SubMenu (_barItems.Children [_currentChild]) != null!:
+                    CheckSubMenu ();
+
+                    break;
+            }
+        }
+    }
+
     private void SetParentSetNeedsDisplay ()
     {
         if (_host._openSubMenu is { })
@@ -815,151 +807,193 @@ internal sealed class Menu : View
         _host.SetNeedsDraw ();
     }
 
-    protected override bool OnMouseEvent (MouseEventArgs me)
+    // By doing this we draw last, over everything else.
+    private void Top_DrawComplete (object? sender, DrawEventArgs e)
     {
-        if (!_host._handled && !_host.HandleGrabView (me, this))
+        if (!Visible)
         {
-            return false;
+            return;
         }
 
-        _host._handled = false;
-        bool disabled;
-
-        if (me.Flags == MouseFlags.Button1Clicked)
+        if (_barItems!.Children is null)
         {
-            disabled = false;
+            return;
+        }
 
-            if (me.Position.Y < 0)
+        DrawBorderAndPadding ();
+        RenderLineCanvas ();
+
+        // BUGBUG: Views should not change the clip. Doing so is an indcation of poor design or a bug in the framework.
+        Region? savedClip = SetClipToScreen ();
+
+        SetAttribute (GetNormalColor ());
+
+        for (int i = Viewport.Y; i < _barItems!.Children.Length; i++)
+        {
+            if (i < 0)
             {
-                return me.Handled = true;
+                continue;
             }
 
-            if (me.Position.Y >= _barItems!.Children!.Length)
+            if (ViewportToScreen (Viewport).Y + i >= Driver.Rows)
             {
-                return me.Handled = true;
+                break;
             }
 
-            MenuItem item = _barItems.Children [me.Position.Y]!;
+            MenuItem? item = _barItems.Children [i];
 
-            // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
-            if (item is null || !item.IsEnabled ())
+            SetAttribute (
+
+                          // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+                          item is null ? GetNormalColor () :
+                          i == _currentChild ? GetFocusColor () : GetNormalColor ()
+                         );
+
+            if (item is null && BorderStyle != LineStyle.None)
             {
-                disabled = true;
+                Point s = ViewportToScreen (new Point (-1, i));
+                Driver.Move (s.X, s.Y);
+                Driver.AddRune (Glyphs.LeftTee);
             }
-
-            if (disabled)
+            else if (Frame.X < Driver.Cols)
             {
-                return me.Handled = true;
+                Move (0, i);
             }
 
-            _currentChild = me.Position.Y;
-            RunSelected ();
+            SetAttribute (DetermineColorSchemeFor (item, i));
 
-            return me.Handled = true;
-        }
+            for (int p = Viewport.X; p < Frame.Width - 2; p++)
+            {
+                // This - 2 is for the border
+                if (p < 0)
+                {
+                    continue;
+                }
 
-        if (me.Flags != MouseFlags.Button1Pressed
-            && me.Flags != MouseFlags.Button1DoubleClicked
-            && me.Flags != MouseFlags.Button1TripleClicked
-            && me.Flags != MouseFlags.ReportMousePosition
-            && !me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
-        {
-            return false;
-        }
+                if (ViewportToScreen (Viewport).X + p >= Driver.Cols)
+                {
+                    break;
+                }
 
-        {
-            disabled = false;
+                if (item is null)
+                {
+                    Driver.AddRune (Glyphs.HLine);
+                }
+                else if (i == 0 && p == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { })
+                {
+                    Driver.AddRune (Glyphs.LeftArrow);
+                }
 
-            if (me.Position.Y < 0 || me.Position.Y >= _barItems!.Children!.Length)
-            {
-                return me.Handled = true;
+                // This `- 3` is left border + right border + one row in from right
+                else if (p == Frame.Width - 3 && _barItems?.SubMenu (_barItems.Children [i]!) is { })
+                {
+                    Driver.AddRune (Glyphs.RightArrow);
+                }
+                else
+                {
+                    Driver.AddRune ((Rune)' ');
+                }
             }
 
-            MenuItem item = _barItems.Children [me.Position.Y]!;
-
-            // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
             if (item is null)
             {
-                return me.Handled = true;
+                if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width)
+                {
+                    Point s = ViewportToScreen (new Point (Frame.Width - 2, i));
+                    Driver.Move (s.X, s.Y);
+                    Driver.AddRune (Glyphs.RightTee);
+                }
+
+                continue;
             }
 
-            if (item.IsEnabled () != true)
+            string? textToDraw;
+            Rune nullCheckedChar = Glyphs.CheckStateNone;
+            Rune checkChar = Glyphs.Selected;
+            Rune uncheckedChar = Glyphs.UnSelected;
+
+            if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked))
             {
-                disabled = true;
+                checkChar = Glyphs.CheckStateChecked;
+                uncheckedChar = Glyphs.CheckStateUnChecked;
             }
 
-            if (!disabled)
+            // Support Checked even though CheckType wasn't set
+            if (item is { CheckType: MenuItemCheckStyle.Checked, Checked: null })
             {
-                _currentChild = me.Position.Y;
+                textToDraw = $"{nullCheckedChar} {item.Title}";
             }
-
-            if (_host.UseSubMenusSingleFrame || !CheckSubMenu ())
+            else if (item.Checked == true)
             {
-                SetNeedsDraw ();
-                SetParentSetNeedsDisplay ();
-
-                return me.Handled = true;
+                textToDraw = $"{checkChar} {item.Title}";
             }
-
-            _host.OnMenuOpened ();
-
-            return me.Handled = true;
-        }
-    }
-
-    internal bool CheckSubMenu ()
-    {
-        if (_currentChild == -1 || _barItems?.Children? [_currentChild] is null)
-        {
-            return true;
-        }
-
-        MenuBarItem? subMenu = _barItems.SubMenu (_barItems.Children [_currentChild]!);
-
-        // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
-        if (subMenu is { })
-        {
-            int pos = -1;
-
-            if (_host._openSubMenu is { })
+            else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio))
             {
-                pos = _host._openSubMenu.FindIndex (o => o._barItems == subMenu);
+                textToDraw = $"{uncheckedChar} {item.Title}";
             }
-
-            if (pos == -1
-                && this != _host.OpenCurrentMenu
-                && subMenu.Children != _host.OpenCurrentMenu!._barItems!.Children
-                && !_host.CloseMenu (false, true))
+            else
             {
-                return false;
+                textToDraw = item.Title;
             }
 
-            _host.Activate (_host._selected, pos, subMenu);
-        }
-        else if (_host._openSubMenu?.Count == 0 || _host._openSubMenu?.Last ()._barItems!.IsSubMenuOf (_barItems.Children [_currentChild]!) == false)
-        {
-            return _host.CloseMenu (false, true);
-        }
-        else
-        {
-            SetNeedsDraw ();
-            SetParentSetNeedsDisplay ();
-        }
+            Point screen = ViewportToScreen (new Point (0, i));
 
-        return true;
-    }
+            if (screen.X < Driver.Cols)
+            {
+                Driver.Move (screen.X + 1, screen.Y);
 
-    protected override void Dispose (bool disposing)
-    {
-        RemoveKeyBindingsHotKey (_barItems);
+                if (!item.IsEnabled ())
+                {
+                    DrawHotString (textToDraw, ColorScheme!.Disabled, ColorScheme.Disabled);
+                }
+                else if (i == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { })
+                {
+                    var tf = new TextFormatter
+                    {
+                        ConstrainToWidth = Frame.Width - 3,
+                        ConstrainToHeight = 1,
+                        Alignment = Alignment.Center, HotKeySpecifier = MenuBar.HotKeySpecifier, Text = textToDraw
+                    };
 
-        if (Application.Top is { })
-        {
-            Application.Top.DrawComplete -= Top_DrawComplete;
-            Application.Top.SizeChanging -= Current_TerminalResized;
+                    // The -3 is left/right border + one space (not sure what for)
+                    tf.Draw (
+                             ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)),
+                             i == _currentChild ? GetFocusColor () : GetNormalColor (),
+                             i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
+                             SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
+                            );
+                }
+                else
+                {
+                    DrawHotString (
+                                   textToDraw,
+                                   i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
+                                   i == _currentChild ? GetFocusColor () : GetNormalColor ()
+                                  );
+                }
+
+                // The help string
+                int l = item.ShortcutTag.GetColumns () == 0
+                            ? item.Help.GetColumns ()
+                            : item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2;
+                int col = Frame.Width - l - 3;
+                screen = ViewportToScreen (new Point (col, i));
+
+                if (screen.X < Driver.Cols)
+                {
+                    Driver.Move (screen.X, screen.Y);
+                    Driver.AddStr (item.Help);
+
+                    // The shortcut tag string
+                    if (!string.IsNullOrEmpty (item.ShortcutTag))
+                    {
+                        Driver.Move (screen.X + l - item.ShortcutTag.GetColumns (), screen.Y);
+                        Driver.AddStr (item.ShortcutTag);
+                    }
+                }
+            }
         }
 
-        Application.MouseEvent -= Application_RootMouseEvent;
-        base.Dispose (disposing);
+        SetClip (savedClip);
     }
 }

+ 11 - 3
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -133,16 +133,24 @@ public class MenuBar : View, IDesignable
                                                   {
                                                       CloseOtherOpenedMenuBar ();
 
-                                                      return Select (Menus.IndexOf (ctx.Data));
+                                                      if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                                                      {
+                                                          return false;
+                                                      }
+                                                      return Select (Menus.IndexOf (keyCommandContext.Binding.Data));
                                                   });
         AddCommand (Command.Select, ctx =>
                                     {
-                                        if (ctx.Data is MouseEventArgs)
+                                        if (ctx is not CommandContext<KeyBinding> keyCommandContext)
+                                        {
+                                            return false ;
+                                        }
+                                        if (keyCommandContext.Binding.Data is MouseEventArgs)
                                         {
                                             // HACK: Work around the fact that View.MouseClick always invokes Select
                                             return false;
                                         }
-                                        var res = Run ((ctx.Data as MenuItem)?.Action!);
+                                        var res = Run ((keyCommandContext.Binding.Data as MenuItem)?.Action!);
                                         CloseAllMenus ();
 
                                         return res;

+ 9 - 4
Terminal.Gui/Views/MessageBox.cs

@@ -358,14 +358,19 @@ public static class MessageBox
                 if (count == defaultButton)
                 {
                     b.IsDefault = true;
-                    b.Accepting += (s, e) =>
+                    b.Accepting += (_, e) =>
                                    {
+                                       if (e.Context is not CommandContext<KeyBinding> keyCommandContext)
+                                       {
+                                           return;
+                                       }
+
                                        // TODO: With https://github.com/gui-cs/Terminal.Gui/issues/3778 we can simplify this
-                                       if (e.Context.Data is Button button)
+                                       if (keyCommandContext.Binding.Data is Button button)
                                        {
                                            Clicked = (int)button.Data!;
-                                       } 
-                                       else if (e.Context is CommandContext<KeyBinding> { Binding.BoundView: Button btn })
+                                       }
+                                       else if (keyCommandContext.Binding.BoundView is Button btn)
                                        {
                                            Clicked = (int)btn.Data!;
                                        }

+ 2 - 2
Terminal.Gui/Views/RadioGroup.cs

@@ -64,7 +64,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                                     return false;
                                 }
 
-                                var item = keyCommandContext.Data as int?;
+                                var item = keyCommandContext.Binding.Data as int?;
 
 
                                 if (HasFocus)
@@ -248,7 +248,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                 if (c > -1)
                 {
                     // Just like the user pressing the items' hotkey
-                    e.Handled = InvokeCommand<KeyBinding> (Command.HotKey, new KeyBinding ([Command.HotKey], KeyBindingScope.HotKey, boundView: this, context: c)) == true;
+                    e.Handled = InvokeCommand<KeyBinding> (Command.HotKey, new KeyBinding ([Command.HotKey], KeyBindingScope.HotKey, boundView: this, data: c)) == true;
                 }
             }
 

+ 5 - 4
Terminal.Gui/Views/Shortcut.cs

@@ -300,17 +300,18 @@ public class Shortcut : View, IOrientation, IDesignable
         AddCommand (Command.Select, DispatchCommand);
     }
 
-    private bool? DispatchCommand (ICommandContext commandContext)
+    private bool? DispatchCommand (ICommandContext? commandContext)
     {
         if (commandContext is not CommandContext<KeyBinding> ctx)
         {
             return false;
         }
-        if (ctx.Data != this)
+
+        if (ctx.Binding.Data != this)
         {
             // Invoke Select on the command view to cause it to change state if it wants to
             // If this causes CommandView to raise Accept, we eat it
-            ctx.Data = this;
+            ctx.Binding = ctx.Binding with { Data = this };
             CommandView.InvokeCommand (Command.Select, ctx);
         }
 
@@ -497,7 +498,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
             void CommandViewOnSelecting (object? sender, CommandEventArgs e)
             {
-                if (e.Context.Data != this)
+                if (e.Context is CommandContext<KeyBinding> keyCommandContext && keyCommandContext.Binding.Data != this)
                 {
                     // Forward command to ourselves
                     InvokeCommand<KeyBinding> (Command.Select, new ([Command.Select], KeyBindingScope.Focused, null, this));

+ 12 - 3
UICatalog/Scenarios/Editors/EventLog.cs

@@ -86,15 +86,24 @@ public class EventLog : ListView
 
                 _viewToLog.HandlingHotKey += (s, args) =>
                                         {
-                                            Log ($"HandlingHotKey: {args.Context.Command} {args.Context.Data}");
+                                            if (args.Context is CommandContext<KeyBinding> keyCommandContext)
+                                            {
+                                                Log ($"HandlingHotKey: {args.Context.Command} {keyCommandContext.Binding.Data}");
+                                            }
                                         };
                 _viewToLog.Selecting += (s, args) =>
                                         {
-                                            Log ($"Selecting: {args.Context.Command} {args.Context.Data}");
+                                            if (args.Context is CommandContext<KeyBinding> keyCommandContext)
+                                            {
+                                                Log ($"Selecting: {args.Context.Command} {keyCommandContext.Binding.Data}");
+                                            }
                                         };
                 _viewToLog.Accepting += (s, args) =>
                                         {
-                                            Log ($"Accepting: {args.Context.Command} {args.Context.Data}");
+                                            if (args.Context is CommandContext<KeyBinding> keyCommandContext)
+                                            {
+                                                Log ($"Accepting: {args.Context.Command} {keyCommandContext.Binding.Data}");
+                                            }
                                         };
             }
         }

+ 1 - 1
UnitTests/Views/TextViewTests.cs

@@ -6999,7 +6999,7 @@ TAB to jump between text field",
         {
             Width = Dim.Fill (), Height = Dim.Fill (), Text = "This is the first line.\nThis is the second line.\n"
         };
-        tv.UnwrappedCursorPosition += (s, e) => { cp = e.Point; };
+        tv.UnwrappedCursorPosition += (s, e) => { cp = e; };
         var top = new Toplevel ();
         top.Add (tv);
         Application.Begin (top);