Browse Source

Merge pull request #3766 from tig/v2_3764-Common-Events

Fixes #3764. Makes common UI interactions consistent across built-in Views
Tig 9 months ago
parent
commit
426b99181f
100 changed files with 2410 additions and 1463 deletions
  1. 2 2
      CommunityToolkitExample/LoginView.cs
  2. 1 1
      Example/Example.cs
  3. 1 1
      NativeAot/Program.cs
  4. 2 2
      ReactiveExample/LoginView.cs
  5. 1 1
      SelfContained/Program.cs
  6. 16 0
      Terminal.Gui/Application/Application.Initialization.cs
  7. 2 2
      Terminal.Gui/Application/Application.Keyboard.cs
  8. 39 21
      Terminal.Gui/Application/Application.Mouse.cs
  9. 1 2
      Terminal.Gui/Application/Application.cs
  10. 13 4
      Terminal.Gui/Configuration/ConfigurationManager.cs
  11. 1 1
      Terminal.Gui/Configuration/ThemeManager.cs
  12. 2 0
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  13. 2 2
      Terminal.Gui/FileServices/DefaultFileOperations.cs
  14. 30 8
      Terminal.Gui/Input/Command.cs
  15. 11 1
      Terminal.Gui/Input/CommandContext.cs
  16. 15 0
      Terminal.Gui/Input/CommandEventArgs.cs
  17. 3 0
      Terminal.Gui/Input/KeyBinding.cs
  18. 20 4
      Terminal.Gui/Input/KeyBindingScope.cs
  19. 17 5
      Terminal.Gui/Input/KeyBindings.cs
  20. 3 3
      Terminal.Gui/View/Adornment/Adornment.cs
  21. 7 3
      Terminal.Gui/View/Adornment/Border.cs
  22. 8 2
      Terminal.Gui/View/Adornment/Margin.cs
  23. 42 0
      Terminal.Gui/View/Layout/DimAuto.cs
  24. 353 0
      Terminal.Gui/View/View.Command.cs
  25. 3 9
      Terminal.Gui/View/View.Drawing.cs
  26. 28 144
      Terminal.Gui/View/View.Keyboard.cs
  27. 24 23
      Terminal.Gui/View/View.Layout.cs
  28. 7 8
      Terminal.Gui/View/View.Mouse.cs
  29. 3 2
      Terminal.Gui/View/View.Navigation.cs
  30. 87 74
      Terminal.Gui/View/View.cs
  31. 56 9
      Terminal.Gui/Views/Bar.cs
  32. 95 35
      Terminal.Gui/Views/Button.cs
  33. 117 63
      Terminal.Gui/Views/CheckBox.cs
  34. 3 3
      Terminal.Gui/Views/ColorPicker.cs
  35. 35 25
      Terminal.Gui/Views/ComboBox.cs
  36. 2 2
      Terminal.Gui/Views/DatePicker.cs
  37. 9 9
      Terminal.Gui/Views/FileDialog.cs
  38. 3 1
      Terminal.Gui/Views/FrameView.cs
  39. 49 11
      Terminal.Gui/Views/HexView.cs
  40. 44 17
      Terminal.Gui/Views/Label.cs
  41. 71 37
      Terminal.Gui/Views/ListView.cs
  42. 5 6
      Terminal.Gui/Views/Menu/Menu.cs
  43. 43 9
      Terminal.Gui/Views/Menu/MenuBar.cs
  44. 26 12
      Terminal.Gui/Views/Menuv2.cs
  45. 18 16
      Terminal.Gui/Views/MessageBox.cs
  46. 12 4
      Terminal.Gui/Views/NumericUpDown.cs
  47. 242 107
      Terminal.Gui/Views/RadioGroup.cs
  48. 210 209
      Terminal.Gui/Views/Shortcut.cs
  49. 15 7
      Terminal.Gui/Views/Slider.cs
  50. 4 4
      Terminal.Gui/Views/StatusBar.cs
  51. 19 18
      Terminal.Gui/Views/TableView/TableView.cs
  52. 94 114
      Terminal.Gui/Views/TextField.cs
  53. 68 58
      Terminal.Gui/Views/TextView.cs
  54. 16 15
      Terminal.Gui/Views/Toplevel.cs
  55. 5 3
      Terminal.Gui/Views/TreeView/TreeView.cs
  56. 0 21
      Terminal.Gui/Views/Window.cs
  57. 2 2
      Terminal.Gui/Views/Wizard/Wizard.cs
  58. 5 5
      UICatalog/KeyBindingsDialog.cs
  59. 0 1
      UICatalog/Scenario.cs
  60. 5 3
      UICatalog/Scenarios/ASCIICustomButton.cs
  61. 1 1
      UICatalog/Scenarios/AdornmentEditor.cs
  62. 4 4
      UICatalog/Scenarios/Adornments.cs
  63. 55 51
      UICatalog/Scenarios/AllViewsTester.cs
  64. 7 7
      UICatalog/Scenarios/Bars.cs
  65. 65 21
      UICatalog/Scenarios/Buttons.cs
  66. 10 11
      UICatalog/Scenarios/CharacterMap.cs
  67. 1 1
      UICatalog/Scenarios/ChineseUI.cs
  68. 6 5
      UICatalog/Scenarios/ClassExplorer.cs
  69. 1 1
      UICatalog/Scenarios/Clipping.cs
  70. 0 2
      UICatalog/Scenarios/CombiningMarks.cs
  71. 2 2
      UICatalog/Scenarios/ComboBoxIteration.cs
  72. 4 4
      UICatalog/Scenarios/ComputedLayout.cs
  73. 1 1
      UICatalog/Scenarios/ConfigurationEditor.cs
  74. 1 1
      UICatalog/Scenarios/ContentScrolling.cs
  75. 3 12
      UICatalog/Scenarios/ContextMenus.cs
  76. 2 2
      UICatalog/Scenarios/CsvEditor.cs
  77. 21 12
      UICatalog/Scenarios/Dialogs.cs
  78. 1 1
      UICatalog/Scenarios/DimAutoDemo.cs
  79. 21 21
      UICatalog/Scenarios/DynamicMenuBar.cs
  80. 22 22
      UICatalog/Scenarios/DynamicStatusBar.cs
  81. 5 5
      UICatalog/Scenarios/Editor.cs
  82. 36 35
      UICatalog/Scenarios/FileDialogExamples.cs
  83. 2 2
      UICatalog/Scenarios/Generic.cs
  84. 2 2
      UICatalog/Scenarios/GraphViewExample.cs
  85. 1 1
      UICatalog/Scenarios/Images.cs
  86. 2 2
      UICatalog/Scenarios/InteractiveTree.cs
  87. 1 1
      UICatalog/Scenarios/InvertColors.cs
  88. 5 5
      UICatalog/Scenarios/LineDrawing.cs
  89. 6 23
      UICatalog/Scenarios/LineViewExample.cs
  90. 5 7
      UICatalog/Scenarios/ListColumns.cs
  91. 51 13
      UICatalog/Scenarios/ListViewWithSelection.cs
  92. 2 2
      UICatalog/Scenarios/ListsAndCombos.cs
  93. 5 3
      UICatalog/Scenarios/Localization.cs
  94. 3 3
      UICatalog/Scenarios/MenuBarScenario.cs
  95. 26 12
      UICatalog/Scenarios/MessageBoxes.cs
  96. 1 1
      UICatalog/Scenarios/Mouse.cs
  97. 2 2
      UICatalog/Scenarios/MultiColouredTable.cs
  98. 3 3
      UICatalog/Scenarios/NumericUpDownDemo.cs
  99. 4 4
      UICatalog/Scenarios/Progress.cs
  100. 3 4
      UICatalog/Scenarios/ProgressBarStyles.cs

+ 2 - 2
CommunityToolkitExample/LoginView.cs

@@ -19,13 +19,13 @@ internal partial class LoginView : IRecipient<Message<LoginActions>>
                                      {
                                          ViewModel.Password = passwordInput.Text;
                                      };
-        loginButton.Accept += (_, _) =>
+        loginButton.Accepting += (_, _) =>
                               {
                                   if (!ViewModel.CanLogin) { return; }
                                   ViewModel.LoginCommand.Execute (null);
                               };
 
-        clearButton.Accept += (_, _) =>
+        clearButton.Accepting += (_, _) =>
                               {
                                   ViewModel.ClearCommand.Execute (null);
                               };

+ 1 - 1
Example/Example.cs

@@ -63,7 +63,7 @@ public class ExampleWindow : Window
         };
 
         // When login button is clicked display a message popup
-        btnLogin.Accept += (s, e) =>
+        btnLogin.Accepting += (s, e) =>
                            {
                                if (userNameText.Text == "admin" && passwordText.Text == "password")
                                {

+ 1 - 1
NativeAot/Program.cs

@@ -93,7 +93,7 @@ public class ExampleWindow : Window
         };
 
         // When login button is clicked display a message popup
-        btnLogin.Accept += (s, e) =>
+        btnLogin.Accepting += (s, e) =>
         {
             if (userNameText.Text == "admin" && passwordText.Text == "password")
             {

+ 2 - 2
ReactiveExample/LoginView.cs

@@ -108,7 +108,7 @@ public class LoginView : Window, IViewFor<LoginViewModel>
 
                 login
                     .Events ()
-                    .Accept
+                    .Accepting
                     .InvokeCommand (ViewModel, x => x.Login)
                     .DisposeWith (_disposable);
             })
@@ -120,7 +120,7 @@ public class LoginView : Window, IViewFor<LoginViewModel>
 
                 clear
                     .Events ()
-                    .Accept
+                    .Accepting
                     .InvokeCommand (ViewModel, x => x.ClearCommand)
                     .DisposeWith (_disposable);
             })

+ 1 - 1
SelfContained/Program.cs

@@ -92,7 +92,7 @@ public class ExampleWindow : Window
         };
 
         // When login button is clicked display a message popup
-        btnLogin.Accept += (s, e) =>
+        btnLogin.Accepting += (s, e) =>
                            {
                                if (userNameText.Text == "admin" && passwordText.Text == "password")
                                {

+ 16 - 0
Terminal.Gui/Application/Application.Initialization.cs

@@ -1,4 +1,5 @@
 #nullable enable
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 
@@ -80,6 +81,16 @@ public static partial class Application // Initialization (Init/Shutdown)
         if (driver is { })
         {
             Driver = driver;
+
+            if (driver is FakeDriver)
+            {
+                // We're running unit tests. Disable loading config files other than default
+                if (Locations == ConfigLocations.All)
+                {
+                    Locations = ConfigLocations.DefaultOnly;
+                    Reset ();
+                }
+            }
         }
 
         // Start the process of configuration management.
@@ -88,7 +99,12 @@ public static partial class Application // Initialization (Init/Shutdown)
         // valid after a Driver is loaded. In this case we need just
         // `Settings` so we can determine which driver to use.
         // Don't reset, so we can inherit the theme from the previous run.
+        string previousTheme = Themes?.Theme ?? string.Empty;
         Load ();
+        if (Themes is { } && !string.IsNullOrEmpty (previousTheme) && previousTheme != "Default")
+        {
+            ThemeManager.SelectedTheme = previousTheme;
+        }
         Apply ();
 
         AddApplicationKeyBindings ();

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

@@ -169,7 +169,7 @@ public static partial class Application // Keyboard handling
                                             );
         }
 
-        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
+        if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
         {
             var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
 
@@ -418,7 +418,7 @@ public static partial class Application // Keyboard handling
     /// <summary>
     ///     Commands for Application.
     /// </summary>
-    private static Dictionary<Command, Func<CommandContext, bool?>>? CommandImplementations { get; set; }
+    private static Dictionary<Command, View.CommandImplementation>? CommandImplementations { get; set; }
 
     private static void ReplaceKey (Key oldKey, Key newKey)
     {

+ 39 - 21
Terminal.Gui/Application/Application.Mouse.cs

@@ -6,6 +6,13 @@ namespace Terminal.Gui;
 
 public static partial class Application // Mouse handling
 {
+    internal static Point? _lastMousePosition;
+
+    /// <summary>
+    ///     Gets the most recent position of the mouse.
+    /// </summary>
+    public static Point? GetLastMousePosition () { return _lastMousePosition; }
+
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     public static bool IsMouseDisabled { get; set; }
@@ -132,12 +139,18 @@ public static partial class Application // Mouse handling
     /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
     internal static void OnMouseEvent (MouseEvent mouseEvent)
     {
+        _lastMousePosition = mouseEvent.ScreenPosition;
+
         if (IsMouseDisabled)
         {
             return;
         }
 
-        List<View?> currentViewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.Position);
+        // The position of the mouse is the same as the screen position at the application level.
+        //Debug.Assert (mouseEvent.Position == mouseEvent.ScreenPosition);
+        mouseEvent.Position = mouseEvent.ScreenPosition;
+
+        List<View?> currentViewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.ScreenPosition);
 
         View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault ();
 
@@ -159,7 +172,7 @@ public static partial class Application // Mouse handling
             return;
         }
 
-        if (GrabMouse (deepestViewUnderMouse, mouseEvent))
+        if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
         {
             return;
         }
@@ -180,44 +193,47 @@ public static partial class Application // Mouse handling
             return;
         }
 
-        // TODO: Move this after call to RaiseMouseEnterLeaveEvents once MouseEnter/Leave don't use MouseEvent anymore.
-        MouseEvent? me;
+        // Create a view-relative mouse event to send to the view that is under the mouse.
+        MouseEvent? viewMouseEvent;
 
         if (deepestViewUnderMouse is Adornment adornment)
         {
-            Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
+            Point frameLoc = adornment.ScreenToFrame (mouseEvent.ScreenPosition);
 
-            me = new ()
+            viewMouseEvent = new ()
             {
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
+                ScreenPosition = mouseEvent.ScreenPosition,
                 View = deepestViewUnderMouse
             };
         }
         else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.Position))
         {
-            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.Position);
+            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
 
-            me = new ()
+            viewMouseEvent = new ()
             {
                 Position = viewportLocation,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
+                ScreenPosition = mouseEvent.ScreenPosition,
                 View = deepestViewUnderMouse
             };
         }
         else
         {
-            Debug.Fail ("This should never happen");
+            // The mouse was outside any View's Viewport.
+
+           // Debug.Fail ("This should never happen. If it does please file an Issue!!");
+
             return;
         }
 
-        RaiseMouseEnterLeaveEvents (me.ScreenPosition, currentViewsUnderMouse);
+        RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse);
 
         WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
 
-        while (deepestViewUnderMouse.NewMouseEvent (me) is not true && MouseGrabView is not { })
+        while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabView is not { })
         {
             if (deepestViewUnderMouse is Adornment adornmentView)
             {
@@ -233,38 +249,38 @@ public static partial class Application // Mouse handling
                 break;
             }
 
-            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.Position);
+            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
 
-            me = new ()
+            viewMouseEvent = new ()
             {
                 Position = boundsPoint,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
+                ScreenPosition = mouseEvent.ScreenPosition,
                 View = deepestViewUnderMouse
             };
         }
     }
 
-    internal static bool GrabMouse (View? deepestViewUnderMouse, MouseEvent mouseEvent)
+    internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEvent mouseEvent)
     {
         if (MouseGrabView is { })
         {
-
 #if DEBUG_IDISPOSABLE
             if (MouseGrabView.WasDisposed)
             {
                 throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
             }
 #endif
+
             // If the mouse is grabbed, send the event to the view that grabbed it.
             // The coordinates are relative to the Bounds of the view that grabbed the mouse.
-            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
+            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
 
             var viewRelativeMouseEvent = new MouseEvent
             {
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
+                ScreenPosition = mouseEvent.ScreenPosition,
                 View = deepestViewUnderMouse ?? MouseGrabView
             };
 
@@ -297,6 +313,7 @@ public static partial class Application // Mouse handling
     {
         // Tell any views that are no longer under the mouse that the mouse has left
         List<View?> viewsToLeave = _cachedViewsUnderMouse.Where (v => v is { } && !currentViewsUnderMouse.Contains (v)).ToList ();
+
         foreach (View? view in viewsToLeave)
         {
             if (view is null)
@@ -322,7 +339,8 @@ public static partial class Application // Mouse handling
             }
 
             _cachedViewsUnderMouse.Add (view);
-            bool raise = false;
+            var raise = false;
+
             if (view is Adornment { Parent: { } } adornmentView)
             {
                 Point superViewLoc = adornmentView.Parent.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;

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

@@ -197,6 +197,7 @@ public static partial class Application
         IsInitialized = false;
 
         // Mouse
+        _lastMousePosition = null;
         _cachedViewsUnderMouse.Clear ();
         WantContinuousButtonPressedView = null;
         MouseEvent = null;
@@ -214,8 +215,6 @@ public static partial class Application
 
         AddApplicationKeyBindings ();
 
-        Colors.Reset ();
-
         // Reset synchronization context to allow the user to run async/await,
         // as the main loop has been ended, the synchronization context from
         // gui.cs does no longer process any callbacks. See #1084 for more details:

+ 13 - 4
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -197,10 +197,19 @@ public static class ConfigurationManager
 
         try
         {
-            settings = Settings?.Apply () ?? false;
-
-            themes = !string.IsNullOrEmpty (ThemeManager.SelectedTheme)
-                     && (ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false);
+            if (string.IsNullOrEmpty (ThemeManager.SelectedTheme))
+            {
+                // First start. Apply settings first. This ensures if a config sets Theme to something other than "Default", it gets used
+                settings = Settings?.Apply () ?? false;
+                themes = !string.IsNullOrEmpty (ThemeManager.SelectedTheme)
+                         && (ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false);
+            }
+            else
+            {
+                // Subsequently. Apply Themes first using whatever the SelectedTheme is
+                themes = ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false;
+                settings = Settings?.Apply () ?? false;
+            }
             appSettings = AppSettings?.Apply () ?? false;
         }
         catch (JsonException e)

+ 1 - 1
Terminal.Gui/Configuration/ThemeManager.cs

@@ -110,7 +110,7 @@ public class ThemeManager : IDictionary<string, ThemeScope>
             string oldTheme = _theme;
             _theme = value;
 
-            if (oldTheme != _theme && Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes && themes.ContainsKey (_theme))
+            if ((oldTheme != _theme || oldTheme != Settings! ["Theme"].PropertyValue as string) && Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes && themes.ContainsKey (_theme))
             {
                 Settings! ["Theme"].PropertyValue = _theme;
                 Instance.OnThemeChanged (oldTheme);

+ 2 - 0
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -54,6 +54,8 @@ internal class WindowsConsole
 
     public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord bufferSize, SmallRect window, bool force16Colors)
     {
+        //Debug.WriteLine ("WriteToConsole");
+
         if (_screenBuffer == nint.Zero)
         {
             ReadFromConsoleOutput (size, bufferSize, ref window);

+ 2 - 2
Terminal.Gui/FileServices/DefaultFileOperations.cs

@@ -135,14 +135,14 @@ public class DefaultFileOperations : IFileOperations
         var confirm = false;
         var btnOk = new Button { IsDefault = true, Text = Strings.btnOk };
 
-        btnOk.Accept += (s, e) =>
+        btnOk.Accepting += (s, e) =>
                          {
                              confirm = true;
                              Application.RequestStop ();
                          };
         var btnCancel = new Button { Text = Strings.btnCancel };
 
-        btnCancel.Accept += (s, e) =>
+        btnCancel.Accepting += (s, e) =>
                              {
                                  confirm = false;
                                  Application.RequestStop ();

+ 30 - 8
Terminal.Gui/Input/Command.cs

@@ -3,18 +3,40 @@
 
 namespace Terminal.Gui;
 
-/// <summary>Actions which can be performed by the application or bound to keys in a <see cref="View"/> control.</summary>
+/// <summary>
+///     Actions which can be performed by a <see cref="View"/>. Commands are typically invoked via
+///     <see cref="View.KeyBindings"/> and mouse events.
+/// </summary>
 public enum Command
 {
-    #region Default View Commands
+    #region Base View Commands
 
-    /// <summary>Invoked when the HotKey for the View has been pressed.</summary>
-    HotKey,
-
-    /// <summary>Accepts the current state (e.g. list selection, button press, toggle, etc.).</summary>
+    /// <summary>
+    ///     Accepts the current state of the View (e.g. list selection, button press, checkbox state, etc.).
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.RaiseAccepting"/>. If the event is not handled,
+    ///         the command is invoked on:
+    ///             - Any peer-view that is a <see cref="Button"/> with <see cref="Button.IsDefault"/> set to <see langword="true"/>.
+    ///             - The <see cref="View.SuperView"/>. This enables default Accept behavior.
+    ///     </para>
+    /// </summary>
     Accept,
 
-    /// <summary>Selects an item (e.g. a list item or menu item) without necessarily accepting it.</summary>
+    /// <summary>
+    ///     Performs a hot key action (e.g. setting focus, accepting, and/or moving focus to the next View).
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.SetFocus"/> and then
+    ///         <see cref="View.RaiseHandlingHotKey"/>.
+    ///     </para>
+    /// </summary>
+    HotKey,
+
+    /// <summary>
+    ///     Selects the View or an item in the View (e.g. a list item or menu item) without necessarily accepting it.
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.RaiseSelecting"/>.
+    ///     </para>
+    /// </summary>
     Select,
 
     #endregion
@@ -290,4 +312,4 @@ public enum Command
     Edit,
 
     #endregion
-}
+}

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

@@ -11,6 +11,9 @@ namespace Terminal.Gui;
 ///         use <see cref="View.AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>.
 ///     </para>
 /// </remarks>
+/// <seealso cref="Application.KeyBindings"/>
+/// <seealso cref="View.KeyBindings"/>
+/// <seealso cref="Command"/>
 #pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
 public record struct CommandContext
 {
@@ -20,11 +23,13 @@ public record struct CommandContext
     /// <param name="command"></param>
     /// <param name="key"></param>
     /// <param name="keyBinding"></param>
-    public CommandContext (Command command, Key? key, KeyBinding? keyBinding = null)
+    /// <param name="data"></param>
+    public CommandContext (Command command, Key? key, KeyBinding? keyBinding = null, object? data = null)
     {
         Command = command;
         Key = key;
         KeyBinding = keyBinding;
+        Data = data;
     }
 
     /// <summary>
@@ -41,4 +46,9 @@ public record struct CommandContext
     /// The KeyBinding that was used to invoke the <see cref="Command"/>, if any.
     /// </summary>
     public KeyBinding? KeyBinding { get; set; }
+
+    /// <summary>
+    ///     Arbitrary data.
+    /// </summary>
+    public object? Data { get; set; }
 }

+ 15 - 0
Terminal.Gui/Input/CommandEventArgs.cs

@@ -0,0 +1,15 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Event arguments for <see cref="Command"/> events.
+/// </summary>
+public class CommandEventArgs : CancelEventArgs
+{
+    /// <summary>
+    ///     The context for the command.
+    /// </summary>
+    public CommandContext Context { get; init; }
+}

+ 3 - 0
Terminal.Gui/Input/KeyBinding.cs

@@ -8,6 +8,9 @@ namespace Terminal.Gui;
 /// <summary>
 /// Provides a collection of <see cref="Command"/> objects that are scoped to <see cref="KeyBindingScope"/>.
 /// </summary>
+/// <seealso cref="Application.KeyBindings"/>
+/// <seealso cref="View.KeyBindings"/>
+/// <seealso cref="Command"/>
 public record struct KeyBinding
 {
     /// <summary>Initializes a new instance.</summary>

+ 20 - 4
Terminal.Gui/Input/KeyBindingScope.cs

@@ -7,19 +7,28 @@
 /// <remarks>
 ///     <para>Key bindings are scoped to the most-focused view (<see cref="Focused"/>) by default.</para>
 /// </remarks>
+/// <seealso cref="Application.KeyBindings"/>
+/// <seealso cref="View.KeyBindings"/>
+/// <seealso cref="Command"/>
 [Flags]
 public enum KeyBindingScope
 {
     /// <summary>The key binding is disabled.</summary>
     Disabled = 0,
 
-    /// <summary>The key binding is scoped to just the view that has focus.</summary>
+    /// <summary>
+    ///     The key binding is scoped to just the view that has focus.
+    ///     <para>
+    ///     </para>
+    /// </summary>
+    /// <seealso cref="View.KeyBindings"/>
     Focused = 1,
 
     /// <summary>
-    ///     The key binding is scoped to the View's Superview hierarchy and will be triggered even when the View does not have
+    ///     The key binding is scoped to the View's Superview hierarchy and the bound <see cref="Command"/>s will be invoked
+    ///     even when the View does not have
     ///     focus, as
-    ///     long as the SuperView does have focus. This is typically used for <see cref="View.HotKey"/>s.
+    ///     long as some View up the SuperView hierachy does have focus. This is typically used for <see cref="View.HotKey"/>s.
     ///     <para>
     ///         The View must be visible.
     ///     </para>
@@ -28,15 +37,22 @@ public enum KeyBindingScope
     ///         any of its subviews.
     ///     </para>
     /// </summary>
+    /// <seealso cref="View.KeyBindings"/>
+    /// <seeals cref="View.HotKey"/>
     HotKey = 2,
 
     /// <summary>
-    ///     The key binding will be triggered regardless of which view has focus. This is typically used for global
+    ///     The and the bound <see cref="Command"/>s will be invoked regardless of which View has focus. This is typically used
+    ///     for global
     ///     commands, which are called Shortcuts.
     ///     <para>
+    ///         The View does not need to be visible.
+    ///     </para>
+    ///     <para>
     ///         Application-scoped key bindings are only invoked if the key down event was not handled by the focused view or
     ///         any of its subviews, and if the key was not bound to a <see cref="View.HotKey"/>.
     ///     </para>
     /// </summary>
+    /// <seealso cref="Application.KeyBindings"/>
     Application = 4
 }

+ 17 - 5
Terminal.Gui/Input/KeyBindings.cs

@@ -5,6 +5,9 @@ namespace Terminal.Gui;
 /// <summary>
 ///     Provides a collection of <see cref="KeyBinding"/> objects bound to a <see cref="Key"/>.
 /// </summary>
+/// <seealso cref="Application.KeyBindings"/>
+/// <seealso cref="View.KeyBindings"/>
+/// <seealso cref="Command"/>
 public class KeyBindings
 {
     /// <summary>
@@ -284,12 +287,21 @@ public class KeyBindings
         return Array.Empty<Command> ();
     }
 
-    /// <summary>Gets the Key used by a set of commands.</summary>
-    /// <remarks></remarks>
+    /// <summary>Gets the first Key bound to the set of commands specified by <paramref name="commands"/>.</summary>
     /// <param name="commands">The set of commands to search.</param>
-    /// <returns>The <see cref="Key"/> used by a <see cref="Command"/></returns>
-    /// <exception cref="InvalidOperationException">If no matching set of commands was found.</exception>
-    public Key GetKeyFromCommands (params Command [] commands) { return Bindings.First (a => a.Value.Commands.SequenceEqual (commands)).Key; }
+    /// <returns>The first <see cref="Key"/> bound to the set of commands specified by <paramref name="commands"/>. <see langword="null"/> if the set of caommands was not found.</returns>
+    public Key? GetKeyFromCommands (params Command [] commands)
+    {
+        return Bindings.FirstOrDefault (a => a.Value.Commands.SequenceEqual (commands)).Key;
+    }
+
+    /// <summary>Gets Keys bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <param name="commands">The set of commands to search.</param>
+    /// <returns>The <see cref="Key"/>s 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<Key> GetKeysFromCommands (params Command [] commands)
+    {
+        return Bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key);
+    }
 
     /// <summary>Removes a <see cref="KeyBinding"/> from the collection.</summary>
     /// <param name="key"></param>

+ 3 - 3
Terminal.Gui/View/Adornment/Adornment.cs

@@ -223,10 +223,10 @@ public class Adornment : View
             return false;
         }
 
-        Rectangle frame = Frame;
-        frame.Offset (Parent.Frame.Location);
+        Rectangle outside = Frame;
+        outside.Offset (Parent.Frame.Location);
 
-        return Thickness.Contains (frame, location);
+        return Thickness.Contains (outside, location);
     }
 
     ///// <inheritdoc/>

+ 7 - 3
Terminal.Gui/View/Adornment/Border.cs

@@ -270,7 +270,10 @@ public class Border : Adornment
         }
 
         // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
-        if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
+        if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
+                                    // HACK: Prevents Window from being draggable if it's Top
+                                    //&& Parent is Toplevel { Modal: true }
+                                    )
         {
             Parent!.SetFocus ();
 
@@ -464,8 +467,9 @@ public class Border : Adornment
                                                            parentLoc.X - _startGrabPoint.X,
                                                            parentLoc.Y - _startGrabPoint.Y,
                                                            out int nx,
-                                                           out int ny,
-                                                           out _
+                                                           out int ny
+                                                          //,
+                                                          // out _
                                                           );
 
                         Parent.X = parentLoc.X - _startGrabPoint.X;

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

@@ -18,7 +18,8 @@ public class Margin : Adornment
     {
         /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
 
-        HighlightStyle |= HighlightStyle.Pressed;
+        // BUGBUG: We should not set HighlightStyle.Pressed here, but wherever it is actually needed
+       // HighlightStyle |= HighlightStyle.Pressed;
         Highlight += Margin_Highlight;
         LayoutStarted += Margin_LayoutStarted;
 
@@ -90,17 +91,22 @@ public class Margin : Adornment
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle viewport)
     {
+        if (!NeedsDisplay)
+        {
+            return;
+        }
+
         Rectangle screen = ViewportToScreen (viewport);
         Attribute normalAttr = GetNormalColor ();
 
         Driver?.SetAttribute (normalAttr);
 
-        // This just draws/clears the thickness, not the insides.
         if (ShadowStyle != ShadowStyle.None)
         {
             screen = Rectangle.Inflate (screen, -1, -1);
         }
 
+        // This just draws/clears the thickness, not the insides.
         Thickness.Draw (screen, ToString ());
 
         if (Subviews.Count > 0)

+ 42 - 0
Terminal.Gui/View/Layout/DimAuto.cs

@@ -408,6 +408,48 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 }
 
                 #endregion DimView
+
+                #region DimAuto
+                // [ ] DimAuto      - Dimension is internally calculated
+
+                List<View> dimAutoSubViews;
+
+                if (dimension == Dimension.Width && us.GetType ().Name == "Bar" && us.Subviews.Count == 3)
+                {
+
+                }
+
+                if (dimension == Dimension.Width)
+                {
+                    dimAutoSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has<DimAuto> (out _)).ToList ();
+                }
+                else
+                {
+                    dimAutoSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has<DimAuto> (out _)).ToList ();
+                }
+
+                for (var i = 0; i < dimAutoSubViews.Count; i++)
+                {
+                    View v = dimAutoSubViews [i];
+
+                    if (dimension == Dimension.Width)
+                    {
+                        v.SetRelativeLayout (new (maxCalculatedSize, 0));
+                    }
+                    else
+                    {
+                        v.SetRelativeLayout (new (0, maxCalculatedSize));
+                    }
+
+                    int maxDimAuto= dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+
+                    if (maxDimAuto > maxCalculatedSize)
+                    {
+                        maxCalculatedSize = maxDimAuto;
+                    }
+                }
+
+                #endregion
             }
         }
 

+ 353 - 0
Terminal.Gui/View/View.Command.cs

@@ -0,0 +1,353 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui;
+
+public partial class View // Command APIs
+{
+    #region Default Implementation
+
+    /// <summary>
+    ///     Helper to configure all things Command related for a View. Called from the View constructor.
+    /// </summary>
+    private void SetupCommands ()
+    {
+        // Enter - Raise Accepted
+        AddCommand (Command.Accept, RaiseAccepting);
+
+        // HotKey - SetFocus and raise HandlingHotKey
+        AddCommand (Command.HotKey,
+                    () =>
+                    {
+                        if (RaiseHandlingHotKey () is true)
+                        {
+                            return true;
+                        }
+
+                        SetFocus ();
+
+                        return true;
+                    });
+
+        // Space or single-click - Raise Selecting
+        AddCommand (Command.Select, (ctx) =>
+                                    {
+                                        if (RaiseSelecting (ctx) is true)
+                                        {
+                                            return true;
+                                        }
+
+                                        if (CanFocus)
+                                        {
+                                            SetFocus ();
+
+                                            return true;
+                                        }
+
+                                        return false;
+                                    });
+    }
+
+    /// <summary>
+    ///     Called when the user is accepting the state of the View and the <see cref="Command.Accept"/> has been invoked. Calls <see cref="OnAccepting"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.
+    ///     event. The default <see cref="Command.Accept"/> handler calls this method.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    ///     The <see cref="Accepting"/> event should 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.
+    /// </para>
+    /// <para>
+    ///    If a peer-View raises the Accepting event and the event is not cancelled, the <see cref="Command.Accept"/> will be invoked on the
+    ///    first Button in the SuperView that has <see cref="Button.IsDefault"/> set to <see langword="true"/>.
+    /// </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.
+    /// </returns>
+    protected bool? RaiseAccepting (CommandContext ctx)
+    {
+        CommandEventArgs args = new () { Context = ctx };
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        args.Cancel = OnAccepting (args) || args.Cancel;
+
+        if (!args.Cancel)
+        {
+            // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+            Accepting?.Invoke (this, args);
+        }
+
+        // Accept is a special case where if the event is not canceled, the event is
+        //  - Invoked on any peer-View with IsDefault == true
+        //  - bubbled up the SuperView hierarchy.
+        if (!args.Cancel)
+        {
+            // If there's an IsDefault peer view in Subviews, try it
+            var isDefaultView = SuperView?.Subviews.FirstOrDefault (v => v is Button { IsDefault: true });
+
+            if (isDefaultView != this && isDefaultView is Button { IsDefault: true } button)
+            {
+                bool? handled = isDefaultView.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this));
+                if (handled == true)
+                {
+                    return true;
+                }
+            }
+
+            return SuperView?.InvokeCommand (Command.Accept, ctx: new (Command.Accept, null, null, this)) == true;
+        }
+
+        return Accepting is null ? null : args.Cancel;
+    }
+
+    /// <summary>
+    ///     Called when the user is accepting the state of the View and the <see cref="Command.Accept"/> has been invoked. Set CommandEventArgs.Cancel to
+    ///     <see langword="true"/> and return <see langword="true"/> to stop processing.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    ///    See <see cref="View.RaiseAccepting"/> for more information.
+    /// </para>
+    /// </remarks>
+    /// <param name="args"></param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnAccepting (CommandEventArgs args) { return false; }
+
+    /// <summary>
+    ///     Cancelable event raised when the user is accepting the state of the View and the <see cref="Command.Accept"/> has been invoked. Set
+    ///     CommandEventArgs.Cancel to cancel the event.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    ///    See <see cref="View.RaiseAccepting"/> for more information.
+    /// </para>
+    /// </remarks>
+    public event EventHandler<CommandEventArgs>? Accepting;
+
+    /// <summary>
+    ///     Called when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state. Calls <see cref="OnSelecting"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.
+    ///     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"/>.
+    /// </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.
+    /// </returns>
+    protected bool? RaiseSelecting (CommandContext ctx)
+    {
+        CommandEventArgs args = new () { Context = ctx };
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        if (OnSelecting (args) || args.Cancel)
+        {
+            return true;
+        }
+
+        // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+        Selecting?.Invoke (this, args);
+
+        return Selecting is null ? null : args.Cancel;
+    }
+
+    /// <summary>
+    ///     Called when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state.
+    ///     Set CommandEventArgs.Cancel to
+    ///     <see langword="true"/> and return <see langword="true"/> to cancel the state change. The default implementation does nothing.
+    /// </summary>
+    /// <param name="args">The event arguments.</param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnSelecting (CommandEventArgs args) { return false; }
+
+    /// <summary>
+    ///     Cancelable event raised when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state.
+    ///     CommandEventArgs.Cancel to <see langword="true"/> to cancel the state change.
+    /// </summary>
+    public event EventHandler<CommandEventArgs>? Selecting;
+
+    /// <summary>
+    ///     Called when the View is handling the user pressing the View's <see cref="HotKey"/>s. Calls <see cref="OnHandlingHotKey"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.
+    ///     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.
+    /// </returns>
+    protected bool? RaiseHandlingHotKey ()
+    {
+        CommandEventArgs args = new ();
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        if (OnHandlingHotKey (args) || args.Cancel)
+        {
+            return true;
+        }
+
+        // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+        HandlingHotKey?.Invoke (this, args);
+
+        return HandlingHotKey is null ? null : args.Cancel;
+    }
+
+    /// <summary>
+    ///     Called when the View is handling the user pressing the View's <see cref="HotKey"/>. Set CommandEventArgs.Cancel to
+    ///     <see langword="true"/> to stop processing.
+    /// </summary>
+    /// <param name="args"></param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnHandlingHotKey (CommandEventArgs args) { return false; }
+
+    /// <summary>
+    ///     Cancelable event raised when the View is handling the user pressing the View's <see cref="HotKey"/>. Set
+    ///     CommandEventArgs.Cancel to cancel the event.
+    /// </summary>
+    public event EventHandler<CommandEventArgs>? HandlingHotKey;
+
+    #endregion Default Implementation
+
+    /// <summary>
+    /// Function signature commands.
+    /// </summary>
+    /// <param name="ctx">Provides information about the circumstances of invoking the command (e.g. <see cref="CommandContext.Key"/>)</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.
+    /// </returns>
+    public delegate bool? CommandImplementation (CommandContext ctx);
+
+    /// <summary>
+    ///     <para>
+    ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
+    ///         AddCommand for each command they support.
+    ///     </para>
+    ///     <para>
+    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="impl"/> will
+    ///         replace the old one.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This version of AddCommand is for commands that require <see cref="CommandContext"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="command">The command.</param>
+    /// <param name="impl">The delegate.</param>
+    protected void AddCommand (Command command, CommandImplementation impl) { CommandImplementations [command] = impl; }
+
+    /// <summary>
+    ///     <para>
+    ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
+    ///         AddCommand for each command they support.
+    ///     </para>
+    ///     <para>
+    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="impl"/> will
+    ///         replace the old one.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///         If the command requires context, use
+    ///         <see cref="AddCommand(Command,CommandImplementation)"/>
+    ///     </para>
+    /// </remarks>
+    /// <param name="command">The command.</param>
+    /// <param name="impl">The delegate.</param>
+    protected void AddCommand (Command command, Func<bool?> impl) { CommandImplementations [command] = ctx => impl (); }
+
+    /// <summary>Returns all commands that are supported by this <see cref="View"/>.</summary>
+    /// <returns></returns>
+    public IEnumerable<Command> GetSupportedCommands () { return CommandImplementations.Keys; }
+
+    /// <summary>
+    ///     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>
+    /// <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.
+    /// </returns>
+    public bool? InvokeCommands (Command [] commands, Key? key = null, KeyBinding? keyBinding = null)
+    {
+        bool? toReturn = null;
+
+        foreach (Command command in commands)
+        {
+            if (!CommandImplementations.ContainsKey (command))
+            {
+                throw new NotSupportedException (
+                                                 @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by this View ({GetType ().Name})"
+                                                );
+            }
+
+            // each command has its own return value
+            bool? thisReturn = InvokeCommand (command, key, keyBinding);
+
+            // if we haven't got anything yet, the current command result should be used
+            toReturn ??= thisReturn;
+
+            // if ever see a true then that's what we will return
+            if (thisReturn ?? false)
+            {
+                toReturn = true;
+            }
+        }
+
+        return toReturn;
+    }
+
+    /// <summary>Invokes the specified command.</summary>
+    /// <param name="command">The command 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>
+    /// <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.
+    /// </returns>
+    public bool? InvokeCommand (Command command, Key? key = null, KeyBinding? keyBinding = null)
+    {
+        if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation))
+        {
+            var context = new CommandContext (command, key, keyBinding); // Create the context here
+            return implementation (context);
+        }
+
+        return null;
+    }
+
+    /// <summary>
+    /// Invokes the specified command.
+    /// </summary>
+    /// <param name="command">The command to invoke.</param>
+    /// <param name="ctx">Context to pass with the invocation.</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.
+    /// </returns>
+    public bool? InvokeCommand (Command command, CommandContext ctx)
+    {
+        if (CommandImplementations.TryGetValue (command, out CommandImplementation? implementation))
+        {
+            return implementation (ctx);
+        }
+
+        return null;
+    }
+}

+ 3 - 9
Terminal.Gui/View/View.Drawing.cs

@@ -344,8 +344,8 @@ public partial class View // Drawing APIs
         {
             DrawHotString (
                            text,
-                           Enabled ? GetHotNormalColor () : ColorScheme.Disabled,
-                           Enabled ? GetNormalColor () : ColorScheme.Disabled
+                           Enabled ? GetHotNormalColor () : ColorScheme!.Disabled,
+                           Enabled ? GetNormalColor () : ColorScheme!.Disabled
                           );
         }
     }
@@ -376,13 +376,7 @@ public partial class View // Drawing APIs
     /// </returns>
     public virtual Attribute GetHotFocusColor ()
     {
-        ColorScheme cs = ColorScheme;
-
-
-        if (cs is null)
-        {
-            cs = new ();
-        }
+        ColorScheme? cs = ColorScheme ?? new ();
 
         return Enabled ? GetColor (cs.HotFocus) : cs.Disabled;
     }

+ 28 - 144
Terminal.Gui/View/View.Keyboard.cs

@@ -11,14 +11,12 @@ public partial class View // Keyboard APIs
     private void SetupKeyboard ()
     {
         KeyBindings = new (this);
+        KeyBindings.Add (Key.Space, Command.Select);
+        KeyBindings.Add (Key.Enter, Command.Accept);
+
+        // Note, setting HotKey will bind HotKey to Command.HotKey
         HotKeySpecifier = (Rune)'_';
         TitleTextFormatter.HotKeyChanged += TitleTextFormatter_HotKeyChanged;
-
-        // By default, the HotKey command sets the focus
-        AddCommand (Command.HotKey, OnHotKey);
-
-        // By default, the Accept command raises the Accept event
-        AddCommand (Command.Accept, OnAccept);
     }
 
     /// <summary>
@@ -28,22 +26,6 @@ public partial class View // Keyboard APIs
 
     #region HotKey Support
 
-    /// <summary>
-    ///     Called when the HotKey command (<see cref="Command.HotKey"/>) is invoked. Causes this view to be focused.
-    /// </summary>
-    /// <returns>If <see langword="true"/> the command was canceled.</returns>
-    private bool? OnHotKey ()
-    {
-        if (CanFocus)
-        {
-            SetFocus ();
-
-            return true;
-        }
-
-        return false;
-    }
-
     /// <summary>Invoked when the <see cref="HotKey"/> is changed.</summary>
     public event EventHandler<KeyChangedEventArgs>? HotKeyChanged;
 
@@ -52,16 +34,22 @@ public partial class View // Keyboard APIs
 
     /// <summary>
     ///     Gets or sets the hot key defined for this view. Pressing the hot key on the keyboard while this view has focus will
-    ///     invoke the <see cref="Command.HotKey"/> and <see cref="Command.Accept"/> commands. <see cref="Command.HotKey"/>
-    ///     causes the view to be focused and <see cref="Command.Accept"/> does nothing. By default, the HotKey is
-    ///     automatically set to the first character of <see cref="Text"/> that is prefixed with <see cref="HotKeySpecifier"/>.
+    ///     invoke <see cref="Command.HotKey"/>. By default, the HotKey is set to the first character of <see cref="Text"/>
+    ///     that is prefixed with <see cref="HotKeySpecifier"/>.
+    ///     <para>
+    ///         A HotKey is a keypress that causes a visible UI item to perform an action. For example, in a Dialog,
+    ///         with a Button with the text of "_Text" <c>Alt+T</c> will cause the button to gain focus and to raise its
+    ///         <see cref="Accepting"/> event.
+    ///         Or, in a
+    ///         <see cref="Menu"/> with "_File _Edit", <c>Alt+F</c> will select (show) the "_File" menu. If the "_File" menu
+    ///         has a
+    ///         sub-menu of "_New" <c>Alt+N</c> or <c>N</c> will ONLY select the "_New" sub-menu if the "_File" menu is already
+    ///         opened.
+    ///     </para>
     ///     <para>
-    ///         A HotKey is a keypress that selects a visible UI item. For selecting items across <see cref="View"/>`s (e.g.a
-    ///         <see cref="Button"/> in a <see cref="Dialog"/>) the keypress must include the <see cref="Key.WithAlt"/>
-    ///         modifier. For selecting items within a View that are not Views themselves, the keypress can be key without the
-    ///         Alt modifier. For example, in a Dialog, a Button with the text of "_Text" can be selected with Alt-T. Or, in a
-    ///         <see cref="Menu"/> with "_File _Edit", Alt-F will select (show) the "_File" menu. If the "_File" menu has a
-    ///         sub-menu of "_New" `Alt-N` or `N` will ONLY select the "_New" sub-menu if the "_File" menu is already opened.
+    ///         View subclasses can use <see cref="View.AddCommand(Command,CommandImplementation)"/> to
+    ///         define the
+    ///         behavior of the hot key.
     ///     </para>
     /// </summary>
     /// <remarks>
@@ -510,7 +498,7 @@ public partial class View // Keyboard APIs
     /// <summary>Gets the key bindings for this view.</summary>
     public KeyBindings KeyBindings { get; internal set; } = null!;
 
-    private Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; } = new ();
+    private Dictionary<Command, CommandImplementation> CommandImplementations { get; } = new ();
 
     /// <summary>
     ///     Low-level API called when a user presses a key; invokes any key bindings set on the view. This is called
@@ -523,8 +511,9 @@ public partial class View // Keyboard APIs
     /// <param name="keyEvent">Contains the details about the key that produced the event.</param>
     /// <param name="scope">The scope.</param>
     /// <returns>
-    ///     <see langword="false"/> if the key press was not handled. <see langword="true"/> if the keypress was handled
-    ///     and no other view should see it.
+    ///     <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.
     /// </returns>
     public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
@@ -688,7 +677,7 @@ public partial class View // Keyboard APIs
     }
 
     /// <summary>
-    ///     Invoked when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
+    ///     Raised when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
     ///     stop the key from being processed by other views.
     /// </summary>
     public event EventHandler<Key>? InvokingKeyBindings;
@@ -700,9 +689,9 @@ public partial class View // Keyboard APIs
     /// <param name="key">The key event passed.</param>
     /// <param name="scope">The scope.</param>
     /// <returns>
-    ///     <see langword="null"/> if no command was bound the <paramref name="key"/>. <see langword="true"/> if
-    ///     commands were invoked and at least one handled the command. <see langword="false"/> if commands were invoked and at
-    ///     none handled the command.
+    ///     <see langword="null"/> if no command was invoked; 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 and handled (or cancelled); input proessing should stop.
     /// </returns>
     protected bool? InvokeKeyBindings (Key key, KeyBindingScope scope)
     {
@@ -733,6 +722,7 @@ public partial class View // Keyboard APIs
         }
 
 #endif
+        return InvokeCommands (binding.Commands, key, binding);
 
         foreach (Command command in binding.Commands)
         {
@@ -759,111 +749,5 @@ public partial class View // Keyboard APIs
         return toReturn;
     }
 
-    /// <summary>
-    ///     Invokes the specified commands.
-    /// </summary>
-    /// <param name="commands"></param>
-    /// <param name="key">The key that caused the commands to be invoked, if any.</param>
-    /// <param name="keyBinding"></param>
-    /// <returns>
-    ///     <see langword="null"/> if no command was found.
-    ///     <see langword="true"/> if the command was invoked the command was handled.
-    ///     <see langword="false"/> if the command was invoked and the command was not handled.
-    /// </returns>
-    public bool? InvokeCommands (Command [] commands, Key? key = null, KeyBinding? keyBinding = null)
-    {
-        bool? toReturn = null;
-
-        foreach (Command command in commands)
-        {
-            if (!CommandImplementations.ContainsKey (command))
-            {
-                throw new NotSupportedException (@$"{command} is not supported by ({GetType ().Name}).");
-            }
-
-            // each command has its own return value
-            bool? thisReturn = InvokeCommand (command, key, keyBinding);
-
-            // if we haven't got anything yet, the current command result should be used
-            toReturn ??= thisReturn;
-
-            // if ever see a true then that's what we will return
-            if (thisReturn ?? false)
-            {
-                toReturn = true;
-            }
-        }
-
-        return toReturn;
-    }
-
-    /// <summary>Invokes the specified command.</summary>
-    /// <param name="command">The command to invoke.</param>
-    /// <param name="key">The key that caused the command to be invoked, if any.</param>
-    /// <param name="keyBinding"></param>
-    /// <returns>
-    ///     <see langword="null"/> if no command was found. <see langword="true"/> if the command was invoked, and it
-    ///     handled the command. <see langword="false"/> if the command was invoked, and it did not handle the command.
-    /// </returns>
-    public bool? InvokeCommand (Command command, Key? key = null, KeyBinding? keyBinding = null)
-    {
-        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
-        {
-            var context = new CommandContext (command, key, keyBinding); // Create the context here
-
-            return implementation (context);
-        }
-
-        return null;
-    }
-
-    /// <summary>
-    ///     <para>
-    ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
-    ///         AddCommand for each command they support.
-    ///     </para>
-    ///     <para>
-    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
-    ///         replace the old one.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         This version of AddCommand is for commands that require <see cref="CommandContext"/>. Use
-    ///         <see cref="AddCommand(Command,Func{System.Nullable{bool}})"/>
-    ///         in cases where the command does not require a <see cref="CommandContext"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="command">The command.</param>
-    /// <param name="f">The function.</param>
-    protected void AddCommand (Command command, Func<CommandContext, bool?> f) { CommandImplementations [command] = f; }
-
-    /// <summary>
-    ///     <para>
-    ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
-    ///         AddCommand for each command they support.
-    ///     </para>
-    ///     <para>
-    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
-    ///         replace the old one.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
-    ///         If the command requires context, use
-    ///         <see cref="AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>
-    ///     </para>
-    /// </remarks>
-    /// <param name="command">The command.</param>
-    /// <param name="f">The function.</param>
-    protected void AddCommand (Command command, Func<bool?> f) { CommandImplementations [command] = ctx => f (); }
-
-    /// <summary>Returns all commands that are supported by this <see cref="View"/>.</summary>
-    /// <returns></returns>
-    public IEnumerable<Command> GetSupportedCommands () { return CommandImplementations.Keys; }
-
-    // TODO: Add GetKeysBoundToCommand() - given a Command, return all Keys that would invoke it
-
     #endregion Key Bindings
 }

+ 24 - 23
Terminal.Gui/View/View.Layout.cs

@@ -38,13 +38,14 @@ public partial class View // Layout APIs
         int targetX,
         int targetY,
         out int nx,
-        out int ny,
-        out StatusBar? statusBar
+        out int ny
+       //,
+       // out StatusBar? statusBar
     )
     {
         int maxDimension;
         View? superView;
-        statusBar = null!;
+        //statusBar = null!;
 
         if (viewToMove is not Toplevel || viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
         {
@@ -112,26 +113,26 @@ public partial class View // Layout APIs
 
         ny = Math.Max (targetY, maxDimension);
 
-        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        {
-            statusVisible = Application.Top?.StatusBar?.Visible == true;
-            statusBar = Application.Top?.StatusBar!;
-        }
-        else
-        {
-            View? t = viewToMove!.SuperView;
-
-            while (t is { } and not Toplevel)
-            {
-                t = t.SuperView;
-            }
-
-            if (t is Toplevel topLevel)
-            {
-                statusVisible = topLevel.StatusBar?.Visible == true;
-                statusBar = topLevel.StatusBar!;
-            }
-        }
+        //if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        //{
+        //    statusVisible = Application.Top?.StatusBar?.Visible == true;
+        //    statusBar = Application.Top?.StatusBar!;
+        //}
+        //else
+        //{
+        //    View? t = viewToMove!.SuperView;
+
+        //    while (t is { } and not Toplevel)
+        //    {
+        //        t = t.SuperView;
+        //    }
+
+        //    if (t is Toplevel topLevel)
+        //    {
+        //        statusVisible = topLevel.StatusBar?.Visible == true;
+        //        statusBar = topLevel.StatusBar!;
+        //    }
+        //}
 
         if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
         {

+ 7 - 8
Terminal.Gui/View/View.Mouse.cs

@@ -57,7 +57,7 @@ public partial class View // Mouse APIs
                 return args.Cancel;
             }
 
-            ColorScheme cs = ColorScheme;
+            ColorScheme? cs = ColorScheme;
 
             if (cs is null)
             {
@@ -66,7 +66,7 @@ public partial class View // Mouse APIs
 
             _savedNonHoverColorScheme = cs;
 
-            ColorScheme = ColorScheme.GetHighlightColorScheme ();
+            ColorScheme = ColorScheme?.GetHighlightColorScheme ();
         }
 
         return false;
@@ -369,11 +369,10 @@ public partial class View // Mouse APIs
         }
 
         // Post-conditions
-        if (!HasFocus && CanFocus)
-        {
-            args.Handled = true;
-            SetFocus ();
-        }
+
+        // Always invoke Select command on MouseClick
+        // By default, this will raise Selecting/OnSelecting - Subclasses can override this via AddCommand (Command.Select ...).
+        args.Handled = InvokeCommand (Command.Select, ctx: new (Command.Select, key: null, data: args.MouseEvent)) == true;
 
         return args.Handled;
     }
@@ -404,7 +403,7 @@ public partial class View // Mouse APIs
             }
 
             // If mouse is still in bounds, generate a click
-            if (!WantContinuousButtonPressed && Viewport.Contains (mouseEvent.Position))
+            if (!WantMousePositionReports && Viewport.Contains (mouseEvent.Position))
             {
                 return OnMouseClick (new (mouseEvent));
             }

+ 3 - 2
Terminal.Gui/View/View.Navigation.cs

@@ -292,7 +292,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </returns>
     internal bool RestoreFocus ()
     {
-        View [] indicies = GetFocusChain (NavigationDirection.Forward, TabStop);
+        View [] indicies = GetFocusChain (NavigationDirection.Forward, null);
 
         if (Focused is null && _previouslyFocused is { } && indicies.Contains (_previouslyFocused))
         {
@@ -394,6 +394,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
     ///     </para>
     /// </remarks>
+    /// <returns><see langword="true"/> if the focus changed; <see langword="true"/> false otherwise.</returns>
     public bool SetFocus ()
     {
         (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ());
@@ -420,7 +421,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <exception cref="InvalidOperationException"></exception>
     private (bool focusSet, bool cancelled) SetHasFocusTrue (View? previousFocusedView, bool traversingUp = false)
     {
-        Debug.Assert (ApplicationNavigation.IsInHierarchy (SuperView, this));
+        Debug.Assert (SuperView is null || ApplicationNavigation.IsInHierarchy (SuperView, this));
 
         // Pre-conditions
         if (_hasFocus)

+ 87 - 74
Terminal.Gui/View/View.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui;
 #region API Docs
 
 /// <summary>
-///     View is the base class for all views on the screen and represents a visible element that can render itself and
+///     View is the base class all visible elements. View can render itself and
 ///     contains zero or more nested views, called SubViews. View provides basic functionality for layout, positioning, and
 ///     drawing. In addition, View provides keyboard and mouse event handling.
 /// </summary>
@@ -100,20 +100,14 @@ namespace Terminal.Gui;
 ///         <see cref="View"/> inheritance hierarchies to override base class layout code optimally by doing so only on
 ///         first run, instead of on every run.
 ///     </para>
-///     <para>See <see href="../docs/keyboard.md">for an overview of View keyboard handling.</see></para>
-///     ///
+///     <para>See <see href="../docs/keyboard.md"> for an overview of View keyboard handling.</see></para>
 /// </remarks>
 
 #endregion API Docs
 
 public partial class View : Responder, ISupportInitializeNotification
 {
-    /// <summary>
-    ///     Cancelable event fired when the <see cref="Command.Accept"/> command is invoked. Set
-    ///     <see cref="HandledEventArgs.Handled"/>
-    ///     to cancel the event.
-    /// </summary>
-    public event EventHandler<HandledEventArgs>? Accept;
+    #region Constructors and Initialization
 
     /// <summary>Gets or sets arbitrary data for the view.</summary>
     /// <remarks>This property is not used internally.</remarks>
@@ -124,47 +118,6 @@ public partial class View : Responder, ISupportInitializeNotification
     /// <remarks>The id should be unique across all Views that share a SuperView.</remarks>
     public string Id { get; set; } = "";
 
-    /// <summary>Pretty prints the View</summary>
-    /// <returns></returns>
-    public override string ToString () { return $"{GetType ().Name}({Id}){Frame}"; }
-
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        LineCanvas.Dispose ();
-
-        DisposeKeyboard ();
-        DisposeAdornments ();
-
-        for (int i = InternalSubviews.Count - 1; i >= 0; i--)
-        {
-            View subview = InternalSubviews [i];
-            Remove (subview);
-            subview.Dispose ();
-        }
-
-        base.Dispose (disposing);
-        Debug.Assert (InternalSubviews.Count == 0);
-    }
-
-    /// <summary>
-    ///     Called when the <see cref="Command.Accept"/> command is invoked. Raises <see cref="Accept"/>
-    ///     event.
-    /// </summary>
-    /// <returns>
-    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
-    ///     If <see langword="null"/> no event was raised.
-    /// </returns>
-    protected bool? OnAccept ()
-    {
-        var args = new HandledEventArgs ();
-        Accept?.Invoke (this, args);
-
-        return Accept is null ? null : args.Handled;
-    }
-
-    #region Constructors and Initialization
-
     /// <summary>
     ///     Points to the current driver in use by the view, it is a convenience property for simplifying the development
     ///     of new views.
@@ -181,14 +134,19 @@ public partial class View : Responder, ISupportInitializeNotification
     public View ()
     {
         SetupAdornments ();
+
+        SetupCommands ();
+
         SetupKeyboard ();
 
         //SetupMouse ();
+
         SetupText ();
+
     }
 
     /// <summary>
-    ///     Event called only once when the <see cref="View"/> is being initialized for the first time. Allows
+    ///     Raised once when the <see cref="View"/> is being initialized for the first time. Allows
     ///     configurations and assignments to be performed before the <see cref="View"/> being shown.
     ///     View implements <see cref="ISupportInitializeNotification"/> to allow for more sophisticated initialization.
     /// </summary>
@@ -287,7 +245,7 @@ public partial class View : Responder, ISupportInitializeNotification
         Initialized?.Invoke (this, EventArgs.Empty);
     }
 
-#endregion Constructors and Initialization
+    #endregion Constructors and Initialization
 
     #region Visibility
 
@@ -314,7 +272,10 @@ public partial class View : Responder, ISupportInitializeNotification
                 HasFocus = false;
             }
 
-            if (_enabled && CanFocus && Visible && !HasFocus
+            if (_enabled
+                && CanFocus
+                && Visible
+                && !HasFocus
                 && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
             {
                 SetFocus ();
@@ -346,15 +307,17 @@ public partial class View : Responder, ISupportInitializeNotification
         }
     }
 
-    /// <summary>Event fired when the <see cref="Enabled"/> value is being changed.</summary>
+    /// <summary>Raised when the <see cref="Enabled"/> value is being changed.</summary>
     public event EventHandler? EnabledChanged;
 
-    /// <summary>Method invoked when the <see cref="Enabled"/> property from a view is changed.</summary>
+    // TODO: Change this event to match the standard TG event model.
+    /// <summary>Invoked when the <see cref="Enabled"/> property from a view is changed.</summary>
     public virtual void OnEnabledChanged () { EnabledChanged?.Invoke (this, EventArgs.Empty); }
 
     private bool _visible = true;
 
-    /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> and all its child controls are displayed.</summary>
+    // TODO: Remove virtual once Menu/MenuBar are removed. MenuBar is the only override.
+    /// <summary>Gets or sets a value indicating whether this <see cref="View"/> is visible.</summary>
     public virtual bool Visible
     {
         get => _visible;
@@ -365,6 +328,19 @@ public partial class View : Responder, ISupportInitializeNotification
                 return;
             }
 
+            if (OnVisibleChanging ())
+            {
+                return;
+            }
+
+            CancelEventArgs<bool> args = new (in _visible, ref value);
+            VisibleChanging?.Invoke (this, args);
+
+            if (args.Cancel)
+            {
+                return;
+            }
+
             _visible = value;
 
             if (!_visible)
@@ -375,29 +351,45 @@ public partial class View : Responder, ISupportInitializeNotification
                 }
             }
 
-            if (_visible && CanFocus && Enabled && !HasFocus
+            if (_visible
+                && CanFocus
+                && Enabled
+                && !HasFocus
                 && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
             {
                 SetFocus ();
             }
 
             OnVisibleChanged ();
+            VisibleChanged?.Invoke (this, EventArgs.Empty);
+
             SetNeedsDisplay ();
         }
     }
 
-    /// <summary>Method invoked when the <see cref="Visible"/> property from a view is changed.</summary>
-    public virtual void OnVisibleChanged () { VisibleChanged?.Invoke (this, EventArgs.Empty); }
+    /// <summary>Called when <see cref="Visible"/> is changing. Can be cancelled by returning <see langword="true"/>.</summary>
+    protected virtual bool OnVisibleChanging () { return false; }
+
+    /// <summary>
+    ///     Raised when the <see cref="Visible"/> value is being changed. Can be cancelled by setting Cancel to
+    ///     <see langword="true"/>.
+    /// </summary>
+    public event EventHandler<CancelEventArgs<bool>>? VisibleChanging;
+
+    /// <summary>Called when <see cref="Visible"/> has changed.</summary>
+    protected virtual void OnVisibleChanged () { }
 
-    /// <summary>Event fired when the <see cref="Visible"/> value is being changed.</summary>
+    /// <summary>Raised when <see cref="Visible"/> has changed.</summary>
     public event EventHandler? VisibleChanged;
 
-    // TODO: This API is a hack. We should make Visible propogate automatically, no? See https://github.com/gui-cs/Terminal.Gui/issues/3703
     /// <summary>
     ///     INTERNAL Indicates whether all views up the Superview hierarchy are visible.
     /// </summary>
     /// <param name="view">The view to test.</param>
-    /// <returns> <see langword="false"/> if `view.Visible` is  <see langword="false"/> or any Superview is not visible, <see langword="true"/> otherwise.</returns>
+    /// <returns>
+    ///     <see langword="false"/> if `view.Visible` is  <see langword="false"/> or any Superview is not visible,
+    ///     <see langword="true"/> otherwise.
+    /// </returns>
     internal static bool CanBeVisible (View view)
     {
         if (!view.Visible)
@@ -416,7 +408,7 @@ public partial class View : Responder, ISupportInitializeNotification
         return true;
     }
 
-#endregion Visibility
+    #endregion Visibility
 
     #region Title
 
@@ -492,18 +484,16 @@ public partial class View : Responder, ISupportInitializeNotification
     private void SetTitleTextFormatterSize ()
     {
         TitleTextFormatter.ConstrainToSize = new (
-                                       TextFormatter.GetWidestLineLength (TitleTextFormatter.Text)
-                                       - (TitleTextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
-                                              ? Math.Max (HotKeySpecifier.GetColumns (), 0)
-                                              : 0),
-                                       1);
+                                                  TextFormatter.GetWidestLineLength (TitleTextFormatter.Text)
+                                                  - (TitleTextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+                                                         ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+                                                         : 0),
+                                                  1);
     }
 
+    // TODO: Change this event to match the standard TG event model.
     /// <summary>Called when the <see cref="View.Title"/> has been changed. Invokes the <see cref="TitleChanged"/> event.</summary>
-    protected void OnTitleChanged ()
-    {
-        TitleChanged?.Invoke (this, new (in _title));
-    }
+    protected void OnTitleChanged () { TitleChanged?.Invoke (this, new (in _title)); }
 
     /// <summary>
     ///     Called before the <see cref="View.Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can
@@ -519,14 +509,37 @@ public partial class View : Responder, ISupportInitializeNotification
         return args.Cancel;
     }
 
-    /// <summary>Event fired after the <see cref="View.Title"/> has been changed.</summary>
+    /// <summary>Raised after the <see cref="View.Title"/> has been changed.</summary>
     public event EventHandler<EventArgs<string>>? TitleChanged;
 
     /// <summary>
-    ///     Event fired when the <see cref="View.Title"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to `true`
+    ///     Raised when the <see cref="View.Title"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to `true`
     ///     to cancel the Title change.
     /// </summary>
     public event EventHandler<CancelEventArgs<string>>? TitleChanging;
 
     #endregion
+
+    /// <summary>Pretty prints the View</summary>
+    /// <returns></returns>
+    public override string ToString () { return $"{GetType ().Name}({Id}){Frame}"; }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        LineCanvas.Dispose ();
+
+        DisposeKeyboard ();
+        DisposeAdornments ();
+
+        for (int i = InternalSubviews.Count - 1; i >= 0; i--)
+        {
+            View subview = InternalSubviews [i];
+            Remove (subview);
+            subview.Dispose ();
+        }
+
+        base.Dispose (disposing);
+        Debug.Assert (InternalSubviews.Count == 0);
+    }
 }

+ 56 - 9
Terminal.Gui/Views/Bar.cs

@@ -32,6 +32,7 @@ public class Bar : View, IOrientation, IDesignable
         _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
         Initialized += Bar_Initialized;
+        MouseEvent += OnMouseEvent;
 
         if (shortcuts is null)
         {
@@ -44,7 +45,43 @@ public class Bar : View, IOrientation, IDesignable
         }
     }
 
-    private void Bar_Initialized (object? sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
+    private void OnMouseEvent (object? sender, MouseEventEventArgs e)
+    {
+        NavigationDirection direction = NavigationDirection.Backward;
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledDown)
+        {
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledUp)
+        {
+            direction = NavigationDirection.Forward;
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledRight)
+        {
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledLeft)
+        {
+            direction = NavigationDirection.Forward;
+            e.Handled = true;
+        }
+
+        if (e.Handled)
+        {
+            e.Handled = AdvanceFocus (direction, TabBehavior.TabStop);
+        }
+    }
+
+    private void Bar_Initialized (object? sender, EventArgs e)
+    {
+        ColorScheme = Colors.ColorSchemes ["Menu"];
+        LayoutBarItems (GetContentSize ());
+    }
 
     /// <inheritdoc/>
     public override void SetBorderStyle (LineStyle value)
@@ -159,6 +196,11 @@ public class Bar : View, IOrientation, IDesignable
     {
         base.OnLayoutStarted (args);
 
+        LayoutBarItems (args.OldContentSize);
+    }
+
+    private void LayoutBarItems (Size contentSize)
+    {
         View? prevBarItem = null;
 
         switch (Orientation)
@@ -171,8 +213,6 @@ public class Bar : View, IOrientation, IDesignable
                     barItem.ColorScheme = ColorScheme;
                     barItem.X = Pos.Align (Alignment.Start, AlignmentModes);
                     barItem.Y = 0; //Pos.Center ();
-                    // HACK: This should not be needed
-                    barItem.SetRelativeLayout (GetContentSize ());
                 }
                 break;
 
@@ -206,8 +246,6 @@ public class Bar : View, IOrientation, IDesignable
                     if (barItem is Shortcut scBarItem)
                     {
                         scBarItem.MinimumKeyTextSize = minKeyWidth;
-                        // HACK: This should not be needed
-                        scBarItem.SetRelativeLayout (GetContentSize ());
                         maxBarItemWidth = Math.Max (maxBarItemWidth, scBarItem.Frame.Width);
                     }
 
@@ -231,10 +269,6 @@ public class Bar : View, IOrientation, IDesignable
                 foreach (View barItem in Subviews)
                 {
                     barItem.Width = maxBarItemWidth;
-
-                    if (barItem is Line line)
-                    {
-                    }
                 }
 
                 Height = Dim.Auto (DimAutoStyle.Content, totalHeight);
@@ -264,6 +298,19 @@ public class Bar : View, IOrientation, IDesignable
 
         Add (shortcut);
 
+        shortcut = new Shortcut
+        {
+            Text = "Czech",
+            CommandView = new CheckBox ()
+            {
+                Title = "_Check"
+            },
+            Key = Key.F9,
+            CanFocus = false
+        };
+
+        Add (shortcut);
+
         return true;
     }
 }

+ 95 - 35
Terminal.Gui/Views/Button.cs

@@ -1,27 +1,22 @@
-//
-// Button.cs: Button control
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-
 namespace Terminal.Gui;
 
-/// <summary>Button is a <see cref="View"/> that provides an item that invokes raises the <see cref="View.Accept"/> event.</summary>
+/// <summary>
+///     A button View that can be pressed with the mouse or keybaord.
+/// </summary>
 /// <remarks>
 ///     <para>
-///         Provides a button showing text that raises the <see cref="View.Accept"/> event when clicked on with a mouse or
-///         when the user presses SPACE, ENTER, or the <see cref="View.HotKey"/>. The hot key is the first letter or digit
-///         following the first underscore ('_') in the button text.
+///         The Button will raise the <see cref="View.Accepting"/> event when the user presses <see cref="View.HotKey"/>,
+///         <c>Enter</c>, or <c>Space</c>
+///         or clicks on the button with the mouse.
 ///     </para>
 ///     <para>Use <see cref="View.HotKeySpecifier"/> to change the hot key specifier from the default of ('_').</para>
 ///     <para>
-///         When the button is configured as the default (<see cref="IsDefault"/>) and the user presses the ENTER key, if
-///         no other <see cref="View"/> processes the key, the <see cref="Button"/>'s <see cref="View.Accept"/> event will
-///         be fired.
+///         Button can act as the default <see cref="Command.Accept"/> handler for all peer-Views. See
+///         <see cref="IsDefault"/>.
 ///     </para>
 ///     <para>
-///         Set <see cref="View.WantContinuousButtonPressed"/> to <see langword="true"/> to have the <see cref="View.Accept"/> event
+///         Set <see cref="View.WantContinuousButtonPressed"/> to <see langword="true"/> to have the
+///         <see cref="View.Accepting"/> event
 ///         invoked repeatedly while the button is pressed.
 ///     </para>
 /// </remarks>
@@ -34,13 +29,13 @@ public class Button : View, IDesignable
     private bool _isDefault;
 
     /// <summary>
-    /// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
+    ///     Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
 
     /// <summary>
-    /// Gets or sets the default Highlight Style.
+    ///     Gets or sets the default Highlight Style.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.Pressed | HighlightStyle.Hover;
@@ -61,14 +56,11 @@ public class Button : View, IDesignable
 
         CanFocus = true;
 
-        // Override default behavior of View
-        AddCommand (Command.HotKey, () =>
-        {
-            SetFocus ();
-            return !OnAccept ();
-        });
+        AddCommand (Command.HotKey, HandleHotKeyCommand);
 
+        KeyBindings.Remove (Key.Space);
         KeyBindings.Add (Key.Space, Command.HotKey);
+        KeyBindings.Remove (Key.Enter);
         KeyBindings.Add (Key.Enter, Command.HotKey);
 
         TitleChanged += Button_TitleChanged;
@@ -78,9 +70,38 @@ public class Button : View, IDesignable
         HighlightStyle = DefaultHighlightStyle;
     }
 
+    private bool? HandleHotKeyCommand (CommandContext ctx)
+    {
+        bool cachedIsDefault = IsDefault; // Supports "Swap Default" in Buttons scenario where IsDefault changes
+
+        if (RaiseSelecting (ctx) is true)
+        {
+            return true;
+        }
+
+        bool? handled = RaiseAccepting (ctx);
+
+        if (handled == true)
+        {
+            return true;
+        }
+
+        SetFocus ();
+
+        // TODO: If `IsDefault` were a property on `View` *any* View could work this way. That's theoretical as
+        // TODO: no use-case has been identified for any View other than Button to act like this.
+        // If Accept was not handled...
+        if (cachedIsDefault && SuperView is { })
+        {
+            return SuperView.InvokeCommand (Command.Accept);
+        }
+
+        return false;
+    }
+
     private bool _wantContinuousButtonPressed;
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override bool WantContinuousButtonPressed
     {
         get => _wantContinuousButtonPressed;
@@ -106,7 +127,13 @@ public class Button : View, IDesignable
 
     private void Button_MouseClick (object sender, MouseEventEventArgs e)
     {
-        e.Handled = InvokeCommand (Command.HotKey) == true;
+        if (e.Handled)
+        {
+            return;
+        }
+
+        // TODO: With https://github.com/gui-cs/Terminal.Gui/issues/3778 we won't have to pass data:
+        e.Handled = InvokeCommand (Command.HotKey, new (Command.HotKey, null, data: this)) == true;
     }
 
     private void Button_TitleChanged (object sender, EventArgs<string> e)
@@ -115,37 +142,68 @@ public class Button : View, IDesignable
         TextFormatter.HotKeySpecifier = HotKeySpecifier;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override string Text
     {
-        get => base.Title;
-        set => base.Text = base.Title = value;
+        get => Title;
+        set => base.Text = Title = value;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override Rune HotKeySpecifier
     {
         get => base.HotKeySpecifier;
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
     }
 
-    /// <summary>Gets or sets whether the <see cref="Button"/> is the default action to activate in a dialog.</summary>
-    /// <value><c>true</c> if is default; otherwise, <c>false</c>.</value>
+    /// <summary>
+    ///     Gets or sets whether the <see cref="Button"/> will act as the default handler for <see cref="Command.Accept"/>
+    ///     commands on the <see cref="View.SuperView"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         If <see langword="true"/>:
+    ///     </para>
+    ///     <para>
+    ///         - the Button will display an indicator that it is the default Button.
+    ///     </para>
+    ///     <para>
+    ///         - when clicked, if the Accepting event is not handled, <see cref="Command.Accept"/> will be
+    ///         invoked on the SuperView.
+    ///     </para>
+    ///     <para>
+    ///         - If a peer-View receives <see cref="Command.Accept"/> and does not handle it, the command will be passed to
+    ///         the
+    ///         first Button in the SuperView that has <see cref="IsDefault"/> set to <see langword="true"/>. See
+    ///         <see cref="View.RaiseAccepting"/> for more information.
+    ///     </para>
+    /// </remarks>
     public bool IsDefault
     {
         get => _isDefault;
         set
         {
+            if (_isDefault == value)
+            {
+                return;
+            }
+
             _isDefault = value;
+
             UpdateTextFormatterText ();
             OnResizeNeeded ();
         }
     }
 
-    /// <summary></summary>
+    /// <summary>
+    ///     Gets or sets whether the Button will show decorations or not. If <see langword="true"/> the glyphs that normally
+    ///     brakcet the Button Title and the <see cref="IsDefault"/> indicator will not be shown.
+    /// </summary>
     public bool NoDecorations { get; set; }
 
-    /// <summary></summary>
+    /// <summary>
+    ///     Gets or sets whether the Button will include padding on each side of the Title.
+    /// </summary>
     public bool NoPadding { get; set; }
 
     /// <inheritdoc/>
@@ -158,6 +216,7 @@ public class Button : View, IDesignable
                 if (TextFormatter.Text [i] == Text [0])
                 {
                     Move (i, 0);
+
                     return null; // Don't show the cursor
                 }
             }
@@ -170,6 +229,7 @@ public class Button : View, IDesignable
     protected override void UpdateTextFormatterText ()
     {
         base.UpdateTextFormatterText ();
+
         if (NoDecorations)
         {
             TextFormatter.Text = Text;
@@ -191,11 +251,11 @@ public class Button : View, IDesignable
         }
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public bool EnableForDesign ()
     {
         Title = "_Button";
 
         return true;
     }
-}
+}

+ 117 - 63
Terminal.Gui/Views/CheckBox.cs

@@ -1,11 +1,11 @@
 #nullable enable
 namespace Terminal.Gui;
 
-/// <summary>Shows a check box that can be cycled between three states.</summary>
+/// <summary>Shows a check box that can be cycled between two or three states.</summary>
 public class CheckBox : View
 {
     /// <summary>
-    /// Gets or sets the default Highlight Style.
+    ///     Gets or sets the default Highlight Style.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.PressedOutside | HighlightStyle.Pressed | HighlightStyle.Hover;
@@ -16,26 +16,39 @@ public class CheckBox : View
     public CheckBox ()
     {
         Width = Dim.Auto (DimAutoStyle.Text);
-        Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1);
+        Height = Dim.Auto (DimAutoStyle.Text, 1);
 
         CanFocus = true;
 
-        // Things this view knows how to do
-        AddCommand (Command.Accept, AdvanceCheckState);
-        AddCommand (Command.HotKey, AdvanceCheckState);
+        // Select (Space key and single-click) - Advance state and raise Select event - DO NOT raise Accept
+        AddCommand (Command.Select, AdvanceAndSelect);
 
-        // Default keybindings for this view
-        KeyBindings.Add (Key.Space, Command.Accept);
+        // Hotkey - Advance state and raise Select event - DO NOT raise Accept
+        AddCommand (Command.HotKey, AdvanceAndSelect);
+
+        // Accept (Enter key) - Raise Accept event - DO NOT advance state
+        AddCommand (Command.Accept, RaiseAccepting);
 
         TitleChanged += Checkbox_TitleChanged;
 
         HighlightStyle = DefaultHighlightStyle;
-        MouseClick += CheckBox_MouseClick;
     }
 
-    private void CheckBox_MouseClick (object? sender, MouseEventEventArgs e)
+    private bool? AdvanceAndSelect (CommandContext ctx)
     {
-        e.Handled = AdvanceCheckState () == true;
+        bool? cancelled = AdvanceCheckState ();
+
+        if (cancelled is true)
+        {
+            return true;
+        }
+
+        if (RaiseSelecting (ctx) is true)
+        {
+            return true;
+        }
+
+        return ctx.Command == Command.HotKey ? cancelled : cancelled is false;
     }
 
     private void Checkbox_TitleChanged (object? sender, EventArgs<string> e)
@@ -44,24 +57,25 @@ public class CheckBox : View
         TextFormatter.HotKeySpecifier = HotKeySpecifier;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override string Text
     {
-        get => base.Title;
-        set => base.Text = base.Title = value;
+        get => Title;
+        set => base.Text = Title = value;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override Rune HotKeySpecifier
     {
         get => base.HotKeySpecifier;
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
     }
 
-    private bool _allowNone = false;
+    private bool _allowNone;
 
     /// <summary>
-    ///     If <see langword="true"/> allows <see cref="CheckedState"/> to be <see cref="CheckState.None"/>. The default is <see langword="false"/>.
+    ///     If <see langword="true"/> allows <see cref="CheckedState"/> to be <see cref="CheckState.None"/>. The default is
+    ///     <see langword="false"/>.
     /// </summary>
     public bool AllowCheckStateNone
     {
@@ -72,6 +86,7 @@ public class CheckBox : View
             {
                 return;
             }
+
             _allowNone = value;
 
             if (CheckedState == CheckState.None)
@@ -88,48 +103,106 @@ public class CheckBox : View
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///        If <see cref="AllowCheckStateNone"/> is <see langword="true"/> and <see cref="CheckState.None"/>, the <see cref="CheckBox"/>
-    ///        will display the <c>ConfigurationManager.Glyphs.CheckStateNone</c> character (☒).
+    ///         If <see cref="AllowCheckStateNone"/> is <see langword="true"/> and <see cref="CheckState.None"/>, the
+    ///         <see cref="CheckBox"/>
+    ///         will display the <c>ConfigurationManager.Glyphs.CheckStateNone</c> character (☒).
     ///     </para>
     ///     <para>
-    ///        If <see cref="CheckState.UnChecked"/>, the <see cref="CheckBox"/>
-    ///        will display the <c>ConfigurationManager.Glyphs.CheckStateUnChecked</c> character (☐).
+    ///         If <see cref="CheckState.UnChecked"/>, the <see cref="CheckBox"/>
+    ///         will display the <c>ConfigurationManager.Glyphs.CheckStateUnChecked</c> character (☐).
     ///     </para>
     ///     <para>
-    ///        If <see cref="CheckState.Checked"/>, the <see cref="CheckBox"/>
-    ///        will display the <c>ConfigurationManager.Glyphs.CheckStateChecked</c> character (☑).
+    ///         If <see cref="CheckState.Checked"/>, the <see cref="CheckBox"/>
+    ///         will display the <c>ConfigurationManager.Glyphs.CheckStateChecked</c> character (☑).
     ///     </para>
     /// </remarks>
     public CheckState CheckedState
     {
         get => _checkedState;
-        set
+        set => ChangeCheckedState (value);
+    }
+
+    /// <summary>
+    ///     INTERNAL Sets CheckedState.
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns>
+    ///     <see langword="true"/> if state change was canceled, <see langword="false"/> if the state changed, and
+    ///     <see langword="null"/> if the state was not changed for some other reason.
+    /// </returns>
+    private bool? ChangeCheckedState (CheckState value)
+    {
+        if (_checkedState == value || (value is CheckState.None && !AllowCheckStateNone))
         {
-            if (_checkedState == value || (value is CheckState.None && !AllowCheckStateNone))
-            {
-                return;
-            }
+            return null;
+        }
+
+        CancelEventArgs<CheckState> e = new (in _checkedState, ref value);
+
+        if (OnCheckedStateChanging (e))
+        {
+            return true;
+        }
 
-            _checkedState = value;
-            UpdateTextFormatterText ();
-            OnResizeNeeded ();
+        CheckedStateChanging?.Invoke (this, e);
+
+        if (e.Cancel)
+        {
+            return e.Cancel;
         }
+
+        _checkedState = value;
+        UpdateTextFormatterText ();
+        OnResizeNeeded ();
+
+        EventArgs<CheckState> args = new (in _checkedState);
+        OnCheckedStateChanged (args);
+
+        CheckedStateChanged?.Invoke (this, args);
+
+        return false;
     }
 
-    /// <summary>
-    ///     Advances <see cref="CheckedState"/> to the next value. Invokes the cancelable <see cref="CheckedStateChanging"/> event.
-    /// </summary>
+    /// <summary>Called when the <see cref="CheckBox"/> state is changing.</summary>
     /// <remarks>
+    ///     <para>
+    ///         The state cahnge can be cancelled by setting the args.Cancel to <see langword="true"/>.
+    ///     </para>
+    /// </remarks>
+    protected virtual bool OnCheckedStateChanging (CancelEventArgs<CheckState> args) { return false; }
+
+    /// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         This event can be cancelled. If cancelled, the <see cref="CheckBox"/> will not change its state.
+    ///     </para>
     /// </remarks>
-    /// <returns>If <see langword="true"/> the <see cref="CheckedStateChanging"/> event was canceled.</returns>
+    public event EventHandler<CancelEventArgs<CheckState>>? CheckedStateChanging;
+
+    /// <summary>Called when the <see cref="CheckBox"/> state has changed.</summary>
+    protected virtual void OnCheckedStateChanged (EventArgs<CheckState> args) { }
+
+    /// <summary>Raised when the <see cref="CheckBox"/> state has changed.</summary>
+    public event EventHandler<EventArgs<CheckState>>? CheckedStateChanged;
+
+    /// <summary>
+    ///     Advances <see cref="CheckedState"/> to the next value. Invokes the cancelable <see cref="CheckedStateChanging"/>
+    ///     event.
+    /// </summary>
     /// <remarks>
-    /// <para>
-    ///     Cycles through the states <see cref="CheckState.None"/>, <see cref="CheckState.Checked"/>, and <see cref="CheckState.UnChecked"/>.
-    /// </para>
-    /// <para>
-    ///     If the <see cref="CheckedStateChanging"/> event is not canceled, the <see cref="CheckedState"/> will be updated and the <see cref="Command.Accept"/> event will be raised.
-    /// </para>
+    ///     <para>
+    ///         Cycles through the states <see cref="CheckState.None"/>, <see cref="CheckState.Checked"/>, and
+    ///         <see cref="CheckState.UnChecked"/>.
+    ///     </para>
+    ///     <para>
+    ///         If the <see cref="CheckedStateChanging"/> event is not canceled, the <see cref="CheckedState"/> will be updated
+    ///         and the <see cref="Command.Accept"/> event will be raised.
+    ///     </para>
     /// </remarks>
+    /// <returns>
+    ///     <see langword="true"/> if state change was canceled, <see langword="false"/> if the state changed, and
+    ///     <see langword="null"/> if the state was not changed for some other reason.
+    /// </returns>
     public bool? AdvanceCheckState ()
     {
         CheckState oldValue = CheckedState;
@@ -158,35 +231,16 @@ public class CheckBox : View
                 break;
         }
 
-        CheckedStateChanging?.Invoke (this, e);
-        if (e.Cancel)
-        {
-            return e.Cancel;
-        }
-
-        // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the Accept event is fired.
-        if (OnAccept () == true)
-        {
-            return true;
-        }
-
-        CheckedState = e.NewValue;
+        bool? cancelled = ChangeCheckedState (e.NewValue);
 
-        return true;
+        return cancelled;
     }
 
-    /// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>
-    /// <remarks>
-    /// <para>
-    ///    This event can be cancelled. If cancelled, the <see cref="CheckBox"/> will not change its state.
-    /// </para>
-    /// </remarks>
-    public event EventHandler<CancelEventArgs<CheckState>>? CheckedStateChanging;
-
     /// <inheritdoc/>
     protected override void UpdateTextFormatterText ()
     {
         base.UpdateTextFormatterText ();
+
         switch (TextAlignment)
         {
             case Alignment.Start:

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

@@ -64,7 +64,7 @@ public class ColorPicker : View
                     Width = textFieldWidth
                 };
                 tfValue.HasFocusChanged += UpdateSingleBarValueFromTextField;
-                tfValue.Accept += (s, _)=>UpdateSingleBarValueFromTextField(s);
+                tfValue.Accepting += (s, _)=>UpdateSingleBarValueFromTextField(s);
                 _textFields.Add (bar, tfValue);
             }
 
@@ -154,7 +154,7 @@ public class ColorPicker : View
         _tfName.Autocomplete = auto;
 
         _tfName.HasFocusChanged += UpdateValueFromName;
-        _tfName.Accept += (s, _) => UpdateValueFromName ();
+        _tfName.Accepting += (s, _) => UpdateValueFromName ();
     }
 
     private void CreateTextField ()
@@ -184,7 +184,7 @@ public class ColorPicker : View
         Add (_tfHex);
 
         _tfHex.HasFocusChanged += UpdateValueFromTextField;
-        _tfHex.Accept += (_,_)=> UpdateValueFromTextField();
+        _tfHex.Accepting += (_,_)=> UpdateValueFromTextField();
     }
 
     private void DisposeOldViews ()

+ 35 - 25
Terminal.Gui/Views/ComboBox.cs

@@ -7,8 +7,6 @@
 
 using System.Collections.ObjectModel;
 using System.ComponentModel;
-using System.Diagnostics;
-using System.Threading.Channels;
 
 namespace Terminal.Gui;
 
@@ -35,20 +33,17 @@ public class ComboBox : View, IDesignable
         _listview = new ComboListView (this, HideDropdownListOnClick) { CanFocus = true, TabStop = TabBehavior.NoStop };
 
         _search.TextChanged += Search_Changed;
-        _search.Accept += Search_Accept;
 
         _listview.Y = Pos.Bottom (_search);
-        _listview.OpenSelectedItem += (sender, a) => Selected ();
-
-        Add (_search, _listview);
-
-        // BUGBUG: This should not be needed; LayoutComplete will handle
-        Initialized += (s, e) => ProcessLayout ();
-
-        // On resize
-        LayoutComplete += (sender, a) => ProcessLayout ();
-        ;
-
+        _listview.OpenSelectedItem += (sender, a) => SelectText ();
+        _listview.Accepting += (sender, args) =>
+                              {
+                                  // This prevents Accepted from bubbling up to the combobox
+                                  args.Cancel = true;
+
+                                  // But OpenSelectedItem won't be fired because of that. So do it here.
+                                  SelectText ();
+                              };
         _listview.SelectedItemChanged += (sender, e) =>
                                          {
                                              if (!HideDropdownListOnClick && _searchSet.Count > 0)
@@ -56,6 +51,13 @@ public class ComboBox : View, IDesignable
                                                  SetValue (_searchSet [_listview.SelectedItem]);
                                              }
                                          };
+        Add (_search, _listview);
+
+        // BUGBUG: This should not be needed; LayoutComplete will handle
+        Initialized += (s, e) => ProcessLayout ();
+
+        // On resize
+        LayoutComplete += (sender, a) => ProcessLayout ();
 
         Added += (s, e) =>
                  {
@@ -76,7 +78,14 @@ public class ComboBox : View, IDesignable
                  };
 
         // Things this view knows how to do
-        AddCommand (Command.Accept, () => ActivateSelected ());
+        AddCommand (Command.Accept, (ctx) =>
+                                    {
+                                        if (ctx.Data == _search)
+                                        {
+                                            return null;
+                                        }
+                                        return ActivateSelected (ctx);
+                                    });
         AddCommand (Command.Toggle, () => ExpandCollapse ());
         AddCommand (Command.Expand, () => Expand ());
         AddCommand (Command.Collapse, () => Collapse ());
@@ -90,7 +99,6 @@ public class ComboBox : View, IDesignable
         AddCommand (Command.UnixEmulation, () => UnixEmulation ());
 
         // Default keybindings for this view
-        KeyBindings.Add (Key.Enter, Command.Accept);
         KeyBindings.Add (Key.F4, Command.Toggle);
         KeyBindings.Add (Key.CursorDown, Command.Down);
         KeyBindings.Add (Key.CursorUp, Command.Up);
@@ -316,7 +324,7 @@ public class ComboBox : View, IDesignable
             _search.CursorPosition = _search.Text.GetRuneCount ();
         }
         else
-        { 
+        {
             if (_source?.Count > 0
               && _selectedItem > -1
               && _selectedItem < _source.Count - 1
@@ -384,13 +392,16 @@ public class ComboBox : View, IDesignable
         }
     }
 
-    private bool ActivateSelected ()
+    private bool ActivateSelected (CommandContext ctx)
     {
         if (HasItems ())
         {
-            Selected ();
+            if (SelectText ())
+            {
+                return false;
+            }
 
-            return true;
+            return RaiseAccepting (ctx) == true;
         }
 
         return false;
@@ -658,9 +669,6 @@ public class ComboBox : View, IDesignable
         SetSearchSet ();
     }
 
-    // Tell TextField to handle Accept Command (Enter)
-    void Search_Accept (object sender, HandledEventArgs e) { e.Handled = true; }
-
     private void Search_Changed (object sender, EventArgs e)
     {
         if (_source is null)
@@ -720,7 +728,7 @@ public class ComboBox : View, IDesignable
         }
     }
 
-    private void Selected ()
+    private bool SelectText ()
     {
         IsShow = false;
         _listview.TabStop = TabBehavior.NoStop;
@@ -731,7 +739,7 @@ public class ComboBox : View, IDesignable
             HideList ();
             IsShow = false;
 
-            return;
+            return false;
         }
 
         SetValue (_listview.SelectedItem > -1 ? _searchSet [_listview.SelectedItem] : _text);
@@ -741,6 +749,8 @@ public class ComboBox : View, IDesignable
         Reset (true);
         HideList ();
         IsShow = false;
+
+        return true;
     }
 
     private void SetSearchSet ()

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

@@ -227,7 +227,7 @@ public class DatePicker : View
             ShadowStyle = ShadowStyle.None
         };
 
-        _previousMonthButton.Accept += (sender, e) =>
+        _previousMonthButton.Accepting += (sender, e) =>
                                         {
                                             Date = _date.AddMonths (-1);
                                             CreateCalendar ();
@@ -247,7 +247,7 @@ public class DatePicker : View
             ShadowStyle = ShadowStyle.None
         };
 
-        _nextMonthButton.Accept += (sender, e) =>
+        _nextMonthButton.Accepting += (sender, e) =>
                                     {
                                         Date = _date.AddMonths (1);
                                         CreateCalendar ();

+ 9 - 9
Terminal.Gui/Views/FileDialog.cs

@@ -78,7 +78,7 @@ public class FileDialog : Dialog
             Y = Pos.AnchorEnd (),
             IsDefault = true, Text = Style.OkButtonText
         };
-        _btnOk.Accept += (s, e) => Accept (true);
+        _btnOk.Accepting += (s, e) => Accept (true);
 
 
         _btnCancel = new Button
@@ -88,7 +88,7 @@ public class FileDialog : Dialog
             Text = Strings.btnCancel
         };
 
-        _btnCancel.Accept += (s, e) =>
+        _btnCancel.Accepting += (s, e) =>
         {
             Canceled = true;
             Application.RequestStop ();
@@ -96,15 +96,15 @@ public class FileDialog : Dialog
 
         _btnUp = new Button { X = 0, Y = 1, NoPadding = true };
         _btnUp.Text = GetUpButtonText ();
-        _btnUp.Accept += (s, e) => _history.Up ();
+        _btnUp.Accepting += (s, e) => _history.Up ();
 
         _btnBack = new Button { X = Pos.Right (_btnUp) + 1, Y = 1, NoPadding = true };
         _btnBack.Text = GetBackButtonText ();
-        _btnBack.Accept += (s, e) => _history.Back ();
+        _btnBack.Accepting += (s, e) => _history.Back ();
 
         _btnForward = new Button { X = Pos.Right (_btnBack) + 1, Y = 1, NoPadding = true };
         _btnForward.Text = GetForwardButtonText ();
-        _btnForward.Accept += (s, e) => _history.Forward ();
+        _btnForward.Accepting += (s, e) => _history.Forward ();
 
         _tbPath = new TextField { Width = Dim.Fill (), CaptionColor = new Color (Color.Black) };
 
@@ -182,7 +182,7 @@ public class FileDialog : Dialog
             Y = Pos.AnchorEnd (), Text = GetToggleSplitterText (false)
         };
 
-        _btnToggleSplitterCollapse.Accept += (s, e) =>
+        _btnToggleSplitterCollapse.Accepting += (s, e) =>
                                               {
                                                   Tile tile = _splitContainer.Tiles.ElementAt (0);
 
@@ -610,7 +610,7 @@ public class FileDialog : Dialog
         ApplySort ();
     }
 
-    private new void Accept (IEnumerable<FileSystemInfoStats> toMultiAccept)
+    private void Accept (IEnumerable<FileSystemInfoStats> toMultiAccept)
     {
         if (!AllowsMultipleSelection)
         {
@@ -629,7 +629,7 @@ public class FileDialog : Dialog
         FinishAccept ();
     }
 
-    private new void Accept (IFileInfo f)
+    private void Accept (IFileInfo f)
     {
         if (!IsCompatibleWithOpenMode (f.FullName, out string reason))
         {
@@ -649,7 +649,7 @@ public class FileDialog : Dialog
         FinishAccept ();
     }
 
-    private new void Accept (bool allowMulti)
+    private void Accept (bool allowMulti)
     {
         if (allowMulti && TryAcceptMulti ())
         {

+ 3 - 1
Terminal.Gui/Views/FrameView.cs

@@ -1,5 +1,6 @@
 namespace Terminal.Gui;
 
+// TODO: FrameView is mis-named, really. It's far more about it being a TabGroup than a frame. 
 /// <summary>
 ///     The FrameView is a container View with a border around it. 
 /// </summary>
@@ -23,7 +24,8 @@ public class FrameView : View
 
     private void FrameView_MouseClick (object sender, MouseEventEventArgs e)
     {
-        e.Handled = InvokeCommand (Command.HotKey) == true;
+        // base sets focus on HotKey
+        e.Handled = InvokeCommand (Command.HotKey, ctx: new (Command.HotKey, key: null, data: this)) == true;
     }
 
 

+ 49 - 11
Terminal.Gui/Views/HexView.cs

@@ -25,7 +25,7 @@ namespace Terminal.Gui;
 ///     </para>
 ///     <para>Control the first byte shown by setting the <see cref="DisplayStart"/> property to an offset in the stream.</para>
 /// </remarks>
-public class HexView : View
+public class HexView : View, IDesignable
 {
     private const int bsize = 4;
     private const int displayWidth = 9;
@@ -33,7 +33,8 @@ public class HexView : View
     private int bpl;
     private long displayStart, pos;
     private SortedDictionary<long, byte> edits = [];
-    private bool firstNibble, leftSide;
+    private bool firstNibble;
+    private bool leftSide;
     private Stream source;
     private static readonly Rune SpaceCharRune = new (' ');
     private static readonly Rune PeriodCharRune = new ('.');
@@ -46,8 +47,7 @@ public class HexView : View
     public HexView (Stream source)
     {
         Source = source;
-        // BUG: This will always call the most-derived definition of CanFocus.
-        // Either seal it or don't set it here.
+
         CanFocus = true;
         CursorVisibility = CursorVisibility.Default;
         leftSide = true;
@@ -61,7 +61,8 @@ public class HexView : View
         AddCommand (Command.Right, () => MoveRight ());
         AddCommand (Command.Down, () => MoveDown (bytesPerLine));
         AddCommand (Command.Up, () => MoveUp (bytesPerLine));
-        AddCommand (Command.Accept, () => ToggleSide ());
+        AddCommand (Command.Tab, () => Navigate (NavigationDirection.Forward));
+        AddCommand (Command.BackTab, () => Navigate (NavigationDirection.Backward));
         AddCommand (Command.PageUp, () => MoveUp (bytesPerLine * Frame.Height));
         AddCommand (Command.PageDown, () => MoveDown (bytesPerLine * Frame.Height));
         AddCommand (Command.Start, () => MoveHome ());
@@ -80,7 +81,6 @@ public class HexView : View
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.CursorDown, Command.Down);
         KeyBindings.Add (Key.CursorUp, Command.Up);
-        KeyBindings.Add (Key.Enter, Command.Accept);
 
         KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
         KeyBindings.Add (Key.PageUp, Command.PageUp);
@@ -95,6 +95,9 @@ public class HexView : View
         KeyBindings.Add (Key.CursorUp.WithCtrl, Command.StartOfPage);
         KeyBindings.Add (Key.CursorDown.WithCtrl, Command.EndOfPage);
 
+        KeyBindings.Add (Key.Tab, Command.Tab);
+        KeyBindings.Add (Key.Tab.WithShift, Command.BackTab);
+
         LayoutComplete += HexView_LayoutComplete;
     }
 
@@ -247,7 +250,7 @@ public class HexView : View
     public event EventHandler<HexViewEditEventArgs> Edited;
 
     /// <inheritdoc/>
-    protected internal override bool OnMouseEvent  (MouseEvent me)
+    protected internal override bool OnMouseEvent (MouseEvent me)
     {
         if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
             && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
@@ -529,12 +532,14 @@ public class HexView : View
 
         int x = displayWidth + block * 14 + column + (firstNibble ? 0 : 1);
         int y = line;
+
         if (!leftSide)
         {
             x = displayWidth + bytesPerLine / bsize * 14 + item - 1;
         }
 
         Move (x, y);
+
         return new (x, y);
     }
 
@@ -764,17 +769,50 @@ public class HexView : View
         {
             return;
         }
+
         var delta = (int)(pos - DisplayStart);
         int line = delta / bytesPerLine;
 
         SetNeedsDisplay (new (0, line, Viewport.Width, 1));
     }
 
-    private bool ToggleSide ()
+    private bool Navigate (NavigationDirection direction)
     {
-        leftSide = !leftSide;
-        RedisplayLine (position);
-        firstNibble = true;
+        switch (direction)
+        {
+            case NavigationDirection.Forward:
+                if (leftSide)
+                {
+                    leftSide = false;
+                    RedisplayLine (position);
+                    firstNibble = true;
+
+                    return true;
+                }
+
+                break;
+
+            case NavigationDirection.Backward:
+                if (!leftSide)
+                {
+                    leftSide = true;
+                    RedisplayLine (position);
+                    firstNibble = true;
+                    return true;
+                }
+
+
+                break;
+        }
+
+        return false;
+    }
+
+
+    /// <inheritdoc />
+    bool IDesignable.EnableForDesign ()
+    {
+        Source = new MemoryStream (Encoding.UTF8.GetBytes ("HexEditor Unicode that shouldn't 𝔹Aℝ𝔽!"));
 
         return true;
     }

+ 44 - 17
Terminal.Gui/Views/Label.cs

@@ -1,14 +1,22 @@
 namespace Terminal.Gui;
 
 /// <summary>
-///     The Label <see cref="View"/> displays a string at a given position and supports multiple lines separated by
-///     newline characters. Multi-line Labels support word wrap.
+///     The Label <see cref="View"/> displays text that describes the View next in the <see cref="View.Subviews"/>. When
+///     Label
+///     recieves a <see cref="Command.HotKey"/> command it will pass it to the next <see cref="View"/> in
+///     <see cref="View.Subviews"/>.
 /// </summary>
 /// <remarks>
-///     The <see cref="Label"/> view is functionality identical to <see cref="View"/> and is included for API
-///     backwards compatibility.
+///     <para>
+///         Title and Text are the same property. When Title is set Text s also set. When Text is set Title is also set.
+///     </para>
+///     <para>
+///         If <see cref="View.CanFocus"/> is <see langword="false"/> and the use clicks on the Label,
+///         the <see cref="Command.HotKey"/> will be invoked on the next <see cref="View"/> in
+///         <see cref="View.Subviews"/>."
+///     </para>
 /// </remarks>
-public class Label : View
+public class Label : View, IDesignable
 {
     /// <inheritdoc/>
     public Label ()
@@ -16,21 +24,19 @@ public class Label : View
         Height = Dim.Auto (DimAutoStyle.Text);
         Width = Dim.Auto (DimAutoStyle.Text);
 
-        // Things this view knows how to do
-        AddCommand (Command.HotKey, FocusNext);
-
-        // Default key bindings for this view
-        KeyBindings.Add (Key.Space, Command.Accept);
+        // On HoKey, pass it to the next view
+        AddCommand (Command.HotKey, InvokeHotKeyOnNext);
 
         TitleChanged += Label_TitleChanged;
         MouseClick += Label_MouseClick;
     }
 
+    // TODO: base raises Select, but we want to raise HotKey. This can be simplified?
     private void Label_MouseClick (object sender, MouseEventEventArgs e)
     {
         if (!CanFocus)
         {
-            e.Handled = InvokeCommand (Command.HotKey) == true;
+            e.Handled = InvokeCommand (Command.HotKey, ctx: new (Command.HotKey, key: null, data: this)) == true;
         }
     }
 
@@ -40,28 +46,49 @@ public class Label : View
         TextFormatter.HotKeySpecifier = HotKeySpecifier;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override string Text
     {
-        get => base.Title;
-        set => base.Text = base.Title = value;
+        get => Title;
+        set => base.Text = Title = value;
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public override Rune HotKeySpecifier
     {
         get => base.HotKeySpecifier;
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
     }
 
-    private bool? FocusNext ()
+    private bool? InvokeHotKeyOnNext (CommandContext context)
     {
+        if (RaiseHandlingHotKey () == true)
+        {
+            return true;
+        }
+
+        if (CanFocus)
+        {
+            SetFocus ();
+
+            return true;
+        }
+
         int me = SuperView?.Subviews.IndexOf (this) ?? -1;
+
         if (me != -1 && me < SuperView?.Subviews.Count - 1)
         {
-            SuperView?.Subviews [me + 1].SetFocus ();
+            return SuperView?.Subviews [me + 1].InvokeCommand (Command.HotKey, context.Key, context.KeyBinding) == true;
         }
 
+        return false;
+    }
+
+    /// <inheritdoc/>
+    bool IDesignable.EnableForDesign ()
+    {
+        Text = "_Label";
+
         return true;
     }
 }

+ 71 - 37
Terminal.Gui/Views/ListView.cs

@@ -6,7 +6,7 @@ using static Terminal.Gui.SpinnerStyle;
 namespace Terminal.Gui;
 
 /// <summary>Implement <see cref="IListDataSource"/> to provide custom rendering for a <see cref="ListView"/>.</summary>
-public interface IListDataSource: IDisposable
+public interface IListDataSource : IDisposable
 {
     /// <summary>
     /// Event to raise when an item is added, removed, or moved, or the entire list is refreshed.
@@ -123,23 +123,73 @@ public class ListView : View, IDesignable
 
         // Things this view knows how to do
         // 
-        // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
+        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
         AddCommand (Command.Up, () => MoveUp ());
-        // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
+        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
         AddCommand (Command.Down, () => MoveDown ());
+
         AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
         AddCommand (Command.ScrollDown, () => ScrollVertical (1));
         AddCommand (Command.PageUp, () => MovePageUp ());
         AddCommand (Command.PageDown, () => MovePageDown ());
         AddCommand (Command.Start, () => MoveHome ());
         AddCommand (Command.End, () => MoveEnd ());
-        AddCommand (Command.Accept, () => OnOpenSelectedItem ());
-        AddCommand (Command.Open, () => OnOpenSelectedItem ());
-        AddCommand (Command.Select, () => MarkUnmarkRow ());
-
         AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
         AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
 
+        // Accept (Enter key) - Raise Accept event - DO NOT advance state
+        AddCommand (Command.Accept, (ctx) =>
+                                    {
+                                        if (RaiseAccepting (ctx) == true)
+                                        {
+                                            return true;
+                                        }
+
+                                        if (OnOpenSelectedItem ())
+                                        {
+                                                return true;
+                                        }
+
+                                        return false;
+                                    });
+
+        // Select (Space key and single-click) - If markable, change mark and raise Select event
+        AddCommand (Command.Select, (ctx) =>
+                                    {
+                                        if (_allowsMarking)
+                                        {
+                                            if (RaiseSelecting (ctx) == true)
+                                            {
+                                                return true;
+                                            }
+
+                                            if (MarkUnmarkSelectedItem ())
+                                            {
+                                                return true;
+                                            }
+                                        }
+
+                                        return false;
+                                    });
+
+
+        // Hotkey - If none set, select and raise Select event. SetFocus. - DO NOT raise Accept
+        AddCommand (Command.HotKey, (ctx) =>
+                                    {
+                                        if (SelectedItem == -1)
+                                        {
+                                            SelectedItem = 0;
+                                            if (RaiseSelecting (ctx) == true)
+                                            {
+                                                return true;
+
+                                            }
+                                        }
+
+                                        return !SetFocus ();
+                                    });
+
+
         // Default keybindings for all ListViews
         KeyBindings.Add (Key.CursorUp, Command.Up);
         KeyBindings.Add (Key.P.WithCtrl, Command.Up);
@@ -155,15 +205,13 @@ public class ListView : View, IDesignable
         KeyBindings.Add (Key.Home, Command.Start);
 
         KeyBindings.Add (Key.End, Command.End);
-
-        KeyBindings.Add (Key.Enter, Command.Open);
     }
 
     /// <summary>Gets or sets whether this <see cref="ListView"/> allows items to be marked.</summary>
     /// <value>Set to <see langword="true"/> to allow marking elements of the list.</value>
     /// <remarks>
     ///     If set to <see langword="true"/>, <see cref="ListView"/> will render items marked items with "[x]", and
-    ///     unmarked items with "[ ]" spaces. SPACE key will toggle marking. The default is <see langword="false"/>.
+    ///     unmarked items with "[ ]". SPACE key will toggle marking. The default is <see langword="false"/>.
     /// </remarks>
     public bool AllowsMarking
     {
@@ -171,16 +219,6 @@ public class ListView : View, IDesignable
         set
         {
             _allowsMarking = value;
-
-            if (_allowsMarking)
-            {
-                KeyBindings.Add (Key.Space, Command.Select);
-            }
-            else
-            {
-                KeyBindings.Remove (Key.Space);
-            }
-
             SetNeedsDisplay ();
         }
     }
@@ -334,10 +372,10 @@ public class ListView : View, IDesignable
 
     /// <summary>
     ///     If <see cref="AllowsMarking"/> and <see cref="AllowsMultipleSelection"/> are both <see langword="true"/>,
-    ///     unmarks all marked items other than the currently selected.
+    ///     unmarks all marked items other than <see cref="SelectedItem"/>.
     /// </summary>
     /// <returns><see langword="true"/> if unmarking was successful.</returns>
-    public virtual bool AllowsAll ()
+    public bool UnmarkAllButSelected ()
     {
         if (!_allowsMarking)
         {
@@ -385,16 +423,18 @@ public class ListView : View, IDesignable
 
     /// <summary>Marks the <see cref="SelectedItem"/> if it is not already marked.</summary>
     /// <returns><see langword="true"/> if the <see cref="SelectedItem"/> was marked.</returns>
-    public virtual bool MarkUnmarkRow ()
+    public bool MarkUnmarkSelectedItem ()
     {
-        if (AllowsAll ())
+        if (UnmarkAllButSelected ())
         {
             Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
             SetNeedsDisplay ();
 
-            return true;
+            return Source.IsMarked (SelectedItem);
         }
 
+        // BUGBUG: Shouldn't this retrn Source.IsMarked (SelectedItem)
+
         return false;
     }
 
@@ -458,12 +498,9 @@ public class ListView : View, IDesignable
 
         _selected = Viewport.Y + me.Position.Y;
 
-        if (AllowsAll ())
+        if (MarkUnmarkSelectedItem ())
         {
-            Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
-            SetNeedsDisplay ();
-
-            return true;
+            // return true;
         }
 
         OnSelectedChanged ();
@@ -471,7 +508,7 @@ public class ListView : View, IDesignable
 
         if (me.Flags == MouseFlags.Button1DoubleClicked)
         {
-            OnOpenSelectedItem ();
+            return InvokeCommand (Command.Accept) is true;
         }
 
         return true;
@@ -759,13 +796,9 @@ public class ListView : View, IDesignable
 
         object value = _source.ToList () [_selected];
 
-        // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired.
-        if (OnAccept () == true)
-        {
-            return true;
-        }
-
         OpenSelectedItem?.Invoke (this, new ListViewItemEventArgs (_selected, value));
+
+        // BUGBUG: this should not blindly return true.
         return true;
     }
 
@@ -794,6 +827,7 @@ public class ListView : View, IDesignable
     /// <param name="rowEventArgs"></param>
     public virtual void OnRowRender (ListViewRowEventArgs rowEventArgs) { RowRender?.Invoke (this, rowEventArgs); }
 
+    // TODO: Use standard event model
     /// <summary>Invokes the <see cref="SelectedItemChanged"/> event if it is defined.</summary>
     /// <returns></returns>
     public virtual bool OnSelectedChanged ()

+ 5 - 6
Terminal.Gui/Views/Menu/Menu.cs

@@ -195,7 +195,6 @@ internal sealed class Menu : View
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.Esc, Command.Cancel);
-        KeyBindings.Add (Key.Enter, Command.Accept);
     }
 
     private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem)
@@ -330,7 +329,7 @@ internal sealed class Menu : View
     }
 
     /// <inheritdoc/>
-    public override void OnVisibleChanged ()
+    protected override void OnVisibleChanged ()
     {
         base.OnVisibleChanged ();
 
@@ -386,7 +385,7 @@ internal sealed class Menu : View
             return GetFocusColor ();
         }
 
-        return !item.IsEnabled () ? ColorScheme.Disabled : GetNormalColor ();
+        return !item.IsEnabled () ? ColorScheme!.Disabled : GetNormalColor ();
     }
 
     public override void OnDrawContent (Rectangle viewport)
@@ -518,7 +517,7 @@ internal sealed class Menu : View
 
                 if (!item.IsEnabled ())
                 {
-                    DrawHotString (textToDraw, ColorScheme.Disabled, ColorScheme.Disabled);
+                    DrawHotString (textToDraw, ColorScheme!.Disabled, ColorScheme.Disabled);
                 }
                 else if (i == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { })
                 {
@@ -533,7 +532,7 @@ internal sealed class Menu : View
                     tf.Draw (
                              ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)),
                              i == _currentChild ? GetFocusColor () : GetNormalColor (),
-                             i == _currentChild ? ColorScheme.HotFocus : ColorScheme.HotNormal,
+                             i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
                              SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
                             );
                 }
@@ -541,7 +540,7 @@ internal sealed class Menu : View
                 {
                     DrawHotString (
                                    textToDraw,
-                                   i == _currentChild ? ColorScheme.HotFocus : ColorScheme.HotNormal,
+                                   i == _currentChild ? ColorScheme!.HotFocus : ColorScheme!.HotNormal,
                                    i == _currentChild ? GetFocusColor () : GetNormalColor ()
                                   );
                 }

+ 43 - 9
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -119,11 +119,14 @@ public class MenuBar : View, IDesignable
 
         AddCommand (
                     Command.Accept,
-                    () =>
+                    (ctx) =>
                     {
-                        ProcessMenu (_selected, Menus [_selected]);
+                        if (Menus.Length > 0)
+                        {
+                            ProcessMenu (_selected, Menus [_selected]);
+                        }
 
-                        return true;
+                        return RaiseAccepting (ctx);
                     }
                    );
         AddCommand (Command.Toggle, ctx =>
@@ -134,7 +137,12 @@ public class MenuBar : View, IDesignable
                                                   });
         AddCommand (Command.Select, ctx =>
                                     {
-                                        var res =  Run ((ctx.KeyBinding?.Context as MenuItem)?.Action!);
+                                        if (ctx.Data is MouseEvent)
+                                        {
+                                            // HACK: Work around the fact that View.MouseClick always invokes Select
+                                            return false;
+                                        }
+                                        var res = Run ((ctx.KeyBinding?.Context as MenuItem)?.Action!);
                                         CloseAllMenus ();
 
                                         return res;
@@ -145,7 +153,6 @@ public class MenuBar : View, IDesignable
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.Esc, Command.Cancel);
         KeyBindings.Add (Key.CursorDown, Command.Accept);
-        KeyBindings.Add (Key.Enter, Command.Accept);
 
         KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
         KeyBindings.Add (Key, keyBinding);
@@ -593,6 +600,10 @@ public class MenuBar : View, IDesignable
                     _previousFocused.SetFocus ();
                 }
 
+                if (Application.MouseGrabView == _openMenu)
+                {
+                    Application.UngrabMouse();
+                }
                 _openMenu?.Dispose ();
                 _openMenu = null;
 
@@ -619,6 +630,10 @@ public class MenuBar : View, IDesignable
                     if (OpenCurrentMenu is { })
                     {
                         Application.Top?.Remove (OpenCurrentMenu);
+                        if (Application.MouseGrabView == OpenCurrentMenu)
+                        {
+                            Application.UngrabMouse ();
+                        }
                         OpenCurrentMenu.Dispose ();
                         OpenCurrentMenu = null;
                     }
@@ -803,6 +818,10 @@ public class MenuBar : View, IDesignable
                 if (_openMenu is { })
                 {
                     Application.Top?.Remove (_openMenu);
+                    if (Application.MouseGrabView == _openMenu)
+                    {
+                        Application.UngrabMouse ();
+                    }
                     _openMenu.Dispose ();
                     _openMenu = null;
                 }
@@ -990,6 +1009,10 @@ public class MenuBar : View, IDesignable
             foreach (Menu item in _openSubMenu)
             {
                 Application.Top!.Remove (item);
+                if (Application.MouseGrabView == item)
+                {
+                    Application.UngrabMouse ();
+                }
                 item.Dispose ();
             }
         }
@@ -1154,11 +1177,11 @@ public class MenuBar : View, IDesignable
         SetNeedsDisplay ();
     }
 
-    private void ProcessMenu (int i, MenuBarItem mi)
+    private bool ProcessMenu (int i, MenuBarItem mi)
     {
         if (_selected < 0 && IsMenuOpen)
         {
-            return;
+            return false;
         }
 
         if (mi.IsTopLevel)
@@ -1166,6 +1189,10 @@ public class MenuBar : View, IDesignable
             Point screen = ViewportToScreen (new Point (0, i));
             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
             menu.Run (mi.Action);
+            if (Application.MouseGrabView == menu)
+            {
+                Application.UngrabMouse ();
+            }
             menu.Dispose ();
         }
         else
@@ -1181,16 +1208,18 @@ public class MenuBar : View, IDesignable
                                    )
                 && !CloseMenu ())
             {
-                return;
+                return true;
             }
 
             if (!OpenCurrentMenu.CheckSubMenu ())
             {
-                return;
+                return true;
             }
         }
 
         SetNeedsDisplay ();
+
+        return true;
     }
 
     private void RemoveSubMenu (int index, bool ignoreUseSubMenusSingleFrame = false)
@@ -1409,6 +1438,11 @@ public class MenuBar : View, IDesignable
                             Point screen = ViewportToScreen (new Point (0, i));
                             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
                             menu.Run (Menus [i].Action);
+                            if (Application.MouseGrabView == menu)
+                            {
+                                Application.UngrabMouse ();
+                            }
+
                             menu.Dispose ();
                         }
                         else if (!IsMenuOpen)

+ 26 - 12
Terminal.Gui/Views/Menuv2.cs

@@ -17,13 +17,30 @@ public class Menuv2 : Bar
         Orientation = Orientation.Vertical;
         Width = Dim.Auto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
-        ColorScheme = Colors.ColorSchemes ["Menu"];
         Initialized += Menuv2_Initialized;
+        VisibleChanged += OnVisibleChanged;
+    }
+
+    private void OnVisibleChanged (object sender, EventArgs e)
+    {
+        if (Visible)
+        {
+            //Application.GrabMouse(this);
+        }
+        else
+        {
+            if (Application.MouseGrabView == this)
+            {
+                //Application.UngrabMouse ();
+            }
+        }
     }
 
     private void Menuv2_Initialized (object sender, EventArgs e)
     {
         Border.Thickness = new Thickness (1, 1, 1, 1);
+        Border.LineStyle = LineStyle.Single;
+        ColorScheme = Colors.ColorSchemes ["Menu"];
     }
 
     // Menuv2 arranges the items horizontally.
@@ -52,7 +69,6 @@ public class Menuv2 : Bar
         if (view is Shortcut shortcut)
         {
             shortcut.CanFocus = true;
-            shortcut.KeyBindingScope = KeyBindingScope.Application;
             shortcut.Orientation = Orientation.Vertical;
             shortcut.HighlightStyle |= HighlightStyle.Hover;
 
@@ -60,24 +76,22 @@ public class Menuv2 : Bar
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             //shortcut.AlignmentModes = AlignmentModes.EndToStart;
 
-            shortcut.Accept += ShortcutOnAccept;
+            shortcut.Accepting += ShortcutOnAccept;
 
-            void ShortcutOnAccept (object sender, HandledEventArgs e)
+            void ShortcutOnAccept (object sender, CommandEventArgs e)
             {
-                if (Arrangement.HasFlag(ViewArrangement.Overlapped) && Visible)
+                if (Arrangement.HasFlag (ViewArrangement.Overlapped) && Visible)
                 {
                     Visible = false;
-                    e.Handled = true;
+                    e.Cancel = true;
 
                     return;
-
-                    //Enabled = Visible;
                 }
 
-                if (!e.Handled)
-                {
-                    OnAccept ();
-                }
+                //if (!e.Handled)
+                //{
+                //    RaiseAcceptEvent ();
+                //}
             }
         }
 

+ 18 - 16
Terminal.Gui/Views/MessageBox.cs

@@ -338,6 +338,7 @@ public static class MessageBox
         // Create button array for Dialog
         var count = 0;
         List<Button> buttonList = new ();
+        Clicked = -1;
 
         if (buttons is { })
         {
@@ -351,11 +352,27 @@ public static class MessageBox
                 var b = new Button
                 {
                     Text = s,
+                    Data = count,
                 };
 
                 if (count == defaultButton)
                 {
                     b.IsDefault = true;
+                    b.Accepting += (s, e) =>
+                                   {
+                                       // TODO: With https://github.com/gui-cs/Terminal.Gui/issues/3778 we can simplify this
+                                       if (e.Context.Data is Button button)
+                                       {
+                                           Clicked = (int)button.Data!;
+                                       } 
+                                       else if (e.Context.KeyBinding?.BoundView is Button btn)
+                                       {
+                                           Clicked = (int)btn.Data!;
+                                       }
+
+                                       e.Cancel = true;
+                                       Application.RequestStop ();
+                                   };
                 }
 
                 buttonList.Add (b);
@@ -373,7 +390,7 @@ public static class MessageBox
         };
 
         d.Width = Dim.Auto (DimAutoStyle.Auto,
-                            minimumContentDim: Dim.Func (() => (int)((Application.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * (DefaultMinimumWidth / 100f) )),
+                            minimumContentDim: Dim.Func (() => (int)((Application.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * (DefaultMinimumWidth / 100f))),
                             maximumContentDim: Dim.Func (() => (int)((Application.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * 0.9f)));
 
         d.Height = Dim.Auto (DimAutoStyle.Auto,
@@ -400,21 +417,6 @@ public static class MessageBox
         d.TextFormatter.WordWrap = wrapMessage;
         d.TextFormatter.MultiLine = !wrapMessage;
 
-        // Setup actions
-        Clicked = -1;
-
-        for (var n = 0; n < buttonList.Count; n++)
-        {
-            int buttonId = n;
-            Button b = buttonList [n];
-
-            b.Accept += (s, e) =>
-                         {
-                             Clicked = buttonId;
-                             Application.RequestStop ();
-                         };
-        }
-
         // Run the modal; do not shut down the mainloop driver when done
         Application.Run (d);
         d.Dispose ();

+ 12 - 4
Terminal.Gui/Views/NumericUpDown.cs

@@ -86,8 +86,8 @@ public class NumericUpDown<T> : View where T : notnull
 
         CanFocus = true;
 
-        _down.Accept += OnDownButtonOnAccept;
-        _up.Accept += OnUpButtonOnAccept;
+        _down.Accepting += OnDownButtonOnAccept;
+        _up.Accepting += OnUpButtonOnAccept;
 
         Add (_down, _number, _up);
 
@@ -133,9 +133,17 @@ public class NumericUpDown<T> : View where T : notnull
 
         return;
 
-        void OnDownButtonOnAccept (object? s, HandledEventArgs e) { InvokeCommand (Command.ScrollDown); }
+        void OnDownButtonOnAccept (object? s, CommandEventArgs e)
+        {
+            InvokeCommand (Command.ScrollDown);
+            e.Cancel = true;
+        }
 
-        void OnUpButtonOnAccept (object? s, HandledEventArgs e) { InvokeCommand (Command.ScrollUp); }
+        void OnUpButtonOnAccept (object? s, CommandEventArgs e)
+        {
+            InvokeCommand (Command.ScrollUp);
+            e.Cancel = true;
+        }
     }
 
     private T _value = default!;

+ 242 - 107
Terminal.Gui/Views/RadioGroup.cs

@@ -1,15 +1,11 @@
-namespace Terminal.Gui;
+#nullable enable
+using System.Diagnostics;
 
-/// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
+namespace Terminal.Gui;
+
+/// <summary>Displays a group of labels with an idicator of which one is selected.</summary>
 public class RadioGroup : View, IDesignable, IOrientation
 {
-    private int _cursor;
-    private List<(int pos, int length)> _horizontal;
-    private int _horizontalSpace = 2;
-    private List<string> _radioLabels = [];
-    private int _selected;
-    private readonly OrientationHelper _orientationHelper;
-
     /// <summary>
     ///     Initializes a new instance of the <see cref="RadioGroup"/> class.
     /// </summary>
@@ -20,7 +16,101 @@ public class RadioGroup : View, IDesignable, IOrientation
         Width = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
 
-        // Things this view knows how to do
+
+        // Select (Space key or mouse click) - The default implementation sets focus. RadioGroup does not.
+        AddCommand (
+                    Command.Select,
+                    (ctx) =>
+                    {
+                        bool cursorChanged = false;
+                        if (SelectedItem == Cursor)
+                        {
+                            cursorChanged = MoveDownRight ();
+                            if (!cursorChanged)
+                            {
+                                cursorChanged = MoveHome ();
+                            }
+                        }
+
+                        bool selectedItemChanged = false;
+                        if (SelectedItem != Cursor)
+                        {
+                            selectedItemChanged = ChangeSelectedItem (Cursor);
+                        }
+
+                        if (cursorChanged || selectedItemChanged)
+                        {
+                            if (RaiseSelecting (ctx) == true)
+                            {
+                                return true;
+                            }
+                        }
+
+                        return cursorChanged || selectedItemChanged;
+                    });
+
+        // Accept (Enter key) - Raise Accept event - DO NOT advance state
+        AddCommand (Command.Accept, RaiseAccepting);
+
+        // Hotkey - ctx may indicate a radio item hotkey was pressed. Beahvior depends on HasFocus
+        //          If HasFocus and it's this.HotKey invoke Select command - DO NOT raise Accept
+        //          If it's a radio item HotKey select that item and raise Seelcted event - DO NOT raise Accept
+        //          If nothing is selected, select first and raise Selected event - DO NOT raise Accept
+        AddCommand (Command.HotKey,
+                    ctx =>
+                            {
+                                var item = ctx.KeyBinding?.Context as int?;
+
+                                if (HasFocus)
+                                {
+                                    if (ctx is { KeyBinding: { } } && (ctx.KeyBinding.Value.BoundView != this || HotKey == ctx.Key?.NoAlt.NoCtrl.NoShift))
+                                    {
+                                        // It's this.HotKey OR Another View (Label?) forwarded the hotkey command to us - Act just like `Space` (Select)
+                                        return InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+                                    }
+                                }
+
+                                if (item is { } && item < _radioLabels.Count)
+                                {
+                                    if (item.Value == SelectedItem)
+                                    {
+                                        return true;
+                                    }
+
+                                    // If a RadioItem.HotKey is pressed we always set the selected item - never SetFocus
+                                    bool selectedItemChanged = ChangeSelectedItem (item.Value);
+
+                                    if (selectedItemChanged)
+                                    {
+                                        // Doesn't matter if it's handled
+                                        RaiseSelecting (ctx);
+                                        return true;
+                                    }
+
+
+                                    return false;
+                                }
+
+                                if (SelectedItem == -1 && ChangeSelectedItem (0))
+                                {
+                                    if (RaiseSelecting (ctx) == true)
+                                    {
+                                        return true;
+                                    }
+                                    return false;
+                                }
+
+                                if (RaiseHandlingHotKey () == true)
+                                {
+                                    return true;
+                                };
+
+                                // Default Command.Hotkey sets focus
+                                SetFocus ();
+
+                                return true;
+                            });
+
         AddCommand (
                     Command.Up,
                     () =>
@@ -42,6 +132,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                         {
                             return false;
                         }
+
                         return MoveDownRight ();
                     }
                    );
@@ -76,32 +167,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                     }
                    );
 
-        AddCommand (
-                    Command.Accept,
-                    () =>
-                    {
-                        SelectedItem = _cursor;
-
-                        return OnAccept () is true or null;
-                    }
-                   );
-
-        AddCommand (
-                    Command.HotKey,
-                    ctx =>
-                    {
-                        SetFocus ();
-
-                        if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count)
-                        {
-                            SelectedItem = (int)ctx.KeyBinding?.Context!;
-
-                            return OnAccept () is true or null;
-                        }
-
-                        return true;
-                    });
-
+        // ReSharper disable once UseObjectOrCollectionInitializer
         _orientationHelper = new (this);
         _orientationHelper.Orientation = Orientation.Vertical;
         _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
@@ -120,54 +186,85 @@ public class RadioGroup : View, IDesignable, IOrientation
 
     private void SetupKeyBindings ()
     {
-        KeyBindings.Clear ();
-
         // Default keybindings for this view
         if (Orientation == Orientation.Vertical)
         {
+            KeyBindings.Remove (Key.CursorUp);
             KeyBindings.Add (Key.CursorUp, Command.Up);
+            KeyBindings.Remove (Key.CursorDown);
             KeyBindings.Add (Key.CursorDown, Command.Down);
         }
         else
         {
+            KeyBindings.Remove (Key.CursorLeft);
             KeyBindings.Add (Key.CursorLeft, Command.Up);
+            KeyBindings.Remove (Key.CursorRight);
             KeyBindings.Add (Key.CursorRight, Command.Down);
         }
 
+        KeyBindings.Remove (Key.Home);
         KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Remove (Key.End);
         KeyBindings.Add (Key.End, Command.End);
-        KeyBindings.Add (Key.Space, Command.Accept);
     }
 
-    private void RadioGroup_MouseClick (object sender, MouseEventEventArgs e)
+    /// <summary>
+    ///     Gets or sets whether double clicking on a Radio Item will cause the <see cref="View.Accepting"/> event to be raised.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         If <see langword="false"/> and Accept is not handled, the Accept event on the <see cref="View.SuperView"/> will
+    ///         be raised. The default is
+    ///         <see langword="true"/>.
+    ///     </para>
+    /// </remarks>
+    public bool DoubleClickAccepts { get; set; } = true;
+
+    private void RadioGroup_MouseClick (object? sender, MouseEventEventArgs e)
     {
-        SetFocus ();
+        if (e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
+        {
+            int viewportX = e.MouseEvent.Position.X;
+            int viewportY = e.MouseEvent.Position.Y;
+
+            int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY;
+
+            int rCount = Orientation == Orientation.Horizontal
+                             ? _horizontal!.Last ().pos + _horizontal!.Last ().length
+                             : _radioLabels.Count;
 
-        int viewportX = e.MouseEvent.Position.X;
-        int viewportY = e.MouseEvent.Position.Y;
+            if (pos < rCount)
+            {
+                int c = Orientation == Orientation.Horizontal
+                            ? _horizontal!.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX)
+                            : viewportY;
 
-        int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY;
+                if (c > -1)
+                {
+                    // Just like the user pressing the items' hotkey
+                    e.Handled = InvokeCommand (Command.HotKey, null, new KeyBinding ([Command.HotKey], KeyBindingScope.HotKey, boundView: this, context: c)) == true;
+                }
+            }
 
-        int rCount = Orientation == Orientation.Horizontal
-                         ? _horizontal.Last ().pos + _horizontal.Last ().length
-                         : _radioLabels.Count;
+            return;
+        }
 
-        if (pos < rCount)
+        if (DoubleClickAccepts && e.MouseEvent.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
         {
-            int c = Orientation == Orientation.Horizontal
-                        ? _horizontal.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX)
-                        : viewportY;
+            // NOTE: Drivers ALWAYS generate a Button1Clicked event before Button1DoubleClicked
+            // NOTE: So, we've already selected an item.
 
-            if (c > -1)
-            {
-                _cursor = SelectedItem = c;
-                SetNeedsDisplay ();
-            }
+            // Just like the user pressing `Enter`
+            InvokeCommand (Command.Accept);
         }
 
+        // HACK: Always eat so Select is not invoked by base
         e.Handled = true;
     }
 
+    private List<(int pos, int length)>? _horizontal;
+    private int _horizontalSpace = 2;
+
     /// <summary>
     ///     Gets or sets the horizontal space for this <see cref="RadioGroup"/> if the <see cref="Orientation"/> is
     ///     <see cref="Orientation.Horizontal"/>
@@ -186,8 +283,11 @@ public class RadioGroup : View, IDesignable, IOrientation
         }
     }
 
+    private List<string> _radioLabels = [];
+
     /// <summary>
-    ///     The radio labels to display. A key binding will be added for each radio enabling the user to select
+    ///     The radio labels to display. A <see cref="Command.HotKey"/> key binding will be added for each label enabling the
+    ///     user to select
     ///     and/or focus the radio label using the keyboard. See <see cref="View.HotKey"/> for details on how HotKeys work.
     /// </summary>
     /// <value>The radio labels.</value>
@@ -223,17 +323,40 @@ public class RadioGroup : View, IDesignable, IOrientation
         }
     }
 
-    /// <summary>The currently selected item from the list of radio labels</summary>
-    /// <value>The selected.</value>
+    private int _selected;
+
+    /// <summary>Gets or sets the selected radio label index.</summary>
+    /// <value>The index. -1 if no item is selected.</value>
     public int SelectedItem
     {
         get => _selected;
-        set
+        set => ChangeSelectedItem (value);
+    }
+
+    /// <summary>
+    ///     INTERNAL Sets the selected item.
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns>
+    ///     <see langword="true"/> if the selected item changed.
+    /// </returns>
+    private bool ChangeSelectedItem (int value)
+    {
+        if (_selected == value || value > _radioLabels.Count - 1)
         {
-            OnSelectedItemChanged (value, SelectedItem);
-            _cursor = Math.Max (_selected, 0);
-            SetNeedsDisplay ();
+            return false;
         }
+
+        int savedSelected = _selected;
+        _selected = value;
+        Cursor = Math.Max (_selected, 0);
+
+        OnSelectedItemChanged (value, SelectedItem);
+        SelectedItemChanged?.Invoke (this, new (SelectedItem, savedSelected));
+
+        SetNeedsDisplay ();
+
+        return true;
     }
 
     /// <inheritdoc/>
@@ -252,7 +375,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
                     break;
                 case Orientation.Horizontal:
-                    Move (_horizontal [i].pos, 0);
+                    Move (_horizontal! [i].pos, 0);
 
                     break;
             }
@@ -270,19 +393,19 @@ public class RadioGroup : View, IDesignable, IOrientation
                 {
                     Rune rune = rlRunes [j];
 
-                    if (j == hotPos && i == _cursor)
+                    if (j == hotPos && i == Cursor)
                     {
                         Application.Driver?.SetAttribute (
-                                                         HasFocus
-                                                             ? ColorScheme.HotFocus
-                                                             : GetHotNormalColor ()
-                                                        );
+                                                          HasFocus
+                                                              ? ColorScheme!.HotFocus
+                                                              : GetHotNormalColor ()
+                                                         );
                     }
-                    else if (j == hotPos && i != _cursor)
+                    else if (j == hotPos && i != Cursor)
                     {
                         Application.Driver?.SetAttribute (GetHotNormalColor ());
                     }
-                    else if (HasFocus && i == _cursor)
+                    else if (HasFocus && i == Cursor)
                     {
                         Application.Driver?.SetAttribute (GetFocusColor ());
                     }
@@ -292,15 +415,15 @@ public class RadioGroup : View, IDesignable, IOrientation
                         j++;
                         rune = rlRunes [j];
 
-                        if (i == _cursor)
+                        if (i == Cursor)
                         {
                             Application.Driver?.SetAttribute (
-                                                             HasFocus
-                                                                 ? ColorScheme.HotFocus
-                                                                 : GetHotNormalColor ()
-                                                            );
+                                                              HasFocus
+                                                                  ? ColorScheme!.HotFocus
+                                                                  : GetHotNormalColor ()
+                                                             );
                         }
-                        else if (i != _cursor)
+                        else if (i != Cursor)
                         {
                             Application.Driver?.SetAttribute (GetHotNormalColor ());
                         }
@@ -312,11 +435,13 @@ public class RadioGroup : View, IDesignable, IOrientation
             }
             else
             {
-                DrawHotString (rl, HasFocus && i == _cursor);
+                DrawHotString (rl, HasFocus && i == Cursor);
             }
         }
     }
 
+    #region IOrientation
+
     /// <summary>
     ///     Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
     ///     <see cref="Orientation.Vertical"/>.
@@ -327,13 +452,13 @@ public class RadioGroup : View, IDesignable, IOrientation
         set => _orientationHelper.Orientation = value;
     }
 
-    #region IOrientation
+    private readonly OrientationHelper _orientationHelper;
 
     /// <inheritdoc/>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
     /// <inheritdoc/>
-    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
 
     /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
     /// <param name="newOrientation"></param>
@@ -345,20 +470,22 @@ public class RadioGroup : View, IDesignable, IOrientation
 
     #endregion IOrientation
 
-    // TODO: This should be cancelable
+    // TODO: Add a SelectedItemChanging event like CheckBox has.
     /// <summary>Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.</summary>
     /// <param name="selectedItem"></param>
     /// <param name="previousSelectedItem"></param>
-    public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
-    {
-        if (_selected == selectedItem)
-        {
-            return;
-        }
+    protected virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem) { }
 
-        _selected = selectedItem;
-        SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
-    }
+    /// <summary>
+    ///     Gets or sets the <see cref="RadioLabels"/> index for the cursor. The cursor may or may not be the selected
+    ///     RadioItem.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Maps to either the X or Y position within <see cref="View.Viewport"/> depending on <see cref="Orientation"/>.
+    ///     </para>
+    /// </remarks>
+    public int Cursor { get; set; }
 
     /// <inheritdoc/>
     public override Point? PositionCursor ()
@@ -369,13 +496,13 @@ public class RadioGroup : View, IDesignable, IOrientation
         switch (Orientation)
         {
             case Orientation.Vertical:
-                y = _cursor;
+                y = Cursor;
 
                 break;
             case Orientation.Horizontal:
-                if (_horizontal.Count > 0)
+                if (_horizontal!.Count > 0)
                 {
-                    x = _horizontal [_cursor].pos;
+                    x = _horizontal [Cursor].pos;
                 }
 
                 break;
@@ -389,18 +516,14 @@ public class RadioGroup : View, IDesignable, IOrientation
         return null; // Don't show the cursor
     }
 
-    /// <summary>Allow to invoke the <see cref="SelectedItemChanged"/> after their creation.</summary>
-    public void Refresh () { OnSelectedItemChanged (_selected, -1); }
-
-    // TODO: This should use StateEventArgs<int> and should be cancelable.
-    /// <summary>Invoked when the selected radio label has changed.</summary>
-    public event EventHandler<SelectedItemChangedArgs> SelectedItemChanged;
+    /// <summary>Raised when the selected radio label has changed.</summary>
+    public event EventHandler<SelectedItemChangedArgs>? SelectedItemChanged;
 
     private bool MoveDownRight ()
     {
-        if (_cursor + 1 < _radioLabels.Count)
+        if (Cursor + 1 < _radioLabels.Count)
         {
-            _cursor++;
+            Cursor++;
             SetNeedsDisplay ();
 
             return true;
@@ -410,23 +533,35 @@ public class RadioGroup : View, IDesignable, IOrientation
         return false;
     }
 
-    private void MoveEnd () { _cursor = Math.Max (_radioLabels.Count - 1, 0); }
-    private void MoveHome () { _cursor = 0; }
+    private void MoveEnd () { Cursor = Math.Max (_radioLabels.Count - 1, 0); }
+
+    private bool MoveHome ()
+    {
+        if (Cursor != 0)
+        {
+            Cursor = 0;
+
+            return true;
+        }
+
+        return false;
+    }
 
     private bool MoveUpLeft ()
     {
-        if (_cursor > 0)
+        if (Cursor > 0)
         {
-            _cursor--;
+            Cursor--;
             SetNeedsDisplay ();
 
             return true;
         }
+
         // Moving past should move focus to next view, not wrap
         return false;
     }
 
-    private void RadioGroup_LayoutStarted (object sender, EventArgs e) { SetContentSize (); }
+    private void RadioGroup_LayoutStarted (object? sender, EventArgs e) { SetContentSize (); }
 
     private void SetContentSize ()
     {

+ 210 - 209
Terminal.Gui/Views/Shortcut.cs

@@ -1,4 +1,7 @@
-namespace Terminal.Gui;
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui;
 
 /// <summary>
 ///     Displays a command, help text, and a key binding. When the key specified by <see cref="Key"/> is pressed, the
@@ -9,15 +12,15 @@
 /// <remarks>
 ///     <para>
 ///         The following user actions will invoke the <see cref="Command.Accept"/>, causing the
-///         <see cref="View.Accept"/> event to be fired:
+///         <see cref="View.Accepting"/> event to be fired:
 ///         - Clicking on the <see cref="Shortcut"/>.
 ///         - Pressing the key specified by <see cref="Key"/>.
 ///         - Pressing the HotKey specified by <see cref="CommandView"/>.
 ///     </para>
 ///     <para>
-///         If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked
+///         If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoke
 ///         <see cref="Command.Accept"/>
-///         command regardless of what View has focus, enabling an application-wide keyboard shortcut.
+///         regardless of what View has focus, enabling an application-wide keyboard shortcut.
 ///     </para>
 ///     <para>
 ///         By default, a Shortcut displays the command text on the left side, the help text in the middle, and the key
@@ -38,6 +41,43 @@
 /// </remarks>
 public class Shortcut : View, IOrientation, IDesignable
 {
+    /// <summary>
+    ///     Creates a new instance of <see cref="Shortcut"/>.
+    /// </summary>
+    public Shortcut () : this (Key.Empty, null, null, null) { }
+
+    /// <summary>
+    ///     Creates a new instance of <see cref="Shortcut"/>, binding it to <paramref name="targetView"/> and
+    ///     <paramref name="command"/>. The Key <paramref name="targetView"/>
+    ///     has bound to <paramref name="command"/> will be used as <see cref="Key"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This is a helper API that simplifies creation of multiple Shortcuts when adding them to <see cref="Bar"/>-based
+    ///         objects, like <see cref="MenuBarv2"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="targetView">
+    ///     The View that <paramref name="command"/> will be invoked when user does something that causes the Shortcut's Accept
+    ///     event to be raised.
+    /// </param>
+    /// <param name="command">
+    ///     The Command to invoke on <paramref name="targetView"/>. The Key <paramref name="targetView"/>
+    ///     has bound to <paramref name="command"/> will be used as <see cref="Key"/>
+    /// </param>
+    /// <param name="commandText">The text to display for the command.</param>
+    /// <param name="helpText">The help text to display.</param>
+    public Shortcut (View targetView, Command command, string commandText, string helpText)
+        : this (
+                targetView?.KeyBindings.GetKeyFromCommands (command)!,
+                commandText,
+                null,
+                helpText)
+    {
+        _targetView = targetView;
+        _command = command;
+    }
+
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     /// </summary>
@@ -47,13 +87,14 @@ public class Shortcut : View, IOrientation, IDesignable
     ///     </para>
     /// </remarks>
     /// <param name="key"></param>
-    /// <param name="commandText"></param>
+    /// <param name="commandText">The text to display for the command.</param>
     /// <param name="action"></param>
-    /// <param name="helpText"></param>
-    public Shortcut (Key key, string commandText, Action action, string helpText = null)
+    /// <param name="helpText">The help text to display.</param>
+    public Shortcut (Key key, string? commandText, Action? action, string? helpText = null)
     {
         Id = "_shortcut";
-        HighlightStyle = HighlightStyle.Pressed;
+
+        HighlightStyle = HighlightStyle.None;
         CanFocus = true;
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
@@ -62,48 +103,36 @@ public class Shortcut : View, IOrientation, IDesignable
         _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
         _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
-        AddCommand (Command.HotKey, ctx => OnAccept (ctx));
-        AddCommand (Command.Accept, ctx => OnAccept (ctx));
-        AddCommand (Command.Select, ctx => OnSelect (ctx));
-        KeyBindings.Add (KeyCode.Enter, Command.Accept);
-        KeyBindings.Add (KeyCode.Space, Command.Select);
+        AddCommands ();
 
         TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set
 
         CommandView = new ()
         {
             Width = Dim.Auto (),
-            Height = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: 1)
+            Height = Dim.Auto (DimAutoStyle.Auto, 1)
         };
 
         HelpView.Id = "_helpView";
         HelpView.CanFocus = false;
-        HelpView.Text = helpText;
+        HelpView.Text = helpText ?? string.Empty;
         Add (HelpView);
 
         KeyView.Id = "_keyView";
         KeyView.CanFocus = false;
         Add (KeyView);
 
-        // If the user clicks anywhere on the Shortcut, other than the CommandView, invoke the Command
-        MouseClick += Shortcut_MouseClick;
-        HelpView.MouseClick += Subview_MouseClick;
-        KeyView.MouseClick += Subview_MouseClick;
         LayoutStarted += OnLayoutStarted;
         Initialized += OnInitialized;
 
-        if (key is null)
-        {
-            key = Key.Empty;
-        }
-
+        key ??= Key.Empty;
         Key = key;
-        Title = commandText;
+        Title = commandText ?? string.Empty;
         Action = action;
 
         return;
 
-        void OnInitialized (object sender, EventArgs e)
+        void OnInitialized (object? sender, EventArgs e)
         {
             SuperViewRendersLineCanvas = true;
             Border.Settings &= ~BorderSettings.Title;
@@ -130,23 +159,16 @@ public class Shortcut : View, IOrientation, IDesignable
             return Dim.Auto (
                              DimAutoStyle.Content,
                              Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width)),
-                             Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width)));
+                             Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width)))!;
         }
     }
 
-    /// <summary>
-    ///     Creates a new instance of <see cref="Shortcut"/>.
-    /// </summary>
-    public Shortcut () : this (Key.Empty, string.Empty, null) { }
-
-    private readonly OrientationHelper _orientationHelper;
-
     private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
 
     // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
     private int? _minimumDimAutoWidth;
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     protected override bool OnHighlight (CancelEventArgs<HighlightStyle> args)
     {
         if (args.NewValue.HasFlag (HighlightStyle.Hover))
@@ -157,16 +179,6 @@ public class Shortcut : View, IOrientation, IDesignable
         return true;
     }
 
-    /// <inheritdoc/>
-    public bool EnableForDesign ()
-    {
-        Title = "_Shortcut";
-        HelpText = "Shortcut help";
-        Key = Key.F1;
-
-        return true;
-    }
-
     /// <summary>
     ///     Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Shortcut"/>.
     /// </summary>
@@ -188,30 +200,6 @@ public class Shortcut : View, IOrientation, IDesignable
         }
     }
 
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        if (disposing)
-        {
-            if (CommandView?.IsAdded == false)
-            {
-                CommandView.Dispose ();
-            }
-
-            if (HelpView?.IsAdded == false)
-            {
-                HelpView.Dispose ();
-            }
-
-            if (KeyView?.IsAdded == false)
-            {
-                KeyView.Dispose ();
-            }
-        }
-
-        base.Dispose (disposing);
-    }
-
     // When one of the subviews is "empty" we don't want to show it. So we
     // Use Add/Remove. We need to be careful to add them in the right order
     // so Pos.Align works correctly.
@@ -246,7 +234,7 @@ public class Shortcut : View, IOrientation, IDesignable
     }
 
     // When layout starts, we need to adjust the layout of the HelpView and KeyView
-    private void OnLayoutStarted (object sender, LayoutEventArgs e)
+    private void OnLayoutStarted (object? sender, LayoutEventArgs e)
     {
         if (Width is DimAuto widthAuto)
         {
@@ -314,48 +302,86 @@ public class Shortcut : View, IOrientation, IDesignable
             else
             {
                 // Reset to default
-                //SetCommandViewDefaultLayout();
                 SetHelpViewDefaultLayout ();
-
-                //SetKeyViewDefaultLayout ();
             }
         }
     }
 
-    private bool? OnSelect (CommandContext ctx)
+
+    #region Accept/Select/HotKey Command Handling
+
+    private readonly View? _targetView; // If set, _command will be invoked
+
+    private readonly Command _command; // Used when _targetView is set
+
+    private void AddCommands ()
+    {
+        // Accept (Enter key) -
+        AddCommand (Command.Accept, DispatchCommand);
+        // Hotkey -
+        AddCommand (Command.HotKey, DispatchCommand);
+        // Select (Space key or click) -
+        AddCommand (Command.Select, DispatchCommand);
+    }
+
+    private bool? DispatchCommand (CommandContext ctx)
     {
-        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+        if (ctx.Data != this)
         {
-            return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+            // 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;
+            CommandView.InvokeCommand (Command.Select, ctx);
         }
 
-        return false;
-    }
+        if (RaiseSelecting (ctx) is true)
+        {
+            return true;
+        }
 
-    private void Shortcut_MouseClick (object sender, MouseEventEventArgs e)
-    {
-        // When the Shortcut is clicked, we want to invoke the Command and Set focus
-        var view = sender as View;
+        // The default HotKey handler sets Focus
+        SetFocus ();
+
+        var cancel = false;
+
+        cancel = RaiseAccepting (ctx) is true;
+
+        if (cancel)
+        {
+            return true;
+        }
 
-        if (!e.Handled)
+        if (Action is { })
         {
-            // If the subview (likely CommandView) didn't handle the mouse click, invoke the command.
-            e.Handled = InvokeCommand (Command.Accept) == true;
+            Action.Invoke ();
+
+            // Assume if there's a subscriber to Action, it's handled.
+            cancel = true;
         }
 
-        if (CanFocus)
+        if (_targetView is { })
         {
-            SetFocus ();
+            _targetView.InvokeCommand (_command);
         }
-    }
 
-    private void Subview_MouseClick (object sender, MouseEventEventArgs e)
-    {
-        // TODO: Remove. This does nothing.
+        return cancel;
     }
 
+    /// <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.Accepting"/> event is fired first, and if cancelled, the event will not be invoked.
+    /// </remarks>
+    public Action? Action { get; set; }
+
+    #endregion Accept/Select/HotKey Command Handling
+
     #region IOrientation members
 
+    private readonly OrientationHelper _orientationHelper;
+
     /// <summary>
     ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
     ///     <see cref="Orientation.Horizontal"/>.
@@ -376,10 +402,10 @@ public class Shortcut : View, IOrientation, IDesignable
     }
 
     /// <inheritdoc/>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
     /// <inheritdoc/>
-    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
 
     /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
     /// <param name="newOrientation"></param>
@@ -398,6 +424,7 @@ public class Shortcut : View, IOrientation, IDesignable
     /// <summary>
     ///     Gets or sets the View that displays the command text and hotkey.
     /// </summary>
+    /// <exception cref="ArgumentNullException"></exception>
     /// <remarks>
     ///     <para>
     ///         By default, the <see cref="View.Title"/> of the <see cref="CommandView"/> is displayed as the Shortcut's
@@ -442,17 +469,20 @@ public class Shortcut : View, IOrientation, IDesignable
         get => _commandView;
         set
         {
+            ArgumentNullException.ThrowIfNull (value);
+
             if (value == null)
             {
                 throw new ArgumentNullException ();
             }
 
-            if (_commandView is { })
-            {
-                Remove (_commandView);
-                _commandView?.Dispose ();
-            }
+            // Clean up old 
+            _commandView.Selecting -= CommandViewOnSelecting;
+            _commandView.Accepting -= CommandViewOnAccepted;
+            Remove (_commandView);
+            _commandView?.Dispose ();
 
+            // Set new
             _commandView = value;
             _commandView.Id = "_commandView";
 
@@ -464,7 +494,7 @@ public class Shortcut : View, IOrientation, IDesignable
                                           {
                                               if (e.NewKey != Key.Empty)
                                               {
-                                                  // Add it 
+                                                  // Add it
                                                   AddKeyBindingsForHotKey (e.OldKey, e.NewKey);
                                               }
                                           };
@@ -473,11 +503,34 @@ public class Shortcut : View, IOrientation, IDesignable
 
             Title = _commandView.Text;
 
+            _commandView.Selecting += CommandViewOnSelecting;
+
+            _commandView.Accepting += CommandViewOnAccepted;
+
             SetCommandViewDefaultLayout ();
             SetHelpViewDefaultLayout ();
             SetKeyViewDefaultLayout ();
             ShowHide ();
-            UpdateKeyBinding (Key.Empty);
+            UpdateKeyBindings (Key.Empty);
+
+            return;
+
+            void CommandViewOnAccepted (object? sender, CommandEventArgs e)
+            {
+                // Always eat CommandView.Accept
+                e.Cancel = true;
+            }
+
+            void CommandViewOnSelecting (object? sender, CommandEventArgs e)
+            {
+                if (e.Context.Data != this)
+                {
+                    // Forward command to ourselves
+                    InvokeCommand (Command.Select, new (Command.Select, null, null, this));
+                }
+
+                e.Cancel = true;
+            }
         }
     }
 
@@ -489,7 +542,7 @@ public class Shortcut : View, IOrientation, IDesignable
         HelpView.HighlightStyle = HighlightStyle.None;
     }
 
-    private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
+    private void Shortcut_TitleChanged (object? sender, EventArgs<string> e)
     {
         // If the Title changes, update the CommandView text.
         // This is a helper to make it easier to set the CommandView text.
@@ -525,14 +578,11 @@ public class Shortcut : View, IOrientation, IDesignable
     /// </summary>
     public override string Text
     {
-        get => HelpView?.Text;
+        get => HelpView.Text;
         set
         {
-            if (HelpView is { })
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
 
@@ -541,14 +591,11 @@ public class Shortcut : View, IOrientation, IDesignable
     /// </summary>
     public string HelpText
     {
-        get => HelpView?.Text;
+        get => HelpView.Text;
         set
         {
-            if (HelpView is { })
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
 
@@ -566,15 +613,12 @@ public class Shortcut : View, IOrientation, IDesignable
         get => _key;
         set
         {
-            if (value == null)
-            {
-                throw new ArgumentNullException ();
-            }
+            ArgumentNullException.ThrowIfNull (value);
 
             Key oldKey = _key;
             _key = value;
 
-            UpdateKeyBinding (oldKey);
+            UpdateKeyBindings (oldKey);
 
             KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
             ShowHide ();
@@ -608,7 +652,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
             _keyBindingScope = value;
 
-            UpdateKeyBinding (Key.Empty);
+            UpdateKeyBindings (Key.Empty);
         }
     }
 
@@ -648,7 +692,7 @@ public class Shortcut : View, IOrientation, IDesignable
     {
         KeyView.Margin.Thickness = GetMarginThickness ();
         KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
-        KeyView.Y = 0; //Pos.Center ();
+        KeyView.Y = 0;
         KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
         KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
 
@@ -661,14 +705,10 @@ public class Shortcut : View, IOrientation, IDesignable
         HelpView.HighlightStyle = HighlightStyle.None;
     }
 
-    private void UpdateKeyBinding (Key oldKey)
+    private void UpdateKeyBindings (Key oldKey)
     {
-        if (Key != null && Key.IsValid)
+        if (Key.IsValid)
         {
-            // Disable the command view key bindings
-            CommandView.KeyBindings.Remove (Key);
-            CommandView.KeyBindings.Remove (CommandView.HotKey);
-
             if (KeyBindingScope.FastHasFlags (KeyBindingScope.Application))
             {
                 if (oldKey != Key.Empty)
@@ -677,7 +717,7 @@ public class Shortcut : View, IOrientation, IDesignable
                 }
 
                 Application.KeyBindings.Remove (Key);
-                Application.KeyBindings.Add (Key, this, Command.Accept);
+                Application.KeyBindings.Add (Key, this, Command.HotKey);
             }
             else
             {
@@ -687,93 +727,17 @@ public class Shortcut : View, IOrientation, IDesignable
                 }
 
                 KeyBindings.Remove (Key);
-                KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+                KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.HotKey);
             }
         }
     }
 
     #endregion Key
 
-    #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)
-    {
-        var cancel = false;
-
-        switch (ctx.KeyBinding?.Scope)
-        {
-            case KeyBindingScope.Application:
-                cancel = base.OnAccept () == true;
-
-                break;
-
-            case KeyBindingScope.Focused:
-                base.OnAccept ();
-
-                // cancel if we're focused
-                cancel = true;
-
-                break;
-
-            case KeyBindingScope.HotKey:
-                //if (!CanBeVisible(this))
-                //{
-                //    return true;
-                //}
-                cancel = base.OnAccept () == true;
-
-                if (CanFocus)
-                {
-                    SetFocus ();
-                    cancel = true;
-                }
-
-                break;
-
-            default:
-                // Mouse
-                cancel = base.OnAccept () == true;
-
-                break;
-        }
-
-        CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
-
-        if (Action is { })
-        {
-            Action.Invoke ();
-
-            // Assume if there's a subscriber to Action, it's handled.
-            cancel = true;
-        }
-
-        return cancel;
-    }
-
-    /// <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; }
-
-    #endregion Accept Handling
-
     #region Focus
 
     /// <inheritdoc/>
-    public override ColorScheme ColorScheme
+    public override ColorScheme? ColorScheme
     {
         get => base.ColorScheme;
         set
@@ -788,7 +752,10 @@ public class Shortcut : View, IOrientation, IDesignable
     internal void SetColors (bool highlight = false)
     {
         // Border should match superview.
-        Border.ColorScheme = SuperView?.ColorScheme;
+        if (Border is { })
+        {
+            Border.ColorScheme = SuperView?.ColorScheme;
+        }
 
         if (HasFocus || highlight)
         {
@@ -821,10 +788,44 @@ public class Shortcut : View, IOrientation, IDesignable
     }
 
     /// <inheritdoc/>
-    protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view) { SetColors (); }
+
+    #endregion Focus
+
+    /// <inheritdoc/>
+    public bool EnableForDesign ()
     {
-        SetColors ();
+        Title = "_Shortcut";
+        HelpText = "Shortcut help";
+        Key = Key.F1;
+
+        return true;
     }
 
-    #endregion Focus
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        if (disposing)
+        {
+            TitleChanged -= Shortcut_TitleChanged;
+
+            if (CommandView?.IsAdded == false)
+            {
+                CommandView.Dispose ();
+            }
+
+            if (HelpView?.IsAdded == false)
+            {
+                HelpView.Dispose ();
+            }
+
+            if (KeyView?.IsAdded == false)
+            {
+                KeyView.Dispose ();
+            }
+        }
+
+        base.Dispose (disposing);
+    }
 }

+ 15 - 7
Terminal.Gui/Views/Slider.cs

@@ -1423,7 +1423,7 @@ public class Slider<T> : View, IOrientation
         AddCommand (Command.RightExtend, () => ExtendPlus ());
         AddCommand (Command.LeftExtend, () => ExtendMinus ());
         AddCommand (Command.Select, () => Select ());
-        AddCommand (Command.Accept, () => Accept ());
+        AddCommand (Command.Accept, (ctx) => Accept (ctx));
 
         SetKeyBindings ();
     }
@@ -1496,8 +1496,13 @@ public class Slider<T> : View, IOrientation
         OnOptionsChanged ();
     }
 
-    private void SetFocusedOption ()
+    private bool SetFocusedOption ()
     {
+        if (_options.Count == 0)
+        {
+            return false;
+        }
+        bool changed = false;
         switch (_config._type)
         {
             case SliderType.Single:
@@ -1530,6 +1535,7 @@ public class Slider<T> : View, IOrientation
 
                 // Raise slider changed event.
                 OnOptionsChanged ();
+                changed = true;
 
                 break;
             case SliderType.Multiple:
@@ -1550,6 +1556,7 @@ public class Slider<T> : View, IOrientation
                 }
 
                 OnOptionsChanged ();
+                changed = true;
 
                 break;
 
@@ -1683,11 +1690,14 @@ public class Slider<T> : View, IOrientation
 
                 // Raise Slider Option Changed Event.
                 OnOptionsChanged ();
+                changed = true;
 
                 break;
             default:
                 throw new ArgumentOutOfRangeException (_config._type.ToString ());
         }
+
+        return changed;
     }
 
     internal bool ExtendPlus ()
@@ -1772,16 +1782,14 @@ public class Slider<T> : View, IOrientation
 
     internal bool Select ()
     {
-        SetFocusedOption ();
-
-        return true;
+        return SetFocusedOption ();
     }
 
-    internal new bool Accept ()
+    internal bool Accept (CommandContext ctx)
     {
         SetFocusedOption ();
 
-        return OnAccept () == true;
+        return RaiseAccepting (ctx) == true;
     }
 
     internal bool MovePlus ()

+ 4 - 4
Terminal.Gui/Views/StatusBar.cs

@@ -113,14 +113,14 @@ public class StatusBar : Bar, IDesignable
             Text = "I'll Hide",
             // Visible = false
         };
-        button1.Accept += Button_Clicked;
+        button1.Accepting += Button_Clicked;
         Add (button1);
 
-        shortcut.Accept += (s, e) =>
+        shortcut.Accepting += (s, e) =>
                            {
                                button1.Visible = !button1.Visible;
                                button1.Enabled = button1.Visible;
-                               e.Handled = false;
+                               e.Cancel = false;
                            };
 
         Add (new Label
@@ -134,7 +134,7 @@ public class StatusBar : Bar, IDesignable
         {
             Text = "Or me!",
         };
-        button2.Accept += (s, e) => Application.RequestStop ();
+        button2.Accepting += (s, e) => Application.RequestStop ();
 
         Add (button2);
 

+ 19 - 18
Terminal.Gui/Views/TableView/TableView.cs

@@ -238,24 +238,18 @@ public class TableView : View
                     }
                    );
 
-        AddCommand (
-                    Command.Accept,
-                    () =>
-                    {
-                        // BUGBUG: This should return false if the event is not handled
-                        OnCellActivated (new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow));
-
-                        return true;
-                    }
-                   );
+        AddCommand (Command.Accept, () => OnCellActivated (new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)));
 
         AddCommand (
                     Command.Select, // was Command.ToggleChecked
-                    () =>
+                    (ctx) =>
                     {
-                        ToggleCurrentCellSelection ();
+                        if (ToggleCurrentCellSelection () is true)
+                        {
+                            return RaiseSelecting (ctx) is true;
+                        }
 
-                        return true;
+                        return false;
                     }
                    );
 
@@ -283,8 +277,8 @@ public class TableView : View
         KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
 
         KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
+        KeyBindings.Remove (CellActivationKey);
         KeyBindings.Add (CellActivationKey, Command.Accept);
-        KeyBindings.Add (Key.Space, Command.Select);
     }
 
     // TODO: Update to use Key instead of KeyCode
@@ -1250,7 +1244,12 @@ public class TableView : View
 
     /// <summary>Invokes the <see cref="CellActivated"/> event</summary>
     /// <param name="args"></param>
-    protected virtual void OnCellActivated (CellActivatedEventArgs args) { CellActivated?.Invoke (this, args); }
+    /// <returns><see langword="true"/> if the CellActivated event was raised.</returns>
+    protected virtual bool OnCellActivated (CellActivatedEventArgs args)
+    {
+        CellActivated?.Invoke (this, args);
+        return CellActivated is { };
+    }
 
     /// <summary>Invokes the <see cref="CellToggled"/> event</summary>
     /// <param name="args"></param>
@@ -2047,19 +2046,19 @@ public class TableView : View
                                  );
     }
 
-    private void ToggleCurrentCellSelection ()
+    private bool? ToggleCurrentCellSelection ()
     {
         var e = new CellToggledEventArgs (Table, selectedColumn, selectedRow);
         OnCellToggled (e);
 
         if (e.Cancel)
         {
-            return;
+            return false;
         }
 
         if (!MultiSelect)
         {
-            return;
+            return null;
         }
 
         TableSelection [] regions = GetMultiSelectedRegionsContaining (selectedColumn, selectedRow).ToArray ();
@@ -2104,6 +2103,8 @@ public class TableView : View
                                           );
             }
         }
+
+        return true;
     }
 
     /// <summary>

+ 94 - 114
Terminal.Gui/Views/TextField.cs

@@ -1,4 +1,3 @@
-using System.Data;
 using System.Globalization;
 using Terminal.Gui.Resources;
 
@@ -25,14 +24,14 @@ public class TextField : View
     /// </summary>
     public TextField ()
     {
-        _historyText = new HistoryText ();
+        _historyText = new ();
         _isButtonReleased = true;
         _selectedStart = -1;
-        _text = new List<Rune> ();
-        CaptionColor = new Color (Color.DarkGray);
+        _text = new ();
+        CaptionColor = new (Color.DarkGray);
         ReadOnly = false;
         Autocomplete = new TextFieldAutocomplete ();
-        Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1);
+        Height = Dim.Auto (DimAutoStyle.Text, 1);
 
         CanFocus = true;
         CursorVisibility = CursorVisibility.Default;
@@ -325,10 +324,6 @@ public class TextField : View
                     }
                    );
 
-        // By Default pressing ENTER should be ignored (OnAccept will return false or null). Only cancel if the
-        // event was fired and set Cancel = true.
-        AddCommand (Command.Accept, () => OnAccept () == false);
-
         // Default keybindings for this view
         // We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
         KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
@@ -405,13 +400,13 @@ public class TextField : View
 
         _currentCulture = Thread.CurrentThread.CurrentUICulture;
 
-        ContextMenu = new ContextMenu { Host = this };
+        ContextMenu = new() { Host = this };
         ContextMenu.KeyChanged += ContextMenu_KeyChanged;
 
         KeyBindings.Add (ContextMenu.Key, KeyBindingScope.HotKey, Command.Context);
-        KeyBindings.Add (Key.Enter, Command.Accept);
-    }
 
+        KeyBindings.Remove (Key.Space);
+    }
 
     /// <summary>
     ///     Provides autocomplete context menu based on suggestions at the current cursor position. Configure
@@ -546,13 +541,13 @@ public class TextField : View
             if (!Secret && !_historyText.IsFromHistory)
             {
                 _historyText.Add (
-                                  new List<List<RuneCell>> { TextModel.ToRuneCellList (oldText) },
-                                  new Point (_cursorPosition, 0)
+                                  new() { TextModel.ToRuneCellList (oldText) },
+                                  new (_cursorPosition, 0)
                                  );
 
                 _historyText.Add (
-                                  new List<List<RuneCell>> { TextModel.ToRuneCells (_text) },
-                                  new Point (_cursorPosition, 0),
+                                  new() { TextModel.ToRuneCells (_text) },
+                                  new (_cursorPosition, 0),
                                   HistoryText.LineStatus.Replaced
                                  );
             }
@@ -648,8 +643,8 @@ public class TextField : View
         }
 
         _historyText.Add (
-                          new List<List<RuneCell>> { TextModel.ToRuneCells (_text) },
-                          new Point (_cursorPosition, 0)
+                          new() { TextModel.ToRuneCells (_text) },
+                          new (_cursorPosition, 0)
                          );
 
         if (SelectedLength == 0)
@@ -702,8 +697,8 @@ public class TextField : View
         }
 
         _historyText.Add (
-                          new List<List<RuneCell>> { TextModel.ToRuneCells (_text) },
-                          new Point (_cursorPosition, 0)
+                          new() { TextModel.ToRuneCells (_text) },
+                          new (_cursorPosition, 0)
                          );
 
         if (SelectedLength == 0)
@@ -728,10 +723,7 @@ public class TextField : View
     }
 
     /// <inheritdoc/>
-    public override Attribute GetNormalColor ()
-    {
-        return GetFocusColor ();
-    }
+    public override Attribute GetNormalColor () { return GetFocusColor (); }
 
     /// <summary>
     ///     Inserts the given <paramref name="toAdd"/> text at the current cursor position exactly as if the user had just
@@ -1043,8 +1035,6 @@ public class TextField : View
 
         //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar))
         //	ClearAllSelection ();
-
-        return;
     }
 
     /// TODO: Flush out these docs
@@ -1145,6 +1135,7 @@ public class TextField : View
 
         int pos = _cursorPosition - ScrollOffset + Math.Min (Frame.X, 0);
         Move (pos, 0);
+
         return new Point (pos, 0);
     }
 
@@ -1253,73 +1244,70 @@ public class TextField : View
 
     private MenuBarItem BuildContextMenuBarItem ()
     {
-        return new MenuBarItem (
-                                new MenuItem []
-                                {
-                                    new (
-                                         Strings.ctxSelectAll,
-                                         "",
-                                         () => SelectAll (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)
-                                        ),
-                                    new (
-                                         Strings.ctxDeleteAll,
-                                         "",
-                                         () => DeleteAll (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.DeleteAll)
-                                        ),
-                                    new (
-                                         Strings.ctxCopy,
-                                         "",
-                                         () => Copy (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.Copy)
-                                        ),
-                                    new (
-                                         Strings.ctxCut,
-                                         "",
-                                         () => Cut (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.Cut)
-                                        ),
-                                    new (
-                                         Strings.ctxPaste,
-                                         "",
-                                         () => Paste (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.Paste)
-                                        ),
-                                    new (
-                                         Strings.ctxUndo,
-                                         "",
-                                         () => Undo (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.Undo)
-                                        ),
-                                    new (
-                                         Strings.ctxRedo,
-                                         "",
-                                         () => Redo (),
-                                         null,
-                                         null,
-                                         (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
-                                        )
-                                }
-                               );
+        return new (
+                    new MenuItem []
+                    {
+                        new (
+                             Strings.ctxSelectAll,
+                             "",
+                             () => SelectAll (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)
+                            ),
+                        new (
+                             Strings.ctxDeleteAll,
+                             "",
+                             () => DeleteAll (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.DeleteAll)
+                            ),
+                        new (
+                             Strings.ctxCopy,
+                             "",
+                             () => Copy (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Copy)
+                            ),
+                        new (
+                             Strings.ctxCut,
+                             "",
+                             () => Cut (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Cut)
+                            ),
+                        new (
+                             Strings.ctxPaste,
+                             "",
+                             () => Paste (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Paste)
+                            ),
+                        new (
+                             Strings.ctxUndo,
+                             "",
+                             () => Undo (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Undo)
+                            ),
+                        new (
+                             Strings.ctxRedo,
+                             "",
+                             () => Redo (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
+                            )
+                    }
+                   );
     }
 
-    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e)
-    {
-        KeyBindings.ReplaceKey (e.OldKey.KeyCode, e.NewKey.KeyCode);
-    }
+    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.ReplaceKey (e.OldKey.KeyCode, e.NewKey.KeyCode); }
 
     private List<Rune> DeleteSelectedText ()
     {
@@ -1345,13 +1333,13 @@ public class TextField : View
         List<RuneCell> currentLine = TextModel.ToRuneCellList (Text);
         int cursorPosition = Math.Min (CursorPosition, currentLine.Count);
 
-        Autocomplete.Context = new AutocompleteContext (
-                                                        currentLine,
-                                                        cursorPosition,
-                                                        Autocomplete.Context != null
-                                                            ? Autocomplete.Context.Canceled
-                                                            : false
-                                                       );
+        Autocomplete.Context = new (
+                                    currentLine,
+                                    cursorPosition,
+                                    Autocomplete.Context != null
+                                        ? Autocomplete.Context.Canceled
+                                        : false
+                                   );
 
         Autocomplete.GenerateSuggestions (
                                           Autocomplete.Context
@@ -1372,15 +1360,15 @@ public class TextField : View
 
         if (ColorScheme is null)
         {
-            cs = new ColorScheme ();
+            cs = new ();
         }
 
         if (cs.Disabled.Foreground == cs.Focus.Background)
         {
-            return new Attribute (cs.Focus.Foreground, cs.Focus.Background);
+            return new (cs.Focus.Foreground, cs.Focus.Background);
         }
 
-        return new Attribute (cs.Disabled.Foreground, cs.Focus.Background);
+        return new (cs.Disabled.Foreground, cs.Focus.Background);
     }
 
     private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItemEventArgs obj)
@@ -1398,8 +1386,8 @@ public class TextField : View
     private void InsertText (Key a, bool usePreTextChangedCursorPos)
     {
         _historyText.Add (
-                          new List<List<RuneCell>> { TextModel.ToRuneCells (_text) },
-                          new Point (_cursorPosition, 0)
+                          new() { TextModel.ToRuneCells (_text) },
+                          new (_cursorPosition, 0)
                          );
 
         List<Rune> newText = _text;
@@ -1533,7 +1521,6 @@ public class TextField : View
 
     private bool MoveLeft ()
     {
-
         if (_cursorPosition > 0)
         {
             ClearAllSelection ();
@@ -1672,13 +1659,10 @@ public class TextField : View
             offB = SuperView.Frame.Right - Frame.Right - 1;
         }
 
-        return 0;//offB;
+        return 0; //offB;
     }
 
-    private int PositionCursor (MouseEvent ev)
-    {
-        return PositionCursor (TextModel.GetColFromX (_text, ScrollOffset, ev.Position.X), false);
-    }
+    private int PositionCursor (MouseEvent ev) { return PositionCursor (TextModel.GetColFromX (_text, ScrollOffset, ev.Position.X), false); }
 
     private int PositionCursor (int x, bool getX = true)
     {
@@ -1772,7 +1756,6 @@ public class TextField : View
 
     private void DrawAutocomplete ()
     {
-
         if (SelectedLength > 0)
         {
             return;
@@ -1863,10 +1846,7 @@ public class TextField : View
         }
     }
 
-    private void TextField_Removed (object sender, SuperViewChangedEventArgs e)
-    {
-        Autocomplete.HostControl = null;
-    }
+    private void TextField_Removed (object sender, SuperViewChangedEventArgs e) { Autocomplete.HostControl = null; }
 
     private void TextField_Initialized (object sender, EventArgs e)
     {

+ 68 - 58
Terminal.Gui/Views/TextView.cs

@@ -2010,6 +2010,10 @@ public class TextView : View
         LayoutComplete += TextView_LayoutComplete;
 
         // Things this view knows how to do
+
+        // Note - NewLine is only bound to Enter if Multiline is true
+        AddCommand (Command.NewLine, (ctx) => ProcessEnterKey (ctx));
+
         AddCommand (
                     Command.PageDown,
                     () =>
@@ -2275,7 +2279,6 @@ public class TextView : View
                         return true;
                     }
                    );
-        AddCommand (Command.NewLine, () => ProcessReturn ());
 
         AddCommand (
                     Command.End,
@@ -2403,14 +2406,17 @@ public class TextView : View
                     }
                    );
 
-        // Default keybindings for this view
+        KeyBindings.Remove (Key.Space);
+
+        KeyBindings.Remove (Key.Enter);
+        KeyBindings.Add (Key.Enter, Multiline ? Command.NewLine : Command.Accept);
+
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
 
         KeyBindings.Add (Key.PageDown.WithShift, Command.PageDownExtend);
 
         KeyBindings.Add (Key.PageUp, Command.PageUp);
-        KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
 
         KeyBindings.Add (Key.PageUp.WithShift, Command.PageUpExtend);
 
@@ -2453,34 +2459,26 @@ public class TextView : View
 
         KeyBindings.Add (Key.Delete.WithCtrl.WithShift, Command.CutToEndLine); // kill-to-end
 
-        KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start
-
         KeyBindings.Add (Key.Backspace.WithCtrl.WithShift, Command.CutToStartLine); // kill-to-start
 
         KeyBindings.Add (Key.Y.WithCtrl, Command.Paste); // Control-y, yank
         KeyBindings.Add (Key.Space.WithCtrl, Command.ToggleExtend);
 
-        KeyBindings.Add (Key.C.WithAlt, Command.Copy);
         KeyBindings.Add (Key.C.WithCtrl, Command.Copy);
 
-        KeyBindings.Add (Key.W.WithAlt, Command.Cut);
-        KeyBindings.Add (Key.W.WithCtrl, Command.Cut);
-        KeyBindings.Add (Key.X.WithCtrl, Command.Cut);
+        KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix?
+        KeyBindings.Add (Key.X.WithCtrl, Command.Cut); 
 
         KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft);
-        KeyBindings.Add (Key.B.WithAlt, Command.WordLeft);
 
         KeyBindings.Add (Key.CursorLeft.WithCtrl.WithShift, Command.WordLeftExtend);
 
         KeyBindings.Add (Key.CursorRight.WithCtrl, Command.WordRight);
-        KeyBindings.Add (Key.F.WithAlt, Command.WordRight);
 
         KeyBindings.Add (Key.CursorRight.WithCtrl.WithShift, Command.WordRightExtend);
         KeyBindings.Add (Key.Delete.WithCtrl, Command.KillWordForwards); // kill-word-forwards
         KeyBindings.Add (Key.Backspace.WithCtrl, Command.KillWordBackwards); // kill-word-backwards
 
-        // BUGBUG: If AllowsReturn is false, Key.Enter should not be bound (so that Toplevel can cause Command.Accept).
-        KeyBindings.Add (Key.Enter, Command.NewLine);
         KeyBindings.Add (Key.End.WithCtrl, Command.End);
         KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
         KeyBindings.Add (Key.Home.WithCtrl, Command.Start);
@@ -2496,6 +2494,15 @@ public class TextView : View
         KeyBindings.Add (Key.G.WithCtrl, Command.DeleteAll);
         KeyBindings.Add (Key.D.WithCtrl.WithShift, Command.DeleteAll);
 
+#if UNIX_KEY_BINDINGS
+        KeyBindings.Add (Key.C.WithAlt, Command.Copy);
+        KeyBindings.Add (Key.B.WithAlt, Command.WordLeft);
+        KeyBindings.Add (Key.W.WithAlt, Command.Cut);
+        KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
+        KeyBindings.Add (Key.F.WithAlt, Command.WordRight);
+        KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start
+#endif
+
         _currentCulture = Thread.CurrentThread.CurrentUICulture;
 
         ContextMenu = new ();
@@ -2512,7 +2519,7 @@ public class TextView : View
     // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts.
     /// <summary>
     ///     Gets or sets whether pressing ENTER in a <see cref="TextView"/> creates a new line of text
-    ///     in the view or invokes the <see cref="View.Accept"/> event.
+    ///     in the view or invokes the <see cref="View.Accepting"/> event.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -2700,6 +2707,9 @@ public class TextView : View
                 Height = _savedHeight;
                 SetNeedsDisplay ();
             }
+
+            KeyBindings.Remove (Key.Enter);
+            KeyBindings.Add (Key.Enter, Multiline ? Command.NewLine : Command.Accept);
         }
     }
 
@@ -2729,7 +2739,7 @@ public class TextView : View
     {
         get
         {
-            if (!Selecting || (_model.Count == 1 && _model.GetLine (0).Count == 0))
+            if (!IsSelecting || (_model.Count == 1 && _model.GetLine (0).Count == 0))
             {
                 return string.Empty;
             }
@@ -2739,7 +2749,7 @@ public class TextView : View
     }
 
     /// <summary>Get or sets whether the user is currently selecting text.</summary>
-    public bool Selecting { get; set; }
+    public bool IsSelecting { get; set; }
 
     /// <summary>Start column position of the selected text.</summary>
     public int SelectionStartColumn
@@ -2751,7 +2761,7 @@ public class TextView : View
 
             _selectionStartColumn = value < 0 ? 0 :
                                     value > line.Count ? line.Count : value;
-            Selecting = true;
+            IsSelecting = true;
             SetNeedsDisplay ();
             Adjust ();
         }
@@ -2765,7 +2775,7 @@ public class TextView : View
         {
             _selectionStartRow = value < 0 ? 0 :
                                  value > _model.Count - 1 ? Math.Max (_model.Count - 1, 0) : value;
-            Selecting = true;
+            IsSelecting = true;
             SetNeedsDisplay ();
             Adjust ();
         }
@@ -2897,7 +2907,7 @@ public class TextView : View
     {
         SetWrapModel ();
 
-        if (Selecting)
+        if (IsSelecting)
         {
             SetClipboard (GetRegion ());
             _copyWithoutSelection = false;
@@ -2931,7 +2941,7 @@ public class TextView : View
         }
 
         UpdateWrapModel ();
-        Selecting = false;
+        IsSelecting = false;
         DoNeededAction ();
         OnContentsChanged ();
     }
@@ -2961,7 +2971,7 @@ public class TextView : View
 
         SetWrapModel ();
 
-        if (Selecting)
+        if (IsSelecting)
         {
             _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
 
@@ -3005,7 +3015,7 @@ public class TextView : View
 
         SetWrapModel ();
 
-        if (Selecting)
+        if (IsSelecting)
         {
             _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
 
@@ -3339,7 +3349,7 @@ public class TextView : View
             ProcessMouseClick (ev, out List<RuneCell> line);
             PositionCursor ();
 
-            if (_model.Count > 0 && _shiftSelecting && Selecting)
+            if (_model.Count > 0 && _shiftSelecting && IsSelecting)
             {
                 if (CurrentRow - _topRow >= Viewport.Height - 1 && _model.Count > _topRow + CurrentRow)
                 {
@@ -3403,7 +3413,7 @@ public class TextView : View
             ProcessMouseClick (ev, out _);
             PositionCursor ();
 
-            if (!Selecting)
+            if (!IsSelecting)
             {
                 StartSelecting ();
             }
@@ -3425,12 +3435,12 @@ public class TextView : View
         {
             if (ev.Flags.HasFlag (MouseFlags.ButtonShift))
             {
-                if (!Selecting)
+                if (!IsSelecting)
                 {
                     StartSelecting ();
                 }
             }
-            else if (Selecting)
+            else if (IsSelecting)
             {
                 StopSelecting ();
             }
@@ -3449,7 +3459,7 @@ public class TextView : View
                 }
             }
 
-            if (!Selecting)
+            if (!IsSelecting)
             {
                 StartSelecting ();
             }
@@ -3467,7 +3477,7 @@ public class TextView : View
         }
         else if (ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
         {
-            if (Selecting)
+            if (IsSelecting)
             {
                 StopSelecting ();
             }
@@ -3475,7 +3485,7 @@ public class TextView : View
             ProcessMouseClick (ev, out List<RuneCell> line);
             CurrentColumn = 0;
 
-            if (!Selecting)
+            if (!IsSelecting)
             {
                 StartSelecting ();
             }
@@ -3554,11 +3564,11 @@ public class TextView : View
                 Rune rune = idxCol >= lineRuneCount ? (Rune)' ' : line [idxCol].Rune;
                 int cols = rune.GetColumns ();
 
-                if (idxCol < line.Count && Selecting && PointInSelection (idxCol, idxRow))
+                if (idxCol < line.Count && IsSelecting && PointInSelection (idxCol, idxRow))
                 {
                     OnDrawSelectionColor (line, idxCol, idxRow);
                 }
-                else if (idxCol == CurrentColumn && idxRow == CurrentRow && !Selecting && !Used && HasFocus && idxCol < lineRuneCount)
+                else if (idxCol == CurrentColumn && idxRow == CurrentRow && !IsSelecting && !Used && HasFocus && idxCol < lineRuneCount)
                 {
                     OnDrawUsedColor (line, idxCol, idxRow);
                 }
@@ -3740,7 +3750,7 @@ public class TextView : View
         }
         else
         {
-            if (Selecting)
+            if (IsSelecting)
             {
                 ClearRegion ();
             }
@@ -3748,7 +3758,7 @@ public class TextView : View
             _copyWithoutSelection = false;
             InsertAllText (contents);
 
-            if (Selecting)
+            if (IsSelecting)
             {
                 _historyText.ReplaceLast (
                                           new () { new (GetCurrentLine ()) },
@@ -3761,7 +3771,7 @@ public class TextView : View
         }
 
         UpdateWrapModel ();
-        Selecting = false;
+        IsSelecting = false;
         DoNeededAction ();
     }
 
@@ -3775,7 +3785,7 @@ public class TextView : View
             return null;
         }
 
-        if (Application.MouseGrabView == this && Selecting)
+        if (Application.MouseGrabView == this && IsSelecting)
         {
             // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
             //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);
@@ -4293,7 +4303,7 @@ public class TextView : View
         }
 
         UpdateWrapModel ();
-        Selecting = false;
+        IsSelecting = false;
         DoNeededAction ();
     }
 
@@ -4866,7 +4876,7 @@ public class TextView : View
 
         _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
 
-        if (Selecting)
+        if (IsSelecting)
         {
             ClearSelectedRegion ();
         }
@@ -5266,7 +5276,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5466,7 +5476,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5813,7 +5823,7 @@ public class TextView : View
     private bool ProcessMoveDown ()
     {
         ResetContinuousFindTrack ();
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5832,7 +5842,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5858,7 +5868,7 @@ public class TextView : View
 
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5890,7 +5900,7 @@ public class TextView : View
 
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5911,7 +5921,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5930,7 +5940,7 @@ public class TextView : View
     {
         ResetContinuousFindTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5949,7 +5959,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5968,7 +5978,7 @@ public class TextView : View
     {
         ResetAllTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -5987,7 +5997,7 @@ public class TextView : View
     {
         ResetColumnTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -6006,7 +6016,7 @@ public class TextView : View
     {
         ResetColumnTrack ();
 
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             StopSelecting ();
         }
@@ -6033,7 +6043,7 @@ public class TextView : View
         Paste ();
     }
 
-    private bool ProcessReturn ()
+    private bool ProcessEnterKey (CommandContext ctx)
     {
         ResetColumnTrack ();
 
@@ -6046,7 +6056,7 @@ public class TextView : View
         {
             // By Default pressing ENTER should be ignored (OnAccept will return false or null). Only cancel if the
             // event was fired and set Cancel = true.
-            return OnAccept () == false;
+            return RaiseAccepting (ctx) is null or false;
         }
 
         SetWrapModel ();
@@ -6055,7 +6065,7 @@ public class TextView : View
 
         _historyText.Add (new () { new (currentLine) }, CursorPosition);
 
-        if (Selecting)
+        if (IsSelecting)
         {
             ClearSelectedRegion ();
             currentLine = GetCurrentLine ();
@@ -6164,8 +6174,8 @@ public class TextView : View
     {
         if (!_continuousFind)
         {
-            int col = Selecting ? _selectionStartColumn : CurrentColumn;
-            int row = Selecting ? _selectionStartRow : CurrentRow;
+            int col = IsSelecting ? _selectionStartColumn : CurrentColumn;
+            int row = IsSelecting ? _selectionStartRow : CurrentRow;
             _model.ResetContinuousFind (new (col, row));
         }
     }
@@ -6292,13 +6302,13 @@ public class TextView : View
 
     private void StartSelecting ()
     {
-        if (_shiftSelecting && Selecting)
+        if (_shiftSelecting && IsSelecting)
         {
             return;
         }
 
         _shiftSelecting = true;
-        Selecting = true;
+        IsSelecting = true;
         _selectionStartColumn = CurrentColumn;
         _selectionStartRow = CurrentRow;
     }
@@ -6306,7 +6316,7 @@ public class TextView : View
     private void StopSelecting ()
     {
         _shiftSelecting = false;
-        Selecting = false;
+        IsSelecting = false;
         _isButtonShift = false;
     }
 
@@ -6362,7 +6372,7 @@ public class TextView : View
     private void ToggleSelecting ()
     {
         ResetColumnTrack ();
-        Selecting = !Selecting;
+        IsSelecting = !IsSelecting;
         _selectionStartColumn = CurrentColumn;
         _selectionStartRow = CurrentRow;
     }

+ 16 - 15
Terminal.Gui/Views/Toplevel.cs

@@ -72,9 +72,9 @@ public partial class Toplevel : View
     /// <summary>Gets the latest <see cref="MenuBar"/> added into this Toplevel.</summary>
     public MenuBar? MenuBar => (MenuBar?)Subviews?.LastOrDefault (s => s is MenuBar);
 
-    // TODO: Deprecate - Any view can host a statusbar in v2
-    /// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
-    public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
+    //// TODO: Deprecate - Any view can host a statusbar in v2
+    ///// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
+    //public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
 
     #endregion
 
@@ -225,8 +225,9 @@ public partial class Toplevel : View
                                                             top.Frame.X,
                                                             top.Frame.Y,
                                                             out int nx,
-                                                            out int ny,
-                                                            out StatusBar? sb
+                                                            out int ny
+                                                           //,
+                                                           // out StatusBar? sb
                                                            );
 
         if (superView is null)
@@ -260,16 +261,16 @@ public partial class Toplevel : View
             }
         }
 
-        // TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
-        if (sb != null
-            && !top!.Subviews.Contains (sb)
-            && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
-            && top.Height is DimFill
-            && -top.Height.GetAnchor (0) < 1)
-        {
-            top.Height = Dim.Fill (sb.Visible ? 1 : 0);
-            layoutSubviews = true;
-        }
+        //// TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
+        //if (sb != null
+        //    && !top!.Subviews.Contains (sb)
+        //    && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
+        //    && top.Height is DimFill
+        //    && -top.Height.GetAnchor (0) < 1)
+        //{
+        //    top.Height = Dim.Fill (sb.Visible ? 1 : 0);
+        //    layoutSubviews = true;
+        //}
 
         if (superView.LayoutNeeded || layoutSubviews)
         {

+ 5 - 3
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -296,6 +296,8 @@ public class TreeView<T> : View, ITreeView where T : class
         KeyBindings.Add (Key.Home, Command.Start);
         KeyBindings.Add (Key.End, Command.End);
         KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
+
+        KeyBindings.Remove (ObjectActivationKey);
         KeyBindings.Add (ObjectActivationKey, Command.Select);
     }
 
@@ -441,10 +443,10 @@ public class TreeView<T> : View, ITreeView where T : class
     ///     <para>This method also ensures that the selected object is visible.</para>
     /// </summary>
     /// <returns><see langword="true"/> if <see cref="ObjectActivated"/> was fired.</returns>
-    public bool? ActivateSelectedObjectIfAny ()
+    public bool? ActivateSelectedObjectIfAny (CommandContext ctx)
     {
         // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired.
-        if (OnAccept () == true)
+        if (RaiseAccepting (ctx) == true)
         {
             return true;
         }
@@ -1227,7 +1229,7 @@ public class TreeView<T> : View, ITreeView where T : class
             {
                 Move (0, idx - ScrollOffsetVertical);
 
-                return MultiSelect ? new (0, idx - ScrollOffsetVertical) : null ;
+                return MultiSelect ? new (0, idx - ScrollOffsetVertical) : null;
             }
         }
         return base.PositionCursor ();

+ 0 - 21
Terminal.Gui/Views/Window.cs

@@ -31,27 +31,6 @@ public class Window : Toplevel
         ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property
         BorderStyle = DefaultBorderStyle;
         ShadowStyle = DefaultShadow;
-
-        // This enables the default button to be activated by the Enter key.
-        AddCommand (
-                    Command.Accept,
-                    () =>
-                    {
-                        // TODO: Perhaps all views should support the concept of being default?
-                        // ReSharper disable once InvertIf
-                        if (Subviews.FirstOrDefault (v => v is Button { IsDefault: true, Enabled: true }) is Button
-                            defaultBtn)
-                        {
-                            defaultBtn.InvokeCommand (Command.Accept);
-
-                            return true;
-                        }
-
-                        return OnAccept ();
-                    }
-                   );
-
-        KeyBindings.Add (Key.Enter, Command.Accept);
     }
 
     // TODO: enable this

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

@@ -85,8 +85,8 @@ public class Wizard : Dialog
         AddButton (BackButton);
         AddButton (NextFinishButton);
 
-        BackButton.Accept += BackBtn_Clicked;
-        NextFinishButton.Accept += NextfinishBtn_Clicked;
+        BackButton.Accepting += BackBtn_Clicked;
+        NextFinishButton.Accepting += NextfinishBtn_Clicked;
 
         Loaded += Wizard_Loaded;
         Closing += Wizard_Closing;

+ 5 - 5
UICatalog/KeyBindingsDialog.cs

@@ -19,8 +19,8 @@ internal class KeyBindingsDialog : Dialog
     {
         Title = "Keybindings";
 
-        Height = Dim.Percent (80);
-        Width = Dim.Percent (80);
+        //Height = Dim.Percent (80);
+        //Width = Dim.Percent (80);
         if (ViewTracker.Instance == null)
         {
             ViewTracker.Initialize ();
@@ -44,11 +44,11 @@ internal class KeyBindingsDialog : Dialog
 
         var btnChange = new Button { X = Pos.Percent (50), Y = 1, Text = "Ch_ange" };
         Add (btnChange);
-        btnChange.Accept += RemapKey;
+        btnChange.Accepting += RemapKey;
 
         var close = new Button { Text = "Ok" };
 
-        close.Accept += (s, e) =>
+        close.Accepting += (s, e) =>
                          {
                              Application.RequestStop ();
                              ViewTracker.Instance.StartUsingNewKeyMap (CurrentBindings);
@@ -56,7 +56,7 @@ internal class KeyBindingsDialog : Dialog
         AddButton (close);
 
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accept += (s, e) => Application.RequestStop ();
+        cancel.Accepting += (s, e) => Application.RequestStop ();
         AddButton (cancel);
 
         // Register event handler as the last thing in constructor to prevent early calls

+ 0 - 1
UICatalog/Scenario.cs

@@ -82,7 +82,6 @@ namespace UICatalog;
 public class Scenario : IDisposable
 {
     private static int _maxScenarioNameLen = 30;
-    public string Theme = "Default";
     public string TopLevelColorScheme = "Base";
     private bool _disposedValue;
 

+ 5 - 3
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -54,7 +54,7 @@ public class ASCIICustomButtonTest : Scenario
             ]
         };
 
-        _scrollViewTestWindow = new ScrollViewTestWindow ();
+        _scrollViewTestWindow = new ScrollViewTestWindow { Y = Pos.Bottom (menu) };
 
         top.Add (menu, _scrollViewTestWindow);
         Application.Run (top);
@@ -198,7 +198,7 @@ public class ASCIICustomButtonTest : Scenario
                     Height = BUTTON_HEIGHT
                 };
                 button.Initialized += Button_Initialized;
-                button.Accept += Button_Clicked;
+                button.Accepting += Button_Clicked;
                 button.PointerEnter += Button_PointerEnter;
                 button.MouseClick += Button_MouseClick;
                 button.KeyDown += Button_KeyPress;
@@ -216,7 +216,7 @@ public class ASCIICustomButtonTest : Scenario
                 Height = BUTTON_HEIGHT
             };
             closeButton.Initialized += Button_Initialized;
-            closeButton.Accept += Button_Clicked;
+            closeButton.Accepting += Button_Clicked;
             closeButton.PointerEnter += Button_PointerEnter;
             closeButton.MouseClick += Button_MouseClick;
             closeButton.KeyDown += Button_KeyPress;
@@ -241,6 +241,8 @@ public class ASCIICustomButtonTest : Scenario
             {
                 Add (titleLabel, _scrollView);
             }
+
+            Y = 1;
         }
         private void Button_Initialized (object sender, EventArgs e)
         {

+ 1 - 1
UICatalog/Scenarios/AdornmentEditor.cs

@@ -146,7 +146,7 @@ public class AdornmentEditor : View
             Enabled = false
         };
 
-        copyTop.Accept += (s, e) =>
+        copyTop.Accepting += (s, e) =>
                           {
                               AdornmentToEdit.Thickness = new (_topEdit.Value);
                               _leftEdit.Value = _rightEdit.Value = _bottomEdit.Value = _topEdit.Value;

+ 4 - 4
UICatalog/Scenarios/Adornments.cs

@@ -36,7 +36,7 @@ public class Adornments : Scenario
 
             // X = Pos.Center (),
             Width = Dim.Percent (60),
-            Height = Dim.Percent (80)
+            Height = Dim.Percent (90)
         };
         app.Add (window);
 
@@ -57,7 +57,7 @@ public class Adornments : Scenario
 
         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
 
-        button.Accept += (s, e) =>
+        button.Accepting += (s, e) =>
                              MessageBox.Query (20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
 
         var label = new TextView
@@ -113,7 +113,7 @@ public class Adornments : Scenario
                                       Text = "some text",
                                       CanFocus = true
                                   };
-                                  textFieldInPadding.Accept += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
+                                  textFieldInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
                                   window.Padding.Add (textFieldInPadding);
 
                                   var btnButtonInPadding = new Button
@@ -123,7 +123,7 @@ public class Adornments : Scenario
                                       Text = "_Button in Padding",
                                       CanFocus = true
                                   };
-                                  btnButtonInPadding.Accept += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
+                                  btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
                                   btnButtonInPadding.BorderStyle = LineStyle.Dashed;
                                   btnButtonInPadding.Border.Thickness = new (1, 1, 1, 1);
                                   window.Padding.Add (btnButtonInPadding);

+ 55 - 51
UICatalog/Scenarios/AllViewsTester.cs

@@ -50,7 +50,7 @@ public class AllViewsTester : Scenario
     {
         // Don't create a sub-win (Scenario.Win); just use Application.Top
         Application.Init ();
-        ConfigurationManager.Apply ();
+   //     ConfigurationManager.Apply ();
 
         var app = new Window
         {
@@ -88,18 +88,10 @@ public class AllViewsTester : Scenario
 
         _classListView.SelectedItemChanged += (s, args) =>
                                               {
-                                                  // Remove existing class, if any
-                                                  if (_curView != null)
-                                                  {
-                                                      _curView.LayoutComplete -= LayoutCompleteHandler;
-                                                      _hostPane.Remove (_curView);
-                                                      _curView.Dispose ();
-                                                      _curView = null;
-                                                  }
+                                                  // Dispose existing current View, if any
+                                                  DisposeCurrentView ();
 
-                                                  _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
-                                                  // Add
-                                                  _hostPane.Add (_curView);
+                                                  CreateCurrentView (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
 
                                                   // Force ViewToEdit to be the view and not a subview
                                                   if (_adornmentsEditor is { })
@@ -157,10 +149,10 @@ public class AllViewsTester : Scenario
         var label = new Label { X = 0, Y = 0, Text = "X:" };
         _locationFrame.Add (label);
         _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _xRadioGroup.SelectedItemChanged += OnXRadioGroupOnSelectedItemChanged;
+        _xRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
         _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
 
-        _xText.Accept += (s, args) =>
+        _xText.Accepting += (s, args) =>
                          {
                              try
                              {
@@ -179,7 +171,7 @@ public class AllViewsTester : Scenario
         _locationFrame.Add (label);
         _yText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
 
-        _yText.Accept += (s, args) =>
+        _yText.Accepting += (s, args) =>
                          {
                              try
                              {
@@ -191,7 +183,7 @@ public class AllViewsTester : Scenario
                          };
         _locationFrame.Add (_yText);
         _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _yRadioGroup.SelectedItemChanged += OnYRadioGroupOnSelectedItemChanged;
+        _yRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
         _locationFrame.Add (_yRadioGroup);
 
         _sizeFrame = new ()
@@ -208,10 +200,10 @@ public class AllViewsTester : Scenario
         label = new () { X = 0, Y = 0, Text = "Width:" };
         _sizeFrame.Add (label);
         _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _wRadioGroup.SelectedItemChanged += OnWRadioGroupOnSelectedItemChanged;
+        _wRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
         _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
 
-        _wText.Accept += (s, args) =>
+        _wText.Accepting += (s, args) =>
                          {
                              try
                              {
@@ -242,7 +234,7 @@ public class AllViewsTester : Scenario
         _sizeFrame.Add (label);
         _hText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_hVal}" };
 
-        _hText.Accept += (s, args) =>
+        _hText.Accepting += (s, args) =>
                          {
                              try
                              {
@@ -268,7 +260,7 @@ public class AllViewsTester : Scenario
         _sizeFrame.Add (_hText);
 
         _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _hRadioGroup.SelectedItemChanged += OnHRadioGroupOnSelectedItemChanged;
+        _hRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
         _sizeFrame.Add (_hRadioGroup);
 
         _settingsPane.Add (_sizeFrame);
@@ -326,6 +318,11 @@ public class AllViewsTester : Scenario
             ColorScheme = Colors.ColorSchemes ["Dialog"]
         };
 
+        _hostPane.LayoutStarted += (sender, args) =>
+                                   {
+
+                                   };
+
         app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
 
         _classListView.SelectedItem = 0;
@@ -336,17 +333,13 @@ public class AllViewsTester : Scenario
         Application.Shutdown ();
     }
 
-    private void OnHRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
-
-    private void OnWRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
-
-    private void OnYRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
-
-    private void OnXRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
+    private void OnRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
 
     // TODO: Add Command.HotKey handler (pop a message box?)
-    private View CreateClass (Type type)
+    private void CreateCurrentView (Type type)
     {
+        Debug.Assert(_curView is null);
+
         // If we are to create a generic Type
         if (type.IsGenericType)
         {
@@ -386,14 +379,29 @@ public class AllViewsTester : Scenario
             _orientation.Enabled = false;
         }
 
-        view.Initialized += View_Initialized;
+        view.Initialized += CurrentView_Initialized;
+        view.LayoutComplete += CurrentView_LayoutComplete;
 
-        return view;
+        _curView = view;
+        _hostPane.Add (_curView);
+       // Application.Refresh();
+    }
+
+    private void DisposeCurrentView ()
+    {
+        if (_curView != null)
+        {
+            _curView.Initialized -= CurrentView_Initialized;
+            _curView.LayoutComplete -= CurrentView_LayoutComplete;
+            _hostPane.Remove (_curView);
+            _curView.Dispose ();
+            _curView = null;
+        }
     }
 
     private void DimPosChanged (View view)
     {
-        if (view == null)
+        if (view == null || _updatingSettings)
         {
             return;
         }
@@ -463,7 +471,7 @@ public class AllViewsTester : Scenario
             _hText.Enabled = true;
         }
 
-        UpdateTitle (view);
+        UpdateHostTitle (view);
     }
 
     private List<Type> GetAllViewClassesCollection ()
@@ -484,14 +492,16 @@ public class AllViewsTester : Scenario
         return types;
     }
 
-    private void LayoutCompleteHandler (object sender, LayoutEventArgs args)
+    private void CurrentView_LayoutComplete (object sender, LayoutEventArgs args)
     {
         UpdateSettings (_curView);
-        UpdateTitle (_curView);
+        UpdateHostTitle (_curView);
     }
 
+    private bool _updatingSettings = false;
     private void UpdateSettings (View view)
     {
+        _updatingSettings = true;
         var x = view.X.ToString ();
         var y = view.Y.ToString ();
 
@@ -514,58 +524,52 @@ public class AllViewsTester : Scenario
         _wRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => w.Contains (s)));
         _hRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => h.Contains (s)));
 
-        if (view.Width is DimAuto)
+        if (view.Width.Has<DimAuto> (out _))
         {
             _wText.Text = "Auto";
             _wText.Enabled = false;
         }
         else
         {
-            _wText.Text = "100";
+            _wText.Text = $"{view.Frame.Width}";
             _wText.Enabled = true;
         }
 
-        if (view.Height is DimAuto)
+        if (view.Height.Has<DimAuto> (out _))
         {
             _hText.Text = "Auto";
             _hText.Enabled = false;
         }
         else
         {
-            _hText.Text = "100";
+            _hText.Text = $"{view.Frame.Height}";
             _hText.Enabled = true;
         }
+
+        _updatingSettings = false;
     }
 
-    private void UpdateTitle (View view) { _hostPane.Title = $"{view.GetType ().Name} - {view.X}, {view.Y}, {view.Width}, {view.Height}"; }
+    private void UpdateHostTitle (View view) { _hostPane.Title = $"_Demo of {view.GetType ().Name}"; }
 
-    private void View_Initialized (object sender, EventArgs e)
+    private void CurrentView_Initialized (object sender, EventArgs e)
     {
         if (sender is not View view)
         {
             return;
         }
 
-        if (view.Width is not DimAuto && (view.Width is null || view.Frame.Width == 0))
+        if (!view.Width!.Has<DimAuto> (out _) || (view.Width is null || view.Frame.Width == 0))
         {
             view.Width = Dim.Fill ();
         }
 
-        if (view.Height is not DimAuto && (view.Height is null || view.Frame.Height == 0))
+        if (!view.Height!.Has<DimAuto> (out _) || (view.Height is null || view.Frame.Height == 0))
         {
             view.Height = Dim.Fill ();
         }
 
-        _xRadioGroup.SelectedItemChanged -= OnXRadioGroupOnSelectedItemChanged;
-        _yRadioGroup.SelectedItemChanged -= OnYRadioGroupOnSelectedItemChanged;
-        _hRadioGroup.SelectedItemChanged -= OnHRadioGroupOnSelectedItemChanged;
-        _wRadioGroup.SelectedItemChanged -= OnWRadioGroupOnSelectedItemChanged;
         UpdateSettings (view);
-        _xRadioGroup.SelectedItemChanged += OnXRadioGroupOnSelectedItemChanged;
-        _yRadioGroup.SelectedItemChanged += OnYRadioGroupOnSelectedItemChanged;
-        _hRadioGroup.SelectedItemChanged += OnHRadioGroupOnSelectedItemChanged;
-        _wRadioGroup.SelectedItemChanged += OnWRadioGroupOnSelectedItemChanged;
 
-        UpdateTitle (view);
+        UpdateHostTitle (view);
     }
 }

+ 7 - 7
UICatalog/Scenarios/Bars.cs

@@ -168,9 +168,9 @@ public class Bars : Scenario
         };
         popOverMenu.Add (toggleShortcut);
 
-        popOverMenu.Accept += PopOverMenuOnAccept;
+        popOverMenu.Accepting += PopOverMenuOnAccept;
 
-        void PopOverMenuOnAccept (object o, HandledEventArgs handledEventArgs)
+        void PopOverMenuOnAccept (object o, CommandEventArgs args)
         {
             if (popOverMenu.Visible)
             {
@@ -255,7 +255,7 @@ public class Bars : Scenario
             {
                 foreach (Shortcut sh in barView.Subviews.Where (s => s is Shortcut)!)
                 {
-                    sh.Accept += (o, args) =>
+                    sh.Accepting += (o, args) =>
                                  {
                                      eventSource.Add ($"Accept: {sh!.SuperView.Id} {sh!.CommandView.Text}");
                                      eventLog.MoveDown ();
@@ -522,14 +522,14 @@ public class Bars : Scenario
             Text = "I'll Hide",
             // Visible = false
         };
-        button1.Accept += Button_Clicked;
+        button1.Accepting += Button_Clicked;
         bar.Add (button1);
 
-        shortcut.Accept += (s, e) =>
+        shortcut.Accepting += (s, e) =>
                                                     {
                                                         button1.Visible = !button1.Visible;
                                                         button1.Enabled = button1.Visible;
-                                                        e.Handled = false;
+                                                        e.Cancel = false;
                                                     };
 
         bar.Add (new Label
@@ -543,7 +543,7 @@ public class Bars : Scenario
         {
             Text = "Or me!",
         };
-        button2.Accept += (s, e) => Application.RequestStop ();
+        button2.Accepting += (s, e) => Application.RequestStop ();
 
         bar.Add (button2);
 

+ 65 - 21
UICatalog/Scenarios/Buttons.cs

@@ -32,9 +32,12 @@ public class Buttons : Scenario
         // This is the default button (IsDefault = true); if user presses ENTER in the TextField
         // the scenario will quit
         var defaultButton = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (), IsDefault = true, Text = "_Quit" };
-        defaultButton.Accept += (s, e) => Application.RequestStop ();
+
         main.Add (defaultButton);
 
+        // Note we handle Accept on main, not defaultButton
+        main.Accepting += (s, e) => Application.RequestStop ();
+
         var swapButton = new Button
         {
             X = 50,
@@ -44,19 +47,31 @@ public class Buttons : Scenario
             ColorScheme = Colors.ColorSchemes ["Error"]
         };
 
-        swapButton.Accept += (s, e) =>
+        swapButton.Accepting += (s, e) =>
                              {
+                                 e.Cancel = !swapButton.IsDefault;
                                  defaultButton.IsDefault = !defaultButton.IsDefault;
                                  swapButton.IsDefault = !swapButton.IsDefault;
                              };
+
+        defaultButton.Accepting += (s, e) =>
+                                {
+                                    e.Cancel = !defaultButton.IsDefault;
+
+                                    if (e.Cancel)
+                                    {
+                                        MessageBox.ErrorQuery ("Error", "This button is no longer the Quit button; the Swap Default button is.", "_Ok");
+                                    }
+                                };
         main.Add (swapButton);
 
         static void DoMessage (Button button, string txt)
         {
-            button.Accept += (s, e) =>
+            button.Accepting += (s, e) =>
                              {
                                  string btnText = button.Text;
                                  MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
+                                 e.Cancel = true;
                              };
         }
 
@@ -96,11 +111,19 @@ public class Buttons : Scenario
         main.Add (
                   button = new () { X = 2, Y = Pos.Bottom (button) + 1, Height = 2, Text = "a Newline\nin the button" }
                  );
-        button.Accept += (s, e) => MessageBox.Query ("Message", "Question?", "Yes", "No");
+        button.Accepting += (s, e) =>
+                         {
+                             MessageBox.Query ("Message", "Question?", "Yes", "No");
+                             e.Cancel = true;
+                         };
 
         var textChanger = new Button { X = 2, Y = Pos.Bottom (button) + 1, Text = "Te_xt Changer" };
         main.Add (textChanger);
-        textChanger.Accept += (s, e) => textChanger.Text += "!";
+        textChanger.Accepting += (s, e) =>
+                              {
+                                  textChanger.Text += "!";
+                                  e.Cancel = true;
+                              };
 
         main.Add (
                   button = new ()
@@ -110,6 +133,7 @@ public class Buttons : Scenario
                       Text = "Lets see if this will move as \"Text Changer\" grows"
                   }
                  );
+        button.Accepting += (sender, args) => { args.Cancel = true; };
 
         var removeButton = new Button
         {
@@ -119,9 +143,10 @@ public class Buttons : Scenario
         main.Add (removeButton);
 
         // This in interesting test case because `moveBtn` and below are laid out relative to this one!
-        removeButton.Accept += (s, e) =>
+        removeButton.Accepting += (s, e) =>
                                {
                                    removeButton.Visible = false;
+                                   e.Cancel = true;
                                };
 
         var computedFrame = new FrameView
@@ -144,9 +169,10 @@ public class Buttons : Scenario
             Text = "Move This \u263b Button v_ia Pos"
         };
 
-        moveBtn.Accept += (s, e) =>
+        moveBtn.Accepting += (s, e) =>
                           {
                               moveBtn.X = moveBtn.Frame.X + 5;
+                              e.Cancel = true;
                           };
         computedFrame.Add (moveBtn);
 
@@ -160,9 +186,10 @@ public class Buttons : Scenario
             ColorScheme = Colors.ColorSchemes ["Error"],
         };
 
-        sizeBtn.Accept += (s, e) =>
+        sizeBtn.Accepting += (s, e) =>
                           {
                               sizeBtn.Width = sizeBtn.Frame.Width + 5;
+                              e.Cancel = true;
                           };
         computedFrame.Add (sizeBtn);
 
@@ -179,7 +206,7 @@ public class Buttons : Scenario
         // Demonstrates how changing the View.Frame property can move Views
         var moveBtnA = new Button { ColorScheme = Colors.ColorSchemes ["Error"], Text = "Move This Button via Frame" };
 
-        moveBtnA.Accept += (s, e) =>
+        moveBtnA.Accepting += (s, e) =>
                            {
                                moveBtnA.Frame = new (
                                                      moveBtnA.Frame.X + 5,
@@ -187,6 +214,7 @@ public class Buttons : Scenario
                                                      moveBtnA.Frame.Width,
                                                      moveBtnA.Frame.Height
                                                     );
+                               e.Cancel = true;
                            };
         absoluteFrame.Add (moveBtnA);
 
@@ -196,7 +224,7 @@ public class Buttons : Scenario
             Y = 2, ColorScheme = Colors.ColorSchemes ["Error"], Text = " ~  s  gui.cs   master ↑_10 = Сохранить"
         };
 
-        sizeBtnA.Accept += (s, e) =>
+        sizeBtnA.Accepting += (s, e) =>
                            {
                                sizeBtnA.Frame = new (
                                                      sizeBtnA.Frame.X,
@@ -204,13 +232,14 @@ public class Buttons : Scenario
                                                      sizeBtnA.Frame.Width + 5,
                                                      sizeBtnA.Frame.Height
                                                     );
+                               e.Cancel = true;
                            };
         absoluteFrame.Add (sizeBtnA);
 
         var label = new Label
         {
-            X = 2, Y = Pos.Bottom (computedFrame) + 1, 
-            Text = "Text Alignment (changes the four buttons above): "
+            X = 2, Y = Pos.Bottom (computedFrame) + 1,
+            Text = "Text Ali_gnment (changes the four buttons above): "
         };
         main.Add (label);
 
@@ -219,7 +248,10 @@ public class Buttons : Scenario
             X = 4,
             Y = Pos.Bottom (label) + 1,
             SelectedItem = 2,
-            RadioLabels = new [] { "Start", "End", "Center", "Fill" }
+            RadioLabels = new [] { "_Start", "_End", "_Center", "_Fill" },
+            Title = "_9 RadioGroup",
+            BorderStyle = LineStyle.Dotted,
+            // CanFocus = false
         };
         main.Add (radioGroup);
 
@@ -265,7 +297,11 @@ public class Buttons : Scenario
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Text = mhkb
         };
-        moveHotKeyBtn.Accept += (s, e) => { moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text); };
+        moveHotKeyBtn.Accepting += (s, e) =>
+                                {
+                                    moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text);
+                                    e.Cancel = true;
+                                };
         main.Add (moveHotKeyBtn);
 
         var muhkb = " ~  s  gui.cs   master ↑10 = Сохранить";
@@ -278,7 +314,11 @@ public class Buttons : Scenario
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Text = muhkb
         };
-        moveUnicodeHotKeyBtn.Accept += (s, e) => { moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text); };
+        moveUnicodeHotKeyBtn.Accepting += (s, e) =>
+                                       {
+                                           moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text);
+                                           e.Cancel = true;
+                                       };
         main.Add (moveUnicodeHotKeyBtn);
 
         radioGroup.SelectedItemChanged += (s, args) =>
@@ -358,7 +398,11 @@ public class Buttons : Scenario
             Title = $"Accept Cou_nt: {noRepeatAcceptCount}",
             WantContinuousButtonPressed = false
         };
-        noRepeatButton.Accept += (s, e) => { noRepeatButton.Title = $"Accept Cou_nt: {++noRepeatAcceptCount}"; };
+        noRepeatButton.Accepting += (s, e) =>
+                                 {
+                                     noRepeatButton.Title = $"Accept Cou_nt: {++noRepeatAcceptCount}";
+                                     e.Cancel = true;
+                                 };
         main.Add (label, noRepeatButton);
 
         label = new ()
@@ -376,7 +420,11 @@ public class Buttons : Scenario
             Title = $"Accept Co_unt: {acceptCount}",
             WantContinuousButtonPressed = true
         };
-        repeatButton.Accept += (s, e) => { repeatButton.Title = $"Accept Co_unt: {++acceptCount}"; };
+        repeatButton.Accepting += (s, e) =>
+                               {
+                                   repeatButton.Title = $"Accept Co_unt: {++acceptCount}";
+                                   e.Cancel = true;
+                               };
 
         var enableCB = new CheckBox
         {
@@ -399,10 +447,6 @@ public class Buttons : Scenario
 
         main.Add (decNumericUpDown);
 
-        main.Ready += (s, e) =>
-                      {
-                          radioGroup.Refresh ();
-                      };
         Application.Run (main);
         main.Dispose ();
         Application.Shutdown ();

+ 10 - 11
UICatalog/Scenarios/CharacterMap.cs

@@ -77,7 +77,7 @@ public class CharacterMap : Scenario
         };
         top.Add (_errorLabel);
 
-        jumpEdit.Accept += JumpEditOnAccept;
+        jumpEdit.Accepting += JumpEditOnAccept;
 
         _categoryList = new () { X = Pos.Right (_charMap), Y = Pos.Bottom (jumpLabel), Height = Dim.Fill () };
         _categoryList.FullRowSelect = true;
@@ -173,7 +173,7 @@ public class CharacterMap : Scenario
 
         return;
 
-        void JumpEditOnAccept (object sender, HandledEventArgs e)
+        void JumpEditOnAccept (object sender, CommandEventArgs e)
         {
             if (jumpEdit.Text.Length == 0)
             {
@@ -245,7 +245,7 @@ public class CharacterMap : Scenario
 
 
             // Cancel the event to prevent ENTER from being handled elsewhere
-            e.Handled = true;
+            e.Cancel = true;
         }
     }
 
@@ -449,7 +449,6 @@ internal class CharMap : View
                     }
                    );
 
-        KeyBindings.Add (Key.Enter, Command.Accept);
         KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
         KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
         KeyBindings.Add (Key.CursorLeft, Command.ScrollLeft);
@@ -478,7 +477,7 @@ internal class CharMap : View
             ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
-        up.Accept += (sender, args) => { args.Handled = ScrollVertical (-1) == true; };
+        up.Accepting += (sender, args) => { args.Cancel = ScrollVertical (-1) == true; };
 
         var down = new Button
         {
@@ -493,7 +492,7 @@ internal class CharMap : View
             ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
-        down.Accept += (sender, args) => { ScrollVertical (1); };
+        down.Accepting += (sender, args) => { ScrollVertical (1); };
 
         var left = new Button
         {
@@ -508,7 +507,7 @@ internal class CharMap : View
             ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
-        left.Accept += (sender, args) => { ScrollHorizontal (-1); };
+        left.Accepting += (sender, args) => { ScrollHorizontal (-1); };
 
         var right = new Button
         {
@@ -523,7 +522,7 @@ internal class CharMap : View
             ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
-        right.Accept += (sender, args) => { ScrollHorizontal (1); };
+        right.Accepting += (sender, args) => { ScrollHorizontal (1); };
 
         Padding.Add (up, down, left, right);
     }
@@ -1016,18 +1015,18 @@ internal class CharMap : View
 
             var dlg = new Dialog { Title = title, Buttons = [copyGlyph, copyCP, cancel] };
 
-            copyGlyph.Accept += (s, a) =>
+            copyGlyph.Accepting += (s, a) =>
                                 {
                                     CopyGlyph ();
                                     dlg.RequestStop ();
                                 };
 
-            copyCP.Accept += (s, a) =>
+            copyCP.Accepting += (s, a) =>
                              {
                                  CopyCodePoint ();
                                  dlg.RequestStop ();
                              };
-            cancel.Accept += (s, a) => dlg.RequestStop ();
+            cancel.Accepting += (s, a) => dlg.RequestStop ();
 
             var rune = (Rune)SelectedCodePoint;
             var label = new Label { Text = "IsAscii: ", X = 0, Y = 0 };

+ 1 - 1
UICatalog/Scenarios/ChineseUI.cs

@@ -31,7 +31,7 @@ public class ChineseUI : Scenario
 
         var btn = new Button { X = 1, Y = 1, Text = "你" }; // v1: A
 
-        btn.Accept += (s, e) =>
+        btn.Accepting += (s, e) =>
                       {
                           int result = MessageBox.Query (
                                                          "Confirm",

+ 6 - 5
UICatalog/Scenarios/ClassExplorer.cs

@@ -22,11 +22,6 @@ public class ClassExplorer : Scenario
         Application.Init ();
         var top = new Toplevel ();
 
-        var win = new Window
-        {
-            Title = GetName ()
-        };
-
         var menu = new MenuBar
         {
             Menus =
@@ -61,6 +56,12 @@ public class ClassExplorer : Scenario
         };
         top.Add (menu);
 
+        var win = new Window
+        {
+            Title = GetName (),
+            Y = Pos.Bottom (menu)
+        };
+
         _treeView = new TreeView<object> { X = 0, Y = 1, Width = Dim.Percent (50), Height = Dim.Fill () };
 
         var lblSearch = new Label { Text = "Search" };

+ 1 - 1
UICatalog/Scenarios/Clipping.cs

@@ -70,7 +70,7 @@ public class Clipping : Scenario
         };
 
         var testButton = new Button { X = 2, Y = 2, Text = "click me" };
-        testButton.Accept += (s, e) => { MessageBox.Query (10, 5, "Test", "test message", "Ok"); };
+        testButton.Accepting += (s, e) => { MessageBox.Query (10, 5, "Test", "test message", "Ok"); };
         embedded3.Add (testButton);
         embedded2.Add (embedded3);
 

+ 0 - 2
UICatalog/Scenarios/CombiningMarks.cs

@@ -9,8 +9,6 @@ public class CombiningMarks : Scenario
     public override void Main ()
     {
         Application.Init ();
-        ConfigurationManager.Themes!.Theme = Theme;
-        ConfigurationManager.Apply ();
         var top = new Toplevel { ColorScheme = Colors.ColorSchemes [TopLevelColorScheme] };
 
         top.DrawContentComplete += (s, e) =>

+ 2 - 2
UICatalog/Scenarios/ComboBoxIteration.cs

@@ -60,7 +60,7 @@ public class ComboBoxIteration : Scenario
 
         var btnTwo = new Button { X = Pos.Right (comboBox) + 1, Text = "Two" };
 
-        btnTwo.Accept += (s, e) =>
+        btnTwo.Accepting += (s, e) =>
                           {
                               items = ["one", "two"];
                               comboBox.SetSource (items);
@@ -71,7 +71,7 @@ public class ComboBoxIteration : Scenario
 
         var btnThree = new Button { X = Pos.Right (comboBox) + 1, Y = Pos.Top (comboBox), Text = "Three" };
 
-        btnThree.Accept += (s, e) =>
+        btnThree.Accepting += (s, e) =>
                             {
                                 items =["one", "two", "three"];
                                 comboBox.SetSource (items);

+ 4 - 4
UICatalog/Scenarios/ComputedLayout.cs

@@ -363,7 +363,7 @@ public class ComputedLayout : Scenario
         var anchorButton = new Button { Text = "Button using AnchorEnd", Y = Pos.AnchorEnd () };
         anchorButton.X = Pos.AnchorEnd ();
 
-        anchorButton.Accept += (s, e) =>
+        anchorButton.Accepting += (s, e) =>
                                 {
                                     // This demonstrates how to have a dynamically sized button
                                     // Each time the button is clicked the button's text gets longer
@@ -411,7 +411,7 @@ public class ComputedLayout : Scenario
             Y = Pos.AnchorEnd () - 1
         };
 
-        leftButton.Accept += (s, e) =>
+        leftButton.Accepting += (s, e) =>
                               {
                                   // This demonstrates how to have a dynamically sized button
                                   // Each time the button is clicked the button's text gets longer
@@ -429,7 +429,7 @@ public class ComputedLayout : Scenario
             Y = Pos.AnchorEnd (2),
         };
 
-        centerButton.Accept += (s, e) =>
+        centerButton.Accepting += (s, e) =>
                                 {
                                     // This demonstrates how to have a dynamically sized button
                                     // Each time the button is clicked the button's text gets longer
@@ -447,7 +447,7 @@ public class ComputedLayout : Scenario
             Y = Pos.Y (centerButton)
         };
 
-        rightButton.Accept += (s, e) =>
+        rightButton.Accepting += (s, e) =>
                                {
                                    // This demonstrates how to have a dynamically sized button
                                    // Each time the button is clicked the button's text gets longer

+ 1 - 1
UICatalog/Scenarios/ConfigurationEditor.cs

@@ -70,7 +70,7 @@ public class ConfigurationEditor : Scenario
             Key = Key.F5.WithShift,
             Title = "Reload",
         };
-        reloadShortcut.Accept += (s, e) => { Reload (); };
+        reloadShortcut.Accepting += (s, e) => { Reload (); };
 
         var saveShortcut = new Shortcut ()
         {

+ 1 - 1
UICatalog/Scenarios/ContentScrolling.cs

@@ -362,7 +362,7 @@ public class ContentScrolling : Scenario
             Height = Dim.Auto (DimAutoStyle.Content, maximumContentDim: Dim.Percent (20)),
         };
 
-        charMap.Accept += (s, e) =>
+        charMap.Accepting += (s, e) =>
                               MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
 
         var buttonAnchored = new Button

+ 3 - 12
UICatalog/Scenarios/ContextMenus.cs

@@ -25,7 +25,8 @@ public class ContextMenus : Scenario
         // Setup - Create a top-level application window and configure it.
         Window appWindow = new ()
         {
-            Title = GetQuitKeyAndName ()
+            Title = GetQuitKeyAndName (),
+            Arrangement = ViewArrangement.Fixed
         };
 
         var text = "Context Menu";
@@ -93,18 +94,8 @@ public class ContextMenus : Scenario
                                 Application.MouseEvent -= ApplicationMouseEvent;
                             };
 
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new (
-                     "_File",
-                     new MenuItem [] { new ("_Quit", "", () => Application.RequestStop (), null, null, Application.QuitKey) })
-            ]
-        };
-
         var top = new Toplevel ();
-        top.Add (appWindow, menu);
+        top.Add (appWindow);
 
         // Run - Start the application.
         Application.Run (top);

+ 2 - 2
UICatalog/Scenarios/CsvEditor.cs

@@ -304,13 +304,13 @@ public class CsvEditor : Scenario
 
         var ok = new Button { Text = "Ok", IsDefault = true };
 
-        ok.Accept += (s, e) =>
+        ok.Accepting += (s, e) =>
                      {
                          okPressed = true;
                          Application.RequestStop ();
                      };
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accept += (s, e) => { Application.RequestStop (); };
+        cancel.Accepting += (s, e) => { Application.RequestStop (); };
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
 
         var lbl = new Label { X = 0, Y = 1, Text = label };

+ 21 - 12
UICatalog/Scenarios/Dialogs.cs

@@ -22,6 +22,7 @@ public class Dialogs : Scenario
 
         var frame = new FrameView
         {
+            TabStop = TabBehavior.TabStop, // FrameView normally sets to TabGroup
             X = Pos.Center (),
             Y = 1,
             Width = Dim.Percent (75),
@@ -151,15 +152,18 @@ public class Dialogs : Scenario
         };
         frame.Add (label);
 
-        var labels = Enum.GetNames<Alignment> ();
+        // Add hotkeys
+        var labels = Enum.GetNames<Alignment> ().Select (n => n = "_" + n);
         var alignmentGroup = new RadioGroup
         {
             X = Pos.Right (label) + 1,
             Y = Pos.Top (label),
             RadioLabels = labels.ToArray (),
+            Title = "Ali_gn",
+            BorderStyle = LineStyle.Dashed
         };
         frame.Add (alignmentGroup);
-        alignmentGroup.SelectedItem = labels.ToList ().IndexOf (Dialog.DefaultButtonAlignment.ToString ());
+        alignmentGroup.SelectedItem = labels.ToList ().IndexOf ("_" + Dialog.DefaultButtonAlignment.ToString ());
 
         frame.ValidatePosDim = true;
 
@@ -181,7 +185,7 @@ public class Dialogs : Scenario
             X = Pos.Center (), Y = Pos.Bottom (frame) + 2, IsDefault = true, Text = "_Show Dialog"
         };
 
-        showDialogButton.Accept += (s, e) =>
+        app.Accepting += (s, e) =>
                                    {
                                        Dialog dlg = CreateDemoDialog (
                                                                       widthEdit,
@@ -194,6 +198,7 @@ public class Dialogs : Scenario
                                                                      );
                                        Application.Run (dlg);
                                        dlg.Dispose ();
+                                       e.Cancel = true;
                                    };
 
         app.Add (showDialogButton);
@@ -241,18 +246,19 @@ public class Dialogs : Scenario
 
                     button = new ()
                     {
-                        Text = NumberToWords.Convert (buttonId) + " " + char.ConvertFromUtf32 (buttonId + CODE_POINT),
+                        Text = "_" + NumberToWords.Convert (buttonId) + " " + char.ConvertFromUtf32 (buttonId + CODE_POINT),
                         IsDefault = buttonId == 0
                     };
                 }
                 else
                 {
-                    button = new () { Text = NumberToWords.Convert (buttonId), IsDefault = buttonId == 0 };
+                    button = new () { Text = "_" + NumberToWords.Convert (buttonId), IsDefault = buttonId == 0 };
                 }
 
-                button.Accept += (s, e) =>
+                button.Accepting += (s, e) =>
                                  {
                                      clicked = buttonId;
+                                     e.Cancel = true;
                                      Application.RequestStop ();
                                  };
                 buttons.Add (button);
@@ -264,7 +270,7 @@ public class Dialogs : Scenario
             {
                 Title = titleEdit.Text,
                 Text = "Dialog Text",
-                ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentRadioGroup.RadioLabels [alignmentRadioGroup.SelectedItem]),
+                ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentRadioGroup.RadioLabels [alignmentRadioGroup.SelectedItem].Substring (1)),
 
                 Buttons = buttons.ToArray ()
             };
@@ -285,7 +291,7 @@ public class Dialogs : Scenario
                 Text = "_Add a button"
             };
 
-            add.Accept += (s, e) =>
+            add.Accepting += (s, e) =>
                           {
                               int buttonId = buttons.Count;
                               Button button;
@@ -294,19 +300,20 @@ public class Dialogs : Scenario
                               {
                                   button = new ()
                                   {
-                                      Text = NumberToWords.Convert (buttonId) + " " + char.ConvertFromUtf32 (buttonId + CODE_POINT),
+                                      Text = "_" + NumberToWords.Convert (buttonId) + " " + char.ConvertFromUtf32 (buttonId + CODE_POINT),
                                       IsDefault = buttonId == 0
                                   };
                               }
                               else
                               {
-                                  button = new () { Text = NumberToWords.Convert (buttonId), IsDefault = buttonId == 0 };
+                                  button = new () { Text = "_" + NumberToWords.Convert (buttonId), IsDefault = buttonId == 0 };
                               }
 
-                              button.Accept += (s, e) =>
+                              button.Accepting += (s, e) =>
                                                {
                                                    clicked = buttonId;
                                                    Application.RequestStop ();
+                                                   e.Cancel = true;
                                                };
                               buttons.Add (button);
                               dialog.AddButton (button);
@@ -315,6 +322,7 @@ public class Dialogs : Scenario
                               //{
                               //    button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1;
                               //}
+                              e.Cancel = true;
                           };
             dialog.Add (add);
 
@@ -325,7 +333,7 @@ public class Dialogs : Scenario
                 Text = $"A_dd a {char.ConvertFromUtf32 (CODE_POINT)} to each button. This text is really long for a reason."
             };
 
-            addChar.Accept += (s, e) =>
+            addChar.Accepting += (s, e) =>
                               {
                                   foreach (Button button in buttons)
                                   {
@@ -333,6 +341,7 @@ public class Dialogs : Scenario
                                   }
 
                                   dialog.LayoutSubviews ();
+                                  e.Cancel = true;
                               };
             dialog.Add (addChar);
 

+ 1 - 1
UICatalog/Scenarios/DimAutoDemo.cs

@@ -156,7 +156,7 @@ public class DimAutoDemo : Scenario
             Y = Pos.AnchorEnd ()
         };
 
-        resetButton.Accept += (s, e) =>
+        resetButton.Accepting += (s, e) =>
         {
             //movingButton.Y = Pos.Bottom (hlabel);
             //movingButton.X = 0;

+ 21 - 21
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -203,7 +203,7 @@ public class DynamicMenuBar : Scenario
             {
                 X = Pos.X (lblShortcut), Y = Pos.Bottom (TextShortcutKey) + 1, Text = "Clear Shortcut"
             };
-            btnShortcut.Accept += (s, e) => { TextShortcutKey.Text = ""; };
+            btnShortcut.Accepting += (s, e) => { TextShortcutKey.Text = ""; };
             Add (btnShortcut);
 
             CkbIsTopLevel.CheckedStateChanging += (s, e) =>
@@ -247,9 +247,9 @@ public class DynamicMenuBar : Scenario
                                          }
                                      };
 
-            CkbSubMenu.CheckedStateChanging += (s, e) =>
+            CkbSubMenu.CheckedStateChanged += (s, e) =>
                                   {
-                                      if (e.NewValue == CheckState.Checked)
+                                      if (e.CurrentValue == CheckState.Checked)
                                       {
                                           CkbIsTopLevel.CheckedState = CheckState.UnChecked;
                                           CkbIsTopLevel.SetNeedsDisplay ();
@@ -275,16 +275,16 @@ public class DynamicMenuBar : Scenario
                                           if (_hasParent)
                                           {
                                               TextShortcutKey.Enabled = CkbIsTopLevel.CheckedState == CheckState.UnChecked
-                                                                     && e.NewValue == CheckState.UnChecked;
+                                                                     && e.CurrentValue == CheckState.UnChecked;
                                           }
                                       }
                                   };
 
-            CkbNullCheck.CheckedStateChanging += (s, e) =>
+            CkbNullCheck.CheckedStateChanged += (s, e) =>
                                     {
                                         if (_menuItem != null)
                                         {
-                                            _menuItem.AllowNullChecked = e.NewValue == CheckState.Checked;
+                                            _menuItem.AllowNullChecked = e.CurrentValue == CheckState.Checked;
                                         }
                                     };
 
@@ -393,7 +393,7 @@ public class DynamicMenuBar : Scenario
 
             var btnOk = new Button { IsDefault = true, Text = "Ok" };
 
-            btnOk.Accept += (s, e) =>
+            btnOk.Accepting += (s, e) =>
                             {
                                 if (string.IsNullOrEmpty (TextTitle.Text))
                                 {
@@ -407,7 +407,7 @@ public class DynamicMenuBar : Scenario
                             };
             var btnCancel = new Button { Text = "Cancel" };
 
-            btnCancel.Accept += (s, e) =>
+            btnCancel.Accepting += (s, e) =>
                                 {
                                     TextTitle.Text = string.Empty;
                                     Application.RequestStop ();
@@ -666,7 +666,7 @@ public class DynamicMenuBar : Scenario
             };
             Add (frmMenuDetails);
 
-            btnMenuBarUp.Accept += (s, e) =>
+            btnMenuBarUp.Accepting += (s, e) =>
                                     {
                                         int i = _currentSelectedMenuBar;
 
@@ -688,7 +688,7 @@ public class DynamicMenuBar : Scenario
                                         }
                                     };
 
-            btnMenuBarDown.Accept += (s, e) =>
+            btnMenuBarDown.Accepting += (s, e) =>
                                       {
                                           int i = _currentSelectedMenuBar;
 
@@ -710,7 +710,7 @@ public class DynamicMenuBar : Scenario
                                           }
                                       };
 
-            btnUp.Accept += (s, e) =>
+            btnUp.Accepting += (s, e) =>
                              {
                                  int i = _lstMenus.SelectedItem;
                                  MenuItem menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
@@ -732,7 +732,7 @@ public class DynamicMenuBar : Scenario
                                  }
                              };
 
-            btnDown.Accept += (s, e) =>
+            btnDown.Accepting += (s, e) =>
                                {
                                    int i = _lstMenus.SelectedItem;
                                    MenuItem menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
@@ -754,7 +754,7 @@ public class DynamicMenuBar : Scenario
                                    }
                                };
 
-            btnPreviowsParent.Accept += (s, e) =>
+            btnPreviowsParent.Accepting += (s, e) =>
                                          {
                                              if (_currentMenuBarItem != null && _currentMenuBarItem.Parent != null)
                                              {
@@ -787,7 +787,7 @@ public class DynamicMenuBar : Scenario
             Add (btnOk);
 
             var btnCancel = new Button { X = Pos.Right (btnOk) + 3, Y = Pos.Top (btnOk), Text = "Cancel" };
-            btnCancel.Accept += (s, e) => { SetFrameDetails (_currentEditMenuBarItem); };
+            btnCancel.Accepting += (s, e) => { SetFrameDetails (_currentEditMenuBarItem); };
             Add (btnCancel);
 
             txtDelimiter.TextChanging += (s, e) =>
@@ -813,7 +813,7 @@ public class DynamicMenuBar : Scenario
 
             _lstMenus.SelectedItemChanged += (s, e) => { SetFrameDetails (); };
 
-            btnOk.Accept += (s, e) =>
+            btnOk.Accepting += (s, e) =>
                              {
                                  if (string.IsNullOrEmpty (frmMenuDetails.TextTitle.Text) && _currentEditMenuBarItem != null)
                                  {
@@ -840,7 +840,7 @@ public class DynamicMenuBar : Scenario
                                  }
                              };
 
-            btnAdd.Accept += (s, e) =>
+            btnAdd.Accepting += (s, e) =>
                               {
                                   if (MenuBar == null)
                                   {
@@ -883,7 +883,7 @@ public class DynamicMenuBar : Scenario
                                   }
                               };
 
-            btnRemove.Accept += (s, e) =>
+            btnRemove.Accepting += (s, e) =>
                                 {
                                     MenuItem menuItem = (DataContext.Menus.Count > 0 && _lstMenus.SelectedItem > -1
                                                              ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem
@@ -951,7 +951,7 @@ public class DynamicMenuBar : Scenario
                                    SetFrameDetails (menuBarItem);
                                };
 
-            btnNext.Accept += (s, e) =>
+            btnNext.Accepting += (s, e) =>
                                {
                                    if (_menuBar != null && _currentSelectedMenuBar + 1 < _menuBar.Menus.Length)
                                    {
@@ -961,7 +961,7 @@ public class DynamicMenuBar : Scenario
                                    SelectCurrentMenuBarItem ();
                                };
 
-            btnPrevious.Accept += (s, e) =>
+            btnPrevious.Accepting += (s, e) =>
                                    {
                                        if (_currentSelectedMenuBar - 1 > -1)
                                        {
@@ -980,7 +980,7 @@ public class DynamicMenuBar : Scenario
                                      }
                                  };
 
-            btnAddMenuBar.Accept += (s, e) =>
+            btnAddMenuBar.Accepting += (s, e) =>
                                      {
                                          var frameDetails = new DynamicMenuBarDetails (null);
                                          DynamicMenuItem item = frameDetails.EnterMenuItem ();
@@ -1015,7 +1015,7 @@ public class DynamicMenuBar : Scenario
                                          _menuBar.SetNeedsDisplay ();
                                      };
 
-            btnRemoveMenuBar.Accept += (s, e) =>
+            btnRemoveMenuBar.Accepting += (s, e) =>
                                         {
                                             if (_menuBar == null)
                                             {

+ 22 - 22
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -134,7 +134,7 @@ public class DynamicStatusBar : Scenario
             {
                 X = Pos.X (_lblShortcut), Y = Pos.Bottom (TextShortcut) + 1, Text = "Clear Shortcut"
             };
-            _btnShortcut.Accept += (s, e) => { TextShortcut.Text = ""; };
+            _btnShortcut.Accepting += (s, e) => { TextShortcut.Text = ""; };
             Add (_btnShortcut);
         }
 
@@ -181,7 +181,7 @@ public class DynamicStatusBar : Scenario
 
             var btnOk = new Button { IsDefault = true, Text = "OK" };
 
-            btnOk.Accept += (s, e) =>
+            btnOk.Accepting += (s, e) =>
                               {
                                   if (string.IsNullOrEmpty (TextTitle.Text))
                                   {
@@ -196,7 +196,7 @@ public class DynamicStatusBar : Scenario
                               };
             var btnCancel = new Button { Text = "Cancel" };
 
-            btnCancel.Accept += (s, e) =>
+            btnCancel.Accepting += (s, e) =>
                                   {
                                       TextTitle.Text = string.Empty;
                                       Application.RequestStop ();
@@ -311,7 +311,7 @@ public class DynamicStatusBar : Scenario
             };
             Add (_frmStatusBarDetails);
 
-            _btnUp.Accept += (s, e) =>
+            _btnUp.Accepting += (s, e) =>
                               {
                                   int i = _lstItems.SelectedItem;
                                   Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;
@@ -334,7 +334,7 @@ public class DynamicStatusBar : Scenario
                                   }
                               };
 
-            _btnDown.Accept += (s, e) =>
+            _btnDown.Accepting += (s, e) =>
                                 {
                                     int i = _lstItems.SelectedItem;
                                     Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;
@@ -364,12 +364,12 @@ public class DynamicStatusBar : Scenario
             Add (_btnOk);
 
             var _btnCancel = new Button { X = Pos.Right (_btnOk) + 3, Y = Pos.Top (_btnOk), Text = "Cancel" };
-            _btnCancel.Accept += (s, e) => { SetFrameDetails (_currentEditStatusItem); };
+            _btnCancel.Accepting += (s, e) => { SetFrameDetails (_currentEditStatusItem); };
             Add (_btnCancel);
 
             _lstItems.SelectedItemChanged += (s, e) => { SetFrameDetails (); };
 
-            _btnOk.Accept += (s, e) =>
+            _btnOk.Accepting += (s, e) =>
                               {
                                   if (string.IsNullOrEmpty (_frmStatusBarDetails.TextTitle.Text) && _currentEditStatusItem != null)
                                   {
@@ -388,19 +388,19 @@ public class DynamicStatusBar : Scenario
                                   }
                               };
 
-            _btnAdd.Accept += (s, e) =>
+            _btnAdd.Accepting += (s, e) =>
                                {
-                                   if (StatusBar == null)
-                                   {
-                                       MessageBox.ErrorQuery (
-                                                              "StatusBar Bar Error",
-                                                              "Must add a StatusBar first!",
-                                                              "Ok"
-                                                             );
-                                       _btnAddStatusBar.SetFocus ();
-
-                                       return;
-                                   }
+                                   //if (StatusBar == null)
+                                   //{
+                                   //    MessageBox.ErrorQuery (
+                                   //                           "StatusBar Bar Error",
+                                   //                           "Must add a StatusBar first!",
+                                   //                           "Ok"
+                                   //                          );
+                                   //    _btnAddStatusBar.SetFocus ();
+
+                                   //    return;
+                                   //}
 
                                    var frameDetails = new DynamicStatusBarDetails ();
                                    DynamicStatusItem item = frameDetails.EnterStatusItem ();
@@ -418,7 +418,7 @@ public class DynamicStatusBar : Scenario
                                    SetFrameDetails ();
                                };
 
-            _btnRemove.Accept += (s, e) =>
+            _btnRemove.Accepting += (s, e) =>
                                   {
                                       Shortcut statusItem = DataContext.Items.Count > 0
                                                                   ? DataContext.Items [_lstItems.SelectedItem].Shortcut
@@ -448,7 +448,7 @@ public class DynamicStatusBar : Scenario
                                    SetFrameDetails (statusItem);
                                };
 
-            _btnAddStatusBar.Accept += (s, e) =>
+            _btnAddStatusBar.Accepting += (s, e) =>
                                         {
                                             if (_statusBar != null)
                                             {
@@ -459,7 +459,7 @@ public class DynamicStatusBar : Scenario
                                             Add (_statusBar);
                                         };
 
-            _btnRemoveStatusBar.Accept += (s, e) =>
+            _btnRemoveStatusBar.Accepting += (s, e) =>
                                            {
                                                if (_statusBar == null)
                                                {

+ 5 - 5
UICatalog/Scenarios/Editor.cs

@@ -872,7 +872,7 @@ public class Editor : Scenario
 
             Text = "Find _Next"
         };
-        btnFindNext.Accept += (s, e) => FindNext ();
+        btnFindNext.Accepting += (s, e) => FindNext ();
         d.Add (btnFindNext);
 
         var btnFindPrevious = new Button
@@ -882,7 +882,7 @@ public class Editor : Scenario
             Enabled = !string.IsNullOrEmpty (txtToFind.Text),
             Text = "Find _Previous"
         };
-        btnFindPrevious.Accept += (s, e) => FindPrevious ();
+        btnFindPrevious.Accepting += (s, e) => FindPrevious ();
         d.Add (btnFindPrevious);
 
         txtToFind.TextChanged += (s, e) =>
@@ -1099,7 +1099,7 @@ public class Editor : Scenario
             IsDefault = true,
             Text = "Replace _Next"
         };
-        btnFindNext.Accept += (s, e) => ReplaceNext ();
+        btnFindNext.Accepting += (s, e) => ReplaceNext ();
         d.Add (btnFindNext);
 
         label = new ()
@@ -1129,7 +1129,7 @@ public class Editor : Scenario
             Enabled = !string.IsNullOrEmpty (txtToFind.Text),
             Text = "Replace _Previous"
         };
-        btnFindPrevious.Accept += (s, e) => ReplacePrevious ();
+        btnFindPrevious.Accepting += (s, e) => ReplacePrevious ();
         d.Add (btnFindPrevious);
 
         var btnReplaceAll = new Button
@@ -1139,7 +1139,7 @@ public class Editor : Scenario
             Enabled = !string.IsNullOrEmpty (txtToFind.Text),
             Text = "Replace _All"
         };
-        btnReplaceAll.Accept += (s, e) => ReplaceAll ();
+        btnReplaceAll.Accepting += (s, e) => ReplaceAll ();
         d.Add (btnReplaceAll);
 
         txtToFind.TextChanged += (s, e) =>

+ 36 - 35
UICatalog/Scenarios/FileDialogExamples.cs

@@ -2,6 +2,7 @@
 using System.IO;
 using System.IO.Abstractions;
 using System.Linq;
+using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -33,25 +34,25 @@ public class FileDialogExamples : Scenario
         var x = 1;
         var win = new Window { Title = GetQuitKeyAndName () };
 
-        _cbMustExist = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Must Exist" };
+        _cbMustExist = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Must E_xist" };
         win.Add (_cbMustExist);
 
-        _cbUseColors = new CheckBox { CheckedState = FileDialogStyle.DefaultUseColors ? CheckState.Checked : CheckState.UnChecked, Y = y++, X = x, Text = "Use Colors" };
+        _cbUseColors = new CheckBox { CheckedState = FileDialogStyle.DefaultUseColors ? CheckState.Checked : CheckState.UnChecked, Y = y++, X = x, Text = "_Use Colors" };
         win.Add (_cbUseColors);
 
-        _cbCaseSensitive = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Case Sensitive Search" };
+        _cbCaseSensitive = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "_Case Sensitive Search" };
         win.Add (_cbCaseSensitive);
 
-        _cbAllowMultipleSelection = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Multiple" };
+        _cbAllowMultipleSelection = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "_Multiple" };
         win.Add (_cbAllowMultipleSelection);
 
-        _cbShowTreeBranchLines = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Tree Branch Lines" };
+        _cbShowTreeBranchLines = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Tree Branch _Lines" };
         win.Add (_cbShowTreeBranchLines);
 
-        _cbAlwaysTableShowHeaders = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Always Show Headers" };
+        _cbAlwaysTableShowHeaders = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Always Show _Headers" };
         win.Add (_cbAlwaysTableShowHeaders);
 
-        _cbDrivesOnlyInTree = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Only Show Drives" };
+        _cbDrivesOnlyInTree = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Only Show _Drives" };
         win.Add (_cbDrivesOnlyInTree);
 
         y = 0;
@@ -63,7 +64,7 @@ public class FileDialogExamples : Scenario
         win.Add (new Label { X = x++, Y = y++, Text = "Caption" });
 
         _rgCaption = new RadioGroup { X = x, Y = y };
-        _rgCaption.RadioLabels = new [] { "Ok", "Open", "Save" };
+        _rgCaption.RadioLabels = new [] { "_Ok", "O_pen", "_Save" };
         win.Add (_rgCaption);
 
         y = 0;
@@ -75,7 +76,7 @@ public class FileDialogExamples : Scenario
         win.Add (new Label { X = x++, Y = y++, Text = "OpenMode" });
 
         _rgOpenMode = new RadioGroup { X = x, Y = y };
-        _rgOpenMode.RadioLabels = new [] { "File", "Directory", "Mixed" };
+        _rgOpenMode.RadioLabels = new [] { "_File", "D_irectory", "_Mixed" };
         win.Add (_rgOpenMode);
 
         y = 0;
@@ -87,7 +88,7 @@ public class FileDialogExamples : Scenario
         win.Add (new Label { X = x++, Y = y++, Text = "Icons" });
 
         _rgIcons = new RadioGroup { X = x, Y = y };
-        _rgIcons.RadioLabels = new [] { "None", "Unicode", "Nerd*" };
+        _rgIcons.RadioLabels = new [] { "_None", "_Unicode", "Nerd_*" };
         win.Add (_rgIcons);
 
         win.Add (new Label { Y = Pos.AnchorEnd (2), Text = "* Requires installing Nerd fonts" });
@@ -102,7 +103,7 @@ public class FileDialogExamples : Scenario
         win.Add (new Label { X = x++, Y = y++, Text = "Allowed" });
 
         _rgAllowedTypes = new RadioGroup { X = x, Y = y };
-        _rgAllowedTypes.RadioLabels = new [] { "Any", "Csv (Recommended)", "Csv (Strict)" };
+        _rgAllowedTypes.RadioLabels = new [] { "An_y", "Cs_v (Recommended)", "Csv (S_trict)" };
         win.Add (_rgAllowedTypes);
 
         y = 5;
@@ -113,18 +114,32 @@ public class FileDialogExamples : Scenario
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "Buttons" });
 
-        win.Add (new Label { X = x, Y = y++, Text = "Ok Text:" });
+        win.Add (new Label { X = x, Y = y++, Text = "O_k Text:" });
         _tbOkButton = new TextField { X = x, Y = y++, Width = 12 };
         win.Add (_tbOkButton);
-        win.Add (new Label { X = x, Y = y++, Text = "Cancel Text:" });
+        win.Add (new Label { X = x, Y = y++, Text = "_Cancel Text:" });
         _tbCancelButton = new TextField { X = x, Y = y++, Width = 12 };
         win.Add (_tbCancelButton);
-        _cbFlipButtonOrder = new CheckBox { X = x, Y = y++, Text = "Flip Order" };
+        _cbFlipButtonOrder = new CheckBox { X = x, Y = y++, Text = "Flip Ord_er" };
         win.Add (_cbFlipButtonOrder);
 
-        var btn = new Button { X = 1, Y = 9, Text = "Run Dialog" };
-
-        SetupHandler (btn);
+        var btn = new Button { X = 1, Y = 9, IsDefault = true, Text = "Run Dialog" };
+
+        win.Accepting += (s, e) =>
+                        {
+                            try
+                            {
+                                CreateDialog ();
+                            }
+                            catch (Exception ex)
+                            {
+                                MessageBox.ErrorQuery ("Error", ex.ToString (), "_Ok");
+                            }
+                            finally
+                            {
+                                e.Cancel = true;
+                            }
+                        };
         win.Add (btn);
 
         Application.Run (win);
@@ -138,7 +153,7 @@ public class FileDialogExamples : Scenario
         {
             if (File.Exists (e.Dialog.Path))
             {
-                int result = MessageBox.Query ("Overwrite?", "File already exists", "Yes", "No");
+                int result = MessageBox.Query ("Overwrite?", "File already exists", "_Yes", "_No");
                 e.Cancel = result == 1;
             }
         }
@@ -149,13 +164,13 @@ public class FileDialogExamples : Scenario
         var fd = new FileDialog
         {
             OpenMode = Enum.Parse<OpenMode> (
-                                             _rgOpenMode.RadioLabels [_rgOpenMode.SelectedItem]
+                                             _rgOpenMode.RadioLabels.Select (l => TextFormatter.RemoveHotKeySpecifier(l, 0, _rgOpenMode.HotKeySpecifier)).ToArray() [_rgOpenMode.SelectedItem]
                                             ),
             MustExist = _cbMustExist.CheckedState == CheckState.Checked,
             AllowsMultipleSelection = _cbAllowMultipleSelection.CheckedState == CheckState.Checked
         };
 
-        fd.Style.OkButtonText = _rgCaption.RadioLabels [_rgCaption.SelectedItem];
+        fd.Style.OkButtonText = _rgCaption.RadioLabels.Select (l => TextFormatter.RemoveHotKeySpecifier(l, 0, _rgCaption.HotKeySpecifier)).ToArray() [_rgCaption.SelectedItem];
 
         // If Save style dialog then give them an overwrite prompt
         if (_rgCaption.SelectedItem == 2)
@@ -224,6 +239,7 @@ public class FileDialogExamples : Scenario
                               "You canceled navigation and did not pick anything",
                               "Ok"
                              );
+
         }
         else if (_cbAllowMultipleSelection.CheckedState == CheckState.Checked)
         {
@@ -243,21 +259,6 @@ public class FileDialogExamples : Scenario
         }
     }
 
-    private void SetupHandler (Button btn)
-    {
-        btn.Accept += (s, e) =>
-                       {
-                           try
-                           {
-                               CreateDialog ();
-                           }
-                           catch (Exception ex)
-                           {
-                               MessageBox.ErrorQuery ("Error", ex.ToString (), "Ok");
-                           }
-                       };
-    }
-
     private class CaseSensitiveSearchMatcher : ISearchMatcher
     {
         private string _terms;

+ 2 - 2
UICatalog/Scenarios/Generic.cs

@@ -17,8 +17,8 @@ public sealed class MyScenario : Scenario
             Title = GetQuitKeyAndName (),
         };
 
-        var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
-        button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
+        var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "_Press me!" };
+        button.Accepting += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
         appWindow.Add (button);
 
         // Run - Start the application.

+ 2 - 2
UICatalog/Scenarios/GraphViewExample.cs

@@ -185,7 +185,7 @@ public class GraphViewExample : Scenario
                 CanFocus = false
             }
         };
-        statusBar.Add (diagShortcut).Accept += DiagShortcut_Accept;
+        statusBar.Add (diagShortcut).Accepting += DiagShortcut_Accept;
 
         _graphs [_currentGraph++ % _graphs.Length] ();
 
@@ -196,7 +196,7 @@ public class GraphViewExample : Scenario
         Application.Shutdown ();
     }
 
-    private void DiagShortcut_Accept (object sender, HandledEventArgs e)
+    private void DiagShortcut_Accept (object sender, CommandEventArgs e)
     {
         ToggleDiagnostics ();
 

+ 1 - 1
UICatalog/Scenarios/Images.cs

@@ -55,7 +55,7 @@ public class Images : Scenario
         };
         win.Add (imageView);
 
-        btnOpenImage.Accept += (_, _) =>
+        btnOpenImage.Accepting += (_, _) =>
                                {
                                    var ofd = new OpenDialog { Title = "Open Image", AllowsMultipleSelection = false };
                                    Application.Run (ofd);

+ 2 - 2
UICatalog/Scenarios/InteractiveTree.cs

@@ -82,13 +82,13 @@ public class InteractiveTree : Scenario
 
         var ok = new Button { Text = "Ok", IsDefault = true };
 
-        ok.Accept += (s, e) =>
+        ok.Accepting += (s, e) =>
                      {
                          okPressed = true;
                          Application.RequestStop ();
                      };
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accept += (s, e) => Application.RequestStop ();
+        cancel.Accepting += (s, e) => Application.RequestStop ();
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
 
         var lbl = new Label { X = 0, Y = 1, Text = label };

+ 1 - 1
UICatalog/Scenarios/InvertColors.cs

@@ -36,7 +36,7 @@ public class InvertColors : Scenario
 
         var button = new Button { X = Pos.Center (), Y = foreColors.Length + 1, Text = "Invert color!" };
 
-        button.Accept += (s, e) =>
+        button.Accepting += (s, e) =>
                           {
                               foreach (Label label in labels)
                               {

+ 5 - 5
UICatalog/Scenarios/LineDrawing.cs

@@ -147,10 +147,10 @@ public class LineDrawing : Scenario
             IsDefault = true
         };
 
-        btnOk.Accept += (s, e) =>
+        btnOk.Accepting += (s, e) =>
                         {
                             accept = true;
-                            e.Handled = true;
+                            e.Cancel = true;
                             Application.RequestStop ();
                         };
 
@@ -162,9 +162,9 @@ public class LineDrawing : Scenario
             Width = Dim.Auto ()
         };
 
-        btnCancel.Accept += (s, e) =>
+        btnCancel.Accepting += (s, e) =>
                             {
-                                e.Handled = true;
+                                e.Cancel = true;
                                 Application.RequestStop ();
                             };
 
@@ -228,7 +228,7 @@ public class ToolsView : Window
 
         _addLayerBtn = new() { Text = "New Layer", X = Pos.Center (), Y = Pos.Bottom (_stylePicker) };
 
-        _addLayerBtn.Accept += (s, a) => AddLayer?.Invoke ();
+        _addLayerBtn.Accepting += (s, a) => AddLayer?.Invoke ();
         Add (_colors, _stylePicker, _addLayerBtn);
     }
 

+ 6 - 23
UICatalog/Scenarios/LineViewExample.cs

@@ -1,4 +1,5 @@
-using System.Text;
+using System.Globalization;
+using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -12,19 +13,12 @@ public class LineViewExample : Scenario
     public override void Main ()
     {
         Application.Init ();
-        // Setup - Create a top-level application window and configure it.
-        Toplevel top = new ();
 
-        var menu = new MenuBar
+        var appWindow = new Window()
         {
-            Menus =
-            [
-                new ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
-            ]
+            Title = GetQuitKeyAndName (),
         };
-        top.Add (menu);
 
-        var appWindow = new Window ();
         appWindow.Add (new Label { Y = 1, Text = "Regular Line" });
 
         // creates a horizontal line
@@ -73,22 +67,11 @@ public class LineViewExample : Scenario
 
         appWindow.Add (verticalArrow);
 
-        var statusBar = new StatusBar (
-                                       new Shortcut []
-                                       {
-                                           new (Application.QuitKey, "Quit", Quit)
-                                       }
-                                      );
-        top.Add (statusBar);
-        top.Add (appWindow);
-
         // Run - Start the application.
-        Application.Run (top);
-        top.Dispose ();
+        Application.Run (appWindow);
+        appWindow.Dispose ();
 
         // Shutdown - Calling Application.Shutdown is required.
         Application.Shutdown ();
     }
-
-    private void Quit () { Application.RequestStop (); }
 }

+ 5 - 7
UICatalog/Scenarios/ListColumns.cs

@@ -211,8 +211,6 @@ public class ListColumns : Scenario
             ]
         };
 
-        top.Add (menu);
-
         var statusBar = new StatusBar (
                                        new Shortcut []
                                        {
@@ -222,8 +220,6 @@ public class ListColumns : Scenario
                                            new (Application.QuitKey, "Quit", Quit)
                                        }
                                       );
-        top.Add (statusBar);
-
         appWindow.Add (_listColView);
 
         var selectedCellLabel = new Label
@@ -256,7 +252,9 @@ public class ListColumns : Scenario
 
         _listColView.KeyBindings.ReplaceCommands (Key.Space, Command.Accept);
 
-        top.Add (appWindow);
+        top.Add (menu, appWindow, statusBar);
+        appWindow.Y = 1;
+        appWindow.Height = Dim.Fill(Dim.Func (() => statusBar.Frame.Height));
 
         // Run - Start the application.
         Application.Run (top);
@@ -275,13 +273,13 @@ public class ListColumns : Scenario
         var accepted = false;
         var ok = new Button { Text = "Ok", IsDefault = true };
 
-        ok.Accept += (s, e) =>
+        ok.Accepting += (s, e) =>
                      {
                          accepted = true;
                          Application.RequestStop ();
                      };
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accept += (s, e) => { Application.RequestStop (); };
+        cancel.Accepting += (s, e) => { Application.RequestStop (); };
         var d = new Dialog { Title = prompt, Buttons = [ok, cancel] };
 
         var tf = new TextField { Text = getter (_listColView).ToString (), X = 0, Y = 0, Width = Dim.Fill () };

+ 51 - 13
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -1,7 +1,9 @@
-using System.Collections;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.ComponentModel;
 using System.Text;
 using JetBrains.Annotations;
 using Terminal.Gui;
@@ -20,6 +22,10 @@ public class ListViewWithSelection : Scenario
     private ObservableCollection<Scenario> _scenarios;
     private Window _appWindow;
 
+
+    private ObservableCollection<string> _eventList = new ();
+    private ListView _eventListView;
+
     /// <inheritdoc />
     public override void Main ()
     {
@@ -32,13 +38,13 @@ public class ListViewWithSelection : Scenario
 
         _scenarios = GetScenarios ();
 
-        _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom rendering" };
+        _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom _rendering" };
         _appWindow.Add (_customRenderCB);
         _customRenderCB.CheckedStateChanging += _customRenderCB_Toggle;
 
         _allowMarkingCB = new CheckBox
         {
-            X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow Marking", AllowCheckStateNone = false
+            X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow _Marking", AllowCheckStateNone = false
         };
         _appWindow.Add (_allowMarkingCB);
         _allowMarkingCB.CheckedStateChanging += AllowMarkingCB_Toggle;
@@ -48,22 +54,23 @@ public class ListViewWithSelection : Scenario
             X = Pos.Right (_allowMarkingCB) + 1,
             Y = 0,
             Visible = _allowMarkingCB.CheckedState == CheckState.Checked,
-            Text = "Allow Multi-Select"
+            Text = "Allow Multi-_Select"
         };
         _appWindow.Add (_allowMultipleCB);
         _allowMultipleCB.CheckedStateChanging += AllowMultipleCB_Toggle;
 
         _listView = new ListView
         {
-            X = 1,
-            Y = 2,
+            Title = "_ListView",
+            X = 0,
+            Y = Pos.Bottom(_allowMarkingCB),
             Height = Dim.Fill (),
-            Width = Dim.Fill (1),
+            Width = Dim.Func (() => _listView?.MaxLength ?? 10),
 
-            //ColorScheme = Colors.ColorSchemes ["TopLevel"],
             AllowsMarking = false,
             AllowsMultipleSelection = false
         };
+        _listView.Border.Thickness = new Thickness (0, 1, 0, 0);
         _listView.RowRender += ListView_RowRender;
         _appWindow.Add (_listView);
 
@@ -104,15 +111,46 @@ public class ListViewWithSelection : Scenario
 
         _listView.SetSource (_scenarios);
 
-        var k = "Keep Content Always In Viewport";
+        var k = "_Keep Content Always In Viewport";
 
         var keepCheckBox = new CheckBox
         {
-            X = Pos.AnchorEnd (k.Length + 3), Y = 0, Text = k, CheckedState = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
+            X = Pos.Right(_allowMultipleCB) + 1,
+            Y = 0, 
+            Text = k, 
+            CheckedState = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
         };
         keepCheckBox.CheckedStateChanging += (s, e) => scrollBar.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
         _appWindow.Add (keepCheckBox);
 
+        _eventList = new ();
+
+        _eventListView = new ListView
+        {
+            X = Pos.Right (_listView) + 1,
+            Y = Pos.Top (_listView),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+            Source = new ListWrapper<string> (_eventList)
+        };
+        _eventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
+        _appWindow.Add (_eventListView);
+
+        _listView.SelectedItemChanged += (s, a) => LogEvent (s as View, a, "SelectedItemChanged");
+        _listView.OpenSelectedItem += (s, a) => LogEvent (s as View, a, "OpenSelectedItem");
+        _listView.CollectionChanged += (s, a) => LogEvent (s as View, a, "CollectionChanged");
+        _listView.Accepting += (s, a) => LogEvent (s as View, a, "Accept");
+        _listView.Selecting += (s, a) => LogEvent (s as View, a, "Select");
+
+        bool? LogEvent (View sender, EventArgs args, string message)
+        {
+            var msg = $"{message,-7}: {args}";
+            _eventList.Add (msg);
+            _eventListView.MoveDown ();
+
+            return null;
+        }
+
         Application.Run (_appWindow);
         _appWindow.Dispose ();
         Application.Shutdown ();
@@ -154,18 +192,18 @@ public class ListViewWithSelection : Scenario
 
         if (_listView.AllowsMarking && _listView.Source.IsMarked (obj.Row))
         {
-            obj.RowAttribute = new Attribute (Color.BrightRed, Color.BrightYellow);
+            obj.RowAttribute = new Attribute (Color.Black, Color.White);
 
             return;
         }
 
         if (obj.Row % 2 == 0)
         {
-            obj.RowAttribute = new Attribute (Color.BrightGreen, Color.Magenta);
+            obj.RowAttribute = new Attribute (Color.Green, Color.Black);
         }
         else
         {
-            obj.RowAttribute = new Attribute (Color.BrightMagenta, Color.Green);
+            obj.RowAttribute = new Attribute (Color.Black, Color.Green);
         }
     }
 

+ 2 - 2
UICatalog/Scenarios/ListsAndCombos.cs

@@ -147,13 +147,13 @@ public class ListsAndCombos : Scenario
                                 };
 
         var btnMoveUp = new Button { X = 1, Y = Pos.Bottom (lbListView), Text = "Move _Up" };
-        btnMoveUp.Accept += (s, e) => { listview.MoveUp (); };
+        btnMoveUp.Accepting += (s, e) => { listview.MoveUp (); };
 
         var btnMoveDown = new Button
         {
             X = Pos.Right (btnMoveUp) + 1, Y = Pos.Bottom (lbListView), Text = "Move _Down"
         };
-        btnMoveDown.Accept += (s, e) => { listview.MoveDown (); };
+        btnMoveDown.Accepting += (s, e) => { listview.MoveDown (); };
 
         win.Add (btnMoveUp, btnMoveDown);
 

+ 5 - 3
UICatalog/Scenarios/Localization.cs

@@ -152,14 +152,14 @@ public class Localization : Scenario
         {
             X = Pos.Right (_allowAnyCheckBox) + 1, Y = Pos.Bottom (textAndFileDialogLabel) + 1, Text = "Open"
         };
-        openDialogButton.Accept += (sender, e) => ShowFileDialog (false);
+        openDialogButton.Accepting += (sender, e) => ShowFileDialog (false);
         win.Add (openDialogButton);
 
         var saveDialogButton = new Button
         {
             X = Pos.Right (openDialogButton) + 1, Y = Pos.Bottom (textAndFileDialogLabel) + 1, Text = "Save"
         };
-        saveDialogButton.Accept += (sender, e) => ShowFileDialog (true);
+        saveDialogButton.Accepting += (sender, e) => ShowFileDialog (true);
         win.Add (saveDialogButton);
 
         var wizardLabel = new Label
@@ -173,10 +173,12 @@ public class Localization : Scenario
         win.Add (wizardLabel);
 
         var wizardButton = new Button { X = 2, Y = Pos.Bottom (wizardLabel) + 1, Text = "Open _wizard" };
-        wizardButton.Accept += (sender, e) => ShowWizard ();
+        wizardButton.Accepting += (sender, e) => ShowWizard ();
         win.Add (wizardButton);
 
         win.Unloaded += (sender, e) => Quit ();
+
+        win.Y = Pos.Bottom (menu);
         top.Add (win);
 
         Application.Run (top);

+ 3 - 3
UICatalog/Scenarios/MenuBarScenario.cs

@@ -110,15 +110,15 @@ public class MenuBarScenario : Scenario
         menuBar.LayoutComplete += (s, e) => { _focusedView.Text = appWindow.MostFocused?.ToString () ?? "None"; };
 
         var openBtn = new Button { X = Pos.Center (), Y = 4, Text = "_Open Menu", IsDefault = true };
-        openBtn.Accept += (s, e) => { menuBar.OpenMenu (); };
+        openBtn.Accepting += (s, e) => { menuBar.OpenMenu (); };
         appWindow.Add (openBtn);
 
         var hideBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (openBtn), Text = "Toggle Menu._Visible" };
-        hideBtn.Accept += (s, e) => { menuBar.Visible = !menuBar.Visible; };
+        hideBtn.Accepting += (s, e) => { menuBar.Visible = !menuBar.Visible; };
         appWindow.Add (hideBtn);
 
         var enableBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (hideBtn), Text = "_Toggle Menu.Enable" };
-        enableBtn.Accept += (s, e) => { menuBar.Enabled = !menuBar.Enabled; };
+        enableBtn.Accepting += (s, e) => { menuBar.Enabled = !menuBar.Enabled; };
         appWindow.Add (enableBtn);
 
         appWindow.Add (menuBar);

+ 26 - 12
UICatalog/Scenarios/MessageBoxes.cs

@@ -30,7 +30,7 @@ public class MessageBoxes : Scenario
         app.Add (frame);
 
         // TODO: Use Pos.Align her to demo aligning labels and fields
-        var label = new Label { X = 0, Y = 0, Width = 15, TextAlignment = Alignment.End, Text = "Width:" };
+        var label = new Label { X = 0, Y = 0, Width = 15, TextAlignment = Alignment.End, Text = "W_idth:" };
         frame.Add (label);
 
         var widthEdit = new TextField
@@ -50,7 +50,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Height:"
+            Text = "_Height:"
         };
         frame.Add (label);
 
@@ -90,7 +90,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Title:"
+            Text = "_Title:"
         };
         frame.Add (label);
 
@@ -100,7 +100,7 @@ public class MessageBoxes : Scenario
             Y = Pos.Top (label),
             Width = Dim.Fill (),
             Height = 1,
-            Text = "Title"
+            Text = "The title"
         };
         frame.Add (titleEdit);
 
@@ -112,7 +112,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Message:"
+            Text = "_Message:"
         };
         frame.Add (label);
 
@@ -134,7 +134,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Num Buttons:"
+            Text = "_Num Buttons:"
         };
         frame.Add (label);
 
@@ -156,7 +156,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Default Button:"
+            Text = "_Default Button:"
         };
         frame.Add (label);
 
@@ -178,7 +178,7 @@ public class MessageBoxes : Scenario
             Width = Dim.Width (label),
             Height = 1,
             TextAlignment = Alignment.End,
-            Text = "Style:"
+            Text = "St_yle:"
         };
         frame.Add (label);
 
@@ -188,11 +188,23 @@ public class MessageBoxes : Scenario
         };
         frame.Add (styleRadioGroup);
 
+        label = new ()
+        {
+            X = 0,
+            Y = Pos.Bottom (styleRadioGroup),
+
+            Width = Dim.Width (label),
+            Height = 1,
+            TextAlignment = Alignment.End,
+            Text = "Wra_p:"
+        };
         var ckbWrapMessage = new CheckBox
         {
-            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup), Text = "_Wrap Message", CheckedState = CheckState.Checked
+            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup),
+            CheckedState = CheckState.Checked,
+            Text = "_Wrap Message",
         };
-        frame.Add (ckbWrapMessage);
+        frame.Add (label, ckbWrapMessage);
 
         frame.ValidatePosDim = true;
 
@@ -216,7 +228,7 @@ public class MessageBoxes : Scenario
             X = Pos.Center (), Y = Pos.Bottom (frame) + 2, IsDefault = true, Text = "_Show MessageBox"
         };
 
-        showMessageBoxButton.Accept += (s, e) =>
+        app.Accepting += (s, e) =>
                                        {
                                            try
                                            {
@@ -229,7 +241,7 @@ public class MessageBoxes : Scenario
 
                                                for (var i = 0; i < numButtons; i++)
                                                {
-                                                   btns.Add (NumberToWords.Convert (i));
+                                                   btns.Add ($"_{NumberToWords.Convert (i)}");
                                                }
 
                                                if (styleRadioGroup.SelectedItem == 0)
@@ -263,6 +275,8 @@ public class MessageBoxes : Scenario
                                            {
                                                buttonPressedLabel.Text = "Invalid Options";
                                            }
+
+                                           e.Cancel = true;
                                        };
         app.Add (showMessageBoxButton);
 

+ 1 - 1
UICatalog/Scenarios/Mouse.cs

@@ -241,7 +241,7 @@ public class Mouse : Scenario
         };
         win.Add (label, winLog);
 
-        clearButton.Accept += (s, e) =>
+        clearButton.Accepting += (s, e) =>
                               {
                                   appLogList.Clear ();
                                   appLog.SetSource (appLogList);

+ 2 - 2
UICatalog/Scenarios/MultiColouredTable.cs

@@ -104,13 +104,13 @@ public class MultiColouredTable : Scenario
 
         var ok = new Button { Text = "Ok", IsDefault = true };
 
-        ok.Accept += (s, e) =>
+        ok.Accepting += (s, e) =>
                      {
                          okPressed = true;
                          Application.RequestStop ();
                      };
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accept += (s, e) => { Application.RequestStop (); };
+        cancel.Accepting += (s, e) => { Application.RequestStop (); };
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
 
         var lbl = new Label { X = 0, Y = 1, Text = label };

+ 3 - 3
UICatalog/Scenarios/NumericUpDownDemo.cs

@@ -114,7 +114,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                 Width = 8,
                 Title = "Value",
             };
-            _value.Accept += ValuedOnAccept;
+            _value.Accepting += ValuedOnAccept;
 
             void ValuedOnAccept (object? sender, EventArgs e)
             {
@@ -169,7 +169,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                 Title = "Format",
                 Width = Dim.Width (_value),
             };
-            _format.Accept += FormatOnAccept;
+            _format.Accepting += FormatOnAccept;
 
             void FormatOnAccept (object? o, EventArgs eventArgs)
             {
@@ -217,7 +217,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                 Width = Dim.Width (_value),
             };
 
-            _increment.Accept += IncrementOnAccept;
+            _increment.Accepting += IncrementOnAccept;
 
             void IncrementOnAccept (object? o, EventArgs eventArgs)
             {

+ 4 - 4
UICatalog/Scenarios/Progress.cs

@@ -142,7 +142,7 @@ public class Progress : Scenario
 
         var startBoth = new Button { X = Pos.Center (), Y = Pos.Bottom (mainLoopTimeoutDemo) + 1, Text = "Start Both" };
 
-        startBoth.Accept += (s, e) =>
+        startBoth.Accepting += (s, e) =>
                              {
                                  systemTimerDemo.Start ();
                                  mainLoopTimeoutDemo.Start ();
@@ -193,15 +193,15 @@ public class Progress : Scenario
             Add (LeftFrame);
 
             var startButton = new Button { X = Pos.Right (LeftFrame) + 1, Y = 0, Text = "Start Timer" };
-            startButton.Accept += (s, e) => Start ();
+            startButton.Accepting += (s, e) => Start ();
             var pulseButton = new Button { X = Pos.Right (startButton) + 2, Y = Pos.Y (startButton), Text = "Pulse" };
-            pulseButton.Accept += (s, e) => Pulse ();
+            pulseButton.Accepting += (s, e) => Pulse ();
 
             var stopbutton = new Button
             {
                 X = Pos.Right (pulseButton) + 2, Y = Pos.Top (pulseButton), Text = "Stop Timer"
             };
-            stopbutton.Accept += (s, e) => Stop ();
+            stopbutton.Accepting += (s, e) => Stop ();
 
             Add (startButton);
             Add (pulseButton);

+ 3 - 4
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -72,7 +72,7 @@ public class ProgressBarStyles : Scenario
         };
         container.Add (fgColorPickerBtn);
 
-        fgColorPickerBtn.Accept += (s, e) =>
+        fgColorPickerBtn.Accepting += (s, e) =>
                                     {
                                         if (!LineDrawing.PromptForColor (
                                                                          fgColorPickerBtn.Text,
@@ -102,7 +102,7 @@ public class ProgressBarStyles : Scenario
         };
         container.Add (bgColorPickerBtn);
 
-        bgColorPickerBtn.Accept += (s, e) =>
+        bgColorPickerBtn.Accepting += (s, e) =>
                                     {
                                         if (!LineDrawing.PromptForColor (
                                                                          fgColorPickerBtn.Text,
@@ -172,7 +172,7 @@ public class ProgressBarStyles : Scenario
         };
         container.Add (continuousPB);
 
-        button.Accept += (s, e) =>
+        button.Accepting += (s, e) =>
                           {
                               if (_fractionTimer == null)
                               {
@@ -264,7 +264,6 @@ public class ProgressBarStyles : Scenario
 
         ckbBidirectional.CheckedStateChanging += (s, e) =>
                                    {
-                                       ckbBidirectional.CheckedState = e.NewValue;
                                        marqueesBlocksPB.BidirectionalMarquee =
                                                                   marqueesContinuousPB.BidirectionalMarquee = e.NewValue == CheckState.Checked;
                                    };

Some files were not shown because too many files changed in this diff