Sfoglia il codice sorgente

Merge branch 'v2_develop' into v2_3551_combobox-listview-performance-fix

Tig 1 anno fa
parent
commit
3ba6ae5562
100 ha cambiato i file con 4960 aggiunte e 2009 eliminazioni
  1. 1 1
      Scripts/Terminal.Gui.PowerShell.Analyzers.psd1
  2. 1 1
      Scripts/Terminal.Gui.PowerShell.Build.psd1
  3. 1 1
      Scripts/Terminal.Gui.PowerShell.Core.psd1
  4. 1 1
      Scripts/Terminal.Gui.PowerShell.Git.psd1
  5. 1 1
      Scripts/Terminal.Gui.PowerShell.psd1
  6. 16 11
      Terminal.Gui/Application/Application.cs
  7. 9 4
      Terminal.Gui/Application/ApplicationKeyboard.cs
  8. 9 5
      Terminal.Gui/Drawing/Aligner.cs
  9. 5 2
      Terminal.Gui/Input/CommandContext.cs
  10. 3 0
      Terminal.Gui/Input/KeyBindingScope.cs
  11. 3 1
      Terminal.Gui/Input/KeyBindings.cs
  12. 1 6
      Terminal.Gui/View/Layout/Dim.cs
  13. 99 11
      Terminal.Gui/View/Layout/DimAuto.cs
  14. 24 0
      Terminal.Gui/View/Layout/Pos.cs
  15. 54 49
      Terminal.Gui/View/Layout/PosAlign.cs
  16. 1 0
      Terminal.Gui/View/View.cs
  17. 68 18
      Terminal.Gui/View/ViewAdornments.cs
  18. 5 5
      Terminal.Gui/View/ViewDrawing.cs
  19. 88 20
      Terminal.Gui/View/ViewKeyboard.cs
  20. 13 4
      Terminal.Gui/View/ViewSubViews.cs
  21. 229 0
      Terminal.Gui/Views/Bar.cs
  22. 1 1
      Terminal.Gui/Views/ColorPicker.cs
  23. 1 1
      Terminal.Gui/Views/Dialog.cs
  24. 10 19
      Terminal.Gui/Views/HexView.cs
  25. 58 3
      Terminal.Gui/Views/Line.cs
  26. 3 3
      Terminal.Gui/Views/Menu/Menu.cs
  27. 58 0
      Terminal.Gui/Views/MenuBarv2.cs
  28. 64 0
      Terminal.Gui/Views/Menuv2.cs
  29. 1 1
      Terminal.Gui/Views/OpenDialog.cs
  30. 22 1
      Terminal.Gui/Views/RadioGroup.cs
  31. 1 1
      Terminal.Gui/Views/SaveDialog.cs
  32. 7 4
      Terminal.Gui/Views/ScrollView.cs
  33. 791 0
      Terminal.Gui/Views/Shortcut.cs
  34. 17 6
      Terminal.Gui/Views/Slider.cs
  35. 38 204
      Terminal.Gui/Views/StatusBar.cs
  36. 0 59
      Terminal.Gui/Views/StatusItem.cs
  37. 2 2
      Terminal.Gui/Views/TextField.cs
  38. 2 3
      Terminal.Gui/Views/TextView.cs
  39. 8 8
      Terminal.Gui/Views/Toplevel.cs
  40. 1 1
      Terminal.Gui/Views/Wizard/Wizard.cs
  41. 7 3
      Terminal.Gui/Views/Wizard/WizardStep.cs
  42. 2 0
      UICatalog/Scenario.cs
  43. 2 0
      UICatalog/Scenarios/ASCIICustomButton.cs
  44. 11 13
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  45. 479 0
      UICatalog/Scenarios/Bars.cs
  46. 1 0
      UICatalog/Scenarios/BasicColors.cs
  47. 1 0
      UICatalog/Scenarios/Buttons.cs
  48. 1 0
      UICatalog/Scenarios/CharacterMap.cs
  49. 1 0
      UICatalog/Scenarios/ColorPicker.cs
  50. 5 0
      UICatalog/Scenarios/ComputedLayout.cs
  51. 29 16
      UICatalog/Scenarios/ConfigurationEditor.cs
  52. 102 95
      UICatalog/Scenarios/CsvEditor.cs
  53. 39 87
      UICatalog/Scenarios/DynamicStatusBar.cs
  54. 10 15
      UICatalog/Scenarios/Editor.cs
  55. 185 161
      UICatalog/Scenarios/GraphViewExample.cs
  56. 38 28
      UICatalog/Scenarios/HexEditor.cs
  57. 1 0
      UICatalog/Scenarios/HotKeys.cs
  58. 31 35
      UICatalog/Scenarios/InteractiveTree.cs
  59. 1 0
      UICatalog/Scenarios/LineCanvasExperiment.cs
  60. 31 28
      UICatalog/Scenarios/LineViewExample.cs
  61. 33 37
      UICatalog/Scenarios/ListColumns.cs
  62. 4 3
      UICatalog/Scenarios/Mouse.cs
  63. 58 54
      UICatalog/Scenarios/MultiColouredTable.cs
  64. 54 54
      UICatalog/Scenarios/Notepad.cs
  65. 19 22
      UICatalog/Scenarios/RunTExample.cs
  66. 6 5
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  67. 1 0
      UICatalog/Scenarios/Scrolling.cs
  68. 372 0
      UICatalog/Scenarios/Shortcuts.cs
  69. 86 116
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  70. 36 5
      UICatalog/Scenarios/Sliders.cs
  71. 65 58
      UICatalog/Scenarios/SyntaxHighlighting.cs
  72. 75 74
      UICatalog/Scenarios/TabViewExample.cs
  73. 57 48
      UICatalog/Scenarios/TableEditor.cs
  74. 1 0
      UICatalog/Scenarios/TextAlignmentAndDirection.cs
  75. 1 0
      UICatalog/Scenarios/TextFormatterDemo.cs
  76. 60 38
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  77. 29 27
      UICatalog/Scenarios/TreeUseCases.cs
  78. 2 0
      UICatalog/Scenarios/TrueColors.cs
  79. 54 37
      UICatalog/Scenarios/Unicode.cs
  80. 1 0
      UICatalog/Scenarios/ViewExperiments.cs
  81. 5 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  82. 1 0
      UICatalog/Scenarios/WindowsAndFrameViews.cs
  83. 8 11
      UICatalog/Scenarios/WizardAsView.cs
  84. 132 92
      UICatalog/UICatalog.cs
  85. 196 7
      UnitTests/Application/ApplicationTests.cs
  86. 119 1
      UnitTests/Application/KeyboardTests.cs
  87. 17 5
      UnitTests/Application/MainLoopTests.cs
  88. 12 4
      UnitTests/Application/SynchronizatonContextTests.cs
  89. 9 0
      UnitTests/Configuration/ConfigurationMangerTests.cs
  90. 1 1
      UnitTests/Drawing/AlignerTests.cs
  91. 84 36
      UnitTests/UICatalog/ScenarioTests.cs
  92. 22 4
      UnitTests/View/HotKeyTests.cs
  93. 3 3
      UnitTests/View/KeyboardEventTests.cs
  94. 127 128
      UnitTests/View/NavigationTests.cs
  95. 105 0
      UnitTests/Views/BarTests.cs
  96. 91 3
      UnitTests/Views/ButtonTests.cs
  97. 0 132
      UnitTests/Views/ContextMenuTests.cs
  98. 2 64
      UnitTests/Views/LabelTests.cs
  99. 1 0
      UnitTests/Views/RadioGroupTests.cs
  100. 317 0
      UnitTests/Views/ShortcutTests.cs

+ 1 - 1
Scripts/Terminal.Gui.PowerShell.Analyzers.psd1

@@ -42,7 +42,7 @@ PowerShellHostName = 'ConsoleHost'
 # PowerShellHostVersion = ''
 
 # Processor architecture (None, X86, Amd64) required by this module
-ProcessorArchitecture = 'Amd64'
+ProcessorArchitecture = ''
 
 # Modules that must be imported into the global environment prior to importing this module
 RequiredModules = @('Microsoft.PowerShell.Management','Microsoft.PowerShell.Utility','./Terminal.Gui.PowerShell.Core.psd1')

+ 1 - 1
Scripts/Terminal.Gui.PowerShell.Build.psd1

@@ -36,7 +36,7 @@ PowerShellHostVersion = '7.4.0'
 # Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
 # Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
 # Has nothing to do with runtime use of Terminal.Gui.
-ProcessorArchitecture = 'Amd64'
+ProcessorArchitecture = ''
 
 # Modules that must be imported into the global environment prior to importing this module
 RequiredModules = @(

+ 1 - 1
Scripts/Terminal.Gui.PowerShell.Core.psd1

@@ -44,7 +44,7 @@ PowerShellHostVersion = '7.4.0'
 # Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
 # Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
 # Has nothing to do with runtime use of Terminal.Gui.
-ProcessorArchitecture = 'Amd64'
+ProcessorArchitecture = ''
 
 # Modules that must be imported into the global environment prior to importing this module
 RequiredModules = @(

+ 1 - 1
Scripts/Terminal.Gui.PowerShell.Git.psd1

@@ -44,7 +44,7 @@ PowerShellHostVersion = '7.4.0'
 # Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
 # Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
 # Has nothing to do with runtime use of Terminal.Gui.
-ProcessorArchitecture = 'AMD64'
+ProcessorArchitecture = ''
 
 # Modules that must be imported into the global environment prior to importing this module
 RequiredModules = @(

+ 1 - 1
Scripts/Terminal.Gui.PowerShell.psd1

@@ -49,7 +49,7 @@ PowerShellHostVersion = '7.4.0'
 # Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
 # Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
 # Has nothing to do with runtime use of Terminal.Gui.
-ProcessorArchitecture = 'Amd64'
+ProcessorArchitecture = ''
 
 # Modules that must be imported into the global environment prior to importing this module
 RequiredModules = @(

+ 16 - 11
Terminal.Gui/Application/Application.cs

@@ -76,7 +76,7 @@ public static partial class Application
     // this in a function like this ensures we don't make mistakes in
     // guaranteeing that the state of this singleton is deterministic when Init
     // starts running and after Shutdown returns.
-    internal static void ResetState ()
+    internal static void ResetState (bool ignoreDisposed = false)
     {
         // Shutdown is the bookend for Init. As such it needs to clean up all resources
         // Init created. Apps that do any threading will need to code defensively for this.
@@ -84,11 +84,6 @@ public static partial class Application
         foreach (Toplevel t in _topLevels)
         {
             t.Running = false;
-#if DEBUG_IDISPOSABLE
-
-            // Don't dispose the toplevels. It's up to caller dispose them
-            //Debug.Assert (t.WasDisposed);
-#endif
         }
 
         _topLevels.Clear ();
@@ -96,7 +91,7 @@ public static partial class Application
 #if DEBUG_IDISPOSABLE
 
         // Don't dispose the Top. It's up to caller dispose it
-        if (Top is { })
+        if (!ignoreDisposed && Top is { })
         {
             Debug.Assert (Top.WasDisposed);
 
@@ -177,15 +172,15 @@ public static partial class Application
     /// </para>
     /// <para>
     ///     <see cref="Shutdown"/> must be called when the application is closing (typically after
-    ///     <see cref="Run(Func{Exception, bool}, ConsoleDriver)"/> has returned) to ensure resources are cleaned up and
+    ///     <see cref="Run{T}"/> has returned) to ensure resources are cleaned up and
     ///     terminal settings
     ///     restored.
     /// </para>
     /// <para>
-    ///     The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> function combines
+    ///     The <see cref="Run{T}"/> function combines
     ///     <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
     ///     into a single
-    ///     call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> without explicitly calling
+    ///     call. An application cam use <see cref="Run{T}"/> without explicitly calling
     ///     <see cref="Init(ConsoleDriver, string)"/>.
     /// </para>
     /// <param name="driver">
@@ -313,6 +308,7 @@ public static partial class Application
         SupportedCultures = GetSupportedCultures ();
         _mainThreadId = Thread.CurrentThread.ManagedThreadId;
         _initialized = true;
+        InitializedChanged?.Invoke (null, new (false, _initialized));
     }
 
     private static void Driver_SizeChanged (object sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
@@ -353,8 +349,17 @@ public static partial class Application
         // TODO: Throw an exception if Init hasn't been called.
         ResetState ();
         PrintJsonErrors ();
+        InitializedChanged?.Invoke (null, new (true, _initialized));
     }
 
+    /// <summary>
+    ///     This event is fired after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.
+    /// </summary>
+    /// <remarks>
+    ///     Intended to support unit tests that need to know when the application has been initialized.
+    /// </remarks>
+    public static event EventHandler<StateEventArgs<bool>> InitializedChanged;
+
     #endregion Initialization (Init/Shutdown)
 
     #region Run (Begin, Run, End, Stop)
@@ -677,7 +682,7 @@ public static partial class Application
     /// </param>
     /// <returns>The created T object. The caller is responsible for disposing this object.</returns>
     public static T Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
-        where T : Toplevel, new ()
+        where T : Toplevel, new()
     {
         if (!_initialized)
         {

+ 9 - 4
Terminal.Gui/Application/ApplicationKeyboard.cs

@@ -141,11 +141,16 @@ partial class Application
         {
             foreach (View view in binding.Value)
             {
-                bool? handled = view?.OnInvokingKeyBindings (keyEvent);
-
-                if (handled != null && (bool)handled)
+                if (view is {} && view.KeyBindings.TryGet (binding.Key, (KeyBindingScope)0xFFFF, out KeyBinding kb))
                 {
-                    return true;
+                    //bool? handled = view.InvokeCommands (kb.Commands, binding.Key, kb);
+                    bool? handled = view?.OnInvokingKeyBindings (keyEvent, kb.Scope);
+
+                    if (handled != null && (bool)handled)
+                    {
+                        return true;
+                    }
+
                 }
             }
         }

+ 9 - 5
Terminal.Gui/Drawing/Aligner.cs

@@ -108,10 +108,11 @@ public class Aligner : INotifyPropertyChanged
             spacesToGive = containerSize - totalItemsSize;
         }
 
+        AlignmentModes mode = alignmentMode & ~AlignmentModes.AddSpaceBetweenItems; // copy to avoid modifying the original
         switch (alignment)
         {
             case Alignment.Start:
-                switch (alignmentMode & ~AlignmentModes.AddSpaceBetweenItems)
+                switch (mode)
                 {
                     case AlignmentModes.StartToEnd:
                         return Start (in sizesCopy, maxSpaceBetweenItems, spacesToGive);
@@ -129,7 +130,7 @@ public class Aligner : INotifyPropertyChanged
                 break;
 
             case Alignment.End:
-                switch (alignmentMode & ~AlignmentModes.AddSpaceBetweenItems)
+                switch (mode)
                 {
                     case AlignmentModes.StartToEnd:
                         return End (in sizesCopy, containerSize, totalItemsSize, maxSpaceBetweenItems, spacesToGive);
@@ -147,7 +148,8 @@ public class Aligner : INotifyPropertyChanged
                 break;
 
             case Alignment.Center:
-                switch (alignmentMode & ~AlignmentModes.AddSpaceBetweenItems)
+                mode &= ~AlignmentModes.IgnoreFirstOrLast;
+                switch (mode)
                 {
                     case AlignmentModes.StartToEnd:
                         return Center (in sizesCopy, containerSize, totalItemsSize, maxSpaceBetweenItems, spacesToGive);
@@ -159,7 +161,8 @@ public class Aligner : INotifyPropertyChanged
                 break;
 
             case Alignment.Fill:
-                switch (alignmentMode & ~AlignmentModes.AddSpaceBetweenItems)
+                mode &= ~AlignmentModes.IgnoreFirstOrLast;
+                switch (mode)
                 {
                     case AlignmentModes.StartToEnd:
                         return Fill (in sizesCopy, containerSize, totalItemsSize);
@@ -260,7 +263,8 @@ public class Aligner : INotifyPropertyChanged
             var currentPosition = 0;
             if (totalItemsSize > containerSize)
             {
-                currentPosition = containerSize - totalItemsSize - spacesToGive;
+                // Don't allow negative positions
+                currentPosition = int.Max(0, containerSize - totalItemsSize - spacesToGive);
             }
 
             for (var i = 0; i < sizes.Length; i++)

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

@@ -1,14 +1,17 @@
 #nullable enable
 namespace Terminal.Gui;
+
+#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
 /// <summary>
 ///     Provides context for a <see cref="Command"/> that is being invoked.
-/// </summary
+/// </summary>
 /// <remarks>
 ///     <para>
 ///         To define a <see cref="Command"/> that is invoked with context,
-///         use <see cref="View.AddCommand(Command,Func{CommandContext,Nullable{bool}})"/>
+///         use <see cref="View.AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>.
 ///     </para>
 /// </remarks>
+#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
 public record struct CommandContext
 {
     /// <summary>

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

@@ -13,6 +13,9 @@ namespace Terminal.Gui;
 [GenerateEnumExtensionMethods (FastHasFlags = true)]
 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>
     Focused = 1,
 

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

@@ -1,5 +1,7 @@
 #nullable enable
 
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -38,7 +40,7 @@ public class KeyBindings
         else
         {
             Bindings.Add (key, binding);
-            if (binding.Scope.FastHasFlags (KeyBindingScope.Application))
+            if (binding.Scope.HasFlag (KeyBindingScope.Application))
             {
                 Application.AddKeyBinding (key, BoundView);
             }

+ 1 - 6
Terminal.Gui/View/Layout/Dim.cs

@@ -110,14 +110,9 @@ public abstract class Dim
     ///     Specifies how <see cref="Dim.Auto"/> will compute the dimension. The default is <see cref="DimAutoStyle.Auto"/>.
     /// </param>
     /// <param name="minimumContentDim">The minimum dimension the View's ContentSize will be constrained to.</param>
-    /// <param name="maximumContentDim">The maximum dimension the View's ContentSize will be fit to. NOT CURRENTLY SUPPORTED.</param>
+    /// <param name="maximumContentDim">The maximum dimension the View's ContentSize will be fit to.</param>
     public static Dim? Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim? minimumContentDim = null, Dim? maximumContentDim = null)
     {
-        if (maximumContentDim is { })
-        {
-            Debug.WriteLine (@"WARNING: maximumContentDim is not fully implemented.");
-        }
-
         return new DimAuto ()
         {
             MinimumContentDim = minimumContentDim,

+ 99 - 11
Terminal.Gui/View/Layout/DimAuto.cs

@@ -1,4 +1,6 @@
 #nullable enable
+using System.Drawing;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -81,7 +83,7 @@ public class DimAuto () : Dim
                 // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
                 subviewsSize = 0;
 
-                List<View> includedSubviews = us.Subviews.ToList();//.Where (v => !v.ExcludeFromLayout).ToList ();
+                List<View> includedSubviews = us.Subviews.ToList ();//.Where (v => !v.ExcludeFromLayout).ToList ();
                 List<View> subviews;
 
                 #region Not Anchored and Are Not Dependent
@@ -100,14 +102,16 @@ public class DimAuto () : Dim
                 {
                     subviews = includedSubviews.Where (v => v.X is not PosAnchorEnd
                                                            && v.X is not PosAlign
-                                                           // && v.X is not PosCenter
+                                                            // && v.X is not PosCenter
+                                                            && v.Width is not DimAuto
                                                            && v.Width is not DimFill).ToList ();
                 }
                 else
                 {
                     subviews = includedSubviews.Where (v => v.Y is not PosAnchorEnd
                                                            && v.Y is not PosAlign
-                                                           // && v.Y is not PosCenter
+                                                            // && v.Y is not PosCenter
+                                                            && v.Height is not DimAuto
                                                            && v.Height is not DimFill).ToList ();
                 }
 
@@ -147,6 +151,88 @@ public class DimAuto () : Dim
                 subviewsSize += maxAnchorEnd;
                 #endregion Anchored
 
+                //#region Aligned
+
+                //// Now, handle subviews that are anchored to the end
+                //// [x] PosAnchorEnd
+                //int maxAlign = 0;
+                //if (dimension == Dimension.Width)
+                //{
+                //    // Use Linq to get a list of distinct GroupIds from the subviews
+                //    List<int> groupIds = includedSubviews.Select (v => v.X is PosAlign posAlign ? posAlign.GroupId : -1).Distinct ().ToList ();
+
+                //    foreach (var groupId in groupIds)
+                //    {
+                //        List<int> dimensionsList = new ();
+
+                //        // PERF: If this proves a perf issue, consider caching a ref to this list in each item
+                //        List<PosAlign?> posAlignsInGroup = includedSubviews.Where (
+                //            v =>
+                //            {
+                //                return dimension switch
+                //                {
+                //                    Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
+                //                    Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
+                //                    _ => false
+                //                };
+                //            })
+                //            .Select (v => dimension == Dimension.Width ? v.X as PosAlign : v.Y as PosAlign)
+                //            .ToList ();
+
+                //        if (posAlignsInGroup.Count == 0)
+                //        {
+                //            continue;
+                //        }
+
+                //        maxAlign = PosAlign.CalculateMinDimension (groupId, includedSubviews, dimension);
+                //    }
+                //}
+                //else
+                //{
+                //    subviews = includedSubviews.Where (v => v.Y is PosAlign).ToList ();
+                //}
+
+                //subviewsSize = int.Max (subviewsSize, maxAlign);
+                //#endregion Aligned
+
+
+                #region Auto
+
+                if (dimension == Dimension.Width)
+                {
+                    subviews = includedSubviews.Where (v => v.Width is DimAuto).ToList ();
+                }
+                else
+                {
+                    subviews = includedSubviews.Where (v => v.Height is DimAuto).ToList ();
+                }
+
+                int maxAuto = 0;
+                for (var i = 0; i < subviews.Count; i++)
+                {
+                    View v = subviews [i];
+
+                    //if (dimension == Dimension.Width)
+                    //{
+                    //    v.SetRelativeLayout (new Size (autoMax - subviewsSize, 0));
+                    //}
+                    //else
+                    //{
+                    //    v.SetRelativeLayout (new Size (0, autoMax - subviewsSize));
+                    //}
+                    maxAuto = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+
+                    if (maxAuto > subviewsSize)
+                    {
+                        // BUGBUG: Should we break here? Or choose min/max?
+                        subviewsSize = maxAuto;
+                    }
+                }
+
+//                subviewsSize += maxAuto;
+
+                #endregion Auto
+
                 //#region Center
                 //// Now, handle subviews that are Centered
                 //if (dimension == Dimension.Width)
@@ -174,15 +260,11 @@ public class DimAuto () : Dim
                 // [ ] DimPercent
                 if (dimension == Dimension.Width)
                 {
-                    subviews = includedSubviews.Where (v => v.Width is DimFill
-                                                      // || v.X is PosCenter
-                                                     ).ToList ();
+                    subviews = includedSubviews.Where (v => v.Width is DimFill).ToList ();
                 }
                 else
                 {
-                    subviews = includedSubviews.Where (v => v.Height is DimFill
-                                                      //|| v.Y is PosCenter
-                                                     ).ToList ();
+                    subviews = includedSubviews.Where (v => v.Height is DimFill).ToList ();
                 }
 
                 int maxFill = 0;
@@ -190,6 +272,10 @@ public class DimAuto () : Dim
                 {
                     View v = subviews [i];
 
+                    if (autoMax == int.MaxValue)
+                    {
+                        autoMax = superviewContentSize;
+                    }
                     if (dimension == Dimension.Width)
                     {
                         v.SetRelativeLayout (new Size (autoMax - subviewsSize, 0));
@@ -213,9 +299,11 @@ public class DimAuto () : Dim
         // And, if min: is set, it wins if larger
         max = int.Max (max, autoMin);
 
+        // And, if max: is set, it wins if smaller
+        max = int.Min (max, autoMax);
+
         // Factor in adornments
         Thickness thickness = us.GetAdornmentsThickness ();
-
         max += dimension switch
         {
             Dimension.Width => thickness.Horizontal,
@@ -224,7 +312,7 @@ public class DimAuto () : Dim
             _ => throw new ArgumentOutOfRangeException (nameof (dimension), dimension, null)
         };
 
-        return int.Min (max, autoMax);
+        return max;
     }
 
     internal override bool ReferencesOtherViews ()

+ 24 - 0
Terminal.Gui/View/Layout/Pos.cs

@@ -336,6 +336,30 @@ public abstract class Pos
     /// <returns></returns>
     internal virtual bool ReferencesOtherViews () { return false; }
 
+    /// <summary>
+    ///     Indicates whether the specified type is in the hierarchy of this Pos object.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="pos"></param>
+    /// <returns></returns>
+    public bool Has (Type type, out Pos pos)
+    {
+        pos = this;
+        if (type == GetType ())
+        {
+            return true;
+        }
+
+        // If we are a PosCombine, we have to check the left and right
+        // to see if they are of the type we are looking for.
+        if (this is PosCombine { } combine && (combine.Left.Has (type, out pos) || combine.Right.Has (type, out pos)))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
     #endregion virtual methods
 
     #region operators

+ 54 - 49
Terminal.Gui/View/Layout/PosAlign.cs

@@ -29,7 +29,7 @@ public class PosAlign : Pos
     /// <summary>
     ///     The cached location. Used to store the calculated location to minimize recalculating it.
     /// </summary>
-    private int? _cachedLocation;
+    public int? _cachedLocation;
 
     /// <summary>
     ///     Gets the identifier of a set of views that should be aligned together. When only a single
@@ -70,59 +70,67 @@ public class PosAlign : Pos
         List<int> dimensionsList = new ();
 
         // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-        List<View> viewsInGroup = views.Where (
-                                               v =>
-                                               {
-                                                   return dimension switch
-                                                   {
-                                                       Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
-                                                       Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
-                                                       _ => false
-                                                   };
-                                               })
+        List<PosAlign?> posAligns = views.Select (
+                                                v =>
+                                                {
+                                                    switch (dimension)
+                                                    {
+                                                        case Dimension.Width when v.X.Has (typeof (PosAlign), out var pos):
+
+                                                            if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
+                                                            {
+                                                                return posAlignX;
+                                                            }
+
+                                                            break;
+                                                        case Dimension.Height when v.Y.Has (typeof (PosAlign), out var pos):
+                                                            if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
+                                                            {
+                                                                return posAlignY;
+                                                            }
+
+                                                            break;
+                                                    }
+
+                                                    return null;
+                                                })
                                        .ToList ();
 
-        if (viewsInGroup.Count == 0)
-        {
-            return;
-        }
-
         // PERF: We iterate over viewsInGroup multiple times here.
 
         Aligner? firstInGroup = null;
 
         // Update the dimensionList with the sizes of the views
-        for (var index = 0; index < viewsInGroup.Count; index++)
+        for (var index = 0; index < posAligns.Count; index++)
         {
-            View view = viewsInGroup [index];
-            PosAlign? posAlign = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
-
-            if (posAlign is { })
+            if (posAligns [index] is { })
             {
-                if (index == 0)
+                if (firstInGroup is null)
                 {
-                    firstInGroup = posAlign.Aligner;
+                    firstInGroup = posAligns [index]!.Aligner;
                 }
 
-                dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
+                dimensionsList.Add (dimension == Dimension.Width ? views [index].Frame.Width : views [index].Frame.Height);
             }
         }
 
+        if (firstInGroup is null)
+        {
+            return;
+        }
+
         // Update the first item in the group with the new container size.
-        firstInGroup!.ContainerSize = size;
+        firstInGroup.ContainerSize = size;
 
         // Align
         int [] locations = firstInGroup.Align (dimensionsList.ToArray ());
 
         // Update the cached location for each item
-        for (var index = 0; index < viewsInGroup.Count; index++)
+        for (int posIndex = 0, locIndex = 0; posIndex < posAligns.Count; posIndex++)
         {
-            View view = viewsInGroup [index];
-            PosAlign? align = dimension == Dimension.Width ? view.X as PosAlign : view.Y as PosAlign;
-
-            if (align is { })
+            if (posAligns [posIndex] is { })
             {
-                align._cachedLocation = locations [index];
+                posAligns [posIndex]!._cachedLocation = locations [locIndex++];
             }
         }
     }
@@ -168,7 +176,15 @@ public class PosAlign : Pos
         return 0;
     }
 
-    internal int CalculateMinDimension (int groupId, IList<View> views, Dimension dimension)
+    // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
+    /// <summary>
+    /// Returns the minimum size a group of views with the same <paramref name="groupId"/> can be.
+    /// </summary>
+    /// <param name="groupId"></param>
+    /// <param name="views"></param>
+    /// <param name="dimension"></param>
+    /// <returns></returns>
+    public static int CalculateMinDimension (int groupId, IList<View> views, Dimension dimension)
     {
         List<int> dimensionsList = new ();
 
@@ -177,11 +193,11 @@ public class PosAlign : Pos
                                                v =>
                                                {
                                                    return dimension switch
-                                                          {
-                                                              Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
-                                                              Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
-                                                              _ => false
-                                                          };
+                                                   {
+                                                       Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
+                                                       Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
+                                                       _ => false
+                                                   };
                                                })
                                        .ToList ();
 
@@ -192,8 +208,6 @@ public class PosAlign : Pos
 
         // PERF: We iterate over viewsInGroup multiple times here.
 
-        Aligner? firstInGroup = null;
-
         // Update the dimensionList with the sizes of the views
         for (var index = 0; index < viewsInGroup.Count; index++)
         {
@@ -203,20 +217,11 @@ public class PosAlign : Pos
 
             if (posAlign is { })
             {
-                if (index == 0)
-                {
-                    firstInGroup = posAlign.Aligner;
-                }
-
                 dimensionsList.Add (dimension == Dimension.Width ? view.Frame.Width : view.Frame.Height);
             }
         }
 
         // Align
-        var aligner = firstInGroup;
-        aligner.ContainerSize = dimensionsList.Sum();
-        int [] locations = aligner.Align (dimensionsList.ToArray ());
-
-        return locations.Sum ();
+        return dimensionsList.Sum ();
     }
 }

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

@@ -230,6 +230,7 @@ public partial class View : Responder, ISupportInitializeNotification
         }
 
         Initialized?.Invoke (this, EventArgs.Empty);
+
     }
 
     #endregion Constructors and Initialization

+ 68 - 18
Terminal.Gui/View/ViewAdornments.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using System.ComponentModel;
+
+namespace Terminal.Gui;
 
 public partial class View
 {
@@ -95,29 +97,77 @@ public partial class View
         get => Border?.LineStyle ?? LineStyle.Single;
         set
         {
-            if (Border is null)
-            {
-                return;
-            }
+            StateEventArgs<LineStyle> e = new (Border?.LineStyle ?? LineStyle.None, value);
+            OnBorderStyleChanging (e);
 
-            if (value != LineStyle.None)
-            {
-                if (Border.Thickness == Thickness.Empty)
-                {
-                    Border.Thickness = new (1);
-                }
-            }
-            else
+        }
+    }
+
+    /// <summary>
+    /// Called when the <see cref="BorderStyle"/> is changing. Invokes <see cref="BorderStyleChanging"/>, which allows the event to be cancelled.
+    /// </summary>
+    /// <remarks>
+    ///     Override <see cref="SetBorderStyle"/> to prevent the <see cref="BorderStyle"/> from changing.
+    /// </remarks>
+    /// <param name="e"></param>
+    protected void OnBorderStyleChanging (StateEventArgs<LineStyle> e)
+    {
+        if (Border is null)
+        {
+            return;
+        }
+
+        BorderStyleChanging?.Invoke (this, e);
+        if (e.Cancel)
+        {
+            return;
+        }
+
+        SetBorderStyle (e.NewValue);
+        LayoutAdornments ();
+        SetNeedsLayout ();
+
+        return;
+    }
+
+    /// <summary>
+    ///     Sets the <see cref="BorderStyle"/> of the view to the specified value.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///          <see cref="BorderStyle"/> is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other
+    ///         than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
+    ///         <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
+    ///     </para>
+    ///     <para>
+    ///         Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
+    ///         <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
+    ///     </para>
+    ///     <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
+    /// </remarks>
+    /// <param name="value"></param>
+    public virtual void SetBorderStyle (LineStyle value)
+    {
+        if (value != LineStyle.None)
+        {
+            if (Border.Thickness == Thickness.Empty)
             {
-                Border.Thickness = new (0);
+                Border.Thickness = new (1);
             }
-
-            Border.LineStyle = value;
-            LayoutAdornments ();
-            SetNeedsLayout ();
         }
+        else
+        {
+            Border.Thickness = new (0);
+        }
+
+        Border.LineStyle = value;
     }
 
+    /// <summary>
+    ///     Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
+    /// </summary>
+    public event EventHandler<StateEventArgs<LineStyle>> BorderStyleChanging;
+
     /// <summary>
     ///     The <see cref="Adornment"/> inside of the view that offsets the <see cref="Viewport"/>
     ///     from the <see cref="Border"/>.

+ 5 - 5
Terminal.Gui/View/ViewDrawing.cs

@@ -640,17 +640,17 @@ public partial class View
     {
         SubViewNeedsDisplay = true;
 
+        if (this is Adornment adornment)
+        {
+            adornment.Parent?.SetSubViewNeedsDisplay ();
+        }
+
         if (SuperView is { SubViewNeedsDisplay: false })
         {
             SuperView.SetSubViewNeedsDisplay ();
 
             return;
         }
-
-        if (this is Adornment adornment)
-        {
-            adornment.Parent?.SetSubViewNeedsDisplay ();
-        }
     }
 
     /// <summary>Clears <see cref="NeedsDisplay"/> and <see cref="SubViewNeedsDisplay"/>.</summary>

+ 88 - 20
Terminal.Gui/View/ViewKeyboard.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using System.Diagnostics;
 
 namespace Terminal.Gui;
 
@@ -414,7 +415,7 @@ public partial class View
             return true;
         }
 
-        bool? handled = OnInvokingKeyBindings (keyEvent);
+        bool? handled = OnInvokingKeyBindings (keyEvent, KeyBindingScope.HotKey | KeyBindingScope.Focused);
 
         if (handled is { } && (bool)handled)
         {
@@ -636,10 +637,10 @@ public partial class View
     ///     <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.
     /// </returns>
-    public virtual bool? OnInvokingKeyBindings (Key keyEvent)
+    public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
-        // fire event only if there's an app or hotkey binding for the key
-        if (KeyBindings.TryGet (keyEvent, KeyBindingScope.Application | KeyBindingScope.HotKey, out KeyBinding _))
+        // fire event only if there's an hotkey binding for the key
+        if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding kb))
         {
             InvokingKeyBindings?.Invoke (this, keyEvent);
             if (keyEvent.Handled)
@@ -655,7 +656,7 @@ public partial class View
         //   `InvokeKeyBindings` returns `false`. Continue passing the event (return `false` from `OnInvokeKeyBindings`)..
         // * If key bindings were found, and any handled the key (at least one `Command` returned `true`),
         //   `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`).
-        bool? handled = InvokeKeyBindings (keyEvent);
+        bool? handled = InvokeKeyBindings (keyEvent, scope);
 
         if (handled is { } && (bool)handled)
         {
@@ -664,22 +665,22 @@ public partial class View
             return true;
         }
 
-        if (Margin is { } && ProcessAdornmentKeyBindings (Margin, keyEvent, ref handled))
+        if (Margin is { } && ProcessAdornmentKeyBindings (Margin, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (Padding is { } && ProcessAdornmentKeyBindings (Padding, keyEvent, ref handled))
+        if (Padding is { } && ProcessAdornmentKeyBindings (Padding, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (Border is { } && ProcessAdornmentKeyBindings (Border, keyEvent, ref handled))
+        if (Border is { } && ProcessAdornmentKeyBindings (Border, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (ProcessSubViewKeyBindings (keyEvent, ref handled))
+        if (ProcessSubViewKeyBindings (keyEvent, scope, ref handled))
         {
             return true;
         }
@@ -687,11 +688,11 @@ public partial class View
         return handled;
     }
 
-    private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, ref bool? handled)
+    private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled)
     {
         foreach (View subview in adornment?.Subviews)
         {
-            handled = subview.OnInvokingKeyBindings (keyEvent);
+            handled = subview.OnInvokingKeyBindings (keyEvent, scope);
 
             if (handled is { } && (bool)handled)
             {
@@ -702,26 +703,71 @@ public partial class View
         return false;
     }
 
-    private bool ProcessSubViewKeyBindings (Key keyEvent, ref bool? handled)
+    private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref bool? handled, bool invoke = true)
     {
         // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey.
         foreach (View subview in Subviews)
         {
-            if (subview.KeyBindings.TryGet (keyEvent, KeyBindingScope.HotKey, out KeyBinding binding))
+            if (subview.KeyBindings.TryGet (keyEvent, scope, out KeyBinding binding))
             {
-                //keyEvent.Scope = KeyBindingScope.HotKey;
-                handled = subview.OnInvokingKeyBindings (keyEvent);
+                if (binding.Scope == KeyBindingScope.Focused && !subview.HasFocus)
+                {
+                    continue;
+                }
+
+                if (!invoke)
+                {
+                    return true;
+                }
+
+                handled = subview.OnInvokingKeyBindings (keyEvent, scope);
 
                 if (handled is { } && (bool)handled)
                 {
                     return true;
                 }
             }
+
+            bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled, invoke);
+            if (recurse || (handled is { } && (bool)handled))
+            {
+                return true;
+            }
         }
 
         return false;
     }
 
+    // TODO: This is a "prototype" debug check. It may be too annyoing vs. useful.
+    // TODO: A better approach would be have Application hold a list of bound Hotkeys, similar to
+    // TODO: how Application holds a list of Application Scoped key bindings and then check that list.
+    /// <summary>
+    /// Returns true if Key is bound in this view heirarchy. For debugging
+    /// </summary>
+    /// <param name="key"></param>
+    /// <returns></returns>
+    public bool IsHotKeyKeyBound (Key key, out View boundView)
+    {
+        // recurse through the subviews to find the views that has the key bound
+        boundView = null;
+
+        foreach (View subview in Subviews)
+        {
+            if (subview.KeyBindings.TryGet (key, KeyBindingScope.HotKey, out _))
+            {
+                boundView = subview;
+                return true;
+            }
+
+            if (subview.IsHotKeyKeyBound (key, out boundView))
+            {
+                return true;
+            }
+
+        }
+        return false;
+    }
+
     /// <summary>
     ///     Invoked 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.
@@ -738,15 +784,36 @@ public partial class View
     ///     commands were invoked and at least one handled the command. <see langword="false"/> if commands were invoked and at
     ///     none handled the command.
     /// </returns>
-    protected bool? InvokeKeyBindings (Key key)
+    protected bool? InvokeKeyBindings (Key key, KeyBindingScope scope)
     {
         bool? toReturn = null;
 
-        if (!KeyBindings.TryGet (key, out KeyBinding binding))
+        if (!KeyBindings.TryGet (key, scope, out KeyBinding binding))
         {
             return null;
         }
 
+#if DEBUG
+
+        // TODO: Determine if App scope bindings should be fired first or last (currently last).
+        if (Application.TryGetKeyBindings (key, out List<View> views))
+        {
+            var boundView = views [0];
+            var commandBinding = boundView.KeyBindings.Get (key);
+            Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command.{commandBinding.Commands [0]}: {boundView}.");
+        }
+
+        // TODO: This is a "prototype" debug check. It may be too annyoing vs. useful.
+        // Scour the bindings up our View heirarchy
+        // to ensure that the key is not already bound to a different set of commands.
+        if (SuperView?.IsHotKeyKeyBound (key, out View previouslyBoundView) ?? false)
+        {
+            Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - A subview or peer has bound this Key and will not see it: {previouslyBoundView}.");
+        }
+
+#endif
+
+
         foreach (Command command in binding.Commands)
         {
             if (!CommandImplementations.ContainsKey (command))
@@ -777,10 +844,11 @@ public partial class View
     /// </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 and it handled the command.
-    ///     <see langword="false"/> if the command was invoked and it did not handle the command.
+    ///     <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, [CanBeNull] Key key = null, [CanBeNull] KeyBinding? keyBinding = null)
     {
@@ -871,7 +939,7 @@ public partial class View
     /// <param name="f">The function.</param>
     protected void AddCommand (Command command, Func<bool?> f)
     {
-        CommandImplementations [command] = ctx => f (); ;
+        CommandImplementations [command] = ctx => f ();
     }
 
     /// <summary>Returns all commands that are supported by this <see cref="View"/>.</summary>

+ 13 - 4
Terminal.Gui/View/ViewSubViews.cs

@@ -1,3 +1,5 @@
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 public partial class View
@@ -40,11 +42,13 @@ public partial class View
     ///         the lifecycle of the subviews to be transferred to this View.
     ///     </para>
     /// </remarks>
-    public virtual void Add (View view)
+    /// <param name="view">The view to add.</param>
+    /// <returns>The view that was added.</returns>
+    public virtual View Add (View view)
     {
         if (view is null)
         {
-            return;
+            return view;
         }
 
         if (_subviews is null)
@@ -72,6 +76,7 @@ public partial class View
                 SuperView._addingView = false;
             }
 
+            // QUESTION: This automatic behavior of setting CanFocus to true on the SuperView is not documented, and is annoying.
             CanFocus = true;
             view._tabIndex = _tabIndexes.IndexOf (view);
             _addingView = false;
@@ -94,6 +99,8 @@ public partial class View
         CheckDimAuto ();
         SetNeedsLayout ();
         SetNeedsDisplay ();
+
+        return view;
     }
 
     /// <summary>Adds the specified views (children) to the view.</summary>
@@ -205,11 +212,11 @@ public partial class View
     ///         lifecycle to be transferred to the caller; the caller muse call <see cref="Dispose"/>.
     ///     </para>
     /// </remarks>
-    public virtual void Remove (View view)
+    public virtual View Remove (View view)
     {
         if (view is null || _subviews is null)
         {
-            return;
+            return view;
         }
 
         Rectangle touched = view.Frame;
@@ -234,6 +241,8 @@ public partial class View
         {
             Focused = null;
         }
+
+        return view;
     }
 
     /// <summary>

+ 229 - 0
Terminal.Gui/Views/Bar.cs

@@ -0,0 +1,229 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Provides a horizontally or vertically oriented container for <see cref="Shortcut"/>s to be used as a menu, toolbar, or status
+///     bar.
+/// </summary>
+/// <remarks>
+///     <para>
+///         Any <see cref="View"/> can be added to a <see cref="Bar"/>. However, the <see cref="Bar"/> is designed to work with
+///         <see cref="Shortcut"/> objects. The <see cref="Shortcut"/> class provides a way to display a command, help, and key and
+///         align them in a specific order.
+///     </para>
+/// </remarks>
+public class Bar : View
+{
+    /// <inheritdoc/>
+    public Bar () : this ([]) { }
+
+    /// <inheritdoc/>
+    public Bar (IEnumerable<Shortcut> shortcuts)
+    {
+        CanFocus = true;
+
+        Width = Dim.Auto ();
+        Height = Dim.Auto ();
+
+        Initialized += Bar_Initialized;
+
+        if (shortcuts is null)
+        {
+            return;
+        }
+
+        foreach (Shortcut shortcut in shortcuts)
+        {
+            Add (shortcut);
+        }
+    }
+
+    private void Bar_Initialized (object sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
+
+    /// <inheritdoc/>
+    public override void SetBorderStyle (LineStyle value)
+    {
+        // The default changes the thickness. We don't want that. We just set the style.
+        Border.LineStyle = value;
+    }
+
+    private Orientation _orientation = Orientation.Horizontal;
+
+    /// <summary>
+    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
+    ///     <see cref="Orientation.Horizontal"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Horizontal orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from right to left
+    ///         Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to right.
+    ///     </para>
+    /// </remarks>
+    public Orientation Orientation
+    {
+        get => _orientation;
+        set
+        {
+            _orientation = value;
+            SetNeedsLayout ();
+        }
+    }
+
+    private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd;
+
+    /// <summary>
+    ///     Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Bar"/>. The default is
+    ///     <see cref="AlignmentModes.StartToEnd"/>.
+    /// </summary>
+    public AlignmentModes AlignmentModes
+    {
+        get => _alignmentModes;
+        set
+        {
+            _alignmentModes = value;
+            SetNeedsLayout ();
+        }
+    }
+
+    // TODO: Move this to View
+    /// <summary>Inserts a <see cref="Shortcut"/> in the specified index of <see cref="View.Subviews"/>.</summary>
+    /// <param name="index">The zero-based index at which item should be inserted.</param>
+    /// <param name="item">The item to insert.</param>
+    public void AddShortcutAt (int index, Shortcut item)
+    {
+        List<View> savedSubViewList = Subviews.ToList ();
+        int count = savedSubViewList.Count;
+        RemoveAll ();
+
+        for (var i = 0; i <= count; i++)
+        {
+            if (i == index)
+            {
+                Add (item);
+            }
+
+            if (i < count)
+            {
+                Add (savedSubViewList [i]);
+            }
+        }
+
+        SetNeedsDisplay ();
+    }
+
+    // TODO: Move this to View
+
+    /// <summary>Removes a <see cref="Shortcut"/> at specified index of <see cref="View.Subviews"/>.</summary>
+    /// <param name="index">The zero-based index of the item to remove.</param>
+    /// <returns>The <see cref="Shortcut"/> removed.</returns>
+    public Shortcut RemoveShortcut (int index)
+    {
+        View toRemove = null;
+
+        for (var i = 0; i < Subviews.Count; i++)
+        {
+            if (i == index)
+            {
+                toRemove = Subviews [i];
+            }
+        }
+
+        if (toRemove is { })
+        {
+            Remove (toRemove);
+            SetNeedsDisplay ();
+        }
+
+        return toRemove as Shortcut;
+    }
+
+    /// <inheritdoc />
+    internal override void OnLayoutStarted (LayoutEventArgs args)
+    {
+        base.OnLayoutStarted (args);
+
+        View prevBarItem = null;
+
+        switch (Orientation)
+        {
+            case Orientation.Horizontal:
+                for (var index = 0; index < Subviews.Count; index++)
+                {
+                    View barItem = Subviews [index];
+
+                    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;
+
+            case Orientation.Vertical:
+                // Set the overall size of the Bar and arrange the views vertically
+
+                var minKeyWidth = 0;
+
+                List<Shortcut> shortcuts = Subviews.Where (s => s is Shortcut && s.Visible).Cast<Shortcut> ().ToList ();
+                foreach (Shortcut shortcut in shortcuts)
+                {
+                    // Let AutoSize do its thing to get the minimum width of each CommandView and HelpView
+                    //shortcut.CommandView.SetRelativeLayout (new Size (int.MaxValue, int.MaxValue));
+                    minKeyWidth = int.Max (minKeyWidth, shortcut.KeyView.Text.GetColumns ());
+                }
+
+                var maxBarItemWidth = 0;
+                var totalHeight = 0;
+
+                for (var index = 0; index < Subviews.Count; index++)
+                {
+                    View barItem = Subviews [index];
+
+                    barItem.ColorScheme = ColorScheme;
+
+                    if (!barItem.Visible)
+                    {
+                        continue;
+                    }
+
+                    if (barItem is Shortcut scBarItem)
+                    {
+                        scBarItem.MinimumKeyTextSize = minKeyWidth;
+                        // HACK: This should not be needed
+                        scBarItem.SetRelativeLayout (GetContentSize ());
+                        maxBarItemWidth = Math.Max (maxBarItemWidth, scBarItem.Frame.Width);
+                    }
+
+                    if (prevBarItem == null)
+                    {
+                        barItem.Y = 0;
+                    }
+                    else
+                    {
+                        // Align the view to the bottom of the previous view
+                        barItem.Y = Pos.Bottom (prevBarItem);
+                    }
+
+                    prevBarItem = barItem;
+
+                    barItem.X = 0;
+                    totalHeight += barItem.Frame.Height;
+                }
+
+
+                foreach (View barItem in Subviews)
+                {
+                    barItem.Width = maxBarItemWidth;
+
+                    if (barItem is Line line)
+                    {
+                    }
+                }
+
+                Height = Dim.Auto (DimAutoStyle.Content, totalHeight);
+
+                break;
+        }
+    }
+}

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

@@ -48,7 +48,7 @@ public class ColorPicker : View
 
     private void ColorPicker_MouseClick (object sender, MouseEventEventArgs me)
     {
-        if (CanFocus)
+       // if (CanFocus)
         {
             Cursor = new Point (me.MouseEvent.Position.X / _boxWidth, me.MouseEvent.Position.Y / _boxHeight);
             SetFocus ();

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

@@ -9,7 +9,7 @@ namespace Terminal.Gui;
 /// </summary>
 /// <remarks>
 ///     To run the <see cref="Dialog"/> modally, create the <see cref="Dialog"/>, and pass it to
-///     <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will execute the dialog until
+///     <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. This will execute the dialog until
 ///     it terminates via the
 ///     [ESC] or [CTRL-Q] key, or when one of the views or buttons added to the dialog calls
 ///     <see cref="Application.RequestStop"/>.

+ 10 - 19
Terminal.Gui/Views/HexView.cs

@@ -249,8 +249,6 @@ public class HexView : View
     /// <inheritdoc/>
     protected internal override bool OnMouseEvent  (MouseEvent me)
     {
-        // BUGBUG: Test this with a border! Assumes Frame == Viewport!
-
         if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
             && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
             && !me.Flags.HasFlag (MouseFlags.WheeledDown)
@@ -343,20 +341,17 @@ public class HexView : View
         Driver.SetAttribute (current);
         Move (0, 0);
 
-        // BUGBUG: Viewport!!!!
-        Rectangle frame = Frame;
-
         int nblocks = bytesPerLine / bsize;
-        var data = new byte [nblocks * bsize * frame.Height];
+        var data = new byte [nblocks * bsize * viewport.Height];
         Source.Position = displayStart;
         int n = source.Read (data, 0, data.Length);
 
         Attribute activeColor = ColorScheme.HotNormal;
         Attribute trackingColor = ColorScheme.HotFocus;
 
-        for (var line = 0; line < frame.Height; line++)
+        for (var line = 0; line < viewport.Height; line++)
         {
-            Rectangle lineRect = new (0, line, frame.Width, 1);
+            Rectangle lineRect = new (0, line, viewport.Width, 1);
 
             if (!Viewport.Contains (lineRect))
             {
@@ -597,16 +592,15 @@ public class HexView : View
 
     private bool MoveDown (int bytes)
     {
-        // BUGBUG: Viewport!
         RedisplayLine (position);
 
         if (position + bytes < source.Length)
         {
             position += bytes;
         }
-        else if ((bytes == bytesPerLine * Frame.Height && source.Length >= DisplayStart + bytesPerLine * Frame.Height)
-                 || (bytes <= bytesPerLine * Frame.Height - bytesPerLine
-                     && source.Length <= DisplayStart + bytesPerLine * Frame.Height))
+        else if ((bytes == bytesPerLine * Viewport.Height && source.Length >= DisplayStart + bytesPerLine * Viewport.Height)
+                 || (bytes <= bytesPerLine * Viewport.Height - bytesPerLine
+                     && source.Length <= DisplayStart + bytesPerLine * Viewport.Height))
         {
             long p = position;
 
@@ -618,7 +612,7 @@ public class HexView : View
             position = p;
         }
 
-        if (position >= DisplayStart + bytesPerLine * Frame.Height)
+        if (position >= DisplayStart + bytesPerLine * Viewport.Height)
         {
             SetDisplayStart (DisplayStart + bytes);
             SetNeedsDisplay ();
@@ -635,8 +629,7 @@ public class HexView : View
     {
         position = source.Length;
 
-        // BUGBUG: Viewport!
-        if (position >= DisplayStart + bytesPerLine * Frame.Height)
+        if (position >= DisplayStart + bytesPerLine * Viewport.Height)
         {
             SetDisplayStart (position);
             SetNeedsDisplay ();
@@ -722,8 +715,7 @@ public class HexView : View
             position++;
         }
 
-        // BUGBUG: Viewport!
-        if (position >= DisplayStart + bytesPerLine * Frame.Height)
+        if (position >= DisplayStart + bytesPerLine * Viewport.Height)
         {
             SetDisplayStart (DisplayStart + bytesPerLine);
             SetNeedsDisplay ();
@@ -771,8 +763,7 @@ public class HexView : View
         var delta = (int)(pos - DisplayStart);
         int line = delta / bytesPerLine;
 
-        // BUGBUG: Viewport!
-        SetNeedsDisplay (new (0, line, Frame.Width, 1));
+        SetNeedsDisplay (new (0, line, Viewport.Width, 1));
     }
 
     private bool ToggleSide ()

+ 58 - 3
Terminal.Gui/Views/Line.cs

@@ -8,26 +8,81 @@ public class Line : View
     {
         BorderStyle = LineStyle.Single;
         Border.Thickness = new Thickness (0);
+        SuperViewRendersLineCanvas = true;
     }
 
+    public Dim Length { get; set; } = Dim.Fill ();
+
+    private Orientation _orientation;
+
     /// <summary>
     ///     The direction of the line.  If you change this you will need to manually update the Width/Height of the
     ///     control to cover a relevant area based on the new direction.
     /// </summary>
-    public Orientation Orientation { get; set; }
+    public Orientation Orientation
+    {
+        get => _orientation;
+        set
+        {
+            _orientation = value;
+
+            switch (Orientation)
+            {
+                case Orientation.Horizontal:
+                    Height = 1;
+                    // Width = Length;
+                    //Border.Thickness = new Thickness (1, 0, 1, 0);
+
+                    break;
+                case Orientation.Vertical:
+                    Height = Length;
+                    Width = 1;
+
+                    break;
+
+            }
+        }
+    }
+
+    /// <inheritdoc/>
+    public override void SetBorderStyle (LineStyle value)
+    {
+        // The default changes the thickness. We don't want that. We just set the style.
+        Border.LineStyle = value;
+    }
 
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle viewport)
     {
         LineCanvas lc = LineCanvas;
 
+        if (SuperViewRendersLineCanvas)
+        {
+            lc = SuperView.LineCanvas;
+        }
+
         if (SuperView is Adornment adornment)
         {
             lc = adornment.Parent.LineCanvas;
         }
+
+        Point pos = ViewportToScreen (viewport).Location;
+        int length = Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height;
+
+        if (SuperViewRendersLineCanvas && Orientation == Orientation.Horizontal)
+        {
+            pos.Offset (-SuperView.Border.Thickness.Left, 0);
+            length += SuperView.Border.Thickness.Horizontal;
+        }
+
+        if (SuperViewRendersLineCanvas && Orientation == Orientation.Vertical)
+        {
+            pos.Offset (0, -SuperView.Border.Thickness.Top);
+            length += SuperView.Border.Thickness.Vertical;
+        }
         lc.AddLine (
-                    ViewportToScreen (viewport).Location,
-                    Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height,
+                    pos,
+                    length,
                     Orientation,
                     BorderStyle
                    );

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

@@ -273,9 +273,9 @@ internal sealed class Menu : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key keyEvent)
+    public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
-        bool? handled = base.OnInvokingKeyBindings (keyEvent);
+        bool? handled = base.OnInvokingKeyBindings (keyEvent, scope);
 
         if (handled is { } && (bool)handled)
         {
@@ -284,7 +284,7 @@ internal sealed class Menu : View
 
         // TODO: Determine if there's a cleaner way to handle this
         // This supports the case where the menu bar is a context menu
-        return _host.OnInvokingKeyBindings (keyEvent);
+        return _host.OnInvokingKeyBindings (keyEvent, scope);
     }
 
     private void Current_TerminalResized (object sender, SizeChangedEventArgs e)

+ 58 - 0
Terminal.Gui/Views/MenuBarv2.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Reflection;
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     A menu bar is a <see cref="View"/> that snaps to the top of a <see cref="Toplevel"/> displaying set of
+///     <see cref="Shortcut"/>s.
+/// </summary>
+public class MenuBarv2 : Bar
+{
+    /// <inheritdoc/>
+    public MenuBarv2 () : this ([]) { }
+
+    /// <inheritdoc/>
+    public MenuBarv2 (IEnumerable<Shortcut> shortcuts) : base (shortcuts)
+    {
+        Y = 0;
+        Width = Dim.Fill ();
+        Height = Dim.Auto (DimAutoStyle.Content, 1);
+        BorderStyle = LineStyle.Dashed;
+        ColorScheme = Colors.ColorSchemes ["Menu"];
+        Orientation = Orientation.Horizontal;
+
+        LayoutStarted += MenuBarv2_LayoutStarted;
+    }
+
+    // MenuBarv2 arranges the items horizontally.
+    // The first item has no left border, the last item has no right border.
+    // The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart).
+    private void MenuBarv2_LayoutStarted (object sender, LayoutEventArgs e)
+    {
+       
+    }
+
+    /// <inheritdoc/>
+    public override View Add (View view)
+    {
+        // Call base first, because otherwise it resets CanFocus to true
+        base.Add (view);
+
+        view.CanFocus = true;
+
+        if (view is Shortcut shortcut)
+        {
+            shortcut.KeyBindingScope = KeyBindingScope.Application;
+
+            // TODO: not happy about using AlignmentModes for this. Too implied.
+            // TODO: instead, add a property (a style enum?) to Shortcut to control this
+            //shortcut.AlignmentModes = AlignmentModes.EndToStart;
+
+            shortcut.KeyView.Visible = false;
+            shortcut.HelpView.Visible = false;
+        }
+
+        return view;
+    }
+}

+ 64 - 0
Terminal.Gui/Views/Menuv2.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Reflection;
+
+namespace Terminal.Gui;
+
+/// <summary>
+/// </summary>
+public class Menuv2 : Bar
+{
+    /// <inheritdoc/>
+    public Menuv2 () : this ([]) { }
+
+    /// <inheritdoc/>
+    public Menuv2 (IEnumerable<Shortcut> shortcuts) : base (shortcuts)
+    {
+        Orientation = Orientation.Vertical;
+        Width = Dim.Auto ();
+        Height = Dim.Auto (DimAutoStyle.Content, 1);
+        ColorScheme = Colors.ColorSchemes ["Menu"];
+        Initialized += Menuv2_Initialized;
+    }
+
+    private void Menuv2_Initialized (object sender, EventArgs e)
+    {
+        Border.Thickness = new Thickness (1, 1, 1, 1);
+    }
+
+    // Menuv2 arranges the items horizontally.
+    // The first item has no left border, the last item has no right border.
+    // The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart).
+    internal override void OnLayoutStarted (LayoutEventArgs args)
+    {
+        for (int index = 0; index < Subviews.Count; index++)
+        {
+            View barItem = Subviews [index];
+
+            if (!barItem.Visible)
+            {
+                continue;
+            }
+
+        }
+        base.OnLayoutStarted (args);
+    }
+
+    /// <inheritdoc/>
+    public override View Add (View view)
+    {
+        base.Add (view);
+
+        if (view is Shortcut shortcut)
+        {
+            shortcut.CanFocus = true;
+            shortcut.KeyBindingScope = KeyBindingScope.Application;
+            shortcut.Orientation = Orientation.Vertical;
+
+            // TODO: not happy about using AlignmentModes for this. Too implied.
+            // TODO: instead, add a property (a style enum?) to Shortcut to control this
+            //shortcut.AlignmentModes = AlignmentModes.EndToStart;
+        }
+
+        return view;
+    }
+}

+ 1 - 1
Terminal.Gui/Views/OpenDialog.cs

@@ -36,7 +36,7 @@ public enum OpenMode
 ///     </para>
 ///     <para>
 ///         To use, create an instance of <see cref="OpenDialog"/>, and pass it to
-///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will run the dialog modally, and when this returns,
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. This will run the dialog modally, and when this returns,
 ///         the list of files will be available on the <see cref="FilePaths"/> property.
 ///     </para>
 ///     <para>To select more than one file, users can use the spacebar, or control-t.</para>

+ 22 - 1
Terminal.Gui/Views/RadioGroup.cs

@@ -25,6 +25,11 @@ public class RadioGroup : View
                     Command.LineUp,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
+
                         MoveUpLeft ();
 
                         return true;
@@ -35,6 +40,10 @@ public class RadioGroup : View
                     Command.LineDown,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveDownRight ();
 
                         return true;
@@ -45,6 +54,10 @@ public class RadioGroup : View
                     Command.TopHome,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveHome ();
 
                         return true;
@@ -55,6 +68,10 @@ public class RadioGroup : View
                     Command.BottomEnd,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveEnd ();
 
                         return true;
@@ -355,7 +372,11 @@ public class RadioGroup : View
     /// <param name="selectedItem"></param>
     /// <param name="previousSelectedItem"></param>
     public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
-    {
+    { 
+        if (_selected == selectedItem)
+        {
+            return;
+        }
         _selected = selectedItem;
         SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
     }

+ 1 - 1
Terminal.Gui/Views/SaveDialog.cs

@@ -17,7 +17,7 @@ namespace Terminal.Gui;
 /// <remarks>
 ///     <para>
 ///         To use, create an instance of <see cref="SaveDialog"/>, and pass it to
-///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will run the dialog modally, and when this returns,
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. This will run the dialog modally, and when this returns,
 ///         the <see cref="FileName"/>property will contain the selected file name or null if the user canceled.
 ///     </para>
 /// </remarks>

+ 7 - 4
Terminal.Gui/Views/ScrollView.cs

@@ -346,7 +346,7 @@ public class ScrollView : View
 
     /// <summary>Adds the view to the scrollview.</summary>
     /// <param name="view">The view to add to the scrollview.</param>
-    public override void Add (View view)
+    public override View Add (View view)
     {
         if (view is ScrollBarView.ContentBottomRightCorner)
         {
@@ -365,6 +365,7 @@ public class ScrollView : View
         }
 
         SetNeedsLayout ();
+        return view;
     }
 
     /// <inheritdoc/>
@@ -391,7 +392,7 @@ public class ScrollView : View
             return true;
         }
 
-        bool? result = InvokeKeyBindings (a);
+        bool? result = InvokeKeyBindings (a, KeyBindingScope.HotKey | KeyBindingScope.Focused);
 
         if (result is { })
         {
@@ -456,11 +457,11 @@ public class ScrollView : View
 
     /// <summary>Removes the view from the scrollview.</summary>
     /// <param name="view">The view to remove from the scrollview.</param>
-    public override void Remove (View view)
+    public override View Remove (View view)
     {
         if (view is null)
         {
-            return;
+            return view;
         }
 
         SetNeedsDisplay ();
@@ -479,6 +480,8 @@ public class ScrollView : View
         {
             CanFocus = false;
         }
+
+        return view;
     }
 
     /// <summary>Removes all widgets from this container.</summary>

+ 791 - 0
Terminal.Gui/Views/Shortcut.cs

@@ -0,0 +1,791 @@
+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 command will be invoked. Useful for
+///     displaying a command in <see cref="Bar"/> such as a
+///     menu, toolbar, or status bar.
+/// </summary>
+/// <remarks>
+///     <para>
+///         The following user actions will invoke the <see cref="Command.Accept"/>, causing the
+///         <see cref="View.Accept"/> 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 <see cref="Command.Accept"/>
+///         command 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
+///         binding on the
+///         right side. Set <see cref="AlignmentModes"/> to <see cref="AlignmentModes.EndToStart"/> to reverse the order.
+///     </para>
+///     <para>
+///         The command text can be set by setting the <see cref="CommandView"/>'s Text property or by setting
+///         <see cref="View.Title"/>.
+///     </para>
+///     <para>
+///         The help text can be set by setting the <see cref="HelpText"/> property or by setting <see cref="View.Text"/>.
+///     </para>
+///     <para>
+///         The key text is set by setting the <see cref="Key"/> property.
+///         If the <see cref="Key"/> is <see cref="Key.Empty"/>, the <see cref="Key"/> text is not displayed.
+///     </para>
+/// </remarks>
+public class Shortcut : View
+{
+    /// <summary>
+    ///     Creates a new instance of <see cref="Shortcut"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This is a helper API that mimics the V1 API for creating StatusItems.
+    ///     </para>
+    /// </remarks>
+    /// <param name="key"></param>
+    /// <param name="commandText"></param>
+    /// <param name="action"></param>
+    /// <param name="helpText"></param>
+    public Shortcut (Key key, string commandText, Action action, string helpText = null)
+    {
+        Id = "_shortcut";
+        HighlightStyle = HighlightStyle.Pressed;
+        Highlight += Shortcut_Highlight;
+        CanFocus = true;
+        Width = GetWidthDimAuto ();
+        Height = Dim.Auto (DimAutoStyle.Content, 1);
+
+        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);
+
+        TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set
+
+        CommandView = new ()
+        {
+            Width = Dim.Auto (),
+            Height = Dim.Auto ()
+        };
+
+        HelpView.Id = "_helpView";
+        HelpView.CanFocus = false;
+        HelpView.Text = helpText;
+        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 += Shortcut_MouseClick;
+        KeyView.MouseClick += Shortcut_MouseClick;
+        LayoutStarted += OnLayoutStarted;
+        Initialized += OnInitialized;
+
+        if (key is null)
+        {
+            key = Key.Empty;
+        }
+
+        Key = key;
+        Title = commandText;
+        Action = action;
+
+        return;
+
+        void OnInitialized (object sender, EventArgs e)
+        {
+            SuperViewRendersLineCanvas = true;
+            Border.ShowTitle = false;
+
+            ShowHide ();
+
+            // Force Width to DimAuto to calculate natural width and then set it back
+            Dim savedDim = Width;
+            Width = GetWidthDimAuto ();
+            _minimumDimAutoWidth = Frame.Width;
+            Width = savedDim;
+
+            SetCommandViewDefaultLayout ();
+            SetHelpViewDefaultLayout ();
+            SetKeyViewDefaultLayout ();
+
+            SetColors ();
+        }
+
+        // Helper to set Width consistently
+        Dim GetWidthDimAuto ()
+        {
+            // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
+            return Dim.Auto (
+                             DimAutoStyle.Content,
+                             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 Orientation _orientation = Orientation.Horizontal;
+
+    /// <summary>
+    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Shortcut"/>. The default is
+    ///     <see cref="Orientation.Horizontal"/>, which is ideal for status bar, menu bar, and tool bar items If set to
+    ///     <see cref="Orientation.Vertical"/>,
+    ///     the Shortcut will be configured for vertical layout, which is ideal for menu items.
+    /// </summary>
+    public Orientation Orientation
+    {
+        get => _orientation;
+        set
+        {
+            _orientation = value;
+
+            // TODO: Determine what, if anything, is opinionated about the orientation.
+        }
+    }
+
+    private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
+
+    /// <summary>
+    ///     Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Shortcut"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         The default is <see cref="AlignmentModes.StartToEnd"/>. This means that the CommandView will be on the left,
+    ///         HelpView in the middle, and KeyView on the right.
+    ///     </para>
+    /// </remarks>
+    public AlignmentModes AlignmentModes
+    {
+        get => _alignmentModes;
+        set
+        {
+            _alignmentModes = value;
+            SetCommandViewDefaultLayout ();
+            SetHelpViewDefaultLayout ();
+            SetKeyViewDefaultLayout ();
+        }
+    }
+
+    // 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.
+    internal void ShowHide ()
+    {
+        RemoveAll ();
+
+        if (CommandView.Visible)
+        {
+            Add (CommandView);
+        }
+
+        if (HelpView.Visible && !string.IsNullOrEmpty (HelpView.Text))
+        {
+            Add (HelpView);
+        }
+
+        if (KeyView.Visible && Key != Key.Empty)
+        {
+            Add (KeyView);
+        }
+    }
+
+    // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
+    private int? _minimumDimAutoWidth;
+
+    // When layout starts, we need to adjust the layout of the HelpView and KeyView
+    private void OnLayoutStarted (object sender, LayoutEventArgs e)
+    {
+        if (Width is DimAuto widthAuto)
+        {
+            _minimumDimAutoWidth = Frame.Width;
+        }
+        else
+        {
+            if (string.IsNullOrEmpty (HelpView.Text))
+            {
+                return;
+            }
+
+            int currentWidth = Frame.Width;
+
+            // If our width is smaller than the natural width then reduce width of HelpView first.
+            // Then KeyView.
+            // Don't ever reduce CommandView (it should spill).
+            // When Horizontal, Key is first, then Help, then Command.
+            // When Vertical, Command is first, then Help, then Key.
+            // BUGBUG: This does not do what the above says.
+            // TODO: Add Unit tests for this.
+            if (currentWidth < _minimumDimAutoWidth)
+            {
+                int delta = _minimumDimAutoWidth.Value - currentWidth;
+                int maxHelpWidth = int.Max (0, HelpView.Text.GetColumns () + Margin.Thickness.Horizontal - delta);
+
+                switch (maxHelpWidth)
+                {
+                    case 0:
+                        // Hide HelpView
+                        HelpView.Visible = false;
+                        HelpView.X = 0;
+
+                        break;
+
+                    case 1:
+                        // Scrunch it by removing margins
+                        HelpView.Margin.Thickness = new (0, 0, 0, 0);
+
+                        break;
+
+                    case 2:
+                        // Scrunch just the right margin
+                        Thickness t = GetMarginThickness ();
+                        HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom);
+
+                        break;
+
+                    default:
+                        // Default margin
+                        HelpView.Margin.Thickness = GetMarginThickness ();
+
+                        break;
+                }
+
+                if (maxHelpWidth > 0)
+                {
+                    HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+
+                    // Leverage Dim.Auto's max:
+                    HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: maxHelpWidth);
+                    HelpView.Visible = true;
+                }
+            }
+            else
+            {
+                // Reset to default
+                //SetCommandViewDefaultLayout();
+                SetHelpViewDefaultLayout ();
+
+                //SetKeyViewDefaultLayout ();
+            }
+        }
+    }
+
+    private Thickness GetMarginThickness ()
+    {
+        if (Orientation == Orientation.Vertical)
+        {
+            return new (1, 0, 1, 0);
+        }
+
+        return new (1, 0, 1, 0);
+    }
+
+    private Color? _savedForeColor;
+
+    private void Shortcut_Highlight (object sender, HighlightEventArgs e)
+    {
+        if (e.HighlightStyle.HasFlag (HighlightStyle.Pressed))
+        {
+            if (!_savedForeColor.HasValue)
+            {
+                _savedForeColor = base.ColorScheme.Normal.Foreground;
+            }
+
+            var cs = new ColorScheme (base.ColorScheme)
+            {
+                Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), base.ColorScheme.Normal.Background)
+            };
+            base.ColorScheme = cs;
+        }
+
+        if (e.HighlightStyle == HighlightStyle.None && _savedForeColor.HasValue)
+        {
+            var cs = new ColorScheme (base.ColorScheme)
+            {
+                Normal = new (_savedForeColor.Value, base.ColorScheme.Normal.Background)
+            };
+            base.ColorScheme = cs;
+        }
+
+        SuperView?.SetNeedsDisplay ();
+        e.Cancel = 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;
+
+        if (!e.Handled)
+        {
+            // If the subview (likely CommandView) didn't handle the mouse click, invoke the command.
+            e.Handled = InvokeCommand (Command.Accept) == true;
+        }
+
+        if (CanFocus)
+        {
+            SetFocus ();
+        }
+    }
+
+    #region Command
+
+    private View _commandView = new ();
+
+    /// <summary>
+    ///     Gets or sets the View that displays the command text and hotkey.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         By default, the <see cref="View.Title"/> of the <see cref="CommandView"/> is displayed as the Shortcut's
+    ///         command text.
+    ///     </para>
+    ///     <para>
+    ///         By default, the CommandView is a <see cref="View"/> with <see cref="View.CanFocus"/> set to
+    ///         <see langword="false"/>.
+    ///     </para>
+    ///     <para>
+    ///         Setting the <see cref="CommandView"/> will add it to the <see cref="Shortcut"/> and remove any existing
+    ///         <see cref="CommandView"/>.
+    ///     </para>
+    /// </remarks>
+    /// <example>
+    ///     <para>
+    ///         This example illustrates how to add a <see cref="Shortcut"/> to a <see cref="StatusBar"/> that toggles the
+    ///         <see cref="Application.Force16Colors"/> property.
+    ///     </para>
+    ///     <code>
+    ///     var force16ColorsShortcut = new Shortcut
+    ///     {
+    ///         Key = Key.F6,
+    ///         KeyBindingScope = KeyBindingScope.HotKey,
+    ///         CommandView = new CheckBox { Text = "Force 16 Colors" }
+    ///     };
+    ///     var cb = force16ColorsShortcut.CommandView as CheckBox;
+    ///     cb.Checked = Application.Force16Colors;
+    /// 
+    ///     cb.Toggled += (s, e) =>
+    ///     {
+    ///         var cb = s as CheckBox;
+    ///         Application.Force16Colors = cb!.Checked == true;
+    ///         Application.Refresh();
+    ///     };
+    ///     StatusBar.Add(force16ColorsShortcut);
+    /// </code>
+    /// </example>
+
+    public View CommandView
+    {
+        get => _commandView;
+        set
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException ();
+            }
+
+            if (_commandView is { })
+            {
+                Remove (_commandView);
+                _commandView?.Dispose ();
+            }
+
+            _commandView = value;
+            _commandView.Id = "_commandView";
+
+            // The default behavior is for CommandView to not get focus. I
+            // If you want it to get focus, you need to set it.
+            _commandView.CanFocus = false;
+
+            _commandView.MouseClick += Shortcut_MouseClick;
+            _commandView.Accept += CommandViewAccept;
+
+            _commandView.HotKeyChanged += (s, e) =>
+                                          {
+                                              if (e.NewKey != Key.Empty)
+                                              {
+                                                  // Add it 
+                                                  AddKeyBindingsForHotKey (e.OldKey, e.NewKey);
+                                              }
+                                          };
+
+            _commandView.HotKeySpecifier = new ('_');
+
+            Title = _commandView.Text;
+
+            SetCommandViewDefaultLayout ();
+            SetHelpViewDefaultLayout ();
+            SetKeyViewDefaultLayout ();
+            ShowHide ();
+            UpdateKeyBinding ();
+
+            return;
+
+            void CommandViewAccept (object sender, CancelEventArgs e)
+            {
+                // When the CommandView fires its Accept event, we want to act as though the
+                // Shortcut was clicked.
+                //if (base.OnAccept () == true)
+                //{
+                //    e.Cancel = true;
+                //}
+            }
+        }
+    }
+
+    private void SetCommandViewDefaultLayout ()
+    {
+        CommandView.Margin.Thickness = GetMarginThickness ();
+        CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
+        CommandView.Y = 0; //Pos.Center ();
+    }
+
+    private void Shortcut_TitleChanged (object sender, StateEventArgs<string> e)
+    {
+        // If the Title changes, update the CommandView text.
+        // This is a helper to make it easier to set the CommandView text.
+        // CommandView is public and replaceable, but this is a convenience.
+        _commandView.Text = Title;
+    }
+
+    #endregion Command
+
+    #region Help
+
+    /// <summary>
+    ///     The subview that displays the help text for the command. Internal for unit testing.
+    /// </summary>
+    internal View HelpView { get; } = new ();
+
+    private void SetHelpViewDefaultLayout ()
+    {
+        HelpView.Margin.Thickness = GetMarginThickness ();
+        HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+        HelpView.Y = 0; //Pos.Center ();
+        HelpView.Width = Dim.Auto (DimAutoStyle.Text);
+        HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        HelpView.Visible = true;
+        HelpView.VerticalTextAlignment = Alignment.Center;
+    }
+
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
+    ///     .
+    /// </summary>
+    public override string Text
+    {
+        get => HelpView?.Text;
+        set
+        {
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
+        }
+    }
+
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut.
+    /// </summary>
+    public string HelpText
+    {
+        get => HelpView?.Text;
+        set
+        {
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
+        }
+    }
+
+    #endregion Help
+
+    #region Key
+
+    private Key _key = Key.Empty;
+
+    /// <summary>
+    ///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
+    /// </summary>
+    public Key Key
+    {
+        get => _key;
+        set
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException ();
+            }
+
+            _key = value;
+
+            UpdateKeyBinding ();
+
+            KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
+            ShowHide ();
+        }
+    }
+
+    private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
+
+    /// <summary>
+    ///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
+    /// </summary>
+    public KeyBindingScope KeyBindingScope
+    {
+        get => _keyBindingScope;
+        set
+        {
+            _keyBindingScope = value;
+
+            UpdateKeyBinding ();
+        }
+    }
+
+    /// <summary>
+    ///     Gets the subview that displays the key. Internal for unit testing.
+    /// </summary>
+
+    internal View KeyView { get; } = new ();
+
+    private int _minimumKeyTextSize;
+
+    /// <summary>
+    /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+    /// </summary>
+    public int MinimumKeyTextSize
+    {
+        get => _minimumKeyTextSize;
+        set
+        {
+            if (value == _minimumKeyTextSize)
+            {
+                //return;
+            }
+
+            _minimumKeyTextSize = value;
+            SetKeyViewDefaultLayout ();
+            CommandView.SetNeedsLayout ();
+            HelpView.SetNeedsLayout ();
+            KeyView.SetNeedsLayout ();
+            SetSubViewNeedsDisplay ();
+        }
+    }
+
+    private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
+
+    private void SetKeyViewDefaultLayout ()
+    {
+        KeyView.Margin.Thickness = GetMarginThickness ();
+        KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
+        KeyView.Y = 0; //Pos.Center ();
+        KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
+        KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        KeyView.Visible = true;
+
+        // Right align the text in the keyview
+        KeyView.TextAlignment = Alignment.End;
+        KeyView.VerticalTextAlignment = Alignment.Center;
+        KeyView.KeyBindings.Clear ();
+    }
+
+    private void UpdateKeyBinding ()
+    {
+        if (Key != null)
+        {
+            // Disable the command view key bindings
+            CommandView.KeyBindings.Remove (Key);
+            CommandView.KeyBindings.Remove (CommandView.HotKey);
+            KeyBindings.Remove (Key);
+            KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+            //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
+        }
+    }
+
+    #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 new bool? OnAccept (CommandContext ctx)
+    {
+        var cancel = false;
+
+        switch (ctx.KeyBinding?.Scope)
+        {
+            case KeyBindingScope.Application:
+                cancel = base.OnAccept () == true;
+
+                break;
+
+            case KeyBindingScope.Focused:
+                // TODO: Figure this out
+                cancel = base.OnAccept () == true;
+
+                break;
+
+            case KeyBindingScope.HotKey:
+                cancel = base.OnAccept () == true;
+
+                if (CanFocus)
+                {
+                    SetFocus ();
+                }
+
+                break;
+
+            default:
+                cancel = base.OnAccept () == true;
+                break;
+        }
+
+        CommandView.InvokeCommand (Command.Accept);
+
+        if (!cancel)
+        {
+            Action?.Invoke ();
+        }
+
+        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
+
+    private bool? OnSelect (CommandContext ctx)
+    {
+        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+        {
+           return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        }
+        return false;
+
+    }
+
+
+    #region Focus
+
+    /// <inheritdoc/>
+    public override ColorScheme ColorScheme
+    {
+        get => base.ColorScheme;
+        set
+        {
+            base.ColorScheme = value;
+            SetColors ();
+        }
+    }
+
+    /// <summary>
+    /// </summary>
+    internal void SetColors ()
+    {
+        // Border should match superview.
+        Border.ColorScheme = SuperView?.ColorScheme;
+
+        if (HasFocus)
+        {
+            // When we have focus, we invert the colors
+            base.ColorScheme = new (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.Focus,
+                HotNormal = base.ColorScheme.HotFocus,
+                HotFocus = base.ColorScheme.HotNormal,
+                Focus = base.ColorScheme.Normal
+            };
+        }
+        else
+        {
+            base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+        }
+
+        // Set KeyView's colors to show "hot"
+        if (IsInitialized && base.ColorScheme is { })
+        {
+            var cs = new ColorScheme (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.HotNormal,
+                HotNormal = base.ColorScheme.Normal
+            };
+            KeyView.ColorScheme = cs;
+        }
+    }
+
+    View _lastFocusedView;
+    /// <inheritdoc/>
+    public override bool OnEnter (View view)
+    {
+        SetColors ();
+        _lastFocusedView = view;
+
+        return base.OnEnter (view);
+    }
+
+    /// <inheritdoc/>
+    public override bool OnLeave (View view)
+    {
+        SetColors ();
+        _lastFocusedView = this;
+
+        return base.OnLeave (view);
+    }
+
+    #endregion Focus
+
+    /// <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);
+    }
+}

+ 17 - 6
Terminal.Gui/Views/Slider.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using System.Transactions;
+
+namespace Terminal.Gui;
 
 /// <summary>Slider control.</summary>
 public class Slider : Slider<object>
@@ -1377,7 +1379,8 @@ public class Slider<T> : View
 
             SetNeedsDisplay ();
 
-            return true;
+            mouseEvent.Handled = true;
+            return OnMouseClick (new (mouseEvent));
         }
 
         return false;
@@ -1417,7 +1420,8 @@ public class Slider<T> : View
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.RightExtend, () => ExtendPlus ());
         AddCommand (Command.LeftExtend, () => ExtendMinus ());
-        AddCommand (Command.Accept, () => Set ());
+        AddCommand (Command.Select, () => Select ());
+        AddCommand (Command.Accept, () => Accept ());
 
         SetKeyBindings ();
     }
@@ -1453,7 +1457,7 @@ public class Slider<T> : View
         KeyBindings.Add (Key.Home, Command.LeftHome);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Add (Key.Enter, Command.Accept);
-        KeyBindings.Add (Key.Space, Command.Accept);
+        KeyBindings.Add (Key.Space, Command.Select);
     }
 
     private Dictionary<int, SliderOption<T>> GetSetOptionDictionary () { return _setOptions.ToDictionary (e => e, e => _options [e]); }
@@ -1732,13 +1736,20 @@ public class Slider<T> : View
         return true;
     }
 
-    internal bool Set ()
+    internal bool Select ()
     {
-        SetFocusedOption ();
+        SetFocusedOption();
 
         return true;
     }
 
+    internal bool Accept ()
+    {
+        SetFocusedOption ();
+
+        return OnAccept () == true;
+    }
+
     internal bool MovePlus ()
     {
         bool cancelled = OnOptionFocused (

+ 38 - 204
Terminal.Gui/Views/StatusBar.cs

@@ -1,243 +1,77 @@
+using System;
+using System.Reflection;
+
 namespace Terminal.Gui;
 
 /// <summary>
 ///     A status bar is a <see cref="View"/> that snaps to the bottom of a <see cref="Toplevel"/> displaying set of
-///     <see cref="StatusItem"/>s. The <see cref="StatusBar"/> should be context sensitive. This means, if the main menu
+///     <see cref="Shortcut"/>s. The <see cref="StatusBar"/> should be context sensitive. This means, if the main menu
 ///     and an open text editor are visible, the items probably shown will be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog
 ///     to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. So for each context must be a
 ///     new instance of a status bar.
 /// </summary>
-public class StatusBar : View
+public class StatusBar : Bar
 {
-    private static Rune _shortcutDelimiter = (Rune)'=';
-
-    private StatusItem [] _items = [];
-
-    /// <summary>Initializes a new instance of the <see cref="StatusBar"/> class.</summary>
-    public StatusBar () : this (new StatusItem [] { }) { }
+    /// <inheritdoc/>
+    public StatusBar () : this ([]) { }
 
-    /// <summary>
-    ///     Initializes a new instance of the <see cref="StatusBar"/> class with the specified set of
-    ///     <see cref="StatusItem"/> s. The <see cref="StatusBar"/> will be drawn on the lowest line of the terminal or
-    ///     <see cref="View.SuperView"/> (if not null).
-    /// </summary>
-    /// <param name="items">A list of status bar items.</param>
-    public StatusBar (StatusItem [] items)
+    /// <inheritdoc/>
+    public StatusBar (IEnumerable<Shortcut> shortcuts) : base (shortcuts)
     {
-        if (items is { })
-        {
-            Items = items;
-        }
-
-        CanFocus = false;
-        ColorScheme = Colors.ColorSchemes ["Menu"];
-        X = 0;
+        Orientation = Orientation.Horizontal;
         Y = Pos.AnchorEnd ();
         Width = Dim.Fill ();
-        Height = 1; // BUGBUG: Views should avoid setting Height as doing so implies Frame.Size == GetContentSize ().
-
-        AddCommand (Command.Accept, ctx => InvokeItem ((StatusItem)ctx.KeyBinding?.Context));
-    }
-
-    /// <summary>The items that compose the <see cref="StatusBar"/></summary>
-    public StatusItem [] Items
-    {
-        get => _items;
-        set
-        {
-            foreach (StatusItem item in _items)
-            {
-                KeyBindings.Remove (item.Shortcut);
-            }
-
-            _items = value;
-
-            foreach (StatusItem item in _items.Where (i => i.Shortcut != Key.Empty))
-            {
-                KeyBinding keyBinding = new (new [] { Command.Accept }, KeyBindingScope.HotKey, item);
-                KeyBindings.Add (item.Shortcut, keyBinding);
-            }
-        }
-    }
-
-    /// <summary>Gets or sets shortcut delimiter separator. The default is "-".</summary>
-    public static Rune ShortcutDelimiter
-    {
-        get => _shortcutDelimiter;
-        set
-        {
-            if (_shortcutDelimiter != value)
-            {
-                _shortcutDelimiter = value == default (Rune) ? (Rune)'=' : value;
-            }
-        }
-    }
-
-    /// <summary>Inserts a <see cref="StatusItem"/> in the specified index of <see cref="Items"/>.</summary>
-    /// <param name="index">The zero-based index at which item should be inserted.</param>
-    /// <param name="item">The item to insert.</param>
-    public void AddItemAt (int index, StatusItem item)
-    {
-        List<StatusItem> itemsList = new (Items);
-        itemsList.Insert (index, item);
-        Items = itemsList.ToArray ();
-        SetNeedsDisplay ();
-    }
-
-    ///<inheritdoc/>
-    protected internal override bool OnMouseEvent (MouseEvent me)
-    {
-        if (me.Flags != MouseFlags.Button1Clicked)
-        {
-            return false;
-        }
-
-        var pos = 1;
-
-        for (var i = 0; i < Items.Length; i++)
-        {
-            if (me.Position.X >= pos && me.Position.X < pos + GetItemTitleLength (Items [i].Title))
-            {
-                StatusItem item = Items [i];
-
-                if (item.IsEnabled ())
-                {
-                    Run (item.Action);
-                }
-
-                break;
-            }
-
-            pos += GetItemTitleLength (Items [i].Title) + 3;
-        }
+        Height = Dim.Auto (DimAutoStyle.Content, 1);
+        BorderStyle = LineStyle.Dashed;
+        ColorScheme = Colors.ColorSchemes ["Menu"];
 
-        return true;
+        LayoutStarted += StatusBar_LayoutStarted;
     }
 
-    ///<inheritdoc/>
-    public override void OnDrawContent (Rectangle viewport)
+    // StatusBar arranges the items horizontally.
+    // The first item has no left border, the last item has no right border.
+    // The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart).
+    private void StatusBar_LayoutStarted (object sender, LayoutEventArgs e)
     {
-        Move (0, 0);
-        Driver.SetAttribute (GetNormalColor ());
-
-        for (var i = 0; i < Frame.Width; i++)
+        for (int index = 0; index < Subviews.Count; index++)
         {
-            Driver.AddRune ((Rune)' ');
-        }
-
-        Move (1, 0);
-        Attribute scheme = GetNormalColor ();
-        Driver.SetAttribute (scheme);
+            View barItem = Subviews [index];
 
-        for (var i = 0; i < Items.Length; i++)
-        {
-            string title = Items [i].Title;
-            Driver.SetAttribute (DetermineColorSchemeFor (Items [i]));
+            barItem.BorderStyle = BorderStyle;
 
-            for (var n = 0; n < Items [i].Title.GetRuneCount (); n++)
+            if (index == Subviews.Count - 1)
             {
-                if (title [n] == '~')
-                {
-                    if (Items [i].IsEnabled ())
-                    {
-                        scheme = ToggleScheme (scheme);
-                    }
-
-                    continue;
-                }
-
-                Driver.AddRune ((Rune)title [n]);
+                barItem.Border.Thickness = new Thickness (0, 0, 0, 0);
             }
-
-            if (i + 1 < Items.Length)
+            else
             {
-                Driver.AddRune ((Rune)' ');
-                Driver.AddRune (Glyphs.VLine);
-                Driver.AddRune ((Rune)' ');
+                barItem.Border.Thickness = new Thickness (0, 0, 1, 0);
             }
-        }
-    }
-
-    /// <summary>Removes a <see cref="StatusItem"/> at specified index of <see cref="Items"/>.</summary>
-    /// <param name="index">The zero-based index of the item to remove.</param>
-    /// <returns>The <see cref="StatusItem"/> removed.</returns>
-    public StatusItem RemoveItem (int index)
-    {
-        List<StatusItem> itemsList = new (Items);
-        StatusItem item = itemsList [index];
-        itemsList.RemoveAt (index);
-        Items = itemsList.ToArray ();
-        SetNeedsDisplay ();
 
-        return item;
-    }
-
-    private Attribute DetermineColorSchemeFor (StatusItem item)
-    {
-        if (item is { })
-        {
-            if (item.IsEnabled ())
+            if (barItem is Shortcut shortcut)
             {
-                return GetNormalColor ();
+                shortcut.Orientation = Orientation.Horizontal;
             }
-
-            return ColorScheme.Disabled;
         }
-
-        return GetNormalColor ();
     }
 
-    private int GetItemTitleLength (string title)
+    /// <inheritdoc/>
+    public override View Add (View view)
     {
-        var len = 0;
-
-        foreach (char ch in title)
-        {
-            if (ch == '~')
-            {
-                continue;
-            }
+        // Call base first, because otherwise it resets CanFocus to true
+        base.Add (view);
 
-            len++;
-        }
-
-        return len;
-    }
+        view.CanFocus = false;
 
-    private bool? InvokeItem (StatusItem itemToInvoke)
-    {
-        if (itemToInvoke is { Action: { } })
+        if (view is Shortcut shortcut)
         {
-            itemToInvoke.Action.Invoke ();
-
-            return true;
-        }
-
-        return false;
-    }
+            shortcut.KeyBindingScope = KeyBindingScope.Application;
 
-    private void Run (Action action)
-    {
-        if (action is null)
-        {
-            return;
+            // TODO: not happy about using AlignmentModes for this. Too implied.
+            // TODO: instead, add a property (a style enum?) to Shortcut to control this
+            shortcut.AlignmentModes = AlignmentModes.EndToStart;
         }
 
-        Application.MainLoop.AddIdle (
-                                      () =>
-                                      {
-                                          action ();
-
-                                          return false;
-                                      }
-                                     );
-    }
-
-    private Attribute ToggleScheme (Attribute scheme)
-    {
-        Attribute result = scheme == ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
-        Driver.SetAttribute (result);
-
-        return result;
+        return view;
     }
 }

+ 0 - 59
Terminal.Gui/Views/StatusItem.cs

@@ -1,59 +0,0 @@
-namespace Terminal.Gui;
-
-/// <summary>
-///     <see cref="StatusItem"/> objects are contained by <see cref="StatusBar"/> <see cref="View"/>s. Each
-///     <see cref="StatusItem"/> has a title, a shortcut (hotkey), and an <see cref="Command"/> that will be invoked when
-///     the <see cref="StatusItem.Shortcut"/> is pressed. The <see cref="StatusItem.Shortcut"/> will be a global hotkey for
-///     the application in the current context of the screen. The color of the <see cref="StatusItem.Title"/> will be
-///     changed after each ~. A <see cref="StatusItem.Title"/> set to `~F1~ Help` will render as *F1* using
-///     <see cref="ColorScheme.HotNormal"/> and *Help* as <see cref="ColorScheme.HotNormal"/>.
-/// </summary>
-public class StatusItem
-{
-    /// <summary>Initializes a new <see cref="StatusItem"/>.</summary>
-    /// <param name="shortcut">Shortcut to activate the <see cref="StatusItem"/>.</param>
-    /// <param name="title">Title for the <see cref="StatusItem"/>.</param>
-    /// <param name="action">Action to invoke when the <see cref="StatusItem"/> is activated.</param>
-    /// <param name="canExecute">Function to determine if the action can currently be executed.</param>
-    public StatusItem (Key shortcut, string title, Action action, Func<bool> canExecute = null)
-    {
-        Title = title ?? "";
-        Shortcut = shortcut;
-        Action = action;
-        CanExecute = canExecute;
-    }
-
-    /// <summary>Gets or sets the action to be invoked when the <see cref="StatusItem"/> is triggered</summary>
-    /// <value>Action to invoke.</value>
-    public Action Action { get; set; }
-
-    /// <summary>
-    ///     Gets or sets the action to be invoked to determine if the <see cref="StatusItem"/> can be triggered. If
-    ///     <see cref="CanExecute"/> returns <see langword="true"/> the status item will be enabled. Otherwise, it will be
-    ///     disabled.
-    /// </summary>
-    /// <value>Function to determine if the action is can be executed or not.</value>
-    public Func<bool> CanExecute { get; set; }
-
-    /// <summary>Gets or sets arbitrary data for the status item.</summary>
-    /// <remarks>This property is not used internally.</remarks>
-    public object Data { get; set; }
-
-    /// <summary>Gets the global shortcut to invoke the action on the menu.</summary>
-    public Key Shortcut { get; set; }
-
-    /// <summary>Gets or sets the title.</summary>
-    /// <value>The title.</value>
-    /// <remarks>
-    ///     The colour of the <see cref="StatusItem.Title"/> will be changed after each ~. A
-    ///     <see cref="StatusItem.Title"/> set to `~F1~ Help` will render as *F1* using <see cref="ColorScheme.HotNormal"/> and
-    ///     *Help* as <see cref="ColorScheme.HotNormal"/>.
-    /// </remarks>
-    public string Title { get; set; }
-
-    /// <summary>
-    ///     Returns <see langword="true"/> if the status item is enabled. This method is a wrapper around
-    ///     <see cref="CanExecute"/>.
-    /// </summary>
-    public bool IsEnabled () { return CanExecute?.Invoke () ?? true; }
-}

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

@@ -1034,7 +1034,7 @@ public class TextField : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key a)
+    public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope)
     {
         // Give autocomplete first opportunity to respond to key presses
         if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a))
@@ -1042,7 +1042,7 @@ public class TextField : View
             return true;
         }
 
-        return base.OnInvokingKeyBindings (a);
+        return base.OnInvokingKeyBindings (a, scope);
     }
 
     /// <inheritdoc/>

+ 2 - 3
Terminal.Gui/Views/TextView.cs

@@ -1961,7 +1961,6 @@ public class TextView : View
     private readonly HistoryText _historyText = new ();
     private bool _allowsReturn = true;
     private bool _allowsTab = true;
-    private int _bottomOffset, _rightOffset;
     private bool _clickWithSelecting;
 
     // The column we are tracking, or -1 if we are not tracking any column
@@ -3635,7 +3634,7 @@ public class TextView : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key a)
+    public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope)
     {
         if (!a.IsValid)
         {
@@ -3648,7 +3647,7 @@ public class TextView : View
             return true;
         }
 
-        return base.OnInvokingKeyBindings (a);
+        return base.OnInvokingKeyBindings (a, scope);
     }
 
     /// <inheritdoc/>

+ 8 - 8
Terminal.Gui/Views/Toplevel.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui;
 /// <remarks>
 ///     <para>
 ///         Toplevels can run as modal (popup) views, started by calling
-///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. They return control to the caller when
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. They return control to the caller when
 ///         <see cref="Application.RequestStop(Toplevel)"/> has been called (which sets the <see cref="Toplevel.Running"/>
 ///         property to <c>false</c>).
 ///     </para>
@@ -15,7 +15,7 @@ namespace Terminal.Gui;
 ///         A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init"/>.
 ///         The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created
 ///         and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and call
-///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
 ///     </para>
 /// </remarks>
 public partial class Toplevel : View
@@ -106,7 +106,7 @@ public partial class Toplevel : View
                    );
 
         // Default keybindings for this view
-        KeyBindings.Add (Application.QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
+        KeyBindings.Add (Application.QuitKey, Command.QuitToplevel);
 
         KeyBindings.Add (Key.CursorRight, Command.NextView);
         KeyBindings.Add (Key.CursorDown, Command.NextView);
@@ -186,11 +186,11 @@ public partial class Toplevel : View
     public event EventHandler<ToplevelEventArgs> Activate;
 
     /// <inheritdoc/>
-    public override void Add (View view)
+    public override View Add (View view)
     {
         CanFocus = true;
         AddMenuStatusBar (view);
-        base.Add (view);
+        return base.Add (view);
     }
 
     /// <summary>
@@ -445,20 +445,20 @@ public partial class Toplevel : View
     ///     perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set. changes.
     ///     <para>
     ///         A Ready event handler is a good place to finalize initialization after calling
-    ///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> on this <see cref="Toplevel"/>.
+    ///         <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> on this <see cref="Toplevel"/>.
     ///     </para>
     /// </summary>
     public event EventHandler Ready;
 
     /// <inheritdoc/>
-    public override void Remove (View view)
+    public override View Remove (View view)
     {
         if (this is Toplevel { MenuBar: { } })
         {
             RemoveMenuStatusBar (view);
         }
 
-        base.Remove (view);
+        return base.Remove (view);
     }
 
     /// <inheritdoc/>

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

@@ -125,7 +125,7 @@ public class Wizard : Dialog
     ///             <description>Add the Wizard to a containing view with <see cref="View.Add(View)"/>.</description>
     ///         </item>
     ///     </list>
-    ///     If a non-Modal Wizard is added to the application after <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> has
+    ///     If a non-Modal Wizard is added to the application after <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> has
     ///     been called the first step must be explicitly set by setting <see cref="CurrentStep"/> to
     ///     <see cref="GetNextStep()"/>:
     ///     <code>

+ 7 - 3
Terminal.Gui/Views/Wizard/WizardStep.cs

@@ -126,7 +126,7 @@ public class WizardStep : FrameView
 
     /// <summary>Add the specified <see cref="View"/> to the <see cref="WizardStep"/>.</summary>
     /// <param name="view"><see cref="View"/> to add to this container</param>
-    public override void Add (View view)
+    public override View Add (View view)
     {
         _contentView.Add (view);
 
@@ -136,15 +136,17 @@ public class WizardStep : FrameView
         }
 
         ShowHide ();
+
+        return view;
     }
 
     /// <summary>Removes a <see cref="View"/> from <see cref="WizardStep"/>.</summary>
     /// <remarks></remarks>
-    public override void Remove (View view)
+    public override View Remove (View view)
     {
         if (view is null)
         {
-            return;
+            return view;
         }
 
         SetNeedsDisplay ();
@@ -165,6 +167,8 @@ public class WizardStep : FrameView
         }
 
         ShowHide ();
+
+        return view;
     }
 
     /// <summary>Removes all <see cref="View"/>s from the <see cref="WizardStep"/>.</summary>

+ 2 - 0
UICatalog/Scenario.cs

@@ -188,6 +188,8 @@ public class Scenario : IDisposable
     {
         // Must explicitly call Application.Shutdown method to shutdown.
         Application.Run (Top);
+        Top.Dispose ();
+        Application.Shutdown ();
     }
 
     /// <summary>Override this to implement the <see cref="Scenario"/> setup logic (create controls, etc...).</summary>

+ 2 - 0
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -59,6 +59,8 @@ public class ASCIICustomButtonTest : Scenario
         Application.Run (top);
         top.Dispose ();
 
+        Application.Shutdown ();
+
         return;
 
         void ChangeWindowSize ()

+ 11 - 13
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -88,25 +88,23 @@ public class BackgroundWorkerCollection : Scenario
             ;
             _menu.MenuOpening += Menu_MenuOpening;
             Add (_menu);
-
             var statusBar = new StatusBar (
                                            new []
                                            {
-                                               new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit ()),
-                                               new StatusItem (
-                                                               KeyCode.CtrlMask | KeyCode.R,
-                                                               "~^R~ Run Worker",
-                                                               () => _workerApp.RunWorker ()
-                                                              ),
-                                               new StatusItem (
-                                                               KeyCode.CtrlMask | KeyCode.C,
-                                                               "~^C~ Cancel Worker",
-                                                               () => _workerApp.CancelWorker ()
-                                                              )
+                                               new Shortcut (Application.QuitKey, $"Quit", Quit),
+                                               new Shortcut (
+                                                             Key.R.WithCtrl,
+                                                             "Run Worker",
+                                                             () => _workerApp.RunWorker ()
+                                                            ),
+                                               new Shortcut (
+                                                             Key.C.WithCtrl,
+                                                             "Cancel Worker",
+                                                             () => _workerApp.CancelWorker ()
+                                                            )
                                            }
                                           );
             Add (statusBar);
-
             Ready += OverlappedMain_Ready;
             Activate += OverlappedMain_Activate;
             Deactivate += OverlappedMain_Deactivate;

+ 479 - 0
UICatalog/Scenarios/Bars.cs

@@ -0,0 +1,479 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Bars", "Illustrates Bar views (e.g. StatusBar)")]
+[ScenarioCategory ("Controls")]
+public class Bars : Scenario
+{
+    public override void Main ()
+    {
+        Application.Init ();
+        Toplevel app = new ();
+
+        app.Loaded += App_Loaded;
+
+        Application.Run (app);
+        app.Dispose ();
+        Application.Shutdown ();
+    }
+
+
+    // Setting everything up in Loaded handler because we change the
+    // QuitKey and it only sticks if changed after init
+    private void App_Loaded (object sender, EventArgs e)
+    {
+        Application.Top.Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}";
+
+        ObservableCollection<string> eventSource = new ();
+        ListView eventLog = new ListView ()
+        {
+            Title = "Event Log",
+            X = Pos.AnchorEnd (),
+            Width = Dim.Auto (),
+            Height = Dim.Fill (), // Make room for some wide things
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            Source = new ListWrapper<string> (eventSource)
+        };
+        eventLog.Border.Thickness = new (0, 1, 0, 0);
+        Application.Top.Add (eventLog);
+
+        FrameView menuBarLikeExamples = new ()
+        {
+            Title = "MenuBar-Like Examples",
+            X = 0,
+            Y = 0,
+            Width = Dim.Fill () - Dim.Width (eventLog),
+            Height = Dim.Percent(33),
+        };
+        Application.Top.Add (menuBarLikeExamples);
+
+        Label label = new Label ()
+        {
+            Title = "      Bar:",
+            X = 0,
+            Y = 0,
+        };
+        menuBarLikeExamples.Add (label);
+
+        Bar bar = new Bar
+        {
+            Id = "menuBar-like",
+            X = Pos.Right (label),
+            Y = Pos.Top (label),
+            Width = Dim.Fill (),
+        };
+
+        ConfigMenuBar (bar);
+        menuBarLikeExamples.Add (bar);
+
+        label = new Label ()
+        {
+            Title = "  MenuBar:",
+            X = 0,
+            Y = Pos.Bottom (bar) + 1
+        };
+        menuBarLikeExamples.Add (label);
+
+        bar = new MenuBarv2
+        {
+            Id = "menuBar",
+            X = Pos.Right (label),
+            Y = Pos.Top (label),
+        };
+
+        ConfigMenuBar (bar);
+        menuBarLikeExamples.Add (bar);
+
+        FrameView menuLikeExamples = new ()
+        {
+            Title = "Menu-Like Examples",
+            X = 0,
+            Y = Pos.Center (),
+            Width = Dim.Fill () - Dim.Width (eventLog),
+            Height = Dim.Percent (33),
+        };
+        Application.Top.Add (menuLikeExamples);
+
+        label = new Label ()
+        {
+            Title = "Bar:",
+            X = 0,
+            Y = 0,
+        };
+        menuLikeExamples.Add (label);
+
+        bar = new Bar
+        {
+            Id = "menu-like",
+            X = 0,
+            Y = Pos.Bottom(label),
+            //Width = Dim.Percent (40),
+            Orientation = Orientation.Vertical,
+        };
+            ConfigureMenu (bar);
+
+        menuLikeExamples.Add (bar);
+
+        label = new Label ()
+        {
+            Title = "Menu:",
+            X = Pos.Right(bar) + 1,
+            Y = Pos.Top (label),
+        };
+        menuLikeExamples.Add (label);
+
+        bar = new Menuv2
+        {
+            Id = "menu",
+            X = Pos.Left (label),
+            Y = Pos.Bottom (label),
+        };
+        ConfigureMenu (bar);
+
+        menuLikeExamples.Add (bar);
+
+        FrameView statusBarLikeExamples = new ()
+        {
+            Title = "StatusBar-Like Examples",
+            X = 0,
+            Y = Pos.AnchorEnd (),
+            Width = Dim.Width (menuLikeExamples),
+            Height = Dim.Percent (33),
+        };
+        Application.Top.Add (statusBarLikeExamples);
+
+        label = new Label ()
+        {
+            Title = "      Bar:",
+            X = 0,
+            Y = 0,
+        };
+        statusBarLikeExamples.Add (label);
+        bar = new Bar
+        {
+            Id = "statusBar-like",
+            X = Pos.Right (label),
+            Y = Pos.Top (label),
+            Width = Dim.Fill (),
+            Orientation = Orientation.Horizontal,
+        };
+        ConfigStatusBar (bar);
+        statusBarLikeExamples.Add (bar);
+
+        label = new Label ()
+        {
+            Title = "StatusBar:",
+            X = 0,
+            Y = Pos.Bottom (bar) + 1,
+        };
+        statusBarLikeExamples.Add (label);
+        bar = new StatusBar ()
+        {
+            Id = "statusBar",
+            X = Pos.Right (label),
+            Y = Pos.Top (label),
+            Width = Dim.Fill (),
+        };
+        ConfigStatusBar (bar);
+        statusBarLikeExamples.Add (bar);
+
+        foreach (FrameView frameView in Application.Top.Subviews.Where (f => f is FrameView)!)
+        {
+            foreach (Bar barView in frameView.Subviews.Where (b => b is Bar)!)
+            {
+                foreach (Shortcut sh in barView.Subviews.Where (s => s is Shortcut)!)
+                {
+                    sh.Accept += (o, args) =>
+                                 {
+                                     eventSource.Add ($"Accept: {sh!.SuperView.Id} {sh!.CommandView.Text}");
+                                     eventLog.MoveDown ();
+                                 };
+                }
+            }
+        }
+    }
+
+
+    //private void SetupContentMenu ()
+    //{
+    //    Application.Top.Add (new Label { Text = "Right Click for Context Menu", X = Pos.Center (), Y = 4 });
+    //    Application.Top.MouseClick += ShowContextMenu;
+    //}
+
+    //private void ShowContextMenu (object s, MouseEventEventArgs e)
+    //{
+    //    if (e.MouseEvent.Flags != MouseFlags.Button3Clicked)
+    //    {
+    //        return;
+    //    }
+
+    //    var contextMenu = new Bar
+    //    {
+    //        Id = "contextMenu",
+    //        X = e.MouseEvent.Position.X,
+    //        Y = e.MouseEvent.Position.Y,
+    //        Width = Dim.Auto (DimAutoStyle.Content),
+    //        Height = Dim.Auto (DimAutoStyle.Content),
+    //        Orientation = Orientation.Vertical,
+    //        StatusBarStyle = false,
+    //        BorderStyle = LineStyle.Rounded,
+    //        Modal = true,
+    //    };
+
+    //    var newMenu = new Shortcut
+    //    {
+    //        Title = "_New...",
+    //        Text = "Create a new file",
+    //        Key = Key.N.WithCtrl,
+    //        CanFocus = true
+    //    };
+
+    //    newMenu.Accept += (s, e) =>
+    //                      {
+    //                          contextMenu.RequestStop ();
+
+    //                          Application.AddTimeout (
+    //                                                  new TimeSpan (0),
+    //                                                  () =>
+    //                                                  {
+    //                                                      MessageBox.Query ("File", "New");
+
+    //                                                      return false;
+    //                                                  });
+    //                      };
+
+    //    var open = new Shortcut
+    //    {
+    //        Title = "_Open...",
+    //        Text = "Show the File Open Dialog",
+    //        Key = Key.O.WithCtrl,
+    //        CanFocus = true
+    //    };
+
+    //    open.Accept += (s, e) =>
+    //                   {
+    //                       contextMenu.RequestStop ();
+
+    //                       Application.AddTimeout (
+    //                                               new TimeSpan (0),
+    //                                               () =>
+    //                                               {
+    //                                                   MessageBox.Query ("File", "Open");
+
+    //                                                   return false;
+    //                                               });
+    //                   };
+
+    //    var save = new Shortcut
+    //    {
+    //        Title = "_Save...",
+    //        Text = "Save",
+    //        Key = Key.S.WithCtrl,
+    //        CanFocus = true
+    //    };
+
+    //    save.Accept += (s, e) =>
+    //                   {
+    //                       contextMenu.RequestStop ();
+
+    //                       Application.AddTimeout (
+    //                                               new TimeSpan (0),
+    //                                               () =>
+    //                                               {
+    //                                                   MessageBox.Query ("File", "Save");
+
+    //                                                   return false;
+    //                                               });
+    //                   };
+
+    //    var saveAs = new Shortcut
+    //    {
+    //        Title = "Save _As...",
+    //        Text = "Save As",
+    //        Key = Key.A.WithCtrl,
+    //        CanFocus = true
+    //    };
+
+    //    saveAs.Accept += (s, e) =>
+    //                     {
+    //                         contextMenu.RequestStop ();
+
+    //                         Application.AddTimeout (
+    //                                                 new TimeSpan (0),
+    //                                                 () =>
+    //                                                 {
+    //                                                     MessageBox.Query ("File", "Save As");
+
+    //                                                     return false;
+    //                                                 });
+    //                     };
+
+    //    contextMenu.Add (newMenu, open, save, saveAs);
+
+    //    contextMenu.KeyBindings.Add (Key.Esc, Command.QuitToplevel);
+
+    //    contextMenu.Initialized += Menu_Initialized;
+
+    //    void Application_MouseEvent (object sender, MouseEvent e)
+    //    {
+    //        // If user clicks outside of the menuWindow, close it
+    //        if (!contextMenu.Frame.Contains (e.Position.X, e.Position.Y))
+    //        {
+    //            if (e.Flags is (MouseFlags.Button1Clicked or MouseFlags.Button3Clicked))
+    //            {
+    //                contextMenu.RequestStop ();
+    //            }
+    //        }
+    //    }
+
+    //    Application.MouseEvent += Application_MouseEvent;
+
+    //    Application.Run (contextMenu);
+    //    contextMenu.Dispose ();
+
+    //    Application.MouseEvent -= Application_MouseEvent;
+    //}
+
+    private void Menu_Initialized (object sender, EventArgs e)
+    {
+        // BUGBUG: this should not be needed    
+
+        ((View)(sender)).LayoutSubviews ();
+    }
+
+    private void ConfigMenuBar (Bar bar)
+    {
+        var fileMenuBarItem = new Shortcut
+        {
+            Title = "_File",
+            HelpText = "File Menu",
+            Key = Key.D0.WithAlt,
+        };
+
+        var editMenuBarItem = new Shortcut
+        {
+            Title = "_Edit",
+            HelpText = "Edit Menu",
+            Key = Key.D1.WithAlt
+        };
+
+        var helpMenuBarItem = new Shortcut
+        {
+            Title = "_Help",
+            HelpText = "Halp Menu",
+            Key = Key.D2.WithAlt
+        };
+
+        bar.Add (fileMenuBarItem, editMenuBarItem, helpMenuBarItem);
+    }
+
+    private void ConfigureMenu (Bar bar)
+    {
+
+        var shortcut1 = new Shortcut
+        {
+            Title = "Z_igzag",
+            Key = Key.I.WithCtrl,
+            Text = "Gonna zig zag",
+        };
+
+        var shortcut2 = new Shortcut
+        {
+            Title = "Za_G",
+            Text = "Gonna zag",
+            Key = Key.G.WithAlt,
+        };
+
+        var line = new Line ()
+        {
+            BorderStyle = LineStyle.Dotted,
+            Orientation = Orientation.Horizontal,
+            CanFocus = false,
+        };
+
+        var shortcut3 = new Shortcut
+        {
+            Title = "_Three",
+            Text = "The 3rd item",
+            Key = Key.D3.WithAlt,
+        };
+
+        bar.Add (shortcut1, shortcut2, line, shortcut3);
+    }
+
+    private void ConfigStatusBar (Bar bar)
+    {
+        var shortcut = new Shortcut
+        {
+            Text = "Quit",
+            Title = "Q_uit",
+            Key = Key.Z.WithCtrl,
+        };
+
+        bar.Add (shortcut);
+
+        shortcut = new Shortcut
+        {
+            Text = "Help Text",
+            Title = "Help",
+            Key = Key.F1,
+        };
+
+        bar.Add (shortcut);
+
+        shortcut = new Shortcut
+        {
+            Title = "_Show/Hide",
+            Key = Key.F10,
+            CommandView = new CheckBox
+            {
+                CanFocus = false,
+                Text = "_Show/Hide"
+            },
+        };
+
+        bar.Add (shortcut);
+
+        var button1 = new Button
+        {
+            Text = "I'll Hide",
+            // Visible = false
+        };
+        button1.Accept += Button_Clicked;
+        bar.Add (button1);
+
+        shortcut.Accept += (s, e) =>
+                                                    {
+                                                        button1.Visible = !button1.Visible;
+                                                        button1.Enabled = button1.Visible;
+                                                        e.Cancel = false;
+                                                    };
+
+        bar.Add (new Label
+        {
+            HotKeySpecifier = new Rune ('_'),
+            Text = "Fo_cusLabel",
+            CanFocus = true
+        });
+
+        var button2 = new Button
+        {
+            Text = "Or me!",
+        };
+        button2.Accept += (s, e) => Application.RequestStop ();
+
+        bar.Add (button2);
+
+        return;
+
+        void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+
+    }
+
+}

+ 1 - 0
UICatalog/Scenarios/BasicColors.cs

@@ -111,5 +111,6 @@ public class BasicColors : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 1 - 0
UICatalog/Scenarios/Buttons.cs

@@ -396,6 +396,7 @@ public class Buttons : Scenario
         main.Ready += (s, e) => radioGroup.Refresh ();
         Application.Run (main);
         main.Dispose ();
+        Application.Shutdown ();
     }
 
     /// <summary>

+ 1 - 0
UICatalog/Scenarios/CharacterMap.cs

@@ -180,6 +180,7 @@ public class CharacterMap : Scenario
 
         Application.Run (top);
         top.Dispose ();
+        Application.Shutdown ();
     }
 
     private void _categoryList_Initialized (object sender, EventArgs e) { _charMap.Width = Dim.Fill () - _categoryList.Width; }

+ 1 - 0
UICatalog/Scenarios/ColorPicker.cs

@@ -86,6 +86,7 @@ public class ColorPickers : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 
     /// <summary>Fired when background color is changed.</summary>

+ 5 - 0
UICatalog/Scenarios/ComputedLayout.cs

@@ -53,6 +53,10 @@ public class ComputedLayout : Scenario
 
         app.LayoutComplete += (s, a) =>
                                           {
+                                              if (horizontalRuler.Viewport.Width == 0 || horizontalRuler.Viewport.Height == 0)
+                                              {
+                                                  return;
+                                              }
                                               horizontalRuler.Text =
                                                   rule.Repeat ((int)Math.Ceiling (horizontalRuler.Viewport.Width / (double)rule.Length)) [
                                                    ..horizontalRuler.Viewport.Width];
@@ -409,5 +413,6 @@ public class ComputedLayout : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 29 - 16
UICatalog/Scenarios/ConfigurationEditor.cs

@@ -22,7 +22,7 @@ public class ConfigurationEditor : Scenario
     };
 
     private static Action _editorColorSchemeChanged;
-    private StatusItem _lenStatusItem;
+    private Shortcut _lenShortcut;
     private TileView _tileView;
 
     [SerializableConfigurationProperty (Scope = typeof (AppScope))]
@@ -41,6 +41,7 @@ public class ConfigurationEditor : Scenario
         Application.Init ();
 
         Toplevel top = new ();
+
         _tileView = new TileView (0)
         {
             Width = Dim.Fill (), Height = Dim.Fill (1), Orientation = Orientation.Vertical, LineStyle = LineStyle.Single
@@ -48,21 +49,33 @@ public class ConfigurationEditor : Scenario
 
         top.Add (_tileView);
 
-        _lenStatusItem = new StatusItem (KeyCode.CharMask, "Len: ", null);
+        _lenShortcut = new Shortcut ()
+        {
+            Title = "Len: ",
+        };
 
-        var statusBar = new StatusBar (
-                                       new []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} Quit",
-                                                () => Quit ()
-                                               ),
-                                           new (KeyCode.F5, "~F5~ Reload", () => Reload ()),
-                                           new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
-                                           _lenStatusItem
-                                       }
-                                      );
+        var quitShortcut = new Shortcut ()
+        {
+            Key = Application.QuitKey,
+            Title = $"{Application.QuitKey} Quit",
+            Action = Quit
+        };
+
+        var reloadShortcut = new Shortcut ()
+        {
+            Key = Key.F5.WithShift,
+            Title = "Reload",
+        };
+        reloadShortcut.Accept += (s, e) => { Reload (); };
+
+        var saveShortcut = new Shortcut ()
+        {
+            Key = Key.F4,
+            Title = "Save",
+            Action = Save
+        };
+
+        var statusBar = new StatusBar ([quitShortcut, reloadShortcut, saveShortcut, _lenShortcut]);
 
         top.Add (statusBar);
 
@@ -120,7 +133,7 @@ public class ConfigurationEditor : Scenario
 
             textView.Read ();
 
-            textView.Enter += (s, e) => { _lenStatusItem.Title = $"Len:{textView.Text.Length}"; };
+            textView.Enter += (s, e) => { _lenShortcut.Title = $"Len:{textView.Text.Length}"; };
         }
 
         Application.Top.LayoutSubviews ();

+ 102 - 95
UICatalog/Scenarios/CsvEditor.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using System.Data;
 using System.Globalization;
 using System.IO;
@@ -26,16 +25,23 @@ public class CsvEditor : Scenario
     private MenuItem _miCentered;
     private MenuItem _miLeft;
     private MenuItem _miRight;
-    private TextField _selectedCellLabel;
+    private TextField _selectedCellTextField;
     private TableView _tableView;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
 
-        _tableView = new TableView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (1) };
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ()
+        {
+            Title = $"{GetName ()}"
+        };
+
+        //appWindow.Height = Dim.Fill (1); // status bar
+
+        _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (2) };
 
         var fileMenu = new MenuBarItem (
                                         "_File",
@@ -53,97 +59,96 @@ public class CsvEditor : Scenario
             Menus =
             [
                 fileMenu,
-                new MenuBarItem (
-                                 "_Edit",
-                                 new MenuItem []
-                                 {
-                                     new ("_New Column", "", () => AddColumn ()),
-                                     new ("_New Row", "", () => AddRow ()),
-                                     new (
-                                          "_Rename Column",
-                                          "",
-                                          () => RenameColumn ()
-                                         ),
-                                     new ("_Delete Column", "", () => DeleteColum ()),
-                                     new ("_Move Column", "", () => MoveColumn ()),
-                                     new ("_Move Row", "", () => MoveRow ()),
-                                     new ("_Sort Asc", "", () => Sort (true)),
-                                     new ("_Sort Desc", "", () => Sort (false))
-                                 }
-                                ),
-                new MenuBarItem (
-                                 "_View",
-                                 new []
-                                 {
-                                     _miLeft = new MenuItem (
-                                                             "_Align Left",
-                                                             "",
-                                                             () => Align (Alignment.Start)
-                                                            ),
-                                     _miRight = new MenuItem (
-                                                              "_Align Right",
-                                                              "",
-                                                              () => Align (Alignment.End)
-                                                             ),
-                                     _miCentered = new MenuItem (
-                                                                 "_Align Centered",
-                                                                 "",
-                                                                 () => Align (Alignment.Center)
-                                                                ),
-
-                                     // Format requires hard typed data table, when we read a CSV everything is untyped (string) so this only works for new columns in this demo
-                                     _miCentered = new MenuItem (
-                                                                 "_Set Format Pattern",
-                                                                 "",
-                                                                 () => SetFormat ()
-                                                                )
-                                 }
-                                )
+                new (
+                     "_Edit",
+                     new MenuItem []
+                     {
+                         new ("_New Column", "", () => AddColumn ()),
+                         new ("_New Row", "", () => AddRow ()),
+                         new (
+                              "_Rename Column",
+                              "",
+                              () => RenameColumn ()
+                             ),
+                         new ("_Delete Column", "", () => DeleteColum ()),
+                         new ("_Move Column", "", () => MoveColumn ()),
+                         new ("_Move Row", "", () => MoveRow ()),
+                         new ("_Sort Asc", "", () => Sort (true)),
+                         new ("_Sort Desc", "", () => Sort (false))
+                     }
+                    ),
+                new (
+                     "_View",
+                     new []
+                     {
+                         _miLeft = new (
+                                        "_Align Left",
+                                        "",
+                                        () => Align (Alignment.Start)
+                                       ),
+                         _miRight = new (
+                                         "_Align Right",
+                                         "",
+                                         () => Align (Alignment.End)
+                                        ),
+                         _miCentered = new (
+                                            "_Align Centered",
+                                            "",
+                                            () => Align (Alignment.Center)
+                                           ),
+
+                         // Format requires hard typed data table, when we read a CSV everything is untyped (string) so this only works for new columns in this demo
+                         _miCentered = new (
+                                            "_Set Format Pattern",
+                                            "",
+                                            () => SetFormat ()
+                                           )
+                     }
+                    )
             ]
         };
-        Top.Add (menu);
-
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.O,
-                                                "~^O~ Open",
-                                                () => Open ()
-                                               ),
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.S,
-                                                "~^S~ Save",
-                                                () => Save ()
-                                               ),
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
-                                       }
-                                      );
-        Top.Add (statusBar);
+        appWindow.Add (menu);
 
-        Win.Add (_tableView);
-
-        _selectedCellLabel = new TextField
+        _selectedCellTextField = new ()
         {
-            X = 0,
-            Y = Pos.Bottom (_tableView),
             Text = "0,0",
-            Width = Dim.Fill (),
-            TextAlignment = Alignment.End
+            Width = 10,
+            Height = 1
+        };
+        _selectedCellTextField.TextChanged += SelectedCellLabel_TextChanged;
+
+        var statusBar = new StatusBar (
+                                       [
+                                           new (Application.QuitKey, "Quit", Quit, "Quit!"),
+                                           new (Key.O.WithCtrl, "Open", Open, "Open a file."),
+                                           new (Key.S.WithCtrl, "Save", Save, "Save current."),
+                                           new ()
+                                           {
+                                               HelpText = "Cell:",
+                                               CommandView = _selectedCellTextField,
+                                               AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast,
+                                               Enabled = false
+                                           }
+                                       ])
+        {
+            AlignmentModes = AlignmentModes.IgnoreFirstOrLast
         };
-        _selectedCellLabel.TextChanged += SelectedCellLabel_TextChanged;
+        appWindow.Add (statusBar);
 
-        Win.Add (_selectedCellLabel);
+        appWindow.Add (_tableView);
 
         _tableView.SelectedCellChanged += OnSelectedCellChanged;
         _tableView.CellActivated += EditCurrentCell;
         _tableView.KeyDown += TableViewKeyPress;
 
         SetupScrollBar ();
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     private void AddColumn ()
@@ -300,10 +305,10 @@ public class CsvEditor : Scenario
         var ok = new Button { Text = "Ok", IsDefault = true };
 
         ok.Accept += (s, e) =>
-                      {
-                          okPressed = true;
-                          Application.RequestStop ();
-                      };
+                     {
+                         okPressed = true;
+                         Application.RequestStop ();
+                     };
         var cancel = new Button { Text = "Cancel" };
         cancel.Accept += (s, e) => { Application.RequestStop (); };
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
@@ -425,9 +430,9 @@ public class CsvEditor : Scenario
     private void OnSelectedCellChanged (object sender, SelectedCellChangedEventArgs e)
     {
         // only update the text box if the user is not manually editing it
-        if (!_selectedCellLabel.HasFocus)
+        if (!_selectedCellTextField.HasFocus)
         {
-            _selectedCellLabel.Text = $"{_tableView.SelectedRow},{_tableView.SelectedColumn}";
+            _selectedCellTextField.Text = $"{_tableView.SelectedRow},{_tableView.SelectedColumn}";
         }
 
         if (_tableView.Table == null || _tableView.SelectedColumn == -1)
@@ -446,7 +451,7 @@ public class CsvEditor : Scenario
     {
         var ofd = new FileDialog
         {
-            AllowedTypes = new List<IAllowedType> { new AllowedType ("Comma Separated Values", ".csv") }
+            AllowedTypes = new () { new AllowedType ("Comma Separated Values", ".csv") }
         };
         ofd.Style.OkButtonText = "Open";
 
@@ -456,6 +461,7 @@ public class CsvEditor : Scenario
         {
             Open (ofd.Path);
         }
+
         ofd.Dispose ();
     }
 
@@ -496,7 +502,8 @@ public class CsvEditor : Scenario
 
             // Only set the current filename if we successfully loaded the entire file
             _currentFile = filename;
-            Win.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}";
+            _selectedCellTextField.SuperView.Enabled = true;
+            Application.Top.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}";
         }
         catch (Exception ex)
         {
@@ -561,13 +568,13 @@ public class CsvEditor : Scenario
     private void SelectedCellLabel_TextChanged (object sender, StateEventArgs<string> e)
     {
         // if user is in the text control and editing the selected cell
-        if (!_selectedCellLabel.HasFocus)
+        if (!_selectedCellTextField.HasFocus)
         {
             return;
         }
 
         // change selected cell to the one the user has typed into the box
-        Match match = Regex.Match (_selectedCellLabel.Text, "^(\\d+),(\\d+)$");
+        Match match = Regex.Match (_selectedCellTextField.Text, "^(\\d+),(\\d+)$");
 
         if (match.Success)
         {

+ 39 - 87
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
+using System.Linq;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Text;
@@ -12,15 +13,11 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Top Level Windows")]
 public class DynamicStatusBar : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
-        Application.Init ();
 
-        Top = new ();
-
-        Top.Add (
-                 new DynamicStatusBarSample { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }
-                );
+        Application.Run<DynamicStatusBarSample> ().Dispose ();
+        Application.Shutdown ();
     }
 
     public class Binding
@@ -90,9 +87,9 @@ public class DynamicStatusBar : Scenario
 
     public class DynamicStatusBarDetails : FrameView
     {
-        private StatusItem _statusItem;
+        private Shortcut _statusItem;
 
-        public DynamicStatusBarDetails (StatusItem statusItem = null) : this ()
+        public DynamicStatusBarDetails (Shortcut statusItem = null) : this ()
         {
             _statusItem = statusItem;
             Title = statusItem == null ? "Adding New StatusBar Item." : "Editing StatusBar Item.";
@@ -155,7 +152,7 @@ public class DynamicStatusBar : Scenario
 
             bool CheckShortcut (KeyCode k, bool pre)
             {
-                StatusItem m = _statusItem != null ? _statusItem : new StatusItem (k, "", null);
+                Shortcut m = _statusItem != null ? _statusItem : new Shortcut (k, "", null);
 
                 if (pre && !ShortcutHelper.PreShortcutValidation (k))
                 {
@@ -166,28 +163,10 @@ public class DynamicStatusBar : Scenario
 
                 if (!pre)
                 {
-                    if (!ShortcutHelper.PostShortcutValidation (
-                                                                ShortcutHelper.GetShortcutFromTag (
-                                                                                                   TextShortcut.Text,
-                                                                                                   StatusBar.ShortcutDelimiter
-                                                                                                  )
-                                                               ))
-                    {
-                        TextShortcut.Text = "";
-
-                        return false;
-                    }
-
                     return true;
                 }
 
-                TextShortcut.Text =
-                    Key.ToString (
-                                  k,
-                                  StatusBar
-                                      .ShortcutDelimiter
-                                 ); //ShortcutHelper.GetShortcutTag (k, StatusBar.ShortcutDelimiter);
-
+                TextShortcut.Text = k.ToString ();
                 return true;
             }
 
@@ -213,7 +192,7 @@ public class DynamicStatusBar : Scenario
         public TextField TextTitle { get; }
         public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (item.Title, item.Action, "Ok"); }
 
-        public void EditStatusItem (StatusItem statusItem)
+        public void EditStatusItem (Shortcut statusItem)
         {
             if (statusItem == null)
             {
@@ -231,12 +210,7 @@ public class DynamicStatusBar : Scenario
                                   ? GetTargetAction (statusItem.Action)
                                   : string.Empty;
 
-            TextShortcut.Text =
-                Key.ToString (
-                              (KeyCode)statusItem.Shortcut,
-                              StatusBar
-                                  .ShortcutDelimiter
-                             ); //ShortcutHelper.GetShortcutTag (statusItem.Shortcut, StatusBar.ShortcutDelimiter) ?? "";
+            TextShortcut.Text = statusItem.CommandView.Text;
         }
 
         public DynamicStatusItem EnterStatusItem ()
@@ -334,31 +308,16 @@ public class DynamicStatusBar : Scenario
     public class DynamicStatusBarSample : Window
     {
         private readonly ListView _lstItems;
-        private StatusItem _currentEditStatusItem;
+        private Shortcut _currentEditStatusItem;
         private int _currentSelectedStatusBar = -1;
-        private StatusItem _currentStatusItem;
+        private Shortcut _currentStatusItem;
         private StatusBar _statusBar;
 
         public DynamicStatusBarSample ()
         {
             DataContext = new DynamicStatusItemModel ();
 
-            var _frmDelimiter = new FrameView
-            {
-                X = Pos.Center (),
-                Y = 0,
-                Width = 25,
-                Height = 4,
-                Title = "Shortcut Delimiter:"
-            };
-
-            var _txtDelimiter = new TextField { X = Pos.Center (), Width = 2, Text = $"{StatusBar.ShortcutDelimiter}" };
-
-            _txtDelimiter.TextChanged += (s, _) =>
-                                             StatusBar.ShortcutDelimiter = _txtDelimiter.Text.ToRunes () [0];
-            _frmDelimiter.Add (_txtDelimiter);
-
-            Add (_frmDelimiter);
+            Title = $"{Application.QuitKey} to Quit";
 
             var _frmStatusBar = new FrameView
             {
@@ -404,18 +363,18 @@ public class DynamicStatusBar : Scenario
                 Y = Pos.Top (_frmStatusBar),
                 Width = Dim.Fill (),
                 Height = Dim.Fill (4),
-                Title = "StatusBar Item Details:"
+                Title = "Shortcut Details:"
             };
             Add (_frmStatusBarDetails);
 
             _btnUp.Accept += (s, e) =>
                               {
                                   int i = _lstItems.SelectedItem;
-                                  StatusItem statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
+                                  Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;
 
                                   if (statusItem != null)
                                   {
-                                      StatusItem [] items = _statusBar.Items;
+                                      Shortcut [] items = _statusBar.Subviews.Cast<Shortcut> ().ToArray ();
 
                                       if (i > 0)
                                       {
@@ -434,11 +393,11 @@ public class DynamicStatusBar : Scenario
             _btnDown.Accept += (s, e) =>
                                 {
                                     int i = _lstItems.SelectedItem;
-                                    StatusItem statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
+                                    Shortcut statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].Shortcut : null;
 
                                     if (statusItem != null)
                                     {
-                                        StatusItem [] items = _statusBar.Items;
+                                        Shortcut [] items = _statusBar.Subviews.Cast<Shortcut> ().ToArray ();
 
                                         if (i < items.Length - 1)
                                         {
@@ -511,9 +470,9 @@ public class DynamicStatusBar : Scenario
                                        return;
                                    }
 
-                                   StatusItem newStatusItem = CreateNewStatusBar (item);
+                                   Shortcut newStatusItem = CreateNewStatusBar (item);
                                    _currentSelectedStatusBar++;
-                                   _statusBar.AddItemAt (_currentSelectedStatusBar, newStatusItem);
+                                   _statusBar.AddShortcutAt (_currentSelectedStatusBar, newStatusItem);
                                    DataContext.Items.Add (new DynamicStatusItemList (newStatusItem.Title, newStatusItem));
                                    _lstItems.MoveDown ();
                                    SetFrameDetails ();
@@ -521,13 +480,13 @@ public class DynamicStatusBar : Scenario
 
             _btnRemove.Accept += (s, e) =>
                                   {
-                                      StatusItem statusItem = DataContext.Items.Count > 0
-                                                                  ? DataContext.Items [_lstItems.SelectedItem].StatusItem
+                                      Shortcut statusItem = DataContext.Items.Count > 0
+                                                                  ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                                                   : null;
 
                                       if (statusItem != null)
                                       {
-                                          _statusBar.RemoveItem (_currentSelectedStatusBar);
+                                          _statusBar.RemoveShortcut (_currentSelectedStatusBar);
                                           DataContext.Items.RemoveAt (_lstItems.SelectedItem);
 
                                           if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1)
@@ -542,8 +501,8 @@ public class DynamicStatusBar : Scenario
 
             _lstItems.Enter += (s, e) =>
                                {
-                                   StatusItem statusItem = DataContext.Items.Count > 0
-                                                               ? DataContext.Items [_lstItems.SelectedItem].StatusItem
+                                   Shortcut statusItem = DataContext.Items.Count > 0
+                                                               ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                                                : null;
                                    SetFrameDetails (statusItem);
                                };
@@ -582,14 +541,14 @@ public class DynamicStatusBar : Scenario
 
             var lstItems = new Binding (this, "Items", _lstItems, "Source", listWrapperConverter);
 
-            void SetFrameDetails (StatusItem statusItem = null)
+            void SetFrameDetails (Shortcut statusItem = null)
             {
-                StatusItem newStatusItem;
+                Shortcut newStatusItem;
 
                 if (statusItem == null)
                 {
                     newStatusItem = DataContext.Items.Count > 0
-                                        ? DataContext.Items [_lstItems.SelectedItem].StatusItem
+                                        ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                         : null;
                 }
                 else
@@ -608,10 +567,10 @@ public class DynamicStatusBar : Scenario
                 }
             }
 
-            void SetListViewSource (StatusItem _currentStatusItem, bool fill = false)
+            void SetListViewSource (Shortcut _currentStatusItem, bool fill = false)
             {
                 DataContext.Items = [];
-                StatusItem statusItem = _currentStatusItem;
+                Shortcut statusItem = _currentStatusItem;
 
                 if (!fill)
                 {
@@ -620,35 +579,28 @@ public class DynamicStatusBar : Scenario
 
                 if (statusItem != null)
                 {
-                    foreach (StatusItem si in _statusBar.Items)
+                    foreach (Shortcut si in _statusBar.Subviews.Cast<Shortcut> ())
                     {
                         DataContext.Items.Add (new DynamicStatusItemList (si.Title, si));
                     }
                 }
             }
 
-            StatusItem CreateNewStatusBar (DynamicStatusItem item)
+            Shortcut CreateNewStatusBar (DynamicStatusItem item)
             {
-                var newStatusItem = new StatusItem (
-                                                    ShortcutHelper.GetShortcutFromTag (
-                                                                                       item.Shortcut,
-                                                                                       StatusBar.ShortcutDelimiter
-                                                                                      ),
-                                                    item.Title,
-                                                    _frmStatusBarDetails.CreateAction (item)
-                                                   );
+                var newStatusItem = new Shortcut (Key.Empty, item.Title, null);
 
                 return newStatusItem;
             }
 
             void UpdateStatusItem (
-                StatusItem _currentEditStatusItem,
+                Shortcut _currentEditStatusItem,
                 DynamicStatusItem statusItem,
                 int index
             )
             {
                 _currentEditStatusItem = CreateNewStatusBar (statusItem);
-                _statusBar.Items [index] = _currentEditStatusItem;
+                //_statusBar.Items [index] = _currentEditStatusItem;
 
                 if (DataContext.Items.Count == 0)
                 {
@@ -702,15 +654,15 @@ public class DynamicStatusBar : Scenario
     {
         public DynamicStatusItemList () { }
 
-        public DynamicStatusItemList (string title, StatusItem statusItem)
+        public DynamicStatusItemList (string title, Shortcut statusItem)
         {
             Title = title;
-            StatusItem = statusItem;
+            Shortcut = statusItem;
         }
 
-        public StatusItem StatusItem { get; set; }
+        public Shortcut Shortcut { get; set; }
         public string Title { get; set; }
-        public override string ToString () { return $"{Title}, {StatusItem}"; }
+        public override string ToString () { return $"{Title}, {Shortcut}"; }
     }
 
     public class DynamicStatusItemModel : INotifyPropertyChanged

+ 10 - 15
UICatalog/Scenarios/Editor.cs

@@ -238,27 +238,22 @@ public class Editor : Scenario
 
         _appWindow.Add (menu);
 
-        var siCursorPosition = new StatusItem (KeyCode.Null, "", null);
+        var siCursorPosition = new Shortcut(KeyCode.Null, "", null);
 
         var statusBar = new StatusBar (
                                        new []
                                        {
+                                           new (Application.QuitKey, $"Quit", Quit),
+                                           new (Key.F2, "Open", Open),
+                                           new (Key.F3, "Save", () => Save ()),
+                                           new (Key.F4, "Save As", () => SaveAs ()),
+                                           new (Key.Empty, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null),
                                            siCursorPosition,
-                                           new (KeyCode.F2, "~F2~ Open", () => Open ()),
-                                           new (KeyCode.F3, "~F3~ Save", () => Save ()),
-                                           new (KeyCode.F4, "~F4~ Save As", () => SaveAs ()),
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               ),
-                                           new (
-                                                KeyCode.Null,
-                                                $"OS Clipboard IsSupported : {Clipboard.IsSupported}",
-                                                null
-                                               )
                                        }
-                                      );
+                                      )
+        {
+            AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast
+        };
 
         _textView.UnwrappedCursorPosition += (s, e) =>
                                              {

+ 185 - 161
UICatalog/Scenarios/GraphViewExample.cs

@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using Terminal.Gui;
+using Application = Terminal.Gui.Application;
 
 namespace UICatalog.Scenarios;
 
@@ -18,12 +20,12 @@ public class GraphViewExample : Scenario
     private GraphView _graphView;
     private MenuItem _miDiags;
     private MenuItem _miShowBorder;
+    private ViewDiagnosticFlags _viewDiagnostics;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        Application.Init ();
+        Toplevel app = new ();
 
         _graphs = new []
         {
@@ -41,155 +43,177 @@ public class GraphViewExample : Scenario
         {
             Menus =
             [
-                new MenuBarItem (
-                                 "_File",
-                                 new MenuItem []
-                                 {
-                                     new (
-                                          "Scatter _Plot",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             0] ()
-                                         ),
-                                     new (
-                                          "_V Bar Graph",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             1] ()
-                                         ),
-                                     new (
-                                          "_H Bar Graph",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             2] ()
-                                         ),
-                                     new (
-                                          "P_opulation Pyramid",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             3] ()
-                                         ),
-                                     new (
-                                          "_Line Graph",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             4] ()
-                                         ),
-                                     new (
-                                          "Sine _Wave",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             5] ()
-                                         ),
-                                     new (
-                                          "Silent _Disco",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             6] ()
-                                         ),
-                                     new (
-                                          "_Multi Bar Graph",
-                                          "",
-                                          () => _graphs [_currentGraph =
-                                                             7] ()
-                                         ),
-                                     new ("_Quit", "", () => Quit ())
-                                 }
-                                ),
-                new MenuBarItem (
-                                 "_View",
-                                 new []
-                                 {
-                                     new ("Zoom _In", "", () => Zoom (0.5f)),
-                                     new ("Zoom _Out", "", () => Zoom (2f)),
-                                     new ("MarginLeft++", "", () => Margin (true, true)),
-                                     new ("MarginLeft--", "", () => Margin (true, false)),
-                                     new ("MarginBottom++", "", () => Margin (false, true)),
-                                     new ("MarginBottom--", "", () => Margin (false, false)),
-                                     _miShowBorder = new MenuItem (
-                                                                   "_Enable Margin, Border, and Padding",
-                                                                   "",
-                                                                   () => ShowBorder ()
-                                                                  )
-                                     {
-                                         Checked = true,
-                                         CheckType = MenuItemCheckStyle
-                                             .Checked
-                                     },
-                                     _miDiags = new MenuItem (
-                                                              "Dri_ver Diagnostics",
-                                                              "",
-                                                              () => EnableDiagnostics ()
-                                                             )
-                                     {
-                                         Checked = View.Diagnostics
-                                                   == (ViewDiagnosticFlags
-                                                                    .Padding
-                                                       | ViewDiagnosticFlags
-                                                                      .Ruler),
-                                         CheckType = MenuItemCheckStyle.Checked
-                                     }
-                                 }
-                                )
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new (
+                              "Scatter _Plot",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 0] ()
+                             ),
+                         new (
+                              "_V Bar Graph",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 1] ()
+                             ),
+                         new (
+                              "_H Bar Graph",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 2] ()
+                             ),
+                         new (
+                              "P_opulation Pyramid",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 3] ()
+                             ),
+                         new (
+                              "_Line Graph",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 4] ()
+                             ),
+                         new (
+                              "Sine _Wave",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 5] ()
+                             ),
+                         new (
+                              "Silent _Disco",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 6] ()
+                             ),
+                         new (
+                              "_Multi Bar Graph",
+                              "",
+                              () => _graphs [_currentGraph =
+                                                 7] ()
+                             ),
+                         new ("_Quit", "", () => Quit ())
+                     }
+                    ),
+                new (
+                     "_View",
+                     new []
+                     {
+                         new ("Zoom _In", "", () => Zoom (0.5f)),
+                         new ("Zoom _Out", "", () => Zoom (2f)),
+                         new ("MarginLeft++", "", () => Margin (true, true)),
+                         new ("MarginLeft--", "", () => Margin (true, false)),
+                         new ("MarginBottom++", "", () => Margin (false, true)),
+                         new ("MarginBottom--", "", () => Margin (false, false)),
+                         _miShowBorder = new (
+                                              "_Enable Margin, Border, and Padding",
+                                              "",
+                                              () => ShowBorder ()
+                                             )
+                         {
+                             Checked = true,
+                             CheckType = MenuItemCheckStyle
+                                 .Checked
+                         },
+                         _miDiags = new (
+                                         "_Diagnostics",
+                                         "",
+                                         () => ToggleDiagnostics ()
+                                        )
+                         {
+                             Checked = View.Diagnostics
+                                       == (ViewDiagnosticFlags
+                                               .Padding
+                                           | ViewDiagnosticFlags
+                                               .Ruler),
+                             CheckType = MenuItemCheckStyle.Checked
+                         }
+                     }
+                    )
             ]
         };
-        Top.Add (menu);
+        app.Add (menu);
 
-        _graphView = new GraphView
+        _graphView = new()
         {
             X = 0,
-            Y = 0,
+            Y = 1,
             Width = Dim.Percent (70),
-            Height = Dim.Fill (),
+            Height = Dim.Fill (1),
             BorderStyle = LineStyle.Single
         };
         _graphView.Border.Thickness = _thickness;
         _graphView.Margin.Thickness = _thickness;
         _graphView.Padding.Thickness = _thickness;
 
-        Win.Add (_graphView);
+        app.Add (_graphView);
 
         var frameRight = new FrameView
         {
-            X = Pos.Right (_graphView) + 1,
-            Y = 0,
+            X = Pos.Right (_graphView),
+            Y = Pos.Top (_graphView),
             Width = Dim.Fill (),
-            Height = Dim.Fill (),
+            Height = Dim.Height (_graphView),
             Title = "About"
         };
 
         frameRight.Add (
-                        _about = new TextView { Width = Dim.Fill (), Height = Dim.Fill () }
+                        _about = new() { Width = Dim.Fill (), Height = Dim.Fill () }
                        );
 
-        Win.Add (frameRight);
+        app.Add (frameRight);
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
+                                       new Shortcut []
                                        {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               ),
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.G,
-                                                "~^G~ Next",
-                                                () => _graphs [_currentGraph++ % _graphs.Length] ()
-                                               )
+                                           new (Key.G.WithCtrl, "Next Graph", () => _graphs [_currentGraph++ % _graphs.Length] ()),
+                                           new (Key.CursorUp, "Zoom In", () => Zoom (0.5f)),
+                                           new (Key.CursorDown, "Zoom Out", () => Zoom (2f))
                                        }
                                       );
-        Top.Add (statusBar);
+        app.Add (statusBar);
+
+        var diagShortcut = new Shortcut
+        {
+            Key = Key.F10,
+            CommandView = new CheckBox
+            {
+                Title = "Diagnostics",
+                CanFocus = false
+            }
+        };
+        statusBar.Add (diagShortcut).Accept += DiagShortcut_Accept;
+
+        _graphs [_currentGraph++ % _graphs.Length] ();
+
+        _viewDiagnostics = View.Diagnostics;
+        Application.Run (app);
+        View.Diagnostics = _viewDiagnostics;
+        app.Dispose ();
+        Application.Shutdown ();
+    }
+
+    private void DiagShortcut_Accept (object sender, CancelEventArgs e)
+    {
+        ToggleDiagnostics ();
+
+        if (sender is Shortcut shortcut && shortcut.CommandView is CheckBox checkBox)
+        {
+            checkBox.Checked = _miDiags.Checked;
+        }
     }
 
-    private void EnableDiagnostics ()
+    private void ToggleDiagnostics ()
     {
         _miDiags.Checked = !_miDiags.Checked;
 
         View.Diagnostics = _miDiags.Checked == true
-                                        ? ViewDiagnosticFlags.Padding
-                                          | ViewDiagnosticFlags.Ruler
-                                        : ViewDiagnosticFlags.Off;
+                               ? ViewDiagnosticFlags.Padding
+                                 | ViewDiagnosticFlags.Ruler
+                               : ViewDiagnosticFlags.Off;
         Application.Refresh ();
     }
 
@@ -216,7 +240,7 @@ public class GraphViewExample : Scenario
         _about.Text = "Housing Expenditures by income thirds 1996-2003";
 
         Color fore = _graphView.ColorScheme.Normal.Foreground == new Color (ColorName.Black)
-                         ? new Color (ColorName.White)
+                         ? new (ColorName.White)
                          : _graphView.ColorScheme.Normal.Foreground;
         var black = new Attribute (fore, Color.Black);
         var cyan = new Attribute (Color.BrightCyan, Color.Black);
@@ -238,7 +262,7 @@ public class GraphViewExample : Scenario
         series.AddBars ("'02", stiple, 6600, 11000, 16700);
         series.AddBars ("'03", stiple, 7000, 12000, 17000);
 
-        _graphView.CellSize = new PointF (0.25f, 1000);
+        _graphView.CellSize = new (0.25f, 1000);
         _graphView.Series.Add (series);
         _graphView.SetNeedsDisplay ();
 
@@ -254,20 +278,20 @@ public class GraphViewExample : Scenario
 
         _graphView.AxisY.Minimum = 0;
 
-        var legend = new LegendAnnotation (new Rectangle (_graphView.Viewport.Width - 20, 0, 20, 5));
+        var legend = new LegendAnnotation (new (_graphView.Viewport.Width - 20, 0, 20, 5));
 
         legend.AddEntry (
-                         new GraphCellToRender (stiple, series.SubSeries.ElementAt (0).OverrideBarColor),
+                         new (stiple, series.SubSeries.ElementAt (0).OverrideBarColor),
                          "Lower Third"
                         );
 
         legend.AddEntry (
-                         new GraphCellToRender (stiple, series.SubSeries.ElementAt (1).OverrideBarColor),
+                         new (stiple, series.SubSeries.ElementAt (1).OverrideBarColor),
                          "Middle Third"
                         );
 
         legend.AddEntry (
-                         new GraphCellToRender (stiple, series.SubSeries.ElementAt (2).OverrideBarColor),
+                         new (stiple, series.SubSeries.ElementAt (2).OverrideBarColor),
                          "Upper Third"
                         );
         _graphView.Annotations.Add (legend);
@@ -299,7 +323,7 @@ public class GraphViewExample : Scenario
                                    for (var i = 0; i < 31; i++)
                                    {
                                        bars.Add (
-                                                 new BarSeriesBar (null, stiple, r.Next (0, 100))
+                                                 new (null, stiple, r.Next (0, 100))
                                                  {
                                                      //ColorGetter = colorDelegate
                                                  }
@@ -319,7 +343,7 @@ public class GraphViewExample : Scenario
         _graphView.Series.Add (series);
 
         // How much graph space each cell of the console depicts
-        _graphView.CellSize = new PointF (1, 10);
+        _graphView.CellSize = new (1, 10);
         _graphView.AxisX.Increment = 0; // No graph ticks
         _graphView.AxisX.ShowLabelsEvery = 0; // no labels
 
@@ -374,7 +398,7 @@ public class GraphViewExample : Scenario
 
         var barSeries = new BarSeries
         {
-            Bars = new List<BarSeriesBar>
+            Bars = new()
             {
                 new ("Switzerland", softStiple, 83.4f),
                 new (
@@ -451,7 +475,7 @@ public class GraphViewExample : Scenario
             barSeries.Orientation = Orientation.Vertical;
 
             // How much graph space each cell of the console depicts
-            _graphView.CellSize = new PointF (0.1f, 0.25f);
+            _graphView.CellSize = new (0.1f, 0.25f);
 
             // No axis marks since Bar will add it's own categorical marks
             _graphView.AxisX.Increment = 0f;
@@ -469,14 +493,14 @@ public class GraphViewExample : Scenario
             _graphView.MarginLeft = 6;
 
             // Start the graph at 80 years because that is where most of our data is
-            _graphView.ScrollOffset = new PointF (0, 80);
+            _graphView.ScrollOffset = new (0, 80);
         }
         else
         {
             barSeries.Orientation = Orientation.Horizontal;
 
             // How much graph space each cell of the console depicts
-            _graphView.CellSize = new PointF (0.1f, 1f);
+            _graphView.CellSize = new (0.1f, 1f);
 
             // No axis marks since Bar will add it's own categorical marks
             _graphView.AxisY.Increment = 0f;
@@ -495,7 +519,7 @@ public class GraphViewExample : Scenario
             _graphView.MarginLeft = (uint)barSeries.Bars.Max (b => b.Text.Length) + 2;
 
             // Start the graph at 80 years because that is where most of our data is
-            _graphView.ScrollOffset = new PointF (80, 0);
+            _graphView.ScrollOffset = new (80, 0);
         }
 
         _graphView.SetNeedsDisplay ();
@@ -522,7 +546,7 @@ public class GraphViewExample : Scenario
 
         for (var i = 0; i < 10; i++)
         {
-            randomPoints.Add (new PointF (r.Next (100), r.Next (100)));
+            randomPoints.Add (new (r.Next (100), r.Next (100)));
         }
 
         var points = new ScatterSeries { Points = randomPoints };
@@ -535,14 +559,14 @@ public class GraphViewExample : Scenario
         _graphView.Series.Add (points);
         _graphView.Annotations.Add (line);
 
-        randomPoints = new List<PointF> ();
+        randomPoints = new ();
 
         for (var i = 0; i < 10; i++)
         {
-            randomPoints.Add (new PointF (r.Next (100), r.Next (100)));
+            randomPoints.Add (new (r.Next (100), r.Next (100)));
         }
 
-        var points2 = new ScatterSeries { Points = randomPoints, Fill = new GraphCellToRender ((Rune)'x', red) };
+        var points2 = new ScatterSeries { Points = randomPoints, Fill = new ((Rune)'x', red) };
 
         var line2 = new PathAnnotation
         {
@@ -553,7 +577,7 @@ public class GraphViewExample : Scenario
         _graphView.Annotations.Add (line2);
 
         // How much graph space each cell of the console depicts
-        _graphView.CellSize = new PointF (2, 5);
+        _graphView.CellSize = new (2, 5);
 
         // leave space for axis labels
         _graphView.MarginBottom = 2;
@@ -574,10 +598,10 @@ public class GraphViewExample : Scenario
                                     new TextAnnotation
                                     {
                                         Text = "(Max)",
-                                        GraphPosition = new PointF (
-                                                                    max.X + 2 * _graphView.CellSize.X,
-                                                                    max.Y
-                                                                   )
+                                        GraphPosition = new (
+                                                             max.X + 2 * _graphView.CellSize.X,
+                                                             max.Y
+                                                            )
                                     }
                                    );
 
@@ -597,7 +621,7 @@ public class GraphViewExample : Scenario
         _graphView.Series.Add (
                                new ScatterSeries
                                {
-                                   Points = new List<PointF>
+                                   Points = new()
                                    {
                                        new (1, 1.007f),
                                        new (2, 4.002f),
@@ -719,7 +743,7 @@ public class GraphViewExample : Scenario
                               );
 
         // How much graph space each cell of the console depicts
-        _graphView.CellSize = new PointF (1, 5);
+        _graphView.CellSize = new (1, 5);
 
         // leave space for axis labels
         _graphView.MarginBottom = 2;
@@ -772,10 +796,10 @@ public class GraphViewExample : Scenario
         _graphView.Reset ();
 
         // How much graph space each cell of the console depicts
-        _graphView.CellSize = new PointF (100_000, 1);
+        _graphView.CellSize = new (100_000, 1);
 
         //center the x axis in middle of screen to show both sides
-        _graphView.ScrollOffset = new PointF (-3_000_000, 0);
+        _graphView.ScrollOffset = new (-3_000_000, 0);
 
         _graphView.AxisX.Text = "Number Of People";
         _graphView.AxisX.Increment = 500_000;
@@ -801,7 +825,7 @@ public class GraphViewExample : Scenario
         var malesSeries = new BarSeries
         {
             Orientation = Orientation.Horizontal,
-            Bars = new List<BarSeriesBar>
+            Bars = new()
             {
                 new ("0-4", stiple, -2009363),
                 new ("5-9", stiple, -2108550),
@@ -832,7 +856,7 @@ public class GraphViewExample : Scenario
         var femalesSeries = new BarSeries
         {
             Orientation = Orientation.Horizontal,
-            Bars = new List<BarSeriesBar>
+            Bars = new()
             {
                 new ("0-4", stiple, 1915127),
                 new ("5-9", stiple, 2011016),
@@ -895,15 +919,15 @@ public class GraphViewExample : Scenario
         // Generate line graph with 2,000 points
         for (float x = -500; x < 500; x += 0.5f)
         {
-            points.Points.Add (new PointF (x, (float)Math.Sin (x)));
-            line.Points.Add (new PointF (x, (float)Math.Sin (x)));
+            points.Points.Add (new (x, (float)Math.Sin (x)));
+            line.Points.Add (new (x, (float)Math.Sin (x)));
         }
 
         _graphView.Series.Add (points);
         _graphView.Annotations.Add (line);
 
         // How much graph space each cell of the console depicts
-        _graphView.CellSize = new PointF (0.1f, 0.1f);
+        _graphView.CellSize = new (0.1f, 0.1f);
 
         // leave space for axis labels
         _graphView.MarginBottom = 2;
@@ -920,7 +944,7 @@ public class GraphViewExample : Scenario
         _graphView.AxisY.Text = "↑Y";
         _graphView.AxisY.LabelGetter = v => v.Value.ToString ("N2");
 
-        _graphView.ScrollOffset = new PointF (-2.5f, -1);
+        _graphView.ScrollOffset = new (-2.5f, -1);
 
         _graphView.SetNeedsDisplay ();
     }
@@ -946,10 +970,10 @@ public class GraphViewExample : Scenario
 
     private void Zoom (float factor)
     {
-        _graphView.CellSize = new PointF (
-                                          _graphView.CellSize.X * factor,
-                                          _graphView.CellSize.Y * factor
-                                         );
+        _graphView.CellSize = new (
+                                   _graphView.CellSize.X * factor,
+                                   _graphView.CellSize.Y * factor
+                                  );
 
         _graphView.AxisX.Increment *= factor;
         _graphView.AxisY.Increment *= factor;
@@ -967,11 +991,11 @@ public class GraphViewExample : Scenario
 
         public DiscoBarSeries ()
         {
-            _green = new Attribute (Color.BrightGreen, Color.Black);
-            _brightgreen = new Attribute (Color.Green, Color.Black);
-            _brightyellow = new Attribute (Color.BrightYellow, Color.Black);
-            _red = new Attribute (Color.Red, Color.Black);
-            _brightred = new Attribute (Color.BrightRed, Color.Black);
+            _green = new (Color.BrightGreen, Color.Black);
+            _brightgreen = new (Color.Green, Color.Black);
+            _brightyellow = new (Color.BrightYellow, Color.Black);
+            _red = new (Color.Red, Color.Black);
+            _brightred = new (Color.BrightRed, Color.Black);
         }
 
         protected override void DrawBarLine (GraphView graph, Point start, Point end, BarSeriesBar beingDrawn)

+ 38 - 28
UICatalog/Scenarios/HexEditor.cs

@@ -16,21 +16,31 @@ public class HexEditor : Scenario
     private HexView _hexView;
     private MenuItem _miAllowEdits;
     private bool _saved = true;
-    private StatusItem _siPositionChanged;
+    private Shortcut _siPositionChanged;
     private StatusBar _statusBar;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName () + "-" + _fileName ?? "Untitled";
+        Application.Init ();
+        Toplevel app = new Toplevel ()
+        {
+            ColorScheme = Colors.ColorSchemes ["Base"]
+        };
 
         CreateDemoFile (_fileName);
 
-        //CreateUnicodeDemoFile (_fileName);
-
-        _hexView = new HexView (LoadFile ()) { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
+        _hexView = new HexView (new MemoryStream (Encoding.UTF8.GetBytes ("Demo text.")))
+        {
+            X = 0,
+            Y = 1,
+            Width = Dim.Fill (),
+            Height = Dim.Fill (1),
+            Title = _fileName ?? "Untitled",
+            BorderStyle = LineStyle.Rounded,
+        };
         _hexView.Edited += _hexView_Edited;
         _hexView.PositionChanged += _hexView_PositionChanged;
-        Win.Add (_hexView);
+        app.Add (_hexView);
 
         var menu = new MenuBar
         {
@@ -74,20 +84,20 @@ public class HexEditor : Scenario
                                 )
             ]
         };
-        Top.Add (menu);
+        app.Add (menu);
 
         _statusBar = new StatusBar (
                                     new []
                                     {
-                                        new (KeyCode.F2, "~F2~ Open", () => Open ()),
-                                        new (KeyCode.F3, "~F3~ Save", () => Save ()),
+                                        new (Key.F2, "Open", () => Open ()),
+                                        new (Key.F3, "Save", () => Save ()),
                                         new (
                                              Application.QuitKey,
-                                             $"{Application.QuitKey} to Quit",
+                                             $"Quit",
                                              () => Quit ()
                                             ),
-                                        _siPositionChanged = new StatusItem (
-                                                                             KeyCode.Null,
+                                        _siPositionChanged = new Shortcut (
+                                                                             Key.Empty,
                                                                              $"Position: {
                                                                                  _hexView.Position
                                                                              } Line: {
@@ -100,8 +110,17 @@ public class HexEditor : Scenario
                                                                              () => { }
                                                                             )
                                     }
-                                   );
-        Top.Add (_statusBar);
+                                   )
+        {
+            AlignmentModes = AlignmentModes.IgnoreFirstOrLast
+        };
+        app.Add (_statusBar);
+
+        _hexView.Source = LoadFile ();
+
+        Application.Run (app);
+        app.Dispose ();
+        Application.Shutdown ();
     }
 
     private void _hexView_Edited (object sender, HexViewEditEventArgs e) { _saved = false; }
@@ -109,16 +128,7 @@ public class HexEditor : Scenario
     private void _hexView_PositionChanged (object sender, HexViewEventArgs obj)
     {
         _siPositionChanged.Title =
-            $"Position: {
-                obj.Position
-            } Line: {
-                obj.CursorPosition.Y
-            } Col: {
-                obj.CursorPosition.X
-            } Line length: {
-                obj.BytesPerLine
-            }";
-        _statusBar.SetNeedsDisplay ();
+            $"Position: {obj.Position} Line: {obj.CursorPosition.Y} Col: {obj.CursorPosition.X} Line length: {obj.BytesPerLine}";
     }
 
     private void Copy () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
@@ -154,7 +164,7 @@ public class HexEditor : Scenario
     {
         var stream = new MemoryStream ();
 
-        if (!_saved && _hexView != null && _hexView.Edits.Count > 0)
+        if (!_saved && _hexView.Edits.Count > 0)
         {
             if (MessageBox.ErrorQuery (
                                        "Save",
@@ -175,12 +185,12 @@ public class HexEditor : Scenario
         {
             byte [] bin = File.ReadAllBytes (_fileName);
             stream.Write (bin);
-            Win.Title = GetName () + "-" + _fileName;
+            _hexView.Title = _fileName;
             _saved = true;
         }
         else
         {
-            Win.Title = GetName () + "-" + (_fileName ?? "Untitled");
+            _hexView.Title = (_fileName ?? "Untitled");
         }
 
         return stream;

+ 1 - 0
UICatalog/Scenarios/HotKeys.cs

@@ -123,5 +123,6 @@ public class HotKeys : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 31 - 35
UICatalog/Scenarios/InteractiveTree.cs

@@ -10,52 +10,48 @@ public class InteractiveTree : Scenario
 {
     private TreeView _treeView;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        Application.Init ();
+        var appWindow = new Toplevel ()
+        {
+            Title = GetName (),
+        };
 
         var menu = new MenuBar
         {
             Menus =
             [
-                new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", Quit) })
+                new ("_File", new MenuItem [] { new ("_Quit", "", Quit) })
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
-        _treeView = new TreeView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (1) };
+        _treeView = new ()
+        {
+            X = 0,
+            Y = 1,
+            Width = Dim.Fill (),
+            Height = Dim.Fill (1)
+        };
         _treeView.KeyDown += TreeView_KeyPress;
 
-        Win.Add (_treeView);
+        appWindow.Add (_treeView);
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
+                                       new Shortcut []
                                        {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                Quit
-                                               ),
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.C,
-                                                "~^C~ Add Child",
-                                                AddChildNode
-                                               ),
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.T,
-                                                "~^T~ Add Root",
-                                                AddRootNode
-                                               ),
-                                           new (
-                                                KeyCode.CtrlMask | KeyCode.R,
-                                                "~^R~ Rename Node",
-                                                RenameNode
-                                               )
+                                           new (Application.QuitKey, "Quit", Quit),
+                                           new (Key.C.WithCtrl, "Add Child", AddChildNode),
+                                           new (Key.T.WithCtrl, "Add Root", AddRootNode),
+                                           new (Key.R.WithCtrl, "Rename Node", RenameNode)
                                        }
                                       );
-        Top.Add (statusBar);
+        appWindow.Add (statusBar);
+
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+        Application.Shutdown ();
     }
 
     private void AddChildNode ()
@@ -87,10 +83,10 @@ public class InteractiveTree : Scenario
         var ok = new Button { Text = "Ok", IsDefault = true };
 
         ok.Accept += (s, e) =>
-                      {
-                          okPressed = true;
-                          Application.RequestStop ();
-                      };
+                     {
+                         okPressed = true;
+                         Application.RequestStop ();
+                     };
         var cancel = new Button { Text = "Cancel" };
         cancel.Accept += (s, e) => Application.RequestStop ();
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
@@ -128,7 +124,7 @@ public class InteractiveTree : Scenario
 
     private void TreeView_KeyPress (object sender, Key obj)
     {
-        if (obj.KeyCode == KeyCode.Delete)
+        if (obj.KeyCode == Key.Delete)
         {
             ITreeNode toDelete = _treeView.SelectedObject;
 

+ 1 - 0
UICatalog/Scenarios/LineCanvasExperiment.cs

@@ -139,5 +139,6 @@ public class LineCanvasExperiment : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 31 - 28
UICatalog/Scenarios/LineViewExample.cs

@@ -9,60 +9,60 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Borders")]
 public class LineViewExample : Scenario
 {
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        Application.Init ();
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
 
         var menu = new MenuBar
         {
             Menus =
             [
-                new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
+                new ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
-        Win.Add (new Label { Y = 0, Text = "Regular Line" });
+        appWindow.Add (new Label { Y = 1, Text = "Regular Line" });
 
         // creates a horizontal line
-        var line = new LineView { Y = 1 };
+        var line = new LineView { Y = 2 };
 
-        Win.Add (line);
+        appWindow.Add (line);
 
-        Win.Add (new Label { Y = 2, Text = "Double Width Line" });
+        appWindow.Add (new Label { Y = 3, Text = "Double Width Line" });
 
         // creates a horizontal line
-        var doubleLine = new LineView { Y = 3, LineRune = (Rune)'\u2550' };
+        var doubleLine = new LineView { Y = 4, LineRune = (Rune)'\u2550' };
 
-        Win.Add (doubleLine);
+        appWindow.Add (doubleLine);
 
-        Win.Add (new Label { Y = 4, Text = "Short Line" });
+        appWindow.Add (new Label { Y = 5, Text = "Short Line" });
 
         // creates a horizontal line
         var shortLine = new LineView { Y = 5, Width = 10 };
 
-        Win.Add (shortLine);
+        appWindow.Add (shortLine);
 
-        Win.Add (new Label { Y = 6, Text = "Arrow Line" });
+        appWindow.Add (new Label { Y = 7, Text = "Arrow Line" });
 
         // creates a horizontal line
         var arrowLine = new LineView
         {
-            Y = 7, Width = 10, StartingAnchor = CM.Glyphs.LeftTee, EndingAnchor = (Rune)'>'
+            Y = 8, Width = 10, StartingAnchor = CM.Glyphs.LeftTee, EndingAnchor = (Rune)'>'
         };
 
-        Win.Add (arrowLine);
+        appWindow.Add (arrowLine);
 
-        Win.Add (new Label { Y = 9, X = 11, Text = "Vertical Line" });
+        appWindow.Add (new Label { Y = 10, X = 11, Text = "Vertical Line" });
 
         // creates a horizontal line
         var verticalLine = new LineView (Orientation.Vertical) { X = 25 };
 
-        Win.Add (verticalLine);
+        appWindow.Add (verticalLine);
 
-        Win.Add (new Label { Y = 11, X = 28, Text = "Vertical Arrow" });
+        appWindow.Add (new Label { Y = 12, X = 28, Text = "Vertical Arrow" });
 
         // creates a horizontal line
         var verticalArrow = new LineView (Orientation.Vertical)
@@ -70,19 +70,22 @@ public class LineViewExample : Scenario
             X = 27, StartingAnchor = CM.Glyphs.TopTee, EndingAnchor = (Rune)'V'
         };
 
-        Win.Add (verticalArrow);
+        appWindow.Add (verticalArrow);
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
+                                       new Shortcut []
                                        {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
+                                           new (Application.QuitKey, "Quit", Quit)
                                        }
                                       );
-        Top.Add (statusBar);
+        appWindow.Add (statusBar);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     private void Quit () { Application.RequestStop (); }

+ 33 - 37
UICatalog/Scenarios/ListColumns.cs

@@ -47,19 +47,24 @@ public class ListColumns : Scenario
         return list;
     }
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
 
-        _listColView = new()
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ()
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+        };
+
+        _listColView = new ()
         {
             X = 0,
-            Y = 0,
+            Y = 1,
             Width = Dim.Fill (),
             Height = Dim.Fill (1),
-            Style = new()
+            Style = new ()
             {
                 ShowHeaders = false,
                 ShowHorizontalHeaderOverline = false,
@@ -209,36 +214,20 @@ public class ListColumns : Scenario
             ]
         };
 
-        Top.Add (menu);
+        appWindow.Add (menu);
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
+                                       new Shortcut []
                                        {
-                                           new (
-                                                KeyCode.F2,
-                                                "~F2~ OpenBigListEx",
-                                                () => OpenSimpleList (true)
-                                               ),
-                                           new (
-                                                KeyCode.F3,
-                                                "~F3~ CloseExample",
-                                                () => CloseExample ()
-                                               ),
-                                           new (
-                                                KeyCode.F4,
-                                                "~F4~ OpenSmListEx",
-                                                () => OpenSimpleList (false)
-                                               ),
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
+                                           new (Key.F2, "OpenBigListEx", () => OpenSimpleList (true)),
+                                           new (Key.F3, "CloseExample", CloseExample),
+                                           new (Key.F4, "OpenSmListEx", () => OpenSimpleList (false)),
+                                           new (Application.QuitKey, "Quit", Quit)
                                        }
                                       );
-        Top.Add (statusBar);
+        appWindow.Add (statusBar);
 
-        Win.Add (_listColView);
+        appWindow.Add (_listColView);
 
         var selectedCellLabel = new Label
         {
@@ -250,18 +239,18 @@ public class ListColumns : Scenario
             TextAlignment = Alignment.End
         };
 
-        Win.Add (selectedCellLabel);
+        appWindow.Add (selectedCellLabel);
 
         _listColView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{_listColView.SelectedRow},{_listColView.SelectedColumn}"; };
         _listColView.KeyDown += TableViewKeyPress;
 
         SetupScrollBar ();
 
-        _alternatingColorScheme = new()
+        _alternatingColorScheme = new ()
         {
-            Disabled = Win.ColorScheme.Disabled,
-            HotFocus = Win.ColorScheme.HotFocus,
-            Focus = Win.ColorScheme.Focus,
+            Disabled = appWindow.ColorScheme.Disabled,
+            HotFocus = appWindow.ColorScheme.HotFocus,
+            Focus = appWindow.ColorScheme.Focus,
             Normal = new (Color.White, Color.BrightBlue)
         };
 
@@ -269,6 +258,13 @@ public class ListColumns : Scenario
         _listColView.MouseClick += (s, e) => { _listColView.ScreenToCell (e.MouseEvent.Position, out int? clickedCol); };
 
         _listColView.KeyBindings.Add (Key.Space, Command.Accept);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     private void CloseExample () { _listColView.Table = null; }
@@ -370,7 +366,7 @@ public class ListColumns : Scenario
 
     private void TableViewKeyPress (object sender, Key e)
     {
-        if (e.KeyCode == KeyCode.Delete)
+        if (e.KeyCode == Key.Delete)
         {
             // set all selected cells to null
             foreach (Point pt in _listColView.GetAllSelectedCells ())

+ 4 - 3
UICatalog/Scenarios/Mouse.cs

@@ -18,7 +18,7 @@ public class Mouse : Scenario
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
         };
 
-        Slider<MouseFlags> filterSlider = new()
+        Slider<MouseFlags> filterSlider = new ()
         {
             Title = "_Filter",
             X = 0,
@@ -57,7 +57,7 @@ public class Mouse : Scenario
         win.Add (clearButton);
         Label ml;
         var count = 0;
-        ml = new() { X = Pos.Right (filterSlider), Y = 0, Text = "Mouse: " };
+        ml = new () { X = Pos.Right (filterSlider), Y = 0, Text = "Mouse: " };
 
         win.Add (ml);
 
@@ -138,7 +138,7 @@ public class Mouse : Scenario
                                       }
                                   };
 
-        label = new()
+        label = new ()
         {
             Text = "_Window Events:",
             X = Pos.Right (appLog) + 1,
@@ -184,6 +184,7 @@ public class Mouse : Scenario
 
         Application.Run (win);
         win.Dispose ();
+        Application.Shutdown ();
     }
 
     public class MouseDemo : View

+ 58 - 54
UICatalog/Scenarios/MultiColouredTable.cs

@@ -14,36 +14,33 @@ public class MultiColouredTable : Scenario
     private DataTable _table;
     private TableViewColors _tableView;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
 
-        _tableView = new TableViewColors { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (1) };
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ()
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+        };
+
+        _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
 
         var menu = new MenuBar
         {
             Menus =
             [
-                new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
+                new ("_File", new MenuItem [] { new ("_Quit", "", Quit) })
             ]
         };
-        Top.Add (menu);
-
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
-                                       }
-                                      );
-        Top.Add (statusBar);
-
-        Win.Add (_tableView);
+        appWindow.Add (menu);
+
+        var statusBar = new StatusBar (new Shortcut [] { new (Application.QuitKey, "Quit", Quit) });
+
+        appWindow.Add (statusBar);
+
+        appWindow.Add (_tableView);
 
         _tableView.CellActivated += EditCurrentCell;
 
@@ -58,15 +55,22 @@ public class MultiColouredTable : Scenario
         dt.Rows.Add (DBNull.Value, DBNull.Value);
         dt.Rows.Add (DBNull.Value, DBNull.Value);
 
-        _tableView.ColorScheme = new ColorScheme
+        _tableView.ColorScheme = new ()
         {
-            Disabled = Win.ColorScheme.Disabled,
-            HotFocus = Win.ColorScheme.HotFocus,
-            Focus = Win.ColorScheme.Focus,
-            Normal = new Attribute (Color.DarkGray, Color.Black)
+            Disabled = appWindow.ColorScheme.Disabled,
+            HotFocus = appWindow.ColorScheme.HotFocus,
+            Focus = appWindow.ColorScheme.Focus,
+            Normal = new (Color.DarkGray, Color.Black)
         };
 
         _tableView.Table = new DataTableSource (_table = dt);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     private void EditCurrentCell (object sender, CellActivatedEventArgs e)
@@ -101,10 +105,10 @@ public class MultiColouredTable : Scenario
         var ok = new Button { Text = "Ok", IsDefault = true };
 
         ok.Accept += (s, e) =>
-                      {
-                          okPressed = true;
-                          Application.RequestStop ();
-                      };
+                     {
+                         okPressed = true;
+                         Application.RequestStop ();
+                     };
         var cancel = new Button { Text = "Cancel" };
         cancel.Accept += (s, e) => { Application.RequestStop (); };
         var d = new Dialog { Title = title, Buttons = [ok, cancel] };
@@ -137,7 +141,7 @@ public class MultiColouredTable : Scenario
             {
                 if (unicorns != -1 && i >= unicorns && i <= unicorns + 8)
                 {
-                    Driver.SetAttribute (new Attribute (Color.White, cellColor.Background));
+                    Driver.SetAttribute (new (Color.White, cellColor.Background));
                 }
 
                 if (rainbows != -1 && i >= rainbows && i <= rainbows + 8)
@@ -147,60 +151,60 @@ public class MultiColouredTable : Scenario
                     switch (letterOfWord)
                     {
                         case 0:
-                            Driver.SetAttribute (new Attribute (Color.Red, cellColor.Background));
+                            Driver.SetAttribute (new (Color.Red, cellColor.Background));
 
                             break;
                         case 1:
                             Driver.SetAttribute (
-                                                 new Attribute (
-                                                                Color.BrightRed,
-                                                                cellColor.Background
-                                                               )
+                                                 new (
+                                                      Color.BrightRed,
+                                                      cellColor.Background
+                                                     )
                                                 );
 
                             break;
                         case 2:
                             Driver.SetAttribute (
-                                                 new Attribute (
-                                                                Color.BrightYellow,
-                                                                cellColor.Background
-                                                               )
+                                                 new (
+                                                      Color.BrightYellow,
+                                                      cellColor.Background
+                                                     )
                                                 );
 
                             break;
                         case 3:
-                            Driver.SetAttribute (new Attribute (Color.Green, cellColor.Background));
+                            Driver.SetAttribute (new (Color.Green, cellColor.Background));
 
                             break;
                         case 4:
                             Driver.SetAttribute (
-                                                 new Attribute (
-                                                                Color.BrightGreen,
-                                                                cellColor.Background
-                                                               )
+                                                 new (
+                                                      Color.BrightGreen,
+                                                      cellColor.Background
+                                                     )
                                                 );
 
                             break;
                         case 5:
                             Driver.SetAttribute (
-                                                 new Attribute (
-                                                                Color.BrightBlue,
-                                                                cellColor.Background
-                                                               )
+                                                 new (
+                                                      Color.BrightBlue,
+                                                      cellColor.Background
+                                                     )
                                                 );
 
                             break;
                         case 6:
                             Driver.SetAttribute (
-                                                 new Attribute (
-                                                                Color.BrightCyan,
-                                                                cellColor.Background
-                                                               )
+                                                 new (
+                                                      Color.BrightCyan,
+                                                      cellColor.Background
+                                                     )
                                                 );
 
                             break;
                         case 7:
-                            Driver.SetAttribute (new Attribute (Color.Cyan, cellColor.Background));
+                            Driver.SetAttribute (new (Color.Cyan, cellColor.Background));
 
                             break;
                     }

+ 54 - 54
UICatalog/Scenarios/Notepad.cs

@@ -11,7 +11,7 @@ namespace UICatalog.Scenarios;
 public class Notepad : Scenario
 {
     private TabView _focusedTabView;
-    private StatusItem _lenStatusItem;
+    public Shortcut LenShortcut { get; private set; } 
     private int _numNewTabs = 1;
     private TabView _tabView;
 
@@ -39,11 +39,11 @@ public class Notepad : Scenario
                               | KeyCode.CtrlMask
                               | KeyCode.AltMask
                              ),
-                         new ("_Open", "", () => Open ()),
-                         new ("_Save", "", () => Save ()),
+                         new ("_Open", "", Open),
+                         new ("_Save", "", Save),
                          new ("Save _As", "", () => SaveAs ()),
-                         new ("_Close", "", () => Close ()),
-                         new ("_Quit", "", () => Quit ())
+                         new ("_Close", "", Close),
+                         new ("_Quit", "", Quit)
                      }
                     ),
                 new (
@@ -66,33 +66,31 @@ public class Notepad : Scenario
         split.LineStyle = LineStyle.None;
 
         top.Add (split);
-
-        _lenStatusItem = new (KeyCode.CharMask, "Len: ", null);
-
-        var statusBar = new StatusBar (
-                                       new []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               ),
-
-                                           // These shortcut keys don't seem to work correctly in linux 
-                                           //new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),
-                                           //new StatusItem(Key.CtrlMask | Key.N, "~^N~ New", () => New()),
-
-                                           new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
-                                           new (KeyCode.CtrlMask | KeyCode.W, "~^W~ Close", () => Close ()),
-                                           _lenStatusItem
+        LenShortcut = new (Key.Empty, "Len: ", null);
+
+        var statusBar = new StatusBar (new [] {
+                                           new (Application.QuitKey, $"Quit", Quit),
+                                           new Shortcut(Key.F2, "Open", Open),
+                                           new Shortcut(Key.F1, "New", New),
+                                           new (Key.F3, "Save", Save),
+                                           new (Key.F6, "Close", Close),
+                                           LenShortcut
                                        }
-                                      );
+                                      )
+        {
+            AlignmentModes = AlignmentModes.IgnoreFirstOrLast
+        };
+        top.Add (statusBar);
+
         _focusedTabView = _tabView;
         _tabView.SelectedTabChanged += TabView_SelectedTabChanged;
         _tabView.Enter += (s, e) => _focusedTabView = _tabView;
 
-        top.Add (statusBar);
-        top.Ready += (s, e) => New ();
+        top.Ready += (s, e) =>
+                     {
+                         New ();
+                         LenShortcut.Title = $"Len:{_focusedTabView.Text?.Length ?? 0}";
+                     };
 
         Application.Run (top);
         top.Dispose ();
@@ -279,7 +277,7 @@ public class Notepad : Scenario
     /// <param name="fileInfo">File that was read or null if a new blank document</param>
     private void Open (FileInfo fileInfo, string tabName)
     {
-        var tab = new OpenedFile { DisplayText = tabName, File = fileInfo };
+        var tab = new OpenedFile (this) { DisplayText = tabName, File = fileInfo };
         tab.View = tab.CreateTextView (fileInfo);
         tab.SavedText = tab.View.Text;
         tab.RegisterTextViewEvents (_focusedTabView);
@@ -323,7 +321,8 @@ public class Notepad : Scenario
 
     private void TabView_SelectedTabChanged (object sender, TabChangedEventArgs e)
     {
-        _lenStatusItem.Title = $"Len:{e.NewTab?.View?.Text?.Length ?? 0}";
+        LenShortcut.Title = $"Len:{e.NewTab?.View?.Text?.Length ?? 0}";
+
         e.NewTab?.View?.SetFocus ();
     }
 
@@ -370,11 +369,13 @@ public class Notepad : Scenario
         e.MouseEvent.Handled = true;
     }
 
-    private class OpenedFile : Tab
+    private class OpenedFile (Notepad notepad) : Tab
     {
+        private Notepad _notepad = notepad;
+
         public OpenedFile CloneTo (TabView other)
         {
-            var newTab = new OpenedFile { DisplayText = base.Text, File = File };
+            var newTab = new OpenedFile (_notepad) { DisplayText = base.Text, File = File };
             newTab.View = newTab.CreateTextView (newTab.File);
             newTab.SavedText = newTab.View.Text;
             newTab.RegisterTextViewEvents (other);
@@ -410,28 +411,27 @@ public class Notepad : Scenario
             var textView = (TextView)View;
 
             // when user makes changes rename tab to indicate unsaved
-            textView.KeyUp += (s, k) =>
-                              {
-                                  // if current text doesn't match saved text
-                                  bool areDiff = UnsavedChanges;
-
-                                  if (areDiff)
-                                  {
-                                      if (!Text.EndsWith ('*'))
-                                      {
-                                          Text = Text + '*';
-                                          parent.SetNeedsDisplay ();
-                                      }
-                                  }
-                                  else
-                                  {
-                                      if (Text.EndsWith ('*'))
-                                      {
-                                          Text = Text.TrimEnd ('*');
-                                          parent.SetNeedsDisplay ();
-                                      }
-                                  }
-                              };
+            textView.ContentsChanged += (s, k) =>
+                                        {
+                                            // if current text doesn't match saved text
+                                            bool areDiff = UnsavedChanges;
+
+                                            if (areDiff)
+                                            {
+                                                if (!DisplayText.EndsWith ('*'))
+                                                {
+                                                    DisplayText = Text + '*';
+                                                }
+                                            }
+                                            else
+                                            {
+                                                if (DisplayText.EndsWith ('*'))
+                                                {
+                                                    DisplayText = Text.TrimEnd ('*');
+                                                }
+                                            }
+                                            _notepad.LenShortcut.Title = $"Len:{textView.Text.Length}";
+                                        };
         }
 
         /// <summary>The text of the tab the last time it was saved</summary>
@@ -452,7 +452,7 @@ public class Notepad : Scenario
             System.IO.File.WriteAllText (File.FullName, newText);
             SavedText = newText;
 
-            Text = Text.TrimEnd ('*');
+            DisplayText = DisplayText.TrimEnd ('*');
         }
     }
 }

+ 19 - 22
UICatalog/Scenarios/RunTExample.cs

@@ -6,16 +6,13 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Top Level Windows")]
 public class RunTExample : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
         // No need to call Init if Application.Run<T> is used
-        Application.Run<ExampleWindow> ();
-
-        Application.Top.Dispose ();
+        Application.Run<ExampleWindow> ().Dispose ();
+        Application.Shutdown ();
     }
 
-    public override void Run () { }
-
     public class ExampleWindow : Window
     {
         private readonly TextField _usernameText;
@@ -27,7 +24,7 @@ public class RunTExample : Scenario
             // Create input components and labels
             var usernameLabel = new Label { Text = "Username:" };
 
-            _usernameText = new TextField
+            _usernameText = new()
             {
                 // Position text field adjacent to the label
                 X = Pos.Right (usernameLabel) + 1,
@@ -64,21 +61,21 @@ public class RunTExample : Scenario
 
             // When login button is clicked display a message popup
             btnLogin.Accept += (s, e) =>
-                                {
-                                    if (_usernameText.Text == "admin" && passwordText.Text == "password")
-                                    {
-                                        MessageBox.Query ("Login Successful", $"Username: {_usernameText.Text}", "Ok");
-                                        Application.RequestStop ();
-                                    }
-                                    else
-                                    {
-                                        MessageBox.ErrorQuery (
-                                                               "Error Logging In",
-                                                               "Incorrect username or password (hint: admin/password)",
-                                                               "Ok"
-                                                              );
-                                    }
-                                };
+                               {
+                                   if (_usernameText.Text == "admin" && passwordText.Text == "password")
+                                   {
+                                       MessageBox.Query ("Login Successful", $"Username: {_usernameText.Text}", "Ok");
+                                       Application.RequestStop ();
+                                   }
+                                   else
+                                   {
+                                       MessageBox.ErrorQuery (
+                                                              "Error Logging In",
+                                                              "Incorrect username or password (hint: admin/password)",
+                                                              "Ok"
+                                                             );
+                                   }
+                               };
 
             // Add the views to the Window
             Add (usernameLabel, _usernameText, passwordLabel, passwordText, btnLogin);

+ 6 - 5
UICatalog/Scenarios/RuneWidthGreaterThanOne.cs

@@ -17,11 +17,11 @@ public class RuneWidthGreaterThanOne : Scenario
     private TextField _text;
     private Window _win;
 
-    public override void Init ()
+    public override void Main ()
     {
         Application.Init ();
 
-        Top = new ();
+        Toplevel topLevel = new ();
 
         var menu = new MenuBar
         {
@@ -87,16 +87,17 @@ public class RuneWidthGreaterThanOne : Scenario
         };
         _win = new Window { X = 5, Y = 5, Width = Dim.Fill (22), Height = Dim.Fill (5) };
         _win.Add (_label, _text, _button, _labelR, _labelV);
-        Top.Add (menu, _win);
+        topLevel.Add (menu, _win);
 
         WideRunes ();
 
         //NarrowRunes ();
         //MixedRunes ();
-        Application.Run (Top);
+        Application.Run (topLevel);
+        topLevel.Dispose ();
+        Application.Shutdown ();
     }
 
-    public override void Run () { }
     private void MixedMessage (object sender, EventArgs e) { MessageBox.Query ("Say Hello 你", $"Hello {_text.Text}", "Ok"); }
 
     private void MixedRunes ()

+ 1 - 0
UICatalog/Scenarios/Scrolling.cs

@@ -247,6 +247,7 @@ public class Scrolling : Scenario
         app.Loaded -= App_Loaded;
         app.Unloaded -= app_Unloaded;
         app.Dispose ();
+        Application.Shutdown ();
 
         return;
 

+ 372 - 0
UICatalog/Scenarios/Shortcuts.cs

@@ -0,0 +1,372 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Timers;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Shortcuts", "Illustrates Shortcut class.")]
+[ScenarioCategory ("Controls")]
+public class Shortcuts : Scenario
+{
+    public override void Main ()
+    {
+        Application.Init ();
+        Window app = new ();
+
+        app.Loaded += App_Loaded;
+
+        Application.Run (app);
+        app.Dispose ();
+        Application.Shutdown ();
+    }
+
+    // Setting everything up in Loaded handler because we change the
+    // QuitKey and it only sticks if changed after init
+    private void App_Loaded (object sender, EventArgs e)
+    {
+        Application.QuitKey = Key.Z.WithCtrl;
+        Application.Top.Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}";
+
+        ObservableCollection<string> eventSource = new ();
+
+        var eventLog = new ListView
+        {
+            X = Pos.AnchorEnd (),
+            Width = 40,
+            Height = Dim.Fill (4),
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            Source = new ListWrapper<string> (eventSource),
+            BorderStyle = LineStyle.Double,
+            Title = "E_vents"
+        };
+        Application.Top.Add (eventLog);
+
+        var vShortcut1 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Width = 35,
+            Title = "A_pp Shortcut",
+            Key = Key.F1,
+            Text = "Width is 35",
+            KeyBindingScope = KeyBindingScope.Application,
+        };
+        Application.Top.Add (vShortcut1);
+
+        var vShortcut2 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut1),
+            Width = 35,
+            Key = Key.F2,
+            Text = "Width is 35",
+            KeyBindingScope = KeyBindingScope.HotKey,
+            CommandView = new RadioGroup
+            {
+                Orientation = Orientation.Vertical,
+                RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"]
+            }
+        };
+
+        ((RadioGroup)vShortcut2.CommandView).SelectedItemChanged += (o, args) =>
+                                                                   {
+                                                                       eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
+                                                                       eventLog.MoveDown ();
+                                                                   };
+
+        vShortcut2.Accept += (o, args) =>
+                            {
+                                // Cycle to next item. If at end, set 0
+                                if (((RadioGroup)vShortcut2.CommandView).SelectedItem < ((RadioGroup)vShortcut2.CommandView).RadioLabels.Length - 1)
+                                {
+                                    ((RadioGroup)vShortcut2.CommandView).SelectedItem++;
+                                }
+                                else
+                                {
+                                    ((RadioGroup)vShortcut2.CommandView).SelectedItem = 0;
+                                }
+                            };
+        Application.Top.Add (vShortcut2);
+
+        var vShortcut3 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut2),
+            CommandView = new CheckBox { Text = "_Align" },
+            Key = Key.F5.WithCtrl.WithAlt.WithShift,
+            HelpText = "Width is Fill",
+            Width = Dim.Fill () - Dim.Width (eventLog),
+            KeyBindingScope = KeyBindingScope.HotKey,
+        };
+
+        ((CheckBox)vShortcut3.CommandView).Toggled += (s, e) =>
+                                                      {
+                                                          if (vShortcut3.CommandView is CheckBox cb)
+                                                          {
+                                                              eventSource.Add ($"Toggled: {cb.Text}");
+                                                              eventLog.MoveDown ();
+
+                                                              var max = 0;
+                                                              var toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Orientation: Orientation.Vertical, Width: not DimAbsolute });
+
+                                                              if (e.NewValue == true)
+                                                              {
+                                                                  foreach (Shortcut peer in toAlign)
+                                                                  {
+                                                                      // DANGER: KeyView is internal so we can't access it. So we assume this is how it works.
+                                                                      max = Math.Max (max, peer.Key.ToString ().GetColumns ());
+                                                                  }
+                                                              }
+
+                                                              foreach (Shortcut peer in toAlign)
+                                                              {
+                                                                  peer.MinimumKeyTextSize = max;
+                                                              }
+                                                          }
+                                                      };
+        Application.Top.Add (vShortcut3);
+
+        var vShortcut4 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut3),
+            Width = Dim.Width (vShortcut3),
+            CommandView = new Button
+            {
+                Title = "B_utton",
+            },
+            HelpText = "Width is Fill",
+            Key = Key.K,
+            KeyBindingScope = KeyBindingScope.HotKey,
+        };
+        Button button = (Button)vShortcut4.CommandView;
+        vShortcut4.CommandView.Accept += Button_Clicked;
+
+        Application.Top.Add (vShortcut4);
+
+        var vShortcut5 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut4),
+            Width = Dim.Width (vShortcut4),
+
+            Key = Key.F4,
+            HelpText = "CommandView.CanFocus",
+            KeyBindingScope = KeyBindingScope.HotKey,
+            CommandView = new CheckBox { Text = "_CanFocus" },
+        };
+
+        ((CheckBox)vShortcut5.CommandView).Toggled += (s, e) =>
+                                                     {
+                                                         if (vShortcut5.CommandView is CheckBox cb)
+                                                         {
+                                                             eventSource.Add ($"Toggled: {cb.Text}");
+                                                             eventLog.MoveDown ();
+
+                                                             foreach (Shortcut peer in Application.Top.Subviews.Where (v => v is Shortcut)!)
+                                                             {
+                                                                 if (peer.CanFocus)
+                                                                 {
+                                                                     peer.CommandView.CanFocus = e.NewValue == true;
+                                                                 }
+                                                             }
+                                                         }
+                                                     };
+        Application.Top.Add (vShortcut5);
+
+        var vShortcutSlider = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut5),
+            HelpText = "Width is Fill",
+            Width = Dim.Width (vShortcut5),
+
+            KeyBindingScope = KeyBindingScope.HotKey,
+            CommandView = new Slider<string>
+            {
+                Orientation = Orientation.Vertical,
+                AllowEmpty = true
+            },
+            Key = Key.F5,
+        };
+
+        ((Slider<string>)vShortcutSlider.CommandView).Options = new () { new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" } };
+        ((Slider<string>)vShortcutSlider.CommandView).SetOption (0);
+
+        ((Slider<string>)vShortcutSlider.CommandView).OptionsChanged += (o, args) =>
+                                                                       {
+                                                                           eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider<string>)o).GetSetOptions ())}");
+                                                                           eventLog.MoveDown ();
+                                                                       };
+
+        Application.Top.Add (vShortcutSlider);
+
+        var vShortcut6 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcutSlider),
+            Width = Dim.Width (vShortcutSlider),
+
+            Title = "_No Key",
+            HelpText = "Keyless",
+        };
+
+        Application.Top.Add (vShortcut6);
+
+
+        var vShortcut7 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut6),
+            Width = Dim.Width (vShortcutSlider),
+            Key = Key.F6,
+            Title = "Not _very much help",
+            HelpText = "",
+        };
+
+        Application.Top.Add (vShortcut7);
+        vShortcut7.SetFocus ();
+
+
+        // Horizontal
+        var hShortcut1 = new Shortcut
+        {
+            X = Pos.Align (Alignment.Start, AlignmentModes.IgnoreFirstOrLast, 1),
+            Y = Pos.Bottom (eventLog) + 1,
+            Key = Key.F7,
+            HelpText = "Horizontal",
+            CanFocus = false
+        };
+
+        hShortcut1.CommandView = new ProgressBar
+        {
+            Text = "Progress",
+            Title = "P",
+            Fraction = 0.5f,
+            Width = 10,
+            Height = 1,
+            ProgressBarStyle = ProgressBarStyle.Continuous
+        };
+        hShortcut1.CommandView.Width = 10;
+        hShortcut1.CommandView.Height = 1;
+        hShortcut1.CommandView.CanFocus = false;
+
+        Timer timer = new (10)
+        {
+            AutoReset = true,
+        };
+        timer.Elapsed += (o, args) =>
+                         {
+                             if (hShortcut1.CommandView is ProgressBar pb)
+                             {
+                                 if (pb.Fraction == 1.0)
+                                 {
+                                     pb.Fraction = 0;
+                                 }
+                                 pb.Fraction += 0.01f;
+
+                                 Application.Wakeup ();
+
+                                 pb.SetNeedsDisplay ();
+                             }
+                         };
+        timer.Start ();
+
+        Application.Top.Add (hShortcut1);
+
+        var textField = new TextField ()
+        {
+            Text = "Edit me",
+            Width = 10,
+            Height = 1,
+            CanFocus = true
+        };
+
+        var hShortcut2 = new Shortcut
+        {
+            Orientation = Orientation.Horizontal,
+            X = Pos.Align (Alignment.Start, AlignmentModes.IgnoreFirstOrLast, 1),
+            Y = Pos.Top (hShortcut1),
+            Key = Key.F8,
+            HelpText = "TextField",
+            CanFocus = true,
+            CommandView = textField,
+        };
+
+        Application.Top.Add (hShortcut2);
+
+        var hShortcutBG = new Shortcut
+        {
+            Orientation = Orientation.Horizontal,
+            X = Pos.Align (Alignment.Start, AlignmentModes.IgnoreFirstOrLast, 1) - 1,
+            Y = Pos.Top (hShortcut2),
+            Key = Key.F9,
+            HelpText = "BG Color",
+            CanFocus = false
+        };
+
+        var bgColor = new ColorPicker ()
+        {
+            CanFocus = false,
+            BoxHeight = 1,
+            BoxWidth = 1,
+        };
+        bgColor.ColorChanged += (o, args) =>
+                                {
+                                    Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme)
+                                    {
+                                        Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.Color),
+                                    };
+                                };
+        hShortcutBG.CommandView = bgColor;
+
+        Application.Top.Add (hShortcutBG);
+
+        var hShortcut3 = new Shortcut
+        {
+            Orientation = Orientation.Horizontal,
+            X = Pos.Align (Alignment.Start, AlignmentModes.IgnoreFirstOrLast, 1),
+            Y = Pos.Top (hShortcut2),
+            Key = Key.Esc,
+            KeyBindingScope = KeyBindingScope.Application,
+            Title = "Quit",
+            HelpText = "App Scope",
+            CanFocus = false
+        };
+        hShortcut3.Accept += (o, args) =>
+                            {
+                                Application.RequestStop ();
+                            };
+
+        Application.Top.Add (hShortcut3);
+
+        foreach (View sh in Application.Top.Subviews.Where (v => v is Shortcut)!)
+        {
+            if (sh is Shortcut shortcut)
+            {
+                shortcut.Accept += (o, args) =>
+                                   {
+                                       eventSource.Add ($"Accept: {shortcut!.CommandView.Text}");
+                                       eventLog.MoveDown ();
+                                       args.Cancel = true;
+                                   };
+            }
+        }
+
+        //((CheckBox)vShortcut5.CommandView).OnToggled ();
+    }
+
+    private void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+}

+ 86 - 116
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -12,15 +12,12 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Top Level Windows")]
 public class SingleBackgroundWorker : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
-        Application.Run<MainApp> ();
-
-        Application.Top.Dispose ();
+        Application.Run<MainApp> ().Dispose ();
+        Application.Shutdown ();
     }
 
-    public override void Run () { }
-
     public class MainApp : Toplevel
     {
         private readonly ListView _listLog;
@@ -34,57 +31,47 @@ public class SingleBackgroundWorker : Scenario
             {
                 Menus =
                 [
-                    new MenuBarItem (
-                                     "_Options",
-                                     new MenuItem []
-                                     {
-                                         new (
-                                              "_Run Worker",
-                                              "",
-                                              () => RunWorker (),
-                                              null,
-                                              null,
-                                              KeyCode.CtrlMask | KeyCode.R
-                                             ),
-                                         null,
-                                         new (
-                                              "_Quit",
-                                              "",
-                                              () => Application.RequestStop (),
-                                              null,
-                                              null,
-                                              KeyCode.CtrlMask | KeyCode.Q
-                                             )
-                                     }
-                                    )
+                    new (
+                         "_Options",
+                         new MenuItem []
+                         {
+                             new (
+                                  "_Run Worker",
+                                  "",
+                                  () => RunWorker (),
+                                  null,
+                                  null,
+                                  KeyCode.CtrlMask | KeyCode.R
+                                 ),
+                             null,
+                             new (
+                                  "_Quit",
+                                  "",
+                                  () => Application.RequestStop (),
+                                  null,
+                                  null,
+                                  KeyCode.CtrlMask | KeyCode.Q
+                                 )
+                         }
+                        )
                 ]
             };
             Add (menu);
 
             var statusBar = new StatusBar (
-                                           new []
-                                           {
-                                               new StatusItem (
-                                                               Application.QuitKey,
-                                                               $"{Application.QuitKey} to Quit",
-                                                               () => Application.RequestStop ()
-                                                              ),
-                                               new StatusItem (
-                                                               KeyCode.CtrlMask | KeyCode.P,
-                                                               "~^R~ Run Worker",
-                                                               () => RunWorker ()
-                                                              )
-                                           }
-                                          );
+                                           [
+                                               new (Application.QuitKey, "Quit", () => Application.RequestStop ()),
+                                               new (Key.R.WithCtrl, "Run Worker", RunWorker)
+                                           ]);
             Add (statusBar);
 
-            var workerLogTop = new Toplevel () { Title = "Worker Log Top"};
+            var workerLogTop = new Toplevel { Title = "Worker Log Top" };
 
             workerLogTop.Add (
-                     new Label { X = Pos.Center (), Y = 0, Text = "Worker Log" }
-                    );
+                              new Label { X = Pos.Center (), Y = 0, Text = "Worker Log" }
+                             );
 
-            _listLog = new ListView
+            _listLog = new()
             {
                 X = 0,
                 Y = 2,
@@ -99,26 +86,26 @@ public class SingleBackgroundWorker : Scenario
 
         private void RunWorker ()
         {
-            _worker = new BackgroundWorker { WorkerSupportsCancellation = true };
+            _worker = new() { WorkerSupportsCancellation = true };
 
             var cancel = new Button { Text = "Cancel Worker" };
 
             cancel.Accept += (s, e) =>
-                              {
-                                  if (_worker == null)
-                                  {
-                                      _log.Add ($"Worker is not running at {DateTime.Now}!");
-                                      _listLog.SetNeedsDisplay ();
+                             {
+                                 if (_worker == null)
+                                 {
+                                     _log.Add ($"Worker is not running at {DateTime.Now}!");
+                                     _listLog.SetNeedsDisplay ();
 
-                                      return;
-                                  }
+                                     return;
+                                 }
 
-                                  _log.Add (
-                                            $"Worker {_startStaging}.{_startStaging:fff} is canceling at {DateTime.Now}!"
-                                           );
-                                  _listLog.SetNeedsDisplay ();
-                                  _worker.CancelAsync ();
-                              };
+                                 _log.Add (
+                                           $"Worker {_startStaging}.{_startStaging:fff} is canceling at {DateTime.Now}!"
+                                          );
+                                 _listLog.SetNeedsDisplay ();
+                                 _worker.CancelAsync ();
+                             };
 
             _startStaging = DateTime.Now;
             _log.Add ($"Worker is started at {_startStaging}.{_startStaging:fff}");
@@ -164,15 +151,7 @@ public class SingleBackgroundWorker : Scenario
                                               {
                                                   // Failed
                                                   _log.Add (
-                                                            $"Exception occurred {
-                                                                e.Error.Message
-                                                            } on Worker {
-                                                                _startStaging
-                                                            }.{
-                                                                _startStaging
-                                                                :fff} at {
-                                                                DateTime.Now
-                                                            }"
+                                                            $"Exception occurred {e.Error.Message} on Worker {_startStaging}.{_startStaging:fff} at {DateTime.Now}"
                                                            );
                                                   _listLog.SetNeedsDisplay ();
                                               }
@@ -195,7 +174,7 @@ public class SingleBackgroundWorker : Scenario
 
                                                   var builderUI =
                                                       new StagingUIController (_startStaging, e.Result as ObservableCollection<string>);
-                                                  var top = Application.Top;
+                                                  Toplevel top = Application.Top;
                                                   top.Visible = false;
                                                   Application.Current.Visible = false;
                                                   builderUI.Load ();
@@ -217,7 +196,7 @@ public class SingleBackgroundWorker : Scenario
 
         public StagingUIController (DateTime? start, ObservableCollection<string> list)
         {
-            _top = new Toplevel
+            _top = new()
             {
                 Title = "_top", Width = Dim.Fill (), Height = Dim.Fill ()
             };
@@ -250,46 +229,44 @@ public class SingleBackgroundWorker : Scenario
             {
                 Menus =
                 [
-                    new MenuBarItem (
-                                     "_Stage",
-                                     new MenuItem []
-                                     {
-                                         new (
-                                              "_Close",
-                                              "",
-                                              () =>
-                                              {
-                                                  if (Close ())
-                                                  {
-                                                      Application.RequestStop ();
-                                                  }
-                                              },
-                                              null,
-                                              null,
-                                              KeyCode.CtrlMask | KeyCode.C
-                                             )
-                                     }
-                                    )
+                    new (
+                         "_Stage",
+                         new MenuItem []
+                         {
+                             new (
+                                  "_Close",
+                                  "",
+                                  () =>
+                                  {
+                                      if (Close ())
+                                      {
+                                          Application.RequestStop ();
+                                      }
+                                  },
+                                  null,
+                                  null,
+                                  KeyCode.CtrlMask | KeyCode.C
+                                 )
+                         }
+                        )
                 ]
             };
             _top.Add (menu);
 
             var statusBar = new StatusBar (
-                                           new []
-                                           {
-                                               new StatusItem (
-                                                               KeyCode.CtrlMask | KeyCode.C,
-                                                               "~^C~ Close",
-                                                               () =>
-                                                               {
-                                                                   if (Close ())
-                                                                   {
-                                                                       Application.RequestStop ();
-                                                                   }
-                                                               }
-                                                              )
-                                           }
-                                          );
+                                           [
+                                               new (
+                                                    Key.C.WithCtrl,
+                                                    "Close",
+                                                    () =>
+                                                    {
+                                                        if (Close ())
+                                                        {
+                                                            Application.RequestStop ();
+                                                        }
+                                                    }
+                                                   )
+                                           ]);
             _top.Add (statusBar);
 
             Title = $"Worker started at {start}.{start:fff}";
@@ -309,18 +286,11 @@ public class SingleBackgroundWorker : Scenario
             _top.Add (this);
         }
 
-        public void Load () {
+        public void Load ()
+        {
             Application.Run (_top);
             _top.Dispose ();
             _top = null;
         }
-
-        ///// <inheritdoc />
-        //protected override void Dispose (bool disposing)
-        //{
-        //    _top?.Dispose ();
-        //    _top = null;
-        //    base.Dispose (disposing);
-        //}
     }
 }

+ 36 - 5
UICatalog/Scenarios/Sliders.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Tracing;
 using System.Linq;
 using System.Text;
 using Terminal.Gui;
@@ -242,7 +244,7 @@ public class Sliders : Scenario
                                       }
                                   };
         configView.Add (dimAutoUsesMin);
-    
+
         #region Slider Orientation Slider
 
         Slider<string> orientationSlider = new (new List<string> { "Horizontal", "Vertical" })
@@ -393,7 +395,7 @@ public class Sliders : Scenario
         FrameView spacingOptions = new ()
         {
             Title = "Spacing Options",
-            X = Pos.Right(orientationSlider),
+            X = Pos.Right (orientationSlider),
             Y = Pos.Top (orientationSlider),
             Width = Dim.Fill (),
             Height = Dim.Auto (),
@@ -407,7 +409,7 @@ public class Sliders : Scenario
 
         Buttons.NumericUpDown<int> innerSpacingUpDown = new ()
         {
-           X = Pos.Right(label) + 1
+            X = Pos.Right (label) + 1
         };
 
         innerSpacingUpDown.Value = app.Subviews.OfType<Slider> ().First ().MinimumInnerSpacing;
@@ -429,8 +431,8 @@ public class Sliders : Scenario
 
 
 
-        spacingOptions.Add(label, innerSpacingUpDown);
-        configView.Add(spacingOptions);
+        spacingOptions.Add (label, innerSpacingUpDown);
+        configView.Add (spacingOptions);
 
         #endregion
 
@@ -564,6 +566,35 @@ public class Sliders : Scenario
 
         #endregion Config Slider
 
+        ObservableCollection<string> eventSource = new ();
+        var eventLog = new ListView
+        {
+            X = Pos.Right (sliderBGColor),
+            Y = Pos.Bottom (spacingOptions),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            Source = new ListWrapper<string> (eventSource)
+        };
+        configView.Add (eventLog);
+
+
+        foreach (Slider slider in app.Subviews.Where (v => v is Slider)!)
+        {
+            slider.Accept += (o, args) =>
+                             {
+                                 eventSource.Add ($"Accept: {string.Join(",", slider.GetSetOptions ())}");
+                                 eventLog.MoveDown ();
+                                 args.Cancel = true;
+                             };
+            slider.OptionsChanged += (o, args) =>
+                             {
+                                 eventSource.Add ($"OptionsChanged: {string.Join (",", slider.GetSetOptions ())}");
+                                 eventLog.MoveDown ();
+                                 args.Cancel = true;
+                             };
+        }
+
         app.FocusFirst ();
 
         Application.Run (app);

+ 65 - 58
UICatalog/Scenarios/SyntaxHighlighting.cs

@@ -120,70 +120,77 @@ public class SyntaxHighlighting : Scenario
         }
     }
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
 
         var menu = new MenuBar
         {
             Menus =
             [
-                new MenuBarItem (
-                                 "_TextView",
-                                 new []
-                                 {
-                                     _miWrap = new MenuItem (
-                                                             "_Word Wrap",
-                                                             "",
-                                                             () => WordWrap ()
-                                                            )
-                                     {
-                                         CheckType = MenuItemCheckStyle
-                                             .Checked
-                                     },
-                                     null,
-                                     new (
-                                          "_Syntax Highlighting",
-                                          "",
-                                          () => ApplySyntaxHighlighting ()
-                                         ),
-                                     null,
-                                     new (
-                                          "_Load Rune Cells",
-                                          "",
-                                          () => ApplyLoadRuneCells ()
-                                         ),
-                                     new (
-                                          "_Save Rune Cells",
-                                          "",
-                                          () => SaveRuneCells ()
-                                         ),
-                                     null,
-                                     new ("_Quit", "", () => Quit ())
-                                 }
-                                )
+                new (
+                     "_TextView",
+                     new []
+                     {
+                         _miWrap = new (
+                                        "_Word Wrap",
+                                        "",
+                                        () => WordWrap ()
+                                       )
+                         {
+                             CheckType = MenuItemCheckStyle
+                                 .Checked
+                         },
+                         null,
+                         new (
+                              "_Syntax Highlighting",
+                              "",
+                              () => ApplySyntaxHighlighting ()
+                             ),
+                         null,
+                         new (
+                              "_Load Rune Cells",
+                              "",
+                              () => ApplyLoadRuneCells ()
+                             ),
+                         new (
+                              "_Save Rune Cells",
+                              "",
+                              () => SaveRuneCells ()
+                             ),
+                         null,
+                         new ("_Quit", "", () => Quit ())
+                     }
+                    )
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
-        _textView = new TextView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
+        _textView = new()
+        {
+            Y = 1,
+            Width = Dim.Fill (),
+            Height = Dim.Fill (1)
+        };
 
         ApplySyntaxHighlighting ();
 
-        Win.Add (_textView);
-
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
-                                       }
-                                      );
+        appWindow.Add (_textView);
+
+        var statusBar = new StatusBar ([new (Application.QuitKey, "Quit", Quit)]);
+
+        appWindow.Add (statusBar);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
 
-        Top.Add (statusBar);
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     /// <summary>
@@ -236,10 +243,10 @@ public class SyntaxHighlighting : Scenario
 
             foreach (Rune rune in csName.EnumerateRunes ())
             {
-                runeCells.Add (new RuneCell { Rune = rune, ColorScheme = color.Value });
+                runeCells.Add (new() { Rune = rune, ColorScheme = color.Value });
             }
 
-            runeCells.Add (new RuneCell { Rune = (Rune)'\n', ColorScheme = color.Value });
+            runeCells.Add (new() { Rune = (Rune)'\n', ColorScheme = color.Value });
         }
 
         if (File.Exists (_path))
@@ -260,10 +267,10 @@ public class SyntaxHighlighting : Scenario
     {
         ClearAllEvents ();
 
-        _green = new ColorScheme (new Attribute (Color.Green, Color.Black));
-        _blue = new ColorScheme (new Attribute (Color.Blue, Color.Black));
-        _magenta = new ColorScheme (new Attribute (Color.Magenta, Color.Black));
-        _white = new ColorScheme (new Attribute (Color.White, Color.Black));
+        _green = new (new Attribute (Color.Green, Color.Black));
+        _blue = new (new Attribute (Color.Blue, Color.Black));
+        _magenta = new (new Attribute (Color.Magenta, Color.Black));
+        _white = new (new Attribute (Color.White, Color.Black));
         _textView.ColorScheme = _white;
 
         _textView.Text =
@@ -342,7 +349,7 @@ public class SyntaxHighlighting : Scenario
     private string IdxToWord (List<Rune> line, int idx)
     {
         string [] words = Regex.Split (
-                                       new string (line.Select (r => (char)r.Value).ToArray ()),
+                                       new (line.Select (r => (char)r.Value).ToArray ()),
                                        "\\b"
                                       );
 

+ 75 - 74
UICatalog/Scenarios/TabViewExample.cs

@@ -15,76 +15,78 @@ public class TabViewExample : Scenario
     private MenuItem _miTabsOnBottom;
     private TabView _tabView;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
 
         var menu = new MenuBar
         {
             Menus =
             [
-                new MenuBarItem (
-                                 "_File",
-                                 new MenuItem []
-                                 {
-                                     new ("_Add Blank Tab", "", AddBlankTab),
-                                     new (
-                                          "_Clear SelectedTab",
-                                          "",
-                                          () => _tabView.SelectedTab = null
-                                         ),
-                                     new ("_Quit", "", Quit)
-                                 }
-                                ),
-                new MenuBarItem (
-                                 "_View",
-                                 new []
-                                 {
-                                     _miShowTopLine =
-                                         new MenuItem ("_Show Top Line", "", ShowTopLine)
-                                         {
-                                             Checked = true, CheckType = MenuItemCheckStyle.Checked
-                                         },
-                                     _miShowBorder =
-                                         new MenuItem ("_Show Border", "", ShowBorder)
-                                         {
-                                             Checked = true, CheckType = MenuItemCheckStyle.Checked
-                                         },
-                                     _miTabsOnBottom =
-                                         new MenuItem ("_Tabs On Bottom", "", SetTabsOnBottom)
-                                         {
-                                             Checked = false, CheckType = MenuItemCheckStyle.Checked
-                                         },
-                                     _miShowTabViewBorder =
-                                         new MenuItem (
-                                                       "_Show TabView Border",
-                                                       "",
-                                                       ShowTabViewBorder
-                                                      ) { Checked = true, CheckType = MenuItemCheckStyle.Checked }
-                                 }
-                                )
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new ("_Add Blank Tab", "", AddBlankTab),
+                         new (
+                              "_Clear SelectedTab",
+                              "",
+                              () => _tabView.SelectedTab = null
+                             ),
+                         new ("_Quit", "", Quit)
+                     }
+                    ),
+                new (
+                     "_View",
+                     new []
+                     {
+                         _miShowTopLine =
+                             new ("_Show Top Line", "", ShowTopLine)
+                             {
+                                 Checked = true, CheckType = MenuItemCheckStyle.Checked
+                             },
+                         _miShowBorder =
+                             new ("_Show Border", "", ShowBorder)
+                             {
+                                 Checked = true, CheckType = MenuItemCheckStyle.Checked
+                             },
+                         _miTabsOnBottom =
+                             new ("_Tabs On Bottom", "", SetTabsOnBottom)
+                             {
+                                 Checked = false, CheckType = MenuItemCheckStyle.Checked
+                             },
+                         _miShowTabViewBorder =
+                             new (
+                                  "_Show TabView Border",
+                                  "",
+                                  ShowTabViewBorder
+                                 ) { Checked = true, CheckType = MenuItemCheckStyle.Checked }
+                     }
+                    )
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
-        _tabView = new TabView
+        _tabView = new()
         {
             X = 0,
-            Y = 0,
+            Y = 1,
             Width = 60,
             Height = 20,
             BorderStyle = LineStyle.Single
         };
 
-        _tabView.AddTab (new Tab { DisplayText = "Tab1", View = new Label { Text = "hodor!" } }, false);
-        _tabView.AddTab (new Tab { DisplayText = "Tab2", View = new TextField { Text = "durdur" } }, false);
-        _tabView.AddTab (new Tab { DisplayText = "Interactive Tab", View = GetInteractiveTab () }, false);
-        _tabView.AddTab (new Tab { DisplayText = "Big Text", View = GetBigTextFileTab () }, false);
+        _tabView.AddTab (new() { DisplayText = "Tab1", View = new Label { Text = "hodor!" } }, false);
+        _tabView.AddTab (new() { DisplayText = "Tab2", View = new TextField { Text = "durdur" } }, false);
+        _tabView.AddTab (new() { DisplayText = "Interactive Tab", View = GetInteractiveTab () }, false);
+        _tabView.AddTab (new() { DisplayText = "Big Text", View = GetBigTextFileTab () }, false);
 
         _tabView.AddTab (
-                         new Tab
+                         new()
                          {
                              DisplayText =
                                  "Long name Tab, I mean seriously long.  Like you would not believe how long this tab's name is its just too much really woooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooowwww thats long",
@@ -98,7 +100,7 @@ public class TabViewExample : Scenario
                         );
 
         _tabView.AddTab (
-                         new Tab
+                         new()
                          {
                              DisplayText = "Les Mise" + '\u0301' + "rables", View = new Label { Text = "This tab name is unicode" }
                          },
@@ -106,7 +108,7 @@ public class TabViewExample : Scenario
                         );
 
         _tabView.AddTab (
-                         new Tab
+                         new()
                          {
                              DisplayText = "Les Mise" + '\u0328' + '\u0301' + "rables",
                              View = new Label
@@ -121,21 +123,21 @@ public class TabViewExample : Scenario
         for (var i = 0; i < 100; i++)
         {
             _tabView.AddTab (
-                             new Tab { DisplayText = $"Tab{i}", View = new Label { Text = $"Welcome to tab {i}" } },
+                             new() { DisplayText = $"Tab{i}", View = new Label { Text = $"Welcome to tab {i}" } },
                              false
                             );
         }
 
         _tabView.SelectedTab = _tabView.Tabs.First ();
 
-        Win.Add (_tabView);
+        appWindow.Add (_tabView);
 
         var frameRight = new FrameView
         {
             X = Pos.Right (_tabView),
-            Y = 0,
+            Y = 1,
             Width = Dim.Fill (),
-            Height = Dim.Fill (),
+            Height = Dim.Fill (1),
             Title = "About"
         };
 
@@ -148,14 +150,14 @@ public class TabViewExample : Scenario
                         }
                        );
 
-        Win.Add (frameRight);
+        appWindow.Add (frameRight);
 
         var frameBelow = new FrameView
         {
             X = 0,
             Y = Pos.Bottom (_tabView),
             Width = _tabView.Width,
-            Height = Dim.Fill (),
+            Height = Dim.Fill (1),
             Title = "Bottom Frame"
         };
 
@@ -169,22 +171,21 @@ public class TabViewExample : Scenario
                         }
                        );
 
-        Win.Add (frameBelow);
-
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                Quit
-                                               )
-                                       }
-                                      );
-        Top.Add (statusBar);
+        appWindow.Add (frameBelow);
+
+        var statusBar = new StatusBar ([new (Application.QuitKey, "Quit", Quit)]);
+        appWindow.Add (statusBar);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
-    private void AddBlankTab () { _tabView.AddTab (new Tab (), false); }
+    private void AddBlankTab () { _tabView.AddTab (new (), false); }
 
     private View GetBigTextFileTab ()
     {

+ 57 - 48
UICatalog/Scenarios/TableEditor.cs

@@ -427,13 +427,15 @@ public class TableEditor : Scenario
         return dt;
     }
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
 
-        _tableView = new() { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (1) };
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
+
+        _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
 
         var menu = new MenuBar
         {
@@ -669,48 +671,48 @@ public class TableEditor : Scenario
             ]
         };
 
-        Top.Add (menu);
+        appWindow.Add (menu);
+
+        var selectedCellLabel = new Label
+        {
+            Text = "0,0"
+        };
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
+                                       [
+                                           new (
+                                                Application.QuitKey,
+                                                "Quit",
+                                                Quit
+                                               ),
                                            new (
-                                                KeyCode.F2,
-                                                "~F2~ OpenExample",
+                                                Key.F2,
+                                                "OpenExample",
                                                 () => OpenExample (true)
                                                ),
                                            new (
-                                                KeyCode.F3,
-                                                "~F3~ CloseExample",
-                                                () => CloseExample ()
+                                                Key.F3,
+                                                "CloseExample",
+                                                CloseExample
                                                ),
                                            new (
-                                                KeyCode.F4,
-                                                "~F4~ OpenSimple",
+                                                Key.F4,
+                                                "OpenSimple",
                                                 () => OpenSimple (true)
                                                ),
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
-                                       }
-                                      );
-        Top.Add (statusBar);
-
-        Win.Add (_tableView);
-
-        var selectedCellLabel = new Label
+                                           new ()
+                                           {
+                                               HelpText = "Cell:",
+                                               CommandView = selectedCellLabel
+                                           }
+                                       ]
+                                      )
         {
-            X = 0,
-            Y = Pos.Bottom (_tableView),
-            Text = "0,0",
-
-            Width = Dim.Fill (),
-            TextAlignment = Alignment.End
+            AlignmentModes = AlignmentModes.IgnoreFirstOrLast
         };
+        appWindow.Add (statusBar);
 
-        Win.Add (selectedCellLabel);
+        appWindow.Add (_tableView);
 
         _tableView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{_tableView.SelectedRow},{_tableView.SelectedColumn}"; };
         _tableView.CellActivated += EditCurrentCell;
@@ -718,27 +720,27 @@ public class TableEditor : Scenario
 
         SetupScrollBar ();
 
-        _redColorScheme = new()
+        _redColorScheme = new ()
         {
-            Disabled = Win.ColorScheme.Disabled,
-            HotFocus = Win.ColorScheme.HotFocus,
-            Focus = Win.ColorScheme.Focus,
-            Normal = new (Color.Red, Win.ColorScheme.Normal.Background)
+            Disabled = appWindow.ColorScheme.Disabled,
+            HotFocus = appWindow.ColorScheme.HotFocus,
+            Focus = appWindow.ColorScheme.Focus,
+            Normal = new (Color.Red, appWindow.ColorScheme.Normal.Background)
         };
 
-        _alternatingColorScheme = new()
+        _alternatingColorScheme = new ()
         {
-            Disabled = Win.ColorScheme.Disabled,
-            HotFocus = Win.ColorScheme.HotFocus,
-            Focus = Win.ColorScheme.Focus,
+            Disabled = appWindow.ColorScheme.Disabled,
+            HotFocus = appWindow.ColorScheme.HotFocus,
+            Focus = appWindow.ColorScheme.Focus,
             Normal = new (Color.White, Color.BrightBlue)
         };
 
-        _redColorSchemeAlt = new()
+        _redColorSchemeAlt = new ()
         {
-            Disabled = Win.ColorScheme.Disabled,
-            HotFocus = Win.ColorScheme.HotFocus,
-            Focus = Win.ColorScheme.Focus,
+            Disabled = appWindow.ColorScheme.Disabled,
+            HotFocus = appWindow.ColorScheme.HotFocus,
+            Focus = appWindow.ColorScheme.Focus,
             Normal = new (Color.Red, Color.BrightBlue)
         };
 
@@ -768,6 +770,13 @@ public class TableEditor : Scenario
                                  };
 
         _tableView.KeyBindings.Add (Key.Space, Command.Accept);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     protected override void Dispose (bool disposing)
@@ -1017,7 +1026,7 @@ public class TableEditor : Scenario
                                                       _tableView,
                                                       "Name",
                                                       tree,
-                                                      new()
+                                                      new ()
                                                       {
                                                           { "Extension", f => f.Extension },
                                                           { "CreationTime", f => f.CreationTime },

+ 1 - 0
UICatalog/Scenarios/TextAlignmentAndDirection.cs

@@ -592,6 +592,7 @@ public class TextAlignmentAndDirection : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
 
         void ToggleJustify (bool oldValue, bool wasJustOptions = false)
         {

+ 1 - 0
UICatalog/Scenarios/TextFormatterDemo.cs

@@ -144,5 +144,6 @@ public class TextFormatterDemo : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 60 - 38
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -13,17 +13,22 @@ public class TextViewAutocompletePopup : Scenario
     private int _height = 10;
     private MenuItem _miMultiline;
     private MenuItem _miWrap;
-    private StatusItem _siMultiline;
-    private StatusItem _siWrap;
+    private Shortcut _siMultiline;
+    private Shortcut _siWrap;
     private TextView _textViewBottomLeft;
     private TextView _textViewBottomRight;
     private TextView _textViewCentered;
     private TextView _textViewTopLeft;
     private TextView _textViewTopRight;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
+
         var width = 20;
         var text = " jamp jemp jimp jomp jump";
 
@@ -31,44 +36,52 @@ public class TextViewAutocompletePopup : Scenario
         {
             Menus =
             [
-                new MenuBarItem (
-                                 "_File",
-                                 new []
-                                 {
-                                     _miMultiline =
-                                         new MenuItem (
-                                                       "_Multiline",
-                                                       "",
-                                                       () => Multiline ()
-                                                      ) { CheckType = MenuItemCheckStyle.Checked },
-                                     _miWrap = new MenuItem (
-                                                             "_Word Wrap",
-                                                             "",
-                                                             () => WordWrap ()
-                                                            ) { CheckType = MenuItemCheckStyle.Checked },
-                                     new ("_Quit", "", () => Quit ())
-                                 }
-                                )
+                new (
+                     "_File",
+                     new []
+                     {
+                         _miMultiline =
+                             new (
+                                  "_Multiline",
+                                  "",
+                                  () => Multiline ()
+                                 ) { CheckType = MenuItemCheckStyle.Checked },
+                         _miWrap = new (
+                                        "_Word Wrap",
+                                        "",
+                                        () => WordWrap ()
+                                       ) { CheckType = MenuItemCheckStyle.Checked },
+                         new ("_Quit", "", () => Quit ())
+                     }
+                    )
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
-        _textViewTopLeft = new TextView { Width = width, Height = _height, Text = text };
+        _textViewTopLeft = new()
+        {
+            Y = 1,
+            Width = width, Height = _height, Text = text
+        };
         _textViewTopLeft.DrawContent += TextViewTopLeft_DrawContent;
-        Win.Add (_textViewTopLeft);
+        appWindow.Add (_textViewTopLeft);
 
-        _textViewTopRight = new TextView { X = Pos.AnchorEnd (width), Width = width, Height = _height, Text = text };
+        _textViewTopRight = new()
+        {
+            X = Pos.AnchorEnd (width), Y = 1,
+            Width = width, Height = _height, Text = text
+        };
         _textViewTopRight.DrawContent += TextViewTopRight_DrawContent;
-        Win.Add (_textViewTopRight);
+        appWindow.Add (_textViewTopRight);
 
-        _textViewBottomLeft = new TextView
+        _textViewBottomLeft = new()
         {
             Y = Pos.AnchorEnd (_height), Width = width, Height = _height, Text = text
         };
         _textViewBottomLeft.DrawContent += TextViewBottomLeft_DrawContent;
-        Win.Add (_textViewBottomLeft);
+        appWindow.Add (_textViewBottomLeft);
 
-        _textViewBottomRight = new TextView
+        _textViewBottomRight = new()
         {
             X = Pos.AnchorEnd (width),
             Y = Pos.AnchorEnd (_height),
@@ -77,9 +90,9 @@ public class TextViewAutocompletePopup : Scenario
             Text = text
         };
         _textViewBottomRight.DrawContent += TextViewBottomRight_DrawContent;
-        Win.Add (_textViewBottomRight);
+        appWindow.Add (_textViewBottomRight);
 
-        _textViewCentered = new TextView
+        _textViewCentered = new()
         {
             X = Pos.Center (),
             Y = Pos.Center (),
@@ -88,7 +101,7 @@ public class TextViewAutocompletePopup : Scenario
             Text = text
         };
         _textViewCentered.DrawContent += TextViewCentered_DrawContent;
-        Win.Add (_textViewCentered);
+        appWindow.Add (_textViewCentered);
 
         _miMultiline.Checked = _textViewTopLeft.Multiline;
         _miWrap.Checked = _textViewTopLeft.WordWrap;
@@ -98,16 +111,24 @@ public class TextViewAutocompletePopup : Scenario
                                        {
                                            new (
                                                 Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
+                                                "Quit",
                                                 () => Quit ()
                                                ),
-                                           _siMultiline = new StatusItem (KeyCode.Null, "", null),
-                                           _siWrap = new StatusItem (KeyCode.Null, "", null)
+                                           _siMultiline = new (Key.Empty, "", null),
+                                           _siWrap = new (Key.Empty, "", null)
                                        }
                                       );
-        Top.Add (statusBar);
+        appWindow.Add (statusBar);
+
+        appWindow.LayoutStarted += Win_LayoutStarted;
+
+        // Run - Start the application.
+        Application.Run (appWindow);
 
-        Win.LayoutStarted += Win_LayoutStarted;
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 
     private void Multiline ()
@@ -133,6 +154,7 @@ public class TextViewAutocompletePopup : Scenario
     }
 
     private void SetMultilineStatusText () { _siMultiline.Title = $"Multiline: {_miMultiline.Checked}"; }
+
     private void SetWrapStatusText () { _siWrap.Title = $"WordWrap: {_miWrap.Checked}"; }
     private void TextViewBottomLeft_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewBottomLeft); }
     private void TextViewBottomRight_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewBottomRight); }

+ 29 - 27
UICatalog/Scenarios/TreeUseCases.cs

@@ -11,11 +11,13 @@ public class TreeUseCases : Scenario
 {
     private View _currentTree;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        Win.Title = GetName ();
-        Win.Y = 1; // menu
-        Win.Height = Dim.Fill (1); // status bar
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Toplevel appWindow = new ();
 
         var menu = new MenuBar
         {
@@ -47,23 +49,23 @@ public class TreeUseCases : Scenario
             ]
         };
 
-        Top.Add (menu);
+        appWindow.Add (menu);
+
+        var statusBar = new StatusBar ([new (Application.QuitKey, "Quit", Quit)]);
+
+        appWindow.Add (statusBar);
+
+        appWindow.Ready += (sender, args) =>
+                                        // Start with the most basic use case
+                                        LoadSimpleNodes ();
 
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               )
-                                       }
-                                      );
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
 
-        Top.Add (statusBar);
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
 
-        // Start with the most basic use case
-        LoadSimpleNodes ();
     }
 
     private void LoadArmies (bool useDelegate)
@@ -76,11 +78,11 @@ public class TreeUseCases : Scenario
 
         if (_currentTree != null)
         {
-            Win.Remove (_currentTree);
+            Application.Top.Remove (_currentTree);
             _currentTree.Dispose ();
         }
 
-        TreeView<GameObject> tree = new () { X = 0, Y = 0, Width = 40, Height = 20 };
+        TreeView<GameObject> tree = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
 
         if (useDelegate)
         {
@@ -96,7 +98,7 @@ public class TreeUseCases : Scenario
             tree.TreeBuilder = new GameObjectTreeBuilder ();
         }
 
-        Win.Add (tree);
+        Application.Top.Add (tree);
 
         tree.AddObject (army1);
 
@@ -116,13 +118,13 @@ public class TreeUseCases : Scenario
 
         if (_currentTree != null)
         {
-            Win.Remove (_currentTree);
+            Application.Top.Remove (_currentTree);
             _currentTree.Dispose ();
         }
 
-        var tree = new TreeView { X = 0, Y = 0, Width = 40, Height = 20 };
+        var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill(), Height = Dim.Fill (1) };
 
-        Win.Add (tree);
+        Application.Top.Add (tree);
 
         tree.AddObject (myHouse);
 
@@ -133,13 +135,13 @@ public class TreeUseCases : Scenario
     {
         if (_currentTree != null)
         {
-            Win.Remove (_currentTree);
+            Application.Top.Remove (_currentTree);
             _currentTree.Dispose ();
         }
 
-        var tree = new TreeView { X = 0, Y = 0, Width = 40, Height = 20 };
+        var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
 
-        Win.Add (tree);
+        Application.Top.Add (tree);
 
         var root1 = new TreeNode ("Root1");
         root1.Children.Add (new TreeNode ("Child1.1"));

+ 2 - 0
UICatalog/Scenarios/TrueColors.cs

@@ -125,6 +125,8 @@ public class TrueColors : Scenario
         Application.Run (app);
         app.Dispose ();
 
+        Application.Shutdown ();
+
         return;
 
         void SetupGradient (string name, int x, ref int y, Func<int, Color> colorFunc)

+ 54 - 37
UICatalog/Scenarios/Unicode.cs

@@ -10,7 +10,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 public class UnicodeInMenu : Scenario
 {
-    public override void Setup ()
+    public override void Main ()
     {
         var unicode =
             "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
@@ -28,6 +28,15 @@ public class UnicodeInMenu : Scenario
                 CM.Glyphs.HorizontalEllipsis
             }";
 
+        // Init
+        Application.Init ();
+
+        // Setup - Create a top-level application window and configure it.
+        Window appWindow = new ()
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+        };
+
         var menu = new MenuBar
         {
             Menus =
@@ -60,24 +69,24 @@ public class UnicodeInMenu : Scenario
                     )
             ]
         };
-        Top.Add (menu);
+        appWindow.Add (menu);
 
         var statusBar = new StatusBar (
-                                       new StatusItem []
+                                       new Shortcut []
                                        {
                                            new (
                                                 Application.QuitKey,
-                                                $"{Application.QuitKey} Выход",
+                                                "Выход",
                                                 () => Application.RequestStop ()
                                                ),
-                                           new (KeyCode.Null, "~F2~ Создать", null),
-                                           new (KeyCode.Null, "~F3~ Со_хранить", null)
+                                           new (Key.F2, "Создать", null),
+                                           new (Key.F3, "Со_хранить", null)
                                        }
                                       );
-        Top.Add (statusBar);
+        appWindow.Add (statusBar);
 
         var label = new Label { X = 0, Y = 1, Text = "Label:" };
-        Win.Add (label);
+        appWindow.Add (label);
 
         var testlabel = new Label
         {
@@ -87,16 +96,16 @@ public class UnicodeInMenu : Scenario
             Width = Dim.Percent (50),
             Text = gitString
         };
-        Win.Add (testlabel);
+        appWindow.Add (testlabel);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Label (CanFocus):" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Label (CanFocus):" };
+        appWindow.Add (label);
         var sb = new StringBuilder ();
         sb.Append ('e');
         sb.Append ('\u0301');
         sb.Append ('\u0301');
 
-        testlabel = new()
+        testlabel = new ()
         {
             X = 20,
             Y = Pos.Y (label),
@@ -106,14 +115,14 @@ public class UnicodeInMenu : Scenario
             HotKeySpecifier = new ('&'),
             Text = $"Should be [e with two accents, but isn't due to #2616]: [{sb}]"
         };
-        Win.Add (testlabel);
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Button:" };
-        Win.Add (label);
+        appWindow.Add (testlabel);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Button:" };
+        appWindow.Add (label);
         var button = new Button { X = 20, Y = Pos.Y (label), Text = "A123456789♥♦♣♠JQK" };
-        Win.Add (button);
+        appWindow.Add (button);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "CheckBox:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "CheckBox:" };
+        appWindow.Add (label);
 
         var checkBox = new CheckBox
         {
@@ -135,27 +144,27 @@ public class UnicodeInMenu : Scenario
             TextAlignment = Alignment.End,
             Text = $"End - {gitString}"
         };
-        Win.Add (checkBox, checkBoxRight);
+        appWindow.Add (checkBox, checkBoxRight);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1, Text = "ComboBox:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1, Text = "ComboBox:" };
+        appWindow.Add (label);
         var comboBox = new ComboBox { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
         comboBox.SetSource (new ObservableCollection<string> { gitString, "Со_хранить" });
 
-        Win.Add (comboBox);
+        appWindow.Add (comboBox);
         comboBox.Text = gitString;
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (label) + 2, Text = "HexView:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 2, Text = "HexView:" };
+        appWindow.Add (label);
 
         var hexView = new HexView (new MemoryStream (Encoding.ASCII.GetBytes (gitString + " Со_хранить")))
         {
             X = 20, Y = Pos.Y (label), Width = Dim.Percent (60), Height = 5
         };
-        Win.Add (hexView);
+        appWindow.Add (hexView);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1, Text = "ListView:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1, Text = "ListView:" };
+        appWindow.Add (label);
 
         var listView = new ListView
         {
@@ -167,10 +176,10 @@ public class UnicodeInMenu : Scenario
                                               ["item #1", gitString, "Со_хранить", unicode]
                                              )
         };
-        Win.Add (listView);
+        appWindow.Add (listView);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (listView) + 1, Text = "RadioGroup:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (listView) + 1, Text = "RadioGroup:" };
+        appWindow.Add (label);
 
         var radioGroup = new RadioGroup
         {
@@ -179,19 +188,19 @@ public class UnicodeInMenu : Scenario
             Width = Dim.Percent (60),
             RadioLabels = new [] { "item #1", gitString, "Со_хранить", "𝔽𝕆𝕆𝔹𝔸ℝ" }
         };
-        Win.Add (radioGroup);
+        appWindow.Add (radioGroup);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (radioGroup) + 1, Text = "TextField:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (radioGroup) + 1, Text = "TextField:" };
+        appWindow.Add (label);
 
         var textField = new TextField
         {
             X = 20, Y = Pos.Y (label), Width = Dim.Percent (60), Text = gitString + " = Со_хранить"
         };
-        Win.Add (textField);
+        appWindow.Add (textField);
 
-        label = new() { X = Pos.X (label), Y = Pos.Bottom (textField) + 1, Text = "TextView:" };
-        Win.Add (label);
+        label = new () { X = Pos.X (label), Y = Pos.Bottom (textField) + 1, Text = "TextView:" };
+        appWindow.Add (label);
 
         var textView = new TextView
         {
@@ -201,6 +210,14 @@ public class UnicodeInMenu : Scenario
             Height = 5,
             Text = unicode
         };
-        Win.Add (textView);
+        appWindow.Add (textView);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 }

+ 1 - 0
UICatalog/Scenarios/ViewExperiments.cs

@@ -248,5 +248,6 @@ public class ViewExperiments : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 5 - 1
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -104,7 +104,7 @@ public class VkeyPacketSimulator : Scenario
                                 if (_outputStarted)
                                 {
                                     // If the key wasn't handled by the TextView will popup a Dialog with the keys pressed.
-                                    bool? handled = tvOutput.OnInvokingKeyBindings (e);
+                                    bool? handled = tvOutput.OnInvokingKeyBindings (e, KeyBindingScope.HotKey | KeyBindingScope.Focused);
 
                                     if (handled == null || handled == false)
                                     {
@@ -260,6 +260,10 @@ public class VkeyPacketSimulator : Scenario
 
         void Win_LayoutComplete (object sender, LayoutEventArgs obj)
         {
+            if (inputHorizontalRuler.Viewport.Width == 0 || inputVerticalRuler.Viewport.Height == 0)
+            {
+                return;
+            }
             inputHorizontalRuler.Text = outputHorizontalRuler.Text =
                                             ruler.Repeat (
                                                           (int)Math.Ceiling (

+ 1 - 0
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -207,5 +207,6 @@ public class WindowsAndFrameViews : Scenario
 
         Application.Run (app);
         app.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 8 - 11
UICatalog/Scenarios/WizardAsView.cs

@@ -6,7 +6,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Wizards")]
 public class WizardAsView : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
         Application.Init ();
 
@@ -52,8 +52,9 @@ public class WizardAsView : Scenario
                                 )
             ]
         };
-        Top = new ();
-        Top.Add (menu);
+
+        Toplevel topLevel = new ();
+        topLevel.Add (menu);
 
         // No need for a Title because the border is disabled
         var wizard = new Wizard { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
@@ -139,13 +140,9 @@ public class WizardAsView : Scenario
         lastStep.HelpText =
             "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing Esc will cancel.";
 
-        Top.Add (wizard);
-        Application.Run (Top);
-    }
-
-    public override void Run ()
-    {
-        // Do nothing in the override because we call Application.Run above
-        // (just to make it clear how the Top is being run and not the Wizard).
+        topLevel.Add (wizard);
+        Application.Run (topLevel);
+        topLevel.Dispose ();
+        Application.Shutdown ();
     }
 }

+ 132 - 92
UICatalog/UICatalog.cs

@@ -371,12 +371,13 @@ internal class UICatalogApp
     public class UICatalogTopLevel : Toplevel
     {
         public ListView CategoryList;
-        public StatusItem DriverName;
-        public MenuItem? miForce16Colors;
-        public MenuItem? miIsMenuBorderDisabled;
-        public MenuItem? miIsMouseDisabled;
-        public MenuItem? miUseSubMenusSingleFrame;
-        public StatusItem OS;
+        public MenuItem? MiForce16Colors;
+        public MenuItem? MiIsMenuBorderDisabled;
+        public MenuItem? MiIsMouseDisabled;
+        public MenuItem? MiUseSubMenusSingleFrame;
+        public Shortcut? ShForce16Colors;
+        //public Shortcut? ShDiagnostics;
+        public Shortcut? ShVersion;
 
         // UI Catalog uses TableView for the scenario list instead of a ListView to demonstate how
         // TableView works. There's no real reason not to use ListView. Because we use TableView, and TableView
@@ -446,42 +447,74 @@ internal class UICatalogApp
                 ]
             };
 
-            DriverName = new (Key.Empty, "Driver:", null);
-            OS = new (Key.Empty, "OS:", null);
-
-            StatusBar = new () { Visible = ShowStatusBar };
-
-            StatusBar.Items = new []
+            StatusBar = new ()
             {
-                new (
-                     Application.QuitKey,
-                     $"~{Application.QuitKey} to quit",
-                     () =>
-                     {
-                         if (_selectedScenario is null)
-                         {
-                             // This causes GetScenarioToRun to return null
-                             _selectedScenario = null;
-                             RequestStop ();
-                         }
-                     }
-                    ),
-                new (
-                     Key.F10,
-                     "~F10~ Status Bar",
-                     () =>
-                     {
-                         StatusBar.Visible = !StatusBar.Visible;
-
-                         //ContentPane!.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
-                         LayoutSubviews ();
-                         SetSubViewNeedsDisplay ();
-                     }
-                    ),
-                DriverName,
-                OS
+                Visible = ShowStatusBar,
             };
 
+            if (StatusBar is { })
+            {
+                ShVersion = new ()
+                {
+                    Title = "Version Info",
+                    CanFocus = false,
+
+                };
+
+                Shortcut statusBarShortcut = new Shortcut ()
+                {
+                    Key = Key.F10,
+                    Title = "Show/Hide Status Bar",
+                };
+                statusBarShortcut.Accept += (sender, args) => { StatusBar.Visible = !StatusBar.Visible; };
+
+                ShForce16Colors = new Shortcut ()
+                {
+                    CommandView = new CheckBox ()
+                    {
+                        Title = "16 color mode",
+                        Checked = Application.Force16Colors,
+                        CanFocus = false,
+                    },
+                    HelpText = "",
+                    Key = Key.F6,
+                };
+
+                ShForce16Colors.Accept += (sender, args) =>
+                                          {
+                                              ((CheckBox)ShForce16Colors.CommandView).Checked =
+                                                  Application.Force16Colors = (bool)!((CheckBox)ShForce16Colors.CommandView).Checked!;
+                                              MiForce16Colors.Checked = Application.Force16Colors;
+                                              Application.Refresh ();
+
+                                          };
+
+                //ShDiagnostics = new Shortcut ()
+                //{
+                //    HelpText = "Diagnostic flags",
+                //    CommandView = new RadioGroup()
+                //    {
+                //        RadioLabels = ["Off", "Ruler", "Padding", "MouseEnter"],
+
+                //        CanFocus = false,
+                //        Orientation = Orientation.Vertical,
+                //    }
+                //};
+
+                StatusBar.Add (
+                               new Shortcut ()
+                               {
+                                   Title = "Quit",
+                                   Key = Application.QuitKey,
+                               },
+                               statusBarShortcut,
+                               ShForce16Colors,
+
+                               //ShDiagnostics,
+                               ShVersion
+                              );
+            }
+
             // Create the Category list view. This list never changes.
             CategoryList = new ()
             {
@@ -507,7 +540,7 @@ internal class UICatalogApp
                 X = Pos.Right (CategoryList) - 1,
                 Y = 1,
                 Width = Dim.Fill (),
-                Height = Dim.Fill (1),
+                Height = Dim.Height (CategoryList),
 
                 //AllowsMarking = false,
                 CanFocus = true,
@@ -585,7 +618,11 @@ internal class UICatalogApp
             Add (ScenarioList);
 
             Add (MenuBar);
-            Add (StatusBar);
+
+            if (StatusBar is { })
+            {
+                Add (StatusBar);
+            }
 
             Loaded += LoadedHandler;
             Unloaded += UnloadedHandler;
@@ -620,16 +657,14 @@ internal class UICatalogApp
             ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
 
             MenuBar.Menus [0].Children [0].Shortcut = (KeyCode)Application.QuitKey;
-            StatusBar.Items [0].Shortcut = Application.QuitKey;
-            StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit";
-
-            miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
 
-            int height = ShowStatusBar ? 1 : 0; // + (MenuBar.Visible ? 1 : 0);
-
-            //ContentPane.Height = Dim.Fill (height);
+            if (StatusBar is { })
+            {
+                ((Shortcut)StatusBar.Subviews [0]).Key = Application.QuitKey;
+                StatusBar.Visible = ShowStatusBar;
+            }
 
-            StatusBar.Visible = ShowStatusBar;
+            MiIsMouseDisabled!.Checked = Application.IsMouseDisabled;
 
             Application.Top.SetNeedsDisplay ();
         }
@@ -894,22 +929,22 @@ internal class UICatalogApp
         private MenuItem [] CreateDisabledEnabledMenuBorder ()
         {
             List<MenuItem> menuItems = new ();
-            miIsMenuBorderDisabled = new () { Title = "Disable Menu _Border" };
+            MiIsMenuBorderDisabled = new () { Title = "Disable Menu _Border" };
 
-            miIsMenuBorderDisabled.Shortcut =
-                (KeyCode)new Key (miIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt
+            MiIsMenuBorderDisabled.Shortcut =
+                (KeyCode)new Key (MiIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt
                                                                                        .WithCtrl.NoShift;
-            miIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
+            MiIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
 
-            miIsMenuBorderDisabled.Action += () =>
+            MiIsMenuBorderDisabled.Action += () =>
                                              {
-                                                 miIsMenuBorderDisabled.Checked = (bool)!miIsMenuBorderDisabled.Checked!;
+                                                 MiIsMenuBorderDisabled.Checked = (bool)!MiIsMenuBorderDisabled.Checked!;
 
-                                                 MenuBar.MenusBorderStyle = !(bool)miIsMenuBorderDisabled.Checked
+                                                 MenuBar.MenusBorderStyle = !(bool)MiIsMenuBorderDisabled.Checked
                                                                                 ? LineStyle.Single
                                                                                 : LineStyle.None;
                                              };
-            menuItems.Add (miIsMenuBorderDisabled);
+            menuItems.Add (MiIsMenuBorderDisabled);
 
             return menuItems.ToArray ();
         }
@@ -917,18 +952,18 @@ internal class UICatalogApp
         private MenuItem [] CreateDisabledEnabledMouseItems ()
         {
             List<MenuItem> menuItems = new ();
-            miIsMouseDisabled = new () { Title = "_Disable Mouse" };
+            MiIsMouseDisabled = new () { Title = "_Disable Mouse" };
 
-            miIsMouseDisabled.Shortcut =
-                (KeyCode)new Key (miIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl.NoShift;
-            miIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
+            MiIsMouseDisabled.Shortcut =
+                (KeyCode)new Key (MiIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl.NoShift;
+            MiIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
 
-            miIsMouseDisabled.Action += () =>
+            MiIsMouseDisabled.Action += () =>
                                         {
-                                            miIsMouseDisabled.Checked =
-                                                Application.IsMouseDisabled = (bool)!miIsMouseDisabled.Checked!;
+                                            MiIsMouseDisabled.Checked =
+                                                Application.IsMouseDisabled = (bool)!MiIsMouseDisabled.Checked!;
                                         };
-            menuItems.Add (miIsMouseDisabled);
+            menuItems.Add (MiIsMouseDisabled);
 
             return menuItems.ToArray ();
         }
@@ -937,20 +972,20 @@ internal class UICatalogApp
         private MenuItem [] CreateDisabledEnableUseSubMenusSingleFrame ()
         {
             List<MenuItem> menuItems = new ();
-            miUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
+            MiUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
 
-            miUseSubMenusSingleFrame.Shortcut = KeyCode.CtrlMask
+            MiUseSubMenusSingleFrame.Shortcut = KeyCode.CtrlMask
                                                 | KeyCode.AltMask
-                                                | (KeyCode)miUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
+                                                | (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
                                                  0];
-            miUseSubMenusSingleFrame.CheckType |= MenuItemCheckStyle.Checked;
+            MiUseSubMenusSingleFrame.CheckType |= MenuItemCheckStyle.Checked;
 
-            miUseSubMenusSingleFrame.Action += () =>
+            MiUseSubMenusSingleFrame.Action += () =>
                                                {
-                                                   miUseSubMenusSingleFrame.Checked = (bool)!miUseSubMenusSingleFrame.Checked!;
-                                                   MenuBar.UseSubMenusSingleFrame = (bool)miUseSubMenusSingleFrame.Checked;
+                                                   MiUseSubMenusSingleFrame.Checked = (bool)!MiUseSubMenusSingleFrame.Checked!;
+                                                   MenuBar.UseSubMenusSingleFrame = (bool)MiUseSubMenusSingleFrame.Checked;
                                                };
-            menuItems.Add (miUseSubMenusSingleFrame);
+            menuItems.Add (MiUseSubMenusSingleFrame);
 
             return menuItems.ToArray ();
         }
@@ -959,21 +994,22 @@ internal class UICatalogApp
         {
             List<MenuItem> menuItems = new ();
 
-            miForce16Colors = new ()
+            MiForce16Colors = new ()
             {
                 Title = "Force _16 Colors",
                 Shortcut = (KeyCode)Key.F6,
                 Checked = Application.Force16Colors,
                 CanExecute = () => Application.Driver.SupportsTrueColor
             };
-            miForce16Colors.CheckType |= MenuItemCheckStyle.Checked;
+            MiForce16Colors.CheckType |= MenuItemCheckStyle.Checked;
 
-            miForce16Colors.Action += () =>
+            MiForce16Colors.Action += () =>
                                       {
-                                          miForce16Colors.Checked = Application.Force16Colors = (bool)!miForce16Colors.Checked!;
+                                          MiForce16Colors.Checked = Application.Force16Colors = (bool)!MiForce16Colors.Checked!;
+                                          ((CheckBox)ShForce16Colors!.CommandView!).Checked = Application.Force16Colors;
                                           Application.Refresh ();
                                       };
-            menuItems.Add (miForce16Colors);
+            menuItems.Add (MiForce16Colors);
 
             return menuItems.ToArray ();
         }
@@ -1000,11 +1036,12 @@ internal class UICatalogApp
         {
             ConfigChanged ();
 
-            miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
-            DriverName.Title = $"Driver: {Driver.GetVersionInfo ()}";
+            MiIsMouseDisabled!.Checked = Application.IsMouseDisabled;
 
-            OS.Title =
-                $"OS: {RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}";
+            if (ShVersion is { })
+            {
+                ShVersion.Title = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {Driver.GetVersionInfo ()}";
+            }
 
             if (_selectedScenario != null)
             {
@@ -1017,18 +1054,21 @@ internal class UICatalogApp
                 ScenarioList.SetFocus ();
             }
 
-            StatusBar.VisibleChanged += (s, e) =>
-                                        {
-                                            ShowStatusBar = StatusBar.Visible;
+            if (StatusBar is { })
+            {
+                StatusBar.VisibleChanged += (s, e) =>
+                                            {
+                                                ShowStatusBar = StatusBar.Visible;
 
-                                            int height = StatusBar.Visible ? 1 : 0;
-                                            CategoryList.Height = Dim.Fill (height);
-                                            ScenarioList.Height = Dim.Fill (height);
+                                                int height = StatusBar.Visible ? 1 : 0;
+                                                CategoryList.Height = Dim.Fill (height);
+                                                ScenarioList.Height = Dim.Fill (height);
 
-                                            // ContentPane.Height = Dim.Fill (height);
-                                            LayoutSubviews ();
-                                            SetSubViewNeedsDisplay ();
-                                        };
+                                                // ContentPane.Height = Dim.Fill (height);
+                                                LayoutSubviews ();
+                                                SetSubViewNeedsDisplay ();
+                                            };
+            }
 
             Loaded -= LoadedHandler;
             CategoryList.EnsureSelectedItemVisible ();

+ 196 - 7
UnitTests/Application/ApplicationTests.cs

@@ -1,4 +1,5 @@
-using Xunit.Abstractions;
+using Microsoft.VisualBasic;
+using Xunit.Abstractions;
 
 // Alias Console to MockConsole so we don't accidentally use Console
 
@@ -147,8 +148,12 @@ public class ApplicationTests
         Shutdown ();
     }
 
-    [Fact]
-    public void Init_ResetState_Resets_Properties ()
+    [Theory]
+    [InlineData (typeof (FakeDriver))]
+    [InlineData (typeof (NetDriver))]
+    [InlineData (typeof (WindowsDriver))]
+    [InlineData (typeof (CursesDriver))]
+    public void Init_ResetState_Resets_Properties (Type driverType)
     {
         ConfigurationManager.ThrowOnJsonErrors = true;
 
@@ -156,7 +161,7 @@ public class ApplicationTests
 
         // Set some values
 
-        Application.Init ();
+        Application.Init (driverName: driverType.Name);
         Application._initialized = true;
 
         // Reset
@@ -228,7 +233,7 @@ public class ApplicationTests
         Application.AlternateBackwardKey = Key.A;
         Application.AlternateForwardKey = Key.B;
         Application.QuitKey = Key.C;
-        Application.AddKeyBinding(Key.A, new View ());
+        Application.AddKeyBinding (Key.A, new View ());
 
         //Application.OverlappedChildren = new List<View> ();
         //Application.OverlappedTop = 
@@ -266,6 +271,71 @@ public class ApplicationTests
 #endif
     }
 
+    [Theory]
+    [InlineData (typeof (FakeDriver))]
+    [InlineData (typeof (NetDriver))]
+    [InlineData (typeof (WindowsDriver))]
+    [InlineData (typeof (CursesDriver))]
+    public void Init_Shutdown_Fire_InitializedChanged (Type driverType)
+    {
+        bool initialized = false;
+        bool shutdown = false;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (driverName: driverType.Name);
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        Application.Shutdown ();
+        Assert.True (initialized);
+        Assert.True (shutdown);
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        return;
+
+        void OnApplicationOnInitializedChanged (object s, StateEventArgs<bool> a)
+        {
+            if (a.NewValue)
+            {
+                initialized = true;
+            }
+            else
+            {
+                shutdown = true;
+            }
+        }
+    }
+
+
+    [Fact]
+    public void Run_Iteration_Fires ()
+    {
+        int iteration = 0;
+
+        Application.Init (new FakeDriver ());
+
+        Application.Iteration += Application_Iteration;
+        Application.Run<Toplevel> ().Dispose ();
+
+        Assert.Equal (1, iteration);
+        Application.Shutdown ();
+
+        return;
+
+        void Application_Iteration (object sender, IterationEventArgs e)
+        {
+            if (iteration > 0)
+            {
+                Assert.Fail ();
+            }
+            iteration++;
+            Application.RequestStop ();
+        }
+    }
+
+
     [Fact]
     public void Init_Unbalanced_Throws ()
     {
@@ -826,10 +896,10 @@ public class ApplicationTests
 
         Application.OnMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
         Assert.Equal (w.Border, Application.MouseGrabView);
-        Assert.Equal (new Point (0,0), w.Frame.Location);
+        Assert.Equal (new Point (0, 0), w.Frame.Location);
 
         // Move down and to the right.
-        Application.OnMouseEvent (new () { Position = new (1,1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
+        Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
         Assert.Equal (new Point (1, 1), w.Frame.Location);
 
         Application.End (rs);
@@ -1024,4 +1094,123 @@ public class ApplicationTests
     }
 
     #endregion
+
+
+    private object _timeoutLock;
+
+    [Fact]
+    public void AddTimeout_Fires ()
+    {
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new object ();
+
+        uint timeoutTime = 250;
+        bool initialized = false;
+        int iteration = 0;
+        bool shutdown = false;
+        object timeout = null;
+        int timeoutCount = 0;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (new FakeDriver ());
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
+        Application.Run<Toplevel> ().Dispose ();
+        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
+
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        Assert.Equal (1, timeoutCount);
+        Application.Shutdown ();
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                Application.RemoveTimeout (timeout);
+                timeout = null;
+            }
+        }
+
+        Assert.True (initialized);
+        Assert.True (shutdown);
+
+#if DEBUG_IDISPOSABLE
+        Assert.Empty (Responder.Instances);
+#endif
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
+
+        return;
+
+        void OnApplicationOnInitializedChanged (object s, StateEventArgs<bool> a)
+        {
+            if (a.NewValue)
+            {
+                Application.Iteration += OnApplicationOnIteration;
+                initialized = true;
+
+                lock (_timeoutLock)
+                {
+                    _output.WriteLine ($"Setting timeout for {timeoutTime}ms");
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (timeoutTime), TimeoutCallback);
+                }
+
+            }
+            else
+            {
+                Application.Iteration -= OnApplicationOnIteration;
+                shutdown = true;
+            }
+        }
+
+        bool TimeoutCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                _output.WriteLine ($"TimeoutCallback. Count: {++timeoutCount}. Application Iteration: {iteration}");
+                if (timeout is { })
+                {
+                    _output.WriteLine ($"  Nulling timeout.");
+                    timeout = null;
+                }
+            }
+
+            // False means "don't re-do timer and remove it"
+            return false;
+        }
+
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            lock (_timeoutLock)
+            {
+                if (timeoutCount > 0)
+                {
+                    _output.WriteLine ($"Iteration #{iteration} - Timeout fired. Calling Application.RequestStop.");
+                    Application.RequestStop ();
+
+                    return;
+                }
+            }
+            iteration++;
+
+            // Simulate a delay
+            Thread.Sleep ((int)timeoutTime / 10);
+
+            // Worst case scenario - something went wrong
+            if (Application._initialized && iteration > 25)
+            {
+                _output.WriteLine ($"Too many iterations ({iteration}): Calling Application.RequestStop.");
+                Application.RequestStop ();
+            }
+        }
+    }
 }

+ 119 - 1
UnitTests/Application/KeyboardTests.cs

@@ -1,4 +1,5 @@
-using Xunit.Abstractions;
+using UICatalog;
+using Xunit.Abstractions;
 
 namespace Terminal.Gui.ApplicationTests;
 
@@ -58,6 +59,123 @@ public class KeyboardTests
         top.Dispose ();
     }
 
+    [Fact]
+    public void QuitKey_Default_Is_CtrlQ ()
+    {
+        Application.ResetState (true);
+        // Before Init
+        Assert.Equal (Key.Empty, Application.QuitKey);
+
+        Application.Init (new FakeDriver ());
+        // After Init
+        Assert.Equal (Key.Q.WithCtrl, Application.QuitKey);
+
+        Application.Shutdown();
+    }
+
+    private object _timeoutLock;
+
+    [Fact]
+    public void QuitKey_Quits ()
+    {
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new object ();
+
+        uint abortTime = 500;
+        bool initialized = false;
+        int iteration = 0;
+        bool shutdown = false;
+        object timeout = null;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (new FakeDriver ());
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
+        Application.Run<Toplevel> ().Dispose ();
+        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
+
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        Assert.Equal (1, iteration);
+
+        Application.Shutdown ();
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                Application.RemoveTimeout (timeout);
+                timeout = null;
+            }
+        }
+
+        Assert.True (initialized);
+        Assert.True (shutdown);
+
+#if DEBUG_IDISPOSABLE
+        Assert.Empty (Responder.Instances);
+#endif
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
+
+        return;
+
+        void OnApplicationOnInitializedChanged (object s, StateEventArgs<bool> a)
+        {
+            _output.WriteLine ("OnApplicationOnInitializedChanged: {0}", a.NewValue);
+            if (a.NewValue)
+            {
+                Application.Iteration += OnApplicationOnIteration;
+                initialized = true;
+                lock (_timeoutLock)
+                {
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
+                }
+            }
+            else
+            {
+                Application.Iteration -= OnApplicationOnIteration;
+                shutdown = true;
+            }
+        }
+
+        bool ForceCloseCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                _output.WriteLine ($"ForceCloseCallback. iteration: {iteration}");
+                if (timeout is { })
+                {
+                    timeout = null;
+                }
+            }
+            Application.ResetState (true);
+            Assert.Fail ($"Failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
+
+            return false;
+        }
+
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            _output.WriteLine ("Iteration: {0}", iteration);
+            iteration++;
+            Assert.True (iteration < 2, "Too many iterations, something is wrong.");
+            if (Application._initialized)
+            {
+                _output.WriteLine ("  Pressing QuitKey");
+                Application.OnKeyDown (Application.QuitKey);
+            }
+        }
+    }
+
     [Fact]
     public void AlternateForwardKey_AlternateBackwardKey_Tests ()
     {

+ 17 - 5
UnitTests/Application/MainLoopTests.cs

@@ -1,4 +1,5 @@
 using System.Diagnostics;
+using System.IO;
 
 // Alias Console to MockConsole so we don't accidentally use Console
 
@@ -619,16 +620,22 @@ public class MainLoopTests
                        );
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public async Task InvokeLeakTest ()
+    [Theory]
+    [InlineData (typeof (FakeDriver))]
+    //[InlineData (typeof (NetDriver))] // BUGBUG: NetDriver never exits in this test
+
+    //[InlineData (typeof (ANSIDriver))]
+    //[InlineData (typeof (WindowsDriver))] // BUGBUG: NetDriver never exits in this test
+    //[InlineData (typeof (CursesDriver))] // BUGBUG: CursesDriver never exits in this test
+    public async Task InvokeLeakTest (Type driverType)
     {
+        Application.Init (driverName: driverType.Name);
         Random r = new ();
         TextField tf = new ();
         var top = new Toplevel ();
         top.Add (tf);
 
-        const int numPasses = 5;
+        const int numPasses = 2;
         const int numIncrements = 500;
         const int pollMs = 2500;
 
@@ -641,10 +648,10 @@ public class MainLoopTests
 
         Assert.Equal (numIncrements * numPasses, tbCounter);
         top.Dispose ();
+        Application.Shutdown ();
     }
 
     [Theory]
-    [AutoInitShutdown]
     [MemberData (nameof (TestAddIdle))]
     public void Mainloop_Invoke_Or_AddIdle_Can_Be_Used_For_Events_Or_Actions (
         Action action,
@@ -658,6 +665,9 @@ public class MainLoopTests
         int pfour
     )
     {
+        // TODO: Expand this test to test all drivers
+        Application.Init (new FakeDriver());
+
         total = 0;
         btn = null;
         clickMe = pclickMe;
@@ -720,6 +730,8 @@ public class MainLoopTests
         Assert.True (taskCompleted);
         Assert.Equal (clickMe, btn.Text);
         Assert.Equal (four, total);
+
+        Application.Shutdown ();
     }
 
     [Fact]

+ 12 - 4
UnitTests/Application/SynchronizatonContextTests.cs

@@ -5,9 +5,9 @@ namespace Terminal.Gui.ApplicationTests;
 public class SyncrhonizationContextTests
 {
     [Fact]
-    [AutoInitShutdown]
     public void SynchronizationContext_CreateCopy ()
     {
+        Application.Init ();
         SynchronizationContext context = SynchronizationContext.Current;
         Assert.NotNull (context);
 
@@ -15,12 +15,17 @@ public class SyncrhonizationContextTests
         Assert.NotNull (contextCopy);
 
         Assert.NotEqual (context, contextCopy);
+        Application.Shutdown ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void SynchronizationContext_Post ()
+    [Theory]
+    [InlineData (typeof (FakeDriver))]
+    //[InlineData (typeof (NetDriver))]
+    [InlineData (typeof (WindowsDriver))]
+    //[InlineData (typeof (CursesDriver))]
+    public void SynchronizationContext_Post (Type driverType)
     {
+        Application.Init (driverName: driverType.Name);
         SynchronizationContext context = SynchronizationContext.Current;
 
         var success = false;
@@ -48,12 +53,14 @@ public class SyncrhonizationContextTests
         // blocks here until the RequestStop is processed at the end of the test
         Application.Run ().Dispose ();
         Assert.True (success);
+        Application.Shutdown ();
     }
 
     [Fact]
     [AutoInitShutdown]
     public void SynchronizationContext_Send ()
     {
+        Application.Init ();
         SynchronizationContext context = SynchronizationContext.Current;
 
         var success = false;
@@ -81,5 +88,6 @@ public class SyncrhonizationContextTests
         // blocks here until the RequestStop is processed at the end of the test
         Application.Run ().Dispose ();
         Assert.True (success);
+        Application.Shutdown ();
     }
 }

+ 9 - 0
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -1,5 +1,6 @@
 using System.Reflection;
 using System.Text.Json;
+using Xunit.Abstractions;
 using static Terminal.Gui.ConfigurationManager;
 #pragma warning disable IDE1006
 
@@ -7,6 +8,13 @@ namespace Terminal.Gui.ConfigurationTests;
 
 public class ConfigurationManagerTests
 {
+    private readonly ITestOutputHelper _output;
+
+    public ConfigurationManagerTests (ITestOutputHelper output)
+    {
+        _output = output;
+    }
+
     public static readonly JsonSerializerOptions _jsonOptions = new ()
     {
         Converters = { new AttributeJsonConverter (), new ColorJsonConverter () }
@@ -402,6 +410,7 @@ public class ConfigurationManagerTests
         // Application is a static class
         PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
         Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
+        
 
         // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
         pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");

+ 1 - 1
UnitTests/Drawing/AlignerTests.cs

@@ -218,7 +218,7 @@ public class AlignerTests (ITestOutputHelper output)
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3 }, 9, new [] { 0, 2, 6 })]
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3 }, 10, new [] { 0, 2, 7 })]
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3 }, 11, new [] { 0, 2, 8 })]
-    [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3 }, 5, new [] { -1, 0, 2 })] // 5 is too small to fit the items. The first item is at -1.})]
+    [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3 }, 5, new [] { 0, 1, 2 })] 
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })]
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })]
     [InlineData (Alignment.Start, AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems | AlignmentModes.IgnoreFirstOrLast, new [] { 3, 3, 3 }, 21, new [] { 0, 4, 18 })]

+ 84 - 36
UnitTests/UICatalog/ScenarioTests.cs

@@ -23,6 +23,7 @@ public class ScenarioTests : TestsAllViews
                      .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario)))
                      .Select (type => new object [] { type });
 
+    private object _timeoutLock;
 
     /// <summary>
     ///     <para>This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run.</para>
@@ -30,60 +31,107 @@ public class ScenarioTests : TestsAllViews
     /// </summary>
     [Theory]
     [MemberData (nameof (AllScenarioTypes))]
-    public void Run_All_Scenarios (Type scenarioType)
+    public void All_Scenarios_Quit_And_Init_Shutdown_Properly (Type scenarioType)
     {
-        _output.WriteLine ($"Running Scenario '{scenarioType}'");
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new object ();
+
+        // If a previous test failed, this will ensure that the Application is in a clean state
+        Application.ResetState (true);
 
+        _output.WriteLine ($"Running Scenario '{scenarioType}'");
         Scenario scenario = (Scenario)Activator.CreateInstance (scenarioType);
 
-        Application.Init (new FakeDriver ());
+        uint abortTime = 1500;
+        bool initialized = false;
+        bool shutdown = false;
+        object timeout = null;
 
-        // Press QuitKey 
-        Assert.Empty (FakeConsole.MockKeyPresses);
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
 
-        FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
+        Application.ForceDriver = "FakeDriver";
+        scenario.Main ();
+        scenario.Dispose ();
+        scenario = null;
+        Application.ForceDriver = string.Empty;
 
-        uint abortTime = 500;
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
 
-        // If the scenario doesn't close within 500ms, this will force it to quit
-        bool ForceCloseCallback ()
+        lock (_timeoutLock)
         {
-            if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0)
+            if (timeout is { })
             {
-                Application.RequestStop ();
-
-                // See #2474 for why this is commented out
-                Assert.Fail (
-                             $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
+                timeout = null;
             }
-
-            return false;
         }
 
-        //output.WriteLine ($"  Add timeout to force quit after {abortTime}ms");
-        _ = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
-
-        Application.Iteration += (s, a) =>
-                                 {
-                                     // Press QuitKey 
-                                     Assert.Empty (FakeConsole.MockKeyPresses);
-                                     FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
-
-                                     //output.WriteLine ($"  iteration {++iterations}");
-                                     if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0)
-                                     {
-                                         Application.RequestStop ();
-                                         Assert.Fail ($"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey}. Force quit.");
-                                     }
-                                 };
 
-        scenario.Main ();
-        scenario.Dispose ();
+        Assert.True (initialized);
+        Assert.True (shutdown);
 
-        Application.Shutdown ();
 #if DEBUG_IDISPOSABLE
         Assert.Empty (Responder.Instances);
 #endif
+
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
+
+        return;
+
+
+        void OnApplicationOnInitializedChanged (object s, StateEventArgs<bool> a)
+        {
+            if (a.NewValue)
+            {
+                Assert.Equal (Key.Q.WithCtrl, Application.QuitKey);
+
+                Application.Iteration += OnApplicationOnIteration;
+                initialized = true;
+                lock (_timeoutLock)
+                {
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
+                }
+                _output.WriteLine ($"Initialized '{Application.Driver}'");
+                //Dictionary<Key, List<View>> bindings = Application.GetKeyBindings ();
+                //Assert.NotEmpty (bindings);
+                //_output.WriteLine ($"bindings: {string.Join (",", bindings.Keys)}");
+                //Assert.True (bindings.ContainsKey (Application.QuitKey));
+            }
+            else
+            {
+                Application.Iteration -= OnApplicationOnIteration;
+                shutdown = true;
+            }
+        }
+
+        // If the scenario doesn't close within 500ms, this will force it to quit
+        bool ForceCloseCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                if (timeout is { })
+                {
+                    timeout = null;
+                }
+            }
+            Assert.Fail (
+                         $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
+
+            Application.ResetState (true);
+            return false;
+        }
+
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            if (Application._initialized)
+            {
+                // Press QuitKey 
+                //_output.WriteLine ($"Forcing Quit with {Application.QuitKey}");
+                Application.OnKeyDown (Application.QuitKey);
+            }
+        }
     }
 
     [Fact]

+ 22 - 4
UnitTests/View/HotKeyTests.cs

@@ -81,7 +81,7 @@ public class HotKeyTests
     [InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true)]
     [InlineData (KeyCode.CtrlMask, false)]
     [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, false)]
-    public void KeyPress_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
+    public void NewKeyDownEvent_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
     {
         var view = new View { HotKeySpecifier = (Rune)'^', Title = "^Test" };
         view.CanFocus = true;
@@ -91,10 +91,10 @@ public class HotKeyTests
     }
 
     [Fact]
-    public void ProcessKeyDown_Ignores_KeyBindings_Out_Of_Scope_SuperView ()
+    public void NewKeyDownEvent_Ignores_Focus_KeyBindings_SuperView ()
     {
         var view = new View ();
-        view.KeyBindings.Add (Key.A, Command.HotKey);
+        view.KeyBindings.Add (Key.A, Command.HotKey); // implies KeyBindingScope.Focused - so this should not be invoked
         view.InvokingKeyBindings += (s, e) => { Assert.Fail (); };
 
         var superView = new View ();
@@ -105,7 +105,25 @@ public class HotKeyTests
     }
 
     [Fact]
-    public void ProcessKeyDown_Invokes_HotKey_Command_With_SuperView ()
+    public void NewKeyDownEvent_Honors_HotKey_KeyBindings_SuperView ()
+    {
+        var view = new View ();
+        view.KeyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey); 
+        bool invoked = false;
+        view.InvokingKeyBindings += (s, e) => { invoked = true; };
+
+        var superView = new View ();
+        superView.Add (view);
+
+        var ke = Key.A;
+        superView.NewKeyDownEvent (ke);
+
+        Assert.True (invoked);
+    }
+
+
+    [Fact]
+    public void NewKeyDownEvent_InNewKeyDownEventvokes_HotKey_Command_With_SuperView ()
     {
         var view = new View { HotKeySpecifier = (Rune)'^', Title = "^Test" };
 

+ 3 - 3
UnitTests/View/KeyboardEventTests.cs

@@ -421,7 +421,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
         var view = new KeyBindingsTestView ();
         view.CommandReturns = toReturn;
 
-        bool? result = view.OnInvokingKeyBindings (Key.A);
+        bool? result = view.OnInvokingKeyBindings (Key.A, KeyBindingScope.HotKey | KeyBindingScope.Focused);
         Assert.Equal (expected, result);
     }
 
@@ -449,9 +449,9 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
         public bool OnKeyUpContinued { get; set; }
         public override string Text { get; set; }
 
-        public override bool? OnInvokingKeyBindings (Key keyEvent)
+        public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
         {
-            bool? handled = base.OnInvokingKeyBindings (keyEvent);
+            bool? handled = base.OnInvokingKeyBindings (keyEvent, scope);
 
             if (handled != null && (bool)handled)
             {

+ 127 - 128
UnitTests/View/NavigationTests.cs

@@ -620,133 +620,133 @@ public class NavigationTests (ITestOutputHelper output)
         top1.Dispose ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void HotKey_Will_Invoke_KeyPressed_Only_For_The_MostFocused_With_Top_KeyPress_Event ()
-    {
-        var sbQuiting = false;
-        var tfQuiting = false;
-        var topQuiting = false;
-
-        var sb = new StatusBar (
-                                new StatusItem []
-                                {
-                                    new (
-                                         KeyCode.CtrlMask | KeyCode.Q,
-                                         "~^Q~ Quit",
-                                         () => sbQuiting = true
-                                        )
-                                }
-                               );
-        var tf = new TextField ();
-        tf.KeyDown += Tf_KeyPressed;
-
-        void Tf_KeyPressed (object sender, Key obj)
-        {
-            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
-            {
-                obj.Handled = tfQuiting = true;
-            }
-        }
-
-        var win = new Window ();
-        win.Add (sb, tf);
-        Toplevel top = new ();
-        top.KeyDown += Top_KeyPress;
-
-        void Top_KeyPress (object sender, Key obj)
-        {
-            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
-            {
-                obj.Handled = topQuiting = true;
-            }
-        }
-
-        top.Add (win);
-        Application.Begin (top);
-
-        Assert.False (sbQuiting);
-        Assert.False (tfQuiting);
-        Assert.False (topQuiting);
-
-        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
-        Assert.False (sbQuiting);
-        Assert.True (tfQuiting);
-        Assert.False (topQuiting);
-
-#if BROKE_WITH_2927
-        tf.KeyPressed -= Tf_KeyPress;
-        tfQuiting = false;
-        Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
-        Application.MainLoop.RunIteration ();
-        Assert.True (sbQuiting);
-        Assert.False (tfQuiting);
-        Assert.False (topQuiting);
-
-        sb.RemoveItem (0);
-        sbQuiting = false;
-        Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
-        Application.MainLoop.RunIteration ();
-        Assert.False (sbQuiting);
-        Assert.False (tfQuiting);
-
-// This test is now invalid because `win` is focused, so it will receive the keypress
-        Assert.True (topQuiting);
-#endif
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void HotKey_Will_Invoke_KeyPressed_Only_For_The_MostFocused_Without_Top_KeyPress_Event ()
-    {
-        var sbQuiting = false;
-        var tfQuiting = false;
-
-        var sb = new StatusBar (
-                                new StatusItem []
-                                {
-                                    new (
-                                         KeyCode.CtrlMask | KeyCode.Q,
-                                         "~^Q~ Quit",
-                                         () => sbQuiting = true
-                                        )
-                                }
-                               );
-        var tf = new TextField ();
-        tf.KeyDown += Tf_KeyPressed;
-
-        void Tf_KeyPressed (object sender, Key obj)
-        {
-            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
-            {
-                obj.Handled = tfQuiting = true;
-            }
-        }
-
-        var win = new Window ();
-        win.Add (sb, tf);
-        Toplevel top = new ();
-        top.Add (win);
-        Application.Begin (top);
-
-        Assert.False (sbQuiting);
-        Assert.False (tfQuiting);
-
-        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
-        Assert.False (sbQuiting);
-        Assert.True (tfQuiting);
-
-        tf.KeyDown -= Tf_KeyPressed;
-        tfQuiting = false;
-        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
-        Application.MainLoop.RunIteration ();
-#if BROKE_WITH_2927
-        Assert.True (sbQuiting);
-        Assert.False (tfQuiting);
-#endif
-        top.Dispose ();
-    }
+//    [Fact]
+//    [AutoInitShutdown]
+//    public void HotKey_Will_Invoke_KeyPressed_Only_For_The_MostFocused_With_Top_KeyPress_Event ()
+//    {
+//        var sbQuiting = false;
+//        var tfQuiting = false;
+//        var topQuiting = false;
+
+//        var sb = new StatusBar (
+//                                new Shortcut []
+//                                {
+//                                    new (
+//                                         KeyCode.CtrlMask | KeyCode.Q,
+//                                         "Quit",
+//                                         () => sbQuiting = true
+//                                        )
+//                                }
+//                               );
+//        var tf = new TextField ();
+//        tf.KeyDown += Tf_KeyPressed;
+
+//        void Tf_KeyPressed (object sender, Key obj)
+//        {
+//            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
+//            {
+//                obj.Handled = tfQuiting = true;
+//            }
+//        }
+
+//        var win = new Window ();
+//        win.Add (sb, tf);
+//        Toplevel top = new ();
+//        top.KeyDown += Top_KeyPress;
+
+//        void Top_KeyPress (object sender, Key obj)
+//        {
+//            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
+//            {
+//                obj.Handled = topQuiting = true;
+//            }
+//        }
+
+//        top.Add (win);
+//        Application.Begin (top);
+
+//        Assert.False (sbQuiting);
+//        Assert.False (tfQuiting);
+//        Assert.False (topQuiting);
+
+//        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
+//        Assert.False (sbQuiting);
+//        Assert.True (tfQuiting);
+//        Assert.False (topQuiting);
+
+//#if BROKE_WITH_2927
+//        tf.KeyPressed -= Tf_KeyPress;
+//        tfQuiting = false;
+//        Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
+//        Application.MainLoop.RunIteration ();
+//        Assert.True (sbQuiting);
+//        Assert.False (tfQuiting);
+//        Assert.False (topQuiting);
+
+//        sb.RemoveItem (0);
+//        sbQuiting = false;
+//        Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
+//        Application.MainLoop.RunIteration ();
+//        Assert.False (sbQuiting);
+//        Assert.False (tfQuiting);
+
+//// This test is now invalid because `win` is focused, so it will receive the keypress
+//        Assert.True (topQuiting);
+//#endif
+//        top.Dispose ();
+//    }
+
+//    [Fact]
+//    [AutoInitShutdown]
+//    public void HotKey_Will_Invoke_KeyPressed_Only_For_The_MostFocused_Without_Top_KeyPress_Event ()
+//    {
+//        var sbQuiting = false;
+//        var tfQuiting = false;
+
+//        var sb = new StatusBar (
+//                                new Shortcut []
+//                                {
+//                                    new (
+//                                         KeyCode.CtrlMask | KeyCode.Q,
+//                                         "~^Q~ Quit",
+//                                         () => sbQuiting = true
+//                                        )
+//                                }
+//                               );
+//        var tf = new TextField ();
+//        tf.KeyDown += Tf_KeyPressed;
+
+//        void Tf_KeyPressed (object sender, Key obj)
+//        {
+//            if (obj.KeyCode == (KeyCode.Q | KeyCode.CtrlMask))
+//            {
+//                obj.Handled = tfQuiting = true;
+//            }
+//        }
+
+//        var win = new Window ();
+//        win.Add (sb, tf);
+//        Toplevel top = new ();
+//        top.Add (win);
+//        Application.Begin (top);
+
+//        Assert.False (sbQuiting);
+//        Assert.False (tfQuiting);
+
+//        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
+//        Assert.False (sbQuiting);
+//        Assert.True (tfQuiting);
+
+//        tf.KeyDown -= Tf_KeyPressed;
+//        tfQuiting = false;
+//        Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
+//        Application.MainLoop.RunIteration ();
+//#if BROKE_WITH_2927
+//        Assert.True (sbQuiting);
+//        Assert.False (tfQuiting);
+//#endif
+//        top.Dispose ();
+//    }
 
     [Fact]
     [SetupFakeDriver]
@@ -1535,7 +1535,6 @@ public class NavigationTests (ITestOutputHelper output)
     }
 
     [Fact]
-    [AutoInitShutdown]
     public void WindowDispose_CanFocusProblem ()
     {
         // Arrange

+ 105 - 0
UnitTests/Views/BarTests.cs

@@ -0,0 +1,105 @@
+using JetBrains.Annotations;
+
+namespace Terminal.Gui.ViewsTests;
+
+[TestSubject (typeof (Bar))]
+public class BarTests
+{
+    [Fact]
+    public void Constructor_Defaults ()
+    {
+        var bar = new Bar ();
+
+        Assert.NotNull (bar);
+        Assert.True (bar.CanFocus);
+        Assert.IsType<DimAuto> (bar.Width);
+        Assert.IsType<DimAuto> (bar.Height);
+
+        // TOOD: more
+    }
+
+    [Fact]
+    public void Constructor_InitializesEmpty_WhenNoShortcutsProvided ()
+    {
+        var bar = new Bar ();
+        Assert.Empty (bar.Subviews);
+    }
+
+    [Fact]
+    public void Constructor_InitializesWithShortcuts_WhenProvided ()
+    {
+        var shortcuts = new List<Shortcut>
+        {
+            new Shortcut(Key.Empty, "Command1", null, null),
+            new Shortcut(Key.Empty, "Command2", null, null)
+        };
+
+        var bar = new Bar (shortcuts);
+
+        Assert.Equal (shortcuts.Count, bar.Subviews.Count);
+        for (int i = 0; i < shortcuts.Count; i++)
+        {
+            Assert.Same (shortcuts [i], bar.Subviews [i]);
+        }
+    }
+
+    [Fact]
+    public void OrientationProperty_SetsCorrectly ()
+    {
+        var bar = new Bar ();
+        Assert.Equal (Orientation.Horizontal, bar.Orientation); // Default value
+
+        bar.Orientation = Orientation.Vertical;
+        Assert.Equal (Orientation.Vertical, bar.Orientation);
+    }
+
+    [Fact]
+    public void AlignmentModesProperty_SetsCorrectly ()
+    {
+        var bar = new Bar ();
+        Assert.Equal (AlignmentModes.StartToEnd, bar.AlignmentModes); // Default value
+
+        bar.AlignmentModes = AlignmentModes.EndToStart;
+        Assert.Equal (AlignmentModes.EndToStart, bar.AlignmentModes);
+    }
+
+    [Fact]
+    public void AddShortcutAt_InsertsShortcutCorrectly ()
+    {
+        var bar = new Bar ();
+        var shortcut = new Shortcut (Key.Empty, "Command", null, null);
+        bar.AddShortcutAt (0, shortcut);
+
+        Assert.Contains (shortcut, bar.Subviews);
+    }
+
+    [Fact]
+    public void RemoveShortcut_RemovesShortcutCorrectly ()
+    {
+        var shortcut1 = new Shortcut (Key.Empty, "Command1", null, null);
+        var shortcut2 = new Shortcut (Key.Empty, "Command2", null, null);
+        var bar = new Bar (new List<Shortcut> { shortcut1, shortcut2 });
+
+        var removedShortcut = bar.RemoveShortcut (0);
+
+        Assert.Same (shortcut1, removedShortcut);
+        Assert.DoesNotContain (shortcut1, bar.Subviews);
+        Assert.Contains (shortcut2, bar.Subviews);
+    }
+
+    [Fact]
+    public void Layout_ChangesBasedOnOrientation ()
+    {
+        var shortcut1 = new Shortcut (Key.Empty, "Command1", null, null);
+        var shortcut2 = new Shortcut (Key.Empty, "Command2", null, null);
+        var bar = new Bar (new List<Shortcut> { shortcut1, shortcut2 });
+
+        bar.Orientation = Orientation.Horizontal;
+        bar.LayoutSubviews ();
+        // TODO: Assert specific layout expectations for horizontal orientation
+
+        bar.Orientation = Orientation.Vertical;
+        bar.LayoutSubviews ();
+        // TODO: Assert specific layout expectations for vertical orientation
+    }
+}

+ 91 - 3
UnitTests/Views/ButtonTests.cs

@@ -258,6 +258,93 @@ public class ButtonTests (ITestOutputHelper output)
         Assert.True (clicked);
     }
 
+    [Theory]
+    [InlineData (false, 0)]
+    [InlineData (true, 1)]
+    public void Space_Fires_Accept (bool focused, int expected)
+    {
+        View superView = new View ()
+        {
+            CanFocus = true,
+        };
+
+        Button button = new ();
+
+        button.CanFocus = focused;
+
+        int acceptInvoked = 0;
+        button.Accept += (s, e) => acceptInvoked++;
+
+        superView.Add (button);
+        button.SetFocus ();
+        Assert.Equal (focused, button.HasFocus);
+
+        superView.NewKeyDownEvent (Key.Space);
+
+        Assert.Equal (expected, acceptInvoked);
+
+        superView.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (false, 0)]
+    [InlineData (true, 1)]
+    public void Enter_Fires_Accept (bool focused, int expected)
+    {
+        View superView = new View ()
+        {
+            CanFocus = true,
+        };
+
+        Button button = new ();
+
+        button.CanFocus = focused;
+
+        int acceptInvoked = 0;
+        button.Accept += (s, e) => acceptInvoked++;
+
+        superView.Add (button);
+        button.SetFocus ();
+        Assert.Equal (focused, button.HasFocus);
+
+        superView.NewKeyDownEvent (Key.Enter);
+
+        Assert.Equal (expected, acceptInvoked);
+
+        superView.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (false, 1)]
+    [InlineData (true, 1)]
+    public void HotKey_Fires_Accept (bool focused, int expected)
+    {
+        View superView = new View ()
+        {
+            CanFocus = true,
+        };
+
+        Button button = new ()
+        {
+            HotKey = Key.A
+        };
+
+        button.CanFocus = focused;
+
+        int acceptInvoked = 0;
+        button.Accept += (s, e) => acceptInvoked++;
+
+        superView.Add (button);
+        button.SetFocus ();
+        Assert.Equal (focused, button.HasFocus);
+
+        superView.NewKeyDownEvent (Key.A);
+
+        Assert.Equal (expected, acceptInvoked);
+
+        superView.Dispose ();
+    }
+
     /// <summary>
     ///     This test demonstrates how to change the activation key for Button as described in the README.md keyboard
     ///     handling section
@@ -279,7 +366,9 @@ public class ButtonTests (ITestOutputHelper output)
         top.Add (btn);
         Application.Begin (top);
 
-        // default keybinding is Space which results in keypress
+        Assert.True (btn.HasFocus);
+
+        // default keybinding is Space which results in Command.Accept (when focused)
         Application.OnKeyDown (new ((KeyCode)' '));
         Assert.Equal (1, pressed);
 
@@ -292,8 +381,7 @@ public class ButtonTests (ITestOutputHelper output)
         Assert.Equal (1, pressed);
 
         // Set a new binding of b for the click (Accept) event
-        btn.KeyBindings.Add (Key.B, Command.HotKey);
-        btn.KeyBindings.Add (Key.B, Command.Accept);
+        btn.KeyBindings.Add (Key.B, Command.HotKey); // b will now trigger the Accept command (when focused or not)
 
         // now pressing B should call the button click event
         Application.OnKeyDown (Key.B);

+ 0 - 132
UnitTests/Views/ContextMenuTests.cs

@@ -113,138 +113,6 @@ public class ContextMenuTests (ITestOutputHelper output)
         top.Dispose ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void ContextMenu_On_Toplevel_With_A_MenuBar_TextField_StatusBar ()
-    {
-        Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new MenuBarItem ("File", "", null),
-                new MenuBarItem ("Edit", "", null)
-            ]
-        };
-
-        var label = new Label { X = 2, Y = 3, Text = "Label:" };
-
-        var tf = new TextField { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = 20, Text = "TextField" };
-
-        var statusBar = new StatusBar (
-                                       [
-                                           new StatusItem (KeyCode.F1, "~F1~ Help", null),
-                                           new StatusItem (KeyCode.CtrlMask | KeyCode.Q, "~^Q~ Quit", null)
-                                       ]
-                                      );
-
-        var top = new Toplevel ();
-        top.Add (menu, label, tf, statusBar);
-        ((FakeDriver)Application.Driver).SetBufferSize (45, 17);
-        Application.Begin (top);
-
-        Assert.Equal (new Rectangle (9, 3, 20, 1), tf.Frame);
-        Assert.True (tf.HasFocus);
-
-        tf.ContextMenu.Show ();
-        Assert.True (ContextMenu.IsShow);
-        Assert.Equal (new Point (9, 3), tf.ContextMenu.Position);
-        Application.Top.Draw ();
-
-        var expected = @"
- File  Edit                     
-                                
-                                
-  Label: TextField              
-         ┌─────────────────────┐
-         │ Select All   Ctrl+T │
-         │ Delete All   Ctrl+R │
-         │ Copy         Ctrl+C │
-         │ Cut          Ctrl+X │
-         │ Paste        Ctrl+V │
-         │ Undo         Ctrl+Z │
-         │ Redo         Ctrl+Y │
-         └─────────────────────┘
-                                
-                                
-                                
- F1 Help │ ^Q Quit              
-";
-
-        Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new Rectangle (1, 0, 32, 17), pos);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void ContextMenu_On_Toplevel_With_A_MenuBar_Window_TextField_StatusBar ()
-    {
-        Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
-
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new MenuBarItem ("File", "", null),
-                new MenuBarItem ("Edit", "", null)
-            ]
-        };
-
-        var label = new Label { X = 2, Y = 3, Text = "Label:" };
-
-        var tf = new TextField { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = 20, Text = "TextField" };
-
-        var win = new Window ();
-        win.Add (label, tf);
-
-        var statusBar = new StatusBar (
-                                       new []
-                                       {
-                                           new StatusItem (KeyCode.F1, "~F1~ Help", null),
-                                           new StatusItem (KeyCode.CtrlMask | KeyCode.Q, "~^Q~ Quit", null)
-                                       }
-                                      );
-
-        var top = new Toplevel ();
-        top.Add (menu, win, statusBar);
-        Application.Begin (top);
-        ((FakeDriver)Application.Driver).SetBufferSize (44, 17);
-
-        Assert.Equal (new Rectangle (9, 3, 20, 1), tf.Frame);
-        Assert.True (tf.HasFocus);
-
-        tf.ContextMenu.Show ();
-        Assert.True (ContextMenu.IsShow);
-        Assert.Equal (new Point (10, 5), tf.ContextMenu.Position);
-        Application.Top.Draw ();
-
-        var expected = @"
- File  Edit                                 
-┌──────────────────────────────────────────┐
-│                                          │
-│                                          │
-│                                          │
-│  Label: TextField                        │
-│         ┌─────────────────────┐          │
-│         │ Select All   Ctrl+T │          │
-│         │ Delete All   Ctrl+R │          │
-│         │ Copy         Ctrl+C │          │
-│         │ Cut          Ctrl+X │          │
-│         │ Paste        Ctrl+V │          │
-│         │ Undo         Ctrl+Z │          │
-│         │ Redo         Ctrl+Y │          │
-│         └─────────────────────┘          │
-└──────────────────────────────────────────┘
- F1 Help │ ^Q Quit                          
-";
-
-        Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new Rectangle (1, 0, 44, 17), pos);
-        top.Dispose ();
-    }
-
     [Fact]
     [AutoInitShutdown]
     public void Draw_A_ContextMenu_Over_A_Borderless_Top ()

+ 2 - 64
UnitTests/Views/LabelTests.cs

@@ -907,69 +907,6 @@ e
         top.Dispose ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void AnchorEnd_Better_Than_Bottom_Equal_Inside_Window_With_MenuBar_And_StatusBar_On_Toplevel ()
-    {
-        var win = new Window ();
-
-        // Label is AutoSize == true
-        var label = new Label
-        {
-            Text = "This should be the last line.",
-            ColorScheme = Colors.ColorSchemes ["Menu"],
-
-            X = 0,
-            Y = Pos.AnchorEnd (1)
-        };
-
-        win.Add (label);
-
-        var menu = new MenuBar { Menus = new MenuBarItem [] { new ("Menu", "", null) } };
-        var status = new StatusBar (new StatusItem [] { new (KeyCode.F1, "~F1~ Help", null) });
-        Toplevel top = new ();
-        top.Add (win, menu, status);
-        RunState rs = Application.Begin (top);
-
-        Assert.Equal (new (0, 0, 80, 25), top.Frame);
-        Assert.Equal (new (0, 0, 80, 1), menu.Frame);
-        Assert.Equal (new (0, 24, 80, 1), status.Frame);
-        Assert.Equal (new (0, 1, 80, 23), win.Frame);
-        Assert.Equal (new (0, 20, 29, 1), label.Frame);
-
-        var expected = @"
- Menu                                                                           
-┌──────────────────────────────────────────────────────────────────────────────┐
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│                                                                              │
-│This should be the last line.                                                 │
-└──────────────────────────────────────────────────────────────────────────────┘
- F1 Help                                                                        
-";
-
-        TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-        Application.End (rs);
-        top.Dispose ();
-    }
-
     // TODO: This is a Label test. Move to label tests if there's not already a test for this.
     [Fact]
     [AutoInitShutdown]
@@ -1018,6 +955,7 @@ e
         top.Dispose ();
     }
 
+#if V2_STATUSBAR
     // TODO: This is a Label test. Move to label tests if there's not already a test for this.
 
     [Fact]
@@ -1083,7 +1021,7 @@ e
         Application.End (rs);
         top.Dispose ();
     }
-
+#endif
     // TODO: This is a Dim test. Move to Dim tests.
 
     [Fact]

+ 1 - 0
UnitTests/Views/RadioGroupTests.cs

@@ -73,6 +73,7 @@ public class RadioGroupTests (ITestOutputHelper output)
     public void KeyBindings_Command ()
     {
         var rg = new RadioGroup { RadioLabels = new [] { "Test", "New Test" } };
+        rg.SetFocus();
 
         Assert.True (rg.NewKeyDownEvent (Key.CursorUp));
         Assert.True (rg.NewKeyDownEvent (Key.CursorDown));

+ 317 - 0
UnitTests/Views/ShortcutTests.cs

@@ -0,0 +1,317 @@
+using JetBrains.Annotations;
+
+namespace Terminal.Gui.ViewsTests;
+
+[TestSubject (typeof (Shortcut))]
+public class ShortcutTests
+{
+    [Fact]
+    public void Constructor_Defaults ()
+    {
+        var shortcut = new Shortcut ();
+
+        Assert.NotNull (shortcut);
+        Assert.True (shortcut.CanFocus);
+        Assert.IsType<DimAuto> (shortcut.Width);
+        Assert.IsType<DimAuto> (shortcut.Height);
+
+        // TOOD: more
+    }
+
+    [Theory]
+    [InlineData ("", "", KeyCode.Null, 2)]
+    [InlineData ("C", "", KeyCode.Null, 3)]
+    [InlineData ("", "H", KeyCode.Null, 5)]
+    [InlineData ("", "", KeyCode.K, 5)]
+    [InlineData ("C", "", KeyCode.K, 6)]
+    [InlineData ("C", "H", KeyCode.Null, 6)]
+    [InlineData ("", "H", KeyCode.K, 8)]
+    [InlineData ("C", "H", KeyCode.K, 9)]
+    public void NaturalSize (string command, string help, Key key, int expectedWidth)
+    {
+        var shortcut = new Shortcut
+        {
+            Title = command,
+            HelpText = help,
+            Key = key
+        };
+
+        Assert.IsType<DimAuto> (shortcut.Width);
+        Assert.IsType<DimAuto> (shortcut.Height);
+
+        shortcut.LayoutSubviews ();
+        shortcut.SetRelativeLayout (new (100, 100));
+
+        // |0123456789
+        // | C  H  K |
+        Assert.Equal (expectedWidth, shortcut.Frame.Width);
+    }
+
+    [Theory]
+    [InlineData (5, 0, 3, 6)]
+    [InlineData (6, 0, 3, 6)]
+    [InlineData (7, 0, 3, 6)]
+    [InlineData (8, 0, 3, 6)]
+    [InlineData (9, 0, 3, 6)]
+    [InlineData (10, 0, 4, 7)]
+    [InlineData (11, 0, 5, 8)]
+    public void Set_Width_Layouts_Correctly (int width, int expectedCmdX, int expectedHelpX, int expectedKeyX)
+    {
+        var shortcut = new Shortcut
+        {
+            Width = width,
+            Title = "C",
+            Text = "H",
+            Key = Key.K
+        };
+
+        shortcut.LayoutSubviews ();
+        shortcut.SetRelativeLayout (new (100, 100));
+
+        // 0123456789
+        // -C--H--K- 
+        Assert.Equal (expectedCmdX, shortcut.CommandView.Frame.X);
+        Assert.Equal (expectedHelpX, shortcut.HelpView.Frame.X);
+        Assert.Equal (expectedKeyX, shortcut.KeyView.Frame.X);
+    }
+
+    [Fact]
+    public void CommandView_Text_And_Title_Track ()
+    {
+        var shortcut = new Shortcut
+        {
+            Title = "T"
+        };
+
+        Assert.Equal (shortcut.Title, shortcut.CommandView.Text);
+
+        shortcut = new ();
+
+        shortcut.CommandView = new()
+        {
+            Text = "T"
+        };
+        Assert.Equal (shortcut.Title, shortcut.CommandView.Text);
+    }
+
+    [Fact]
+    public void HelpText_And_Text_Are_The_Same ()
+    {
+        var shortcut = new Shortcut
+        {
+            Text = "H"
+        };
+
+        Assert.Equal (shortcut.Text, shortcut.HelpText);
+
+        shortcut = new()
+        {
+            HelpText = "H"
+        };
+
+        Assert.Equal (shortcut.Text, shortcut.HelpText);
+    }
+
+    [Theory]
+    [InlineData (KeyCode.Null, "")]
+    [InlineData (KeyCode.F1, "F1")]
+    public void KeyView_Text_Tracks_Key (Key key, string expected)
+    {
+        var shortcut = new Shortcut
+        {
+            Key = key
+        };
+
+        Assert.Equal (expected, shortcut.KeyView.Text);
+    }
+
+    // Test Key
+    [Fact]
+    public void Key_Defaults_To_Empty ()
+    {
+        var shortcut = new Shortcut ();
+
+        Assert.Equal (Key.Empty, shortcut.Key);
+    }
+
+    [Fact]
+    public void Key_Can_Be_Set ()
+    {
+        var shortcut = new Shortcut ();
+
+        shortcut.Key = Key.F1;
+
+        Assert.Equal (Key.F1, shortcut.Key);
+    }
+
+    [Fact]
+    public void Key_Can_Be_Set_To_Empty ()
+    {
+        var shortcut = new Shortcut ();
+
+        shortcut.Key = Key.Empty;
+
+        Assert.Equal (Key.Empty, shortcut.Key);
+    }
+
+    // Test KeyBindingScope
+
+    // Test Key gets bound correctly
+    [Fact]
+    public void KeyBindingScope_Defaults_To_HotKey ()
+    {
+        var shortcut = new Shortcut ();
+
+        Assert.Equal (KeyBindingScope.HotKey, shortcut.KeyBindingScope);
+    }
+
+    [Fact]
+    public void KeyBindingScope_Can_Be_Set ()
+    {
+        var shortcut = new Shortcut ();
+
+        shortcut.KeyBindingScope = KeyBindingScope.Application;
+
+        Assert.Equal (KeyBindingScope.Application, shortcut.KeyBindingScope);
+    }
+
+    [Fact]
+    public void Setting_Key_Binds_Key_To_CommandView_Accept ()
+    {
+        var shortcut = new Shortcut ();
+
+        shortcut.Key = Key.F1;
+
+        // TODO:
+    }
+
+    [Theory]
+    [InlineData (Orientation.Horizontal)]
+    [InlineData (Orientation.Vertical)]
+    public void Orientation_SetsCorrectly (Orientation orientation)
+    {
+        var shortcut = new Shortcut
+        {
+            Orientation = orientation
+        };
+
+        Assert.Equal (orientation, shortcut.Orientation);
+    }
+
+    [Theory]
+    [InlineData (AlignmentModes.StartToEnd)]
+    [InlineData (AlignmentModes.EndToStart)]
+    public void AlignmentModes_SetsCorrectly (AlignmentModes alignmentModes)
+    {
+        var shortcut = new Shortcut
+        {
+            AlignmentModes = alignmentModes
+        };
+
+        Assert.Equal (alignmentModes, shortcut.AlignmentModes);
+    }
+
+    [Fact]
+    public void Action_SetsAndGetsCorrectly ()
+    {
+        var actionInvoked = false;
+
+        var shortcut = new Shortcut
+        {
+            Action = () => { actionInvoked = true; }
+        };
+
+        shortcut.Action.Invoke ();
+
+        Assert.True (actionInvoked);
+    }
+
+    [Fact]
+    public void ColorScheme_SetsAndGetsCorrectly ()
+    {
+        var colorScheme = new ColorScheme ();
+
+        var shortcut = new Shortcut
+        {
+            ColorScheme = colorScheme
+        };
+
+        Assert.Same (colorScheme, shortcut.ColorScheme);
+    }
+
+    [Fact]
+    public void Subview_Visibility_Controlled_By_Removal ()
+    {
+        var shortcut = new Shortcut ();
+
+        Assert.True (shortcut.CommandView.Visible);
+        Assert.Contains (shortcut.CommandView, shortcut.Subviews);
+        Assert.True (shortcut.HelpView.Visible);
+        Assert.DoesNotContain (shortcut.HelpView, shortcut.Subviews);
+        Assert.True (shortcut.KeyView.Visible);
+        Assert.DoesNotContain (shortcut.KeyView, shortcut.Subviews);
+
+        shortcut.HelpText = "help";
+        Assert.True (shortcut.HelpView.Visible);
+        Assert.Contains (shortcut.HelpView, shortcut.Subviews);
+        Assert.True (shortcut.KeyView.Visible);
+        Assert.DoesNotContain (shortcut.KeyView, shortcut.Subviews);
+
+        shortcut.Key = Key.A;
+        Assert.True (shortcut.HelpView.Visible);
+        Assert.Contains (shortcut.HelpView, shortcut.Subviews);
+        Assert.True (shortcut.KeyView.Visible);
+        Assert.Contains (shortcut.KeyView, shortcut.Subviews);
+
+        shortcut.HelpView.Visible = false;
+        shortcut.ShowHide ();
+        Assert.False (shortcut.HelpView.Visible);
+        Assert.DoesNotContain (shortcut.HelpView, shortcut.Subviews);
+        Assert.True (shortcut.KeyView.Visible);
+        Assert.Contains (shortcut.KeyView, shortcut.Subviews);
+
+        shortcut.KeyView.Visible = false;
+        shortcut.ShowHide ();
+        Assert.False (shortcut.HelpView.Visible);
+        Assert.DoesNotContain (shortcut.HelpView, shortcut.Subviews);
+        Assert.False (shortcut.KeyView.Visible);
+        Assert.DoesNotContain (shortcut.KeyView, shortcut.Subviews);
+    }
+
+    [Fact]
+    public void Focus_CanFocus_Default_Is_True ()
+    {
+        Shortcut shortcut = new ();
+        shortcut.Key = Key.A;
+        shortcut.Text = "Help";
+        shortcut.Title = "Command";
+        Assert.True (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+    }
+
+    [Fact]
+    public void Focus_CanFocus_CommandView_Add_Tracks ()
+    {
+        Shortcut shortcut = new ();
+        Assert.True (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView = new () { CanFocus = true };
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = true;
+        Assert.True (shortcut.CommandView.CanFocus);
+
+        shortcut.CanFocus = false;
+        Assert.False (shortcut.CanFocus);
+        Assert.True (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = false;
+        Assert.False (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = true;
+        Assert.False (shortcut.CanFocus);
+        Assert.True (shortcut.CommandView.CanFocus);
+    }
+}

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