Browse Source

Fix typos

Tig 1 year ago
parent
commit
7ed969f1aa
66 changed files with 3385 additions and 2586 deletions
  1. 4 4
      .github/workflows/api-docs.yml
  2. 0 3
      ReactiveExample/FodyWeavers.xml
  3. 0 26
      ReactiveExample/FodyWeavers.xsd
  4. 126 174
      ReactiveExample/LoginView.cs
  5. 38 43
      ReactiveExample/LoginViewModel.cs
  6. 1 1
      ReactiveExample/Program.cs
  7. 1 1
      ReactiveExample/README.md
  8. 1 1
      ReactiveExample/ReactiveExample.csproj
  9. 24 0
      ReactiveExample/ViewExtensions.cs
  10. 25 2
      SelfContained/Program.cs
  11. 1 1
      SelfContained/README.md
  12. 1 1
      SelfContained/SelfContained.csproj
  13. 2 0
      Terminal.Gui/Application/Application.Initialization.cs
  14. 97 72
      Terminal.Gui/Application/Application.Keyboard.cs
  15. 3 223
      Terminal.Gui/Application/Application.Navigation.cs
  16. 94 78
      Terminal.Gui/Application/Application.cs
  17. 229 0
      Terminal.Gui/Application/ApplicationNavigation.cs
  18. 0 0
      Terminal.Gui/Application/ApplicationOverlapped.cs
  19. 66 65
      Terminal.Gui/Input/KeyBindings.cs
  20. 7 4
      Terminal.Gui/Resources/config.json
  21. 1 1
      Terminal.Gui/View/Navigation/TabBehavior.cs
  22. 42 0
      Terminal.Gui/View/Orientation/IOrientation.cs
  23. 0 0
      Terminal.Gui/View/Orientation/Orientation.cs
  24. 138 0
      Terminal.Gui/View/Orientation/OrientationHelper.cs
  25. 19 4
      Terminal.Gui/View/View.Drawing.cs
  26. 27 2
      Terminal.Gui/View/View.Navigation.cs
  27. 49 8
      Terminal.Gui/Views/Bar.cs
  28. 3 3
      Terminal.Gui/Views/Dialog.cs
  29. 32 15
      Terminal.Gui/Views/Line.cs
  30. 9 3
      Terminal.Gui/Views/MessageBox.cs
  31. 0 19
      Terminal.Gui/Views/OrientationEventArgs.cs
  32. 46 39
      Terminal.Gui/Views/RadioGroup.cs
  33. 159 103
      Terminal.Gui/Views/Shortcut.cs
  34. 44 44
      Terminal.Gui/Views/Slider.cs
  35. 71 1
      Terminal.Gui/Views/StatusBar.cs
  36. 9 5
      Terminal.Gui/Views/TableView/TableView.cs
  37. 1 0
      Terminal.Gui/Views/Window.cs
  38. 71 70
      UICatalog/Scenarios/AdornmentsEditor.cs
  39. 4 6
      UICatalog/Scenarios/AllViewsTester.cs
  40. 1 1
      UICatalog/Scenarios/Bars.cs
  41. 3 4
      UICatalog/Scenarios/ExpanderButton.cs
  42. 1 1
      UICatalog/Scenarios/Shortcuts.cs
  43. 1 1
      UICatalog/UICatalog.csproj
  44. 168 163
      UnitTests/Application/ApplicationTests.cs
  45. 26 44
      UnitTests/Application/KeyboardTests.cs
  46. 20 20
      UnitTests/Configuration/ConfigurationMangerTests.cs
  47. 15 16
      UnitTests/Configuration/SettingsScopeTests.cs
  48. 30 1
      UnitTests/Dialogs/DialogTests.cs
  49. 12 0
      UnitTests/Dialogs/MessageBoxTests.cs
  50. 14 0
      UnitTests/FileServices/FileDialogTests.cs
  51. 80 45
      UnitTests/Input/KeyBindingTests.cs
  52. 3 1
      UnitTests/UICatalog/ScenarioTests.cs
  53. 6 3
      UnitTests/UnitTests.csproj
  54. 1200 1204
      UnitTests/View/NavigationTests.cs
  55. 107 0
      UnitTests/View/Orientation/OrientationHelperTests.cs
  56. 136 0
      UnitTests/View/Orientation/OrientationTests.cs
  57. 11 1
      UnitTests/Views/MenuBarTests.cs
  58. 6 6
      UnitTests/Views/OverlappedTests.cs
  59. 38 7
      UnitTests/Views/ShortcutTests.cs
  60. 4 4
      UnitTests/Views/TextViewTests.cs
  61. 6 6
      UnitTests/Views/ToplevelTests.cs
  62. 7 5
      docfx/docs/config.md
  63. 9 2
      docfx/docs/index.md
  64. 21 18
      docfx/docs/keyboard.md
  65. 1 1
      docfx/docs/layout.md
  66. 14 10
      docfx/docs/toc.yml

+ 4 - 4
.github/workflows/api-docs.yml

@@ -17,11 +17,11 @@ jobs:
     runs-on: windows-latest
     runs-on: windows-latest
     steps:
     steps:
     - name: Checkout
     - name: Checkout
-      if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
+      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       uses: actions/checkout@v4
       uses: actions/checkout@v4
 
 
     - name: DocFX Build
     - name: DocFX Build
-      if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
+      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       working-directory: docfx
       working-directory: docfx
       run: |
       run: |
         dotnet tool install -g docfx
         dotnet tool install -g docfx
@@ -31,11 +31,11 @@ jobs:
       continue-on-error: false
       continue-on-error: false
 
 
     - name: Setup Pages
     - name: Setup Pages
-      if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
+      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       uses: actions/configure-pages@v5
       uses: actions/configure-pages@v5
       
       
     - name: Upload artifact
     - name: Upload artifact
-      if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
+      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       uses: actions/upload-pages-artifact@v3
       uses: actions/upload-pages-artifact@v3
       with:
       with:
         path: docfx/_site
         path: docfx/_site

+ 0 - 3
ReactiveExample/FodyWeavers.xml

@@ -1,3 +0,0 @@
-<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
-  <ReactiveUI />
-</Weavers>

+ 0 - 26
ReactiveExample/FodyWeavers.xsd

@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
-  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
-  <xs:element name="Weavers">
-    <xs:complexType>
-      <xs:all>
-        <xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" />
-      </xs:all>
-      <xs:attribute name="VerifyAssembly" type="xs:boolean">
-        <xs:annotation>
-          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
-        </xs:annotation>
-      </xs:attribute>
-      <xs:attribute name="VerifyIgnoreCodes" type="xs:string">
-        <xs:annotation>
-          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
-        </xs:annotation>
-      </xs:attribute>
-      <xs:attribute name="GenerateXsd" type="xs:boolean">
-        <xs:annotation>
-          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
-        </xs:annotation>
-      </xs:attribute>
-    </xs:complexType>
-  </xs:element>
-</xs:schema>

+ 126 - 174
ReactiveExample/LoginView.cs

@@ -8,20 +8,137 @@ namespace ReactiveExample;
 
 
 public class LoginView : Window, IViewFor<LoginViewModel>
 public class LoginView : Window, IViewFor<LoginViewModel>
 {
 {
-    private readonly CompositeDisposable _disposable = new ();
+    private const string SuccessMessage = "The input is valid!";
+    private const string ErrorMessage = "Please enter a valid user name and password.";
+    private const string ProgressMessage = "Logging in...";
+    private const string IdleMessage = "Press 'Login' to log in.";
+
+    private readonly CompositeDisposable _disposable = [];
 
 
     public LoginView (LoginViewModel viewModel)
     public LoginView (LoginViewModel viewModel)
     {
     {
         Title = $"Reactive Extensions Example - {Application.QuitKey} to Exit";
         Title = $"Reactive Extensions Example - {Application.QuitKey} to Exit";
         ViewModel = viewModel;
         ViewModel = viewModel;
-        Label usernameLengthLabel = UsernameLengthLabel (TitleLabel ());
-        TextField usernameInput = UsernameInput (usernameLengthLabel);
-        Label passwordLengthLabel = PasswordLengthLabel (usernameLengthLabel);
-        TextField passwordInput = PasswordInput (passwordLengthLabel);
-        Label validationLabel = ValidationLabel (passwordInput);
-        Button loginButton = LoginButton (validationLabel);
-        Button clearButton = ClearButton (loginButton);
-        LoginProgressLabel (clearButton);
+        var title = this.AddControl<Label> (x => x.Text = "Login Form");
+        var unLengthLabel = title.AddControlAfter<Label> ((previous, unLength) =>
+            {
+                unLength.X = Pos.Left (previous);
+                unLength.Y = Pos.Top (previous) + 1;
+
+                ViewModel
+                    .WhenAnyValue (x => x.UsernameLength)
+                    .Select (length => $"_Username ({length} characters):")
+                    .BindTo (unLength, x => x.Text)
+                    .DisposeWith (_disposable);
+            });
+        unLengthLabel.AddControlAfter<TextField> ((previous, unInput) =>
+            {
+                unInput.X = Pos.Right (previous) + 1;
+                unInput.Y = Pos.Top (previous);
+                unInput.Width = 40;
+                unInput.Text = ViewModel.Username;
+
+                ViewModel
+                    .WhenAnyValue (x => x.Username)
+                    .BindTo (unInput, x => x.Text)
+                    .DisposeWith (_disposable);
+
+                unInput
+                    .Events ()
+                    .TextChanged
+                    .Select (_ => unInput.Text)
+                    .DistinctUntilChanged ()
+                    .BindTo (ViewModel, x => x.Username)
+                    .DisposeWith (_disposable);
+            });
+        unLengthLabel.AddControlAfter<Label> ((previous, pwLength) =>
+            {
+                pwLength.X = Pos.Left (previous);
+                pwLength.Y = Pos.Top (previous) + 1;
+
+                ViewModel
+                    .WhenAnyValue (x => x.PasswordLength)
+                    .Select (length => $"_Password ({length} characters):")
+                    .BindTo (pwLength, x => x.Text)
+                    .DisposeWith (_disposable);
+            })
+            .AddControlAfter<TextField> ((previous, pwInput) =>
+            {
+                pwInput.X = Pos.Right (previous) + 1;
+                pwInput.Y = Pos.Top (previous);
+                pwInput.Width = 40;
+                pwInput.Text = ViewModel.Password;
+
+                ViewModel
+                    .WhenAnyValue (x => x.Password)
+                    .BindTo (pwInput, x => x.Text)
+                    .DisposeWith (_disposable);
+
+                pwInput
+                    .Events ()
+                    .TextChanged
+                    .Select (_ => pwInput.Text)
+                    .DistinctUntilChanged ()
+                    .BindTo (ViewModel, x => x.Password)
+                    .DisposeWith (_disposable);
+            })
+            .AddControlAfter<Label> ((previous, validation) =>
+            {
+                validation.X = Pos.Left (previous);
+                validation.Y = Pos.Top (previous) + 1;
+                validation.Text = ErrorMessage;
+
+                ViewModel
+                    .WhenAnyValue (x => x.IsValid)
+                    .Select (valid => valid ? SuccessMessage : ErrorMessage)
+                    .BindTo (validation, x => x.Text)
+                    .DisposeWith (_disposable);
+
+                ViewModel
+                    .WhenAnyValue (x => x.IsValid)
+                    .Select (valid => valid ? Colors.ColorSchemes ["Base"] : Colors.ColorSchemes ["Error"])
+                    .BindTo (validation, x => x.ColorScheme)
+                    .DisposeWith (_disposable);
+            })
+            .AddControlAfter<Button> ((previous, login) =>
+            {
+                login.X = Pos.Left (previous);
+                login.Y = Pos.Top (previous) + 1;
+                login.Text = "_Login";
+
+                login
+                    .Events ()
+                    .Accept
+                    .InvokeCommand (ViewModel, x => x.Login)
+                    .DisposeWith (_disposable);
+            })
+            .AddControlAfter<Button> ((previous, clear) =>
+            {
+                clear.X = Pos.Left (previous);
+                clear.Y = Pos.Top (previous) + 1;
+                clear.Text = "_Clear";
+
+                clear
+                    .Events ()
+                    .Accept
+                    .InvokeCommand (ViewModel, x => x.ClearCommand)
+                    .DisposeWith (_disposable);
+            })
+            .AddControlAfter<Label> ((previous, progress) =>
+            {
+                progress.X = Pos.Left (previous);
+                progress.Y = Pos.Top (previous) + 1;
+                progress.Width = 40;
+                progress.Height = 1;
+                progress.Text = IdleMessage;
+
+                ViewModel
+                    .WhenAnyObservable (x => x.Login.IsExecuting)
+                    .Select (executing => executing ? ProgressMessage : IdleMessage)
+                    .ObserveOn (RxApp.MainThreadScheduler)
+                    .BindTo (progress, x => x.Text)
+                    .DisposeWith (_disposable);
+            });
     }
     }
 
 
     public LoginViewModel ViewModel { get; set; }
     public LoginViewModel ViewModel { get; set; }
@@ -37,169 +154,4 @@ public class LoginView : Window, IViewFor<LoginViewModel>
         _disposable.Dispose ();
         _disposable.Dispose ();
         base.Dispose (disposing);
         base.Dispose (disposing);
     }
     }
-
-    private Button ClearButton (View previous)
-    {
-        var clearButton = new Button
-        {
-            X = Pos.Left (previous), Y = Pos.Top (previous) + 1, Text = "_Clear"
-        };
-
-        clearButton
-            .Events ()
-            .Accept
-            .InvokeCommand (ViewModel, x => x.Clear)
-            .DisposeWith (_disposable);
-        Add (clearButton);
-
-        return clearButton;
-    }
-
-    private Button LoginButton (View previous)
-    {
-        var loginButton = new Button
-        {
-            X = Pos.Left (previous), Y = Pos.Top (previous) + 1, Text = "_Login"
-        };
-
-        loginButton
-            .Events ()
-            .Accept
-            .InvokeCommand (ViewModel, x => x.Login)
-            .DisposeWith (_disposable);
-        Add (loginButton);
-
-        return loginButton;
-    }
-
-    private Label LoginProgressLabel (View previous)
-    {
-        var progress = "Logging in...";
-        var idle = "Press 'Login' to log in.";
-
-        var loginProgressLabel = new Label
-        {
-            X = Pos.Left (previous), Y = Pos.Top (previous) + 1, Width = 40, Height = 1, Text = idle
-        };
-
-        ViewModel
-            .WhenAnyObservable (x => x.Login.IsExecuting)
-            .Select (executing => executing ? progress : idle)
-            .ObserveOn (RxApp.MainThreadScheduler)
-            .BindTo (loginProgressLabel, x => x.Text)
-            .DisposeWith (_disposable);
-        Add (loginProgressLabel);
-
-        return loginProgressLabel;
-    }
-
-    private TextField PasswordInput (View previous)
-    {
-        var passwordInput = new TextField
-        {
-            X = Pos.Right (previous) + 1, Y = Pos.Top (previous), Width = 40, Text = ViewModel.Password
-        };
-
-        ViewModel
-            .WhenAnyValue (x => x.Password)
-            .BindTo (passwordInput, x => x.Text)
-            .DisposeWith (_disposable);
-
-        passwordInput
-            .Events ()
-            .TextChanged
-            .Select (old => passwordInput.Text)
-            .DistinctUntilChanged ()
-            .BindTo (ViewModel, x => x.Password)
-            .DisposeWith (_disposable);
-        Add (passwordInput);
-
-        return passwordInput;
-    }
-
-    private Label PasswordLengthLabel (View previous)
-    {
-        var passwordLengthLabel = new Label { X = Pos.Left (previous), Y = Pos.Top (previous) + 1, };
-
-        ViewModel
-            .WhenAnyValue (x => x.PasswordLength)
-            .Select (length => $"_Password ({length} characters):")
-            .BindTo (passwordLengthLabel, x => x.Text)
-            .DisposeWith (_disposable);
-        Add (passwordLengthLabel);
-
-        return passwordLengthLabel;
-    }
-
-    private Label TitleLabel ()
-    {
-        var label = new Label { Text = "Login Form" };
-        Add (label);
-
-        return label;
-    }
-
-    private TextField UsernameInput (View previous)
-    {
-        var usernameInput = new TextField
-        {
-            X = Pos.Right (previous) + 1, Y = Pos.Top (previous), Width = 40, Text = ViewModel.Username
-        };
-
-        ViewModel
-            .WhenAnyValue (x => x.Username)
-            .BindTo (usernameInput, x => x.Text)
-            .DisposeWith (_disposable);
-
-        usernameInput
-            .Events ()
-            .TextChanged
-            .Select (old => usernameInput.Text)
-            .DistinctUntilChanged ()
-            .BindTo (ViewModel, x => x.Username)
-            .DisposeWith (_disposable);
-        Add (usernameInput);
-
-        return usernameInput;
-    }
-
-    private Label UsernameLengthLabel (View previous)
-    {
-        var usernameLengthLabel = new Label { X = Pos.Left (previous), Y = Pos.Top (previous) + 1 };
-
-        ViewModel
-            .WhenAnyValue (x => x.UsernameLength)
-            .Select (length => $"_Username ({length} characters):")
-            .BindTo (usernameLengthLabel, x => x.Text)
-            .DisposeWith (_disposable);
-        Add (usernameLengthLabel);
-
-        return usernameLengthLabel;
-    }
-
-    private Label ValidationLabel (View previous)
-    {
-        var error = "Please enter a valid user name and password.";
-        var success = "The input is valid!";
-
-        var validationLabel = new Label
-        {
-           X = Pos.Left (previous), Y = Pos.Top (previous) + 1, Text = error
-        };
-
-        ViewModel
-            .WhenAnyValue (x => x.IsValid)
-            .Select (valid => valid ? success : error)
-            .BindTo (validationLabel, x => x.Text)
-            .DisposeWith (_disposable);
-
-        ViewModel
-            .WhenAnyValue (x => x.IsValid)
-            .Select (valid => valid ? Colors.ColorSchemes ["Base"] : Colors.ColorSchemes ["Error"])
-            .BindTo (validationLabel, x => x.ColorScheme)
-            .DisposeWith (_disposable);
-        Add (validationLabel);
-
-        return validationLabel;
-    }
 }
 }

+ 38 - 43
ReactiveExample/LoginViewModel.cs

@@ -5,7 +5,7 @@ using System.Reactive.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using ReactiveUI;
 using ReactiveUI;
-using ReactiveUI.Fody.Helpers;
+using ReactiveUI.SourceGenerators;
 
 
 namespace ReactiveExample;
 namespace ReactiveExample;
 
 
@@ -21,41 +21,53 @@ namespace ReactiveExample;
 // See also: https://www.reactiveui.net../docs/handbook/data-persistence/
 // See also: https://www.reactiveui.net../docs/handbook/data-persistence/
 //
 //
 [DataContract]
 [DataContract]
-public class LoginViewModel : ReactiveObject
+public partial class LoginViewModel : ReactiveObject
 {
 {
-    private readonly ObservableAsPropertyHelper<bool> _isValid;
-    private readonly ObservableAsPropertyHelper<int> _passwordLength;
-    private readonly ObservableAsPropertyHelper<int> _usernameLength;
+    [IgnoreDataMember]
+    [ObservableAsProperty] private bool _isValid;
 
 
-    public LoginViewModel ()
-    {
-        IObservable<bool> canLogin = this.WhenAnyValue (
-                                                        x => x.Username,
-                                                        x => x.Password,
-                                                        (username, password) =>
-                                                            !string.IsNullOrEmpty (username) && !string.IsNullOrEmpty (password)
-                                                       );
+    [IgnoreDataMember]
+    [ObservableAsProperty] private int _passwordLength;
 
 
-        _isValid = canLogin.ToProperty (this, x => x.IsValid);
+    [IgnoreDataMember]
+    [ObservableAsProperty] private int _usernameLength;
 
 
-        Login = ReactiveCommand.CreateFromTask<CancelEventArgs> (
-                                                                 e => Task.Delay (TimeSpan.FromSeconds (1)),
-                                                                 canLogin
-                                                                );
+    [DataMember]
+    [Reactive] private string _password = string.Empty;
 
 
-        _usernameLength = this
+    [DataMember]
+    [Reactive] private string _username = string.Empty;
+
+    public LoginViewModel ()
+    {
+        InitializeCommands ();
+        IObservable<bool> canLogin = this.WhenAnyValue
+            (
+                x => x.Username,
+                x => x.Password,
+                (username, password) =>
+                    !string.IsNullOrEmpty (username) && !string.IsNullOrEmpty (password)
+            );
+
+        _isValidHelper = canLogin.ToProperty (this, x => x.IsValid);
+
+        Login = ReactiveCommand.CreateFromTask<HandledEventArgs>
+            (
+                e => Task.Delay (TimeSpan.FromSeconds (1)),
+                canLogin
+            );
+
+        _usernameLengthHelper = this
                           .WhenAnyValue (x => x.Username)
                           .WhenAnyValue (x => x.Username)
                           .Select (name => name.Length)
                           .Select (name => name.Length)
                           .ToProperty (this, x => x.UsernameLength);
                           .ToProperty (this, x => x.UsernameLength);
 
 
-        _passwordLength = this
+        _passwordLengthHelper = this
                           .WhenAnyValue (x => x.Password)
                           .WhenAnyValue (x => x.Password)
                           .Select (password => password.Length)
                           .Select (password => password.Length)
                           .ToProperty (this, x => x.PasswordLength);
                           .ToProperty (this, x => x.PasswordLength);
 
 
-        Clear = ReactiveCommand.Create<CancelEventArgs> (e => { });
-
-        Clear.Subscribe (
+        ClearCommand.Subscribe (
                          unit =>
                          unit =>
                          {
                          {
                              Username = string.Empty;
                              Username = string.Empty;
@@ -64,26 +76,9 @@ public class LoginViewModel : ReactiveObject
                         );
                         );
     }
     }
 
 
-    [IgnoreDataMember]
-    public ReactiveCommand<CancelEventArgs, Unit> Clear { get; }
-
-    [IgnoreDataMember]
-    public bool IsValid => _isValid.Value;
-
-    [IgnoreDataMember]
-    public ReactiveCommand<CancelEventArgs, Unit> Login { get; }
-
-    [Reactive]
-    [DataMember]
-    public string Password { get; set; } = string.Empty;
-
-    [IgnoreDataMember]
-    public int PasswordLength => _passwordLength.Value;
-
-    [Reactive]
-    [DataMember]
-    public string Username { get; set; } = string.Empty;
+    [ReactiveCommand]
+    public void Clear (HandledEventArgs args) { }
 
 
     [IgnoreDataMember]
     [IgnoreDataMember]
-    public int UsernameLength => _usernameLength.Value;
+    public ReactiveCommand<HandledEventArgs, Unit> Login { get; }
 }
 }

+ 1 - 1
ReactiveExample/Program.cs

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

+ 1 - 1
ReactiveExample/README.md

@@ -1,4 +1,4 @@
-This is a sample app that shows how to use `System.Reactive` and `ReactiveUI` with `Terminal.Gui`. The app uses the MVVM architecture that may seem familiar to folks coming from WPF, Xamarin Forms, UWP, Avalonia, or Windows Forms. In this app, we implement the data bindings using ReactiveUI `WhenAnyValue` syntax and [Pharmacist](https://github.com/reactiveui/pharmacist) — a tool that converts all events in a NuGet package into observable wrappers.
+This is a sample app that shows how to use `System.Reactive` and `ReactiveUI` with `Terminal.Gui`. The app uses the MVVM architecture that may seem familiar to folks coming from WPF, Xamarin Forms, UWP, Avalonia, or Windows Forms. In this app, we implement the data bindings using ReactiveUI `WhenAnyValue` syntax and [ObservableEvents](https://github.com/reactivemarbles/ObservableEvents) — a Source Generator that turns events into observable wrappers.
 
 
 <img src="https://user-images.githubusercontent.com/6759207/94748621-646a7280-038a-11eb-8ea0-34629dc799b3.gif" width="450">
 <img src="https://user-images.githubusercontent.com/6759207/94748621-646a7280-038a-11eb-8ea0-34629dc799b3.gif" width="450">
 
 

+ 1 - 1
ReactiveExample/ReactiveExample.csproj

@@ -11,9 +11,9 @@
     <InformationalVersion>2.0</InformationalVersion>
     <InformationalVersion>2.0</InformationalVersion>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="ReactiveUI.Fody" Version="[19.5.41,21)" />
     <PackageReference Include="ReactiveUI" Version="[20.1.1,21)" />
     <PackageReference Include="ReactiveUI" Version="[20.1.1,21)" />
     <PackageReference Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="[1.3.1,2)" PrivateAssets="all" />
     <PackageReference Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="[1.3.1,2)" PrivateAssets="all" />
+    <PackageReference Include="ReactiveUI.SourceGenerators" Version="[1.0.3,2)" PrivateAssets="all" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />

+ 24 - 0
ReactiveExample/ViewExtensions.cs

@@ -0,0 +1,24 @@
+using System;
+using Terminal.Gui;
+
+namespace ReactiveExample;
+public static class ViewExtensions
+{
+    public static (Window MainView, TOut LastControl) AddControl<TOut> (this Window view, Action<TOut> action)
+        where TOut : View, new()
+    {
+        TOut result = new ();
+        action (result);
+        view.Add (result);
+        return (view, result);
+    }
+
+    public static (Window MainView, TOut LastControl) AddControlAfter<TOut> (this (Window MainView, View LastControl) view, Action<View, TOut> action)
+        where TOut : View, new()
+    {
+        TOut result = new ();
+        action (view.LastControl, result);
+        view.MainView.Add (result);
+        return (view.MainView, result);
+    }
+}

+ 25 - 2
SelfContained/Program.cs

@@ -1,6 +1,8 @@
-// This is a simple example application for a self-contained single file.
+// This is a test application for a self-contained single file.
 
 
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
 namespace SelfContained;
 namespace SelfContained;
@@ -10,7 +12,28 @@ public static class Program
     [RequiresUnreferencedCode ("Calls Terminal.Gui.Application.Run<T>(Func<Exception, Boolean>, ConsoleDriver)")]
     [RequiresUnreferencedCode ("Calls Terminal.Gui.Application.Run<T>(Func<Exception, Boolean>, ConsoleDriver)")]
     private static void Main (string [] args)
     private static void Main (string [] args)
     {
     {
-        Application.Run<ExampleWindow> ().Dispose ();
+        Application.Init ();
+
+        #region The code in this region is not intended for use in a self-contained single-file. It's just here to make sure there is no functionality break with localization in Terminal.Gui using single-file
+
+        if (Equals (Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures?.Count == 0)
+        {
+            // Only happens if the project has <InvariantGlobalization>true</InvariantGlobalization>
+            Debug.Assert (Application.SupportedCultures.Count == 0);
+        }
+        else
+        {
+            Debug.Assert (Application.SupportedCultures?.Count > 0);
+            Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
+        }
+
+        #endregion
+
+        ExampleWindow app = new ();
+        Application.Run (app);
+
+        // Dispose the app object before shutdown
+        app.Dispose ();
 
 
         // Before the application exits, reset Terminal.Gui for clean shutdown
         // Before the application exits, reset Terminal.Gui for clean shutdown
         Application.Shutdown ();
         Application.Shutdown ();

+ 1 - 1
SelfContained/README.md

@@ -1,6 +1,6 @@
 # Terminal.Gui C# SelfContained
 # Terminal.Gui C# SelfContained
 
 
-This example shows how to use the `Terminal.Gui` library to create a simple `self-contained` `single file` GUI application in C#.
+This project aims to test the `Terminal.Gui` library to create a simple `self-contained` `single-file` GUI application in C#, ensuring that all its features are available.
 
 
 With `Debug` the `.csproj` is used and with `Release` the latest `nuget package` is used, either in `Solution Configurations` or in `Profile Publish`.
 With `Debug` the `.csproj` is used and with `Release` the latest `nuget package` is used, either in `Solution Configurations` or in `Profile Publish`.
 
 

+ 1 - 1
SelfContained/SelfContained.csproj

@@ -8,7 +8,7 @@
     <PublishTrimmed>true</PublishTrimmed>
     <PublishTrimmed>true</PublishTrimmed>
     <TrimMode>Link</TrimMode>
     <TrimMode>Link</TrimMode>
     <PublishSingleFile>true</PublishSingleFile>
     <PublishSingleFile>true</PublishSingleFile>
-    <InvariantGlobalization>true</InvariantGlobalization>
+    <InvariantGlobalization>false</InvariantGlobalization>
     <DebugType>embedded</DebugType>
     <DebugType>embedded</DebugType>
   </PropertyGroup>
   </PropertyGroup>
 
 

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

@@ -74,6 +74,8 @@ public static partial class Application // Initialization (Init/Shutdown)
             ResetState ();
             ResetState ();
         }
         }
 
 
+        Navigation = new ();
+
         // For UnitTests
         // For UnitTests
         if (driver is { })
         if (driver is { })
         {
         {

+ 97 - 72
Terminal.Gui/Application/Application.Keyboard.cs

@@ -1,66 +1,83 @@
 #nullable enable
 #nullable enable
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
-using static System.Formats.Asn1.AsnWriter;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public static partial class Application // Keyboard handling
 public static partial class Application // Keyboard handling
 {
 {
-    private static Key _alternateForwardKey = Key.Empty; // Defined in config.json
+    private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
 
 
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [JsonConverter (typeof (KeyJsonConverter))]
     [JsonConverter (typeof (KeyJsonConverter))]
-    public static Key AlternateForwardKey
+    public static Key NextTabKey
     {
     {
-        get => _alternateForwardKey;
+        get => _nextTabKey;
         set
         set
         {
         {
-            if (_alternateForwardKey != value)
+            if (_nextTabKey != value)
             {
             {
-                Key oldKey = _alternateForwardKey;
-                _alternateForwardKey = value;
-
-                if (_alternateForwardKey == Key.Empty)
-                {
-                    KeyBindings.Remove (_alternateForwardKey);
-                }
-                else
-                {
-                    KeyBindings.ReplaceKey (oldKey, _alternateForwardKey);
-                }
+                ReplaceKey (_nextTabKey, value);
+                _nextTabKey = value;
             }
             }
         }
         }
     }
     }
 
 
-    private static Key _alternateBackwardKey = Key.Empty; // Defined in config.json
+    private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
 
 
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [JsonConverter (typeof (KeyJsonConverter))]
     [JsonConverter (typeof (KeyJsonConverter))]
-    public static Key AlternateBackwardKey
+    public static Key PrevTabKey
     {
     {
-        get => _alternateBackwardKey;
+        get => _prevTabKey;
         set
         set
         {
         {
-            if (_alternateBackwardKey != value)
+            if (_prevTabKey != value)
             {
             {
-                Key oldKey = _alternateBackwardKey;
-                _alternateBackwardKey = value;
+                ReplaceKey (_prevTabKey, value);
+                _prevTabKey = value;
+            }
+        }
+    }
 
 
-                if (_alternateBackwardKey == Key.Empty)
-                {
-                    KeyBindings.Remove (_alternateBackwardKey);
-                }
-                else
-                {
-                    KeyBindings.ReplaceKey (oldKey, _alternateBackwardKey);
-                }
+    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
+
+    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    [JsonConverter (typeof (KeyJsonConverter))]
+    public static Key NextTabGroupKey
+    {
+        get => _nextTabGroupKey;
+        set
+        {
+            if (_nextTabGroupKey != value)
+            {
+                ReplaceKey (_nextTabGroupKey, value);
+                _nextTabGroupKey = value;
             }
             }
         }
         }
     }
     }
 
 
-    private static Key _quitKey = Key.Empty; // Defined in config.json
+    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
+
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    [JsonConverter (typeof (KeyJsonConverter))]
+    public static Key PrevTabGroupKey
+    {
+        get => _prevTabGroupKey;
+        set
+        {
+            if (_prevTabGroupKey != value)
+            {
+                ReplaceKey (_prevTabGroupKey, value);
+                _prevTabGroupKey = value;
+            }
+        }
+    }
+
+    private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
 
 
     /// <summary>Gets or sets the key to quit the application.</summary>
     /// <summary>Gets or sets the key to quit the application.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
@@ -72,20 +89,29 @@ public static partial class Application // Keyboard handling
         {
         {
             if (_quitKey != value)
             if (_quitKey != value)
             {
             {
-                Key oldKey = _quitKey;
+                ReplaceKey (_quitKey, value);
                 _quitKey = value;
                 _quitKey = value;
-                if (_quitKey == Key.Empty)
-                {
-                    KeyBindings.Remove (_quitKey);
-                }
-                else
-                {
-                    KeyBindings.ReplaceKey (oldKey, _quitKey);
-                }
             }
             }
         }
         }
     }
     }
 
 
+    private static void ReplaceKey (Key oldKey, Key newKey)
+    {
+        if (KeyBindings.Bindings.Count == 0)
+        {
+            return;
+        }
+
+        if (newKey == Key.Empty)
+        {
+            KeyBindings.Remove (oldKey);
+        }
+        else
+        {
+            KeyBindings.ReplaceKey (oldKey, newKey);
+        }
+    }
+
     /// <summary>
     /// <summary>
     ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
     ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
     ///     <para>
     ///     <para>
@@ -139,7 +165,7 @@ public static partial class Application // Keyboard handling
         }
         }
         else
         else
         {
         {
-            if (Application.Current.NewKeyDownEvent (keyEvent))
+            if (Current.NewKeyDownEvent (keyEvent))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -147,7 +173,7 @@ public static partial class Application // Keyboard handling
 
 
         // Invoke any Application-scoped KeyBindings.
         // Invoke any Application-scoped KeyBindings.
         // The first view that handles the key will stop the loop.
         // The first view that handles the key will stop the loop.
-        foreach (var binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode))
+        foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode))
         {
         {
             if (binding.Value.BoundView is { })
             if (binding.Value.BoundView is { })
             {
             {
@@ -193,7 +219,6 @@ public static partial class Application // Keyboard handling
             }
             }
         }
         }
 
 
-
         return false;
         return false;
     }
     }
 
 
@@ -252,13 +277,13 @@ public static partial class Application // Keyboard handling
     public static KeyBindings KeyBindings { get; internal set; } = new ();
     public static KeyBindings KeyBindings { get; internal set; } = new ();
 
 
     /// <summary>
     /// <summary>
-    /// Commands for Application.
+    ///     Commands for Application.
     /// </summary>
     /// </summary>
     private static Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; set; }
     private static Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; set; }
 
 
     /// <summary>
     /// <summary>
     ///     <para>
     ///     <para>
-    ///         Sets the function that will be invoked for a <see cref="Command"/>. 
+    ///         Sets the function that will be invoked for a <see cref="Command"/>.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
@@ -266,28 +291,23 @@ public static partial class Application // Keyboard handling
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    /// <para>
-    ///     This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
-    /// </para>
+    ///     <para>
+    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///     </para>
     /// </remarks>
     /// </remarks>
     /// <param name="command">The command.</param>
     /// <param name="command">The command.</param>
     /// <param name="f">The function.</param>
     /// <param name="f">The function.</param>
-    private static void AddCommand (Command command, Func<bool?> f)
-    {
-        CommandImplementations [command] = ctx => f ();
-    }
+    private static void AddCommand (Command command, Func<bool?> f) { CommandImplementations [command] = ctx => f (); }
 
 
-    static Application ()
-    {
-        AddApplicationKeyBindings();
-    }
+    static Application () { AddApplicationKeyBindings (); }
 
 
     internal static void AddApplicationKeyBindings ()
     internal static void AddApplicationKeyBindings ()
     {
     {
-        CommandImplementations = new Dictionary<Command, Func<CommandContext, bool?>> ();
+        CommandImplementations = new ();
+
         // Things this view knows how to do
         // Things this view knows how to do
         AddCommand (
         AddCommand (
-                    Command.QuitToplevel,  // TODO: IRunnable: Rename to Command.Quit to make more generic.
+                    Command.QuitToplevel, // TODO: IRunnable: Rename to Command.Quit to make more generic.
                     () =>
                     () =>
                     {
                     {
                         if (ApplicationOverlapped.OverlappedTop is { })
                         if (ApplicationOverlapped.OverlappedTop is { })
@@ -296,7 +316,7 @@ public static partial class Application // Keyboard handling
                         }
                         }
                         else
                         else
                         {
                         {
-                            Application.RequestStop ();
+                            RequestStop ();
                         }
                         }
 
 
                         return true;
                         return true;
@@ -363,26 +383,31 @@ public static partial class Application // Keyboard handling
                     }
                     }
                    );
                    );
 
 
-
         KeyBindings.Clear ();
         KeyBindings.Clear ();
 
 
-        KeyBindings.Add (Application.QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
+        // Resources/config.json overrrides
+        NextTabKey = Key.Tab;
+        PrevTabKey = Key.Tab.WithShift;
+        NextTabGroupKey = Key.F6;
+        PrevTabGroupKey = Key.F6.WithShift;
+        QuitKey = Key.Esc;
+
+        KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
 
 
         KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousView);
         KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousView);
         KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousView);
         KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousView);
+        KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextView);
+        KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousView);
 
 
-        KeyBindings.Add (Key.Tab, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.Application, Command.PreviousView);
-        KeyBindings.Add (Key.Tab.WithCtrl, KeyBindingScope.Application, Command.NextViewOrTop);
-        KeyBindings.Add (Key.Tab.WithShift.WithCtrl, KeyBindingScope.Application, Command.PreviousViewOrTop);
+        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop); // Needed on Unix
+        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop); // Needed on Unix
 
 
         // TODO: Refresh Key should be configurable
         // TODO: Refresh Key should be configurable
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
-        KeyBindings.Add (Application.AlternateForwardKey, KeyBindingScope.Application, Command.NextViewOrTop); // Needed on Unix
-        KeyBindings.Add (Application.AlternateBackwardKey, KeyBindingScope.Application, Command.PreviousViewOrTop); // Needed on Unix
 
 
+        // TODO: Suspend Key should be configurable
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         {
         {
             KeyBindings.Add (Key.Z.WithCtrl, KeyBindingScope.Application, Command.Suspend);
             KeyBindings.Add (Key.Z.WithCtrl, KeyBindingScope.Application, Command.Suspend);
@@ -433,10 +458,10 @@ public static partial class Application // Keyboard handling
     /// <param name="view">The view that is bound to the key.</param>
     /// <param name="view">The view that is bound to the key.</param>
     internal static void RemoveKeyBindings (View view)
     internal static void RemoveKeyBindings (View view)
     {
     {
-        var list = KeyBindings.Bindings
-                          .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
-                          .Select (kv => kv.Value)
-                          .Distinct ()
-                          .ToList ();
+        List<KeyBinding> list = KeyBindings.Bindings
+                                           .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
+                                           .Select (kv => kv.Value)
+                                           .Distinct ()
+                                           .ToList ();
     }
     }
 }
 }

+ 3 - 223
Terminal.Gui/Application/Application.Navigation.cs

@@ -1,230 +1,10 @@
 #nullable enable
 #nullable enable
-using System.Diagnostics;
-using System.Reflection.PortableExecutable;
-using System.Security.Cryptography;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>
-///     Static helper class for <see cref="Application"/> navigation.
-/// </summary>
-public static class ApplicationNavigation
+public static partial class Application // Navigation stuff
 {
 {
-    private static View? _focused = null;
-
-    /// <summary>
-    ///     Gets the most focused <see cref="View"/> in the application, if there is one.
-    /// </summary>
-    public static View? GetFocused () { return _focused; }
-
-    /// <summary>
-    ///     INTERNAL method to record the most focused <see cref="View"/> in the application.
-    /// </summary>
-    /// <remarks>
-    ///     Raises <see cref="FocusedChanged"/>.
-    /// </remarks>
-    internal static void SetFocused (View? value)
-    {
-        if (_focused == value)
-        {
-            return;
-        }
-
-        _focused = value;
-
-        FocusedChanged?.Invoke (null, EventArgs.Empty);
-
-        return;
-    }
-
-    /// <summary>
-    ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
-    /// </summary>
-    /// <param name="start"></param>
-    /// <param name="view"></param>
-    /// <returns></returns>
-    public static bool IsInHierarchy (View start, View? view)
-    {
-        if (view is null)
-        {
-            return false;
-        }
-
-        if (view == start)
-        {
-            return true;
-        }
-
-        foreach (View subView in start.Subviews)
-        {
-            if (view == subView)
-            {
-                return true;
-            }
-
-            var found = IsInHierarchy (subView, view);
-            if (found)
-            {
-                return found;
-            }
-        }
-
-        return false;
-    }
-
     /// <summary>
     /// <summary>
-    ///     Raised when the most focused <see cref="View"/> in the application has changed.
+    ///     Gets the <see cref="ApplicationNavigation"/> instance for the current <see cref="Application"/>.
     /// </summary>
     /// </summary>
-    public static event EventHandler<EventArgs>? FocusedChanged;
-
-
-    /// <summary>
-    ///    Gets the deepest focused subview of the specified <paramref name="view"/>.
-    /// </summary>
-    /// <param name="view"></param>
-    /// <returns></returns>
-    internal static View? GetDeepestFocusedSubview (View? view)
-    {
-        if (view is null)
-        {
-            return null;
-        }
-
-        foreach (View v in view.Subviews)
-        {
-            if (v.HasFocus)
-            {
-                return GetDeepestFocusedSubview (v);
-            }
-        }
-
-        return view;
-    }
-
-    /// <summary>
-    ///     Moves the focus to the next focusable view.
-    ///     Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
-    ///     if the current and next subviews are not overlapped.
-    /// </summary>
-    internal static void MoveNextView ()
-    {
-        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Forward, null);
-        }
-
-        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.Focused?.SetNeedsDisplay ();
-        }
-        else
-        {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
-        }
-    }
-
-    /// <summary>
-    ///     Moves the focus to the next <see cref="Toplevel"/> subview or the next subview that has <see cref="ApplicationOverlapped.OverlappedTop"/> set.
-    /// </summary>
-    internal static void MoveNextViewOrTop ()
-    {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-
-            if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
-            {
-                Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-
-                if (Application.Current.Focused is null)
-                {
-                    Application.Current.RestoreFocus (TabBehavior.TabGroup);
-                }
-            }
-
-            if (top != Application.Current.Focused && top != Application.Current.Focused?.Focused)
-            {
-                top?.SetNeedsDisplay ();
-                Application.Current.Focused?.SetNeedsDisplay ();
-            }
-            else
-            {
-                ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
-            }
-
-
-
-            //top!.AdvanceFocus (NavigationDirection.Forward);
-
-            //if (top.Focused is null)
-            //{
-            //    top.AdvanceFocus (NavigationDirection.Forward);
-            //}
-
-            //top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMoveNext ();
-        }
-    }
-
-    // TODO: These methods should return bool to indicate if the focus was moved or not.
-
-    /// <summary>
-    ///     Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
-    ///     if the current and next subviews are not overlapped.
-    /// </summary>
-    internal static void MovePreviousView ()
-    {
-        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Backward, null);
-        }
-
-        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.Focused?.SetNeedsDisplay ();
-        }
-        else
-        {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes?.Reverse (), NavigationDirection.Backward);
-        }
-    }
-
-    internal static void MovePreviousViewOrTop ()
-    {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-            top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
-
-            if (top.Focused is null)
-            {
-                top.AdvanceFocus (NavigationDirection.Backward, null);
-            }
-
-            top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMovePrevious ();
-        }
-    }
-
-    public static void ResetState ()
-    {
-        _focused?.Dispose ();
-        _focused = null;
-
-        FocusedChanged = null;
-    }
+    public static ApplicationNavigation? Navigation { get; internal set; }
 }
 }

+ 94 - 78
Terminal.Gui/Application/Application.cs

@@ -2,6 +2,8 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Globalization;
 using System.Globalization;
 using System.Reflection;
 using System.Reflection;
+using System.Resources;
+using Terminal.Gui.Resources;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -24,9 +26,82 @@ public static partial class Application
     /// <summary>Gets all cultures supported by the application without the invariant language.</summary>
     /// <summary>Gets all cultures supported by the application without the invariant language.</summary>
     public static List<CultureInfo>? SupportedCultures { get; private set; }
     public static List<CultureInfo>? SupportedCultures { get; private set; }
 
 
+    /// <summary>
+    ///     Gets a string representation of the Application as rendered by <see cref="Driver"/>.
+    /// </summary>
+    /// <returns>A string representation of the Application </returns>
+    public new static string ToString ()
+    {
+        ConsoleDriver driver = Driver;
+
+        if (driver is null)
+        {
+            return string.Empty;
+        }
+
+        return ToString (driver);
+    }
+
+    /// <summary>
+    ///     Gets a string representation of the Application rendered by the provided <see cref="ConsoleDriver"/>.
+    /// </summary>
+    /// <param name="driver">The driver to use to render the contents.</param>
+    /// <returns>A string representation of the Application </returns>
+    public static string ToString (ConsoleDriver driver)
+    {
+        var sb = new StringBuilder ();
+
+        Cell [,] contents = driver.Contents;
+
+        for (var r = 0; r < driver.Rows; r++)
+        {
+            for (var c = 0; c < driver.Cols; c++)
+            {
+                Rune rune = contents [r, c].Rune;
+
+                if (rune.DecodeSurrogatePair (out char [] sp))
+                {
+                    sb.Append (sp);
+                }
+                else
+                {
+                    sb.Append ((char)rune.Value);
+                }
+
+                if (rune.GetColumns () > 1)
+                {
+                    c++;
+                }
+
+                // See Issue #2616
+                //foreach (var combMark in contents [r, c].CombiningMarks) {
+                //	sb.Append ((char)combMark.Value);
+                //}
+            }
+
+            sb.AppendLine ();
+        }
+
+        return sb.ToString ();
+    }
+
+    internal static List<CultureInfo> GetAvailableCulturesFromEmbeddedResources ()
+    {
+        ResourceManager rm = new (typeof (Strings));
+
+        CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures);
+
+        return cultures.Where (
+                               cultureInfo =>
+                                   !cultureInfo.Equals (CultureInfo.InvariantCulture)
+                                   && rm.GetResourceSet (cultureInfo, true, false) is { }
+                              )
+                       .ToList ();
+    }
+
     internal static List<CultureInfo> GetSupportedCultures ()
     internal static List<CultureInfo> GetSupportedCultures ()
     {
     {
-        CultureInfo [] culture = CultureInfo.GetCultures (CultureTypes.AllCultures);
+        CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures);
 
 
         // Get the assembly
         // Get the assembly
         var assembly = Assembly.GetExecutingAssembly ();
         var assembly = Assembly.GetExecutingAssembly ();
@@ -35,15 +110,21 @@ public static partial class Application
         string assemblyLocation = AppDomain.CurrentDomain.BaseDirectory;
         string assemblyLocation = AppDomain.CurrentDomain.BaseDirectory;
 
 
         // Find the resource file name of the assembly
         // Find the resource file name of the assembly
-        var resourceFilename = $"{Path.GetFileNameWithoutExtension (AppContext.BaseDirectory)}.resources.dll";
-
-        // Return all culture for which satellite folder found with culture code.
-        return culture.Where (
-                              cultureInfo =>
-                                  Directory.Exists (Path.Combine (assemblyLocation, cultureInfo.Name))
-                                  && File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename))
-                             )
-                      .ToList ();
+        var resourceFilename = $"{assembly.GetName ().Name}.resources.dll";
+
+        if (cultures.Length > 1 && Directory.Exists (Path.Combine (assemblyLocation, "pt-PT")))
+        {
+            // Return all culture for which satellite folder found with culture code.
+            return cultures.Where (
+                                   cultureInfo =>
+                                       Directory.Exists (Path.Combine (assemblyLocation, cultureInfo.Name))
+                                       && File.Exists (Path.Combine (assemblyLocation, cultureInfo.Name, resourceFilename))
+                                  )
+                           .ToList ();
+        }
+
+        // It's called from a self-contained single-file and get available cultures from the embedded resources strings.
+        return GetAvailableCulturesFromEmbeddedResources ();
     }
     }
 
 
     // IMPORTANT: Ensure all property/fields are reset here. See Init_ResetState_Resets_Properties unit test.
     // IMPORTANT: Ensure all property/fields are reset here. See Init_ResetState_Resets_Properties unit test.
@@ -122,12 +203,12 @@ public static partial class Application
         UnGrabbedMouse = null;
         UnGrabbedMouse = null;
 
 
         // Keyboard
         // Keyboard
-        AlternateBackwardKey = Key.Empty;
-        AlternateForwardKey = Key.Empty;
-        QuitKey = Key.Empty;
         KeyDown = null;
         KeyDown = null;
         KeyUp = null;
         KeyUp = null;
         SizeChanging = null;
         SizeChanging = null;
+
+        Navigation = null;
+
         AddApplicationKeyBindings ();
         AddApplicationKeyBindings ();
 
 
         Colors.Reset ();
         Colors.Reset ();
@@ -139,70 +220,5 @@ public static partial class Application
         SynchronizationContext.SetSynchronizationContext (null);
         SynchronizationContext.SetSynchronizationContext (null);
     }
     }
 
 
-#nullable enable
-#nullable restore
-
-#nullable enable
-
     // Only return true if the Current has changed.
     // Only return true if the Current has changed.
-#nullable restore
-
-    /// <summary>
-    ///     Gets a string representation of the Application as rendered by <see cref="Driver"/>.
-    /// </summary>
-    /// <returns>A string representation of the Application </returns>
-    public new static string ToString ()
-    {
-        ConsoleDriver driver = Driver;
-
-        if (driver is null)
-        {
-            return string.Empty;
-        }
-
-        return ToString (driver);
-    }
-
-    /// <summary>
-    ///     Gets a string representation of the Application rendered by the provided <see cref="ConsoleDriver"/>.
-    /// </summary>
-    /// <param name="driver">The driver to use to render the contents.</param>
-    /// <returns>A string representation of the Application </returns>
-    public static string ToString (ConsoleDriver driver)
-    {
-        var sb = new StringBuilder ();
-
-        Cell [,] contents = driver.Contents;
-
-        for (var r = 0; r < driver.Rows; r++)
-        {
-            for (var c = 0; c < driver.Cols; c++)
-            {
-                Rune rune = contents [r, c].Rune;
-
-                if (rune.DecodeSurrogatePair (out char [] sp))
-                {
-                    sb.Append (sp);
-                }
-                else
-                {
-                    sb.Append ((char)rune.Value);
-                }
-
-                if (rune.GetColumns () > 1)
-                {
-                    c++;
-                }
-
-                // See Issue #2616
-                //foreach (var combMark in contents [r, c].CombiningMarks) {
-                //	sb.Append ((char)combMark.Value);
-                //}
-            }
-
-            sb.AppendLine ();
-        }
-
-        return sb.ToString ();
-    }
 }
 }

+ 229 - 0
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -0,0 +1,229 @@
+#nullable enable
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Helper class for <see cref="Application"/> navigation. Held by <see cref="Application.Navigation"/>
+/// </summary>
+public class ApplicationNavigation
+{
+
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="ApplicationNavigation"/> class.
+    /// </summary>
+    public ApplicationNavigation ()
+    {
+        // TODO: Move navigation key bindings here from AddApplicationKeyBindings
+    }
+
+    private View? _focused = null;
+
+    /// <summary>
+    ///     Gets the most focused <see cref="View"/> in the application, if there is one.
+    /// </summary>
+    public View? GetFocused () { return _focused; }
+
+    /// <summary>
+    ///     INTERNAL method to record the most focused <see cref="View"/> in the application.
+    /// </summary>
+    /// <remarks>
+    ///     Raises <see cref="FocusedChanged"/>.
+    /// </remarks>
+    internal void SetFocused (View? value)
+    {
+        if (_focused == value)
+        {
+            return;
+        }
+
+        _focused = value;
+
+        FocusedChanged?.Invoke (null, EventArgs.Empty);
+
+        return;
+    }
+
+    /// <summary>
+    ///     Raised when the most focused <see cref="View"/> in the application has changed.
+    /// </summary>
+    public event EventHandler<EventArgs>? FocusedChanged;
+
+
+    /// <summary>
+    ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
+    /// </summary>
+    /// <param name="start"></param>
+    /// <param name="view"></param>
+    /// <returns></returns>
+    public static bool IsInHierarchy (View start, View? view)
+    {
+        if (view is null)
+        {
+            return false;
+        }
+
+        if (view == start)
+        {
+            return true;
+        }
+
+        foreach (View subView in start.Subviews)
+        {
+            if (view == subView)
+            {
+                return true;
+            }
+
+            var found = IsInHierarchy (subView, view);
+            if (found)
+            {
+                return found;
+            }
+        }
+
+        return false;
+    }
+
+
+    /// <summary>
+    ///     Gets the deepest focused subview of the specified <paramref name="view"/>.
+    /// </summary>
+    /// <param name="view"></param>
+    /// <returns></returns>
+    internal static View? GetDeepestFocusedSubview (View? view)
+    {
+        if (view is null)
+        {
+            return null;
+        }
+
+        foreach (View v in view.Subviews)
+        {
+            if (v.HasFocus)
+            {
+                return GetDeepestFocusedSubview (v);
+            }
+        }
+
+        return view;
+    }
+
+    /// <summary>
+    ///     Moves the focus to the next focusable view.
+    ///     Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
+    ///     if the current and next subviews are not overlapped.
+    /// </summary>
+    internal static void MoveNextView ()
+    {
+        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
+
+        if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop))
+        {
+            Application.Current.AdvanceFocus (NavigationDirection.Forward, null);
+        }
+
+        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
+        {
+            old?.SetNeedsDisplay ();
+            Application.Current.Focused?.SetNeedsDisplay ();
+        }
+        else
+        {
+            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
+        }
+    }
+
+    /// <summary>
+    ///     Moves the focus to the next <see cref="Toplevel"/> subview or the next subview that has
+    ///     <see cref="ApplicationOverlapped.OverlappedTop"/> set.
+    /// </summary>
+    internal static void MoveNextViewOrTop ()
+    {
+        if (ApplicationOverlapped.OverlappedTop is null)
+        {
+            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
+
+            if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
+            {
+                Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+
+                if (Application.Current.Focused is null)
+                {
+                    Application.Current.RestoreFocus ();
+                }
+            }
+
+            if (top != Application.Current.Focused && top != Application.Current.Focused?.Focused)
+            {
+                top?.SetNeedsDisplay ();
+                Application.Current.Focused?.SetNeedsDisplay ();
+            }
+            else
+            {
+                ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
+            }
+
+            //top!.AdvanceFocus (NavigationDirection.Forward);
+
+            //if (top.Focused is null)
+            //{
+            //    top.AdvanceFocus (NavigationDirection.Forward);
+            //}
+
+            //top.SetNeedsDisplay ();
+            ApplicationOverlapped.BringOverlappedTopToFront ();
+        }
+        else
+        {
+            ApplicationOverlapped.OverlappedMoveNext ();
+        }
+    }
+
+    // TODO: These methods should return bool to indicate if the focus was moved or not.
+
+    /// <summary>
+    ///     Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next
+    ///     subview
+    ///     if the current and next subviews are not overlapped.
+    /// </summary>
+    internal static void MovePreviousView ()
+    {
+        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
+
+        if (!Application.Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop))
+        {
+            Application.Current.AdvanceFocus (NavigationDirection.Backward, null);
+        }
+
+        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
+        {
+            old?.SetNeedsDisplay ();
+            Application.Current.Focused?.SetNeedsDisplay ();
+        }
+        else
+        {
+            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes?.Reverse (), NavigationDirection.Backward);
+        }
+    }
+
+    internal static void MovePreviousViewOrTop ()
+    {
+        if (ApplicationOverlapped.OverlappedTop is null)
+        {
+            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
+            top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+
+            if (top.Focused is null)
+            {
+                top.AdvanceFocus (NavigationDirection.Backward, null);
+            }
+
+            top.SetNeedsDisplay ();
+            ApplicationOverlapped.BringOverlappedTopToFront ();
+        }
+        else
+        {
+            ApplicationOverlapped.OverlappedMovePrevious ();
+        }
+    }
+}

+ 0 - 0
Terminal.Gui/Application/Application.Overlapped.cs → Terminal.Gui/Application/ApplicationOverlapped.cs


+ 66 - 65
Terminal.Gui/Input/KeyBindings.cs

@@ -1,12 +1,9 @@
 #nullable enable
 #nullable enable
 
 
-using System.Diagnostics;
-using Microsoft.CodeAnalysis;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
-/// Provides a collection of <see cref="KeyBinding"/> objects bound to a <see cref="Key"/>.
+///     Provides a collection of <see cref="KeyBinding"/> objects bound to a <see cref="Key"/>.
 /// </summary>
 /// </summary>
 public class KeyBindings
 public class KeyBindings
 {
 {
@@ -19,19 +16,6 @@ public class KeyBindings
     /// <summary>Initializes a new instance bound to <paramref name="boundView"/>.</summary>
     /// <summary>Initializes a new instance bound to <paramref name="boundView"/>.</summary>
     public KeyBindings (View boundView) { BoundView = boundView; }
     public KeyBindings (View boundView) { BoundView = boundView; }
 
 
-    /// <summary>
-    ///     The view that the <see cref="KeyBindings"/> are bound to.
-    /// </summary>
-    /// <remarks>
-    ///     If <see langword="null"/>, the <see cref="KeyBindings"/> are not bound to a <see cref="View"/>. This is used for Application.KeyBindings.
-    /// </remarks>
-    public View? BoundView { get; }
-
-    // TODO: Add a dictionary comparer that ignores Scope
-    // TODO: This should not be public!
-    /// <summary>The collection of <see cref="KeyBinding"/> objects.</summary>
-    public Dictionary<Key, KeyBinding> Bindings { get; } = new ();
-
     /// <summary>Adds a <see cref="KeyBinding"/> to the collection.</summary>
     /// <summary>Adds a <see cref="KeyBinding"/> to the collection.</summary>
     /// <param name="key"></param>
     /// <param name="key"></param>
     /// <param name="binding"></param>
     /// <param name="binding"></param>
@@ -45,21 +29,21 @@ public class KeyBindings
 
 
         if (TryGet (key, out KeyBinding _))
         if (TryGet (key, out KeyBinding _))
         {
         {
-            throw new InvalidOperationException(@$"A key binding for {key} exists ({binding}).");
+            throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
+
             //Bindings [key] = binding;
             //Bindings [key] = binding;
         }
         }
+
+        if (BoundView is { })
+        {
+            binding.BoundView = BoundView;
+        }
         else
         else
         {
         {
-            if (BoundView is { })
-            {
-                binding.BoundView = BoundView;
-            }
-            else
-            {
-                binding.BoundView = boundViewForAppScope;
-            }
-            Bindings.Add (key, binding);
+            binding.BoundView = boundViewForAppScope;
         }
         }
+
+        Bindings.Add (key, binding);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -102,14 +86,12 @@ public class KeyBindings
         if (TryGet (key, out KeyBinding binding))
         if (TryGet (key, out KeyBinding binding))
         {
         {
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
+
             //Bindings [key] = new (commands, scope, BoundView);
             //Bindings [key] = new (commands, scope, BoundView);
         }
         }
-        else
-        {
-            Add (key, new KeyBinding (commands, scope, BoundView), boundViewForAppScope);
-        }
-    }
 
 
+        Add (key, new KeyBinding (commands, scope, BoundView), boundViewForAppScope);
+    }
 
 
     /// <summary>
     /// <summary>
     ///     <para>Adds a new key combination that will trigger the commands in <paramref name="commands"/>.</para>
     ///     <para>Adds a new key combination that will trigger the commands in <paramref name="commands"/>.</para>
@@ -129,17 +111,16 @@ public class KeyBindings
     ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
     ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
     ///     consumed if any took effect.
     ///     consumed if any took effect.
     /// </param>
     /// </param>
-    public void Add (Key key, KeyBindingScope scope,  params Command [] commands)
+    public void Add (Key key, KeyBindingScope scope, params Command [] commands)
     {
     {
         if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
         if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
         {
         {
             throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
             throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
         }
         }
 
 
-        if (key is null || !key.IsValid)
+        if (key == Key.Empty || !key.IsValid)
         {
         {
-            //throw new ArgumentException ("Invalid Key", nameof (commands));
-            return;
+            throw new ArgumentException (@"Invalid Key", nameof (commands));
         }
         }
 
 
         if (commands.Length == 0)
         if (commands.Length == 0)
@@ -150,12 +131,9 @@ public class KeyBindings
         if (TryGet (key, out KeyBinding binding))
         if (TryGet (key, out KeyBinding binding))
         {
         {
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
             throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
-            //Bindings [key] = new (commands, scope, BoundView);
-        }
-        else
-        {
-            Add (key, new KeyBinding (commands, scope, BoundView), null);
         }
         }
+
+        Add (key, new KeyBinding (commands, scope, BoundView));
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -164,7 +142,8 @@ public class KeyBindings
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (<see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
+    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (
+    ///         <see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
     ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
     ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
@@ -187,8 +166,9 @@ public class KeyBindings
     {
     {
         if (BoundView is null && boundViewForAppScope is null)
         if (BoundView is null && boundViewForAppScope is null)
         {
         {
-            throw new ArgumentException (@"Application scoped KeyBindings must provide a bound view to Add.", nameof(boundViewForAppScope));
+            throw new ArgumentException (@"Application scoped KeyBindings must provide a bound view to Add.", nameof (boundViewForAppScope));
         }
         }
+
         Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, boundViewForAppScope, commands);
         Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, boundViewForAppScope, commands);
     }
     }
 
 
@@ -198,7 +178,8 @@ public class KeyBindings
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (<see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
+    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (
+    ///         <see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
     ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
     ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
@@ -222,14 +203,26 @@ public class KeyBindings
         {
         {
             throw new ArgumentException (@"Application scoped KeyBindings must provide a boundViewForAppScope to Add.");
             throw new ArgumentException (@"Application scoped KeyBindings must provide a boundViewForAppScope to Add.");
         }
         }
+
         Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, null, commands);
         Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, null, commands);
     }
     }
 
 
+    // TODO: Add a dictionary comparer that ignores Scope
+    // TODO: This should not be public!
+    /// <summary>The collection of <see cref="KeyBinding"/> objects.</summary>
+    public Dictionary<Key, KeyBinding> Bindings { get; } = new ();
+
+    /// <summary>
+    ///     The view that the <see cref="KeyBindings"/> are bound to.
+    /// </summary>
+    /// <remarks>
+    ///     If <see langword="null"/>, the <see cref="KeyBindings"/> are not bound to a <see cref="View"/>. This is used for
+    ///     Application.KeyBindings.
+    /// </remarks>
+    public View? BoundView { get; }
+
     /// <summary>Removes all <see cref="KeyBinding"/> objects from the collection.</summary>
     /// <summary>Removes all <see cref="KeyBinding"/> objects from the collection.</summary>
-    public void Clear ()
-    {
-        Bindings.Clear ();
-    }
+    public void Clear () { Bindings.Clear (); }
 
 
     /// <summary>
     /// <summary>
     ///     Removes all key bindings that trigger the given command set. Views can have multiple different keys bound to
     ///     Removes all key bindings that trigger the given command set. Views can have multiple different keys bound to
@@ -257,6 +250,7 @@ public class KeyBindings
         {
         {
             return binding;
             return binding;
         }
         }
+
         throw new InvalidOperationException ($"Key {key} is not bound.");
         throw new InvalidOperationException ($"Key {key} is not bound.");
     }
     }
 
 
@@ -270,6 +264,7 @@ public class KeyBindings
         {
         {
             return binding;
             return binding;
         }
         }
+
         throw new InvalidOperationException ($"Key {key}/{scope} is not bound.");
         throw new InvalidOperationException ($"Key {key}/{scope} is not bound.");
     }
     }
 
 
@@ -301,7 +296,6 @@ public class KeyBindings
     /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
     /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
     public void Remove (Key key, View? boundViewForAppScope = null)
     public void Remove (Key key, View? boundViewForAppScope = null)
     {
     {
-
         if (!TryGet (key, out KeyBinding binding))
         if (!TryGet (key, out KeyBinding binding))
         {
         {
             return;
             return;
@@ -310,22 +304,6 @@ public class KeyBindings
         Bindings.Remove (key);
         Bindings.Remove (key);
     }
     }
 
 
-    /// <summary>Replaces a key combination already bound to a set of <see cref="Command"/>s.</summary>
-    /// <remarks></remarks>
-    /// <param name="oldKey">The key to be replaced.</param>
-    /// <param name="newKey">The new key to be used.</param>
-    public void ReplaceKey (Key oldKey, Key newKey)
-    {
-        if (!TryGet (oldKey, out KeyBinding _))
-        {
-            return;
-        }
-
-        KeyBinding value = Bindings [oldKey];
-        Remove (oldKey);
-        Add (newKey, value);
-    }
-
     /// <summary>Replaces the commands already bound to a key.</summary>
     /// <summary>Replaces the commands already bound to a key.</summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
@@ -346,6 +324,27 @@ public class KeyBindings
         }
         }
     }
     }
 
 
+    /// <summary>Replaces a key combination already bound to a set of <see cref="Command"/>s.</summary>
+    /// <remarks></remarks>
+    /// <param name="oldKey">The key to be replaced.</param>
+    /// <param name="newKey">The new key to be used. If <see cref="Key.Empty"/> no action will be taken.</param>
+    public void ReplaceKey (Key oldKey, Key newKey)
+    {
+        if (!TryGet (oldKey, out KeyBinding _))
+        {
+            throw new InvalidOperationException ($"Key {oldKey} is not bound.");
+        }
+
+        if (!newKey.IsValid)
+        {
+            throw new InvalidOperationException ($"Key {newKey} is is not valid.");
+        }
+
+        KeyBinding value = Bindings [oldKey];
+        Remove (oldKey);
+        Add (newKey, value);
+    }
+
     /// <summary>Gets the commands bound with the specified Key.</summary>
     /// <summary>Gets the commands bound with the specified Key.</summary>
     /// <remarks></remarks>
     /// <remarks></remarks>
     /// <param name="key">The key to check.</param>
     /// <param name="key">The key to check.</param>
@@ -357,6 +356,7 @@ public class KeyBindings
     public bool TryGet (Key key, out KeyBinding binding)
     public bool TryGet (Key key, out KeyBinding binding)
     {
     {
         binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
         binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
+
         if (key.IsValid)
         if (key.IsValid)
         {
         {
             return Bindings.TryGetValue (key, out binding);
             return Bindings.TryGetValue (key, out binding);
@@ -377,6 +377,7 @@ public class KeyBindings
     public bool TryGet (Key key, KeyBindingScope scope, out KeyBinding binding)
     public bool TryGet (Key key, KeyBindingScope scope, out KeyBinding binding)
     {
     {
         binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
         binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
+
         if (key.IsValid && Bindings.TryGetValue (key, out binding))
         if (key.IsValid && Bindings.TryGetValue (key, out binding))
         {
         {
             if (scope.HasFlag (binding.Scope))
             if (scope.HasFlag (binding.Scope))

+ 7 - 4
Terminal.Gui/Resources/config.json

@@ -17,8 +17,10 @@
   // to throw exceptions. 
   // to throw exceptions. 
   "ConfigurationManager.ThrowOnJsonErrors": false,
   "ConfigurationManager.ThrowOnJsonErrors": false,
 
 
-  "Application.AlternateBackwardKey": "Ctrl+PageUp",
-  "Application.AlternateForwardKey": "Ctrl+PageDown",
+  "Application.NextTabKey": "Tab",
+  "Application.PrevTabKey": "Shift+Tab",
+  "Application.NextTabGroupKey": "F6",
+  "Application.PrevTabGroupKey": "Shift+F6",
   "Application.QuitKey": "Esc",
   "Application.QuitKey": "Esc",
 
 
   "Theme": "Default",
   "Theme": "Default",
@@ -29,8 +31,9 @@
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
         "FrameView.DefaultBorderStyle": "Single",
         "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
-        "Dialog.DefaultBorderStyle": "Single",
-        "MessageBox.DefaultBorderStyle": "Double",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
         "Button.DefaultShadow": "None",
         "Button.DefaultShadow": "None",
         "ColorSchemes": [
         "ColorSchemes": [
           {
           {

+ 1 - 1
Terminal.Gui/View/Navigation/TabBehavior.cs

@@ -16,7 +16,7 @@ public enum TabBehavior
     TabStop = 1,
     TabStop = 1,
 
 
     /// <summary>
     /// <summary>
-    ///     The View will be a stop-point for keyboard-based navigation across groups (e.g. if the user preesses <see cref="Application.NextTabGroupKey"/> (`Ctrl-PageDown`).
+    ///     The View will be a stop-point for keyboard-based navigation across groups (e.g. if the user presses <see cref="Application.NextTabGroupKey"/> (`Ctrl-PageDown`).
     /// </summary>
     /// </summary>
     TabGroup = 2,
     TabGroup = 2,
 }
 }

+ 42 - 0
Terminal.Gui/View/Orientation/IOrientation.cs

@@ -0,0 +1,42 @@
+
+namespace Terminal.Gui;
+using System;
+
+/// <summary>
+///     Implement this interface to provide orientation support.
+/// </summary>
+/// <remarks>
+///     See <see cref="OrientationHelper"/> for a helper class that implements this interface.
+/// </remarks>
+public interface IOrientation
+{
+    /// <summary>
+    ///     Gets or sets the orientation of the View.
+    /// </summary>
+    Orientation Orientation { get; set; }
+
+    /// <summary>
+    ///     Raised when <see cref="Orientation"/> is changing. Can be cancelled.
+    /// </summary>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <summary>
+    ///     Called when <see cref="Orientation"/> is changing.
+    /// </summary>
+    /// <param name="currentOrientation">The current orientation.</param>
+    /// <param name="newOrientation">The new orientation.</param>
+    /// <returns><see langword="true"/> to cancel the change.</returns>
+    public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; }
+
+    /// <summary>
+    ///     Raised when <see cref="Orientation"/> has changed.
+    /// </summary>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+    /// <summary>
+    ///     Called when <see cref="Orientation"/> has been changed.
+    /// </summary>
+    /// <param name="newOrientation"></param>
+    /// <returns></returns>
+    public void OnOrientationChanged (Orientation newOrientation) { return; }
+}

+ 0 - 0
Terminal.Gui/Views/GraphView/Orientation.cs → Terminal.Gui/View/Orientation/Orientation.cs


+ 138 - 0
Terminal.Gui/View/Orientation/OrientationHelper.cs

@@ -0,0 +1,138 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Helper class for implementing <see cref="IOrientation"/>.
+/// </summary>
+/// <remarks>
+///     <para>
+///         Implements the standard pattern for changing/changed events.
+///     </para>
+/// </remarks>
+/// <example>
+///     <code>
+/// private class OrientedView : View, IOrientation
+/// {
+///     private readonly OrientationHelper _orientationHelper;
+/// 
+///     public OrientedView ()
+///     {
+///         _orientationHelper = new (this);
+///         Orientation = Orientation.Vertical;
+///         _orientationHelper.OrientationChanging += (sender, e) =&gt; OrientationChanging?.Invoke (this, e);
+///         _orientationHelper.OrientationChanged += (sender, e) =&gt; OrientationChanged?.Invoke (this, e);
+///     }
+/// 
+///     public Orientation Orientation
+///     {
+///         get =&gt; _orientationHelper.Orientation;
+///         set =&gt; _orientationHelper.Orientation = value;
+///     }
+/// 
+///     public event EventHandler&lt;CancelEventArgs&lt;Orientation&gt;&gt; OrientationChanging;
+///     public event EventHandler&lt;EventArgs&lt;Orientation&gt;&gt; OrientationChanged;
+/// 
+///     public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation)
+///     {
+///        // Custom logic before orientation changes
+///        return false; // Return true to cancel the change
+///     }
+/// 
+///     public void OnOrientationChanged (Orientation newOrientation)
+///     {
+///         // Custom logic after orientation has changed
+///     }
+/// }
+/// </code>
+/// </example>
+public class OrientationHelper
+{
+    private Orientation _orientation;
+    private readonly IOrientation _owner;
+
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="OrientationHelper"/> class.
+    /// </summary>
+    /// <param name="owner">Specifies the object that owns this helper instance and implements <see cref="IOrientation"/>.</param>
+    public OrientationHelper (IOrientation owner) { _owner = owner; }
+
+    /// <summary>
+    ///     Gets or sets the orientation of the View.
+    /// </summary>
+    public Orientation Orientation
+    {
+        get => _orientation;
+        set
+        {
+            if (_orientation == value)
+            {
+                return;
+            }
+
+            // Best practice is to invoke the virtual method first.
+            // This allows derived classes to handle the event and potentially cancel it.
+            if (_owner?.OnOrientationChanging (value, _orientation) ?? false)
+            {
+                return;
+            }
+
+            // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+            CancelEventArgs<Orientation> args = new (in _orientation, ref value);
+            OrientationChanging?.Invoke (_owner, args);
+
+            if (args.Cancel)
+            {
+                return;
+            }
+
+            // If the event is not canceled, update the value.
+            Orientation old = _orientation;
+
+            if (_orientation != value)
+            {
+                _orientation = value;
+
+                if (_owner is { })
+                {
+                    _owner.Orientation = value;
+                }
+            }
+
+            // Best practice is to invoke the virtual method first.
+            _owner?.OnOrientationChanged (_orientation);
+
+            // Even though Changed is not cancelable, it is still a good practice to raise the event after.
+            OrientationChanged?.Invoke (_owner, new (in _orientation));
+        }
+    }
+
+    /// <summary>
+    ///     Raised when the orientation is changing. This is cancelable.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Views that implement <see cref="IOrientation"/> should raise <see cref="IOrientation.OrientationChanging"/>
+    ///         after the orientation has changed
+    ///         (<code>_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);</code>).
+    ///     </para>
+    ///     <para>
+    ///         This event will be raised after the <see cref="IOrientation.OnOrientationChanging"/> method is called (assuming
+    ///         it was not canceled).
+    ///     </para>
+    /// </remarks>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <summary>
+    ///     Raised when the orientation has changed.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Views that implement <see cref="IOrientation"/> should raise <see cref="IOrientation.OrientationChanged"/>
+    ///         after the orientation has changed
+    ///         (<code>_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);</code>).
+    ///     </para>
+    ///     <para>
+    ///         This event will be raised after the <see cref="IOrientation.OnOrientationChanged"/> method is called.
+    ///     </para>
+    /// </remarks>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+}

+ 19 - 4
Terminal.Gui/View/View.Drawing.cs

@@ -501,16 +501,31 @@ public partial class View // Drawing APIs
         // TODO: Implement OnDrawSubviews (cancelable);
         // TODO: Implement OnDrawSubviews (cancelable);
         if (_subviews is { } && SubViewNeedsDisplay)
         if (_subviews is { } && SubViewNeedsDisplay)
         {
         {
-            IEnumerable<View> subviewsNeedingDraw = _subviews.Where (
-                                                                     view => view.Visible
-                                                                             && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
-                                                                    );
+            IEnumerable<View> subviewsNeedingDraw;
+            if (TabStop == TabBehavior.TabGroup && _subviews.Count(v => v.Arrangement.HasFlag (ViewArrangement.Overlapped)) > 0)
+            {
+                // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also View.SetFocus
+                subviewsNeedingDraw = _tabIndexes.Where (
+                                                       view => view.Visible
+                                                               && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
+                                                      ).Reverse ();
+
+            }
+            else
+            {
+                subviewsNeedingDraw = _subviews.Where (
+                                                                         view => view.Visible
+                                                                                 && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
+                                                                        );
+
+            }
             foreach (View view in subviewsNeedingDraw)
             foreach (View view in subviewsNeedingDraw)
             {
             {
                 if (view.LayoutNeeded)
                 if (view.LayoutNeeded)
                 {
                 {
                     view.LayoutSubviews ();
                     view.LayoutSubviews ();
                 }
                 }
+
                 view.Draw ();
                 view.Draw ();
             }
             }
         }
         }

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

@@ -352,6 +352,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
         {
         {
             if (Focused.AdvanceFocus (direction, behavior))
             if (Focused.AdvanceFocus (direction, behavior))
             {
             {
+                // TODO: Temporary hack to make Application.Navigation.FocusChanged work
+                if (Focused.Focused is null)
+                {
+                    Application.Navigation?.SetFocused (Focused);
+                }
                 return true;
                 return true;
             }
             }
         }
         }
@@ -398,6 +403,12 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
 
             view.FocusDeepest (TabBehavior.TabStop, direction);
             view.FocusDeepest (TabBehavior.TabStop, direction);
 
 
+            // TODO: Temporary hack to make Application.Navigation.FocusChanged work
+            if (view.Focused is null)
+            {
+                Application.Navigation?.SetFocused (view);
+            }
+
             return true;
             return true;
         }
         }
 
 
@@ -507,7 +518,21 @@ public partial class View // Focus and cross-view navigation management (TabStop
         {
         {
             // If there is no SuperView, then this is a top-level view
             // If there is no SuperView, then this is a top-level view
             SetFocus (this);
             SetFocus (this);
+
         }
         }
+
+        // TODO: Temporary hack to make Application.Navigation.FocusChanged work
+        if (HasFocus && Focused.Focused is null)
+        {
+            Application.Navigation?.SetFocused (Focused);
+        }
+
+        // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also: View.OnDrawContent.
+        if (viewToEnterFocus is { } && (viewToEnterFocus.TabStop == TabBehavior.TabGroup && viewToEnterFocus.Arrangement.HasFlag (ViewArrangement.Overlapped)))
+        {
+            viewToEnterFocus.TabIndex = 0;
+        }
+
     }
     }
 
 
 
 
@@ -927,10 +952,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///         focus even if this property is set and vice-versa.
     ///         focus even if this property is set and vice-versa.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         The default <see cref="TabBehavior.TabStop"/> keys are <c>Key.Tab</c> and <c>Key>Tab.WithShift</c>.
+    ///         The default <see cref="TabBehavior.TabStop"/> keys are <see cref="Application.NextTabKey"/> (<c>Key.Tab</c>) and <see cref="Application.PrevTabKey"/> (<c>Key>Tab.WithShift</c>).
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         The default <see cref="TabBehavior.TabGroup"/> keys are <c>Key.Tab.WithCtrl</c> and <c>Key>Key.Tab.WithCtrl.WithShift</c>.
+    ///         The default <see cref="TabBehavior.TabGroup"/> keys are <see cref="Application.NextTabGroupKey"/> (<c>Key.F6</c>) and <see cref="Application.PrevTabGroupKey"/> (<c>Key>Key.F6.WithShift</c>).
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     public TabBehavior? TabStop
     public TabBehavior? TabStop

+ 49 - 8
Terminal.Gui/Views/Bar.cs

@@ -11,8 +11,10 @@ namespace Terminal.Gui;
 ///         align them in a specific order.
 ///         align them in a specific order.
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
-public class Bar : View
+public class Bar : View, IOrientation, IDesignable
 {
 {
+    private readonly OrientationHelper _orientationHelper;
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Bar () : this ([]) { }
     public Bar () : this ([]) { }
 
 
@@ -24,6 +26,10 @@ public class Bar : View
         Width = Dim.Auto ();
         Width = Dim.Auto ();
         Height = Dim.Auto ();
         Height = Dim.Auto ();
 
 
+        _orientationHelper = new (this);
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+
         Initialized += Bar_Initialized;
         Initialized += Bar_Initialized;
 
 
         if (shortcuts is null)
         if (shortcuts is null)
@@ -46,7 +52,7 @@ public class Bar : View
         Border.LineStyle = value;
         Border.LineStyle = value;
     }
     }
 
 
-    private Orientation _orientation = Orientation.Horizontal;
+    #region IOrientation members
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
     ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
@@ -58,15 +64,26 @@ public class Bar : View
     ///         Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to right.
     ///         Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to right.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
+
     public Orientation Orientation
     public Orientation Orientation
     {
     {
-        get => _orientation;
-        set
-        {
-            _orientation = value;
-            SetNeedsLayout ();
-        }
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
+
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <inheritdoc/>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation newOrientation)
+    {
+        SetNeedsLayout ();
     }
     }
+    #endregion
 
 
     private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd;
     private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd;
 
 
@@ -224,4 +241,28 @@ public class Bar : View
                 break;
                 break;
         }
         }
     }
     }
+
+    /// <inheritdoc />
+    public bool EnableForDesign ()
+    {
+        var shortcut = new Shortcut
+        {
+            Text = "Quit",
+            Title = "Q_uit",
+            Key = Key.Z.WithCtrl,
+        };
+
+        Add (shortcut);
+
+        shortcut = new Shortcut
+        {
+            Text = "Help Text",
+            Title = "Help",
+            Key = Key.F1,
+        };
+
+        Add (shortcut);
+
+        return true;
+    }
 }
 }

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

@@ -20,7 +20,7 @@ public class Dialog : Window
     /// <remarks>This property can be set in a Theme.</remarks>
     /// <remarks>This property can be set in a Theme.</remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
     [JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
-    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
+    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End; // Default is set in config.json
 
 
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     /// <remarks>This property can be set in a Theme.</remarks>
@@ -48,7 +48,7 @@ public class Dialog : Window
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter<ShadowStyle>))]
     [JsonConverter (typeof (JsonStringEnumConverter<ShadowStyle>))]
-    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
+    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None; // Default is set in config.json
 
 
     /// <summary>
     /// <summary>
     ///     Defines the default border styling for <see cref="Dialog"/>. Can be configured via
     ///     Defines the default border styling for <see cref="Dialog"/>. Can be configured via
@@ -57,7 +57,7 @@ public class Dialog : Window
 
 
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
-    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
 
 
     private readonly List<Button> _buttons = new ();
     private readonly List<Button> _buttons = new ();
 
 

+ 32 - 15
Terminal.Gui/Views/Line.cs

@@ -1,43 +1,60 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Draws a single line using the <see cref="LineStyle"/> specified by <see cref="View.BorderStyle"/>.</summary>
 /// <summary>Draws a single line using the <see cref="LineStyle"/> specified by <see cref="View.BorderStyle"/>.</summary>
-public class Line : View
+public class Line : View, IOrientation
 {
 {
+    private readonly OrientationHelper _orientationHelper;
+
     /// <summary>Constructs a Line object.</summary>
     /// <summary>Constructs a Line object.</summary>
     public Line ()
     public Line ()
     {
     {
         BorderStyle = LineStyle.Single;
         BorderStyle = LineStyle.Single;
         Border.Thickness = new Thickness (0);
         Border.Thickness = new Thickness (0);
         SuperViewRendersLineCanvas = true;
         SuperViewRendersLineCanvas = true;
+
+        _orientationHelper = new (this);
+        _orientationHelper.Orientation = Orientation.Horizontal;
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
     }
     }
 
 
-    private Orientation _orientation;
 
 
+    #region IOrientation members
     /// <summary>
     /// <summary>
     ///     The direction of the line.  If you change this you will need to manually update the Width/Height of the
     ///     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.
     ///     control to cover a relevant area based on the new direction.
     /// </summary>
     /// </summary>
     public Orientation Orientation
     public Orientation Orientation
     {
     {
-        get => _orientation;
-        set
-        {
-            _orientation = value;
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
+
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
 
 
-            switch (Orientation)
-            {
-                case Orientation.Horizontal:
-                    Height = 1;
+    /// <inheritdoc/>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation newOrientation)
+    {
+
+        switch (newOrientation)
+        {
+            case Orientation.Horizontal:
+                Height = 1;
 
 
-                    break;
-                case Orientation.Vertical:
-                    Width = 1;
+                break;
+            case Orientation.Vertical:
+                Width = 1;
 
 
-                    break;
+                break;
 
 
-            }
         }
         }
     }
     }
+    #endregion
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void SetBorderStyle (LineStyle value)
     public override void SetBorderStyle (LineStyle value)

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

@@ -33,7 +33,13 @@ public static class MessageBox
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
-    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
+
+    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
+    /// <remarks>This property can be set in a Theme.</remarks>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
+    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.Center; // Default is set in config.json
 
 
     /// <summary>
     /// <summary>
     ///     Defines the default minimum MessageBox width, as a percentage of the screen width. Can be configured via
     ///     Defines the default minimum MessageBox width, as a percentage of the screen width. Can be configured via
@@ -365,10 +371,10 @@ public static class MessageBox
         var d = new Dialog
         var d = new Dialog
         {
         {
             Title = title,
             Title = title,
-            Buttons = buttonList.ToArray (),
-            ButtonAlignment = Alignment.Center,
+            ButtonAlignment = MessageBox.DefaultButtonAlignment,
             ButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems,
             ButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems,
             BorderStyle = MessageBox.DefaultBorderStyle,
             BorderStyle = MessageBox.DefaultBorderStyle,
+            Buttons = buttonList.ToArray (),
         };
         };
 
 
         d.Width = Dim.Auto (DimAutoStyle.Auto,
         d.Width = Dim.Auto (DimAutoStyle.Auto,

+ 0 - 19
Terminal.Gui/Views/OrientationEventArgs.cs

@@ -1,19 +0,0 @@
-namespace Terminal.Gui;
-
-/// <summary><see cref="EventArgs"/> for <see cref="Orientation"/> events.</summary>
-public class OrientationEventArgs : EventArgs
-{
-    /// <summary>Constructs a new instance.</summary>
-    /// <param name="orientation">the new orientation</param>
-    public OrientationEventArgs (Orientation orientation)
-    {
-        Orientation = orientation;
-        Cancel = false;
-    }
-
-    /// <summary>If set to true, the orientation change operation will be canceled, if applicable.</summary>
-    public bool Cancel { get; set; }
-
-    /// <summary>The new orientation.</summary>
-    public Orientation Orientation { get; set; }
-}

+ 46 - 39
Terminal.Gui/Views/RadioGroup.cs

@@ -1,14 +1,14 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
 /// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
-public class RadioGroup : View, IDesignable
+public class RadioGroup : View, IDesignable, IOrientation
 {
 {
     private int _cursor;
     private int _cursor;
     private List<(int pos, int length)> _horizontal;
     private List<(int pos, int length)> _horizontal;
     private int _horizontalSpace = 2;
     private int _horizontalSpace = 2;
-    private Orientation _orientation = Orientation.Vertical;
     private List<string> _radioLabels = [];
     private List<string> _radioLabels = [];
     private int _selected;
     private int _selected;
+    private readonly OrientationHelper _orientationHelper;
 
 
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of the <see cref="RadioGroup"/> class.
     ///     Initializes a new instance of the <see cref="RadioGroup"/> class.
@@ -54,6 +54,7 @@ public class RadioGroup : View, IDesignable
                         {
                         {
                             return false;
                             return false;
                         }
                         }
+
                         MoveHome ();
                         MoveHome ();
 
 
                         return true;
                         return true;
@@ -68,6 +69,7 @@ public class RadioGroup : View, IDesignable
                         {
                         {
                             return false;
                             return false;
                         }
                         }
+
                         MoveEnd ();
                         MoveEnd ();
 
 
                         return true;
                         return true;
@@ -89,6 +91,7 @@ public class RadioGroup : View, IDesignable
                     ctx =>
                     ctx =>
                     {
                     {
                         SetFocus ();
                         SetFocus ();
+
                         if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count)
                         if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count)
                         {
                         {
                             SelectedItem = (int)ctx.KeyBinding?.Context!;
                             SelectedItem = (int)ctx.KeyBinding?.Context!;
@@ -99,6 +102,11 @@ public class RadioGroup : View, IDesignable
                         return true;
                         return true;
                     });
                     });
 
 
+        _orientationHelper = new (this);
+        _orientationHelper.Orientation = Orientation.Vertical;
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+
         SetupKeyBindings ();
         SetupKeyBindings ();
 
 
         LayoutStarted += RadioGroup_LayoutStarted;
         LayoutStarted += RadioGroup_LayoutStarted;
@@ -138,15 +146,15 @@ public class RadioGroup : View, IDesignable
         int viewportX = e.MouseEvent.Position.X;
         int viewportX = e.MouseEvent.Position.X;
         int viewportY = e.MouseEvent.Position.Y;
         int viewportY = e.MouseEvent.Position.Y;
 
 
-        int pos = _orientation == Orientation.Horizontal ? viewportX : viewportY;
+        int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY;
 
 
-        int rCount = _orientation == Orientation.Horizontal
+        int rCount = Orientation == Orientation.Horizontal
                          ? _horizontal.Last ().pos + _horizontal.Last ().length
                          ? _horizontal.Last ().pos + _horizontal.Last ().length
                          : _radioLabels.Count;
                          : _radioLabels.Count;
 
 
         if (pos < rCount)
         if (pos < rCount)
         {
         {
-            int c = _orientation == Orientation.Horizontal
+            int c = Orientation == Orientation.Horizontal
                         ? _horizontal.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX)
                         ? _horizontal.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX)
                         : viewportY;
                         : viewportY;
 
 
@@ -169,7 +177,7 @@ public class RadioGroup : View, IDesignable
         get => _horizontalSpace;
         get => _horizontalSpace;
         set
         set
         {
         {
-            if (_horizontalSpace != value && _orientation == Orientation.Horizontal)
+            if (_horizontalSpace != value && Orientation == Orientation.Horizontal)
             {
             {
                 _horizontalSpace = value;
                 _horizontalSpace = value;
                 UpdateTextFormatterText ();
                 UpdateTextFormatterText ();
@@ -178,16 +186,6 @@ public class RadioGroup : View, IDesignable
         }
         }
     }
     }
 
 
-    /// <summary>
-    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
-    ///     <see cref="Orientation.Vertical"/>.
-    /// </summary>
-    public Orientation Orientation
-    {
-        get => _orientation;
-        set => OnOrientationChanged (value);
-    }
-
     /// <summary>
     /// <summary>
     ///     The radio labels to display. A key binding will be added for each radio enabling the user to select
     ///     The radio labels to display. A key binding will be added for each radio enabling the user to select
     ///     and/or focus the radio label using the keyboard. See <see cref="View.HotKey"/> for details on how HotKeys work.
     ///     and/or focus the radio label using the keyboard. See <see cref="View.HotKey"/> for details on how HotKeys work.
@@ -319,44 +317,49 @@ public class RadioGroup : View, IDesignable
         }
         }
     }
     }
 
 
-    /// <summary>Called when the view orientation has changed. Invokes the <see cref="OrientationChanged"/> event.</summary>
-    /// <param name="newOrientation"></param>
-    /// <returns>True of the event was cancelled.</returns>
-    public virtual bool OnOrientationChanged (Orientation newOrientation)
+    /// <summary>
+    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
+    ///     <see cref="Orientation.Vertical"/>.
+    /// </summary>
+    public Orientation Orientation
     {
     {
-        var args = new OrientationEventArgs (newOrientation);
-        OrientationChanged?.Invoke (this, args);
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
 
 
-        if (!args.Cancel)
-        {
-            _orientation = newOrientation;
-            SetupKeyBindings ();
-            SetContentSize ();
-        }
+    #region IOrientation
+
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <inheritdoc/>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
 
 
-        return args.Cancel;
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation newOrientation)
+    {
+        SetupKeyBindings ();
+        SetContentSize ();
     }
     }
 
 
+    #endregion IOrientation
+
     // TODO: This should be cancelable
     // TODO: This should be cancelable
     /// <summary>Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.</summary>
     /// <summary>Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.</summary>
     /// <param name="selectedItem"></param>
     /// <param name="selectedItem"></param>
     /// <param name="previousSelectedItem"></param>
     /// <param name="previousSelectedItem"></param>
     public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
     public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
-    { 
+    {
         if (_selected == selectedItem)
         if (_selected == selectedItem)
         {
         {
             return;
             return;
         }
         }
+
         _selected = selectedItem;
         _selected = selectedItem;
         SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
         SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
     }
     }
 
 
-    /// <summary>
-    ///     Fired when the view orientation has changed. Can be cancelled by setting
-    ///     <see cref="OrientationEventArgs.Cancel"/> to true.
-    /// </summary>
-    public event EventHandler<OrientationEventArgs> OrientationChanged;
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override Point? PositionCursor ()
     public override Point? PositionCursor ()
     {
     {
@@ -370,7 +373,10 @@ public class RadioGroup : View, IDesignable
 
 
                 break;
                 break;
             case Orientation.Horizontal:
             case Orientation.Horizontal:
-                x = _horizontal [_cursor].pos;
+                if (_horizontal.Count > 0)
+                {
+                    x = _horizontal [_cursor].pos;
+                }
 
 
                 break;
                 break;
 
 
@@ -424,7 +430,7 @@ public class RadioGroup : View, IDesignable
 
 
     private void SetContentSize ()
     private void SetContentSize ()
     {
     {
-        switch (_orientation)
+        switch (Orientation)
         {
         {
             case Orientation.Vertical:
             case Orientation.Vertical:
                 var width = 0;
                 var width = 0;
@@ -457,10 +463,11 @@ public class RadioGroup : View, IDesignable
         }
         }
     }
     }
 
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public bool EnableForDesign ()
     public bool EnableForDesign ()
     {
     {
         RadioLabels = new [] { "Option _1", "Option _2", "Option _3" };
         RadioLabels = new [] { "Option _1", "Option _2", "Option _3" };
+
         return true;
         return true;
     }
     }
 }
 }

+ 159 - 103
Terminal.Gui/Views/Shortcut.cs

@@ -1,10 +1,8 @@
-using System.ComponentModel;
-using System.Threading.Channels;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <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
+///     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
 ///     displaying a command in <see cref="Bar"/> such as a
 ///     menu, toolbar, or status bar.
 ///     menu, toolbar, or status bar.
 /// </summary>
 /// </summary>
@@ -12,12 +10,13 @@ namespace Terminal.Gui;
 ///     <para>
 ///     <para>
 ///         The following user actions will invoke the <see cref="Command.Accept"/>, causing the
 ///         The following user actions will invoke the <see cref="Command.Accept"/>, causing the
 ///         <see cref="View.Accept"/> event to be fired:
 ///         <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"/>.
+///         - Clicking on the <see cref="Shortcut"/>.
+///         - Pressing the key specified by <see cref="Key"/>.
+///         - Pressing the HotKey specified by <see cref="CommandView"/>.
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
-///         If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked <see cref="Command.Accept"/>
+///         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.
 ///         command regardless of what View has focus, enabling an application-wide keyboard shortcut.
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
@@ -37,7 +36,7 @@ namespace Terminal.Gui;
 ///         If the <see cref="Key"/> is <see cref="Key.Empty"/>, the <see cref="Key"/> text is not displayed.
 ///         If the <see cref="Key"/> is <see cref="Key.Empty"/>, the <see cref="Key"/> text is not displayed.
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
-public class Shortcut : View
+public class Shortcut : View, IOrientation, IDesignable
 {
 {
     /// <summary>
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     ///     Creates a new instance of <see cref="Shortcut"/>.
@@ -60,6 +59,10 @@ public class Shortcut : View
         Width = GetWidthDimAuto ();
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
         Height = Dim.Auto (DimAutoStyle.Content, 1);
 
 
+        _orientationHelper = new (this);
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+
         AddCommand (Command.HotKey, ctx => OnAccept (ctx));
         AddCommand (Command.HotKey, ctx => OnAccept (ctx));
         AddCommand (Command.Accept, ctx => OnAccept (ctx));
         AddCommand (Command.Accept, ctx => OnAccept (ctx));
         AddCommand (Command.Select, ctx => OnSelect (ctx));
         AddCommand (Command.Select, ctx => OnSelect (ctx));
@@ -132,33 +135,30 @@ public class Shortcut : View
         }
         }
     }
     }
 
 
-
     /// <summary>
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     ///     Creates a new instance of <see cref="Shortcut"/>.
     /// </summary>
     /// </summary>
     public Shortcut () : this (Key.Empty, string.Empty, null) { }
     public Shortcut () : this (Key.Empty, string.Empty, null) { }
 
 
-    private Orientation _orientation = Orientation.Horizontal;
+    private readonly OrientationHelper _orientationHelper;
 
 
-    /// <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
+    private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
+
+    // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
+    private int? _minimumDimAutoWidth;
+
+    private Color? _savedForeColor;
+
+    /// <inheritdoc/>
+    public bool EnableForDesign ()
     {
     {
-        get => _orientation;
-        set
-        {
-            _orientation = value;
+        Title = "_Shortcut";
+        HelpText = "Shortcut help";
+        Key = Key.F1;
 
 
-            // TODO: Determine what, if anything, is opinionated about the orientation.
-        }
+        return true;
     }
     }
 
 
-    private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
-
     /// <summary>
     /// <summary>
     ///     Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Shortcut"/>.
     ///     Gets or sets the <see cref="AlignmentModes"/> for this <see cref="Shortcut"/>.
     /// </summary>
     /// </summary>
@@ -180,6 +180,30 @@ public class Shortcut : View
         }
         }
     }
     }
 
 
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        if (disposing)
+        {
+            if (CommandView?.IsAdded == false)
+            {
+                CommandView.Dispose ();
+            }
+
+            if (HelpView?.IsAdded == false)
+            {
+                HelpView.Dispose ();
+            }
+
+            if (KeyView?.IsAdded == false)
+            {
+                KeyView.Dispose ();
+            }
+        }
+
+        base.Dispose (disposing);
+    }
+
     // When one of the subviews is "empty" we don't want to show it. So we
     // 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
     // Use Add/Remove. We need to be careful to add them in the right order
     // so Pos.Align works correctly.
     // so Pos.Align works correctly.
@@ -203,8 +227,15 @@ public class Shortcut : View
         }
         }
     }
     }
 
 
-    // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
-    private int? _minimumDimAutoWidth;
+    private Thickness GetMarginThickness ()
+    {
+        if (Orientation == Orientation.Vertical)
+        {
+            return new (1, 0, 1, 0);
+        }
+
+        return new (1, 0, 1, 0);
+    }
 
 
     // When layout starts, we need to adjust the layout of the HelpView and KeyView
     // When layout starts, we need to adjust the layout of the HelpView and KeyView
     private void OnLayoutStarted (object sender, LayoutEventArgs e)
     private void OnLayoutStarted (object sender, LayoutEventArgs e)
@@ -283,18 +314,16 @@ public class Shortcut : View
         }
         }
     }
     }
 
 
-    private Thickness GetMarginThickness ()
+    private bool? OnSelect (CommandContext ctx)
     {
     {
-        if (Orientation == Orientation.Vertical)
+        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
         {
         {
-            return new (1, 0, 1, 0);
+            return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
         }
         }
 
 
-        return new (1, 0, 1, 0);
+        return false;
     }
     }
 
 
-    private Color? _savedForeColor;
-
     private void Shortcut_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
     private void Shortcut_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
     {
     {
         if (e.CurrentValue.HasFlag (HighlightStyle.Pressed))
         if (e.CurrentValue.HasFlag (HighlightStyle.Pressed))
@@ -344,9 +373,45 @@ public class Shortcut : View
     private void Subview_MouseClick (object sender, MouseEventEventArgs e)
     private void Subview_MouseClick (object sender, MouseEventEventArgs e)
     {
     {
         // TODO: Remove. This does nothing.
         // TODO: Remove. This does nothing.
-        return;
     }
     }
 
 
+    #region IOrientation members
+
+    /// <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 => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
+
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <inheritdoc/>
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation newOrientation)
+    {
+        // TODO: Determine what, if anything, is opinionated about the orientation.
+        SetNeedsLayout ();
+    }
+
+    #endregion
+
     #region Command
     #region Command
 
 
     private View _commandView = new ();
     private View _commandView = new ();
@@ -433,9 +498,7 @@ public class Shortcut : View
             SetHelpViewDefaultLayout ();
             SetHelpViewDefaultLayout ();
             SetKeyViewDefaultLayout ();
             SetKeyViewDefaultLayout ();
             ShowHide ();
             ShowHide ();
-            UpdateKeyBinding ();
-
-            return;
+            UpdateKeyBinding (Key.Empty);
         }
         }
     }
     }
 
 
@@ -475,38 +538,38 @@ public class Shortcut : View
         HelpView.VerticalTextAlignment = Alignment.Center;
         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
+    /// <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
     {
     {
-        if (HelpView is {})
+        get => HelpView?.Text;
+        set
         {
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView is { })
+            {
+                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
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut.
+    /// </summary>
+    public string HelpText
     {
     {
-        if (HelpView is {})
+        get => HelpView?.Text;
+        set
         {
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView is { })
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
         }
         }
     }
     }
-}
 
 
     #endregion Help
     #endregion Help
 
 
@@ -527,9 +590,10 @@ public string HelpText
                 throw new ArgumentNullException ();
                 throw new ArgumentNullException ();
             }
             }
 
 
+            Key oldKey = _key;
             _key = value;
             _key = value;
 
 
-            UpdateKeyBinding ();
+            UpdateKeyBinding (oldKey);
 
 
             KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
             KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
             ShowHide ();
             ShowHide ();
@@ -546,9 +610,24 @@ public string HelpText
         get => _keyBindingScope;
         get => _keyBindingScope;
         set
         set
         {
         {
+            if (value == _keyBindingScope)
+            {
+                return;
+            }
+
+            if (_keyBindingScope == KeyBindingScope.Application)
+            {
+                Application.KeyBindings.Remove (Key);
+            }
+
+            if (_keyBindingScope is KeyBindingScope.HotKey or KeyBindingScope.Focused)
+            {
+                KeyBindings.Remove (Key);
+            }
+
             _keyBindingScope = value;
             _keyBindingScope = value;
 
 
-            UpdateKeyBinding ();
+            UpdateKeyBinding (Key.Empty);
         }
         }
     }
     }
 
 
@@ -561,7 +640,7 @@ public string HelpText
     private int _minimumKeyTextSize;
     private int _minimumKeyTextSize;
 
 
     /// <summary>
     /// <summary>
-    /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+    ///     Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
     /// </summary>
     /// </summary>
     public int MinimumKeyTextSize
     public int MinimumKeyTextSize
     {
     {
@@ -600,9 +679,9 @@ public string HelpText
         KeyView.KeyBindings.Clear ();
         KeyView.KeyBindings.Clear ();
     }
     }
 
 
-    private void UpdateKeyBinding ()
+    private void UpdateKeyBinding (Key oldKey)
     {
     {
-        if (Key != null)
+        if (Key != null && Key.IsValid)
         {
         {
             // Disable the command view key bindings
             // Disable the command view key bindings
             CommandView.KeyBindings.Remove (Key);
             CommandView.KeyBindings.Remove (Key);
@@ -610,11 +689,21 @@ public string HelpText
 
 
             if (KeyBindingScope.FastHasFlags (KeyBindingScope.Application))
             if (KeyBindingScope.FastHasFlags (KeyBindingScope.Application))
             {
             {
+                if (oldKey != Key.Empty)
+                {
+                    Application.KeyBindings.Remove (oldKey);
+                }
+
                 Application.KeyBindings.Remove (Key);
                 Application.KeyBindings.Remove (Key);
                 Application.KeyBindings.Add (Key, this, Command.Accept);
                 Application.KeyBindings.Add (Key, this, Command.Accept);
             }
             }
             else
             else
             {
             {
+                if (oldKey != Key.Empty)
+                {
+                    KeyBindings.Remove (oldKey);
+                }
+
                 KeyBindings.Remove (Key);
                 KeyBindings.Remove (Key);
                 KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
                 KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
             }
             }
@@ -675,6 +764,7 @@ public string HelpText
         if (Action is { })
         if (Action is { })
         {
         {
             Action.Invoke ();
             Action.Invoke ();
+
             // Assume if there's a subscriber to Action, it's handled.
             // Assume if there's a subscriber to Action, it's handled.
             cancel = true;
             cancel = true;
         }
         }
@@ -694,17 +784,6 @@ public string HelpText
 
 
     #endregion Accept Handling
     #endregion Accept Handling
 
 
-    private bool? OnSelect (CommandContext ctx)
-    {
-        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
-        {
-            return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
-        }
-        return false;
-
-    }
-
-
     #region Focus
     #region Focus
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
@@ -753,7 +832,8 @@ public string HelpText
         }
         }
     }
     }
 
 
-    View _lastFocusedView;
+    private View _lastFocusedView;
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override bool OnEnter (View view)
     public override bool OnEnter (View view)
     {
     {
@@ -773,28 +853,4 @@ public string HelpText
     }
     }
 
 
     #endregion Focus
     #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);
-    }
 }
 }

+ 44 - 44
Terminal.Gui/Views/Slider.cs

@@ -21,7 +21,7 @@ public class Slider : Slider<object>
 ///     keyboard or mouse.
 ///     keyboard or mouse.
 /// </summary>
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <typeparam name="T"></typeparam>
-public class Slider<T> : View
+public class Slider<T> : View, IOrientation
 {
 {
     private readonly SliderConfiguration _config = new ();
     private readonly SliderConfiguration _config = new ();
 
 
@@ -31,6 +31,8 @@ public class Slider<T> : View
     // Options
     // Options
     private List<SliderOption<T>> _options;
     private List<SliderOption<T>> _options;
 
 
+    private OrientationHelper _orientationHelper;
+
     #region Initialize
     #region Initialize
 
 
     private void SetInitialProperties (
     private void SetInitialProperties (
@@ -45,11 +47,13 @@ public class Slider<T> : View
 
 
         _options = options ?? new List<SliderOption<T>> ();
         _options = options ?? new List<SliderOption<T>> ();
 
 
-        _config._sliderOrientation = orientation;
+        _orientationHelper = new (this);
+        _orientationHelper.Orientation = _config._sliderOrientation = orientation;
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
 
         SetDefaultStyle ();
         SetDefaultStyle ();
         SetCommands ();
         SetCommands ();
-
         SetContentSize ();
         SetContentSize ();
 
 
         // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit
         // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit
@@ -222,12 +226,45 @@ public class Slider<T> : View
         }
         }
     }
     }
 
 
-    /// <summary>Slider Orientation. <see cref="Gui.Orientation"></see></summary>
+
+    /// <summary>
+    ///     Gets or sets the <see cref="Orientation"/>. The default is <see cref="Orientation.Horizontal"/>.
+    /// </summary>
     public Orientation Orientation
     public Orientation Orientation
     {
     {
-        get => _config._sliderOrientation;
-        set => OnOrientationChanged (value);
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
+
+    #region IOrientation members
+
+    /// <inheritdoc />
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <inheritdoc />
+    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+    /// <inheritdoc />
+    public void OnOrientationChanged (Orientation newOrientation)
+    {
+        _config._sliderOrientation = newOrientation;
+
+        switch (_config._sliderOrientation)
+        {
+            case Orientation.Horizontal:
+                Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─'
+
+                break;
+            case Orientation.Vertical:
+                Style.SpaceChar = new () { Rune = Glyphs.VLine };
+
+                break;
+        }
+
+        SetKeyBindings ();
+        SetContentSize ();
     }
     }
+    #endregion
 
 
     /// <summary>Legends Orientation. <see cref="Gui.Orientation"></see></summary>
     /// <summary>Legends Orientation. <see cref="Gui.Orientation"></see></summary>
     public Orientation LegendsOrientation
     public Orientation LegendsOrientation
@@ -309,43 +346,6 @@ public class Slider<T> : View
 
 
     #region Events
     #region Events
 
 
-    /// <summary>
-    ///     Fired when the slider orientation has changed. Can be cancelled by setting
-    ///     <see cref="OrientationEventArgs.Cancel"/> to true.
-    /// </summary>
-    public event EventHandler<OrientationEventArgs> OrientationChanged;
-
-    /// <summary>Called when the slider orientation has changed. Invokes the <see cref="OrientationChanged"/> event.</summary>
-    /// <param name="newOrientation"></param>
-    /// <returns>True of the event was cancelled.</returns>
-    public virtual bool OnOrientationChanged (Orientation newOrientation)
-    {
-        var args = new OrientationEventArgs (newOrientation);
-        OrientationChanged?.Invoke (this, args);
-
-        if (!args.Cancel)
-        {
-            _config._sliderOrientation = newOrientation;
-
-            switch (_config._sliderOrientation)
-            {
-                case Orientation.Horizontal:
-                    Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─'
-
-                    break;
-                case Orientation.Vertical:
-                    Style.SpaceChar = new () { Rune = Glyphs.VLine };
-
-                    break;
-            }
-
-            SetKeyBindings ();
-            SetContentSize ();
-        }
-
-        return args.Cancel;
-    }
-
     /// <summary>Event raised when the slider option/s changed. The dictionary contains: key = option index, value = T</summary>
     /// <summary>Event raised when the slider option/s changed. The dictionary contains: key = option index, value = T</summary>
     public event EventHandler<SliderEventArgs<T>> OptionsChanged;
     public event EventHandler<SliderEventArgs<T>> OptionsChanged;
 
 
@@ -1742,7 +1742,7 @@ public class Slider<T> : View
 
 
     internal bool Select ()
     internal bool Select ()
     {
     {
-        SetFocusedOption();
+        SetFocusedOption ();
 
 
         return true;
         return true;
     }
     }

+ 71 - 1
Terminal.Gui/Views/StatusBar.cs

@@ -10,7 +10,7 @@ namespace Terminal.Gui;
 ///     to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. So for each context must be a
 ///     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.
 ///     new instance of a status bar.
 /// </summary>
 /// </summary>
-public class StatusBar : Bar
+public class StatusBar : Bar, IDesignable
 {
 {
     /// <inheritdoc/>
     /// <inheritdoc/>
     public StatusBar () : this ([]) { }
     public StatusBar () : this ([]) { }
@@ -73,4 +73,74 @@ public class StatusBar : Bar
 
 
         return view;
         return view;
     }
     }
+
+    /// <inheritdoc />
+    bool IDesignable.EnableForDesign ()
+    {
+        var shortcut = new Shortcut
+        {
+            Text = "Quit",
+            Title = "Q_uit",
+            Key = Key.Z.WithCtrl,
+        };
+
+        Add (shortcut);
+
+        shortcut = new Shortcut
+        {
+            Text = "Help Text",
+            Title = "Help",
+            Key = Key.F1,
+        };
+
+        Add (shortcut);
+
+        shortcut = new Shortcut
+        {
+            Title = "_Show/Hide",
+            Key = Key.F10,
+            CommandView = new CheckBox
+            {
+                CanFocus = false,
+                Text = "_Show/Hide"
+            },
+        };
+
+        Add (shortcut);
+
+        var button1 = new Button
+        {
+            Text = "I'll Hide",
+            // Visible = false
+        };
+        button1.Accept += Button_Clicked;
+        Add (button1);
+
+        shortcut.Accept += (s, e) =>
+                           {
+                               button1.Visible = !button1.Visible;
+                               button1.Enabled = button1.Visible;
+                               e.Handled = false;
+                           };
+
+        Add (new Label
+        {
+            HotKeySpecifier = new Rune ('_'),
+            Text = "Fo_cusLabel",
+            CanFocus = true
+        });
+
+        var button2 = new Button
+        {
+            Text = "Or me!",
+        };
+        button2.Accept += (s, e) => Application.RequestStop ();
+
+        Add (button2);
+
+        return true;
+
+        void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+    }
+
 }
 }

+ 9 - 5
Terminal.Gui/Views/TableView/TableView.cs

@@ -324,11 +324,15 @@ public class TableView : View
         {
         {
             if (cellActivationKey != value)
             if (cellActivationKey != value)
             {
             {
-                KeyBindings.ReplaceKey (cellActivationKey, value);
+                if (KeyBindings.TryGet (cellActivationKey, out _))
+                {
+                    KeyBindings.ReplaceKey (cellActivationKey, value);
+                }
+                else
+                {
+                    KeyBindings.Add (value, Command.Accept);
+                }
 
 
-                // of API user is mixing and matching old and new methods of keybinding then they may have lost
-                // the old binding (e.g. with ClearKeybindings) so KeyBindings.Replace alone will fail
-                KeyBindings.Add (value, Command.Accept);
                 cellActivationKey = value;
                 cellActivationKey = value;
             }
             }
         }
         }
@@ -792,7 +796,7 @@ public class TableView : View
     }
     }
 
 
     ///<inheritdoc/>
     ///<inheritdoc/>
-    protected internal override bool OnMouseEvent  (MouseEvent me)
+    protected internal override bool OnMouseEvent (MouseEvent me)
     {
     {
         if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
         if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
             && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
             && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)

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

@@ -29,6 +29,7 @@ public class Window : Toplevel
     {
     {
         CanFocus = true;
         CanFocus = true;
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
+        Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped;
         ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property
         ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property
         BorderStyle = DefaultBorderStyle;
         BorderStyle = DefaultBorderStyle;
         ShadowStyle = DefaultShadow;
         ShadowStyle = DefaultShadow;

+ 71 - 70
UICatalog/Scenarios/AdornmentsEditor.cs

@@ -9,23 +9,10 @@ namespace UICatalog.Scenarios;
 /// </summary>
 /// </summary>
 public class AdornmentsEditor : View
 public class AdornmentsEditor : View
 {
 {
-    private View _viewToEdit;
-
-    private Label _lblView; // Text describing the vi
-
-    private MarginEditor _marginEditor;
-    private BorderEditor _borderEditor;
-    private PaddingEditor _paddingEditor;
-
-    // TODO: Move Diagnostics to a separate Editor class (DiagnosticsEditor?).
-    private CheckBox _diagPaddingCheckBox;
-    private CheckBox _diagRulerCheckBox;
-    private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
-
     public AdornmentsEditor ()
     public AdornmentsEditor ()
     {
     {
         //ColorScheme = Colors.ColorSchemes ["Dialog"];
         //ColorScheme = Colors.ColorSchemes ["Dialog"];
-        Title = $"AdornmentsEditor";
+        Title = "AdornmentsEditor";
 
 
         Width = Dim.Auto (DimAutoStyle.Content);
         Width = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
@@ -35,38 +22,61 @@ public class AdornmentsEditor : View
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
 
 
         //Application.MouseEvent += Application_MouseEvent;
         //Application.MouseEvent += Application_MouseEvent;
-        ApplicationNavigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
+        Application.Navigation!.FocusedChanged += ApplicationNavigationOnFocusedChanged;
         Initialized += AdornmentsEditor_Initialized;
         Initialized += AdornmentsEditor_Initialized;
     }
     }
 
 
-    private void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
+    private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
+    private View _viewToEdit;
+
+    private Label _lblView; // Text describing the vi
+
+    private MarginEditor _marginEditor;
+    private BorderEditor _borderEditor;
+    private PaddingEditor _paddingEditor;
+
+    // TODO: Move Diagnostics to a separate Editor class (DiagnosticsEditor?).
+    private CheckBox _diagPaddingCheckBox;
+    private CheckBox _diagRulerCheckBox;
+
+    /// <summary>
+    ///     Gets or sets whether the AdornmentsEditor should automatically select the View to edit when the mouse is clicked
+    ///     anywhere outside the editor.
+    /// </summary>
+    public bool AutoSelectViewToEdit { get; set; }
+
+    public View ViewToEdit
     {
     {
-        if (ApplicationNavigation.IsInHierarchy (this, ApplicationNavigation.Focused))
+        get => _viewToEdit;
+        set
         {
         {
-            return;
-        }
+            if (_viewToEdit == value)
+            {
+                return;
+            }
 
 
-        if (ApplicationNavigation.Focused is Adornment adornment)
-        {
-            ViewToEdit = adornment.Parent;
-        }
-        else
-        {
-            ViewToEdit = ApplicationNavigation.Focused;
+            _viewToEdit = value;
+
+            _marginEditor.AdornmentToEdit = _viewToEdit?.Margin ?? null;
+            _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null;
+            _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null;
+
+            _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty;
         }
         }
     }
     }
 
 
-    /// <summary>
-    /// Gets or sets whether the AdornmentsEditor should automatically select the View to edit when the mouse is clicked
-    /// anywhere outside the editor.
-    /// </summary>
-    public bool AutoSelectViewToEdit { get; set; }
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        Diagnostics = _savedDiagnosticFlags;
+        base.Dispose (disposing);
+    }
 
 
     private void AdornmentsEditor_Initialized (object sender, EventArgs e)
     private void AdornmentsEditor_Initialized (object sender, EventArgs e)
     {
     {
         BorderStyle = LineStyle.Dotted;
         BorderStyle = LineStyle.Dotted;
 
 
-        ExpanderButton expandButton = new ExpanderButton ()
+        var expandButton = new ExpanderButton
         {
         {
             Orientation = Orientation.Horizontal
             Orientation = Orientation.Horizontal
         };
         };
@@ -76,7 +86,7 @@ public class AdornmentsEditor : View
         {
         {
             X = 0,
             X = 0,
             Y = 0,
             Y = 0,
-            Height = 2,
+            Height = 2
         };
         };
         _lblView.TextFormatter.WordWrap = true;
         _lblView.TextFormatter.WordWrap = true;
         _lblView.TextFormatter.MultiLine = true;
         _lblView.TextFormatter.MultiLine = true;
@@ -113,34 +123,34 @@ public class AdornmentsEditor : View
         _diagPaddingCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding) ? CheckState.Checked : CheckState.UnChecked;
         _diagPaddingCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding) ? CheckState.Checked : CheckState.UnChecked;
 
 
         _diagPaddingCheckBox.Toggle += (s, e) =>
         _diagPaddingCheckBox.Toggle += (s, e) =>
-                                        {
-                                            if (e.NewValue == CheckState.Checked)
-                                            {
-                                                Diagnostics |= ViewDiagnosticFlags.Padding;
-                                            }
-                                            else
-                                            {
-                                                Diagnostics &= ~ViewDiagnosticFlags.Padding;
-                                            }
-                                        };
+                                       {
+                                           if (e.NewValue == CheckState.Checked)
+                                           {
+                                               Diagnostics |= ViewDiagnosticFlags.Padding;
+                                           }
+                                           else
+                                           {
+                                               Diagnostics &= ~ViewDiagnosticFlags.Padding;
+                                           }
+                                       };
 
 
         Add (_diagPaddingCheckBox);
         Add (_diagPaddingCheckBox);
         _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
         _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
 
 
         _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
         _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
-        _diagRulerCheckBox.State = Diagnostics.FastHasFlags(ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
+        _diagRulerCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
 
 
         _diagRulerCheckBox.Toggle += (s, e) =>
         _diagRulerCheckBox.Toggle += (s, e) =>
-                                      {
-                                          if (e.NewValue == CheckState.Checked)
-                                          {
-                                              Diagnostics |= ViewDiagnosticFlags.Ruler;
-                                          }
-                                          else
-                                          {
-                                              Diagnostics &= ~ViewDiagnosticFlags.Ruler;
-                                          }
-                                      };
+                                     {
+                                         if (e.NewValue == CheckState.Checked)
+                                         {
+                                             Diagnostics |= ViewDiagnosticFlags.Ruler;
+                                         }
+                                         else
+                                         {
+                                             Diagnostics &= ~ViewDiagnosticFlags.Ruler;
+                                         }
+                                     };
 
 
         Add (_diagRulerCheckBox);
         Add (_diagRulerCheckBox);
         _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
         _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
@@ -154,7 +164,8 @@ public class AdornmentsEditor : View
         }
         }
 
 
         // TODO: Add a setting (property) so only subviews of a specified view are considered.
         // TODO: Add a setting (property) so only subviews of a specified view are considered.
-        var view = e.View;
+        View view = e.View;
+
         if (view is { } && e.Flags == MouseFlags.Button1Clicked)
         if (view is { } && e.Flags == MouseFlags.Button1Clicked)
         {
         {
             if (view is Adornment adornment)
             if (view is Adornment adornment)
@@ -168,22 +179,12 @@ public class AdornmentsEditor : View
         }
         }
     }
     }
 
 
-    /// <inheritdoc />
-    protected override void Dispose (bool disposing)
-    {
-        View.Diagnostics = _savedDiagnosticFlags;
-        base.Dispose (disposing);
-    }
-
-    public View ViewToEdit
+    private void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
     {
     {
-        get => _viewToEdit;
-        set
+        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
         {
         {
-            if (_viewToEdit == value)
-            {
-                return;
-            }
+            return;
+        }
 
 
             _viewToEdit = value;
             _viewToEdit = value;
 
 
@@ -197,4 +198,4 @@ public class AdornmentsEditor : View
             return;
             return;
         }
         }
     }
     }
-}
+}

+ 4 - 6
UICatalog/Scenarios/AllViewsTester.cs

@@ -272,9 +272,9 @@ public class AllViewsTester : Scenario
 
 
         _orientation.SelectedItemChanged += (s, selected) =>
         _orientation.SelectedItemChanged += (s, selected) =>
                                             {
                                             {
-                                                if (_curView?.GetType ().GetProperty ("Orientation") is { } prop)
+                                                if (_curView is IOrientation orientatedView)
                                                 {
                                                 {
-                                                    prop.GetSetMethod ()?.Invoke (_curView, new object [] { _orientation.SelectedItem });
+                                                    orientatedView.Orientation = (Orientation)_orientation.SelectedItem;
                                                 }
                                                 }
                                             };
                                             };
         _settingsPane.Add (label, _orientation);
         _settingsPane.Add (label, _orientation);
@@ -358,11 +358,9 @@ public class AllViewsTester : Scenario
             view.Title = "_Test Title";
             view.Title = "_Test Title";
         }
         }
 
 
-        // TODO: Add IOrientation so this doesn't require reflection
-        // If the view supports a Title property, set it so we have something to look at
-        if (view?.GetType ().GetProperty ("Orientation") is { } prop)
+        if (view is IOrientation orientatedView)
         {
         {
-            _orientation.SelectedItem = (int)prop.GetGetMethod ()!.Invoke (view, null)!;
+            _orientation.SelectedItem = (int)orientatedView.Orientation;
             _orientation.Enabled = true;
             _orientation.Enabled = true;
         }
         }
         else
         else

+ 1 - 1
UICatalog/Scenarios/Bars.cs

@@ -402,7 +402,7 @@ public class Bars : Scenario
         bar.Add (shortcut1, shortcut2, line, shortcut3);
         bar.Add (shortcut1, shortcut2, line, shortcut3);
     }
     }
 
 
-    private void ConfigStatusBar (Bar bar)
+    public void ConfigStatusBar (Bar bar)
     {
     {
         var shortcut = new Shortcut
         var shortcut = new Shortcut
         {
         {

+ 3 - 4
UICatalog/Scenarios/ExpanderButton.cs

@@ -74,7 +74,7 @@ public class ExpanderButton : Button
     /// <returns>True of the event was cancelled.</returns>
     /// <returns>True of the event was cancelled.</returns>
     protected virtual bool OnOrientationChanging (Orientation newOrientation)
     protected virtual bool OnOrientationChanging (Orientation newOrientation)
     {
     {
-        var args = new OrientationEventArgs (newOrientation);
+        var args = new CancelEventArgs<Orientation> (in _orientation, ref newOrientation);
         OrientationChanging?.Invoke (this, args);
         OrientationChanging?.Invoke (this, args);
 
 
         if (!args.Cancel)
         if (!args.Cancel)
@@ -103,10 +103,9 @@ public class ExpanderButton : Button
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Fired when the orientation has changed. Can be cancelled by setting
-    ///     <see cref="OrientationEventArgs.Cancel"/> to true.
+    ///     Fired when the orientation has changed. Can be cancelled.
     /// </summary>
     /// </summary>
-    public event EventHandler<OrientationEventArgs> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
 
 
     /// <summary>
     /// <summary>
     ///     The glyph to display when the view is collapsed.
     ///     The glyph to display when the view is collapsed.

+ 1 - 1
UICatalog/Scenarios/Shortcuts.cs

@@ -30,7 +30,7 @@ public class Shortcuts : Scenario
     // QuitKey and it only sticks if changed after init
     // QuitKey and it only sticks if changed after init
     private void App_Loaded (object sender, EventArgs e)
     private void App_Loaded (object sender, EventArgs e)
     {
     {
-        Application.QuitKey = Key.Z.WithCtrl;
+        Application.QuitKey = Key.F4.WithCtrl;
         Application.Top.Title = GetQuitKeyAndName ();
         Application.Top.Title = GetQuitKeyAndName ();
 
 
         ObservableCollection<string> eventSource = new ();
         ObservableCollection<string> eventSource = new ();

+ 1 - 1
UICatalog/UICatalog.csproj

@@ -31,7 +31,7 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />
     <PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />
     <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21,2)" />
     <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21,2)" />
-    <PackageReference Include="SixLabors.ImageSharp" Version="[3.1.4,4)" />
+    <PackageReference Include="SixLabors.ImageSharp" Version="[3.1.5,4)" />
     <PackageReference Include="CsvHelper" Version="[33.0.1,34)" />
     <PackageReference Include="CsvHelper" Version="[33.0.1,34)" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="[3.1.6,4)" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="[3.1.6,4)" />
     <PackageReference Include="System.CommandLine" Version="[2.0.0-beta4.22272.1,3)" />
     <PackageReference Include="System.CommandLine" Version="[2.0.0-beta4.22272.1,3)" />

+ 168 - 163
UnitTests/Application/ApplicationTests.cs

@@ -1,5 +1,4 @@
-using Microsoft.VisualBasic;
-using Xunit.Abstractions;
+using Xunit.Abstractions;
 
 
 // Alias Console to MockConsole so we don't accidentally use Console
 // Alias Console to MockConsole so we don't accidentally use Console
 
 
@@ -7,8 +6,6 @@ namespace Terminal.Gui.ApplicationTests;
 
 
 public class ApplicationTests
 public class ApplicationTests
 {
 {
-    private readonly ITestOutputHelper _output;
-
     public ApplicationTests (ITestOutputHelper output)
     public ApplicationTests (ITestOutputHelper output)
     {
     {
         _output = output;
         _output = output;
@@ -20,6 +17,127 @@ public class ApplicationTests
 #endif
 #endif
     }
     }
 
 
+    private readonly ITestOutputHelper _output;
+
+    private object _timeoutLock;
+
+    [Fact]
+    public void AddTimeout_Fires ()
+    {
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new ();
+
+        uint timeoutTime = 250;
+        var initialized = false;
+        var iteration = 0;
+        var shutdown = false;
+        object timeout = null;
+        var timeoutCount = 0;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (new FakeDriver ());
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
+        Application.Run<Toplevel> ().Dispose ();
+        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
+
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        Assert.Equal (1, timeoutCount);
+        Application.Shutdown ();
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                Application.RemoveTimeout (timeout);
+                timeout = null;
+            }
+        }
+
+        Assert.True (initialized);
+        Assert.True (shutdown);
+
+#if DEBUG_IDISPOSABLE
+        Assert.Empty (Responder.Instances);
+#endif
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
+
+        return;
+
+        void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
+        {
+            if (a.CurrentValue)
+            {
+                Application.Iteration += OnApplicationOnIteration;
+                initialized = true;
+
+                lock (_timeoutLock)
+                {
+                    _output.WriteLine ($"Setting timeout for {timeoutTime}ms");
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (timeoutTime), TimeoutCallback);
+                }
+            }
+            else
+            {
+                Application.Iteration -= OnApplicationOnIteration;
+                shutdown = true;
+            }
+        }
+
+        bool TimeoutCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                _output.WriteLine ($"TimeoutCallback. Count: {++timeoutCount}. Application Iteration: {iteration}");
+
+                if (timeout is { })
+                {
+                    _output.WriteLine ("  Nulling timeout.");
+                    timeout = null;
+                }
+            }
+
+            // False means "don't re-do timer and remove it"
+            return false;
+        }
+
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            lock (_timeoutLock)
+            {
+                if (timeoutCount > 0)
+                {
+                    _output.WriteLine ($"Iteration #{iteration} - Timeout fired. Calling Application.RequestStop.");
+                    Application.RequestStop ();
+
+                    return;
+                }
+            }
+
+            iteration++;
+
+            // Simulate a delay
+            Thread.Sleep ((int)timeoutTime / 10);
+
+            // Worst case scenario - something went wrong
+            if (Application.IsInitialized && iteration > 25)
+            {
+                _output.WriteLine ($"Too many iterations ({iteration}): Calling Application.RequestStop.");
+                Application.RequestStop ();
+            }
+        }
+    }
+
     [Fact]
     [Fact]
     public void Begin_Null_Toplevel_Throws ()
     public void Begin_Null_Toplevel_Throws ()
     {
     {
@@ -184,15 +302,18 @@ public class ApplicationTests
             Assert.Null (Application.Driver);
             Assert.Null (Application.Driver);
             Assert.Null (Application.MainLoop);
             Assert.Null (Application.MainLoop);
             Assert.False (Application.EndAfterFirstIteration);
             Assert.False (Application.EndAfterFirstIteration);
-            Assert.Equal (Key.Empty, Application.AlternateBackwardKey);
-            Assert.Equal (Key.Empty, Application.AlternateForwardKey);
-            Assert.Equal (Key.Empty, Application.QuitKey);
+            Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey);
+            Assert.Equal (Key.Tab, Application.NextTabKey);
+            Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
+            Assert.Equal (Key.F6, Application.NextTabGroupKey);
+            Assert.Equal (Key.Esc, Application.QuitKey);
             Assert.Null (ApplicationOverlapped.OverlappedChildren);
             Assert.Null (ApplicationOverlapped.OverlappedChildren);
             Assert.Null (ApplicationOverlapped.OverlappedTop);
             Assert.Null (ApplicationOverlapped.OverlappedTop);
 
 
             // Internal properties
             // Internal properties
             Assert.False (Application.IsInitialized);
             Assert.False (Application.IsInitialized);
             Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
             Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
+            Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures);
             Assert.False (Application._forceFakeConsole);
             Assert.False (Application._forceFakeConsole);
             Assert.Equal (-1, Application.MainThreadId);
             Assert.Equal (-1, Application.MainThreadId);
             Assert.Empty (Application.TopLevels);
             Assert.Empty (Application.TopLevels);
@@ -201,7 +322,8 @@ public class ApplicationTests
             // Keyboard
             // Keyboard
             Assert.Empty (Application.GetViewKeyBindings ());
             Assert.Empty (Application.GetViewKeyBindings ());
 
 
-            Assert.Null (ApplicationNavigation.Focused);
+            // Navigation
+            Assert.Null (Application.Navigation);
 
 
             // Events - Can't check
             // Events - Can't check
             //Assert.Null (Application.NotifyNewRunState);
             //Assert.Null (Application.NotifyNewRunState);
@@ -232,10 +354,10 @@ public class ApplicationTests
 
 
         //Application.ForceDriver = "driver";
         //Application.ForceDriver = "driver";
         Application.EndAfterFirstIteration = true;
         Application.EndAfterFirstIteration = true;
-        Application.AlternateBackwardKey = Key.A;
-        Application.AlternateForwardKey = Key.B;
+        Application.PrevTabGroupKey = Key.A;
+        Application.NextTabGroupKey = Key.B;
         Application.QuitKey = Key.C;
         Application.QuitKey = Key.C;
-        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Cancel);
+        Application.KeyBindings.Add (Key.D, KeyBindingScope.Application, Command.Cancel);
 
 
         //ApplicationOverlapped.OverlappedChildren = new List<View> ();
         //ApplicationOverlapped.OverlappedChildren = new List<View> ();
         //ApplicationOverlapped.OverlappedTop = 
         //ApplicationOverlapped.OverlappedTop = 
@@ -243,6 +365,8 @@ public class ApplicationTests
 
 
         //Application.WantContinuousButtonPressedView = new View ();
         //Application.WantContinuousButtonPressedView = new View ();
 
 
+        Application.Navigation = new ();
+
         Application.ResetState ();
         Application.ResetState ();
         CheckReset ();
         CheckReset ();
 
 
@@ -280,8 +404,8 @@ public class ApplicationTests
     [InlineData (typeof (CursesDriver))]
     [InlineData (typeof (CursesDriver))]
     public void Init_Shutdown_Fire_InitializedChanged (Type driverType)
     public void Init_Shutdown_Fire_InitializedChanged (Type driverType)
     {
     {
-        bool initialized = false;
-        bool shutdown = false;
+        var initialized = false;
+        var shutdown = false;
 
 
         Application.InitializedChanged += OnApplicationOnInitializedChanged;
         Application.InitializedChanged += OnApplicationOnInitializedChanged;
 
 
@@ -310,34 +434,6 @@ public class ApplicationTests
         }
         }
     }
     }
 
 
-
-    [Fact]
-    public void Run_Iteration_Fires ()
-    {
-        int iteration = 0;
-
-        Application.Init (new FakeDriver ());
-
-        Application.Iteration += Application_Iteration;
-        Application.Run<Toplevel> ().Dispose ();
-
-        Assert.Equal (1, iteration);
-        Application.Shutdown ();
-
-        return;
-
-        void Application_Iteration (object sender, IterationEventArgs e)
-        {
-            if (iteration > 0)
-            {
-                Assert.Fail ();
-            }
-            iteration++;
-            Application.RequestStop ();
-        }
-    }
-
-
     [Fact]
     [Fact]
     public void Init_Unbalanced_Throws ()
     public void Init_Unbalanced_Throws ()
     {
     {
@@ -444,6 +540,33 @@ public class ApplicationTests
         Application.Shutdown ();
         Application.Shutdown ();
     }
     }
 
 
+    [Fact]
+    public void Run_Iteration_Fires ()
+    {
+        var iteration = 0;
+
+        Application.Init (new FakeDriver ());
+
+        Application.Iteration += Application_Iteration;
+        Application.Run<Toplevel> ().Dispose ();
+
+        Assert.Equal (1, iteration);
+        Application.Shutdown ();
+
+        return;
+
+        void Application_Iteration (object sender, IterationEventArgs e)
+        {
+            if (iteration > 0)
+            {
+                Assert.Fail ();
+            }
+
+            iteration++;
+            Application.RequestStop ();
+        }
+    }
+
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
     public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
@@ -894,15 +1017,15 @@ public class ApplicationTests
         RunState rs = Application.Begin (w);
         RunState rs = Application.Begin (w);
 
 
         // Don't use visuals to test as style of border can change over time.
         // Don't use visuals to test as style of border can change over time.
-        Assert.Equal (new Point (0, 0), w.Frame.Location);
+        Assert.Equal (new (0, 0), w.Frame.Location);
 
 
         Application.OnMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
         Application.OnMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
         Assert.Equal (w.Border, Application.MouseGrabView);
         Assert.Equal (w.Border, Application.MouseGrabView);
-        Assert.Equal (new Point (0, 0), w.Frame.Location);
+        Assert.Equal (new (0, 0), w.Frame.Location);
 
 
         // Move down and to the right.
         // Move down and to the right.
         Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
         Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
-        Assert.Equal (new Point (1, 1), w.Frame.Location);
+        Assert.Equal (new (1, 1), w.Frame.Location);
 
 
         Application.End (rs);
         Application.End (rs);
         w.Dispose ();
         w.Dispose ();
@@ -1037,6 +1160,7 @@ public class ApplicationTests
         Assert.Throws<InvalidOperationException> (() => Application.Run (new Toplevel ()));
         Assert.Throws<InvalidOperationException> (() => Application.Run (new Toplevel ()));
 
 
         Application.Init (driver);
         Application.Init (driver);
+
         Application.Iteration += (s, e) =>
         Application.Iteration += (s, e) =>
                                  {
                                  {
                                      Assert.NotNull (Application.Top);
                                      Assert.NotNull (Application.Top);
@@ -1096,123 +1220,4 @@ public class ApplicationTests
     }
     }
 
 
     #endregion
     #endregion
-
-
-    private object _timeoutLock;
-
-    [Fact]
-    public void AddTimeout_Fires ()
-    {
-        Assert.Null (_timeoutLock);
-        _timeoutLock = new object ();
-
-        uint timeoutTime = 250;
-        bool initialized = false;
-        int iteration = 0;
-        bool shutdown = false;
-        object timeout = null;
-        int timeoutCount = 0;
-
-        Application.InitializedChanged += OnApplicationOnInitializedChanged;
-
-        Application.Init (new FakeDriver ());
-        Assert.True (initialized);
-        Assert.False (shutdown);
-
-        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
-        Application.Run<Toplevel> ().Dispose ();
-        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
-
-        Assert.True (initialized);
-        Assert.False (shutdown);
-
-        Assert.Equal (1, timeoutCount);
-        Application.Shutdown ();
-
-        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
-
-        lock (_timeoutLock)
-        {
-            if (timeout is { })
-            {
-                Application.RemoveTimeout (timeout);
-                timeout = null;
-            }
-        }
-
-        Assert.True (initialized);
-        Assert.True (shutdown);
-
-#if DEBUG_IDISPOSABLE
-        Assert.Empty (Responder.Instances);
-#endif
-        lock (_timeoutLock)
-        {
-            _timeoutLock = null;
-        }
-
-        return;
-
-        void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
-        {
-            if (a.CurrentValue)
-            {
-                Application.Iteration += OnApplicationOnIteration;
-                initialized = true;
-
-                lock (_timeoutLock)
-                {
-                    _output.WriteLine ($"Setting timeout for {timeoutTime}ms");
-                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (timeoutTime), TimeoutCallback);
-                }
-
-            }
-            else
-            {
-                Application.Iteration -= OnApplicationOnIteration;
-                shutdown = true;
-            }
-        }
-
-        bool TimeoutCallback ()
-        {
-            lock (_timeoutLock)
-            {
-                _output.WriteLine ($"TimeoutCallback. Count: {++timeoutCount}. Application Iteration: {iteration}");
-                if (timeout is { })
-                {
-                    _output.WriteLine ($"  Nulling timeout.");
-                    timeout = null;
-                }
-            }
-
-            // False means "don't re-do timer and remove it"
-            return false;
-        }
-
-        void OnApplicationOnIteration (object s, IterationEventArgs a)
-        {
-            lock (_timeoutLock)
-            {
-                if (timeoutCount > 0)
-                {
-                    _output.WriteLine ($"Iteration #{iteration} - Timeout fired. Calling Application.RequestStop.");
-                    Application.RequestStop ();
-
-                    return;
-                }
-            }
-            iteration++;
-
-            // Simulate a delay
-            Thread.Sleep ((int)timeoutTime / 10);
-
-            // Worst case scenario - something went wrong
-            if (Application.IsInitialized && iteration > 25)
-            {
-                _output.WriteLine ($"Too many iterations ({iteration}): Calling Application.RequestStop.");
-                Application.RequestStop ();
-            }
-        }
-    }
 }
 }

+ 26 - 44
UnitTests/Application/KeyboardTests.cs

@@ -65,7 +65,7 @@ public class KeyboardTests
     {
     {
         Application.ResetState (true);
         Application.ResetState (true);
         // Before Init
         // Before Init
-        Assert.Equal (Key.Empty, Application.QuitKey);
+        Assert.Equal (Key.Esc, Application.QuitKey);
 
 
         Application.Init (new FakeDriver ());
         Application.Init (new FakeDriver ());
         // After Init
         // After Init
@@ -178,7 +178,7 @@ public class KeyboardTests
     }
     }
 
 
     [Fact (Skip = "Replace when new key statics are added.")]
     [Fact (Skip = "Replace when new key statics are added.")]
-    public void AlternateForwardKey_AlternateBackwardKey_Tests ()
+    public void NextTabGroupKey_PrevTabGroupKey_Tests ()
     {
     {
         Application.Init (new FakeDriver ());
         Application.Init (new FakeDriver ());
 
 
@@ -200,45 +200,27 @@ public class KeyboardTests
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
                                      // Using default keys.
                                      // Using default keys.
-                                     Application.OnKeyDown (Key.Tab.WithCtrl);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithCtrl);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithCtrl);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithCtrl);
-                                     Assert.True (v1.HasFocus);
-
-                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
-                                     Assert.True (v1.HasFocus);
-
-                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
-
-                                     // Using another's alternate keys.
-                                     Application.AlternateForwardKey = Key.F7;
-                                     Application.AlternateBackwardKey = Key.F6;
+                                     
+                                     // Using alternate keys.
+                                     Application.NextTabGroupKey = Key.F7;
+                                     Application.PrevTabGroupKey = Key.F8;
 
 
                                      Application.OnKeyDown (Key.F7);
                                      Application.OnKeyDown (Key.F7);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
@@ -249,13 +231,13 @@ public class KeyboardTests
                                      Application.OnKeyDown (Key.F7);
                                      Application.OnKeyDown (Key.F7);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     Application.OnKeyDown (Key.F6);
+                                     Application.OnKeyDown (Key.F8);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
+                                     Application.OnKeyDown (Key.F8);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
+                                     Application.OnKeyDown (Key.F8);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
+                                     Application.OnKeyDown (Key.F8);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
                                      Application.RequestStop ();
                                      Application.RequestStop ();
@@ -264,12 +246,12 @@ public class KeyboardTests
         Application.Run (top);
         Application.Run (top);
 
 
         // Replacing the defaults keys to avoid errors on others unit tests that are using it.
         // Replacing the defaults keys to avoid errors on others unit tests that are using it.
-        Application.AlternateForwardKey = Key.PageDown.WithCtrl;
-        Application.AlternateBackwardKey = Key.PageUp.WithCtrl;
+        Application.NextTabGroupKey = Key.PageDown.WithCtrl;
+        Application.PrevTabGroupKey = Key.PageUp.WithCtrl;
         Application.QuitKey = Key.Q.WithCtrl;
         Application.QuitKey = Key.Q.WithCtrl;
 
 
-        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
-        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
+        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.NextTabGroupKey.KeyCode);
+        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.PrevTabGroupKey.KeyCode);
         Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
 
 
         top.Dispose ();
         top.Dispose ();
@@ -321,14 +303,14 @@ public class KeyboardTests
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        Application.OnKeyDown (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.F6);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        Application.OnKeyDown (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.F6);
         Assert.False (win.CanFocus);
         Assert.False (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
@@ -374,14 +356,14 @@ public class KeyboardTests
         Assert.False (win2.HasFocus);
         Assert.False (win2.HasFocus);
         Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
 
 
-        Application.OnKeyDown (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.F6);
         Assert.True (win.CanFocus);
         Assert.True (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        Application.OnKeyDown (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.F6);
         Assert.True (win.CanFocus);
         Assert.True (win.CanFocus);
         Assert.True (win.HasFocus);
         Assert.True (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);

+ 20 - 20
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -33,14 +33,14 @@ public class ConfigurationManagerTests
 
 
             // assert
             // assert
             Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
             Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
-            Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
-            Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
+            Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode);
+            Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode);
         }
         }
 
 
         // act
         // act
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
         Apply ();
         Apply ();
 
 
@@ -152,8 +152,8 @@ public class ConfigurationManagerTests
         Reset ();
         Reset ();
 
 
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
         Updated += ConfigurationManager_Updated;
         Updated += ConfigurationManager_Updated;
         var fired = false;
         var fired = false;
@@ -166,13 +166,13 @@ public class ConfigurationManagerTests
             Assert.Equal (Key.Esc, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
             Assert.Equal (Key.Esc, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
 
 
             Assert.Equal (
             Assert.Equal (
-                          KeyCode.PageDown | KeyCode.CtrlMask,
-                          ((Key)Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode
+                          KeyCode.F6,
+                          ((Key)Settings ["Application.NextTabGroupKey"].PropertyValue).KeyCode
                          );
                          );
 
 
             Assert.Equal (
             Assert.Equal (
-                          KeyCode.PageUp | KeyCode.CtrlMask,
-                          ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode
+                          KeyCode.F6 | KeyCode.ShiftMask,
+                          ((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode
                          );
                          );
         }
         }
 
 
@@ -229,14 +229,14 @@ public class ConfigurationManagerTests
         // arrange
         // arrange
         Reset ();
         Reset ();
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings.Apply ();
         Settings.Apply ();
 
 
         // assert apply worked
         // assert apply worked
         Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
-        Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
-        Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
+        Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode);
+        Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode);
 
 
         //act
         //act
         Reset ();
         Reset ();
@@ -245,13 +245,13 @@ public class ConfigurationManagerTests
         Assert.NotEmpty (Themes);
         Assert.NotEmpty (Themes);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal (Key.Esc, Application.QuitKey);
         Assert.Equal (Key.Esc, Application.QuitKey);
-        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
-        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
+        Assert.Equal (Key.F6, Application.NextTabGroupKey);
+        Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
 
 
         // arrange
         // arrange
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings.Apply ();
         Settings.Apply ();
 
 
         Locations = ConfigLocations.DefaultOnly;
         Locations = ConfigLocations.DefaultOnly;
@@ -264,8 +264,8 @@ public class ConfigurationManagerTests
         Assert.NotEmpty (Themes);
         Assert.NotEmpty (Themes);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
-        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
-        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
+        Assert.Equal (Key.F6, Application.NextTabGroupKey);
+        Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
         Reset ();
         Reset ();
     }
     }
 
 

+ 15 - 16
UnitTests/Configuration/SettingsScopeTests.cs

@@ -12,26 +12,26 @@ public class SettingsScopeTests
         Assert.Equal (Key.Esc, (Key)Settings ["Application.QuitKey"].PropertyValue);
         Assert.Equal (Key.Esc, (Key)Settings ["Application.QuitKey"].PropertyValue);
 
 
         Assert.Equal (
         Assert.Equal (
-                      KeyCode.PageDown | KeyCode.CtrlMask,
-                      ((Key)Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode
+                      Key.F6,
+                      (Key)Settings ["Application.NextTabGroupKey"].PropertyValue
                      );
                      );
 
 
         Assert.Equal (
         Assert.Equal (
-                      KeyCode.PageUp | KeyCode.CtrlMask,
-                      ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode
+                      Key.F6.WithShift,
+                      (Key)Settings["Application.PrevTabGroupKey"].PropertyValue
                      );
                      );
 
 
         // act
         // act
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
         Settings.Apply ();
         Settings.Apply ();
 
 
         // assert
         // assert
-        Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
-        Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
-        Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
+        Assert.Equal (Key.Q, Application.QuitKey);
+        Assert.Equal (Key.F, Application.NextTabGroupKey);
+        Assert.Equal (Key.B, Application.PrevTabGroupKey);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -39,18 +39,17 @@ public class SettingsScopeTests
     public void CopyUpdatedPropertiesFrom_ShouldCopyChangedPropertiesOnly ()
     public void CopyUpdatedPropertiesFrom_ShouldCopyChangedPropertiesOnly ()
     {
     {
         Settings ["Application.QuitKey"].PropertyValue = Key.End;
         Settings ["Application.QuitKey"].PropertyValue = Key.End;
-        ;
 
 
         var updatedSettings = new SettingsScope ();
         var updatedSettings = new SettingsScope ();
 
 
         ///Don't set Quitkey
         ///Don't set Quitkey
-        updatedSettings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
-        updatedSettings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
+        updatedSettings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
+        updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
         Settings.Update (updatedSettings);
         Settings.Update (updatedSettings);
         Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
         Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
-        Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.AlternateForwardKey"].PropertyValue).KeyCode);
-        Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode);
+        Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.NextTabGroupKey"].PropertyValue).KeyCode);
+        Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -65,8 +64,8 @@ public class SettingsScopeTests
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal ("Default", Themes.Theme);
 
 
         Assert.True (Settings ["Application.QuitKey"].PropertyValue is Key);
         Assert.True (Settings ["Application.QuitKey"].PropertyValue is Key);
-        Assert.True (Settings ["Application.AlternateForwardKey"].PropertyValue is Key);
-        Assert.True (Settings ["Application.AlternateBackwardKey"].PropertyValue is Key);
+        Assert.True (Settings ["Application.NextTabGroupKey"].PropertyValue is Key);
+        Assert.True (Settings ["Application.PrevTabGroupKey"].PropertyValue is Key);
 
 
         Assert.True (Settings ["Theme"].PropertyValue is string);
         Assert.True (Settings ["Theme"].PropertyValue is string);
         Assert.Equal ("Default", Settings ["Theme"].PropertyValue as string);
         Assert.Equal ("Default", Settings ["Theme"].PropertyValue as string);

+ 30 - 1
UnitTests/Dialogs/DialogTests.cs

@@ -26,6 +26,10 @@ public class DialogTests
         int width = $@"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}".Length;
         int width = $@"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}".Length;
         d.SetBufferSize (width, 1);
         d.SetBufferSize (width, 1);
 
 
+        // Override CM
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         // Default (center)
         // Default (center)
         var dlg = new Dialog
         var dlg = new Dialog
         {
         {
@@ -151,6 +155,7 @@ public class DialogTests
         int width = buttonRow.Length;
         int width = buttonRow.Length;
         d.SetBufferSize (buttonRow.Length, 3);
         d.SetBufferSize (buttonRow.Length, 3);
 
 
+
         // Default - Center
         // Default - Center
         (runstate, Dialog dlg) = RunButtonTestDialog (
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     title,
@@ -874,6 +879,11 @@ public class DialogTests
     {
     {
         ((FakeDriver)Driver).SetBufferSize (20, 5);
         ((FakeDriver)Driver).SetBufferSize (20, 5);
 
 
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         var win = new Window ();
         var win = new Window ();
 
 
         var iterations = 0;
         var iterations = 0;
@@ -889,7 +899,6 @@ public class DialogTests
 
 
         win.Loaded += (s, a) =>
         win.Loaded += (s, a) =>
                       {
                       {
-                          Dialog.DefaultButtonAlignment = Alignment.Center;
                           var dlg = new Dialog { Width = 18, Height = 3, Buttons = [new () { Text = "Ok" }] };
                           var dlg = new Dialog { Width = 18, Height = 3, Buttons = [new () { Text = "Ok" }] };
 
 
                           dlg.Loaded += (s, a) =>
                           dlg.Loaded += (s, a) =>
@@ -975,7 +984,10 @@ public class DialogTests
         var win = new Window ();
         var win = new Window ();
 
 
         int iterations = -1;
         int iterations = -1;
+
+        // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
 
 
         Iteration += (s, a) =>
         Iteration += (s, a) =>
                      {
                      {
@@ -1012,7 +1024,10 @@ public class DialogTests
     public void Dialog_Opened_From_Another_Dialog ()
     public void Dialog_Opened_From_Another_Dialog ()
     {
     {
         ((FakeDriver)Driver).SetBufferSize (30, 10);
         ((FakeDriver)Driver).SetBufferSize (30, 10);
+
+        // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
 
 
         var btn1 = new Button { Text = "press me 1" };
         var btn1 = new Button { Text = "press me 1" };
         Button btn2 = null;
         Button btn2 = null;
@@ -1159,6 +1174,11 @@ public class DialogTests
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Location_When_Application_Top_Not_Default ()
     public void Location_When_Application_Top_Not_Default ()
     {
     {
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         var expected = 5;
         var expected = 5;
         var d = new Dialog { X = expected, Y = expected, Height = 5, Width = 5 };
         var d = new Dialog { X = expected, Y = expected, Height = 5, Width = 5 };
         Begin (d);
         Begin (d);
@@ -1188,6 +1208,11 @@ public class DialogTests
 
 
         int iterations = -1;
         int iterations = -1;
 
 
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         Iteration += (s, a) =>
         Iteration += (s, a) =>
                      {
                      {
                          iterations++;
                          iterations++;
@@ -1361,6 +1386,10 @@ public class DialogTests
         params Button [] btns
         params Button [] btns
     )
     )
     {
     {
+        // Override CM
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         var dlg = new Dialog
         var dlg = new Dialog
         {
         {
             Title = title,
             Title = title,

+ 12 - 0
UnitTests/Dialogs/MessageBoxTests.cs

@@ -174,6 +174,10 @@ public class MessageBoxTests
         var btn =
         var btn =
             $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
             $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
 
+        // Override CM
+        MessageBox.DefaultButtonAlignment = Alignment.End;
+        MessageBox.DefaultBorderStyle = LineStyle.Double;
+
         Application.Iteration += (s, a) =>
         Application.Iteration += (s, a) =>
                                  {
                                  {
                                      iterations++;
                                      iterations++;
@@ -239,6 +243,10 @@ public class MessageBoxTests
         var btn =
         var btn =
             $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
             $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
 
+        // Override CM
+        MessageBox.DefaultButtonAlignment = Alignment.End;
+        MessageBox.DefaultBorderStyle = LineStyle.Double;
+
         Application.Iteration += (s, a) =>
         Application.Iteration += (s, a) =>
                                  {
                                  {
                                      iterations++;
                                      iterations++;
@@ -415,6 +423,10 @@ public class MessageBoxTests
         int iterations = -1;
         int iterations = -1;
         ((FakeDriver)Application.Driver).SetBufferSize (70, 15);
         ((FakeDriver)Application.Driver).SetBufferSize (70, 15);
 
 
+        // Override CM
+        MessageBox.DefaultButtonAlignment = Alignment.End;
+        MessageBox.DefaultBorderStyle = LineStyle.Double;
+
         Application.Iteration += (s, a) =>
         Application.Iteration += (s, a) =>
                                  {
                                  {
                                      iterations++;
                                      iterations++;

+ 14 - 0
UnitTests/FileServices/FileDialogTests.cs

@@ -572,6 +572,11 @@ public class FileDialogTests (ITestOutputHelper output)
 
 
     private FileDialog GetInitializedFileDialog ()
     private FileDialog GetInitializedFileDialog ()
     {
     {
+
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         var dlg = new FileDialog ();
         var dlg = new FileDialog ();
         Begin (dlg);
         Begin (dlg);
 
 
@@ -580,6 +585,10 @@ public class FileDialogTests (ITestOutputHelper output)
 
 
     private FileDialog GetLinuxDialog ()
     private FileDialog GetLinuxDialog ()
     {
     {
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         // Arrange
         // Arrange
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), "/");
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), "/");
         fileSystem.MockTime (() => new (2010, 01, 01, 11, 12, 43));
         fileSystem.MockTime (() => new (2010, 01, 01, 11, 12, 43));
@@ -623,6 +632,11 @@ public class FileDialogTests (ITestOutputHelper output)
 
 
     private FileDialog GetWindowsDialog ()
     private FileDialog GetWindowsDialog ()
     {
     {
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         // Arrange
         // Arrange
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), @"c:\");
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), @"c:\");
         fileSystem.MockTime (() => new (2010, 01, 01, 11, 12, 43));
         fileSystem.MockTime (() => new (2010, 01, 01, 11, 12, 43));

+ 80 - 45
UnitTests/Input/KeyBindingTests.cs

@@ -4,15 +4,15 @@ namespace Terminal.Gui.InputTests;
 
 
 public class KeyBindingTests
 public class KeyBindingTests
 {
 {
-    private readonly ITestOutputHelper _output;
     public KeyBindingTests (ITestOutputHelper output) { _output = output; }
     public KeyBindingTests (ITestOutputHelper output) { _output = output; }
+    private readonly ITestOutputHelper _output;
 
 
     [Fact]
     [Fact]
-    public void Add_Empty_Throws ()
+    public void Add_Invalid_Key_Throws ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         List<Command> commands = new ();
         List<Command> commands = new ();
-        Assert.Throws<ArgumentException> (() => keyBindings.Add (Key.A, commands.ToArray ()));
+        Assert.Throws<ArgumentException> (() => keyBindings.Add (Key.Empty, KeyBindingScope.HotKey, Command.Accept));
     }
     }
 
 
     [Fact]
     [Fact]
@@ -32,6 +32,14 @@ public class KeyBindingTests
         Assert.Contains (Command.Left, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
     }
     }
 
 
+    [Fact]
+    public void Add_No_Commands_Throws ()
+    {
+        var keyBindings = new KeyBindings ();
+        List<Command> commands = new ();
+        Assert.Throws<ArgumentException> (() => keyBindings.Add (Key.A, commands.ToArray ()));
+    }
+
     [Fact]
     [Fact]
     public void Add_Single_Adds ()
     public void Add_Single_Adds ()
     {
     {
@@ -45,6 +53,39 @@ public class KeyBindingTests
         Assert.Contains (Command.HotKey, resultCommands);
         Assert.Contains (Command.HotKey, resultCommands);
     }
     }
 
 
+    // Add should not allow duplicates
+    [Fact]
+    public void Add_Throws_If_Exists ()
+    {
+        var keyBindings = new KeyBindings ();
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept));
+
+        Command [] resultCommands = keyBindings.GetCommands (Key.A);
+        Assert.Contains (Command.HotKey, resultCommands);
+
+        keyBindings = new ();
+        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
+
+        resultCommands = keyBindings.GetCommands (Key.A);
+        Assert.Contains (Command.HotKey, resultCommands);
+
+        keyBindings = new ();
+        keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
+
+        resultCommands = keyBindings.GetCommands (Key.A);
+        Assert.Contains (Command.HotKey, resultCommands);
+
+        keyBindings = new ();
+        keyBindings.Add (Key.A, new KeyBinding (new [] { Command.HotKey }, KeyBindingScope.HotKey));
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, new KeyBinding (new [] { Command.Accept }, KeyBindingScope.HotKey)));
+
+        resultCommands = keyBindings.GetCommands (Key.A);
+        Assert.Contains (Command.HotKey, resultCommands);
+    }
+
     // Clear
     // Clear
     [Fact]
     [Fact]
     public void Clear_Clears ()
     public void Clear_Clears ()
@@ -65,6 +106,14 @@ public class KeyBindingTests
         Assert.Throws<InvalidOperationException> (() => keyBindings.GetKeyFromCommands (Command.Accept));
         Assert.Throws<InvalidOperationException> (() => keyBindings.GetKeyFromCommands (Command.Accept));
     }
     }
 
 
+    [Fact]
+    public void Get_Binding_Not_Found_Throws ()
+    {
+        var keyBindings = new KeyBindings ();
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Get (Key.A));
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Get (Key.B, KeyBindingScope.Application));
+    }
+
     // GetCommands
     // GetCommands
     [Fact]
     [Fact]
     public void GetCommands_Unknown_ReturnsEmpty ()
     public void GetCommands_Unknown_ReturnsEmpty ()
@@ -159,41 +208,8 @@ public class KeyBindingTests
         Assert.Equal (Key.A, resultKey);
         Assert.Equal (Key.A, resultKey);
     }
     }
 
 
-    // Add should not allow duplicates
     [Fact]
     [Fact]
-    public void Add_Throws_If_Exists ()
-    {
-        var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept));
-
-        Command [] resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.Contains (Command.HotKey, resultCommands);
-
-        keyBindings = new ();
-        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
-
-        resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.Contains (Command.HotKey, resultCommands);
-
-        keyBindings = new ();
-        keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
-
-        resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.Contains (Command.HotKey, resultCommands);
-
-        keyBindings = new ();
-        keyBindings.Add (Key.A, new KeyBinding (new [] { Command.HotKey }, KeyBindingScope.HotKey));
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, new KeyBinding (new [] { Command.Accept }, KeyBindingScope.HotKey)));
-
-        resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.Contains (Command.HotKey, resultCommands);
-    }
-
-    [Fact]
-    public void Replace_Key ()
+    public void ReplaceKey_Replaces ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
         keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
@@ -218,6 +234,33 @@ public class KeyBindingTests
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.H));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.H));
     }
     }
 
 
+    [Fact]
+    public void ReplaceKey_Replaces_Leaves_Old_Binding ()
+    {
+        var keyBindings = new KeyBindings ();
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
+
+        keyBindings.ReplaceKey (keyBindings.GetKeyFromCommands (Command.Accept), Key.C);
+        Assert.Empty (keyBindings.GetCommands (Key.A));
+        Assert.Contains (Command.Accept, keyBindings.GetCommands (Key.C));
+    }
+
+    [Fact]
+    public void ReplaceKey_Throws_If_DoesNotContain_Old ()
+    {
+        var keyBindings = new KeyBindings ();
+        Assert.Throws<InvalidOperationException> (() => keyBindings.ReplaceKey (Key.A, Key.B));
+    }
+
+    [Fact]
+    public void ReplaceKey_Throws_If_New_Is_Empty ()
+    {
+        var keyBindings = new KeyBindings ();
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.ReplaceKey (Key.A, Key.Empty));
+    }
+
     // Add with scope does the right things
     // Add with scope does the right things
     [Theory]
     [Theory]
     [InlineData (KeyBindingScope.Focused)]
     [InlineData (KeyBindingScope.Focused)]
@@ -263,14 +306,6 @@ public class KeyBindingTests
         Assert.Contains (Command.Left, binding.Commands);
         Assert.Contains (Command.Left, binding.Commands);
     }
     }
 
 
-    [Fact]
-    public void Get_Binding_Not_Found_Throws ()
-    {
-        var keyBindings = new KeyBindings ();
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Get (Key.A));
-        Assert.Throws<InvalidOperationException> (() => keyBindings.Get (Key.B, KeyBindingScope.Application));
-    }
-
     [Theory]
     [Theory]
     [InlineData (KeyBindingScope.Focused)]
     [InlineData (KeyBindingScope.Focused)]
     [InlineData (KeyBindingScope.HotKey)]
     [InlineData (KeyBindingScope.HotKey)]

+ 3 - 1
UnitTests/UICatalog/ScenarioTests.cs

@@ -40,6 +40,7 @@ public class ScenarioTests : TestsAllViews
         var initialized = false;
         var initialized = false;
         var shutdown = false;
         var shutdown = false;
         object timeout = null;
         object timeout = null;
+        int iterationCount = 0;
 
 
         Application.InitializedChanged += OnApplicationOnInitializedChanged;
         Application.InitializedChanged += OnApplicationOnInitializedChanged;
 
 
@@ -106,7 +107,7 @@ public class ScenarioTests : TestsAllViews
             }
             }
 
 
             Assert.Fail (
             Assert.Fail (
-                         $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
+                         $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
 
 
             Application.ResetState (true);
             Application.ResetState (true);
 
 
@@ -115,6 +116,7 @@ public class ScenarioTests : TestsAllViews
 
 
         void OnApplicationOnIteration (object s, IterationEventArgs a)
         void OnApplicationOnIteration (object s, IterationEventArgs a)
         {
         {
+            iterationCount++;
             if (Application.IsInitialized)
             if (Application.IsInitialized)
             {
             {
                 // Press QuitKey 
                 // Press QuitKey 

+ 6 - 3
UnitTests/UnitTests.csproj

@@ -31,9 +31,9 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />
     <PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="[17.10,18)" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="[17.10,18)" />
-    <PackageReference Include="Moq" Version="4.20.70" />
-    <PackageReference Include="ReportGenerator" Version="5.3.8" />
-    <PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
+    <PackageReference Include="Moq" Version="[4.20.70,5)" />
+    <PackageReference Include="ReportGenerator" Version="[5.3.8,6)" />
+    <PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="[21.0.29,22)" />
     <PackageReference Include="xunit" Version="[2.9.0,3)" />
     <PackageReference Include="xunit" Version="[2.9.0,3)" />
     <PackageReference Include="Xunit.Combinatorial" Version="[1.6.24,2)" />
     <PackageReference Include="Xunit.Combinatorial" Version="[1.6.24,2)" />
     <PackageReference Include="xunit.runner.visualstudio" Version="[2.8.2,3)">
     <PackageReference Include="xunit.runner.visualstudio" Version="[2.8.2,3)">
@@ -59,6 +59,9 @@
     <Using Include="Terminal.Gui" />
     <Using Include="Terminal.Gui" />
     <Using Include="Xunit" />
     <Using Include="Xunit" />
   </ItemGroup>
   </ItemGroup>
+  <ItemGroup>
+    <Folder Include="View\Orientation\" />
+  </ItemGroup>
   <PropertyGroup Label="FineCodeCoverage">
   <PropertyGroup Label="FineCodeCoverage">
     <Enabled>
     <Enabled>
       False
       False

File diff suppressed because it is too large
+ 1200 - 1204
UnitTests/View/NavigationTests.cs


+ 107 - 0
UnitTests/View/Orientation/OrientationHelperTests.cs

@@ -0,0 +1,107 @@
+using Moq;
+
+namespace Terminal.Gui.ViewTests.OrientationTests;
+
+public class OrientationHelperTests
+{
+    [Fact]
+    public void Orientation_Set_NewValue_InvokesChangingAndChangedEvents ()
+    {
+        // Arrange
+        Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
+        var orientationHelper = new OrientationHelper (mockIOrientation.Object);
+        var changingEventInvoked = false;
+        var changedEventInvoked = false;
+
+        orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked = true; };
+        orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; };
+
+        // Act
+        orientationHelper.Orientation = Orientation.Vertical;
+
+        // Assert
+        Assert.True (changingEventInvoked, "OrientationChanging event was not invoked.");
+        Assert.True (changedEventInvoked, "OrientationChanged event was not invoked.");
+    }
+
+    [Fact]
+    public void Orientation_Set_NewValue_InvokesOnChangingAndOnChangedOverrides ()
+    {
+        // Arrange
+        Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
+        var onChangingOverrideCalled = false;
+        var onChangedOverrideCalled = false;
+
+        mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
+                        .Callback (() => onChangingOverrideCalled = true)
+                        .Returns (false); // Ensure it doesn't cancel the change
+
+        mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny<Orientation> ()))
+                        .Callback (() => onChangedOverrideCalled = true);
+
+        var orientationHelper = new OrientationHelper (mockIOrientation.Object);
+
+        // Act
+        orientationHelper.Orientation = Orientation.Vertical;
+
+        // Assert
+        Assert.True (onChangingOverrideCalled, "OnOrientationChanging override was not called.");
+        Assert.True (onChangedOverrideCalled, "OnOrientationChanged override was not called.");
+    }
+
+    [Fact]
+    public void Orientation_Set_SameValue_DoesNotInvokeChangingOrChangedEvents ()
+    {
+        // Arrange
+        Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
+        var orientationHelper = new OrientationHelper (mockIOrientation.Object);
+        orientationHelper.Orientation = Orientation.Horizontal; // Set initial orientation
+        var changingEventInvoked = false;
+        var changedEventInvoked = false;
+
+        orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked = true; };
+        orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; };
+
+        // Act
+        orientationHelper.Orientation = Orientation.Horizontal; // Set to the same value
+
+        // Assert
+        Assert.False (changingEventInvoked, "OrientationChanging event was invoked.");
+        Assert.False (changedEventInvoked, "OrientationChanged event was invoked.");
+    }
+
+    [Fact]
+    public void Orientation_Set_NewValue_OrientationChanging_CancellationPreventsChange ()
+    {
+        // Arrange
+        Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
+        var orientationHelper = new OrientationHelper (mockIOrientation.Object);
+        orientationHelper.OrientationChanging += (sender, e) => { e.Cancel = true; }; // Cancel the change
+
+        // Act
+        orientationHelper.Orientation = Orientation.Vertical;
+
+        // Assert
+        Assert.Equal (Orientation.Horizontal, orientationHelper.Orientation); // Initial orientation is Horizontal
+    }
+
+    [Fact]
+    public void Orientation_Set_NewValue_OnOrientationChanging_CancelsChange ()
+    {
+        // Arrange
+        Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
+
+        mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
+                        .Returns (true); // Override to return true, cancelling the change
+
+        var orientationHelper = new OrientationHelper (mockIOrientation.Object);
+
+        // Act
+        orientationHelper.Orientation = Orientation.Vertical;
+
+        // Assert
+        Assert.Equal (
+                      Orientation.Horizontal,
+                      orientationHelper.Orientation); // Initial orientation is Horizontal, and it should remain unchanged due to cancellation
+    }
+}

+ 136 - 0
UnitTests/View/Orientation/OrientationTests.cs

@@ -0,0 +1,136 @@
+namespace Terminal.Gui.ViewTests.OrientationTests;
+
+public class OrientationTests
+{
+    private class CustomView : View, IOrientation
+    {
+        private readonly OrientationHelper _orientationHelper;
+
+        public CustomView ()
+        {
+            _orientationHelper = new (this);
+            Orientation = Orientation.Vertical;
+            _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+            _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+        }
+
+        public Orientation Orientation
+        {
+            get => _orientationHelper.Orientation;
+            set => _orientationHelper.Orientation = value;
+        }
+
+        public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+        public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+
+        public bool CancelOnOrientationChanging { get; set; }
+
+        public bool OnOrientationChangingCalled { get; private set; }
+        public bool OnOrientationChangedCalled { get; private set; }
+
+        public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation)
+        {
+            OnOrientationChangingCalled = true;
+            // Custom logic before orientation changes
+            return CancelOnOrientationChanging; // Return true to cancel the change
+        }
+
+        public void OnOrientationChanged (Orientation newOrientation)
+        {
+            OnOrientationChangedCalled = true;
+            // Custom logic after orientation has changed
+        }
+    }
+
+    [Fact]
+    public void Orientation_Change_IsSuccessful ()
+    {
+        // Arrange
+        var customView = new CustomView ();
+        var orientationChanged = false;
+        customView.OrientationChanged += (sender, e) => orientationChanged = true;
+
+        // Act
+        customView.Orientation = Orientation.Horizontal;
+
+        // Assert
+        Assert.True (orientationChanged, "OrientationChanged event was not invoked.");
+        Assert.Equal (Orientation.Horizontal, customView.Orientation);
+    }
+
+    [Fact]
+    public void Orientation_Change_OrientationChanging_Set_Cancel_IsCancelled ()
+    {
+        // Arrange
+        var customView = new CustomView ();
+        customView.OrientationChanging += (sender, e) => e.Cancel = true; // Cancel the orientation change
+        var orientationChanged = false;
+        customView.OrientationChanged += (sender, e) => orientationChanged = true;
+
+        // Act
+        customView.Orientation = Orientation.Horizontal;
+
+        // Assert
+        Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation.");
+        Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation
+    }
+
+    [Fact]
+    public void Orientation_Change_OnOrientationChanging_Return_True_IsCancelled ()
+    {
+        // Arrange
+        var customView = new CustomView ();
+        customView.CancelOnOrientationChanging = true; // Cancel the orientation change
+
+        var orientationChanged = false;
+        customView.OrientationChanged += (sender, e) => orientationChanged = true;
+
+        // Act
+        customView.Orientation = Orientation.Horizontal;
+
+        // Assert
+        Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation.");
+        Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation
+    }
+
+
+    [Fact]
+    public void OrientationChanging_VirtualMethodCalledBeforeEvent ()
+    {
+        // Arrange
+        var radioGroup = new CustomView ();
+        bool eventCalled = false;
+
+        radioGroup.OrientationChanging += (sender, e) =>
+                                          {
+                                              eventCalled = true;
+                                              Assert.True (radioGroup.OnOrientationChangingCalled, "OnOrientationChanging was not called before the event.");
+                                          };
+
+        // Act
+        radioGroup.Orientation = Orientation.Horizontal;
+
+        // Assert
+        Assert.True (eventCalled, "OrientationChanging event was not called.");
+    }
+
+    [Fact]
+    public void OrientationChanged_VirtualMethodCalledBeforeEvent ()
+    {
+        // Arrange
+        var radioGroup = new CustomView ();
+        bool eventCalled = false;
+
+        radioGroup.OrientationChanged += (sender, e) =>
+                                         {
+                                             eventCalled = true;
+                                             Assert.True (radioGroup.OnOrientationChangedCalled, "OnOrientationChanged was not called before the event.");
+                                         };
+
+        // Act
+        radioGroup.Orientation = Orientation.Horizontal;
+
+        // Assert
+        Assert.True (eventCalled, "OrientationChanged event was not called.");
+    }
+}

+ 11 - 1
UnitTests/Views/MenuBarTests.cs

@@ -358,6 +358,11 @@ public class MenuBarTests (ITestOutputHelper output)
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Draw_A_Menu_Over_A_Dialog ()
     public void Draw_A_Menu_Over_A_Dialog ()
     {
     {
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
         Toplevel top = new ();
         Toplevel top = new ();
         var win = new Window ();
         var win = new Window ();
         top.Add (win);
         top.Add (win);
@@ -586,7 +591,12 @@ public class MenuBarTests (ITestOutputHelper output)
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Draw_A_Menu_Over_A_Top_Dialog ()
     public void Draw_A_Menu_Over_A_Top_Dialog ()
     {
     {
-        ((FakeDriver)Application.Driver!).SetBufferSize (40, 15);
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+
+        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);
 
 
         Assert.Equal (new (0, 0, 40, 15), Application.Driver?.Clip);
         Assert.Equal (new (0, 0, 40, 15), Application.Driver?.Clip);
         TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
         TestHelpers.AssertDriverContentsWithFrameAre (@"", output);

+ 6 - 6
UnitTests/Views/OverlappedTests.cs

@@ -1116,10 +1116,10 @@ public class OverlappedTests
         Assert.True (Application.OnKeyDown (Key.Tab.WithShift));
         Assert.True (Application.OnKeyDown (Key.Tab.WithShift));
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));    // move to win2
+        Assert.True (Application.OnKeyDown (Key.F6));    // move to win2
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift));    // move back to win1
+        Assert.True (Application.OnKeyDown (Key.F6.WithShift));    // move back to win1
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
 
 
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (tvW1, win1.MostFocused);
@@ -1156,19 +1156,19 @@ public class OverlappedTests
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (tf2W1, win1.MostFocused);
         Assert.Equal (tf2W1, win1.MostFocused);
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl)); // Move to win2
+        Assert.True (Application.OnKeyDown (Key.F6)); // Move to win2
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (tf1W2, win2.MostFocused);
         Assert.Equal (tf1W2, win2.MostFocused);
         tf2W2.SetFocus ();
         tf2W2.SetFocus ();
         Assert.True (tf2W2.HasFocus);
         Assert.True (tf2W2.HasFocus);
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift));
+        Assert.True (Application.OnKeyDown (Key.F6.WithShift));
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (tf2W1, win1.MostFocused);
         Assert.Equal (tf2W1, win1.MostFocused);
-        Assert.True (Application.OnKeyDown (Application.AlternateForwardKey));
+        Assert.True (Application.OnKeyDown (Application.NextTabGroupKey));
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win2, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (tf2W2, win2.MostFocused);
         Assert.Equal (tf2W2, win2.MostFocused);
-        Assert.True (Application.OnKeyDown (Application.AlternateBackwardKey));
+        Assert.True (Application.OnKeyDown (Application.PrevTabGroupKey));
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (win1, ApplicationOverlapped.OverlappedChildren [0]);
         Assert.Equal (tf2W1, win1.MostFocused);
         Assert.Equal (tf2W1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorDown));
         Assert.True (Application.OnKeyDown (Key.CursorDown));

+ 38 - 7
UnitTests/Views/ShortcutTests.cs

@@ -155,7 +155,29 @@ public class ShortcutTests
         Assert.Equal (Key.Empty, shortcut.Key);
         Assert.Equal (Key.Empty, shortcut.Key);
     }
     }
 
 
-    // Test KeyBindingScope
+
+    [Fact]
+    public void Key_Set_Binds_Key_To_CommandView_Accept ()
+    {
+        var shortcut = new Shortcut ();
+
+        shortcut.Key = Key.F1;
+
+        // TODO:
+    }
+
+    [Fact]
+    public void Key_Changing_Removes_Previous_Binding ()
+    {
+        Shortcut shortcut = new Shortcut ();
+
+        shortcut.Key = Key.A;
+        Assert.Contains (Key.A, shortcut.KeyBindings.Bindings.Keys);
+
+        shortcut.Key = Key.B;
+        Assert.DoesNotContain (Key.A, shortcut.KeyBindings.Bindings.Keys);
+        Assert.Contains (Key.B, shortcut.KeyBindings.Bindings.Keys);
+    }
 
 
     // Test Key gets bound correctly
     // Test Key gets bound correctly
     [Fact]
     [Fact]
@@ -177,15 +199,22 @@ public class ShortcutTests
     }
     }
 
 
     [Fact]
     [Fact]
-    public void Setting_Key_Binds_Key_To_CommandView_Accept ()
+    public void KeyBindingScope_Changing_Adjusts_KeyBindings ()
     {
     {
-        var shortcut = new Shortcut ();
+        Shortcut shortcut = new Shortcut ();
 
 
-        shortcut.Key = Key.F1;
+        shortcut.Key = Key.A;
+        Assert.Contains (Key.A, shortcut.KeyBindings.Bindings.Keys);
 
 
-        // TODO:
-    }
+        shortcut.KeyBindingScope = KeyBindingScope.Application;
+        Assert.DoesNotContain (Key.A, shortcut.KeyBindings.Bindings.Keys);
+        Assert.Contains (Key.A, Application.KeyBindings.Bindings.Keys);
 
 
+        shortcut.KeyBindingScope = KeyBindingScope.HotKey;
+        Assert.Contains (Key.A, shortcut.KeyBindings.Bindings.Keys);
+        Assert.DoesNotContain (Key.A, Application.KeyBindings.Bindings.Keys);
+    }
+    
     [Theory]
     [Theory]
     [InlineData (Orientation.Horizontal)]
     [InlineData (Orientation.Horizontal)]
     [InlineData (Orientation.Vertical)]
     [InlineData (Orientation.Vertical)]
@@ -567,7 +596,9 @@ public class ShortcutTests
         Application.OnKeyDown (key);
         Application.OnKeyDown (key);
 
 
         Assert.Equal (expectedAction, action);
         Assert.Equal (expectedAction, action);
-
         current.Dispose ();
         current.Dispose ();
     }
     }
+
+
+
 }
 }

+ 4 - 4
UnitTests/Views/TextViewTests.cs

@@ -5407,10 +5407,10 @@ This is the second line.
                       tv.Text
                       tv.Text
                      );
                      );
         Assert.True (tv.AllowsTab);
         Assert.True (tv.AllowsTab);
-        Assert.False (tv.NewKeyDownEvent (Key.Tab.WithCtrl));
-        Assert.False (tv.NewKeyDownEvent (Application.AlternateForwardKey));
-        Assert.False (tv.NewKeyDownEvent (Key.Tab.WithCtrl.WithShift));
-        Assert.False (tv.NewKeyDownEvent (Application.AlternateBackwardKey));
+        Assert.False (tv.NewKeyDownEvent (Key.F6));
+        Assert.False (tv.NewKeyDownEvent (Application.NextTabGroupKey));
+        Assert.False (tv.NewKeyDownEvent (Key.F6.WithShift));
+        Assert.False (tv.NewKeyDownEvent (Application.PrevTabGroupKey));
 
 
         Assert.True (tv.NewKeyDownEvent (ContextMenu.DefaultKey));
         Assert.True (tv.NewKeyDownEvent (ContextMenu.DefaultKey));
         Assert.True (tv.ContextMenu != null && tv.ContextMenu.MenuBar.Visible);
         Assert.True (tv.ContextMenu != null && tv.ContextMenu.MenuBar.Visible);

+ 6 - 6
UnitTests/Views/ToplevelTests.cs

@@ -486,10 +486,10 @@ public partial class ToplevelTests (ITestOutputHelper output)
 
 
         var prevMostFocusedSubview = top.MostFocused;
         var prevMostFocusedSubview = top.MostFocused;
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl)); // move to next TabGroup (win2)
+        Assert.True (Application.OnKeyDown (Key.F6)); // move to next TabGroup (win2)
         Assert.Equal (win2, top.Focused);
         Assert.Equal (win2, top.Focused);
 
 
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift)); // move to prev TabGroup (win1)
+        Assert.True (Application.OnKeyDown (Key.F6.WithShift)); // move to prev TabGroup (win1)
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
         Assert.Equal (tf2W1, top.MostFocused);  // BUGBUG: Should be prevMostFocusedSubview - We need to cache the last focused view in the TabGroup somehow
         Assert.Equal (tf2W1, top.MostFocused);  // BUGBUG: Should be prevMostFocusedSubview - We need to cache the last focused view in the TabGroup somehow
 
 
@@ -527,16 +527,16 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal (tvW1, top.MostFocused);
         Assert.Equal (tvW1, top.MostFocused);
 
 
         // nav to win2
         // nav to win2
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.F6));
         Assert.Equal (win2, top.Focused);
         Assert.Equal (win2, top.Focused);
         Assert.Equal (tf1W2, top.MostFocused);
         Assert.Equal (tf1W2, top.MostFocused);
-        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift));
+        Assert.True (Application.OnKeyDown (Key.F6.WithShift));
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
         Assert.Equal (tf2W1, top.MostFocused);
         Assert.Equal (tf2W1, top.MostFocused);
-        Assert.True (Application.OnKeyDown (Application.AlternateForwardKey));
+        Assert.True (Application.OnKeyDown (Application.NextTabGroupKey));
         Assert.Equal (win2, top.Focused);
         Assert.Equal (win2, top.Focused);
         Assert.Equal (tf1W2, top.MostFocused);
         Assert.Equal (tf1W2, top.MostFocused);
-        Assert.True (Application.OnKeyDown (Application.AlternateBackwardKey));
+        Assert.True (Application.OnKeyDown (Application.PrevTabGroupKey));
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
         Assert.Equal (tf2W1, top.MostFocused);
         Assert.Equal (tf2W1, top.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorUp));
         Assert.True (Application.OnKeyDown (Key.CursorUp));

+ 7 - 5
docfx/docs/config.md

@@ -33,12 +33,14 @@ The `UI Catalog` application provides an example of how to use the [`Configurati
 (Note, this list may not be complete; search the source code for `SerializableConfigurationProperty` to find all settings that can be configured.)
 (Note, this list may not be complete; search the source code for `SerializableConfigurationProperty` to find all settings that can be configured.)
 
 
   * [Application.QuitKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_QuitKey)
   * [Application.QuitKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_QuitKey)
-  * [Application.AlternateForwardKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_AlternateForwardKey)
-  * [Application.AlternateBackwardKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_AlternateBackwardKey)
-  * [Application.UseSystemConsole](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_UseSystemConsole)
+  * [Application.NextTabKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_NextTabKey)
+  * [Application.PrevTabKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_PrevTabKey)
+  * [Application.NextTabGroupKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_NextTabGroupKey)
+  * [Application.PrevTabGroupKey](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_PrevTabGroupKey)
+  * [Application.ForceDriver](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_ForceDriver)
+  * [Application.Force16Colors](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_Force16Colors)
   * [Application.IsMouseDisabled](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_IsMouseDisabled)
   * [Application.IsMouseDisabled](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_IsMouseDisabled)
-  * [Application.EnableConsoleScrolling](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_EnableConsoleScrolling)
-
+  
 ## Glyphs
 ## Glyphs
 
 
 The standard set of glyphs used for standard views (e.g. the default indicator for [Button](~/api/Terminal.Gui.Button.yml)) and line drawing (e.g. [LineCanvas](~/api/Terminal.Gui.LineCanvas.yml)) can be configured.
 The standard set of glyphs used for standard views (e.g. the default indicator for [Button](~/api/Terminal.Gui.Button.yml)) and line drawing (e.g. [LineCanvas](~/api/Terminal.Gui.LineCanvas.yml)) can be configured.

+ 9 - 2
docfx/docs/index.md

@@ -6,21 +6,28 @@
 
 
 * **[Cross Platform](drivers.md)** - Windows, Mac, and Linux. Terminal drivers for Curses, Windows, and the .NET Console mean apps will work well on both color and monochrome terminals. Apps also work over SSH.
 * **[Cross Platform](drivers.md)** - Windows, Mac, and Linux. Terminal drivers for Curses, Windows, and the .NET Console mean apps will work well on both color and monochrome terminals. Apps also work over SSH.
 * **[Templates](getting-started.md)** - The `dotnet new` command can be used to create a new Terminal.Gui app.
 * **[Templates](getting-started.md)** - The `dotnet new` command can be used to create a new Terminal.Gui app.
+* **[Extensible UI](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.View.html)** - All visible UI elements are subclasses of the `View` class, and these in turn can contain an arbitrary number of sub-views. Dozens of [Built-in Views](views.md) are provided.
 * **[Keyboard](keyboard.md) and [Mouse](mouse.md) Input** - The library handles all the details of input processing and provides a simple event-based API for applications to consume.
 * **[Keyboard](keyboard.md) and [Mouse](mouse.md) Input** - The library handles all the details of input processing and provides a simple event-based API for applications to consume.
-* **[Extensible Widgets](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.View.html)** - All visible UI elements are subclasses of the `View` class, and these in turn can contain an arbitrary number of sub-views. Dozens of [Built-in Views](views.md) are provided.
 * **[Powerful Layout Engine](layout.md)** - The layout engine makes it easy to lay out controls relative to each other and enables dynamic terminal UIs. 
 * **[Powerful Layout Engine](layout.md)** - The layout engine makes it easy to lay out controls relative to each other and enables dynamic terminal UIs. 
+* **[Machine, User, and App-Level Configuration](configuration.md)** - Persistent configuration settings, including overriding default look & feel with Themes, keyboard bindings, and more via the [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) class.
 * **[Clipboard support](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.Clipboard.html)** - Cut, Copy, and Paste is provided through the [`Clipboard`] class.
 * **[Clipboard support](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.Clipboard.html)** - Cut, Copy, and Paste is provided through the [`Clipboard`] class.
 * **Multi-tasking** - The [Mainloop](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.MainLoop.html) supports processing events, idle handlers, and timers. Most classes are safe for threading.
 * **Multi-tasking** - The [Mainloop](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.MainLoop.html) supports processing events, idle handlers, and timers. Most classes are safe for threading.
 * **[Reactive Extensions](https://github.com/dotnet/reactive)** - Use reactive extensions and benefit from increased code readability, and the ability to apply the MVVM pattern and [ReactiveUI](https://www.reactiveui.net/) data bindings. See the [source code](https://github.com/gui-cs/Terminal.GuiV2Docs/tree/master/ReactiveExample) of a sample app.
 * **[Reactive Extensions](https://github.com/dotnet/reactive)** - Use reactive extensions and benefit from increased code readability, and the ability to apply the MVVM pattern and [ReactiveUI](https://www.reactiveui.net/) data bindings. See the [source code](https://github.com/gui-cs/Terminal.GuiV2Docs/tree/master/ReactiveExample) of a sample app.
 
 
+See [What's New in V2 For more](newinv2.md).
+
 ## Conceptual Documentation
 ## Conceptual Documentation
 
 
+* [Guide to Migrating from Terminal.Gui v1](migratingfromv1.md)
 * [List of Views](views.md)
 * [List of Views](views.md)
+* [Layout Engine](layout.md)
+* [Navigation](navigation.md)
 * [Keyboard API](keyboard.md)
 * [Keyboard API](keyboard.md)
 * [Mouse API](mouse.md)
 * [Mouse API](mouse.md)
+* [Configuration and Theme Manager](config.md)
 * [Multi-tasking and the Application Main Loop](mainloop.md)
 * [Multi-tasking and the Application Main Loop](mainloop.md)
 * [Cross-platform Driver Model](drivers.md)
 * [Cross-platform Driver Model](drivers.md)
-* [Configuration and Theme Manager](config.md)
+* [Dim.Auto Deep Dive](dimauto.md)
 * [TableView Deep Dive](tableview.md)
 * [TableView Deep Dive](tableview.md)
 * [TreeView Deep Dive](treeview.md)
 * [TreeView Deep Dive](treeview.md)
 
 

+ 21 - 18
docfx/docs/keyboard.md

@@ -6,9 +6,9 @@ Tenets higher in the list have precedence over tenets lower in the list.
 
 
 * **Users Have Control** - *Terminal.Gui* provides default key bindings consistent with these tenets, but those defaults are configurable by the user. For example, `ConfigurationManager` allows users to redefine key bindings for the system, a user, or an application.
 * **Users Have Control** - *Terminal.Gui* provides default key bindings consistent with these tenets, but those defaults are configurable by the user. For example, `ConfigurationManager` allows users to redefine key bindings for the system, a user, or an application.
 
 
-* **More Editor than Command Line** - Once a *Terminal.Gui* app starts, the user is no longer using the command line. Users expect keyboard idioms in TUI apps to be consistent with GUI apps (such as VS Code, Vim, and Emacs). For example, in almost all GUI apps, `Ctrl-V` is `Paste`. But the Linux shells often use `Shift-Insert`. *Terminal.Gui* binds `Ctrl-V` by default.
+* **More Editor than Command Line** - Once a *Terminal.Gui* app starts, the user is no longer using the command line. Users expect keyboard idioms in TUI apps to be consistent with GUI apps (such as VS Code, Vim, and Emacs). For example, in almost all GUI apps, `Ctrl+V` is `Paste`. But the Linux shells often use `Shift+Insert`. *Terminal.Gui* binds `Ctrl+V` by default.
 
 
-* **Be Consistent With the User's Platform** - Users get to choose the platform they run *Terminal.Gui* apps on and those apps should respond to keyboard input in a way that is consistent with the platform. For example, on Windows to erase a word to the left, users press `Ctrl-Backspace`. But on Linux, `Ctrl-W` is used.
+* **Be Consistent With the User's Platform** - Users get to choose the platform they run *Terminal.Gui* apps on and those apps should respond to keyboard input in a way that is consistent with the platform. For example, on Windows to erase a word to the left, users press `Ctrl+Backspace`. But on Linux, `Ctrl+W` is used.
 
 
 * **The Source of Truth is Wikipedia** - We use this [Wikipedia article](https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts) as our guide for default key bindings.
 * **The Source of Truth is Wikipedia** - We use this [Wikipedia article](https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts) as our guide for default key bindings.
 
 
@@ -27,31 +27,33 @@ See [Key](~/api/Terminal.Gui.Key.yml) for more details.
 
 
 ### **[Key Bindings](~/api/Terminal.Gui.KeyBindings.yml)**
 ### **[Key Bindings](~/api/Terminal.Gui.KeyBindings.yml)**
 
 
-The default key for activating a button is `Space`. You can change this using  
-`Keybindings.Clear` and `Keybinding.Add` methods:
+The default key for activating a button is `Space`. You can change this using 
+`KeyBindings.ReplaceKey()`:
 
 
 ```csharp
 ```csharp
-var btn = new Button ("Press Me");
-btn.Keybinding.Remove (Command.Accept);
-btn.KeyBinding.Add (Key.B, Command.Accept);
+var btn = new Button () { Title = "Press me" };
+btn.KeyBindings.ReplaceKey (btn.KeyBindings.GetKeyFromCommands (Command.Accept));
 ```
 ```
 
 
 The [Command](~/api/Terminal.Gui.Command.yml) enum lists generic operations that are implemented by views. For example `Command.Accept` in a `Button` results in the `Clicked` event 
 The [Command](~/api/Terminal.Gui.Command.yml) enum lists generic operations that are implemented by views. For example `Command.Accept` in a `Button` results in the `Clicked` event 
 firing while in `TableView` it is bound to `CellActivated`. Not all commands
 firing while in `TableView` it is bound to `CellActivated`. Not all commands
 are implemented by all views (e.g. you cannot scroll in a `Button`). Use the `GetSupportedCommands()` method to determine which commands are implemented by a `View`. 
 are implemented by all views (e.g. you cannot scroll in a `Button`). Use the `GetSupportedCommands()` method to determine which commands are implemented by a `View`. 
 
 
+Key Bindings can be added at the `Application` or `View` level. For Application-scoped Key Bindings see [ApplicationNavigation](~/api/Terminal.Gui.ApplicationNavigation.yml). For View-scoped Key Bindings see [Key Bindings](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_KeyBinings).
+
 ### **[HotKey](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKey)** 
 ### **[HotKey](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKey)** 
 
 
-A **HotKey** is a keypress that selects a visible UI item. For selecting items across `View`s (e.g. a `Button` in a `Dialog`) the keypress must have the `Alt` modifier. For selecting items within a `View` that are not `View`s themselves, the keypress can be key without the `Alt` modifier.  For example, in a `Dialog`, a `Button` with the text of "_Text" can be selected with `Alt-T`. Or, in a `Menu` with "_File _Edit", `Alt-F` will select (show) the "_File" menu. If the "_File" menu has a sub-menu of "_New" `Alt-N` or `N` will ONLY select the "_New" sub-menu if the "_File" menu is already opened.
+A **HotKey** is a key press that selects a visible UI item. For selecting items across `View`s (e.g. a `Button` in a `Dialog`) the key press must have the `Alt` modifier. For selecting items within a `View` that are not `View`s themselves, the key press can be key without the `Alt` modifier.  For example, in a `Dialog`, a `Button` with the text of "_Text" can be selected with `Alt+T`. Or, in a `Menu` with "_File _Edit", `Alt+F` will select (show) the "_File" menu. If the "_File" menu has a sub-menu of "_New" `Alt+N` or `N` will ONLY select the "_New" sub-menu if the "_File" menu is already opened.
 
 
 By default, the `Text` of a `View` is used to determine the `HotKey` by looking for the first occurrence of the [HotKeySpecifier](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKeySpecifier) (which is underscore (`_`) by default). The character following the underscore is the `HotKey`. If the `HotKeySpecifier` is not found in `Text`, the first character of `Text` is used as the `HotKey`. The `Text` of a `View` can be changed at runtime, and the `HotKey` will be updated accordingly. [HotKey](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKey) is `virtual` enabling this behavior to be customized.
 By default, the `Text` of a `View` is used to determine the `HotKey` by looking for the first occurrence of the [HotKeySpecifier](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKeySpecifier) (which is underscore (`_`) by default). The character following the underscore is the `HotKey`. If the `HotKeySpecifier` is not found in `Text`, the first character of `Text` is used as the `HotKey`. The `Text` of a `View` can be changed at runtime, and the `HotKey` will be updated accordingly. [HotKey](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_HotKey) is `virtual` enabling this behavior to be customized.
 
 
-### **[Shortcut](~/api/Terminal.Gui.Shortcut.yml) - An opinionated (visually & API) View for displaying a command, helptext, key.
-**
+### **[Shortcut](~/api/Terminal.Gui.Shortcut.yml)**
+
+A **Shortcut** is an opinionated (visually & API) View for displaying a command, help text, key key press that invokes a [Command](~/api/Terminal.Gui.Command.yml).
 
 
-A **Shortcut** is a keypress that invokes a [Command](~/api/Terminal.Gui.Command.yml) or `View`-defined action even if the `View` that defines them is not focused or visible (but the `View` must be enabled). Shortcuts can be any keypress; `Key.A`, `Key.A | Key.Ctrl`, `Key.A | Key.Ctrl | Key.Alt`, `Key.Del`, and `Key.F1`, are all valid. 
+The Command can be invoked even if the `View` that defines them is not focused or visible (but the `View` must be enabled). Shortcuts can be any key press; `Key.A`, `Key.A.WithCtrl`, `Key.A.WithCtrl.WithAlt`, `Key.Del`, and `Key.F1`, are all valid. 
 
 
-`Shortcuts` are used to define application-wide actions (e.g. `Quit`), or actions that are not visible (e.g. `Copy`).
+`Shortcuts` are used to define application-wide actions or actions that are not visible (e.g. `Copy`).
 
 
 [MenuBar](~/api/Terminal.Gui.MenuBar.yml), [ContextMenu](~/api/Terminal.Gui.ContextMenu.yml), and [StatusBar](~/api/Terminal.Gui.StatusBar.yml) support `Shortcut`s. 
 [MenuBar](~/api/Terminal.Gui.MenuBar.yml), [ContextMenu](~/api/Terminal.Gui.ContextMenu.yml), and [StatusBar](~/api/Terminal.Gui.StatusBar.yml) support `Shortcut`s. 
 
 
@@ -65,14 +67,14 @@ to the [Application](~/api/Terminal.Gui.Application.yml) class by the [Main Loop
 
 
 If the view is enabled, the [NewKeyDownEvent](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_NewKeyDownEvent_Terminal_Gui_Key_) method will do the following: 
 If the view is enabled, the [NewKeyDownEvent](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_NewKeyDownEvent_Terminal_Gui_Key_) method will do the following: 
 
 
-1) If the view has a subview that has focus, 'ProcessKeyDown' on the focused view will be called. If the focused view handles the keypress, processing stops.
-2) If there is no focused sub-view, or the focused sub-view does not handle the keypress, [OnKeyDown](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnKeyDown_Terminal_Gui_Key_) will be called. If the view handles the keypress, processing stops.
-3) If the view does not handle the keypress, [OnInvokingKeyBindings](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnInvokingKeyBindings_Terminal_Gui_Key_) will be called. This method calls[InvokeKeyBindings](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_InvokeKeyBindings_Terminal_Gui_Key_) to invoke any keys bound to commands. If the key is bound and any of it's command handlers return true, processing stops.
-4) If the key is not bound, or the bound command handlers do not return true, [OnProcessKeyDow](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnProcessKeyDown_Terminal_Gui_Key_) is called. If the view handles the keypress, processing stops.
+1) If the view has a subview that has focus, 'ProcessKeyDown' on the focused view will be called. If the focused view handles the key press, processing stops.
+2) If there is no focused sub-view, or the focused sub-view does not handle the key press, [OnKeyDown](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnKeyDown_Terminal_Gui_Key_) will be called. If the view handles the key press, processing stops.
+3) If the view does not handle the key press, [OnInvokingKeyBindings](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnInvokingKeyBindings_Terminal_Gui_Key_) will be called. This method calls[InvokeKeyBindings](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_InvokeKeyBindings_Terminal_Gui_Key_) to invoke any keys bound to commands. If the key is bound and any of it's command handlers return true, processing stops.
+4) If the key is not bound, or the bound command handlers do not return true, [OnProcessKeyDow](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_OnProcessKeyDown_Terminal_Gui_Key_) is called. If the view handles the key press, processing stops.
 
 
-## **[Global Key Handling](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_OnKeyDown_Terminal_Gui_Key_)**
+## **[Application Key Handling](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_OnKeyDown_Terminal_Gui_Key_)**
 
 
-To define global key handling logic for an entire application in cases where the methods listed above are not suitable, use the `Application.OnKeyDown` event. 
+To define application key handling logic for an entire application in cases where the methods listed above are not suitable, use the `Application.OnKeyDown` event. 
 
 
 ## **[Key Down/Up Events](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_KeyDown)**
 ## **[Key Down/Up Events](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_KeyDown)**
 
 
@@ -109,6 +111,7 @@ To define global key handling logic for an entire application in cases where the
 ## Application
 ## Application
 
 
 * Implements support for `KeyBindingScope.Application`.
 * Implements support for `KeyBindingScope.Application`.
+* Exposes [Application.KeyBindings](~/api/Terminal.Gui.Application.yml#Terminal_Gui_Application_KeyBindings_).
 * Exposes cancelable `KeyDown/Up` events (via `Handled = true`). The `OnKey/Down/Up/` methods are public and can be used to simulate keyboard input.
 * Exposes cancelable `KeyDown/Up` events (via `Handled = true`). The `OnKey/Down/Up/` methods are public and can be used to simulate keyboard input.
 
 
 ## View
 ## View

+ 1 - 1
docfx/docs/layout.md

@@ -5,7 +5,7 @@ Terminal.Gui provides a rich system for how `View` objects are laid out relative
 ## Coordinates
 ## Coordinates
 
 
 * **Screen-Relative** - Describes the dimensions and characteristics of the underlying terminal. Currently Terminal.Gui only supports applications that run "full-screen", meaning they fill the entire terminal when running. As the user resizes their terminal, the `Screen` changes size and the applicaiton will be resized to fit. *Screen-Relative* means an origin (`0, 0`) at the top-left corner of the terminal. `ConsoleDriver`s operate exclusively on *Screen-Relative* coordinates.
 * **Screen-Relative** - Describes the dimensions and characteristics of the underlying terminal. Currently Terminal.Gui only supports applications that run "full-screen", meaning they fill the entire terminal when running. As the user resizes their terminal, the `Screen` changes size and the applicaiton will be resized to fit. *Screen-Relative* means an origin (`0, 0`) at the top-left corner of the terminal. `ConsoleDriver`s operate exclusively on *Screen-Relative* coordinates.
-* **Application.Relative** - The dimensions and characteristics of the application. Because only full-screen apps are currently supported, `Application` is effectively the same as `Screen` from a layout perspective. *Application-Relative* currently means an origin (`0, 0`) at the top-left corner of the terminal. `Applicaiton.Top` is a `View` with a top-left corner fixed at the *Application.Relative* coordinate of (`0, 0`) and is the size of `Screen`.
+* **Application-Relative** - The dimensions and characteristics of the application. Because only full-screen apps are currently supported, `Application` is effectively the same as `Screen` from a layout perspective. *Application-Relative* currently means an origin (`0, 0`) at the top-left corner of the terminal. `Applicaiton.Top` is a `View` with a top-left corner fixed at the *Application.Relative* coordinate of (`0, 0`) and is the size of `Screen`.
 * **Frame-Relative**  - The `Frame` property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. *Frame-Relative* means a coordinate is relative to the top-left corner of the View in question. `View.FrameToScreen ()` and `View.ScreenToFrame ()` are helper methods for translating a *Frame-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
 * **Frame-Relative**  - The `Frame` property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. *Frame-Relative* means a coordinate is relative to the top-left corner of the View in question. `View.FrameToScreen ()` and `View.ScreenToFrame ()` are helper methods for translating a *Frame-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
 * **Content-Relative** - A rectangle, with an origin of (`0, 0`) and size (defined by `View.GetContentSize()`) where the View's content exists. *Content-Relative* means a coordinate is relative to the top-left corner of the content, which is always (`0,0`). `View.ContentToScreen ()` and `View.ScreenToContent ()` are helper methods for translating a *Content-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
 * **Content-Relative** - A rectangle, with an origin of (`0, 0`) and size (defined by `View.GetContentSize()`) where the View's content exists. *Content-Relative* means a coordinate is relative to the top-left corner of the content, which is always (`0,0`). `View.ContentToScreen ()` and `View.ScreenToContent ()` are helper methods for translating a *Content-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
 * **Viewport-Relative** - A *Content-Relative* rectangle representing the subset of the View's content that is visible to the user. If `View.GetContentSize()` is larger than the Viewport, scrolling is enabled. *Viewport-Relative* means a coordinate that is bound by (`0,0`) and the size of the inner-rectangle of the View's `Padding`. The View drawing primitives (e.g. `View.Move`) take *Viewport-Relative* coordinates; `Move (0, 0)` means the `Cell` in the top-left corner of the inner rectangle of `Padding`. `View.ViewportToScreen ()` and `View.ScreenToViewport ()` are helper methods for translating a *Viewport-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa. To convert a *Viewport-Relative* coordinate to a *Content-Relative* coordinate, simply subtract `Viewport.X` and/or `Viewport.Y` from the *Content-Relative* coordinate. To convert a *Viewport-Relative* coordinate to a *Frame-Relative* coordinate, subtract the point returned by `View.GetViewportOffsetFromFrame`.
 * **Viewport-Relative** - A *Content-Relative* rectangle representing the subset of the View's content that is visible to the user. If `View.GetContentSize()` is larger than the Viewport, scrolling is enabled. *Viewport-Relative* means a coordinate that is bound by (`0,0`) and the size of the inner-rectangle of the View's `Padding`. The View drawing primitives (e.g. `View.Move`) take *Viewport-Relative* coordinates; `Move (0, 0)` means the `Cell` in the top-left corner of the inner rectangle of `Padding`. `View.ViewportToScreen ()` and `View.ScreenToViewport ()` are helper methods for translating a *Viewport-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa. To convert a *Viewport-Relative* coordinate to a *Content-Relative* coordinate, simply subtract `Viewport.X` and/or `Viewport.Y` from the *Content-Relative* coordinate. To convert a *Viewport-Relative* coordinate to a *Frame-Relative* coordinate, subtract the point returned by `View.GetViewportOffsetFromFrame`.

+ 14 - 10
docfx/docs/toc.yml

@@ -4,24 +4,28 @@
   href: getting-started.md
   href: getting-started.md
 - name: What's new in v2
 - name: What's new in v2
   href: newinv2.md
   href: newinv2.md
-- name: v1 To v2 Migration Guide
+- name: v1 To v2 Migration
   href: migratingfromv1.md  
   href: migratingfromv1.md  
 - name: List of Views
 - name: List of Views
   href: views.md
   href: views.md
+- name: Layout Engine
+  href: layout.md
+- name: Navigation
+  href: navigation.md
+- name: Keyboard
+  href: keyboard.md
+- name: Mouse
+  href: mouse.md
 - name: Configuration
 - name: Configuration
   href: config.md
   href: config.md
-- name: Drawing (Text, Lines, and Color)
+- name: Drawing
   href: drawing.md
   href: drawing.md
-- name: Cross-platform Driver Model
+- name: Drivers
   href: drivers.md
   href: drivers.md
-- name: Keyboard Event Processing
-  href: keyboard.md
-- name: Mouse Event Processing
-  href: mouse.md
-- name: The View Layout Engine
-  href: layout.md
-- name: Mutli-Tasking and Application Main Loop
+- name: Multi-Tasking
   href: mainloop.md
   href: mainloop.md
+- name: Dim.Auto Deep Dive
+  href: dimauto.md
 - name: TableView Deep Dive
 - name: TableView Deep Dive
   href: tableview.md
   href: tableview.md
 - name: TreeView Deep Dive
 - name: TreeView Deep Dive

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