瀏覽代碼

Fixed key binding issue with shortcuts

Tig 1 年之前
父節點
當前提交
28b3362ba4

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

@@ -1239,7 +1239,7 @@ public static partial class Application
     ///     Only relevant in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
     /// </remarks>
     /// <value>The current.</value>
-    public static Toplevel Current { get; private set; }
+    public static Toplevel Current { get; internal set; }
 
     private static void EnsureModalOrVisibleAlwaysOnTop (Toplevel topLevel)
     {

+ 2 - 0
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -4,6 +4,7 @@ using System.Collections;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
+using System.Runtime.Versioning;
 using System.Text.Encodings.Web;
 using System.Text.Json;
 using System.Text.Json.Serialization;
@@ -49,6 +50,7 @@ namespace Terminal.Gui;
 ///         Lowest Precedence.
 ///     </para>
 /// </summary>
+[ComponentGuarantees (ComponentGuaranteesOptions.None)]
 public static class ConfigurationManager
 {
     /// <summary>

+ 3 - 2
Terminal.Gui/Resources/config.json

@@ -27,10 +27,11 @@
       "Default": {
         "Dialog.DefaultButtonAlignment": "End",
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
-        "FrameView.DefaultBorderStyle": "Rounded",
+        "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
-        "Dialog.DefaultBorderStyle": "Double",
+        "Dialog.DefaultBorderStyle": "Single",
         "MessageBox.DefaultBorderStyle": "Double",
+        "Button.DefaultShadow": "None",
         "ColorSchemes": [
           {
             "TopLevel": {

+ 7 - 2
Terminal.Gui/View/Adornment/Margin.cs

@@ -23,6 +23,9 @@ public class Margin : Adornment
         HighlightStyle |= HighlightStyle.Pressed;
         Highlight += Margin_Highlight;
         LayoutStarted += Margin_LayoutStarted;
+
+        // Margin should not be focusable
+        CanFocus = false;
     }
 
     private void Margin_LayoutStarted (object? sender, LayoutEventArgs e)
@@ -81,7 +84,9 @@ public class Margin : Adornment
     {
         Rectangle screen = ViewportToScreen (viewport);
         Attribute normalAttr = GetNormalColor ();
-        Driver.SetAttribute (normalAttr);
+
+        Driver?.SetAttribute (normalAttr);
+
 
         // This just draws/clears the thickness, not the insides.
         if (ShadowStyle != ShadowStyle.None)
@@ -152,7 +157,7 @@ public class Margin : Adornment
     {
         if (ShadowStyle == style)
         {
-           // return style;
+            // return style;
         }
 
         if (ShadowStyle != ShadowStyle.None)

+ 1 - 1
Terminal.Gui/View/ViewMouse.cs

@@ -324,7 +324,7 @@ public partial class View
             {
                 Application.GrabMouse (this);
 
-                if (CanFocus)
+                if (!HasFocus && CanFocus)
                 {
                     // Set the focus, but don't invoke Accept
                     SetFocus ();

+ 7 - 2
Terminal.Gui/Views/Button.cs

@@ -41,7 +41,7 @@ public class Button : View
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter))]
 
-    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Opaque;
+    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
 
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
     public Button ()
@@ -107,7 +107,12 @@ public class Button : View
 
     private void Button_MouseClick (object sender, MouseEventEventArgs e)
     {
-       e.Handled = InvokeCommand (Command.HotKey) == true;
+        if (!CanFocus)
+        {
+            return;
+        }
+
+        e.Handled = InvokeCommand (Command.HotKey) == true;
     }
 
     private void Button_TitleChanged (object sender, StateEventArgs<string> e)

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

@@ -48,7 +48,7 @@ public class Dialog : Window
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter))]
-    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Transparent;
+    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
 
     /// <summary>
     ///     Defines the default border styling for <see cref="Dialog"/>. Can be configured via
@@ -57,7 +57,7 @@ public class Dialog : Window
 
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter))]
-    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Rounded;
+    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 
     private readonly List<Button> _buttons = new ();
 

+ 271 - 270
Terminal.Gui/Views/Shortcut.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using System.Threading.Channels;
 
 namespace Terminal.Gui;
 
@@ -84,8 +85,8 @@ public class Shortcut : View
 
         // If the user clicks anywhere on the Shortcut, other than the CommandView, invoke the Command
         MouseClick += Shortcut_MouseClick;
-        HelpView.MouseClick += Shortcut_MouseClick;
-        KeyView.MouseClick += Shortcut_MouseClick;
+        HelpView.MouseClick += Subview_MouseClick;
+        KeyView.MouseClick += Subview_MouseClick;
         LayoutStarted += OnLayoutStarted;
         Initialized += OnInitialized;
 
@@ -340,6 +341,12 @@ public class Shortcut : View
         }
     }
 
+    private void Subview_MouseClick (object sender, MouseEventEventArgs e)
+    {
+        // TODO: Remove. This does nothing.
+        return;
+    }
+
     #region Command
 
     private View _commandView = new ();
@@ -409,9 +416,6 @@ public class Shortcut : View
             // If you want it to get focus, you need to set it.
             _commandView.CanFocus = false;
 
-            _commandView.MouseClick += Shortcut_MouseClick;
-            _commandView.Accept += CommandViewAccept;
-
             _commandView.HotKeyChanged += (s, e) =>
                                           {
                                               if (e.NewKey != Key.Empty)
@@ -432,360 +436,357 @@ public class Shortcut : View
             UpdateKeyBinding ();
 
             return;
-
-            void CommandViewAccept (object sender, CancelEventArgs e)
-            {
-                // When the CommandView fires its Accept event, we want to act as though the
-                // Shortcut was clicked.
-                //if (base.OnAccept () == true)
-                //{
-                //    e.Cancel = true;
-                //}
-            }
         }
     }
 
     private void SetCommandViewDefaultLayout ()
-    {
-        CommandView.Margin.Thickness = GetMarginThickness ();
-        CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
-        CommandView.Y = 0; //Pos.Center ();
-    }
+{
+    CommandView.Margin.Thickness = GetMarginThickness ();
+    CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
+    CommandView.Y = 0; //Pos.Center ();
+}
 
-    private void Shortcut_TitleChanged (object sender, StateEventArgs<string> e)
-    {
-        // If the Title changes, update the CommandView text.
-        // This is a helper to make it easier to set the CommandView text.
-        // CommandView is public and replaceable, but this is a convenience.
-        _commandView.Text = Title;
-    }
+private void Shortcut_TitleChanged (object sender, StateEventArgs<string> e)
+{
+    // If the Title changes, update the CommandView text.
+    // This is a helper to make it easier to set the CommandView text.
+    // CommandView is public and replaceable, but this is a convenience.
+    _commandView.Text = Title;
+}
 
-    #endregion Command
+#endregion Command
 
-    #region Help
+#region Help
 
-    /// <summary>
-    ///     The subview that displays the help text for the command. Internal for unit testing.
-    /// </summary>
-    internal View HelpView { get; } = new ();
+/// <summary>
+///     The subview that displays the help text for the command. Internal for unit testing.
+/// </summary>
+internal View HelpView { get; } = new ();
 
-    private void SetHelpViewDefaultLayout ()
-    {
-        HelpView.Margin.Thickness = GetMarginThickness ();
-        HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
-        HelpView.Y = 0; //Pos.Center ();
-        HelpView.Width = Dim.Auto (DimAutoStyle.Text);
-        HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-        HelpView.Visible = true;
-        HelpView.VerticalTextAlignment = Alignment.Center;
-    }
+private void SetHelpViewDefaultLayout ()
+{
+    HelpView.Margin.Thickness = GetMarginThickness ();
+    HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+    HelpView.Y = 0; //Pos.Center ();
+    HelpView.Width = Dim.Auto (DimAutoStyle.Text);
+    HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+    HelpView.Visible = true;
+    HelpView.VerticalTextAlignment = Alignment.Center;
+}
 
-    /// <summary>
-    ///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
-    ///     .
-    /// </summary>
-    public override string Text
+/// <summary>
+///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
+///     .
+/// </summary>
+public override string Text
+{
+    get => HelpView?.Text;
+    set
     {
-        get => HelpView?.Text;
-        set
+        if (HelpView != null)
         {
-            if (HelpView != null)
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
+}
 
-    /// <summary>
-    ///     Gets or sets the help text displayed in the middle of the Shortcut.
-    /// </summary>
-    public string HelpText
+/// <summary>
+///     Gets or sets the help text displayed in the middle of the Shortcut.
+/// </summary>
+public string HelpText
+{
+    get => HelpView?.Text;
+    set
     {
-        get => HelpView?.Text;
-        set
+        if (HelpView != null)
         {
-            if (HelpView != null)
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
+}
 
-    #endregion Help
+#endregion Help
 
-    #region Key
+#region Key
 
-    private Key _key = Key.Empty;
+private Key _key = Key.Empty;
 
-    /// <summary>
-    ///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
-    /// </summary>
-    public Key Key
+/// <summary>
+///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
+/// </summary>
+public Key Key
+{
+    get => _key;
+    set
     {
-        get => _key;
-        set
+        if (value == null)
         {
-            if (value == null)
-            {
-                throw new ArgumentNullException ();
-            }
+            throw new ArgumentNullException ();
+        }
 
-            _key = value;
+        _key = value;
 
-            UpdateKeyBinding ();
+        UpdateKeyBinding ();
 
-            KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
-            ShowHide ();
-        }
+        KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
+        ShowHide ();
     }
+}
 
-    private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
+private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
 
-    /// <summary>
-    ///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
-    /// </summary>
-    public KeyBindingScope KeyBindingScope
+/// <summary>
+///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
+/// </summary>
+public KeyBindingScope KeyBindingScope
+{
+    get => _keyBindingScope;
+    set
     {
-        get => _keyBindingScope;
-        set
-        {
-            _keyBindingScope = value;
+        _keyBindingScope = value;
 
-            UpdateKeyBinding ();
-        }
+        UpdateKeyBinding ();
     }
+}
 
-    /// <summary>
-    ///     Gets the subview that displays the key. Internal for unit testing.
-    /// </summary>
+/// <summary>
+///     Gets the subview that displays the key. Internal for unit testing.
+/// </summary>
 
-    internal View KeyView { get; } = new ();
+internal View KeyView { get; } = new ();
 
-    private int _minimumKeyTextSize;
+private int _minimumKeyTextSize;
 
-    /// <summary>
-    /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
-    /// </summary>
-    public int MinimumKeyTextSize
+/// <summary>
+/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+/// </summary>
+public int MinimumKeyTextSize
+{
+    get => _minimumKeyTextSize;
+    set
     {
-        get => _minimumKeyTextSize;
-        set
+        if (value == _minimumKeyTextSize)
         {
-            if (value == _minimumKeyTextSize)
-            {
-                //return;
-            }
-
-            _minimumKeyTextSize = value;
-            SetKeyViewDefaultLayout ();
-            CommandView.SetNeedsLayout ();
-            HelpView.SetNeedsLayout ();
-            KeyView.SetNeedsLayout ();
-            SetSubViewNeedsDisplay ();
+            //return;
         }
+
+        _minimumKeyTextSize = value;
+        SetKeyViewDefaultLayout ();
+        CommandView.SetNeedsLayout ();
+        HelpView.SetNeedsLayout ();
+        KeyView.SetNeedsLayout ();
+        SetSubViewNeedsDisplay ();
     }
+}
 
-    private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
+private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
-    private void SetKeyViewDefaultLayout ()
-    {
-        KeyView.Margin.Thickness = GetMarginThickness ();
-        KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
-        KeyView.Y = 0; //Pos.Center ();
-        KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
-        KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-        KeyView.Visible = true;
-
-        // Right align the text in the keyview
-        KeyView.TextAlignment = Alignment.End;
-        KeyView.VerticalTextAlignment = Alignment.Center;
-        KeyView.KeyBindings.Clear ();
-    }
+private void SetKeyViewDefaultLayout ()
+{
+    KeyView.Margin.Thickness = GetMarginThickness ();
+    KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
+    KeyView.Y = 0; //Pos.Center ();
+    KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
+    KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+    KeyView.Visible = true;
+
+    // Right align the text in the keyview
+    KeyView.TextAlignment = Alignment.End;
+    KeyView.VerticalTextAlignment = Alignment.Center;
+    KeyView.KeyBindings.Clear ();
+}
 
-    private void UpdateKeyBinding ()
+private void UpdateKeyBinding ()
+{
+    if (Key != null)
     {
-        if (Key != null)
-        {
-            // Disable the command view key bindings
-            CommandView.KeyBindings.Remove (Key);
-            CommandView.KeyBindings.Remove (CommandView.HotKey);
-            KeyBindings.Remove (Key);
-            KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
-            //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
-        }
+        // Disable the command view key bindings
+        CommandView.KeyBindings.Remove (Key);
+        CommandView.KeyBindings.Remove (CommandView.HotKey);
+        KeyBindings.Remove (Key);
+        KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+        //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
     }
+}
 
-    #endregion Key
+#endregion Key
 
-    #region Accept Handling
+#region Accept Handling
 
-    /// <summary>
-    ///     Called when the <see cref="Command.Accept"/> command is received. This
-    ///     occurs
-    ///     - if the user clicks anywhere on the shortcut with the mouse
-    ///     - if the user presses Key
-    ///     - if the user presses the HotKey specified by CommandView
-    ///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
-    /// </summary>
-    protected bool? OnAccept (CommandContext ctx)
+/// <summary>
+///     Called when the <see cref="Command.Accept"/> command is received. This
+///     occurs
+///     - if the user clicks anywhere on the shortcut with the mouse
+///     - if the user presses Key
+///     - if the user presses the HotKey specified by CommandView
+///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
+/// </summary>
+protected bool? OnAccept (CommandContext ctx)
+{
+    var cancel = false;
+
+    switch (ctx.KeyBinding?.Scope)
     {
-        var cancel = false;
+        case KeyBindingScope.Application:
+            cancel = base.OnAccept () == true;
 
-        switch (ctx.KeyBinding?.Scope)
-        {
-            case KeyBindingScope.Application:
-                cancel = base.OnAccept () == true;
+            break;
 
-                break;
+        case KeyBindingScope.Focused:
+            base.OnAccept ();
 
-            case KeyBindingScope.Focused:
-                // TODO: Figure this out
-                cancel = base.OnAccept () == true;
+            // cancel if we're focused
+            cancel = true;
 
-                break;
+            break;
 
-            case KeyBindingScope.HotKey:
-                cancel = base.OnAccept () == true;
+        case KeyBindingScope.HotKey:
+            cancel = base.OnAccept () == true;
 
-                if (CanFocus)
-                {
-                    SetFocus ();
-                }
+            if (CanFocus)
+            {
+                SetFocus ();
+                cancel = true;
+            }
 
-                break;
+            break;
 
-            default:
-                cancel = base.OnAccept () == true;
-                break;
-        }
+        default:
+            // Mouse
+            cancel = base.OnAccept () == true;
 
-        CommandView.InvokeCommand (Command.Accept);
+            break;
+    }
 
-        if (!cancel)
-        {
-            Action?.Invoke ();
-        }
+    CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
 
-        return cancel;
+    if (Action is { })
+    {
+        Action.Invoke ();
+        // Assume if there's a subscriber to Action, it's handled.
+        cancel = true;
     }
 
-    /// <summary>
-    ///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
-    ///     mouse.
-    /// </summary>
-    /// <remarks>
-    ///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
-    /// </remarks>
-    [CanBeNull]
-    public Action Action { get; set; }
+    return cancel;
+}
 
-    #endregion Accept Handling
+/// <summary>
+///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
+///     mouse.
+/// </summary>
+/// <remarks>
+///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
+/// </remarks>
+[CanBeNull]
+public Action Action { get; set; }
 
-    private bool? OnSelect (CommandContext ctx)
-    {
-        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
-        {
-           return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
-        }
-        return false;
+#endregion Accept Handling
 
+private bool? OnSelect (CommandContext ctx)
+{
+    if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+    {
+        return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
     }
+    return false;
+
+}
 
 
-    #region Focus
+#region Focus
 
-    /// <inheritdoc/>
-    public override ColorScheme ColorScheme
+/// <inheritdoc/>
+public override ColorScheme ColorScheme
+{
+    get => base.ColorScheme;
+    set
     {
-        get => base.ColorScheme;
-        set
-        {
-            base.ColorScheme = value;
-            SetColors ();
-        }
+        base.ColorScheme = value;
+        SetColors ();
     }
+}
 
-    /// <summary>
-    /// </summary>
-    internal void SetColors ()
-    {
-        // Border should match superview.
-        Border.ColorScheme = SuperView?.ColorScheme;
+/// <summary>
+/// </summary>
+internal void SetColors ()
+{
+    // Border should match superview.
+    Border.ColorScheme = SuperView?.ColorScheme;
 
-        if (HasFocus)
-        {
-            // When we have focus, we invert the colors
-            base.ColorScheme = new (base.ColorScheme)
-            {
-                Normal = base.ColorScheme.Focus,
-                HotNormal = base.ColorScheme.HotFocus,
-                HotFocus = base.ColorScheme.HotNormal,
-                Focus = base.ColorScheme.Normal
-            };
-        }
-        else
+    if (HasFocus)
+    {
+        // When we have focus, we invert the colors
+        base.ColorScheme = new (base.ColorScheme)
         {
-            base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
-        }
+            Normal = base.ColorScheme.Focus,
+            HotNormal = base.ColorScheme.HotFocus,
+            HotFocus = base.ColorScheme.HotNormal,
+            Focus = base.ColorScheme.Normal
+        };
+    }
+    else
+    {
+        base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+    }
 
-        // Set KeyView's colors to show "hot"
-        if (IsInitialized && base.ColorScheme is { })
+    // Set KeyView's colors to show "hot"
+    if (IsInitialized && base.ColorScheme is { })
+    {
+        var cs = new ColorScheme (base.ColorScheme)
         {
-            var cs = new ColorScheme (base.ColorScheme)
-            {
-                Normal = base.ColorScheme.HotNormal,
-                HotNormal = base.ColorScheme.Normal
-            };
-            KeyView.ColorScheme = cs;
-        }
+            Normal = base.ColorScheme.HotNormal,
+            HotNormal = base.ColorScheme.Normal
+        };
+        KeyView.ColorScheme = cs;
     }
+}
 
-    View _lastFocusedView;
-    /// <inheritdoc/>
-    public override bool OnEnter (View view)
-    {
-        SetColors ();
-        _lastFocusedView = view;
+View _lastFocusedView;
+/// <inheritdoc/>
+public override bool OnEnter (View view)
+{
+    SetColors ();
+    _lastFocusedView = view;
 
-        return base.OnEnter (view);
-    }
+    return base.OnEnter (view);
+}
 
-    /// <inheritdoc/>
-    public override bool OnLeave (View view)
-    {
-        SetColors ();
-        _lastFocusedView = this;
+/// <inheritdoc/>
+public override bool OnLeave (View view)
+{
+    SetColors ();
+    _lastFocusedView = this;
 
-        return base.OnLeave (view);
-    }
+    return base.OnLeave (view);
+}
 
-    #endregion Focus
+#endregion Focus
 
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
+/// <inheritdoc/>
+protected override void Dispose (bool disposing)
+{
+    if (disposing)
     {
-        if (disposing)
+        if (CommandView?.IsAdded == false)
         {
-            if (CommandView?.IsAdded == false)
-            {
-                CommandView.Dispose ();
-            }
-
-            if (HelpView?.IsAdded == false)
-            {
-                HelpView.Dispose ();
-            }
+            CommandView.Dispose ();
+        }
 
-            if (KeyView?.IsAdded == false)
-            {
-                KeyView.Dispose ();
-            }
+        if (HelpView?.IsAdded == false)
+        {
+            HelpView.Dispose ();
         }
 
-        base.Dispose (disposing);
+        if (KeyView?.IsAdded == false)
+        {
+            KeyView.Dispose ();
+        }
     }
+
+    base.Dispose (disposing);
+}
 }

+ 1 - 0
UICatalog/Scenarios/Bars.cs

@@ -193,6 +193,7 @@ public class Bars : Scenario
                                  {
                                      eventSource.Add ($"Accept: {sh!.SuperView.Id} {sh!.CommandView.Text}");
                                      eventLog.MoveDown ();
+                                     args.Cancel = true;
                                  };
                 }
             }

+ 13 - 2
UICatalog/Scenarios/Shortcuts.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
 using System.Text;
@@ -141,7 +142,7 @@ public class Shortcuts : Scenario
             Width = Dim.Width (vShortcut3),
             CommandView = new Button
             {
-                Title = "B_utton",
+                Title = "_Button",
             },
             HelpText = "Width is Fill",
             Key = Key.K,
@@ -362,11 +363,21 @@ public class Shortcuts : Scenario
                                        eventLog.MoveDown ();
                                        args.Cancel = true;
                                    };
+
+                shortcut.CommandView.Accept += (o, args) =>
+                                               {
+                                                   eventSource.Add ($"CommandView.Accept: {shortcut!.CommandView.Text}");
+                                                   eventLog.MoveDown ();
+                                               };
             }
         }
 
         //((CheckBox)vShortcut5.CommandView).OnToggled ();
     }
 
-    private void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+    private void Button_Clicked (object sender, CancelEventArgs e)
+    {
+        //e.Cancel = true;
+        MessageBox.Query ("Hi", $"You clicked {sender}"); 
+    }
 }

+ 6 - 4
UnitTests/View/ViewTests.cs

@@ -460,14 +460,16 @@ At 0,0
             ColorScheme = Colors.ColorSchemes ["Menu"], X = 0, Y = 0, Text = "This should be the first line."
         };
 
-        var button = new Button
+        var view = new View
         {
             X = 0, // don't overcomplicate unit tests
             Y = 1,
+            Height = Dim.Auto (DimAutoStyle.Text),
+            Width = Dim.Auto(DimAutoStyle.Text),
             Text = "Press me!"
         };
 
-        frame.Add (label, button);
+        frame.Add (label, view);
 
         frame.X = Pos.Center ();
         frame.Y = Pos.Center ();
@@ -486,7 +488,7 @@ At 0,0
 
         label.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 38, 1), label._needsDisplayRect); };
 
-        button.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 13, 1), button._needsDisplayRect); };
+        view.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 13, 1), view._needsDisplayRect); };
 
         Assert.Equal (new (0, 0, 80, 25), top.Frame);
         Assert.Equal (new (20, 8, 40, 8), frame.Frame);
@@ -501,7 +503,7 @@ At 0,0
                                     )
                      );
         Assert.Equal (new (0, 0, 30, 1), label.Frame);
-        Assert.Equal (new (0, 1, 13, 1), button.Frame); // this proves frame was set
+        Assert.Equal (new (0, 1, 9, 1), view.Frame); // this proves frame was set
         Application.End (runState);
         top.Dispose ();
     }

+ 259 - 2
UnitTests/Views/ShortcutTests.cs

@@ -87,7 +87,7 @@ public class ShortcutTests
 
         shortcut = new ();
 
-        shortcut.CommandView = new()
+        shortcut.CommandView = new ()
         {
             Text = "T"
         };
@@ -104,7 +104,7 @@ public class ShortcutTests
 
         Assert.Equal (shortcut.Text, shortcut.HelpText);
 
-        shortcut = new()
+        shortcut = new ()
         {
             HelpText = "H"
         };
@@ -314,4 +314,261 @@ public class ShortcutTests
         Assert.False (shortcut.CanFocus);
         Assert.True (shortcut.CommandView.CanFocus);
     }
+
+    [Theory]
+    //  0123456789
+    // " C  0  A "
+    [InlineData (-1, 0)]
+    [InlineData (0, 1)]
+    [InlineData (1, 1)]
+    [InlineData (2, 1)]
+    [InlineData (3, 1)]
+    [InlineData (4, 1)]
+    [InlineData (5, 1)]
+    [InlineData (6, 1)]
+    [InlineData (7, 1)]
+    [InlineData (8, 1)]
+    [InlineData (9, 0)]
+    [AutoInitShutdown]
+    public void MouseClick_Fires_Accept (int x, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "C"
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnMouseEvent (new MouseEvent ()
+        {
+            Position = new Point (x, 0),
+            Flags = MouseFlags.Button1Clicked,
+        });
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+    }
+
+    [Theory]
+    //  0123456789
+    // " C  0  A "
+    [InlineData (-1, 0, 0)]
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 1)]
+    [InlineData (2, 1, 1)]
+    [InlineData (3, 1, 1)]
+    [InlineData (4, 1, 1)]
+    [InlineData (5, 1, 1)]
+    [InlineData (6, 1, 1)]
+    [InlineData (7, 1, 1)]
+    [InlineData (8, 1, 1)]
+    [InlineData (9, 0, 0)]
+    [AutoInitShutdown]
+    public void MouseClick_Button_CommandView_Fires_Accept (int x, int expectedAccept, int expectedButtonAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+        };
+        shortcut.CommandView = new Button ()
+        {
+            Title = "C",
+            NoDecorations = true,
+            NoPadding = true,
+            CanFocus = false
+        };
+        int buttonAccepted = 0;
+        shortcut.CommandView.Accept += (s, e) =>
+                                       {
+                                           buttonAccepted++;
+                                       };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Assert.True (shortcut.HasFocus);
+
+        Application.OnMouseEvent (new MouseEvent ()
+        {
+            Position = new Point (x, 0),
+            Flags = MouseFlags.Button1Clicked,
+        });
+
+        Assert.Equal (expectedAccept, accepted);
+        Assert.Equal (expectedButtonAccept, buttonAccepted);
+
+        current.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_Invokes_Accept (bool canFocus, KeyCode key, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+
+    }
+
+
+    [Theory]
+    [InlineData (KeyCode.A, 1)]
+    [InlineData (KeyCode.C, 1)]
+    [InlineData (KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (KeyCode.Enter, 1)]
+    [InlineData (KeyCode.Space, 0)]
+    [InlineData (KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            KeyBindingScope = KeyBindingScope.Application,
+            Text = "0",
+            Title = "_C",
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+    }
+
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_Invokes_Action (bool canFocus, KeyCode key, int expectedAction)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int action = 0;
+        shortcut.Action += () => action++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAction, action);
+
+        current.Dispose ();
+
+    }
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int expectedAction)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            KeyBindingScope = KeyBindingScope.Application,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int action = 0;
+        shortcut.Action += () => action++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAction, action);
+
+        current.Dispose ();
+
+    }
+
 }