Browse Source

Merge branch 'v2_develop' into v2_2489_scroll-scrollbar-new

BDisp 1 year ago
parent
commit
057e89ce1d
100 changed files with 4389 additions and 1581 deletions
  1. 19 0
      CommunityToolkitExample/CommunityToolkitExample.csproj
  2. 8 0
      CommunityToolkitExample/LoginActions.cs
  3. 62 0
      CommunityToolkitExample/LoginView.Designer.cs
  4. 72 0
      CommunityToolkitExample/LoginView.cs
  5. 128 0
      CommunityToolkitExample/LoginViewModel.cs
  6. 6 0
      CommunityToolkitExample/Message.cs
  7. 26 0
      CommunityToolkitExample/Program.cs
  8. 154 0
      CommunityToolkitExample/README.md
  9. 1 0
      ReactiveExample/Program.cs
  10. 1 1
      Scripts/Terminal.Gui.PowerShell.Analyzers.psd1
  11. 1 1
      Scripts/Terminal.Gui.PowerShell.Build.psd1
  12. 1 1
      Scripts/Terminal.Gui.PowerShell.Core.psd1
  13. 1 1
      Scripts/Terminal.Gui.PowerShell.Git.psd1
  14. 1 1
      Scripts/Terminal.Gui.PowerShell.psd1
  15. 39 28
      Terminal.Gui/Application/Application.cs
  16. 9 4
      Terminal.Gui/Application/ApplicationKeyboard.cs
  17. 12 1
      Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
  18. 50 31
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  19. 0 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs
  20. 30 6
      Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
  21. 9 5
      Terminal.Gui/Drawing/Aligner.cs
  22. 5 2
      Terminal.Gui/Input/CommandContext.cs
  23. 3 0
      Terminal.Gui/Input/KeyBindingScope.cs
  24. 3 1
      Terminal.Gui/Input/KeyBindings.cs
  25. 1 6
      Terminal.Gui/View/Layout/Dim.cs
  26. 99 11
      Terminal.Gui/View/Layout/DimAuto.cs
  27. 24 0
      Terminal.Gui/View/Layout/Pos.cs
  28. 54 49
      Terminal.Gui/View/Layout/PosAlign.cs
  29. 1 0
      Terminal.Gui/View/View.cs
  30. 68 18
      Terminal.Gui/View/ViewAdornments.cs
  31. 5 5
      Terminal.Gui/View/ViewDrawing.cs
  32. 88 20
      Terminal.Gui/View/ViewKeyboard.cs
  33. 19 5
      Terminal.Gui/View/ViewSubViews.cs
  34. 229 0
      Terminal.Gui/Views/Bar.cs
  35. 1 1
      Terminal.Gui/Views/ColorPicker.cs
  36. 16 2
      Terminal.Gui/Views/ComboBox.cs
  37. 1 1
      Terminal.Gui/Views/Dialog.cs
  38. 10 19
      Terminal.Gui/Views/HexView.cs
  39. 58 3
      Terminal.Gui/Views/Line.cs
  40. 51 3
      Terminal.Gui/Views/ListView.cs
  41. 3 3
      Terminal.Gui/Views/Menu/Menu.cs
  42. 2 0
      Terminal.Gui/Views/Menu/MenuBar.cs
  43. 58 0
      Terminal.Gui/Views/MenuBarv2.cs
  44. 64 0
      Terminal.Gui/Views/Menuv2.cs
  45. 1 1
      Terminal.Gui/Views/OpenDialog.cs
  46. 22 1
      Terminal.Gui/Views/RadioGroup.cs
  47. 1 1
      Terminal.Gui/Views/SaveDialog.cs
  48. 7 4
      Terminal.Gui/Views/ScrollView.cs
  49. 791 0
      Terminal.Gui/Views/Shortcut.cs
  50. 17 6
      Terminal.Gui/Views/Slider.cs
  51. 38 204
      Terminal.Gui/Views/StatusBar.cs
  52. 0 59
      Terminal.Gui/Views/StatusItem.cs
  53. 2 2
      Terminal.Gui/Views/TextField.cs
  54. 2 3
      Terminal.Gui/Views/TextView.cs
  55. 8 8
      Terminal.Gui/Views/Toplevel.cs
  56. 1 1
      Terminal.Gui/Views/ToplevelOverlapped.cs
  57. 1 1
      Terminal.Gui/Views/Wizard/Wizard.cs
  58. 7 3
      Terminal.Gui/Views/Wizard/WizardStep.cs
  59. 6 0
      Terminal.sln
  60. 2 0
      UICatalog/Scenario.cs
  61. 2 0
      UICatalog/Scenarios/ASCIICustomButton.cs
  62. 11 13
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  63. 479 0
      UICatalog/Scenarios/Bars.cs
  64. 1 0
      UICatalog/Scenarios/BasicColors.cs
  65. 3 0
      UICatalog/Scenarios/Buttons.cs
  66. 1 0
      UICatalog/Scenarios/CharacterMap.cs
  67. 1 0
      UICatalog/Scenarios/ColorPicker.cs
  68. 5 0
      UICatalog/Scenarios/ComputedLayout.cs
  69. 29 16
      UICatalog/Scenarios/ConfigurationEditor.cs
  70. 102 95
      UICatalog/Scenarios/CsvEditor.cs
  71. 39 87
      UICatalog/Scenarios/DynamicStatusBar.cs
  72. 10 15
      UICatalog/Scenarios/Editor.cs
  73. 185 161
      UICatalog/Scenarios/GraphViewExample.cs
  74. 38 28
      UICatalog/Scenarios/HexEditor.cs
  75. 1 0
      UICatalog/Scenarios/HotKeys.cs
  76. 31 35
      UICatalog/Scenarios/InteractiveTree.cs
  77. 1 0
      UICatalog/Scenarios/LineCanvasExperiment.cs
  78. 31 28
      UICatalog/Scenarios/LineViewExample.cs
  79. 33 37
      UICatalog/Scenarios/ListColumns.cs
  80. 1 0
      UICatalog/Scenarios/ListViewWithSelection.cs
  81. 4 3
      UICatalog/Scenarios/Mouse.cs
  82. 58 54
      UICatalog/Scenarios/MultiColouredTable.cs
  83. 54 54
      UICatalog/Scenarios/Notepad.cs
  84. 19 22
      UICatalog/Scenarios/RunTExample.cs
  85. 6 5
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  86. 1 0
      UICatalog/Scenarios/Scrolling.cs
  87. 372 0
      UICatalog/Scenarios/Shortcuts.cs
  88. 86 116
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  89. 36 5
      UICatalog/Scenarios/Sliders.cs
  90. 65 58
      UICatalog/Scenarios/SyntaxHighlighting.cs
  91. 75 74
      UICatalog/Scenarios/TabViewExample.cs
  92. 57 48
      UICatalog/Scenarios/TableEditor.cs
  93. 1 0
      UICatalog/Scenarios/TextAlignmentAndDirection.cs
  94. 1 0
      UICatalog/Scenarios/TextFormatterDemo.cs
  95. 60 38
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  96. 29 27
      UICatalog/Scenarios/TreeUseCases.cs
  97. 2 0
      UICatalog/Scenarios/TrueColors.cs
  98. 54 37
      UICatalog/Scenarios/Unicode.cs
  99. 1 0
      UICatalog/Scenarios/ViewExperiments.cs
  100. 5 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs

+ 19 - 0
CommunityToolkitExample/CommunityToolkitExample.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
+  </ItemGroup>
+
+</Project>

+ 8 - 0
CommunityToolkitExample/LoginActions.cs

@@ -0,0 +1,8 @@
+namespace CommunityToolkitExample;
+
+internal enum LoginActions
+{
+    Clear,
+    Validation,
+    LoginProgress
+}

+ 62 - 0
CommunityToolkitExample/LoginView.Designer.cs

@@ -0,0 +1,62 @@
+using Terminal.Gui;
+
+namespace CommunityToolkitExample;
+
+internal partial class LoginView : Window
+{
+    private Label titleLabel;
+    private Label usernameLengthLabel;
+    private TextField usernameInput;
+    private Label passwordLengthLabel;
+    private TextField passwordInput;
+    private Label validationLabel;
+    private Button loginButton;
+    private Button clearButton;
+    private Label loginProgressLabel;
+
+    private void InitializeComponent ()
+    {
+        titleLabel = new Label ();
+        titleLabel.Text = "Login Form";
+        Add (titleLabel);
+        usernameLengthLabel = new Label ();
+        usernameLengthLabel.X = Pos.Left (titleLabel);
+        usernameLengthLabel.Y = Pos.Top (titleLabel) + 1;
+        Add (usernameLengthLabel);
+        usernameInput = new TextField ();
+        usernameInput.X = Pos.Right (usernameLengthLabel) + 1;
+        usernameInput.Y = Pos.Top (usernameLengthLabel);
+        usernameInput.Width = 40;
+        Add (usernameInput);
+        passwordLengthLabel = new Label ();
+        passwordLengthLabel.X = Pos.Left (usernameLengthLabel);
+        passwordLengthLabel.Y = Pos.Top (usernameLengthLabel) + 1;
+        Add (passwordLengthLabel);
+        passwordInput = new TextField ();
+        passwordInput.X = Pos.Right (passwordLengthLabel) + 1;
+        passwordInput.Y = Pos.Top (passwordLengthLabel);
+        passwordInput.Width = 40;
+        passwordInput.Secret = true;
+        Add (passwordInput);
+        validationLabel = new Label ();
+        validationLabel.X = Pos.Left (passwordInput);
+        validationLabel.Y = Pos.Top (passwordInput) + 1;
+        Add (validationLabel);
+        loginButton = new Button ();
+        loginButton.X = Pos.Left (validationLabel);
+        loginButton.Y = Pos.Top (validationLabel) + 1;
+        loginButton.Text = "_Login";
+        Add (loginButton);
+        clearButton = new Button ();
+        clearButton.X = Pos.Left (loginButton);
+        clearButton.Y = Pos.Top (loginButton) + 1;
+        clearButton.Text = "_Clear";
+        Add (clearButton);
+        loginProgressLabel = new Label ();
+        loginProgressLabel.X = Pos.Left (clearButton);
+        loginProgressLabel.Y = Pos.Top (clearButton) + 1;
+        loginProgressLabel.Width = 40;
+        loginProgressLabel.Height = 1;
+        Add (loginProgressLabel);
+    }
+}

+ 72 - 0
CommunityToolkitExample/LoginView.cs

@@ -0,0 +1,72 @@
+using CommunityToolkit.Mvvm.Messaging;
+using Terminal.Gui;
+
+namespace CommunityToolkitExample;
+
+internal partial class LoginView : IRecipient<Message<LoginActions>>
+{
+    public LoginView (LoginViewModel viewModel)
+    {
+        WeakReferenceMessenger.Default.Register (this);
+        Title = $"Community Toolkit MVVM Example - {Application.QuitKey} to Exit";
+        ViewModel = viewModel;
+        InitializeComponent ();
+        usernameInput.TextChanged += (_, _) =>
+                                     {
+                                         ViewModel.Username = usernameInput.Text;
+                                     };
+        passwordInput.TextChanged += (_, _) =>
+                                     {
+                                         ViewModel.Password = passwordInput.Text;
+                                     };
+        loginButton.Accept += (_, _) =>
+                              {
+                                  if (!ViewModel.CanLogin) { return; }
+                                  ViewModel.LoginCommand.Execute (null);
+                              };
+
+        clearButton.Accept += (_, _) =>
+                              {
+                                  ViewModel.ClearCommand.Execute (null);
+                              };
+
+        Initialized += (_, _) => { ViewModel.Initialized (); };
+    }
+
+    public LoginViewModel ViewModel { get; set; }
+
+    public void Receive (Message<LoginActions> message)
+    {
+        switch (message.Value)
+        {
+            case LoginActions.Clear:
+                {
+                    loginProgressLabel.Text = ViewModel.LoginProgressMessage;
+                    validationLabel.Text = ViewModel.ValidationMessage;
+                    validationLabel.ColorScheme = ViewModel.ValidationColorScheme;
+                    break;
+                }
+            case LoginActions.LoginProgress:
+                {
+                    loginProgressLabel.Text = ViewModel.LoginProgressMessage;
+                    break;
+                }
+            case LoginActions.Validation:
+                {
+                    validationLabel.Text = ViewModel.ValidationMessage;
+                    validationLabel.ColorScheme = ViewModel.ValidationColorScheme;
+                    break;
+                }
+        }
+        SetText();
+        Application.Refresh ();
+    }
+
+    private void SetText ()
+    {
+        usernameInput.Text = ViewModel.Username;
+        usernameLengthLabel.Text = ViewModel.UsernameLengthMessage;
+        passwordInput.Text = ViewModel.Password;
+        passwordLengthLabel.Text = ViewModel.PasswordLengthMessage;
+    }
+}

+ 128 - 0
CommunityToolkitExample/LoginViewModel.cs

@@ -0,0 +1,128 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.Mvvm.Messaging;
+using Terminal.Gui;
+
+namespace CommunityToolkitExample;
+
+internal partial class LoginViewModel : ObservableObject
+{
+    private const string DEFAULT_LOGIN_PROGRESS_MESSAGE = "Press 'Login' to log in.";
+    private const string INVALID_LOGIN_MESSAGE = "Please enter a valid user name and password.";
+    private const string LOGGING_IN_PROGRESS_MESSAGE = "Logging in...";
+    private const string VALID_LOGIN_MESSAGE = "The input is valid!";
+    
+    [ObservableProperty]
+    private bool _canLogin;
+
+    [ObservableProperty]
+    private string _loginProgressMessage;
+
+    private string _password;
+
+    [ObservableProperty]
+    private string _passwordLengthMessage;
+
+    private string _username;
+
+    [ObservableProperty]
+    private string _usernameLengthMessage;
+    
+    [ObservableProperty]
+    private ColorScheme? _validationColorScheme;
+
+    [ObservableProperty]
+    private string _validationMessage;
+    public LoginViewModel ()
+    {
+        _loginProgressMessage = string.Empty;
+        _password = string.Empty;
+        _passwordLengthMessage = string.Empty;
+        _username = string.Empty;
+        _usernameLengthMessage = string.Empty;
+        _validationMessage = string.Empty;
+
+        Username = string.Empty;
+        Password = string.Empty;
+
+        ClearCommand = new (Clear);
+        LoginCommand = new (Execute);
+
+        Clear ();
+
+        return;
+
+        async void Execute () { await Login (); }
+    }
+
+    public RelayCommand ClearCommand { get; }
+
+    public RelayCommand LoginCommand { get; }
+
+    public string Password
+    {
+        get => _password;
+        set
+        {
+            SetProperty (ref _password, value);
+            PasswordLengthMessage = $"_Password ({_password.Length} characters):";
+            ValidateLogin ();
+        }
+    }
+
+    public string Username
+    {
+        get => _username;
+        set
+        {
+            SetProperty (ref _username, value);
+            UsernameLengthMessage = $"_Username ({_username.Length} characters):";
+            ValidateLogin ();
+        }
+    }
+
+    public void Initialized ()
+    {
+        Clear ();
+    }
+
+    private void Clear ()
+    {
+        Username = string.Empty;
+        Password = string.Empty;
+        SendMessage (LoginActions.Clear, DEFAULT_LOGIN_PROGRESS_MESSAGE);
+    }
+
+    private async Task Login ()
+    {
+        SendMessage (LoginActions.LoginProgress, LOGGING_IN_PROGRESS_MESSAGE);
+        await Task.Delay (TimeSpan.FromSeconds (1));
+        Clear ();
+    }
+
+    private void SendMessage (LoginActions loginAction, string message = "")
+    {
+        switch (loginAction)
+        {
+             case LoginActions.Clear:
+                LoginProgressMessage = message;
+                ValidationMessage = INVALID_LOGIN_MESSAGE;
+                ValidationColorScheme = Colors.ColorSchemes ["Error"];
+                break;
+            case LoginActions.LoginProgress:
+                LoginProgressMessage = message;
+                break;
+            case LoginActions.Validation:
+                ValidationMessage = CanLogin ? VALID_LOGIN_MESSAGE : INVALID_LOGIN_MESSAGE;
+                ValidationColorScheme = CanLogin ? Colors.ColorSchemes ["Base"] : Colors.ColorSchemes ["Error"];
+                break;
+        }
+        WeakReferenceMessenger.Default.Send (new Message<LoginActions> { Value = loginAction });
+    }
+
+    private void ValidateLogin ()
+    {
+        CanLogin = !string.IsNullOrEmpty (Username) && !string.IsNullOrEmpty (Password);
+        SendMessage (LoginActions.Validation);
+    }
+}

+ 6 - 0
CommunityToolkitExample/Message.cs

@@ -0,0 +1,6 @@
+namespace CommunityToolkitExample;
+
+internal class Message<T>
+{
+    public T? Value { get; set; }
+}

+ 26 - 0
CommunityToolkitExample/Program.cs

@@ -0,0 +1,26 @@
+using Microsoft.Extensions.DependencyInjection;
+using Terminal.Gui;
+
+namespace CommunityToolkitExample;
+
+public static class Program
+{
+    public static IServiceProvider? Services { get; private set; }
+
+    private static void Main (string [] args)
+    {
+        Services = ConfigureServices ();
+        Application.Init ();
+        Application.Run (Services.GetRequiredService<LoginView> ());
+        Application.Top.Dispose();
+        Application.Shutdown ();
+    }
+
+    private static IServiceProvider ConfigureServices ()
+    {
+        var services = new ServiceCollection ();
+        services.AddTransient<LoginView> ();
+        services.AddTransient<LoginViewModel> ();
+        return services.BuildServiceProvider ();
+    }
+}

+ 154 - 0
CommunityToolkitExample/README.md

@@ -0,0 +1,154 @@
+# CommunityToolkit.MVVM Example
+
+This small demo gives an example of using the `CommunityToolkit.MVVM` framework's `ObservableObject`, `ObservableProperty`, and `IRecipient<T>` in conjunction with `Microsoft.Extensions.DependencyInjection`. 
+
+Right away we use IoC to load our views and view models.
+
+``` csharp
+// As a public property for access further in the application if needed. 
+public static IServiceProvider Services { get; private set; }
+...
+// In Main
+Services = ConfigureServices ();
+...
+private static IServiceProvider ConfigureServices ()
+{
+    var services = new ServiceCollection ();
+    services.AddTransient<LoginView> ();
+    services.AddTransient<LoginViewModel> ();
+    return services.BuildServiceProvider ();
+}
+```
+
+Now, we start the app and get our main view.
+
+``` csharp
+Application.Run (Services.GetRequiredService<LoginView> ());
+```
+
+Our view implements `IRecipient<T>` to demonstrate the use of the `WeakReferenceMessenger`. The binding of the view events is then created.
+
+``` csharp
+internal partial class LoginView : IRecipient<Message<LoginAction>>
+{
+    public LoginView (LoginViewModel viewModel)
+    {
+        // Initialize our Receive method
+        WeakReferenceMessenger.Default.Register (this);
+        ...
+        ViewModel = viewModel;
+        ...
+        passwordInput.TextChanged += (_, _) =>
+                                     {
+                                         ViewModel.Password = passwordInput.Text;
+                                         SetText ();
+                                     };
+        loginButton.Accept += (_, _) =>
+                              {
+                                  if (!ViewModel.CanLogin) { return; }
+                                  ViewModel.LoginCommand.Execute (null);
+                              };
+        ...
+        // Let the view model know the view is intialized.
+        Initialized += (_, _) => { ViewModel.Initialized (); };
+    }
+    ...
+}
+```
+
+Momentarily slipping over to the view model, all bindable properties use some form of `ObservableProperty` with the class deriving from `ObservableObject`. Commands are of the `RelayCommand` type. The use of `ObservableProperty` generates the code for handling `INotifyPropertyChanged` and `INotifyPropertyChanging`.
+
+``` csharp
+internal partial class LoginViewModel : ObservableObject
+{
+    ...
+    [ObservableProperty]
+    private bool _canLogin;
+
+    private string _password;
+    ...
+    public LoginViewModel ()
+    {
+        ...
+        Password = string.Empty;
+        ...   
+        LoginCommand = new (Execute);
+
+        Clear ();
+
+        return;
+
+        async void Execute () { await Login (); }
+    }
+    ...
+    public RelayCommand LoginCommand { get; }
+
+    public string Password
+    {
+        get => _password;
+        set
+        {
+            SetProperty (ref _password, value);
+            PasswordLengthMessage = $"_Password ({_password.Length} characters):";
+            ValidateLogin ();
+        }
+    }
+```
+
+The use of `WeakReferenceMessenger` provides one method of signaling the view from the view model. It's just one way to handle cross-thread messaging in this framework.
+
+``` csharp
+...
+private async Task Login ()
+{
+    SendMessage (LoginAction.LoginProgress, LOGGING_IN_PROGRESS_MESSAGE);
+    await Task.Delay (TimeSpan.FromSeconds (1));
+    Clear ();
+}
+
+private void SendMessage (LoginAction loginAction, string message = "")
+{
+    switch (loginAction)
+    {
+        case LoginAction.LoginProgress:
+            LoginProgressMessage = message;
+            break;
+        case LoginAction.Validation:
+            ValidationMessage = CanLogin ? VALID_LOGIN_MESSAGE : INVALID_LOGIN_MESSAGE;
+            ValidationColorScheme = CanLogin ? Colors.ColorSchemes ["Base"] : Colors.ColorSchemes ["Error"];
+            break;
+    }
+    WeakReferenceMessenger.Default.Send (new Message<LoginAction> { Value = loginAction });
+}
+
+private void ValidateLogin ()
+{
+    CanLogin = !string.IsNullOrEmpty (Username) && !string.IsNullOrEmpty (Password);
+    SendMessage (LoginAction.Validation);
+}
+...
+```
+
+And the view's `Receive` function which provides an `Application.Refresh()` call to update the UI immediately.
+
+``` csharp
+public void Receive (Message<LoginAction> message)
+{
+    switch (message.Value)
+    {
+        case LoginAction.LoginProgress:
+            {
+                loginProgressLabel.Text = ViewModel.LoginProgressMessage;
+                break;
+            }
+        case LoginAction.Validation:
+            {
+                validationLabel.Text = ViewModel.ValidationMessage;
+                validationLabel.ColorScheme = ViewModel.ValidationColorScheme;
+                break;
+            }
+    }
+    SetText();
+    Application.Refresh ();
+}
+```

+ 1 - 0
ReactiveExample/Program.cs

@@ -12,6 +12,7 @@ public static class Program
         RxApp.MainThreadScheduler = TerminalScheduler.Default;
         RxApp.TaskpoolScheduler = TaskPoolScheduler.Default;
         Application.Run (new LoginView (new LoginViewModel ()));
+        Application.Top.Dispose();
         Application.Shutdown ();
     }
 }

+ 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 = @(

+ 39 - 28
Terminal.Gui/Application/Application.cs

@@ -1,7 +1,6 @@
 using System.Diagnostics;
 using System.Globalization;
 using System.Reflection;
-using System.Text.Json.Serialization;
 
 namespace Terminal.Gui;
 
@@ -77,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.
@@ -85,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 ();
@@ -97,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);
 
@@ -178,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
-    ///     <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>
+    ///     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">
@@ -314,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); }
@@ -345,7 +340,7 @@ public static partial class Application
     /// <summary>Shutdown an application initialized with <see cref="Init"/>.</summary>
     /// <remarks>
     ///     Shutdown must be called for every call to <see cref="Init"/> or
-    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to ensure all resources are cleaned
+    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to ensure all resources are cleaned
     ///     up (Disposed)
     ///     and terminal settings are restored.
     /// </remarks>
@@ -354,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)
@@ -531,6 +535,7 @@ public static partial class Application
         toplevel.LayoutSubviews ();
         toplevel.PositionToplevels ();
         toplevel.FocusFirst ();
+        BringOverlappedTopToFront ();
 
         if (refreshDriver)
         {
@@ -640,7 +645,7 @@ public static partial class Application
 
     /// <summary>
     ///     Runs the application by creating a <see cref="Toplevel"/> object and calling
-    ///     <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
+    ///     <see cref="Run(Toplevel, Func{Exception, bool})"/>.
     /// </summary>
     /// <remarks>
     ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
@@ -657,7 +662,7 @@ public static partial class Application
 
     /// <summary>
     ///     Runs the application by creating a <see cref="Toplevel"/>-derived object of type <c>T</c> and calling
-    ///     <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
+    ///     <see cref="Run(Toplevel, Func{Exception, bool})"/>.
     /// </summary>
     /// <remarks>
     ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
@@ -677,11 +682,17 @@ 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)
+        {
+            // Init() has NOT been called.
+            InternalInit (driver, null, true);
+        }
+
         var top = new T ();
 
-        Run (top, errorHandler, driver);
+        Run (top, errorHandler);
 
         return top;
     }
@@ -693,11 +704,11 @@ public static partial class Application
     ///         modal <see cref="View"/>s such as <see cref="Dialog"/> boxes.
     ///     </para>
     ///     <para>
-    ///         To make a <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> stop execution, call
+    ///         To make a <see cref="Run(Toplevel, Func{Exception, bool})"/> stop execution, call
     ///         <see cref="Application.RequestStop"/>.
     ///     </para>
     ///     <para>
-    ///         Calling <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> is equivalent to calling
+    ///         Calling <see cref="Run(Toplevel, Func{Exception, bool})"/> is equivalent to calling
     ///         <see cref="Begin(Toplevel)"/>, followed by <see cref="RunLoop(RunState)"/>, and then calling
     ///         <see cref="End(RunState)"/>.
     ///     </para>
@@ -708,7 +719,10 @@ public static partial class Application
     ///         <see cref="RunLoop(RunState)"/> method will only process any pending events, timers, idle handlers and then
     ///         return control immediately.
     ///     </para>
-    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
+    ///     <para>When using <see cref="Run{T}"/> or
+    ///         <see cref="Run(System.Func{System.Exception,bool},Terminal.Gui.ConsoleDriver)"/>
+    ///         <see cref="Init"/> will be called automatically.
+    ///     </para>
     ///     <para>
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
@@ -721,12 +735,7 @@ public static partial class Application
     ///     RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true,
     ///     rethrows when null).
     /// </param>
-    /// <param name="driver">
-    ///     The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the platform will
-    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
-    ///     <see langword="null"/> if <see cref="Init"/> was called.
-    /// </param>
-    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
+    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null)
     {
         ArgumentNullException.ThrowIfNull (view);
 
@@ -746,7 +755,9 @@ public static partial class Application
         else
         {
             // Init() has NOT been called.
-            InternalInit (driver, null, true);
+            throw new InvalidOperationException (
+                                                 "Init() has not been called. Only Run() or Run<T>() can be used without calling Init()."
+                                                );
         }
 
         var resume = true;
@@ -991,7 +1002,7 @@ public static partial class Application
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
     /// <param name="top">The <see cref="Toplevel"/> to stop.</param>
     /// <remarks>
-    ///     <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to return.</para>
+    ///     <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to return.</para>
     ///     <para>
     ///         Calling <see cref="Application.RequestStop"/> is equivalent to setting the <see cref="Toplevel.Running"/>
     ///         property on the currently running <see cref="Toplevel"/> to false.

+ 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;
+                    }
+
                 }
             }
         }

+ 12 - 1
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -859,13 +859,24 @@ public static class ConsoleKeyMapping
             case ConsoleKey.F24:
                 keyCode = KeyCode.F24;
 
+                break;
+            case ConsoleKey.Clear:
+                keyCode = KeyCode.Clear;
+
                 break;
             case ConsoleKey.Tab:
                 keyCode = KeyCode.Tab;
 
                 break;
             default:
-                keyCode = (KeyCode)consoleKeyInfo.KeyChar;
+                if ((int)consoleKeyInfo.KeyChar is >= 1 and <= 26)
+                {
+                    keyCode = (KeyCode)(consoleKeyInfo.KeyChar + 64);
+                }
+                else
+                {
+                    keyCode = (KeyCode)consoleKeyInfo.KeyChar;
+                }
 
                 break;
         }

+ 50 - 31
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -515,9 +515,10 @@ internal class CursesDriver : ConsoleDriver
             {
                 // The ESC-number handling, debatable.
                 // Simulates the AltMask itself by pressing Alt + Space.
+                // Needed for macOS
                 if (wch2 == (int)KeyCode.Space)
                 {
-                    k = KeyCode.AltMask;
+                    k = KeyCode.AltMask | KeyCode.Space;
                 }
                 else if (wch2 - (int)KeyCode.Space >= (uint)KeyCode.A
                          && wch2 - (int)KeyCode.Space <= (uint)KeyCode.Z)
@@ -532,41 +533,51 @@ internal class CursesDriver : ConsoleDriver
                 {
                     k = (KeyCode)((uint)KeyCode.AltMask + (uint)KeyCode.D0 + (wch2 - (uint)KeyCode.D0));
                 }
-                else if (wch2 == Curses.KeyCSI)
+                else
                 {
                     ConsoleKeyInfo [] cki =
-                    {
-                        new ((char)KeyCode.Esc, 0, false, false, false), new ('[', 0, false, false, false)
-                    };
+                    [
+                        new ((char)KeyCode.Esc, 0, false, false, false), new ((char)wch2, 0, false, false, false)
+                    ];
                     HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);
 
                     return;
                 }
-                else
-                {
-                    // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
-                    if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0)
-                    {
-                        k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
-                    }
-
-                    if (wch2 == 0)
-                    {
-                        k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
-                    }
-                    else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
-                    {
-                        k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
-                    }
-                    else if (wch2 < 256)
-                    {
-                        k = (KeyCode)wch2; // | KeyCode.AltMask;
-                    }
-                    else
-                    {
-                        k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
-                    }
-                }
+                //else if (wch2 == Curses.KeyCSI)
+                //{
+                //    ConsoleKeyInfo [] cki =
+                //    {
+                //        new ((char)KeyCode.Esc, 0, false, false, false), new ('[', 0, false, false, false)
+                //    };
+                //    HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);
+
+                //    return;
+                //}
+                //else
+                //{
+                //    // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
+                //    if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0)
+                //    {
+                //        k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
+                //    }
+
+                //    if (wch2 == 0)
+                //    {
+                //        k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
+                //    }
+                //    //else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
+                //    //{
+                //    //    k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
+                //    //}
+                //    else if (wch2 < 256)
+                //    {
+                //        k = (KeyCode)wch2; // | KeyCode.AltMask;
+                //    }
+                //    else
+                //    {
+                //        k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
+                //    }
+                //}
 
                 key = new Key (k);
             }
@@ -584,6 +595,13 @@ internal class CursesDriver : ConsoleDriver
             OnKeyDown (new Key (k));
             OnKeyUp (new Key (k));
         }
+        else if (wch == 127)
+        {
+            // Backspace needed for macOS
+            k = KeyCode.Backspace;
+            OnKeyDown (new Key (k));
+            OnKeyUp (new Key (k));
+        }
         else
         {
             // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
@@ -611,7 +629,8 @@ internal class CursesDriver : ConsoleDriver
             }
 
             // Strip the KeyCode.Space flag off if it's set
-            if (k != KeyCode.Space && k.HasFlag (KeyCode.Space))
+            //if (k != KeyCode.Space && k.HasFlag (KeyCode.Space))
+            if (Key.GetIsKeyCodeAtoZ (k) && (k & KeyCode.Space) != 0)
             {
                 k &= ~KeyCode.Space;
             }

+ 0 - 1
Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs

@@ -106,7 +106,6 @@ public partial class Curses
     public const int KeyPPage = 0x153;
     public const int KeyHome = 0x106;
     public const int KeyMouse = 0x199;
-    public const int KeyCSI = 0x5b;
     public const int KeyEnd = 0x168;
     public const int KeyDeleteChar = 0x14a;
     public const int KeyInsertChar = 0x14b;

+ 30 - 6
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -195,6 +195,7 @@ public static class EscSeqUtils
         buttonState = new List<MouseFlags> { 0 };
         pos = default (Point);
         isResponse = false;
+        char keyChar = '\0';
 
         switch (c1Control)
         {
@@ -242,10 +243,10 @@ public static class EscSeqUtils
 
                 break;
             case "SS3":
-                key = GetConsoleKey (terminator [0], values [0], ref mod);
+                key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar);
 
                 newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                        '\0',
+                                                        keyChar,
                                                         key,
                                                         (mod & ConsoleModifiers.Shift) != 0,
                                                         (mod & ConsoleModifiers.Alt) != 0,
@@ -271,7 +272,7 @@ public static class EscSeqUtils
 
                 if (!string.IsNullOrEmpty (terminator))
                 {
-                    key = GetConsoleKey (terminator [0], values [0], ref mod);
+                    key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar);
 
                     if (key != 0 && values.Length > 1)
                     {
@@ -279,7 +280,7 @@ public static class EscSeqUtils
                     }
 
                     newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            '\0',
+                                                            keyChar,
                                                             key,
                                                             (mod & ConsoleModifiers.Shift) != 0,
                                                             (mod & ConsoleModifiers.Alt) != 0,
@@ -342,15 +343,26 @@ public static class EscSeqUtils
     ///     <see cref="CSI_SendDeviceAttributes2"/>.
     /// </param>
     /// <param name="value">The value.</param>
-    /// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
+    /// <param name="mod">The <see cref="ConsoleModifiers"/> which may change.</param>
+    /// <param name="keyChar">Normally is '\0' but on some cases may need other value.</param>
     /// <returns>The <see cref="ConsoleKey"/> and probably the <see cref="ConsoleModifiers"/>.</returns>
-    public static ConsoleKey GetConsoleKey (char terminator, string? value, ref ConsoleModifiers mod)
+    public static ConsoleKey GetConsoleKey (char terminator, string? value, ref ConsoleModifiers mod, ref char keyChar)
     {
         if (terminator == 'Z')
         {
             mod |= ConsoleModifiers.Shift;
         }
 
+        if (terminator == 'l')
+        {
+            keyChar = '+';
+        }
+
+        if (terminator == 'm')
+        {
+            keyChar = '-';
+        }
+
         return (terminator, value) switch
                {
                    ('A', _) => ConsoleKey.UpArrow,
@@ -376,6 +388,18 @@ public static class EscSeqUtils
                    ('~', "21") => ConsoleKey.F10,
                    ('~', "23") => ConsoleKey.F11,
                    ('~', "24") => ConsoleKey.F12,
+                   ('l', _) => ConsoleKey.Add,
+                   ('m', _) => ConsoleKey.Subtract,
+                   ('p', _) => ConsoleKey.Insert,
+                   ('q', _) => ConsoleKey.End,
+                   ('r', _) => ConsoleKey.DownArrow,
+                   ('s', _) => ConsoleKey.PageDown,
+                   ('t', _) => ConsoleKey.LeftArrow,
+                   ('u', _) => ConsoleKey.Clear,
+                   ('v', _) => ConsoleKey.RightArrow,
+                   ('w', _) => ConsoleKey.Home,
+                   ('x', _) => ConsoleKey.UpArrow,
+                   ('y', _) => ConsoleKey.PageUp,
                    (_, _) => 0
                };
     }

+ 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>

+ 19 - 5
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>
@@ -438,7 +447,7 @@ public partial class View
                 SetHasFocus (false, this);
                 SuperView?.EnsureFocus ();
 
-                if (SuperView is { } && SuperView.Focused is null)
+                if (SuperView is { Focused: null })
                 {
                     SuperView.FocusNext ();
 
@@ -477,6 +486,11 @@ public partial class View
                         }
                     }
                 }
+
+                if (this is Toplevel && Application.Current.Focused != this)
+                {
+                    Application.BringOverlappedTopToFront ();
+                }
             }
 
             OnCanFocusChanged ();

+ 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 ();

+ 16 - 2
Terminal.Gui/Views/ComboBox.cs

@@ -187,7 +187,7 @@ public class ComboBox : View
             {
                 SelectedItem = -1;
                 _search.Text = string.Empty;
-                Search_Changed (this, new StateEventArgs<string> (string.Empty, _search.Text)); 
+                Search_Changed (this, new StateEventArgs<string> (string.Empty, _search.Text));
                 SetNeedsDisplay ();
             }
         }
@@ -645,7 +645,9 @@ public class ComboBox : View
 
     private void ResetSearchSet (bool noCopy = false)
     {
+        _listview.SuspendCollectionChangedEvent ();
         _searchSet.Clear ();
+        _listview.ResumeSuspendCollectionChangedEvent ();
 
         if (_autoHide || noCopy)
         {
@@ -682,6 +684,8 @@ public class ComboBox : View
 
             if (!string.IsNullOrEmpty (_search.Text))
             {
+                _listview.SuspendCollectionChangedEvent ();
+
                 foreach (object item in _source.ToList ())
                 {
                     // Iterate to preserver object type and force deep copy
@@ -694,6 +698,8 @@ public class ComboBox : View
                         _searchSet.Add (item);
                     }
                 }
+
+                _listview.ResumeSuspendCollectionChangedEvent ();
             }
         }
 
@@ -738,11 +744,16 @@ public class ComboBox : View
             return;
         }
 
+        // PERF: At the request of @dodexahedron in the comment https://github.com/gui-cs/Terminal.Gui/pull/3552#discussion_r1648112410.
+        _listview.SuspendCollectionChangedEvent ();
+
         // force deep copy
         foreach (object item in Source.ToList ())
         {
             _searchSet.Add (item);
         }
+
+        _listview.ResumeSuspendCollectionChangedEvent ();
     }
 
     private void SetSearchText (string value) { _search.Text = _text = value; }
@@ -765,8 +776,11 @@ public class ComboBox : View
     /// Consider making public
     private void ShowList ()
     {
+        _listview.SuspendCollectionChangedEvent ();
         _listview.SetSource (_searchSet);
-        _listview.Clear (); 
+        _listview.ResumeSuspendCollectionChangedEvent ();
+
+        _listview.Clear ();
         _listview.Height = CalculatetHeight ();
         SuperView?.BringSubviewToFront (this);
     }

+ 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
                    );

+ 51 - 3
Terminal.Gui/Views/ListView.cs

@@ -19,6 +19,12 @@ public interface IListDataSource: IDisposable
     /// <summary>Returns the maximum length of elements to display</summary>
     int Length { get; }
 
+    /// <summary>
+    /// Allow suspending the <see cref="CollectionChanged"/> event from being invoked,
+    /// if <see langword="true"/>, otherwise is <see langword="false"/>.
+    /// </summary>
+    bool SuspendCollectionChangedEvent { get; set; }
+
     /// <summary>Should return whether the specified item is currently marked.</summary>
     /// <returns><see langword="true"/>, if marked, <see langword="false"/> otherwise.</returns>
     /// <param name="item">Item index.</param>
@@ -893,6 +899,28 @@ public class ListView : View
 
         base.Dispose (disposing);
     }
+
+    /// <summary>
+    /// Allow suspending the <see cref="CollectionChanged"/> event from being invoked,
+    /// </summary>
+    public void SuspendCollectionChangedEvent ()
+    {
+        if (Source is { })
+        {
+            Source.SuspendCollectionChangedEvent = true;
+        }
+    }
+
+    /// <summary>
+    /// Allow resume the <see cref="CollectionChanged"/> event from being invoked,
+    /// </summary>
+    public void ResumeSuspendCollectionChangedEvent ()
+    {
+        if (Source is { })
+        {
+            Source.SuspendCollectionChangedEvent = false;
+        }
+    }
 }
 
 /// <summary>
@@ -920,8 +948,11 @@ public class ListWrapper<T> : IListDataSource, IDisposable
 
     private void Source_CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
     {
-        CheckAndResizeMarksIfRequired ();
-        CollectionChanged?.Invoke (sender, e);
+        if (!SuspendCollectionChangedEvent)
+        {
+            CheckAndResizeMarksIfRequired ();
+            CollectionChanged?.Invoke (sender, e);
+        }
     }
 
     /// <inheritdoc />
@@ -933,7 +964,24 @@ public class ListWrapper<T> : IListDataSource, IDisposable
     /// <inheritdoc/>
     public int Length { get; private set; }
 
-    void CheckAndResizeMarksIfRequired ()
+    private bool _suspendCollectionChangedEvent;
+
+    /// <inheritdoc />
+    public bool SuspendCollectionChangedEvent
+    {
+        get => _suspendCollectionChangedEvent;
+        set
+        {
+            _suspendCollectionChangedEvent = value;
+
+            if (!_suspendCollectionChangedEvent)
+            {
+                CheckAndResizeMarksIfRequired ();
+            }
+        }
+    }
+
+    private void CheckAndResizeMarksIfRequired ()
     {
         if (_source != null && _count != _source.Count)
         {

+ 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)

+ 2 - 0
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -136,6 +136,8 @@ public class MenuBar : View
 
         // TODO: Why do we have two keybindings for opening the menu? Ctrl-Space and Key?
         KeyBindings.Add (Key.Space.WithCtrl, keyBinding);
+        // This is needed for macOS because Key.Space.WithCtrl doesn't work
+        KeyBindings.Add (Key.Space.WithAlt, keyBinding);
 
         // TODO: Figure out how to make Alt work (on Windows)
         //KeyBindings.Add (Key.WithAlt, keyBinding);

+ 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/ToplevelOverlapped.cs

@@ -67,7 +67,7 @@ public static partial class Application
 
         View top = FindTopFromView (Top?.MostFocused);
 
-        if (top is Toplevel && Top.Subviews.Count > 1 && Top.Subviews [Top.Subviews.Count - 1] != top)
+        if (top is Toplevel && Top.Subviews.Count > 1 && Top.Subviews [^1] != top)
         {
             Top.BringSubviewToFront (top);
         }

+ 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>

+ 6 - 0
Terminal.sln

@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui.Analyzers.Inte
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui.Analyzers.Internal.Debugging", "Analyzers\Terminal.Gui.Analyzers.Internal.Debugging\Terminal.Gui.Analyzers.Internal.Debugging.csproj", "{C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkitExample", "CommunityToolkitExample\CommunityToolkitExample.csproj", "{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}"
+EndProject
 Global
 	GlobalSection(NestedProjects) = preSolution
 		{5DE91722-8765-4E2B-97E4-2A18010B5CED} = {CCADA0BC-61CF-4B4B-96BA-A3B0C0A7F54D}
@@ -81,6 +83,10 @@ Global
 		{C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 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 ();
     }
 }

+ 3 - 0
UICatalog/Scenarios/Buttons.cs

@@ -14,6 +14,8 @@ public class Buttons : Scenario
 {
     public override void Main ()
     {
+        Application.Init ();
+
         Window main = new ()
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
@@ -394,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 ())

+ 1 - 0
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -207,6 +207,7 @@ public class ListViewWithSelection : Scenario
         public event NotifyCollectionChangedEventHandler CollectionChanged;
         public int Count => Scenarios != null ? Scenarios.Count : 0;
         public int Length { get; private set; }
+        public bool SuspendCollectionChangedEvent { get => throw new System.NotImplementedException (); set => throw new System.NotImplementedException (); }
 
         public void Render (
             ListView container,

+ 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 (

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