瀏覽代碼

Merged v2_develop

Tig 8 月之前
父節點
當前提交
dc7bd44ff5
共有 100 個文件被更改,包括 4650 次插入3120 次删除
  1. 4 1
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 7 1
      .github/workflows/dotnet-core.yml
  3. 3 3
      .github/workflows/publish.yml
  4. 0 3
      .gitignore
  5. 2 2
      CommunityToolkitExample/LoginView.cs
  6. 1 1
      Example/Example.cs
  7. 1 1
      NativeAot/NativeAot.csproj
  8. 1 1
      NativeAot/Program.cs
  9. 1 1
      NativeAot/Properties/launchSettings.json
  10. 3 3
      README.md
  11. 2 2
      ReactiveExample/LoginView.cs
  12. 1 1
      SelfContained/Program.cs
  13. 1 1
      SelfContained/SelfContained.csproj
  14. 7 1
      Terminal.Gui/Application/Application.Driver.cs
  15. 28 6
      Terminal.Gui/Application/Application.Initialization.cs
  16. 116 227
      Terminal.Gui/Application/Application.Keyboard.cs
  17. 191 129
      Terminal.Gui/Application/Application.Mouse.cs
  18. 79 0
      Terminal.Gui/Application/Application.Navigation.cs
  19. 93 307
      Terminal.Gui/Application/Application.Run.cs
  20. 0 6
      Terminal.Gui/Application/Application.Screen.cs
  21. 1 43
      Terminal.Gui/Application/Application.Toplevel.cs
  22. 2 4
      Terminal.Gui/Application/Application.cs
  23. 17 2
      Terminal.Gui/Application/ApplicationNavigation.cs
  24. 0 444
      Terminal.Gui/Application/ApplicationOverlapped.cs
  25. 1 1
      Terminal.Gui/Configuration/AppScope.cs
  26. 18 5
      Terminal.Gui/Configuration/ColorJsonConverter.cs
  27. 14 5
      Terminal.Gui/Configuration/ConfigurationManager.cs
  28. 24 17
      Terminal.Gui/Configuration/RuneJsonConverter.cs
  29. 25 8
      Terminal.Gui/Configuration/SettingsScope.cs
  30. 3 1
      Terminal.Gui/Configuration/SourceGenerationContext.cs
  31. 1 1
      Terminal.Gui/Configuration/ThemeManager.cs
  32. 9 2
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  33. 257 83
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  34. 15 253
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs
  35. 3 3
      Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
  36. 13 0
      Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
  37. 3 3
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  38. 16 6
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  39. 24 7
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  40. 55 53
      Terminal.Gui/Drawing/Alignment.cs
  41. 8 14
      Terminal.Gui/Drawing/AlignmentModes.cs
  42. 20 0
      Terminal.Gui/Drawing/AssumeSupportDetector.cs
  43. 6 6
      Terminal.Gui/Drawing/Attribute.cs
  44. 152 6
      Terminal.Gui/Drawing/Cell.cs
  45. 8 8
      Terminal.Gui/Drawing/CellEventArgs.cs
  46. 47 44
      Terminal.Gui/Drawing/Color.ColorExtensions.cs
  47. 2 2
      Terminal.Gui/Drawing/Color.ColorName.cs
  48. 180 177
      Terminal.Gui/Drawing/Color.Formatting.cs
  49. 3 3
      Terminal.Gui/Drawing/Color.Operators.cs
  50. 40 40
      Terminal.Gui/Drawing/Color.cs
  51. 43 55
      Terminal.Gui/Drawing/ColorScheme.cs
  52. 30 27
      Terminal.Gui/Drawing/ColorStrings.cs
  53. 31 5
      Terminal.Gui/Drawing/Glyphs.cs
  54. 15 0
      Terminal.Gui/Drawing/ISixelSupportDetector.cs
  55. 1 1
      Terminal.Gui/Drawing/LineCanvas.cs
  56. 91 0
      Terminal.Gui/Drawing/Quant/ColorQuantizer.cs
  57. 31 0
      Terminal.Gui/Drawing/Quant/EuclideanColorDistance.cs
  58. 18 0
      Terminal.Gui/Drawing/Quant/IColorDistance.cs
  59. 19 0
      Terminal.Gui/Drawing/Quant/IPaletteBuilder.cs
  60. 112 0
      Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs
  61. 252 0
      Terminal.Gui/Drawing/SixelEncoder.cs
  62. 133 0
      Terminal.Gui/Drawing/SixelSupportDetector.cs
  63. 33 0
      Terminal.Gui/Drawing/SixelSupportResult.cs
  64. 19 0
      Terminal.Gui/Drawing/SixelToRender.cs
  65. 1 1
      Terminal.Gui/Drawing/StraightLine.cs
  66. 2 2
      Terminal.Gui/FileServices/DefaultFileOperations.cs
  67. 196 147
      Terminal.Gui/Input/Command.cs
  68. 11 1
      Terminal.Gui/Input/CommandContext.cs
  69. 15 0
      Terminal.Gui/Input/CommandEventArgs.cs
  70. 1 1
      Terminal.Gui/Input/GrabMouseEventArgs.cs
  71. 93 47
      Terminal.Gui/Input/Key.cs
  72. 3 0
      Terminal.Gui/Input/KeyBinding.cs
  73. 30 21
      Terminal.Gui/Input/KeyBindingScope.cs
  74. 17 5
      Terminal.Gui/Input/KeyBindings.cs
  75. 101 0
      Terminal.Gui/Input/MouseEventArgs.cs
  76. 0 32
      Terminal.Gui/Input/MouseEventEventArgs.cs
  77. 2 49
      Terminal.Gui/Input/MouseFlags.cs
  78. 0 173
      Terminal.Gui/Input/ShortcutHelper.cs
  79. 72 0
      Terminal.Gui/Resources/Strings.Designer.cs
  80. 15 12
      Terminal.Gui/Resources/Strings.fr-FR.resx
  81. 58 55
      Terminal.Gui/Resources/Strings.ja-JP.resx
  82. 16 13
      Terminal.Gui/Resources/Strings.pt-PT.resx
  83. 273 249
      Terminal.Gui/Resources/Strings.resx
  84. 9 6
      Terminal.Gui/Resources/Strings.zh-Hans.resx
  85. 324 38
      Terminal.Gui/Resources/config.json
  86. 35 2
      Terminal.Gui/Terminal.Gui.csproj
  87. 1 1
      Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
  88. 1 1
      Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs
  89. 2 2
      Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs
  90. 1 1
      Terminal.Gui/Text/Autocomplete/IAutocomplete.cs
  91. 1 1
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs
  92. 2 2
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
  93. 35 35
      Terminal.Gui/View/Adornment/Adornment.cs
  94. 916 179
      Terminal.Gui/View/Adornment/Border.cs
  95. 15 6
      Terminal.Gui/View/Adornment/Margin.cs
  96. 2 2
      Terminal.Gui/View/Adornment/Padding.cs
  97. 2 2
      Terminal.Gui/View/Adornment/ShadowView.cs
  98. 11 0
      Terminal.Gui/View/CancelEventArgs.cs
  99. 9 8
      Terminal.Gui/View/HighlightStyle.cs
  100. 42 0
      Terminal.Gui/View/Layout/DimAuto.cs

+ 4 - 1
.github/ISSUE_TEMPLATE/bug_report.md

@@ -2,7 +2,7 @@
 name: Bug report
 name: Bug report
 about: Create a report to help us improve
 about: Create a report to help us improve
 title: ''
 title: ''
-labels: ''
+labels: bug
 assignees: ''
 assignees: ''
 
 
 ---
 ---
@@ -36,3 +36,6 @@ If applicable, add screenshots to help explain your problem.
 
 
 **Additional context**
 **Additional context**
 Add any other context about the problem here.
 Add any other context about the problem here.
+
+**Set Project & Milestone**
+If you have access, please don't forget to set the right Project and Milestone.

+ 7 - 1
.github/workflows/dotnet-core.yml

@@ -90,7 +90,13 @@ jobs:
         dotnet-version: 8.x
         dotnet-version: 8.x
         dotnet-quality: 'ga'
         dotnet-quality: 'ga'
 
 
-    - name: Build Release
+    - name: Build Release Terminal.Gui
+      run: dotnet build Terminal.Gui/Terminal.Gui.csproj --configuration Release
+
+    - name: Pack Release Terminal.Gui
+      run: dotnet pack Terminal.Gui/Terminal.Gui.csproj --configuration Release --output ./local_packages
+
+    - name: Build Release Solution
       run: dotnet build --configuration Release
       run: dotnet build --configuration Release
 
 
 
 

+ 3 - 3
.github/workflows/publish.yml

@@ -43,11 +43,11 @@ jobs:
     - name: Build Release
     - name: Build Release
       run: |
       run: |
         dotnet-gitversion /updateprojectfiles
         dotnet-gitversion /updateprojectfiles
-        dotnet build --no-incremental --nologo --force --configuration Release
-        dotnet test --configuration Release
+        dotnet build Terminal.Gui/Terminal.Gui.csproj --no-incremental --nologo --force --configuration Release
+        dotnet test Terminal.Gui/Terminal.Gui.csproj --configuration Release
 
 
     - name: Pack
     - name: Pack
-      run: dotnet pack -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
+      run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
 
 
     # - name: Test to generate Code Coverage Report
     # - name: Test to generate Code Coverage Report
     #   run: |
     #   run: |

+ 0 - 3
.gitignore

@@ -34,9 +34,6 @@ _ReSharper.**
 ~$*
 ~$*
 *~
 *~
 
 
-# NuGet Stuff
-*.nupkg
-*.snupkg
 # Exclude everything in packages directory except the packages/build directory
 # Exclude everything in packages directory except the packages/build directory
 **/[Pp]ackages/*
 **/[Pp]ackages/*
 !**/[Pp]ackages/build/
 !**/[Pp]ackages/build/

+ 2 - 2
CommunityToolkitExample/LoginView.cs

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

+ 1 - 1
Example/Example.cs

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

+ 1 - 1
NativeAot/NativeAot.csproj

@@ -15,7 +15,7 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
   <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PackageReference Include="Terminal.Gui" Version="[2.0.0-v2-develop.2189,3)" />
+    <PackageReference Include="Terminal.Gui" Version="2.0.0" />
     <TrimmerRootAssembly Include="Terminal.Gui" />
     <TrimmerRootAssembly Include="Terminal.Gui" />
   </ItemGroup>
   </ItemGroup>
 
 

+ 1 - 1
NativeAot/Program.cs

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

+ 1 - 1
NativeAot/Properties/launchSettings.json

@@ -3,7 +3,7 @@
     "NativeAot": {
     "NativeAot": {
       "commandName": "Project"
       "commandName": "Project"
     },
     },
-    "WSL : UICatalog": {
+    "WSL : NativeAot": {
       "commandName": "Executable",
       "commandName": "Executable",
       "executablePath": "wsl",
       "executablePath": "wsl",
       "commandLineArgs": "dotnet NativeAot.dll",
       "commandLineArgs": "dotnet NativeAot.dll",

+ 3 - 3
README.md

@@ -8,7 +8,7 @@
 
 
 * The current, stable, release of Terminal.Gui v1 is [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui).
 * The current, stable, release of Terminal.Gui v1 is [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui).
 * The current `prealpha` release of Terminal.Gui v2 can be found on [Nuget](https://www.nuget.org/packages/Terminal.Gui).
 * The current `prealpha` release of Terminal.Gui v2 can be found on [Nuget](https://www.nuget.org/packages/Terminal.Gui).
-* Developers starting new TUI projects are encouraged to target `v2`. The API is signifcantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
+* Developers starting new TUI projects are encouraged to target `v2`. The API is significantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
 * `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
 * `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
  
  
 **Terminal.Gui**: A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
 **Terminal.Gui**: A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
@@ -35,9 +35,9 @@ dotnet run
 * [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
 * [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
 * [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
 * [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
 
 
-The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation here](This is the v2 API documentation. For v1 go here: https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html)
+The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation here](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html).
 
 
-See the [`Terminal.Gui/` README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured. 
+See the [`Terminal.Gui/`README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured. 
 
 
 ## Showcase & Examples
 ## Showcase & Examples
 
 

+ 2 - 2
ReactiveExample/LoginView.cs

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

+ 1 - 1
SelfContained/Program.cs

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

+ 1 - 1
SelfContained/SelfContained.csproj

@@ -18,7 +18,7 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
   <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PackageReference Include="Terminal.Gui" Version="[2.0.0-pre.1788,3)" />
+    <PackageReference Include="Terminal.Gui" Version="2.0.0" />
     <TrimmerRootAssembly Include="Terminal.Gui" />
     <TrimmerRootAssembly Include="Terminal.Gui" />
   </ItemGroup>
   </ItemGroup>
 
 

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

@@ -10,7 +10,7 @@ public static partial class Application // Driver abstractions
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
     ///     Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
-    ///     <see cref="ColorName"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
+    ///     <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
     ///     as long as the selected <see cref="ConsoleDriver"/> supports TrueColor.
     ///     as long as the selected <see cref="ConsoleDriver"/> supports TrueColor.
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
@@ -26,4 +26,10 @@ public static partial class Application // Driver abstractions
     /// </remarks>
     /// </remarks>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     public static string ForceDriver { get; set; } = string.Empty;
     public static string ForceDriver { get; set; } = string.Empty;
+
+    /// <summary>
+    /// Collection of sixel images to write out to screen when updating.
+    /// Only add to this collection if you are sure terminal supports sixel format.
+    /// </summary>
+    public static List<SixelToRender> Sixel = new List<SixelToRender> ();
 }
 }

+ 28 - 6
Terminal.Gui/Application/Application.Initialization.cs

@@ -1,4 +1,5 @@
 #nullable enable
 #nullable enable
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Reflection;
 
 
@@ -71,7 +72,7 @@ public static partial class Application // Initialization (Init/Shutdown)
         if (!calledViaRunT)
         if (!calledViaRunT)
         {
         {
             // Reset all class variables (Application is a singleton).
             // Reset all class variables (Application is a singleton).
-            ResetState ();
+            ResetState (ignoreDisposed: true);
         }
         }
 
 
         Navigation = new ();
         Navigation = new ();
@@ -80,6 +81,16 @@ public static partial class Application // Initialization (Init/Shutdown)
         if (driver is { })
         if (driver is { })
         {
         {
             Driver = driver;
             Driver = driver;
+
+            if (driver is FakeDriver)
+            {
+                // We're running unit tests. Disable loading config files other than default
+                if (Locations == ConfigLocations.All)
+                {
+                    Locations = ConfigLocations.DefaultOnly;
+                    Reset ();
+                }
+            }
         }
         }
 
 
         // Start the process of configuration management.
         // Start the process of configuration management.
@@ -88,7 +99,12 @@ public static partial class Application // Initialization (Init/Shutdown)
         // valid after a Driver is loaded. In this case we need just
         // valid after a Driver is loaded. In this case we need just
         // `Settings` so we can determine which driver to use.
         // `Settings` so we can determine which driver to use.
         // Don't reset, so we can inherit the theme from the previous run.
         // Don't reset, so we can inherit the theme from the previous run.
+        string previousTheme = Themes?.Theme ?? string.Empty;
         Load ();
         Load ();
+        if (Themes is { } && !string.IsNullOrEmpty (previousTheme) && previousTheme != "Default")
+        {
+            ThemeManager.SelectedTheme = previousTheme;
+        }
         Apply ();
         Apply ();
 
 
         AddApplicationKeyBindings ();
         AddApplicationKeyBindings ();
@@ -162,9 +178,9 @@ public static partial class Application // Initialization (Init/Shutdown)
     }
     }
 
 
     private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
     private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
-    private static void Driver_KeyDown (object? sender, Key e) { OnKeyDown (e); }
-    private static void Driver_KeyUp (object? sender, Key e) { OnKeyUp (e); }
-    private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); }
+    private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); }
+    private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); }
+    private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); }
 
 
     /// <summary>Gets of list of <see cref="ConsoleDriver"/> types that are available.</summary>
     /// <summary>Gets of list of <see cref="ConsoleDriver"/> types that are available.</summary>
     /// <returns></returns>
     /// <returns></returns>
@@ -198,10 +214,16 @@ public static partial class Application // Initialization (Init/Shutdown)
     public static void Shutdown ()
     public static void Shutdown ()
     {
     {
         // TODO: Throw an exception if Init hasn't been called.
         // TODO: Throw an exception if Init hasn't been called.
+
+        bool wasInitialized = IsInitialized;
         ResetState ();
         ResetState ();
         PrintJsonErrors ();
         PrintJsonErrors ();
-        bool init = IsInitialized;
-        InitializedChanged?.Invoke (null, new (in init));
+
+        if (wasInitialized)
+        {
+            bool init = IsInitialized;
+            InitializedChanged?.Invoke (null, new (in init));
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 116 - 227
Terminal.Gui/Application/Application.Keyboard.cs

@@ -3,105 +3,28 @@ namespace Terminal.Gui;
 
 
 public static partial class Application // Keyboard handling
 public static partial class Application // Keyboard handling
 {
 {
-    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
-    private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
-
-    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
-
-    private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
-
-    private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
-
-    static Application () { AddApplicationKeyBindings (); }
-
-    /// <summary>Gets the key bindings for this view.</summary>
-    public static KeyBindings KeyBindings { get; internal set; } = new ();
-
-    /// <summary>
-    ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key>? KeyDown;
-
-    /// <summary>
-    ///     Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key>? KeyUp;
-
-    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key NextTabGroupKey
-    {
-        get => _nextTabGroupKey;
-        set
-        {
-            if (_nextTabGroupKey != value)
-            {
-                ReplaceKey (_nextTabGroupKey, value);
-                _nextTabGroupKey = value;
-            }
-        }
-    }
-
-    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key NextTabKey
-    {
-        get => _nextTabKey;
-        set
-        {
-            if (_nextTabKey != value)
-            {
-                ReplaceKey (_nextTabKey, value);
-                _nextTabKey = value;
-            }
-        }
-    }
-
     /// <summary>
     /// <summary>
-    ///     Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
-    ///     then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
-    ///     before <see cref="OnKeyUp"/>.
+    ///     Called when the user presses a key (by the <see cref="ConsoleDriver"/>). Raises the cancelable
+    ///     <see cref="KeyDown"/> event, then calls <see cref="View.NewKeyDownEvent"/> on all top level views, and finally
+    ///     if the key was not handled, invokes any Application-scoped <see cref="KeyBindings"/>.
     /// </summary>
     /// </summary>
     /// <remarks>Can be used to simulate key press events.</remarks>
     /// <remarks>Can be used to simulate key press events.</remarks>
-    /// <param name="keyEvent"></param>
+    /// <param name="key"></param>
     /// <returns><see langword="true"/> if the key was handled.</returns>
     /// <returns><see langword="true"/> if the key was handled.</returns>
-    public static bool OnKeyDown (Key keyEvent)
+    public static bool RaiseKeyDownEvent (Key key)
     {
     {
-        //if (!IsInitialized)
-        //{
-        //    return true;
-        //}
-
-        KeyDown?.Invoke (null, keyEvent);
+        KeyDown?.Invoke (null, key);
 
 
-        if (keyEvent.Handled)
+        if (key.Handled)
         {
         {
             return true;
             return true;
         }
         }
 
 
-        if (Current is null)
+        if (Top is null)
         {
         {
             foreach (Toplevel topLevel in TopLevels.ToList ())
             foreach (Toplevel topLevel in TopLevels.ToList ())
             {
             {
-                if (topLevel.NewKeyDownEvent (keyEvent))
+                if (topLevel.NewKeyDownEvent (key))
                 {
                 {
                     return true;
                     return true;
                 }
                 }
@@ -114,7 +37,7 @@ public static partial class Application // Keyboard handling
         }
         }
         else
         else
         {
         {
-            if (Current.NewKeyDownEvent (keyEvent))
+            if (Top.NewKeyDownEvent (key))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -122,7 +45,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 (KeyValuePair<Key, KeyBinding> binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode))
+        foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.Bindings.Where (b => b.Key == key.KeyCode))
         {
         {
             if (binding.Value.BoundView is { })
             if (binding.Value.BoundView is { })
             {
             {
@@ -135,7 +58,7 @@ public static partial class Application // Keyboard handling
             }
             }
             else
             else
             {
             {
-                if (!KeyBindings.TryGet (keyEvent, KeyBindingScope.Application, out KeyBinding appBinding))
+                if (!KeyBindings.TryGet (key, KeyBindingScope.Application, out KeyBinding appBinding))
                 {
                 {
                     continue;
                     continue;
                 }
                 }
@@ -144,7 +67,7 @@ public static partial class Application // Keyboard handling
 
 
                 foreach (Command command in appBinding.Commands)
                 foreach (Command command in appBinding.Commands)
                 {
                 {
-                    toReturn = InvokeCommand (command, keyEvent, appBinding);
+                    toReturn = InvokeCommand (command, key, appBinding);
                 }
                 }
 
 
                 return toReturn ?? true;
                 return toReturn ?? true;
@@ -152,58 +75,66 @@ public static partial class Application // Keyboard handling
         }
         }
 
 
         return false;
         return false;
-    }
 
 
-    /// <summary>
-    /// INTENRAL method to invoke one of the commands in <see cref="CommandImplementations"/>
-    /// </summary>
-    /// <param name="command"></param>
-    /// <param name="keyEvent"></param>
-    /// <param name="appBinding"></param>
-    /// <returns></returns>
-    /// <exception cref="NotSupportedException"></exception>
-    private static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding)
-    {
-        if (!CommandImplementations!.ContainsKey (command))
+        static bool? InvokeCommand (Command command, Key key, KeyBinding appBinding)
         {
         {
-            throw new NotSupportedException (
-                                             @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
-                                            );
-        }
+            if (!CommandImplementations!.ContainsKey (command))
+            {
+                throw new NotSupportedException (
+                                                 @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by Application."
+                                                );
+            }
 
 
-        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
-        {
-            var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
-            return implementation (context);
-        }
+            if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
+            {
+                var context = new CommandContext (command, key, appBinding); // Create the context here
 
 
-        return false;
+                return implementation (context);
+            }
+
+            return false;
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
-    ///     then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="OnKeyDown"/>.
+    ///     Raised when the user presses a key.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
     /// </summary>
     /// </summary>
-    /// <remarks>Can be used to simulate key press events.</remarks>
-    /// <param name="a"></param>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key>? KeyDown;
+
+    /// <summary>
+    ///     Called when the user releases a key (by the <see cref="ConsoleDriver"/>). Raises the cancelable <see cref="KeyUp"/>
+    ///     event
+    ///     then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="RaiseKeyDownEvent"/>.
+    /// </summary>
+    /// <remarks>Can be used to simulate key release events.</remarks>
+    /// <param name="key"></param>
     /// <returns><see langword="true"/> if the key was handled.</returns>
     /// <returns><see langword="true"/> if the key was handled.</returns>
-    public static bool OnKeyUp (Key a)
+    public static bool RaiseKeyUpEvent (Key key)
     {
     {
         if (!IsInitialized)
         if (!IsInitialized)
         {
         {
             return true;
             return true;
         }
         }
 
 
-        KeyUp?.Invoke (null, a);
+        KeyUp?.Invoke (null, key);
 
 
-        if (a.Handled)
+        if (key.Handled)
         {
         {
             return true;
             return true;
         }
         }
 
 
         foreach (Toplevel topLevel in TopLevels.ToList ())
         foreach (Toplevel topLevel in TopLevels.ToList ())
         {
         {
-            if (topLevel.NewKeyUpEvent (a))
+            if (topLevel.NewKeyUpEvent (key))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -217,50 +148,12 @@ public static partial class Application // Keyboard handling
         return false;
         return false;
     }
     }
 
 
-    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key PrevTabGroupKey
-    {
-        get => _prevTabGroupKey;
-        set
-        {
-            if (_prevTabGroupKey != value)
-            {
-                ReplaceKey (_prevTabGroupKey, value);
-                _prevTabGroupKey = value;
-            }
-        }
-    }
+    #region Application-scoped KeyBindings
 
 
-    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key PrevTabKey
-    {
-        get => _prevTabKey;
-        set
-        {
-            if (_prevTabKey != value)
-            {
-                ReplaceKey (_prevTabKey, value);
-                _prevTabKey = value;
-            }
-        }
-    }
+    static Application () { AddApplicationKeyBindings (); }
 
 
-    /// <summary>Gets or sets the key to quit the application.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key QuitKey
-    {
-        get => _quitKey;
-        set
-        {
-            if (_quitKey != value)
-            {
-                ReplaceKey (_quitKey, value);
-                _quitKey = value;
-            }
-        }
-    }
+    /// <summary>Gets the Application-scoped key bindings.</summary>
+    public static KeyBindings KeyBindings { get; internal set; } = new ();
 
 
     internal static void AddApplicationKeyBindings ()
     internal static void AddApplicationKeyBindings ()
     {
     {
@@ -268,17 +161,10 @@ public static partial class Application // Keyboard handling
 
 
         // 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.Quit,
                     static () =>
                     static () =>
                     {
                     {
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            RequestStop (Current!);
-                        }
-                        else
-                        {
-                            RequestStop ();
-                        }
+                        RequestStop ();
 
 
                         return true;
                         return true;
                     }
                     }
@@ -295,75 +181,74 @@ public static partial class Application // Keyboard handling
                    );
                    );
 
 
         AddCommand (
         AddCommand (
-                    Command.NextView,
+                    Command.NextTabStop,
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
 
 
         AddCommand (
         AddCommand (
-                    Command.PreviousView,
+                    Command.PreviousTabStop,
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop));
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop));
 
 
         AddCommand (
         AddCommand (
-                    Command.NextViewOrTop,
-                    static () =>
-                    {
-                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            ApplicationOverlapped.OverlappedMoveNext ();
+                    Command.NextTabGroup,
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup));
 
 
-                            return true;
-                        }
-
-                        return Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
-                    }
-                   );
+        AddCommand (
+                    Command.PreviousTabGroup,
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup));
 
 
         AddCommand (
         AddCommand (
-                    Command.PreviousViewOrTop,
+                    Command.Refresh,
                     static () =>
                     static () =>
                     {
                     {
-                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            ApplicationOverlapped.OverlappedMovePrevious ();
-
-                            return true;
-                        }
+                        Refresh ();
 
 
-                        return Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+                        return true;
                     }
                     }
                    );
                    );
 
 
         AddCommand (
         AddCommand (
-                    Command.Refresh,
+                    Command.Edit,
                     static () =>
                     static () =>
                     {
                     {
-                        Refresh ();
+                        View? viewToArrange = Navigation?.GetFocused ();
 
 
-                        return true;
-                    }
-                   );
+                        // Go up the superview hierarchy and find the first that is not ViewArrangement.Fixed
+                        while (viewToArrange is { SuperView: { }, Arrangement: ViewArrangement.Fixed })
+                        {
+                            viewToArrange = viewToArrange.SuperView;
+                        }
+
+                        if (viewToArrange is { })
+                        {
+                            return viewToArrange.Border?.EnterArrangeMode (ViewArrangement.Fixed);
+                        }
+
+                        return false;
+                    });
 
 
         KeyBindings.Clear ();
         KeyBindings.Clear ();
 
 
-        // Resources/config.json overrrides
+        // Resources/config.json overrides
         NextTabKey = Key.Tab;
         NextTabKey = Key.Tab;
         PrevTabKey = Key.Tab.WithShift;
         PrevTabKey = Key.Tab.WithShift;
         NextTabGroupKey = Key.F6;
         NextTabGroupKey = Key.F6;
         PrevTabGroupKey = Key.F6.WithShift;
         PrevTabGroupKey = Key.F6.WithShift;
         QuitKey = Key.Esc;
         QuitKey = Key.Esc;
+        ArrangeKey = Key.F5.WithCtrl;
+
+        KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.Quit);
 
 
-        KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousTabStop);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousTabStop);
+        KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousTabStop);
 
 
-        KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (Key.CursorLeft, 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 (NextTabGroupKey, KeyBindingScope.Application, Command.NextTabGroup);
+        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousTabGroup);
 
 
-        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop);
-        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop);
+        KeyBindings.Add (ArrangeKey, KeyBindingScope.Application, Command.Edit);
 
 
         // 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);
@@ -392,6 +277,26 @@ public static partial class Application // Keyboard handling
                           .ToList ();
                           .ToList ();
     }
     }
 
 
+    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);
+        }
+    }
+
+
+    #endregion Application-scoped KeyBindings
+
     /// <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"/>.
@@ -413,22 +318,6 @@ public static partial class Application // Keyboard handling
     /// <summary>
     /// <summary>
     ///     Commands for Application.
     ///     Commands for Application.
     /// </summary>
     /// </summary>
-    private static Dictionary<Command, Func<CommandContext, bool?>>? CommandImplementations { get; set; }
-
-    private static void ReplaceKey (Key oldKey, Key newKey)
-    {
-        if (KeyBindings.Bindings.Count == 0)
-        {
-            return;
-        }
+    private static Dictionary<Command, View.CommandImplementation>? CommandImplementations { get; set; }
 
 
-        if (newKey == Key.Empty)
-        {
-            KeyBindings.Remove (oldKey);
-        }
-        else
-        {
-            KeyBindings.ReplaceKey (oldKey, newKey);
-        }
-    }
 }
 }

+ 191 - 129
Terminal.Gui/Application/Application.Mouse.cs

@@ -1,9 +1,16 @@
 #nullable enable
 #nullable enable
+using System.ComponentModel;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public static partial class Application // Mouse handling
 public static partial class Application // Mouse handling
 {
 {
-    #region Mouse handling
+    internal static Point? _lastMousePosition;
+
+    /// <summary>
+    ///     Gets the most recent position of the mouse.
+    /// </summary>
+    public static Point? GetLastMousePosition () { return _lastMousePosition; }
 
 
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
@@ -37,12 +44,12 @@ public static partial class Application // Mouse handling
     /// <param name="view">View that will receive all mouse events until <see cref="UngrabMouse"/> is invoked.</param>
     /// <param name="view">View that will receive all mouse events until <see cref="UngrabMouse"/> is invoked.</param>
     public static void GrabMouse (View? view)
     public static void GrabMouse (View? view)
     {
     {
-        if (view is null || OnGrabbingMouse (view))
+        if (view is null || RaiseGrabbingMouseEvent (view))
         {
         {
             return;
             return;
         }
         }
 
 
-        OnGrabbedMouse (view);
+        RaiseGrabbedMouseEvent (view);
         MouseGrabView = view;
         MouseGrabView = view;
     }
     }
 
 
@@ -58,16 +65,16 @@ public static partial class Application // Mouse handling
         ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
         ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
 #endif
 #endif
 
 
-        if (!OnUnGrabbingMouse (MouseGrabView))
+        if (!RaiseUnGrabbingMouseEvent (MouseGrabView))
         {
         {
             View view = MouseGrabView;
             View view = MouseGrabView;
             MouseGrabView = null;
             MouseGrabView = null;
-            OnUnGrabbedMouse (view);
+            RaiseUnGrabbedMouseEvent (view);
         }
         }
     }
     }
 
 
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
-    private static bool OnGrabbingMouse (View? view)
+    private static bool RaiseGrabbingMouseEvent (View? view)
     {
     {
         if (view is null)
         if (view is null)
         {
         {
@@ -81,7 +88,7 @@ public static partial class Application // Mouse handling
     }
     }
 
 
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
-    private static bool OnUnGrabbingMouse (View? view)
+    private static bool RaiseUnGrabbingMouseEvent (View? view)
     {
     {
         if (view is null)
         if (view is null)
         {
         {
@@ -95,7 +102,7 @@ public static partial class Application // Mouse handling
     }
     }
 
 
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
-    private static void OnGrabbedMouse (View? view)
+    private static void RaiseGrabbedMouseEvent (View? view)
     {
     {
         if (view is null)
         if (view is null)
         {
         {
@@ -106,7 +113,7 @@ public static partial class Application // Mouse handling
     }
     }
 
 
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
     /// <exception cref="Exception">A delegate callback throws an exception.</exception>
-    private static void OnUnGrabbedMouse (View? view)
+    private static void RaiseUnGrabbedMouseEvent (View? view)
     {
     {
         if (view is null)
         if (view is null)
         {
         {
@@ -116,40 +123,39 @@ public static partial class Application // Mouse handling
         UnGrabbedMouse?.Invoke (view, new (view));
         UnGrabbedMouse?.Invoke (view, new (view));
     }
     }
 
 
-    // Used by OnMouseEvent to track the last view that was clicked on.
-    internal static View? MouseEnteredView { get; set; }
-
-    /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         Use this event to receive mouse events in screen coordinates. Use <see cref="MouseEvent"/> to
-    ///         receive mouse events relative to a <see cref="View.Viewport"/>.
-    ///     </para>
-    ///     <para>The <see cref="MouseEvent.View"/> will contain the <see cref="View"/> that contains the mouse coordinates.</para>
-    /// </remarks>
-    public static event EventHandler<MouseEvent>? MouseEvent;
 
 
-    /// <summary>Called when a mouse event occurs. Raises the <see cref="MouseEvent"/> event.</summary>
+    /// <summary>
+    ///     INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
+    ///     calls the appropriate View mouse event handlers.
+    /// </summary>
     /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
     /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
     /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
     /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
-    internal static void OnMouseEvent (MouseEvent mouseEvent)
+    internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
     {
     {
+        _lastMousePosition = mouseEvent.ScreenPosition;
+
         if (IsMouseDisabled)
         if (IsMouseDisabled)
         {
         {
             return;
             return;
         }
         }
 
 
-        var view = View.FindDeepestView (Current, mouseEvent.Position);
+        // The position of the mouse is the same as the screen position at the application level.
+        //Debug.Assert (mouseEvent.Position == mouseEvent.ScreenPosition);
+        mouseEvent.Position = mouseEvent.ScreenPosition;
+
+        List<View?> currentViewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.ScreenPosition);
 
 
-        if (view is { })
+        View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault ();
+
+        if (deepestViewUnderMouse is { })
         {
         {
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
-            if (view.WasDisposed)
+            if (deepestViewUnderMouse.WasDisposed)
             {
             {
-                throw new ObjectDisposedException (view.GetType ().FullName);
+                throw new ObjectDisposedException (deepestViewUnderMouse.GetType ().FullName);
             }
             }
 #endif
 #endif
-            mouseEvent.View = view;
+            mouseEvent.View = deepestViewUnderMouse;
         }
         }
 
 
         MouseEvent?.Invoke (null, mouseEvent);
         MouseEvent?.Invoke (null, mouseEvent);
@@ -159,160 +165,216 @@ public static partial class Application // Mouse handling
             return;
             return;
         }
         }
 
 
-        if (MouseGrabView is { })
+        if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
         {
         {
-
-#if DEBUG_IDISPOSABLE
-            if (MouseGrabView.WasDisposed)
-            {
-                throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
-            }
-#endif
-            // If the mouse is grabbed, send the event to the view that grabbed it.
-            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
-            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
-
-            var viewRelativeMouseEvent = new MouseEvent
-            {
-                Position = frameLoc,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
-                View = view ?? MouseGrabView
-            };
-
-            if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
-            {
-                // The mouse has moved outside the bounds of the view that grabbed the mouse
-                MouseGrabView.NewMouseLeaveEvent (mouseEvent);
-            }
-
-            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
-            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
-            {
-                return;
-            }
-
-            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
-            if (MouseGrabView is null && view is Adornment)
-            {
-                // The view that grabbed the mouse has been disposed
-                return;
-            }
+            return;
         }
         }
 
 
-        // We can combine this into the switch expression to reduce cognitive complexity even more and likely
-        // avoid one or two of these checks in the process, as well.
-        WantContinuousButtonPressedView = view switch
-                                          {
-                                              { WantContinuousButtonPressed: true } => view,
-                                              _                                     => null
-                                          };
-
-        if (view is not Adornment
-         && (view is null || view == ApplicationOverlapped.OverlappedTop)
-         && Current is { Modal: false }
-         && ApplicationOverlapped.OverlappedTop != null
-         && mouseEvent.Flags is not MouseFlags.ReportMousePosition and not 0)
+        WantContinuousButtonPressedView = deepestViewUnderMouse switch
         {
         {
-            // This occurs when there are multiple overlapped "tops"
-            // E.g. "Mdi" - in the Background Worker Scenario
-            View? top = ApplicationOverlapped.FindDeepestTop (Top!, mouseEvent.Position);
-            view = View.FindDeepestView (top, mouseEvent.Position);
-
-            if (view is { } && view != ApplicationOverlapped.OverlappedTop && top != Current && top is { })
-            {
-                ApplicationOverlapped.MoveCurrent ((Toplevel)top);
-            }
-        }
+            { WantContinuousButtonPressed: true } => deepestViewUnderMouse,
+            _ => null
+        };
 
 
         // May be null before the prior condition or the condition may set it as null.
         // May be null before the prior condition or the condition may set it as null.
         // So, the checking must be outside the prior condition.
         // So, the checking must be outside the prior condition.
-        if (view is null)
+        if (deepestViewUnderMouse is null)
         {
         {
             return;
             return;
         }
         }
 
 
-        MouseEvent? me;
+        // Create a view-relative mouse event to send to the view that is under the mouse.
+        MouseEventArgs? viewMouseEvent;
 
 
-        if (view is Adornment adornment)
+        if (deepestViewUnderMouse is Adornment adornment)
         {
         {
-            Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
+            Point frameLoc = adornment.ScreenToFrame (mouseEvent.ScreenPosition);
 
 
-            me = new ()
+            viewMouseEvent = new ()
             {
             {
                 Position = frameLoc,
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
-                View = view
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
             };
             };
         }
         }
-        else if (view.ViewportToScreen (Rectangle.Empty with { Size = view.Viewport.Size }).Contains (mouseEvent.Position))
+        else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.ScreenPosition))
         {
         {
-            Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
+            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
 
 
-            me = new ()
+            viewMouseEvent = new ()
             {
             {
                 Position = viewportLocation,
                 Position = viewportLocation,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
-                View = view
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
             };
             };
         }
         }
         else
         else
         {
         {
-            return;
-        }
+            // The mouse was outside any View's Viewport.
 
 
-        if (MouseEnteredView is null)
-        {
-            MouseEnteredView = view;
-            view.NewMouseEnterEvent (me);
-        }
-        else if (MouseEnteredView != view)
-        {
-            MouseEnteredView.NewMouseLeaveEvent (me);
-            view.NewMouseEnterEvent (me);
-            MouseEnteredView = view;
-        }
+            // Debug.Fail ("This should never happen. If it does please file an Issue!!");
 
 
-        if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
-        {
             return;
             return;
         }
         }
 
 
-        WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+        RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse);
 
 
-        //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
+        WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
 
 
-        while (view.NewMouseEvent (me) is not true && MouseGrabView is not { })
+        while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabView is not { })
         {
         {
-            if (view is Adornment adornmentView)
+            if (deepestViewUnderMouse is Adornment adornmentView)
             {
             {
-                view = adornmentView.Parent!.SuperView;
+                deepestViewUnderMouse = adornmentView.Parent!.SuperView;
             }
             }
             else
             else
             {
             {
-                view = view.SuperView;
+                deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
             }
             }
 
 
-            if (view is null)
+            if (deepestViewUnderMouse is null)
             {
             {
                 break;
                 break;
             }
             }
 
 
-            Point boundsPoint = view.ScreenToViewport (mouseEvent.Position);
+            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
 
 
-            me = new ()
+            viewMouseEvent = new ()
             {
             {
                 Position = boundsPoint,
                 Position = boundsPoint,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
-                View = view
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
+            };
+        }
+    }
+
+
+#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
+    /// <summary>
+    /// Raised when a mouse event occurs. Can be cancelled by setting <see cref="MouseEventArgs.Handled"/> to <see langword="true"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.ScreenPosition"/> coordinates are screen-relative.
+    ///     </para>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.View"/> will be the deepest view under the under the mouse.
+    ///     </para>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.Position"/> coordinates are view-relative. Only valid if <see cref="MouseEventArgs.View"/> is set.
+    ///     </para>
+    ///     <para>
+    ///         Use this evento to handle mouse events at the application level, before View-specific handling.
+    ///     </para>
+    /// </remarks>
+    public static event EventHandler<MouseEventArgs>? MouseEvent;
+#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
+
+    internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
+    {
+        if (MouseGrabView is { })
+        {
+#if DEBUG_IDISPOSABLE
+            if (MouseGrabView.WasDisposed)
+            {
+                throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
+            }
+#endif
+
+            // If the mouse is grabbed, send the event to the view that grabbed it.
+            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
+            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
+
+            var viewRelativeMouseEvent = new MouseEventArgs
+            {
+                Position = frameLoc,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse ?? MouseGrabView
             };
             };
+
+            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
+            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+            {
+                return true;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
+            {
+                // The view that grabbed the mouse has been disposed
+                return true;
+            }
         }
         }
 
 
-        ApplicationOverlapped.BringOverlappedTopToFront ();
+        return false;
     }
     }
 
 
-    #endregion Mouse handling
+    internal static readonly List<View?> _cachedViewsUnderMouse = new ();
+
+    /// <summary>
+    ///     INTERNAL: Raises the MouseEnter and MouseLeave events for the views that are under the mouse.
+    /// </summary>
+    /// <param name="screenPosition">The position of the mouse.</param>
+    /// <param name="currentViewsUnderMouse">The most recent result from GetViewsUnderMouse().</param>
+    internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse)
+    {
+        // Tell any views that are no longer under the mouse that the mouse has left
+        List<View?> viewsToLeave = _cachedViewsUnderMouse.Where (v => v is { } && !currentViewsUnderMouse.Contains (v)).ToList ();
+
+        foreach (View? view in viewsToLeave)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            view.NewMouseLeaveEvent ();
+            _cachedViewsUnderMouse.Remove (view);
+        }
+
+        // Tell any views that are now under the mouse that the mouse has entered and add them to the list
+        foreach (View? view in currentViewsUnderMouse)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            if (_cachedViewsUnderMouse.Contains (view))
+            {
+                continue;
+            }
+
+            _cachedViewsUnderMouse.Add (view);
+            var raise = false;
+
+            if (view is Adornment { Parent: { } } adornmentView)
+            {
+                Point superViewLoc = adornmentView.Parent.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = adornmentView.Contains (superViewLoc);
+            }
+            else
+            {
+                Point superViewLoc = view.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = view.Contains (superViewLoc);
+            }
+
+            if (!raise)
+            {
+                continue;
+            }
+
+            CancelEventArgs eventArgs = new ();
+            bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+            if (cancelled is true || eventArgs.Cancel)
+            {
+                break;
+            }
+        }
+    }
 }
 }

+ 79 - 0
Terminal.Gui/Application/Application.Navigation.cs

@@ -7,4 +7,83 @@ public static partial class Application // Navigation stuff
     ///     Gets the <see cref="ApplicationNavigation"/> instance for the current <see cref="Application"/>.
     ///     Gets the <see cref="ApplicationNavigation"/> instance for the current <see cref="Application"/>.
     /// </summary>
     /// </summary>
     public static ApplicationNavigation? Navigation { get; internal set; }
     public static ApplicationNavigation? Navigation { get; internal set; }
+
+    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrides
+    private static Key _nextTabKey = Key.Tab; // Resources/config.json overrides
+    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrides
+    private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrides
+
+    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key NextTabGroupKey
+    {
+        get => _nextTabGroupKey;
+        set
+        {
+            if (_nextTabGroupKey != value)
+            {
+                ReplaceKey (_nextTabGroupKey, value);
+                _nextTabGroupKey = value;
+            }
+        }
+    }
+
+    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key NextTabKey
+    {
+        get => _nextTabKey;
+        set
+        {
+            if (_nextTabKey != value)
+            {
+                ReplaceKey (_nextTabKey, value);
+                _nextTabKey = value;
+            }
+        }
+    }
+
+
+    /// <summary>
+    ///     Raised when the user releases a key.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key>? KeyUp;
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key PrevTabGroupKey
+    {
+        get => _prevTabGroupKey;
+        set
+        {
+            if (_prevTabGroupKey != value)
+            {
+                ReplaceKey (_prevTabGroupKey, value);
+                _prevTabGroupKey = value;
+            }
+        }
+    }
+
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key PrevTabKey
+    {
+        get => _prevTabKey;
+        set
+        {
+            if (_prevTabKey != value)
+            {
+                ReplaceKey (_prevTabKey, value);
+                _prevTabKey = value;
+            }
+        }
+    }
 }
 }

+ 93 - 307
Terminal.Gui/Application/Application.Run.cs

@@ -6,6 +6,41 @@ namespace Terminal.Gui;
 
 
 public static partial class Application // Run (Begin, Run, End, Stop)
 public static partial class Application // Run (Begin, Run, End, Stop)
 {
 {
+    private static Key _quitKey = Key.Esc; // Resources/config.json overrides
+
+    /// <summary>Gets or sets the key to quit the application.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key QuitKey
+    {
+        get => _quitKey;
+        set
+        {
+            if (_quitKey != value)
+            {
+                ReplaceKey (_quitKey, value);
+                _quitKey = value;
+            }
+        }
+    }
+
+    private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides
+
+
+    /// <summary>Gets or sets the key to activate arranging views using the keyboard.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key ArrangeKey
+    {
+        get => _arrangeKey;
+        set
+        {
+            if (_arrangeKey != value)
+            {
+                ReplaceKey (_arrangeKey, value);
+                _arrangeKey = value;
+            }
+        }
+    }
+
     // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`.
     // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`.
     // This variable is set in `End` in this case so that `Begin` correctly sets `Top`.
     // This variable is set in `End` in this case so that `Begin` correctly sets `Top`.
     private static Toplevel? _cachedRunStateToplevel;
     private static Toplevel? _cachedRunStateToplevel;
@@ -45,19 +80,14 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     {
     {
         ArgumentNullException.ThrowIfNull (toplevel);
         ArgumentNullException.ThrowIfNull (toplevel);
 
 
-#if DEBUG_IDISPOSABLE
-        Debug.Assert (!toplevel.WasDisposed);
+//#if DEBUG_IDISPOSABLE
+//        Debug.Assert (!toplevel.WasDisposed);
 
 
-        if (_cachedRunStateToplevel is { } && _cachedRunStateToplevel != toplevel)
-        {
-            Debug.Assert (_cachedRunStateToplevel.WasDisposed);
-        }
-#endif
-
-        if (toplevel.IsOverlappedContainer && ApplicationOverlapped.OverlappedTop != toplevel && ApplicationOverlapped.OverlappedTop is { })
-        {
-            throw new InvalidOperationException ("Only one Overlapped Container is allowed.");
-        }
+//        if (_cachedRunStateToplevel is { } && _cachedRunStateToplevel != toplevel)
+//        {
+//            Debug.Assert (_cachedRunStateToplevel.WasDisposed);
+//        }
+//#endif
 
 
         // Ensure the mouse is ungrabbed.
         // Ensure the mouse is ungrabbed.
         MouseGrabView = null;
         MouseGrabView = null;
@@ -96,11 +126,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
                     throw new ObjectDisposedException (Top.GetType ().FullName);
                     throw new ObjectDisposedException (Top.GetType ().FullName);
                 }
                 }
             }
             }
-            else if (ApplicationOverlapped.OverlappedTop is { } && toplevel != Top && TopLevels.Contains (Top!))
-            {
-                // BUGBUG: Don't call OnLeave/OnEnter directly! Set HasFocus to false and let the system handle it.
-                //Top!.OnLeave (toplevel);
-            }
 
 
             // BUGBUG: We should not depend on `Id` internally.
             // BUGBUG: We should not depend on `Id` internally.
             // BUGBUG: It is super unclear what this code does anyway.
             // BUGBUG: It is super unclear what this code does anyway.
@@ -135,79 +160,46 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             }
             }
         }
         }
 
 
-        if (Top is null || toplevel.IsOverlappedContainer)
+        if (Top is null)
         {
         {
             Top = toplevel;
             Top = toplevel;
         }
         }
 
 
-        var refreshDriver = true;
-
-        if (ApplicationOverlapped.OverlappedTop is null
-            || toplevel.IsOverlappedContainer
-            || (Current?.Modal == false && toplevel.Modal)
-            || (Current?.Modal == false && !toplevel.Modal)
-            || (Current?.Modal == true && toplevel.Modal))
+        if ((Top?.Modal == false && toplevel.Modal)
+            || (Top?.Modal == false && !toplevel.Modal)
+            || (Top?.Modal == true && toplevel.Modal))
         {
         {
             if (toplevel.Visible)
             if (toplevel.Visible)
             {
             {
-                if (Current is { HasFocus: true })
+                if (Top is { HasFocus: true })
                 {
                 {
-                    Current.HasFocus = false;
+                    Top.HasFocus = false;
                 }
                 }
 
 
-                Current?.OnDeactivate (toplevel);
-                Toplevel previousCurrent = Current!;
+                Top?.OnDeactivate (toplevel);
+                Toplevel previousCurrent = Top!;
 
 
-                Current = toplevel;
-                Current.OnActivate (previousCurrent);
-
-                ApplicationOverlapped.SetCurrentOverlappedAsTop ();
-            }
-            else
-            {
-                refreshDriver = false;
+                Top = toplevel;
+                Top.OnActivate (previousCurrent);
             }
             }
         }
         }
-        else if ((toplevel != ApplicationOverlapped.OverlappedTop
-                  && Current?.Modal == true
-                  && !TopLevels.Peek ().Modal)
-                 || (toplevel != ApplicationOverlapped.OverlappedTop && Current?.Running == false))
-        {
-            refreshDriver = false;
-            ApplicationOverlapped.MoveCurrent (toplevel);
-        }
-        else
-        {
-            refreshDriver = false;
-            ApplicationOverlapped.MoveCurrent (Current!);
-        }
 
 
         toplevel.SetRelativeLayout (Driver!.Screen.Size);
         toplevel.SetRelativeLayout (Driver!.Screen.Size);
-
         toplevel.LayoutSubviews ();
         toplevel.LayoutSubviews ();
-        toplevel.PositionToplevels ();
 
 
-        // TODO: Should this use FindDeepestFocusableView instead?
         // Try to set initial focus to any TabStop
         // Try to set initial focus to any TabStop
         if (!toplevel.HasFocus)
         if (!toplevel.HasFocus)
         {
         {
             toplevel.SetFocus ();
             toplevel.SetFocus ();
         }
         }
 
 
-        ApplicationOverlapped.BringOverlappedTopToFront ();
+        toplevel.OnLoaded ();
 
 
-        if (refreshDriver)
-        {
-            ApplicationOverlapped.OverlappedTop?.OnChildLoaded (toplevel);
-            toplevel.OnLoaded ();
-            toplevel.SetNeedsDisplay ();
-            toplevel.Draw ();
-            Driver.UpdateScreen ();
+        Refresh ();
 
 
-            if (PositionCursor (toplevel))
-            {
-                Driver.UpdateCursor ();
-            }
+        if (PositionCursor ())
+        {
+            Driver.UpdateCursor ();
         }
         }
 
 
         NotifyNewRunState?.Invoke (toplevel, new (rs));
         NotifyNewRunState?.Invoke (toplevel, new (rs));
@@ -216,35 +208,22 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Calls <see cref="View.PositionCursor"/> on the most focused view in the view starting with <paramref name="view"/>.
+    ///     Calls <see cref="View.PositionCursor"/> on the most focused view.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    ///     Does nothing if <paramref name="view"/> is <see langword="null"/> or if the most focused view is not visible or
-    ///     enabled.
+    ///     Does nothing if there is no most focused view.
     ///     <para>
     ///     <para>
     ///         If the most focused view is not visible within it's superview, the cursor will be hidden.
     ///         If the most focused view is not visible within it's superview, the cursor will be hidden.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     /// <returns><see langword="true"/> if a view positioned the cursor and the position is visible.</returns>
     /// <returns><see langword="true"/> if a view positioned the cursor and the position is visible.</returns>
-    internal static bool PositionCursor (View view)
+    internal static bool PositionCursor ()
     {
     {
         // Find the most focused view and position the cursor there.
         // Find the most focused view and position the cursor there.
-        View? mostFocused = view?.MostFocused;
-
-        if (mostFocused is null)
-        {
-            if (view is { HasFocus: true })
-            {
-                mostFocused = view;
-            }
-            else
-            {
-                return false;
-            }
-        }
+        View? mostFocused = Navigation?.GetFocused ();
 
 
         // If the view is not visible or enabled, don't position the cursor
         // If the view is not visible or enabled, don't position the cursor
-        if (!mostFocused.Visible || !mostFocused.Enabled)
+        if (mostFocused is null || !mostFocused.Visible || !mostFocused.Enabled)
         {
         {
             Driver!.GetCursorVisibility (out CursorVisibility current);
             Driver!.GetCursorVisibility (out CursorVisibility current);
 
 
@@ -510,20 +489,17 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <summary>Triggers a refresh of the entire display.</summary>
     /// <summary>Triggers a refresh of the entire display.</summary>
     public static void Refresh ()
     public static void Refresh ()
     {
     {
-        // TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear
-        Driver!.ClearContents ();
-
-        foreach (Toplevel v in TopLevels.Reverse ())
+        foreach (Toplevel tl in TopLevels.Reverse ())
         {
         {
-            if (v.Visible)
+            if (tl.LayoutNeeded)
             {
             {
-                v.SetNeedsDisplay ();
-                v.SetSubViewNeedsDisplay ();
-                v.Draw ();
+                tl.LayoutSubviews ();
             }
             }
+
+            tl.Draw ();
         }
         }
 
 
-        Driver.Refresh ();
+        Driver!.Refresh ();
     }
     }
 
 
     /// <summary>This event is raised on each iteration of the main loop.</summary>
     /// <summary>This event is raised on each iteration of the main loop.</summary>
@@ -586,80 +562,22 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
 
             MainLoop.RunIteration ();
             MainLoop.RunIteration ();
             Iteration?.Invoke (null, new ());
             Iteration?.Invoke (null, new ());
-            EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
-
-            // TODO: Overlapped - Move elsewhere
-            if (state.Toplevel != Current)
-            {
-                ApplicationOverlapped.OverlappedTop?.OnDeactivate (state.Toplevel);
-                state.Toplevel = Current;
-                ApplicationOverlapped.OverlappedTop?.OnActivate (state.Toplevel!);
-                Top!.SetSubViewNeedsDisplay ();
-                Refresh ();
-            }
         }
         }
 
 
         firstIteration = false;
         firstIteration = false;
 
 
-        if (Current == null)
+        if (Top is null)
         {
         {
             return;
             return;
         }
         }
 
 
-        if (state.Toplevel != Top && (Top!.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
-        {
-            state.Toplevel!.SetNeedsDisplay (state.Toplevel.Frame);
-            Top.Draw ();
-
-            foreach (Toplevel top in TopLevels.Reverse ())
-            {
-                if (top != Top && top != state.Toplevel)
-                {
-                    top.SetNeedsDisplay ();
-                    top.SetSubViewNeedsDisplay ();
-                    top.Draw ();
-                }
-            }
-        }
+        Refresh ();
 
 
-        if (TopLevels.Count == 1
-            && state.Toplevel == Top
-            && (Driver!.Cols != state.Toplevel!.Frame.Width
-                || Driver!.Rows != state.Toplevel.Frame.Height)
-            && (state.Toplevel.NeedsDisplay
-                || state.Toplevel.SubViewNeedsDisplay
-                || state.Toplevel.LayoutNeeded))
-        {
-            Driver.ClearContents ();
-        }
-
-        if (state.Toplevel!.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || ApplicationOverlapped.OverlappedChildNeedsDisplay ())
-        {
-            state.Toplevel.SetNeedsDisplay ();
-            state.Toplevel.Draw ();
-            Driver!.UpdateScreen ();
-
-            //Driver.UpdateCursor ();
-        }
-
-        if (PositionCursor (state.Toplevel))
+        if (PositionCursor ())
         {
         {
             Driver!.UpdateCursor ();
             Driver!.UpdateCursor ();
         }
         }
 
 
-        //        else
-        {
-            //if (PositionCursor (state.Toplevel))
-            //{
-            //    Driver.Refresh ();
-            //}
-            //Driver.UpdateCursor ();
-        }
-
-        if (state.Toplevel != Top && !state.Toplevel.Modal && (Top!.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
-        {
-            Top.Draw ();
-        }
     }
     }
 
 
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
@@ -673,112 +591,26 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// </remarks>
     /// </remarks>
     public static void RequestStop (Toplevel? top = null)
     public static void RequestStop (Toplevel? top = null)
     {
     {
-        if (ApplicationOverlapped.OverlappedTop is null || top is null)
+        if (top is null)
         {
         {
-            top = Current;
+            top = Top;
         }
         }
 
 
-        if (ApplicationOverlapped.OverlappedTop != null
-            && top!.IsOverlappedContainer
-            && top?.Running == true
-            && (Current?.Modal == false || Current is { Modal: true, Running: false }))
+        if (!top!.Running)
         {
         {
-            ApplicationOverlapped.OverlappedTop.RequestStop ();
+            return;
         }
         }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && top != Current
-                 && Current is { Running: true, Modal: true }
-                 && top!.Modal
-                 && top.Running)
-        {
-            var ev = new ToplevelClosingEventArgs (Current);
-            Current.OnClosing (ev);
-
-            if (ev.Cancel)
-            {
-                return;
-            }
-
-            ev = new (top);
-            top.OnClosing (ev);
 
 
-            if (ev.Cancel)
-            {
-                return;
-            }
+        var ev = new ToplevelClosingEventArgs (top);
+        top.OnClosing (ev);
 
 
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
-            top.Running = false;
-            OnNotifyStopRunState (top);
-        }
-        else if ((ApplicationOverlapped.OverlappedTop != null
-                  && top != ApplicationOverlapped.OverlappedTop
-                  && top != Current
-                  && Current is { Modal: false, Running: true }
-                  && !top!.Running)
-                 || (ApplicationOverlapped.OverlappedTop != null
-                     && top != ApplicationOverlapped.OverlappedTop
-                     && top != Current
-                     && Current is { Modal: false, Running: false }
-                     && !top!.Running
-                     && TopLevels.ToArray () [1].Running))
-        {
-            ApplicationOverlapped.MoveCurrent (top);
-        }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && Current != top
-                 && Current?.Running == true
-                 && !top!.Running
-                 && Current?.Modal == true
-                 && top.Modal)
+        if (ev.Cancel)
         {
         {
-            // The Current and the top are both modal so needed to set the Current.Running to false too.
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
-        }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && Current == top
-                 && ApplicationOverlapped.OverlappedTop?.Running == true
-                 && Current?.Running == true
-                 && top!.Running
-                 && Current?.Modal == true
-                 && top!.Modal)
-        {
-            // The OverlappedTop was requested to stop inside a modal Toplevel which is the Current and top,
-            // both are the same, so needed to set the Current.Running to false too.
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
+            return;
         }
         }
-        else
-        {
-            Toplevel currentTop;
 
 
-            if (top == Current || (Current?.Modal == true && !top!.Modal))
-            {
-                currentTop = Current!;
-            }
-            else
-            {
-                currentTop = top!;
-            }
-
-            if (!currentTop.Running)
-            {
-                return;
-            }
-
-            var ev = new ToplevelClosingEventArgs (currentTop);
-            currentTop.OnClosing (ev);
-
-            if (ev.Cancel)
-            {
-                return;
-            }
-
-            currentTop.Running = false;
-            OnNotifyStopRunState (currentTop);
-        }
+        top.Running = false;
+        OnNotifyStopRunState (top);
     }
     }
 
 
     private static void OnNotifyStopRunState (Toplevel top)
     private static void OnNotifyStopRunState (Toplevel top)
@@ -798,14 +630,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     {
     {
         ArgumentNullException.ThrowIfNull (runState);
         ArgumentNullException.ThrowIfNull (runState);
 
 
-        if (ApplicationOverlapped.OverlappedTop is { })
-        {
-            ApplicationOverlapped.OverlappedTop.OnChildUnloaded (runState.Toplevel);
-        }
-        else
-        {
-            runState.Toplevel.OnUnloaded ();
-        }
+        runState.Toplevel.OnUnloaded ();
 
 
         // End the RunState.Toplevel
         // End the RunState.Toplevel
         // First, take it off the Toplevel Stack
         // First, take it off the Toplevel Stack
@@ -824,66 +649,27 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         // Notify that it is closing
         // Notify that it is closing
         runState.Toplevel?.OnClosed (runState.Toplevel);
         runState.Toplevel?.OnClosed (runState.Toplevel);
 
 
-        // If there is a OverlappedTop that is not the RunState.Toplevel then RunState.Toplevel
-        // is a child of MidTop, and we should notify the OverlappedTop that it is closing
-        if (ApplicationOverlapped.OverlappedTop is { } && !runState.Toplevel!.Modal && runState.Toplevel != ApplicationOverlapped.OverlappedTop)
+        if (TopLevels.Count > 0)
         {
         {
-            ApplicationOverlapped.OverlappedTop.OnChildClosed (runState.Toplevel);
+            Top = TopLevels.Peek ();
+            Top.SetNeedsDisplay ();
         }
         }
 
 
-        // Set Current and Top to the next TopLevel on the stack
-        if (TopLevels.Count == 0)
+        if (runState.Toplevel is { HasFocus: true })
         {
         {
-            if (Current is { HasFocus: true })
-            {
-                Current.HasFocus = false;
-            }
-            Current = null;
-        }
-        else
-        {
-            if (TopLevels.Count > 1 && TopLevels.Peek () == ApplicationOverlapped.OverlappedTop && ApplicationOverlapped.OverlappedChildren?.Any (t => t.Visible) != null)
-            {
-                ApplicationOverlapped.OverlappedMoveNext ();
-            }
-
-            Current = TopLevels.Peek ();
-
-            if (TopLevels.Count == 1 && Current == ApplicationOverlapped.OverlappedTop)
-            {
-                ApplicationOverlapped.OverlappedTop.OnAllChildClosed ();
-            }
-            else
-            {
-                ApplicationOverlapped.SetCurrentOverlappedAsTop ();
-                // BUGBUG: We should not call OnEnter/OnLeave directly; they should only be called by SetHasFocus
-                if (runState.Toplevel is { HasFocus: true })
-                {
-                    runState.Toplevel.HasFocus = false;
-                }
-
-                if (Current is { HasFocus: false })
-                {
-                    Current.SetFocus ();
-                }
-            }
-
-            Refresh ();
+            runState.Toplevel.HasFocus = false;
         }
         }
 
 
-        // Don't dispose runState.Toplevel. It's up to caller dispose it
-        // If it's not the same as the current in the RunIteration,
-        // it will be fixed later in the next RunIteration.
-        if (ApplicationOverlapped.OverlappedTop is { } && !TopLevels.Contains (ApplicationOverlapped.OverlappedTop))
-        {
-            _cachedRunStateToplevel = ApplicationOverlapped.OverlappedTop;
-        }
-        else
+        if (Top is { HasFocus: false })
         {
         {
-            _cachedRunStateToplevel = runState.Toplevel;
+            Top.SetFocus ();
         }
         }
 
 
+        _cachedRunStateToplevel = runState.Toplevel;
+
         runState.Toplevel = null;
         runState.Toplevel = null;
         runState.Dispose ();
         runState.Dispose ();
+
+        Refresh ();
     }
     }
 }
 }

+ 0 - 6
Terminal.Gui/Application/Application.Screen.cs

@@ -37,13 +37,7 @@ public static partial class Application // Screen related stuff
         {
         {
             t.SetRelativeLayout (args.Size.Value);
             t.SetRelativeLayout (args.Size.Value);
             t.LayoutSubviews ();
             t.LayoutSubviews ();
-            t.PositionToplevels ();
             t.OnSizeChanging (new (args.Size));
             t.OnSizeChanging (new (args.Size));
-
-            if (PositionCursor (t))
-            {
-                Driver?.UpdateCursor ();
-            }
         }
         }
 
 
         Refresh ();
         Refresh ();

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

@@ -8,49 +8,7 @@ public static partial class Application // Toplevel handling
     /// <summary>Holds the stack of TopLevel views.</summary>
     /// <summary>Holds the stack of TopLevel views.</summary>
     internal static Stack<Toplevel> TopLevels { get; } = new ();
     internal static Stack<Toplevel> TopLevels { get; } = new ();
 
 
-    /// <summary>The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Top"/>)</summary>
+    /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <value>The top.</value>
     /// <value>The top.</value>
     public static Toplevel? Top { get; internal set; }
     public static Toplevel? Top { get; internal set; }
-
-    // TODO: Determine why this can't just return _topLevels.Peek()?
-    /// <summary>
-    ///     The current <see cref="Toplevel"/> object. This is updated in <see cref="Application.Begin"/> enters and leaves to
-    ///     point to the current
-    ///     <see cref="Toplevel"/> .
-    /// </summary>
-    /// <remarks>
-    ///     This will only be distinct from <see cref="Application.Top"/> in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
-    /// </remarks>
-    /// <value>The current.</value>
-    public static Toplevel? Current { get; internal set; }
-
-    /// <summary>
-    ///     If <paramref name="topLevel"/> is not already Current and visible, finds the last Modal Toplevel in the stack and makes it Current.
-    /// </summary>
-    private static void EnsureModalOrVisibleAlwaysOnTop (Toplevel topLevel)
-    {
-        if (!topLevel.Running
-            || (topLevel == Current && topLevel.Visible)
-            || ApplicationOverlapped.OverlappedTop == null
-            || TopLevels.Peek ().Modal)
-        {
-            return;
-        }
-
-        foreach (Toplevel top in TopLevels.Reverse ())
-        {
-            if (top.Modal && top != Current)
-            {
-                ApplicationOverlapped.MoveCurrent (top);
-
-                return;
-            }
-        }
-
-        if (!topLevel.Visible && topLevel == Current)
-        {
-            ApplicationOverlapped.OverlappedMoveNext ();
-        }
-    }
-
 }
 }

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

@@ -149,7 +149,6 @@ public static partial class Application
         }
         }
 
 
         TopLevels.Clear ();
         TopLevels.Clear ();
-        Current = null;
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
 
 
         // Don't dispose the Top. It's up to caller dispose it
         // Don't dispose the Top. It's up to caller dispose it
@@ -198,7 +197,8 @@ public static partial class Application
         IsInitialized = false;
         IsInitialized = false;
 
 
         // Mouse
         // Mouse
-        MouseEnteredView = null;
+        _lastMousePosition = null;
+        _cachedViewsUnderMouse.Clear ();
         WantContinuousButtonPressedView = null;
         WantContinuousButtonPressedView = null;
         MouseEvent = null;
         MouseEvent = null;
         GrabbedMouse = null;
         GrabbedMouse = null;
@@ -215,8 +215,6 @@ public static partial class Application
 
 
         AddApplicationKeyBindings ();
         AddApplicationKeyBindings ();
 
 
-        Colors.Reset ();
-
         // Reset synchronization context to allow the user to run async/await,
         // Reset synchronization context to allow the user to run async/await,
         // as the main loop has been ended, the synchronization context from
         // as the main loop has been ended, the synchronization context from
         // gui.cs does no longer process any callbacks. See #1084 for more details:
         // gui.cs does no longer process any callbacks. See #1084 for more details:

+ 17 - 2
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -1,5 +1,7 @@
 #nullable enable
 #nullable enable
 
 
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
@@ -25,7 +27,19 @@ public class ApplicationNavigation
     /// <summary>
     /// <summary>
     ///     Gets the most focused <see cref="View"/> in the application, if there is one.
     ///     Gets the most focused <see cref="View"/> in the application, if there is one.
     /// </summary>
     /// </summary>
-    public View? GetFocused () { return _focused; }
+    public View? GetFocused ()
+    {
+        return _focused;
+
+        if (_focused is { CanFocus: true, HasFocus: true })
+        {
+            return _focused;
+        }
+
+        _focused = null;
+
+        return null;
+    }
 
 
     /// <summary>
     /// <summary>
     ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
     ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
@@ -75,6 +89,7 @@ public class ApplicationNavigation
         {
         {
             return;
             return;
         }
         }
+        Debug.Assert (value is null or { CanFocus: true, HasFocus: true });
 
 
         _focused = value;
         _focused = value;
 
 
@@ -98,6 +113,6 @@ public class ApplicationNavigation
     /// </returns>
     /// </returns>
     public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
     public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
     {
     {
-        return Application.Current is { } && Application.Current.AdvanceFocus (direction, behavior);
+        return Application.Top is { } && Application.Top.AdvanceFocus (direction, behavior);
     }
     }
 }
 }

+ 0 - 444
Terminal.Gui/Application/ApplicationOverlapped.cs

@@ -1,444 +0,0 @@
-#nullable enable
-using System.Diagnostics;
-using System.Reflection;
-
-namespace Terminal.Gui;
-
-/// <summary>
-/// Helper class for managing overlapped views in the application.
-/// </summary>
-public static class ApplicationOverlapped
-{
-
-    /// <summary>
-    ///     Gets or sets if <paramref name="top"/> is in overlapped mode within a Toplevel container.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    public static bool IsOverlapped (Toplevel? top)
-    {
-        return ApplicationOverlapped.OverlappedTop is { } && ApplicationOverlapped.OverlappedTop != top && !top!.Modal;
-    }
-
-    /// <summary>
-    ///     Gets the list of the Overlapped children which are not modal <see cref="Toplevel"/> from the
-    ///     <see cref="OverlappedTop"/>.
-    /// </summary>
-    public static List<Toplevel>? OverlappedChildren
-    {
-        get
-        {
-            if (OverlappedTop is { })
-            {
-                List<Toplevel> overlappedChildren = new ();
-
-                lock (Application.TopLevels)
-                {
-                    foreach (Toplevel top in Application.TopLevels)
-                    {
-                        if (top != OverlappedTop && !top.Modal)
-                        {
-                            overlappedChildren.Add (top);
-                        }
-                    }
-                }
-
-                return overlappedChildren;
-            }
-
-            return null;
-        }
-    }
-
-    /// <summary>
-    ///     The <see cref="Toplevel"/> object used for the application on startup which
-    ///     <see cref="Toplevel.IsOverlappedContainer"/> is true.
-    /// </summary>
-    public static Toplevel? OverlappedTop
-    {
-        get
-        {
-            if (Application.Top is { IsOverlappedContainer: true })
-            {
-                return Application.Top;
-            }
-
-            return null;
-        }
-    }
-
-    /// <summary>Brings the superview of the most focused overlapped view is on front.</summary>
-    public static void BringOverlappedTopToFront ()
-    {
-        if (OverlappedTop is { })
-        {
-            return;
-        }
-
-        View? top = FindTopFromView (Application.Top?.MostFocused);
-
-        if (top is Toplevel && Application.Top?.Subviews.Count > 1 && Application.Top.Subviews [^1] != top)
-        {
-            Application.Top.MoveSubviewToStart (top);
-        }
-    }
-
-    /// <summary>Gets the current visible Toplevel overlapped child that matches the arguments pattern.</summary>
-    /// <param name="type">The type.</param>
-    /// <param name="exclude">The strings to exclude.</param>
-    /// <returns>The matched view.</returns>
-    public static Toplevel? GetTopOverlappedChild (Type? type = null, string []? exclude = null)
-    {
-        if (OverlappedChildren is null || OverlappedTop is null)
-        {
-            return null;
-        }
-
-        foreach (Toplevel top in OverlappedChildren)
-        {
-            if (type is { } && top.GetType () == type && exclude?.Contains (top.Data?.ToString ()) == false)
-            {
-                return top;
-            }
-
-            if ((type is { } && top.GetType () != type) || exclude?.Contains (top.Data?.ToString ()) == true)
-            {
-                continue;
-            }
-
-            return top;
-        }
-
-        return null;
-    }
-
-
-    /// <summary>
-    /// Sets the focus to the next view in the specified direction within the provided list of views.
-    /// If the end of the list is reached, the focus wraps around to the first view in the list.
-    /// The method considers the current focused view (`Application.Current`) and attempts to move the focus
-    /// to the next view in the specified direction. If the focus cannot be set to the next view, it wraps around
-    /// to the first view in the list.
-    /// </summary>
-    /// <param name="viewsInTabIndexes"></param>
-    /// <param name="direction"></param>
-    internal static void SetFocusToNextViewWithWrap (IEnumerable<View>? viewsInTabIndexes, NavigationDirection direction)
-    {
-        if (viewsInTabIndexes is null)
-        {
-            return;
-        }
-
-        // This code-path only executes in obtuse IsOverlappedContainer scenarios.
-        Debug.Assert (Application.Current!.IsOverlappedContainer);
-
-        bool foundCurrentView = false;
-        bool focusSet = false;
-        IEnumerable<View> indexes = viewsInTabIndexes as View [] ?? viewsInTabIndexes.ToArray ();
-        int viewCount = indexes.Count ();
-        int currentIndex = 0;
-
-        foreach (View view in indexes)
-        {
-            if (view == Application.Current)
-            {
-                foundCurrentView = true;
-            }
-            else if (foundCurrentView && !focusSet)
-            {
-                // One of the views is Current, but view is not. Attempt to Advance...
-                Application.Current!.SuperView?.AdvanceFocus (direction, null);
-                // QUESTION: AdvanceFocus returns false AND sets Focused to null if no view was found to advance to. Should't we only set focusProcessed if it returned true?
-                focusSet = true;
-
-                if (Application.Current.SuperView?.Focused != Application.Current)
-                {
-                    return;
-                }
-
-                // Either AdvanceFocus didn't set focus or the view it set focus to is not current...
-                // continue...
-            }
-
-            currentIndex++;
-
-            if (foundCurrentView && !focusSet && currentIndex == viewCount)
-            {
-                // One of the views is Current AND AdvanceFocus didn't set focus AND we are at the last view in the list...
-                // This means we should wrap around to the first view in the list.
-                indexes.First ().SetFocus ();
-            }
-        }
-    }
-
-    /// <summary>
-    ///     Move to the next Overlapped child from the <see cref="OverlappedTop"/> and set it as the <see cref="Application.Top"/> if
-    ///     it is not already.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    public static bool MoveToOverlappedChild (Toplevel? top)
-    {
-        ArgumentNullException.ThrowIfNull (top);
-
-        if (top.Visible && OverlappedTop is { } && Application.Current?.Modal == false)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
-                Application.Current = top;
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>Move to the next Overlapped child from the <see cref="OverlappedTop"/>.</summary>
-    public static void OverlappedMoveNext ()
-    {
-        if (OverlappedTop is { } && !Application.Current!.Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveNext ();
-                var isOverlapped = false;
-
-                while (Application.TopLevels.Peek () == OverlappedTop || !Application.TopLevels.Peek ().Visible)
-                {
-                    if (!isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        isOverlapped = true;
-                    }
-                    else if (isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        MoveCurrent (Application.Top!);
-
-                        break;
-                    }
-
-                    Application.TopLevels.MoveNext ();
-                }
-
-                Application.Current = Application.TopLevels.Peek ();
-            }
-        }
-    }
-
-    /// <summary>Move to the previous Overlapped child from the <see cref="OverlappedTop"/>.</summary>
-    public static void OverlappedMovePrevious ()
-    {
-        if (OverlappedTop is { } && !Application.Current!.Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MovePrevious ();
-                var isOverlapped = false;
-
-                while (Application.TopLevels.Peek () == OverlappedTop || !Application.TopLevels.Peek ().Visible)
-                {
-                    if (!isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        isOverlapped = true;
-                    }
-                    else if (isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        MoveCurrent (Application.Top!);
-
-                        break;
-                    }
-
-                    Application.TopLevels.MovePrevious ();
-                }
-
-                 Application.Current = Application.TopLevels.Peek ();
-            }
-        }
-    }
-
-    internal static bool OverlappedChildNeedsDisplay ()
-    {
-        if (OverlappedTop is null)
-        {
-            return false;
-        }
-
-        lock (Application.TopLevels)
-        {
-            foreach (Toplevel top in Application.TopLevels)
-            {
-                if (top != Application.Current && top.Visible && (top.NeedsDisplay || top.SubViewNeedsDisplay || top.LayoutNeeded))
-                {
-                    OverlappedTop.SetSubViewNeedsDisplay ();
-
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    internal static bool SetCurrentOverlappedAsTop ()
-    {
-        if (OverlappedTop is null && Application.Current != Application.Top && Application.Current?.SuperView is null && Application.Current?.Modal == false)
-        {
-            Application.Top = Application.Current;
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>
-    ///     Finds the first Toplevel in the stack that is Visible and who's Frame contains the <paramref name="location"/>.
-    /// </summary>
-    /// <param name="start"></param>
-    /// <param name="location"></param>
-    /// <returns></returns>
-    internal static Toplevel? FindDeepestTop (Toplevel start, in Point location)
-    {
-        if (!start.Frame.Contains (location))
-        {
-            return null;
-        }
-
-        lock (Application.TopLevels)
-        {
-            if (Application.TopLevels is not { Count: > 0 })
-            {
-                return start;
-            }
-
-            int rx = location.X - start.Frame.X;
-            int ry = location.Y - start.Frame.Y;
-
-            foreach (Toplevel t in Application.TopLevels)
-            {
-                if (t == Application.Current)
-                {
-                    continue;
-                }
-
-                if (t != start && t.Visible && t.Frame.Contains (rx, ry))
-                {
-                    start = t;
-
-                    break;
-                }
-            }
-        }
-
-        return start;
-    }
-
-    /// <summary>
-    ///     Given <paramref name="view"/>, returns the first Superview up the chain that is <see cref="Application.Top"/>.
-    /// </summary>
-    internal static View? FindTopFromView (View? view)
-    {
-        if (view is null)
-        {
-            return null;
-        }
-
-        View top = view.SuperView is { } && view.SuperView != Application.Top
-                       ? view.SuperView
-                       : view;
-
-        while (top?.SuperView is { } && top?.SuperView != Application.Top)
-        {
-            top = top!.SuperView;
-        }
-
-        return top;
-    }
-
-    /// <summary>
-    ///     If the <see cref="Application.Current"/> is not the <paramref name="top"/> then <paramref name="top"/> is moved to the top of
-    ///     the Toplevel stack and made Current.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    internal static bool MoveCurrent (Toplevel top)
-    {
-        // The Current is modal and the top is not modal Toplevel then
-        // the Current must be moved above the first not modal Toplevel.
-        if (OverlappedTop is { }
-            && top != OverlappedTop
-            && top != Application.Current
-            && Application.Current?.Modal == true
-            && !Application.TopLevels.Peek ().Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (Application.Current, 0, new ToplevelEqualityComparer ());
-            }
-
-            var index = 0;
-            Toplevel [] savedToplevels = Application.TopLevels.ToArray ();
-
-            foreach (Toplevel t in savedToplevels)
-            {
-                if (!t!.Modal && t != Application.Current && t != top && t != savedToplevels [index])
-                {
-                    lock (Application.TopLevels)
-                    {
-                        Application.TopLevels.MoveTo (top, index, new ToplevelEqualityComparer ());
-                    }
-                }
-
-                index++;
-            }
-
-            return false;
-        }
-
-        // The Current and the top are both not running Toplevel then
-        // the top must be moved above the first not running Toplevel.
-        if (OverlappedTop is { }
-            && top != OverlappedTop
-            && top != Application.Current
-            && Application.Current?.Running == false
-            && top?.Running == false)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (Application.Current, 0, new ToplevelEqualityComparer ());
-            }
-
-            var index = 0;
-
-            foreach (Toplevel t in Application.TopLevels.ToArray ())
-            {
-                if (!t.Running && t != Application.Current && index > 0)
-                {
-                    lock (Application.TopLevels)
-                    {
-                        Application.TopLevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
-                    }
-                }
-
-                index++;
-            }
-
-            return false;
-        }
-
-        if ((OverlappedTop is { } && top?.Modal == true && Application.TopLevels.Peek () != top)
-            || (OverlappedTop is { } && Application.Current != OverlappedTop && Application.Current?.Modal == false && top == OverlappedTop)
-            || (OverlappedTop is { } && Application.Current?.Modal == false && top != Application.Current)
-            || (OverlappedTop is { } && Application.Current?.Modal == true && top == OverlappedTop))
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (top!, 0, new ToplevelEqualityComparer ());
-                Application.Current = top;
-            }
-        }
-
-        return true;
-    }
-}

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

@@ -16,7 +16,7 @@ namespace Terminal.Gui;
 /// 	public static bool? MyProperty { get; set; } = true;
 /// 	public static bool? MyProperty { get; set; } = true;
 ///  }
 ///  }
 ///  </code>
 ///  </code>
-///     <para>THe resultant Json will look like this:</para>
+///     <para>The resultant Json will look like this:</para>
 ///     <code>
 ///     <code>
 ///    "AppSettings": {
 ///    "AppSettings": {
 ///      "MyAppSettings.MyProperty": true,
 ///      "MyAppSettings.MyProperty": true,

+ 18 - 5
Terminal.Gui/Configuration/ColorJsonConverter.cs

@@ -1,9 +1,19 @@
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
+using ColorHelper;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Json converter for the <see cref="Color"/> class.</summary>
+/// <summary>
+/// Json converter for the <see cref="Color"/> class.
+/// <para>
+///     Serialization outputs a string with the color name if the color matches a name in <see cref="ColorStrings"/>
+///     or the "#RRGGBB" hexadecimal representation (e.g. "#FF0000" for red).
+/// </para>
+/// <para>
+///     Deserialization formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
+///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", or any W3C color name.</para>
+/// </summary>
 internal class ColorJsonConverter : JsonConverter<Color>
 internal class ColorJsonConverter : JsonConverter<Color>
 {
 {
     private static ColorJsonConverter _instance;
     private static ColorJsonConverter _instance;
@@ -15,7 +25,7 @@ internal class ColorJsonConverter : JsonConverter<Color>
         {
         {
             if (_instance is null)
             if (_instance is null)
             {
             {
-                _instance = new ColorJsonConverter ();
+                _instance = new ();
             }
             }
 
 
             return _instance;
             return _instance;
@@ -31,10 +41,10 @@ internal class ColorJsonConverter : JsonConverter<Color>
             ReadOnlySpan<char> colorString = reader.GetString ();
             ReadOnlySpan<char> colorString = reader.GetString ();
 
 
             // Check if the color string is a color name
             // Check if the color string is a color name
-            if (Enum.TryParse (colorString, true, out ColorName color))
+            if (ColorStrings.TryParseW3CColorName (colorString.ToString (), out Color color1))
             {
             {
                 // Return the parsed color
                 // Return the parsed color
-                return new Color (in color);
+                return new (color1);
             }
             }
 
 
             if (Color.TryParse (colorString, null, out Color parsedColor))
             if (Color.TryParse (colorString, null, out Color parsedColor))
@@ -48,5 +58,8 @@ internal class ColorJsonConverter : JsonConverter<Color>
         throw new JsonException ($"Unexpected token when parsing Color: {reader.TokenType}");
         throw new JsonException ($"Unexpected token when parsing Color: {reader.TokenType}");
     }
     }
 
 
-    public override void Write (Utf8JsonWriter writer, Color value, JsonSerializerOptions options) { writer.WriteStringValue (value.ToString ()); }
+    public override void Write (Utf8JsonWriter writer, Color value, JsonSerializerOptions options)
+    {
+        writer.WriteStringValue (value.ToString ());
+    }
 }
 }

+ 14 - 5
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -24,7 +24,7 @@ namespace Terminal.Gui;
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
 ///         Settings are defined in JSON format, according to this schema:
 ///         Settings are defined in JSON format, according to this schema:
-///         https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json
+///        https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
 ///         Settings that will apply to all applications (global settings) reside in files named <c>config.json</c>.
 ///         Settings that will apply to all applications (global settings) reside in files named <c>config.json</c>.
@@ -197,10 +197,19 @@ public static class ConfigurationManager
 
 
         try
         try
         {
         {
-            settings = Settings?.Apply () ?? false;
-
-            themes = !string.IsNullOrEmpty (ThemeManager.SelectedTheme)
-                     && (ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false);
+            if (string.IsNullOrEmpty (ThemeManager.SelectedTheme))
+            {
+                // First start. Apply settings first. This ensures if a config sets Theme to something other than "Default", it gets used
+                settings = Settings?.Apply () ?? false;
+                themes = !string.IsNullOrEmpty (ThemeManager.SelectedTheme)
+                         && (ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false);
+            }
+            else
+            {
+                // Subsequently. Apply Themes first using whatever the SelectedTheme is
+                themes = ThemeManager.Themes? [ThemeManager.SelectedTheme]?.Apply () ?? false;
+                settings = Settings?.Apply () ?? false;
+            }
             appSettings = AppSettings?.Apply () ?? false;
             appSettings = AppSettings?.Apply () ?? false;
         }
         }
         catch (JsonException e)
         catch (JsonException e)

+ 24 - 17
Terminal.Gui/Configuration/RuneJsonConverter.cs

@@ -6,9 +6,17 @@ using System.Text.RegularExpressions;
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
-///     Json converter for <see cref="Rune"/>. Supports Json converter for <see cref="Rune"/>. Supports A string as
-///     one of: - unicode char (e.g. "☑") - U+hex format (e.g. "U+2611") - \u format (e.g. "\\u2611") A number - The
-///     unicode code in decimal
+///     Json converter for <see cref="Rune"/>.
+///     <para>
+///         If the Rune is printable, it will be serialized as the glyph; otherwise the \u format (e.g. "\\u2611") is used.
+///     </para>
+///     <para>
+///         Supports deserializing as one of:
+///         - unicode glyph in a string (e.g. "☑")
+///         - U+hex format in a string  (e.g. "U+2611")
+///         - \u format in a string (e.g. "\\u2611")
+///         - A decimal number (e.g. 97 for "a")
+///     </para>
 /// </summary>
 /// </summary>
 internal class RuneJsonConverter : JsonConverter<Rune>
 internal class RuneJsonConverter : JsonConverter<Rune>
 {
 {
@@ -108,7 +116,7 @@ internal class RuneJsonConverter : JsonConverter<Rune>
                     throw new JsonException ($"Invalid combined Rune ({value})");
                     throw new JsonException ($"Invalid combined Rune ({value})");
                 }
                 }
 
 
-                return new Rune (combined [0]);
+                return new (combined [0]);
             }
             }
             case JsonTokenType.Number:
             case JsonTokenType.Number:
             {
             {
@@ -116,7 +124,7 @@ internal class RuneJsonConverter : JsonConverter<Rune>
 
 
                 if (Rune.IsValid (num))
                 if (Rune.IsValid (num))
                 {
                 {
-                    return new Rune (num);
+                    return new (num);
                 }
                 }
 
 
                 throw new JsonException ($"Invalid Rune (not a scalar Unicode value): {num}.");
                 throw new JsonException ($"Invalid Rune (not a scalar Unicode value): {num}.");
@@ -128,18 +136,17 @@ internal class RuneJsonConverter : JsonConverter<Rune>
 
 
     public override void Write (Utf8JsonWriter writer, Rune value, JsonSerializerOptions options)
     public override void Write (Utf8JsonWriter writer, Rune value, JsonSerializerOptions options)
     {
     {
-        // HACK: Writes a JSON comment in addition to the glyph to ease debugging.
-        // Technically, JSON comments are not valid, but we use relaxed decoding
-        // (ReadCommentHandling = JsonCommentHandling.Skip)
-        //writer.WriteCommentValue ($"(U+{value.Value:X8})");
-        //var printable = value.MakePrintable ();
-        //if (printable == Rune.ReplacementChar) {
-        //	writer.WriteStringValue (value.ToString ());
-        //} else {
-        //	//writer.WriteRawValue ($"\"{value}\"");
-        //}
-
-        writer.WriteNumberValue (value.Value);
+        Rune printable = value.MakePrintable ();
+        if (printable == Rune.ReplacementChar)
+        {
+            // Write as /u string
+            writer.WriteRawValue ($"\"{value}\"");
+        }
+        else
+        {
+            // Write as the actual glyph
+            writer.WriteStringValue (value.ToString ());
+        }
     }
     }
 }
 }
 #pragma warning restore 1591
 #pragma warning restore 1591

+ 25 - 8
Terminal.Gui/Configuration/SettingsScope.cs

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
-using static Terminal.Gui.SpinnerStyle;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -15,7 +14,7 @@ namespace Terminal.Gui;
 /// <example>
 /// <example>
 ///     <code>
 ///     <code>
 ///  {
 ///  {
-///    "$schema" : "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
+///    "$schema" : "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json",
 ///    "Application.UseSystemConsole" : true,
 ///    "Application.UseSystemConsole" : true,
 ///    "Theme" : "Default",
 ///    "Theme" : "Default",
 ///    "Themes": {
 ///    "Themes": {
@@ -33,7 +32,7 @@ public class SettingsScope : Scope<SettingsScope>
     /// <summary>Points to our JSON schema.</summary>
     /// <summary>Points to our JSON schema.</summary>
     [JsonInclude]
     [JsonInclude]
     [JsonPropertyName ("$schema")]
     [JsonPropertyName ("$schema")]
-    public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json";
+    public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json";
 
 
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>
     /// <param name="stream">Json document to update the settings with.</param>
     /// <param name="stream">Json document to update the settings with.</param>
@@ -87,12 +86,30 @@ public class SettingsScope : Scope<SettingsScope>
             return this;
             return this;
         }
         }
 
 
-        FileStream stream = File.OpenRead (realPath);
-        SettingsScope? s = Update (stream, filePath);
-        stream.Close ();
-        stream.Dispose ();
+        int retryCount = 0;
 
 
-        return s;
+        // Sometimes when the config file is written by an external agent, the change notification comes
+        // before the file is closed. This works around that.
+        while (retryCount < 2)
+        {
+            try
+            {
+                FileStream? stream = File.OpenRead (realPath);
+                SettingsScope? s = Update (stream, filePath);
+                stream.Close ();
+                stream.Dispose ();
+
+                return s;
+            }
+            catch (IOException ioe)
+            {
+                Debug.WriteLine($"Couldn't open {filePath}. Retrying...: {ioe}");
+                Task.Delay (100);
+                retryCount++;
+            }
+        }
+
+        return null;
     }
     }
 
 
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>

+ 3 - 1
Terminal.Gui/Configuration/SourceGenerationContext.cs

@@ -15,9 +15,11 @@ namespace Terminal.Gui;
 [JsonSerializable (typeof (AlignmentModes))]
 [JsonSerializable (typeof (AlignmentModes))]
 [JsonSerializable (typeof (LineStyle))]
 [JsonSerializable (typeof (LineStyle))]
 [JsonSerializable (typeof (ShadowStyle))]
 [JsonSerializable (typeof (ShadowStyle))]
+[JsonSerializable (typeof (HighlightStyle))]
 [JsonSerializable (typeof (bool?))]
 [JsonSerializable (typeof (bool?))]
-[JsonSerializable (typeof (Dictionary<ColorName, string>))]
+[JsonSerializable (typeof (Dictionary<ColorName16, string>))]
 [JsonSerializable (typeof (Dictionary<string, ThemeScope>))]
 [JsonSerializable (typeof (Dictionary<string, ThemeScope>))]
 [JsonSerializable (typeof (Dictionary<string, ColorScheme>))]
 [JsonSerializable (typeof (Dictionary<string, ColorScheme>))]
+[JsonSerializable (typeof (Dictionary<string, Color>))]
 internal partial class SourceGenerationContext : JsonSerializerContext
 internal partial class SourceGenerationContext : JsonSerializerContext
 { }
 { }

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

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

+ 9 - 2
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -485,6 +485,7 @@ public abstract class ConsoleDriver
     public virtual bool SupportsTrueColor => true;
     public virtual bool SupportsTrueColor => true;
 
 
     // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
     // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
+    // BUGBUG: Application.Force16Colors should be bool? so if SupportsTrueColor and Application.Force16Colors == false, this doesn't override
     /// <summary>
     /// <summary>
     ///     Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors.
     ///     Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors.
     ///     See <see cref="Application.Force16Colors"/> to change this setting via <see cref="ConfigurationManager"/>.
     ///     See <see cref="Application.Force16Colors"/> to change this setting via <see cref="ConfigurationManager"/>.
@@ -587,11 +588,17 @@ public abstract class ConsoleDriver
     public void OnKeyUp (Key a) { KeyUp?.Invoke (this, a); }
     public void OnKeyUp (Key a) { KeyUp?.Invoke (this, a); }
 
 
     /// <summary>Event fired when a mouse event occurs.</summary>
     /// <summary>Event fired when a mouse event occurs.</summary>
-    public event EventHandler<MouseEvent>? MouseEvent;
+    public event EventHandler<MouseEventArgs>? MouseEvent;
 
 
     /// <summary>Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.</summary>
     /// <summary>Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.</summary>
     /// <param name="a"></param>
     /// <param name="a"></param>
-    public void OnMouseEvent (MouseEvent a) { MouseEvent?.Invoke (this, a); }
+    public void OnMouseEvent (MouseEventArgs a)
+    {
+        // Ensure ScreenPosition is set
+        a.ScreenPosition = a.Position;
+
+        MouseEvent?.Invoke (this, a);
+    }
 
 
     /// <summary>Simulates a key press.</summary>
     /// <summary>Simulates a key press.</summary>
     /// <param name="keyChar">The key character.</param>
     /// <param name="keyChar">The key character.</param>

+ 257 - 83
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -39,7 +39,7 @@ internal class CursesDriver : ConsoleDriver
         }
         }
     }
     }
 
 
-    public override bool SupportsTrueColor => false;
+    public override bool SupportsTrueColor => true;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override bool EnsureCursorVisibility () { return false; }
     public override bool EnsureCursorVisibility () { return false; }
@@ -200,8 +200,12 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests)
         if (!RunningUnitTests)
         {
         {
             Platform.Suspend ();
             Platform.Suspend ();
-            Curses.Window.Standard.redrawwin ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.Window.Standard.redrawwin ();
+                Curses.refresh ();
+            }
         }
         }
 
 
         StartReportingMouseMoves ();
         StartReportingMouseMoves ();
@@ -214,74 +218,239 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
         if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
         {
         {
             Curses.move (Row, Col);
             Curses.move (Row, Col);
-            Curses.raw ();
-            Curses.noecho ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.raw ();
+                Curses.noecho ();
+                Curses.refresh ();
+            }
         }
         }
     }
     }
 
 
-
     public override void UpdateScreen ()
     public override void UpdateScreen ()
     {
     {
-        for (var row = 0; row < Rows; row++)
+        if (Force16Colors)
         {
         {
-            if (!_dirtyLines [row])
+            for (var row = 0; row < Rows; row++)
             {
             {
-                continue;
-            }
-
-            _dirtyLines [row] = false;
-
-            for (var col = 0; col < Cols; col++)
-            {
-                if (Contents [row, col].IsDirty == false)
+                if (!_dirtyLines [row])
                 {
                 {
                     continue;
                     continue;
                 }
                 }
 
 
-                if (RunningUnitTests)
+                _dirtyLines [row] = false;
+
+                for (var col = 0; col < Cols; col++)
                 {
                 {
-                    // In unit tests, we don't want to actually write to the screen.
-                    continue;
-                }
+                    if (Contents [row, col].IsDirty == false)
+                    {
+                        continue;
+                    }
+
+                    if (RunningUnitTests)
+                    {
+                        // In unit tests, we don't want to actually write to the screen.
+                        continue;
+                    }
 
 
-                Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().PlatformColor);
+                    Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().PlatformColor);
 
 
-                Rune rune = Contents [row, col].Rune;
+                    Rune rune = Contents [row, col].Rune;
 
 
-                if (rune.IsBmp)
-                {
-                    // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
-                    if (rune.GetColumns () < 2)
+                    if (rune.IsBmp)
                     {
                     {
-                        Curses.mvaddch (row, col, rune.Value);
+                        // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
+                        if (rune.GetColumns () < 2)
+                        {
+                            Curses.mvaddch (row, col, rune.Value);
+                        }
+                        else /*if (col + 1 < Cols)*/
+                        {
+                            Curses.mvaddwstr (row, col, rune.ToString ());
+                        }
                     }
                     }
-                    else /*if (col + 1 < Cols)*/
+                    else
                     {
                     {
                         Curses.mvaddwstr (row, col, rune.ToString ());
                         Curses.mvaddwstr (row, col, rune.ToString ());
+
+                        if (rune.GetColumns () > 1 && col + 1 < Cols)
+                        {
+                            // TODO: This is a hack to deal with non-BMP and wide characters.
+                            //col++;
+                            Curses.mvaddch (row, ++col, '*');
+                        }
                     }
                     }
                 }
                 }
-                else
+            }
+
+            if (!RunningUnitTests)
+            {
+                Curses.move (Row, Col);
+                _window.wrefresh ();
+            }
+        }
+        else
+        {
+            if (RunningUnitTests
+                || Console.WindowHeight < 1
+                || Contents.Length != Rows * Cols
+                || Rows != Console.WindowHeight)
+            {
+                return;
+            }
+
+            var top = 0;
+            var left = 0;
+            int rows = Rows;
+            int cols = Cols;
+            var output = new StringBuilder ();
+            Attribute? redrawAttr = null;
+            int lastCol = -1;
+
+            CursorVisibility? savedVisibility = _currentCursorVisibility;
+            SetCursorVisibility (CursorVisibility.Invisible);
+
+            for (int row = top; row < rows; row++)
+            {
+                if (Console.WindowHeight < 1)
+                {
+                    return;
+                }
+
+                if (!_dirtyLines [row])
                 {
                 {
-                    Curses.mvaddwstr (row, col, rune.ToString ());
+                    continue;
+                }
 
 
-                    if (rune.GetColumns () > 1 && col + 1 < Cols)
+                if (!SetCursorPosition (0, row))
+                {
+                    return;
+                }
+
+                _dirtyLines [row] = false;
+                output.Clear ();
+
+                for (int col = left; col < cols; col++)
+                {
+                    lastCol = -1;
+                    var outputWidth = 0;
+
+                    for (; col < cols; col++)
                     {
                     {
-                        // TODO: This is a hack to deal with non-BMP and wide characters.
-                        //col++;
-                        Curses.mvaddch (row, ++col, '*');
+                        if (!Contents [row, col].IsDirty)
+                        {
+                            if (output.Length > 0)
+                            {
+                                WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                            }
+                            else if (lastCol == -1)
+                            {
+                                lastCol = col;
+                            }
+
+                            if (lastCol + 1 < cols)
+                            {
+                                lastCol++;
+                            }
+
+                            continue;
+                        }
+
+                        if (lastCol == -1)
+                        {
+                            lastCol = col;
+                        }
+
+                        Attribute attr = Contents [row, col].Attribute.Value;
+
+                        // Performance: Only send the escape sequence if the attribute has changed.
+                        if (attr != redrawAttr)
+                        {
+                            redrawAttr = attr;
+
+                            output.Append (
+                                           EscSeqUtils.CSI_SetForegroundColorRGB (
+                                                                                  attr.Foreground.R,
+                                                                                  attr.Foreground.G,
+                                                                                  attr.Foreground.B
+                                                                                 )
+                                          );
+
+                            output.Append (
+                                           EscSeqUtils.CSI_SetBackgroundColorRGB (
+                                                                                  attr.Background.R,
+                                                                                  attr.Background.G,
+                                                                                  attr.Background.B
+                                                                                 )
+                                          );
+                        }
+
+                        outputWidth++;
+                        Rune rune = Contents [row, col].Rune;
+                        output.Append (rune);
+
+                        if (Contents [row, col].CombiningMarks.Count > 0)
+                        {
+                            // AtlasEngine does not support NON-NORMALIZED combining marks in a way
+                            // compatible with the driver architecture. Any CMs (except in the first col)
+                            // are correctly combined with the base char, but are ALSO treated as 1 column
+                            // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`.
+                            // 
+                            // For now, we just ignore the list of CMs.
+                            //foreach (var combMark in Contents [row, col].CombiningMarks) {
+                            //	output.Append (combMark);
+                            //}
+                            // WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                        }
+                        else if (rune.IsSurrogatePair () && rune.GetColumns () < 2)
+                        {
+                            WriteToConsole (output, ref lastCol, row, ref outputWidth);
+                            SetCursorPosition (col - 1, row);
+                        }
+
+                        Contents [row, col].IsDirty = false;
                     }
                     }
                 }
                 }
+
+                if (output.Length > 0)
+                {
+                    SetCursorPosition (lastCol, row);
+                    Console.Write (output);
+                }
             }
             }
-        }
 
 
-        if (!RunningUnitTests)
-        {
-            Curses.move (Row, Col);
-            _window.wrefresh ();
+            // SIXELS
+            foreach (var s in Application.Sixel)
+            {
+                SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
+                Console.Write(s.SixelData);
+            }
+
+            SetCursorPosition (0, 0);
+
+            _currentCursorVisibility = savedVisibility;
+
+            void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
+            {
+                SetCursorPosition (lastCol, row);
+                Console.Write (output);
+                output.Clear ();
+                lastCol += outputWidth;
+                outputWidth = 0;
+            }
         }
         }
     }
     }
 
 
+    private bool SetCursorPosition (int col, int row)
+    {
+        // + 1 is needed because non-Windows is based on 1 instead of 0 and
+        // Console.CursorTop/CursorLeft isn't reliable.
+        Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
+
+        return true;
+    }
+
     internal override void End ()
     internal override void End ()
     {
     {
         StopReportingMouseMoves ();
         StopReportingMouseMoves ();
@@ -374,7 +543,7 @@ internal class CursesDriver : ConsoleDriver
                                                            );
                                                            );
         }
         }
 
 
-        CurrentAttribute = new Attribute (ColorName.White, ColorName.Black);
+        CurrentAttribute = new Attribute (ColorName16.White, ColorName16.Black);
 
 
         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         {
         {
@@ -405,7 +574,11 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests)
         if (!RunningUnitTests)
         {
         {
             Curses.CheckWinChange ();
             Curses.CheckWinChange ();
-            Curses.refresh ();
+
+            if (Force16Colors)
+            {
+                Curses.refresh ();
+            }
         }
         }
 
 
         return new MainLoop (_mainLoopDriver);
         return new MainLoop (_mainLoopDriver);
@@ -838,7 +1011,7 @@ internal class CursesDriver : ConsoleDriver
 
 
         _lastMouseFlags = mouseFlag;
         _lastMouseFlags = mouseFlag;
 
 
-        var me = new MouseEvent { Flags = mouseFlag, Position = pos };
+        var me = new MouseEventArgs { Flags = mouseFlag, Position = pos };
         //Debug.WriteLine ($"CursesDriver: ({me.Position}) - {me.Flags}");
         //Debug.WriteLine ($"CursesDriver: ({me.Position}) - {me.Flags}");
 
 
         OnMouseEvent (me);
         OnMouseEvent (me);
@@ -852,15 +1025,16 @@ internal class CursesDriver : ConsoleDriver
     /// <returns></returns>
     /// <returns></returns>
     private static Attribute MakeColor (short foreground, short background)
     private static Attribute MakeColor (short foreground, short background)
     {
     {
-        var v = (short)((ushort)foreground | (background << 4));
+        //var v = (short)((ushort)foreground | (background << 4));
+        var v = (short)(((ushort)(foreground & 0xffff) << 16) | (background & 0xffff));
 
 
         // TODO: for TrueColor - Use InitExtendedPair
         // TODO: for TrueColor - Use InitExtendedPair
         Curses.InitColorPair (v, foreground, background);
         Curses.InitColorPair (v, foreground, background);
 
 
         return new Attribute (
         return new Attribute (
                               Curses.ColorPair (v),
                               Curses.ColorPair (v),
-                              CursesColorNumberToColorName (foreground),
-                              CursesColorNumberToColorName (background)
+                              CursesColorNumberToColorName16 (foreground),
+                              CursesColorNumberToColorName16 (background)
                              );
                              );
     }
     }
 
 
@@ -872,11 +1046,11 @@ internal class CursesDriver : ConsoleDriver
     /// </remarks>
     /// </remarks>
     public override Attribute MakeColor (in Color foreground, in Color background)
     public override Attribute MakeColor (in Color foreground, in Color background)
     {
     {
-        if (!RunningUnitTests)
+        if (!RunningUnitTests && Force16Colors)
         {
         {
             return MakeColor (
             return MakeColor (
-                              ColorNameToCursesColorNumber (foreground.GetClosestNamedColor ()),
-                              ColorNameToCursesColorNumber (background.GetClosestNamedColor ())
+                              ColorNameToCursesColorNumber (foreground.GetClosestNamedColor16 ()),
+                              ColorNameToCursesColorNumber (background.GetClosestNamedColor16 ())
                              );
                              );
         }
         }
 
 
@@ -887,83 +1061,83 @@ internal class CursesDriver : ConsoleDriver
                              );
                              );
     }
     }
 
 
-    private static short ColorNameToCursesColorNumber (ColorName color)
+    private static short ColorNameToCursesColorNumber (ColorName16 color)
     {
     {
         switch (color)
         switch (color)
         {
         {
-            case ColorName.Black:
+            case ColorName16.Black:
                 return Curses.COLOR_BLACK;
                 return Curses.COLOR_BLACK;
-            case ColorName.Blue:
+            case ColorName16.Blue:
                 return Curses.COLOR_BLUE;
                 return Curses.COLOR_BLUE;
-            case ColorName.Green:
+            case ColorName16.Green:
                 return Curses.COLOR_GREEN;
                 return Curses.COLOR_GREEN;
-            case ColorName.Cyan:
+            case ColorName16.Cyan:
                 return Curses.COLOR_CYAN;
                 return Curses.COLOR_CYAN;
-            case ColorName.Red:
+            case ColorName16.Red:
                 return Curses.COLOR_RED;
                 return Curses.COLOR_RED;
-            case ColorName.Magenta:
+            case ColorName16.Magenta:
                 return Curses.COLOR_MAGENTA;
                 return Curses.COLOR_MAGENTA;
-            case ColorName.Yellow:
+            case ColorName16.Yellow:
                 return Curses.COLOR_YELLOW;
                 return Curses.COLOR_YELLOW;
-            case ColorName.Gray:
+            case ColorName16.Gray:
                 return Curses.COLOR_WHITE;
                 return Curses.COLOR_WHITE;
-            case ColorName.DarkGray:
+            case ColorName16.DarkGray:
                 return Curses.COLOR_GRAY;
                 return Curses.COLOR_GRAY;
-            case ColorName.BrightBlue:
+            case ColorName16.BrightBlue:
                 return Curses.COLOR_BLUE | Curses.COLOR_GRAY;
                 return Curses.COLOR_BLUE | Curses.COLOR_GRAY;
-            case ColorName.BrightGreen:
+            case ColorName16.BrightGreen:
                 return Curses.COLOR_GREEN | Curses.COLOR_GRAY;
                 return Curses.COLOR_GREEN | Curses.COLOR_GRAY;
-            case ColorName.BrightCyan:
+            case ColorName16.BrightCyan:
                 return Curses.COLOR_CYAN | Curses.COLOR_GRAY;
                 return Curses.COLOR_CYAN | Curses.COLOR_GRAY;
-            case ColorName.BrightRed:
+            case ColorName16.BrightRed:
                 return Curses.COLOR_RED | Curses.COLOR_GRAY;
                 return Curses.COLOR_RED | Curses.COLOR_GRAY;
-            case ColorName.BrightMagenta:
+            case ColorName16.BrightMagenta:
                 return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY;
                 return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY;
-            case ColorName.BrightYellow:
+            case ColorName16.BrightYellow:
                 return Curses.COLOR_YELLOW | Curses.COLOR_GRAY;
                 return Curses.COLOR_YELLOW | Curses.COLOR_GRAY;
-            case ColorName.White:
+            case ColorName16.White:
                 return Curses.COLOR_WHITE | Curses.COLOR_GRAY;
                 return Curses.COLOR_WHITE | Curses.COLOR_GRAY;
         }
         }
 
 
         throw new ArgumentException ("Invalid color code");
         throw new ArgumentException ("Invalid color code");
     }
     }
 
 
-    private static ColorName CursesColorNumberToColorName (short color)
+    private static ColorName16 CursesColorNumberToColorName16 (short color)
     {
     {
         switch (color)
         switch (color)
         {
         {
             case Curses.COLOR_BLACK:
             case Curses.COLOR_BLACK:
-                return ColorName.Black;
+                return ColorName16.Black;
             case Curses.COLOR_BLUE:
             case Curses.COLOR_BLUE:
-                return ColorName.Blue;
+                return ColorName16.Blue;
             case Curses.COLOR_GREEN:
             case Curses.COLOR_GREEN:
-                return ColorName.Green;
+                return ColorName16.Green;
             case Curses.COLOR_CYAN:
             case Curses.COLOR_CYAN:
-                return ColorName.Cyan;
+                return ColorName16.Cyan;
             case Curses.COLOR_RED:
             case Curses.COLOR_RED:
-                return ColorName.Red;
+                return ColorName16.Red;
             case Curses.COLOR_MAGENTA:
             case Curses.COLOR_MAGENTA:
-                return ColorName.Magenta;
+                return ColorName16.Magenta;
             case Curses.COLOR_YELLOW:
             case Curses.COLOR_YELLOW:
-                return ColorName.Yellow;
+                return ColorName16.Yellow;
             case Curses.COLOR_WHITE:
             case Curses.COLOR_WHITE:
-                return ColorName.Gray;
+                return ColorName16.Gray;
             case Curses.COLOR_GRAY:
             case Curses.COLOR_GRAY:
-                return ColorName.DarkGray;
+                return ColorName16.DarkGray;
             case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
             case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
-                return ColorName.BrightBlue;
+                return ColorName16.BrightBlue;
             case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
             case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
-                return ColorName.BrightGreen;
+                return ColorName16.BrightGreen;
             case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
             case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
-                return ColorName.BrightCyan;
+                return ColorName16.BrightCyan;
             case Curses.COLOR_RED | Curses.COLOR_GRAY:
             case Curses.COLOR_RED | Curses.COLOR_GRAY:
-                return ColorName.BrightRed;
+                return ColorName16.BrightRed;
             case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
             case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
-                return ColorName.BrightMagenta;
+                return ColorName16.BrightMagenta;
             case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
             case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
-                return ColorName.BrightYellow;
+                return ColorName16.BrightYellow;
             case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
             case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
-                return ColorName.White;
+                return ColorName16.White;
         }
         }
 
 
         throw new ArgumentException ("Invalid curses color code");
         throw new ArgumentException ("Invalid curses color code");

+ 15 - 253
Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs

@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-#define GUICS
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
@@ -27,81 +26,6 @@ namespace Unix.Terminal;
 /// </summary>
 /// </summary>
 internal class UnmanagedLibrary
 internal class UnmanagedLibrary
 {
 {
-    private const string UnityEngineApplicationClassName = "UnityEngine.Application, UnityEngine";
-    private const string XamarinAndroidObjectClassName = "Java.Lang.Object, Mono.Android";
-    private const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
-    private static readonly bool IsWindows;
-    private static readonly bool IsLinux;
-    private static readonly bool Is64Bit;
-#if GUICS
-    private static readonly bool IsMono;
-#else
-		static bool IsMono, IsUnity, IsXamarinIOS, IsXamarinAndroid, IsXamarin;
-#endif
-    private static bool IsNetCore;
-    public static bool IsMacOSPlatform { get; }
-
-    [DllImport ("libc")]
-    private static extern int uname (nint buf);
-
-    private static string GetUname ()
-    {
-        nint buffer = Marshal.AllocHGlobal (8192);
-
-        try
-        {
-            if (uname (buffer) == 0)
-            {
-                return Marshal.PtrToStringAnsi (buffer);
-            }
-
-            return string.Empty;
-        }
-        catch
-        {
-            return string.Empty;
-        }
-        finally
-        {
-            if (buffer != nint.Zero)
-            {
-                Marshal.FreeHGlobal (buffer);
-            }
-        }
-    }
-
-    [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
-    static UnmanagedLibrary ()
-    {
-        PlatformID platform = Environment.OSVersion.Platform;
-
-        IsMacOSPlatform = platform == PlatformID.Unix && GetUname () == "Darwin";
-        IsLinux = platform == PlatformID.Unix && !IsMacOSPlatform;
-
-        IsWindows = platform == PlatformID.Win32NT
-                    || platform == PlatformID.Win32S
-                    || platform == PlatformID.Win32Windows;
-        Is64Bit = Marshal.SizeOf (typeof (nint)) == 8;
-        IsMono = Type.GetType ("Mono.Runtime") != null;
-
-        if (!IsMono)
-        {
-            IsNetCore = Type.GetType ("System.MathF") != null;
-        }
-#if GUICS
-
-        //IsUnity = IsXamarinIOS = IsXamarinAndroid = IsXamarin = false;
-#else
-			IsUnity = Type.GetType (UnityEngineApplicationClassName) != null;
-			IsXamarinIOS = Type.GetType (XamarinIOSObjectClassName) != null;
-			IsXamarinAndroid = Type.GetType (XamarinAndroidObjectClassName) != null;
-			IsXamarin = IsXamarinIOS || IsXamarinAndroid;
-#endif
-    }
-
-    // flags for dlopen
-    private const int RTLD_LAZY = 1;
-    private const int RTLD_GLOBAL = 8;
     public readonly string LibraryPath;
     public readonly string LibraryPath;
     public nint NativeLibraryHandle { get; }
     public nint NativeLibraryHandle { get; }
 
 
@@ -114,15 +38,25 @@ internal class UnmanagedLibrary
     {
     {
         if (isFullPath)
         if (isFullPath)
         {
         {
-            LibraryPath = FirstValidLibraryPath (libraryPathAlternatives);
-            NativeLibraryHandle = PlatformSpecificLoadLibrary (LibraryPath);
+            foreach (string path in libraryPathAlternatives)
+            {
+                if (File.Exists (path))
+                {
+                    LibraryPath = path;
+                    break;
+                }
+            }
+
+            if (LibraryPath is null)
+                throw new FileNotFoundException ($"Error loading native library. Not found in any of the possible locations: {string.Join (",", libraryPathAlternatives)}");
+
+            NativeLibraryHandle = NativeLibrary.Load (LibraryPath);
         }
         }
         else
         else
         {
         {
             foreach (string lib in libraryPathAlternatives)
             foreach (string lib in libraryPathAlternatives)
             {
             {
-                NativeLibraryHandle = PlatformSpecificLoadLibrary (lib);
-
+                NativeLibraryHandle = NativeLibrary.Load (lib);
                 if (NativeLibraryHandle != nint.Zero)
                 if (NativeLibraryHandle != nint.Zero)
                 {
                 {
                     LibraryPath = lib;
                     LibraryPath = lib;
@@ -143,55 +77,7 @@ internal class UnmanagedLibrary
     /// <returns></returns>
     /// <returns></returns>
     public nint LoadSymbol (string symbolName)
     public nint LoadSymbol (string symbolName)
     {
     {
-        if (IsWindows)
-        {
-            // See http://stackoverflow.com/questions/10473310 for background on this.
-            if (Is64Bit)
-            {
-                return Windows.GetProcAddress (NativeLibraryHandle, symbolName);
-            }
-
-            // Yes, we could potentially predict the size... but it's a lot simpler to just try
-            // all the candidates. Most functions have a suffix of @0, @4 or @8 so we won't be trying
-            // many options - and if it takes a little bit longer to fail if we've really got the wrong
-            // library, that's not a big problem. This is only called once per function in the native library.
-            symbolName = "_" + symbolName + "@";
-
-            for (var stackSize = 0; stackSize < 128; stackSize += 4)
-            {
-                nint candidate = Windows.GetProcAddress (NativeLibraryHandle, symbolName + stackSize);
-
-                if (candidate != nint.Zero)
-                {
-                    return candidate;
-                }
-            }
-
-            // Fail.
-            return nint.Zero;
-        }
-
-        if (IsLinux)
-        {
-            if (IsMono)
-            {
-                return Mono.dlsym (NativeLibraryHandle, symbolName);
-            }
-
-            if (IsNetCore)
-            {
-                return CoreCLR.dlsym (NativeLibraryHandle, symbolName);
-            }
-
-            return Linux.dlsym (NativeLibraryHandle, symbolName);
-        }
-
-        if (IsMacOSPlatform)
-        {
-            return MacOSX.dlsym (NativeLibraryHandle, symbolName);
-        }
-
-        throw new InvalidOperationException ("Unsupported platform.");
+        return NativeLibrary.GetExport(NativeLibraryHandle, symbolName);
     }
     }
 
 
     public T GetNativeMethodDelegate<T> (string methodName)
     public T GetNativeMethodDelegate<T> (string methodName)
@@ -206,128 +92,4 @@ internal class UnmanagedLibrary
 
 
         return Marshal.GetDelegateForFunctionPointer<T> (ptr); // non-generic version is obsolete
         return Marshal.GetDelegateForFunctionPointer<T> (ptr); // non-generic version is obsolete
     }
     }
-
-    /// <summary>Loads library in a platform specific way.</summary>
-    private static nint PlatformSpecificLoadLibrary (string libraryPath)
-    {
-        if (IsWindows)
-        {
-            return Windows.LoadLibrary (libraryPath);
-        }
-
-        if (IsLinux)
-        {
-            if (IsMono)
-            {
-                return Mono.dlopen (libraryPath, RTLD_GLOBAL + RTLD_LAZY);
-            }
-
-            if (IsNetCore)
-            {
-                try
-                {
-                    return CoreCLR.dlopen (libraryPath, RTLD_GLOBAL + RTLD_LAZY);
-                }
-                catch (Exception)
-                {
-                    IsNetCore = false;
-                }
-            }
-
-            return Linux.dlopen (libraryPath, RTLD_GLOBAL + RTLD_LAZY);
-        }
-
-        if (IsMacOSPlatform)
-        {
-            return MacOSX.dlopen (libraryPath, RTLD_GLOBAL + RTLD_LAZY);
-        }
-
-        throw new InvalidOperationException ("Unsupported platform.");
-    }
-
-    private static string FirstValidLibraryPath (string [] libraryPathAlternatives)
-    {
-        foreach (string path in libraryPathAlternatives)
-        {
-            if (File.Exists (path))
-            {
-                return path;
-            }
-        }
-
-        throw new FileNotFoundException (
-                                         string.Format (
-                                                        "Error loading native library. Not found in any of the possible locations: {0}",
-                                                        string.Join (",", libraryPathAlternatives)
-                                                       )
-                                        );
-    }
-
-    private static class Windows
-    {
-        [DllImport ("kernel32.dll")]
-        internal static extern nint GetProcAddress (nint hModule, string procName);
-
-        [DllImport ("kernel32.dll")]
-        internal static extern nint LoadLibrary (string filename);
-    }
-
-    private static class Linux
-    {
-        [DllImport ("libdl.so")]
-        internal static extern nint dlopen (string filename, int flags);
-
-        [DllImport ("libdl.so")]
-        internal static extern nint dlsym (nint handle, string symbol);
-    }
-
-    private static class MacOSX
-    {
-        [DllImport ("libSystem.dylib")]
-        internal static extern nint dlopen (string filename, int flags);
-
-        [DllImport ("libSystem.dylib")]
-        internal static extern nint dlsym (nint handle, string symbol);
-    }
-
-    /// <summary>
-    ///     On Linux systems, using dlopen and dlsym results in DllNotFoundException("libdl.so not found") if
-    ///     libc6-dev is not installed. As a workaround, we load symbols for dlopen and dlsym from the current process as on
-    ///     Linux Mono sure is linked against these symbols.
-    /// </summary>
-    private static class Mono
-    {
-        [DllImport ("__Internal")]
-        internal static extern nint dlopen (string filename, int flags);
-
-        [DllImport ("__Internal")]
-        internal static extern nint dlsym (nint handle, string symbol);
-    }
-
-    /// <summary>
-    ///     Similarly as for Mono on Linux, we load symbols for dlopen and dlsym from the "libcoreclr.so", to avoid the
-    ///     dependency on libc-dev Linux.
-    /// </summary>
-    private static class CoreCLR
-    {
-        // Custom resolver to support true single-file apps
-        // (those which run directly from bundle; in-memory).
-        //	 -1 on Unix means self-referencing binary (libcoreclr.so)
-        //	 0 means fallback to CoreCLR's internal resolution
-        // Note: meaning of -1 stay the same even for non-single-file form factors.
-        static CoreCLR ()
-        {
-            NativeLibrary.SetDllImportResolver (
-                                                typeof (CoreCLR).Assembly,
-                                                (libraryName, assembly, searchPath) =>
-                                                    libraryName == "libcoreclr.so" ? -1 : nint.Zero
-                                               );
-        }
-
-        [DllImport ("libcoreclr.so")]
-        internal static extern nint dlopen (string filename, int flags);
-
-        [DllImport ("libcoreclr.so")]
-        internal static extern nint dlsym (nint handle, string symbol);
-    }
 }
 }

+ 3 - 3
Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs

@@ -380,9 +380,9 @@ public partial class Curses
 
 
     private static void LoadMethods ()
     private static void LoadMethods ()
     {
     {
-        string [] libs = UnmanagedLibrary.IsMacOSPlatform
-                             ? new [] { "libncurses.dylib" }
-                             : new [] { "libncursesw.so.6", "libncursesw.so.5" };
+        string [] libs = OperatingSystem.IsMacOS()
+                             ? ["libncurses.dylib"]
+                             : ["libncursesw.so.6", "libncursesw.so.5"];
         var attempts = 1;
         var attempts = 1;
 
 
         while (true)
         while (true)

+ 13 - 0
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -1356,6 +1356,19 @@ public static class EscSeqUtils
     /// </summary>
     /// </summary>
     public const string CSI_ReportDeviceAttributes_Terminator = "c";
     public const string CSI_ReportDeviceAttributes_Terminator = "c";
 
 
+    /*
+     TODO: depends on https://github.com/gui-cs/Terminal.Gui/pull/3768
+    /// <summary>
+    ///     CSI 16 t - Request sixel resolution (width and height in pixels)
+    /// </summary>
+    public static readonly AnsiEscapeSequenceRequest CSI_RequestSixelResolution = new () { Request = CSI + "16t", Terminator = "t" };
+
+    /// <summary>
+    ///     CSI 14 t - Request window size in pixels (width x height)
+    /// </summary>
+    public static readonly AnsiEscapeSequenceRequest CSI_RequestWindowSizeInPixels = new () { Request = CSI + "14t", Terminator = "t" };
+    */
+
     /// <summary>
     /// <summary>
     ///     CSI 1 8 t  | yes | yes |  yes  | report window size in chars
     ///     CSI 1 8 t  | yes | yes |  yes  | report window size in chars
     ///     https://terminalguide.namepad.de/seq/csi_st-18/
     ///     https://terminalguide.namepad.de/seq/csi_st-18/

+ 3 - 3
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -93,7 +93,7 @@ public class FakeDriver : ConsoleDriver
         FakeConsole.Clear ();
         FakeConsole.Clear ();
         ResizeScreen ();
         ResizeScreen ();
         CurrentAttribute = new Attribute (Color.White, Color.Black);
         CurrentAttribute = new Attribute (Color.White, Color.Black);
-        ClearContents ();
+        //ClearContents ();
 
 
         _mainLoopDriver = new FakeMainLoop (this);
         _mainLoopDriver = new FakeMainLoop (this);
         _mainLoopDriver.MockKeyPressed = MockKeyPressedHandler;
         _mainLoopDriver.MockKeyPressed = MockKeyPressedHandler;
@@ -165,8 +165,8 @@ public class FakeDriver : ConsoleDriver
                     if (attr != redrawAttr)
                     if (attr != redrawAttr)
                     {
                     {
                         redrawAttr = attr;
                         redrawAttr = attr;
-                        FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor ();
-                        FakeConsole.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor ();
+                        FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor16 ();
+                        FakeConsole.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor16 ();
                     }
                     }
 
 
                     outputWidth++;
                     outputWidth++;

+ 16 - 6
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -961,10 +961,10 @@ internal class NetDriver : ConsoleDriver
                             output.Append (
                             output.Append (
                                            EscSeqUtils.CSI_SetGraphicsRendition (
                                            EscSeqUtils.CSI_SetGraphicsRendition (
                                                                                  MapColors (
                                                                                  MapColors (
-                                                                                            (ConsoleColor)attr.Background.GetClosestNamedColor (),
+                                                                                            (ConsoleColor)attr.Background.GetClosestNamedColor16 (),
                                                                                             false
                                                                                             false
                                                                                            ),
                                                                                            ),
-                                                                                 MapColors ((ConsoleColor)attr.Foreground.GetClosestNamedColor ())
+                                                                                 MapColors ((ConsoleColor)attr.Foreground.GetClosestNamedColor16 ())
                                                                                 )
                                                                                 )
                                           );
                                           );
                         }
                         }
@@ -1020,6 +1020,15 @@ internal class NetDriver : ConsoleDriver
                 SetCursorPosition (lastCol, row);
                 SetCursorPosition (lastCol, row);
                 Console.Write (output);
                 Console.Write (output);
             }
             }
+
+            foreach (var s in Application.Sixel)
+            {
+                if (!string.IsNullOrWhiteSpace (s.SixelData))
+                {
+                    SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
+                    Console.Write (s.SixelData);
+                }
+            }
         }
         }
 
 
         SetCursorPosition (0, 0);
         SetCursorPosition (0, 0);
@@ -1126,9 +1135,10 @@ internal class NetDriver : ConsoleDriver
         _mainLoopDriver = new NetMainLoop (this);
         _mainLoopDriver = new NetMainLoop (this);
         _mainLoopDriver.ProcessInput = ProcessInput;
         _mainLoopDriver.ProcessInput = ProcessInput;
 
 
+
         return new MainLoop (_mainLoopDriver);
         return new MainLoop (_mainLoopDriver);
     }
     }
-
+    
     private void ProcessInput (InputResult inputEvent)
     private void ProcessInput (InputResult inputEvent)
     {
     {
         switch (inputEvent.EventType)
         switch (inputEvent.EventType)
@@ -1154,7 +1164,7 @@ internal class NetDriver : ConsoleDriver
 
 
                 break;
                 break;
             case EventType.Mouse:
             case EventType.Mouse:
-                MouseEvent me = ToDriverMouse (inputEvent.MouseEvent);
+                MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);
                 //Debug.WriteLine ($"NetDriver: ({me.X},{me.Y}) - {me.Flags}");
                 //Debug.WriteLine ($"NetDriver: ({me.X},{me.Y}) - {me.Flags}");
                 OnMouseEvent (me);
                 OnMouseEvent (me);
 
 
@@ -1393,7 +1403,7 @@ internal class NetDriver : ConsoleDriver
         }
         }
     }
     }
 
 
-    private MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
+    private MouseEventArgs ToDriverMouse (NetEvents.MouseEvent me)
     {
     {
        //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
        //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
 
 
@@ -1539,7 +1549,7 @@ internal class NetDriver : ConsoleDriver
             mouseFlag |= MouseFlags.ButtonAlt;
             mouseFlag |= MouseFlags.ButtonAlt;
         }
         }
 
 
-        return new MouseEvent { Position = me.Position, Flags = mouseFlag };
+        return new MouseEventArgs { Position = me.Position, Flags = mouseFlag };
     }
     }
 
 
     #endregion Mouse Handling
     #endregion Mouse Handling

+ 24 - 7
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -37,6 +37,7 @@ internal class WindowsConsole
     private CursorVisibility? _currentCursorVisibility;
     private CursorVisibility? _currentCursorVisibility;
     private CursorVisibility? _pendingCursorVisibility;
     private CursorVisibility? _pendingCursorVisibility;
     private readonly StringBuilder _stringBuilder = new (256 * 1024);
     private readonly StringBuilder _stringBuilder = new (256 * 1024);
+    private string _lastWrite = string.Empty;
 
 
     public WindowsConsole ()
     public WindowsConsole ()
     {
     {
@@ -54,6 +55,8 @@ internal class WindowsConsole
 
 
     public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord bufferSize, SmallRect window, bool force16Colors)
     public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord bufferSize, SmallRect window, bool force16Colors)
     {
     {
+        //Debug.WriteLine ("WriteToConsole");
+
         if (_screenBuffer == nint.Zero)
         if (_screenBuffer == nint.Zero)
         {
         {
             ReadFromConsoleOutput (size, bufferSize, ref window);
             ReadFromConsoleOutput (size, bufferSize, ref window);
@@ -72,7 +75,7 @@ internal class WindowsConsole
                 {
                 {
                     Char = new CharUnion { UnicodeChar = info.Char },
                     Char = new CharUnion { UnicodeChar = info.Char },
                     Attributes =
                     Attributes =
-                        (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor () | ((int)info.Attribute.Background.GetClosestNamedColor () << 4))
+                        (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor16 () | ((int)info.Attribute.Background.GetClosestNamedColor16 () << 4))
                 };
                 };
             }
             }
 
 
@@ -116,7 +119,21 @@ internal class WindowsConsole
 
 
             var s = _stringBuilder.ToString ();
             var s = _stringBuilder.ToString ();
 
 
-            result = WriteConsole (_screenBuffer, s, (uint)s.Length, out uint _, nint.Zero);
+            // TODO: requires extensive testing if we go down this route
+            // If console output has changed
+            if (s != _lastWrite)
+            {
+                // supply console with the new content
+                result = WriteConsole (_screenBuffer, s, (uint)s.Length, out uint _, nint.Zero);
+            }
+
+            _lastWrite = s;
+
+            foreach (var sixel in Application.Sixel)
+            {
+                SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y));
+                WriteConsole (_screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
+            }
         }
         }
 
 
         if (!result)
         if (!result)
@@ -1481,7 +1498,7 @@ internal class WindowsDriver : ConsoleDriver
                 break;
                 break;
 
 
             case WindowsConsole.EventType.Mouse:
             case WindowsConsole.EventType.Mouse:
-                MouseEvent me = ToDriverMouse (inputEvent.MouseEvent);
+                MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);
 
 
                 if (me is null || me.Flags == MouseFlags.None)
                 if (me is null || me.Flags == MouseFlags.None)
                 {
                 {
@@ -1825,9 +1842,9 @@ internal class WindowsDriver : ConsoleDriver
             }
             }
             await Task.Delay (delay);
             await Task.Delay (delay);
 
 
-            var me = new MouseEvent
+            var me = new MouseEventArgs
             {
             {
-                Position = _pointMove,
+                ScreenPosition = _pointMove,
                 Flags = mouseFlag
                 Flags = mouseFlag
             };
             };
 
 
@@ -1881,7 +1898,7 @@ internal class WindowsDriver : ConsoleDriver
     }
     }
 
 
     [CanBeNull]
     [CanBeNull]
-    private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
+    private MouseEventArgs ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
     {
     {
         var mouseFlag = MouseFlags.AllEvents;
         var mouseFlag = MouseFlags.AllEvents;
 
 
@@ -2125,7 +2142,7 @@ internal class WindowsDriver : ConsoleDriver
         //System.Diagnostics.Debug.WriteLine (
         //System.Diagnostics.Debug.WriteLine (
         //	$"point.X:{(point is { } ? ((Point)point).X : -1)};point.Y:{(point is { } ? ((Point)point).Y : -1)}");
         //	$"point.X:{(point is { } ? ((Point)point).X : -1)};point.Y:{(point is { } ? ((Point)point).Y : -1)}");
 
 
-        return new MouseEvent
+        return new MouseEventArgs
         {
         {
             Position = new (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y),
             Position = new (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y),
             Flags = mouseFlag
             Flags = mouseFlag

+ 55 - 53
Terminal.Gui/Drawing/Alignment.cs

@@ -10,72 +10,74 @@ public enum Alignment
 {
 {
     /// <summary>
     /// <summary>
     ///     The items will be aligned to the start (left or top) of the container.
     ///     The items will be aligned to the start (left or top) of the container.
+    ///     <remarks>
+    ///         <para>
+    ///             If the container is smaller than the total size of the items, the end items will be clipped (their
+    ///             locations
+    ///             will be greater than the container size).
+    ///         </para>
+    ///         <para>
+    ///             The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |111 2222 33333    |
+    ///         </c>
+    ///     </example>
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the end items will be clipped (their locations
-    ///         will be greater than the container size).
-    ///     </para>
-    ///     <para>
-    ///         The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |111 2222 33333    |
-    ///     </c>
-    /// </example>
     Start = 0,
     Start = 0,
 
 
     /// <summary>
     /// <summary>
     ///     The items will be aligned to the end (right or bottom) of the container.
     ///     The items will be aligned to the end (right or bottom) of the container.
+    ///     <remarks>
+    ///         <para>
+    ///             If the container is smaller than the total size of the items, the start items will be clipped (their
+    ///             locations
+    ///             will be negative).
+    ///         </para>
+    ///         <para>
+    ///             The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |    111 2222 33333|
+    ///         </c>
+    ///     </example>
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the start items will be clipped (their locations
-    ///         will be negative).
-    ///     </para>
-    ///     <para>
-    ///         The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |    111 2222 33333|
-    ///     </c>
-    /// </example>
     End,
     End,
 
 
     /// <summary>
     /// <summary>
     ///     Center in the available space.
     ///     Center in the available space.
+    ///     <remarks>
+    ///         <para>
+    ///             If centering is not possible, the group will be left-aligned.
+    ///         </para>
+    ///         <para>
+    ///             Extra space will be distributed between the items, biased towards the left.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |  111 2222 33333  |
+    ///         </c>
+    ///     </example>
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///     If centering is not possible, the group will be left-aligned.
-    ///     </para>
-    ///     <para>
-    ///         Extra space will be distributed between the items, biased towards the left.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |  111 2222 33333  |
-    ///     </c>
-    /// </example>
     Center,
     Center,
 
 
     /// <summary>
     /// <summary>
     ///     The items will fill the available space.
     ///     The items will fill the available space.
+    ///     <remarks>
+    ///         <para>
+    ///             Extra space will be distributed between the items, biased towards the end.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |111  2222    33333|
+    ///         </c>
+    ///     </example>
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         Extra space will be distributed between the items, biased towards the end.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///        |111  2222    33333|
-    ///     </c>
-    /// </example>
-    Fill,
-}
+    Fill
+}

+ 8 - 14
Terminal.Gui/Drawing/AlignmentModes.cs

@@ -17,30 +17,24 @@ public enum AlignmentModes
     /// <summary>
     /// <summary>
     ///     The items will be arranged from end (right/bottom) to start (left/top).
     ///     The items will be arranged from end (right/bottom) to start (left/top).
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     Not implemented.
-    /// </remarks>
     EndToStart = 1,
     EndToStart = 1,
 
 
     /// <summary>
     /// <summary>
     ///     At least one space will be added between items. Useful for justifying text where at least one space is needed.
     ///     At least one space will be added between items. Useful for justifying text where at least one space is needed.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    ///     <para>
-    ///         If the total size of the items is greater than the container size, the space between items will be ignored
-    ///         starting from the end.
-    ///     </para>
+    ///     If the total size of the items is greater than the container size, the space between items will be ignored
+    ///     starting from the end.
     /// </remarks>
     /// </remarks>
     AddSpaceBetweenItems = 2,
     AddSpaceBetweenItems = 2,
 
 
     /// <summary>
     /// <summary>
-    ///    When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment (the first or last item) will be ignored.
+    ///     When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment
+    ///     (the first or last item) will be ignored.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the end items will be clipped (their locations
-    ///         will be greater than the container size).
-    ///     </para>
+    ///     If the container is smaller than the total size of the items, the end items will be clipped (their locations
+    ///     will be greater than the container size).
     /// </remarks>
     /// </remarks>
     /// <example>
     /// <example>
     ///     <c>
     ///     <c>
@@ -48,5 +42,5 @@ public enum AlignmentModes
     ///         End:   |111     2222 33333|
     ///         End:   |111     2222 33333|
     ///     </c>
     ///     </c>
     /// </example>
     /// </example>
-    IgnoreFirstOrLast = 4,
-}
+    IgnoreFirstOrLast = 4
+}

+ 20 - 0
Terminal.Gui/Drawing/AssumeSupportDetector.cs

@@ -0,0 +1,20 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Implementation of <see cref="ISixelSupportDetector"/> that assumes best
+///     case scenario (full support including transparency with 10x20 resolution).
+/// </summary>
+public class AssumeSupportDetector : ISixelSupportDetector
+{
+    /// <inheritdoc/>
+    public SixelSupportResult Detect ()
+    {
+        return new()
+        {
+            IsSupported = true,
+            MaxPaletteColors = 256,
+            Resolution = new (10, 20),
+            SupportsTransparency = true
+        };
+    }
+}

+ 6 - 6
Terminal.Gui/Drawing/Attribute.cs

@@ -15,7 +15,7 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
 {
 {
     /// <summary>Default empty attribute.</summary>
     /// <summary>Default empty attribute.</summary>
     [JsonIgnore]
     [JsonIgnore]
-    public static Attribute Default => new (Color.White, ColorName.Black);
+    public static Attribute Default => new (Color.White, Color.Black);
 
 
     /// <summary>The <see cref="ConsoleDriver"/>-specific color value.</summary>
     /// <summary>The <see cref="ConsoleDriver"/>-specific color value.</summary>
     [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
     [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
@@ -65,28 +65,28 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Initializes a new instance with a <see cref="ColorName"/> value. Both <see cref="Foreground"/> and
+    ///     Initializes a new instance with a <see cref="ColorName16"/> value. Both <see cref="Foreground"/> and
     ///     <see cref="Background"/> will be set to the specified color.
     ///     <see cref="Background"/> will be set to the specified color.
     /// </summary>
     /// </summary>
     /// <param name="colorName">Value.</param>
     /// <param name="colorName">Value.</param>
-    internal Attribute (in ColorName colorName) : this (in colorName, in colorName) { }
+    internal Attribute (in ColorName16 colorName) : this (in colorName, in colorName) { }
 
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="backgroundName">Background</param>
     /// <param name="backgroundName">Background</param>
-    public Attribute (in ColorName foregroundName, in ColorName backgroundName)
+    public Attribute (in ColorName16 foregroundName, in ColorName16 backgroundName)
         : this (new Color (in foregroundName), new Color (in backgroundName))
         : this (new Color (in foregroundName), new Color (in backgroundName))
     { }
     { }
 
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="background">Background</param>
     /// <param name="background">Background</param>
-    public Attribute (in ColorName foregroundName, in Color background) : this (new Color (in foregroundName), in background) { }
+    public Attribute (in ColorName16 foregroundName, in Color background) : this (new Color (in foregroundName), in background) { }
 
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foreground">Foreground</param>
     /// <param name="foreground">Foreground</param>
     /// <param name="backgroundName">Background</param>
     /// <param name="backgroundName">Background</param>
-    public Attribute (in Color foreground, in ColorName backgroundName) : this (in foreground, new Color (in backgroundName)) { }
+    public Attribute (in Color foreground, in ColorName16 backgroundName) : this (in foreground, new Color (in backgroundName)) { }
 
 
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of the <see cref="Attribute"/> struct with the same colors for the foreground and
     ///     Initializes a new instance of the <see cref="Attribute"/> struct with the same colors for the foreground and

+ 152 - 6
Terminal.Gui/Drawing/Cell.cs

@@ -4,19 +4,18 @@
 ///     Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
 ///     Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
 ///     <see cref="ConsoleDriver"/>).
 ///     <see cref="ConsoleDriver"/>).
 /// </summary>
 /// </summary>
-public record struct Cell ()
+public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default)
 {
 {
-
     /// <summary>The attributes to use when drawing the Glyph.</summary>
     /// <summary>The attributes to use when drawing the Glyph.</summary>
-    public Attribute? Attribute { get; set; } = null;
+    public Attribute? Attribute { get; set; } = Attribute;
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
     ///     Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
     ///     last time it was drawn.
     ///     last time it was drawn.
     /// </summary>
     /// </summary>
-    public bool IsDirty { get; set; } = false;
+    public bool IsDirty { get; set; } = IsDirty;
 
 
-    private Rune _rune = default;
+    private Rune _rune = Rune;
 
 
     /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
     /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
     public Rune Rune
     public Rune Rune
@@ -29,6 +28,8 @@ public record struct Cell ()
         }
         }
     }
     }
 
 
+    private List<Rune> _combiningMarks;
+
     /// <summary>
     /// <summary>
     ///     The combining marks for <see cref="Rune"/> that when combined makes this Cell a combining sequence. If
     ///     The combining marks for <see cref="Rune"/> that when combined makes this Cell a combining sequence. If
     ///     <see cref="CombiningMarks"/> empty, then <see cref="CombiningMarks"/> is ignored.
     ///     <see cref="CombiningMarks"/> empty, then <see cref="CombiningMarks"/> is ignored.
@@ -37,8 +38,153 @@ public record struct Cell ()
     ///     Only valid in the rare case where <see cref="Rune"/> is a combining sequence that could not be normalized to a
     ///     Only valid in the rare case where <see cref="Rune"/> is a combining sequence that could not be normalized to a
     ///     single Rune.
     ///     single Rune.
     /// </remarks>
     /// </remarks>
-    internal List<Rune> CombiningMarks { get; } = new ();
+    internal List<Rune> CombiningMarks
+    {
+        get => _combiningMarks ?? [];
+        private set => _combiningMarks = value ?? [];
+    }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override string ToString () { return $"[{Rune}, {Attribute}]"; }
     public override string ToString () { return $"[{Rune}, {Attribute}]"; }
+
+    /// <summary>Converts the string into a <see cref="List{Cell}"/>.</summary>
+    /// <param name="str">The string to convert.</param>
+    /// <param name="attribute">The <see cref="Gui.ColorScheme"/> to use.</param>
+    /// <returns></returns>
+    public static List<Cell> ToCellList (string str, Attribute? attribute = null)
+    {
+        List<Cell> cells = new ();
+
+        foreach (Rune rune in str.EnumerateRunes ())
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    /// <summary>
+    ///     Splits a string into a List that will contain a <see cref="List{Cell}"/> for each line.
+    /// </summary>
+    /// <param name="content">The string content.</param>
+    /// <param name="attribute">The color scheme.</param>
+    /// <returns>A <see cref="List{Cell}"/> for each line.</returns>
+    public static List<List<Cell>> StringToLinesOfCells (string content, Attribute? attribute = null)
+    {
+        List<Cell> cells = content.EnumerateRunes ()
+                                  .Select (x => new Cell { Rune = x, Attribute = attribute })
+                                  .ToList ();
+
+        return SplitNewLines (cells);
+    }
+
+    /// <summary>Converts a <see cref="Cell"/> generic collection into a string.</summary>
+    /// <param name="cells">The enumerable cell to convert.</param>
+    /// <returns></returns>
+    public static string ToString (IEnumerable<Cell> cells)
+    {
+        var str = string.Empty;
+
+        foreach (Cell cell in cells)
+        {
+            str += cell.Rune.ToString ();
+        }
+
+        return str;
+    }
+
+    /// <summary>Converts a <see cref="List{Cell}"/> generic collection into a string.</summary>
+    /// <param name="cellsList">The enumerable cell to convert.</param>
+    /// <returns></returns>
+    public static string ToString (List<List<Cell>> cellsList)
+    {
+        var str = string.Empty;
+
+        for (var i = 0; i < cellsList.Count; i++)
+        {
+            IEnumerable<Cell> cellList = cellsList [i];
+            str += ToString (cellList);
+
+            if (i + 1 < cellsList.Count)
+            {
+                str += Environment.NewLine;
+            }
+        }
+
+        return str;
+    }
+
+    // Turns the string into cells, this does not split the contents on a newline if it is present.
+
+    internal static List<Cell> StringToCells (string str, Attribute? attribute = null)
+    {
+        List<Cell> cells = [];
+
+        foreach (Rune rune in str.ToRunes ())
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    internal static List<Cell> ToCells (IEnumerable<Rune> runes, Attribute? attribute = null)
+    {
+        List<Cell> cells = new ();
+
+        foreach (Rune rune in runes)
+        {
+            cells.Add (new () { Rune = rune, Attribute = attribute });
+        }
+
+        return cells;
+    }
+
+    private static List<List<Cell>> SplitNewLines (List<Cell> cells)
+    {
+        List<List<Cell>> lines = [];
+        int start = 0, i = 0;
+        var hasCR = false;
+
+        // ASCII code 13 = Carriage Return.
+        // ASCII code 10 = Line Feed.
+        for (; i < cells.Count; i++)
+        {
+            if (cells [i].Rune.Value == 13)
+            {
+                hasCR = true;
+
+                continue;
+            }
+
+            if (cells [i].Rune.Value == 10)
+            {
+                if (i - start > 0)
+                {
+                    lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start));
+                }
+                else
+                {
+                    lines.Add (StringToCells (string.Empty));
+                }
+
+                start = i + 1;
+                hasCR = false;
+            }
+        }
+
+        if (i - start >= 0)
+        {
+            lines.Add (cells.GetRange (start, i - start));
+        }
+
+        return lines;
+    }
+
+    /// <summary>
+    ///     Splits a rune cell list into a List that will contain a <see cref="List{Cell}"/> for each line.
+    /// </summary>
+    /// <param name="cells">The cells list.</param>
+    /// <returns></returns>
+    public static List<List<Cell>> ToCells (List<Cell> cells) { return SplitNewLines (cells); }
 }
 }

+ 8 - 8
Terminal.Gui/Views/RuneCellEventArgs.cs → Terminal.Gui/Drawing/CellEventArgs.cs

@@ -1,27 +1,27 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Args for events that relate to a specific <see cref="RuneCell"/>.</summary>
-public class RuneCellEventArgs
+/// <summary>Args for events that relate to a specific <see cref="Cell"/>.</summary>
+public record struct CellEventArgs
 {
 {
-    /// <summary>Creates a new instance of the <see cref="RuneCellEventArgs"/> class.</summary>
+    /// <summary>Creates a new instance of the <see cref="CellEventArgs"/> class.</summary>
     /// <param name="line">The line.</param>
     /// <param name="line">The line.</param>
     /// <param name="col">The col index.</param>
     /// <param name="col">The col index.</param>
     /// <param name="unwrappedPosition">The unwrapped row and col index.</param>
     /// <param name="unwrappedPosition">The unwrapped row and col index.</param>
-    public RuneCellEventArgs (List<RuneCell> line, int col, (int Row, int Col) unwrappedPosition)
+    public CellEventArgs (List<Cell> line, int col, (int Row, int Col) unwrappedPosition)
     {
     {
         Line = line;
         Line = line;
         Col = col;
         Col = col;
         UnwrappedPosition = unwrappedPosition;
         UnwrappedPosition = unwrappedPosition;
     }
     }
 
 
-    /// <summary>The index of the RuneCell in the line.</summary>
+    /// <summary>The index of the Cell in the line.</summary>
     public int Col { get; }
     public int Col { get; }
 
 
-    /// <summary>The list of runes the RuneCell is part of.</summary>
-    public List<RuneCell> Line { get; }
+    /// <summary>The list of runes the Cell is part of.</summary>
+    public List<Cell> Line { get; }
 
 
     /// <summary>
     /// <summary>
-    ///     The unwrapped row and column index into the text containing the RuneCell. Unwrapped means the text without
+    ///     The unwrapped row and column index into the text containing the Cell. Unwrapped means the text without
     ///     word wrapping or other visual formatting having been applied.
     ///     word wrapping or other visual formatting having been applied.
     /// </summary>
     /// </summary>
     public (int Row, int Col) UnwrappedPosition { get; }
     public (int Row, int Col) UnwrappedPosition { get; }

+ 47 - 44
Terminal.Gui/Drawing/Color.ColorExtensions.cs

@@ -1,72 +1,75 @@
 using System.Collections.Frozen;
 using System.Collections.Frozen;
+using ColorHelper;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 internal static class ColorExtensions
 internal static class ColorExtensions
 {
 {
-    private static FrozenDictionary<Color, ColorName> colorToNameMap;
+    // TODO: This should be refactored to support all W3CColors (`ColorStrings` and this should be merged).
+    // TODO: ColorName and AnsiColorCode are only needed when a driver is in Force16Color mode and we
+    // TODO: should be able to remove these from any non-Driver-specific usages.
+    private static FrozenDictionary<Color, ColorName16> colorToNameMap;
 
 
     static ColorExtensions ()
     static ColorExtensions ()
     {
     {
-        Dictionary<ColorName, AnsiColorCode> nameToCodeMap = new ()
+        Dictionary<ColorName16, AnsiColorCode> nameToCodeMap = new ()
         {
         {
-            { ColorName.Black, AnsiColorCode.BLACK },
-            { ColorName.Blue, AnsiColorCode.BLUE },
-            { ColorName.Green, AnsiColorCode.GREEN },
-            { ColorName.Cyan, AnsiColorCode.CYAN },
-            { ColorName.Red, AnsiColorCode.RED },
-            { ColorName.Magenta, AnsiColorCode.MAGENTA },
-            { ColorName.Yellow, AnsiColorCode.YELLOW },
-            { ColorName.Gray, AnsiColorCode.WHITE },
-            { ColorName.DarkGray, AnsiColorCode.BRIGHT_BLACK },
-            { ColorName.BrightBlue, AnsiColorCode.BRIGHT_BLUE },
-            { ColorName.BrightGreen, AnsiColorCode.BRIGHT_GREEN },
-            { ColorName.BrightCyan, AnsiColorCode.BRIGHT_CYAN },
-            { ColorName.BrightRed, AnsiColorCode.BRIGHT_RED },
-            { ColorName.BrightMagenta, AnsiColorCode.BRIGHT_MAGENTA },
-            { ColorName.BrightYellow, AnsiColorCode.BRIGHT_YELLOW },
-            { ColorName.White, AnsiColorCode.BRIGHT_WHITE }
+            { ColorName16.Black, AnsiColorCode.BLACK },
+            { ColorName16.Blue, AnsiColorCode.BLUE },
+            { ColorName16.Green, AnsiColorCode.GREEN },
+            { ColorName16.Cyan, AnsiColorCode.CYAN },
+            { ColorName16.Red, AnsiColorCode.RED },
+            { ColorName16.Magenta, AnsiColorCode.MAGENTA },
+            { ColorName16.Yellow, AnsiColorCode.YELLOW },
+            { ColorName16.Gray, AnsiColorCode.WHITE },
+            { ColorName16.DarkGray, AnsiColorCode.BRIGHT_BLACK },
+            { ColorName16.BrightBlue, AnsiColorCode.BRIGHT_BLUE },
+            { ColorName16.BrightGreen, AnsiColorCode.BRIGHT_GREEN },
+            { ColorName16.BrightCyan, AnsiColorCode.BRIGHT_CYAN },
+            { ColorName16.BrightRed, AnsiColorCode.BRIGHT_RED },
+            { ColorName16.BrightMagenta, AnsiColorCode.BRIGHT_MAGENTA },
+            { ColorName16.BrightYellow, AnsiColorCode.BRIGHT_YELLOW },
+            { ColorName16.White, AnsiColorCode.BRIGHT_WHITE }
         };
         };
-        ColorNameToAnsiColorMap = nameToCodeMap.ToFrozenDictionary ();
+        ColorName16ToAnsiColorMap = nameToCodeMap.ToFrozenDictionary ();
 
 
-        ColorToNameMap = new Dictionary<Color, ColorName>
+        ColorToName16Map = new Dictionary<Color, ColorName16>
         {
         {
-            // using "Windows 10 Console/PowerShell 6" here: https://i.stack.imgur.com/9UVnC.png
-            // See also: https://en.wikipedia.org/wiki/ANSI_escape_code
-            { new Color (12, 12, 12), ColorName.Black },
-            { new Color (0, 55, 218), ColorName.Blue },
-            { new Color (19, 161, 14), ColorName.Green },
-            { new Color (58, 150, 221), ColorName.Cyan },
-            { new Color (197, 15, 31), ColorName.Red },
-            { new Color (136, 23, 152), ColorName.Magenta },
-            { new Color (128, 64, 32), ColorName.Yellow },
-            { new Color (204, 204, 204), ColorName.Gray },
-            { new Color (118, 118, 118), ColorName.DarkGray },
-            { new Color (59, 120, 255), ColorName.BrightBlue },
-            { new Color (22, 198, 12), ColorName.BrightGreen },
-            { new Color (97, 214, 214), ColorName.BrightCyan },
-            { new Color (231, 72, 86), ColorName.BrightRed },
-            { new Color (180, 0, 158), ColorName.BrightMagenta },
-            { new Color (249, 241, 165), ColorName.BrightYellow },
-            { new Color (242, 242, 242), ColorName.White }
+            // These match the values in Strings.resx, which are the W3C colors.
+            { new Color(0, 0, 0), ColorName16.Black },
+            { new Color(0, 0, 255), ColorName16.Blue },
+            { new Color(0, 128, 0), ColorName16.Green },
+            { new Color(0, 255, 255), ColorName16.Cyan },
+            { new Color(255, 0, 0), ColorName16.Red },
+            { new Color(255, 0, 255), ColorName16.Magenta },
+            { new Color(255, 255, 0), ColorName16.Yellow },
+            { new Color(128, 128, 128), ColorName16.Gray },
+            { new Color(118, 118, 118), ColorName16.DarkGray },
+            { new Color(59, 120, 255), ColorName16.BrightBlue },
+            { new Color(22, 198, 12), ColorName16.BrightGreen },
+            { new Color(97, 214, 214), ColorName16.BrightCyan },
+            { new Color(231, 72, 86), ColorName16.BrightRed },
+            { new Color(180, 0, 158), ColorName16.BrightMagenta },
+            { new Color(249, 241, 165), ColorName16.BrightYellow },
+            { new Color(255, 255, 255), ColorName16.White }
         }.ToFrozenDictionary ();
         }.ToFrozenDictionary ();
     }
     }
 
 
     /// <summary>Defines the 16 legacy color names and their corresponding ANSI color codes.</summary>
     /// <summary>Defines the 16 legacy color names and their corresponding ANSI color codes.</summary>
-    internal static FrozenDictionary<ColorName, AnsiColorCode> ColorNameToAnsiColorMap { get; }
+    internal static FrozenDictionary<ColorName16, AnsiColorCode> ColorName16ToAnsiColorMap { get; }
 
 
-    /// <summary>Reverse mapping for <see cref="ColorToNameMap"/>.</summary>
-    internal static FrozenDictionary<ColorName, Color> ColorNameToColorMap { get; private set; }
+    /// <summary>Reverse mapping for <see cref="ColorToName16Map"/>.</summary>
+    internal static FrozenDictionary<ColorName16, Color> ColorName16ToColorMap { get; private set; }
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets a <see cref="FrozenDictionary{TKey,TValue}"/> that maps legacy 16-color values to the
     ///     Gets or sets a <see cref="FrozenDictionary{TKey,TValue}"/> that maps legacy 16-color values to the
-    ///     corresponding <see cref="ColorName"/>.
+    ///     corresponding <see cref="ColorName16"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     Setter should be called as infrequently as possible, as <see cref="FrozenDictionary{TKey,TValue}"/> is
     ///     Setter should be called as infrequently as possible, as <see cref="FrozenDictionary{TKey,TValue}"/> is
     ///     expensive to create.
     ///     expensive to create.
     /// </remarks>
     /// </remarks>
-    internal static FrozenDictionary<Color, ColorName> ColorToNameMap
+    internal static FrozenDictionary<Color, ColorName16> ColorToName16Map
     {
     {
         get => colorToNameMap;
         get => colorToNameMap;
         set
         set
@@ -74,7 +77,7 @@ internal static class ColorExtensions
             colorToNameMap = value;
             colorToNameMap = value;
 
 
             //Also be sure to set the reverse mapping
             //Also be sure to set the reverse mapping
-            ColorNameToColorMap = value.ToFrozenDictionary (static kvp => kvp.Value, static kvp => kvp.Key);
+            ColorName16ToColorMap = value.ToFrozenDictionary (static kvp => kvp.Value, static kvp => kvp.Key);
         }
         }
     }
     }
 }
 }

+ 2 - 2
Terminal.Gui/Drawing/Color.ColorName.cs

@@ -8,10 +8,10 @@ namespace Terminal.Gui;
 ///     <para>These colors match the 16 colors defined for ANSI escape sequences for 4-bit (16) colors.</para>
 ///     <para>These colors match the 16 colors defined for ANSI escape sequences for 4-bit (16) colors.</para>
 ///     <para>
 ///     <para>
 ///         For terminals that support 24-bit color (TrueColor), the RGB values for each of these colors can be
 ///         For terminals that support 24-bit color (TrueColor), the RGB values for each of these colors can be
-///         configured using the <see cref="Color.Colors"/> property.
+///         configured using the <see cref="Color.Colors16"/> property.
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
-public enum ColorName
+public enum ColorName16
 {
 {
     /// <summary>The black color. ANSI escape sequence: <c>\u001b[30m</c>.</summary>
     /// <summary>The black color. ANSI escape sequence: <c>\u001b[30m</c>.</summary>
     Black,
     Black,

+ 180 - 177
Terminal.Gui/Drawing/Color.Formatting.cs

@@ -97,49 +97,49 @@ public readonly partial record struct Color
     )
     )
     {
     {
         return (formatString, formatProvider) switch
         return (formatString, formatProvider) switch
-               {
-                   // Null or empty string and null formatProvider - Revert to 'g' case behavior
-                   (null or { Length: 0 }, null) => ToString (),
-
-                   // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
-                   (null or { Length: 0 }, ICustomColorFormatter ccf) => ccf.Format (null, R, G, B, A),
-
-                   // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
-                   (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
-                       string.Format (formatProvider, formatString ?? string.Empty, R, G, B, A),
-
-                   // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
-                   (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
-                       $"#{R:X2}{G:X2}{B:X2}",
-
-                   // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
-                   ({ }, { }) => string.Format (formatProvider, CompositeFormat.Parse (formatString), R, G, B, A),
-
-                   // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
-                   (['g'], null) => ToString (),
-
-                   // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
-                   (['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
-
-                   // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
-                   (['d'], null) => $"rgb({R:D},{G:D},{B:D})",
-
-                   // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
-                   (['D'], null) => $"rgba({R:D},{G:D},{B:D},{A:D})",
-
-                   // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
-                   ({ }, _) => string.Format (
-                                              formatProvider ?? CultureInfo.InvariantCulture,
-                                              CompositeFormat.Parse (formatString),
-                                              R,
-                                              G,
-                                              B,
-                                              A
-                                             ),
-                   _ => throw new InvalidOperationException (
-                                                             $"Unable to create string from Color with value {Argb}, using format string {formatString}"
-                                                            )
-               }
+        {
+            // Null or empty string and null formatProvider - Revert to 'g' case behavior
+            (null or { Length: 0 }, null) => ToString (),
+
+            // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
+            (null or { Length: 0 }, ICustomColorFormatter ccf) => ccf.Format (null, R, G, B, A),
+
+            // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+            (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
+                string.Format (formatProvider, formatString ?? string.Empty, R, G, B, A),
+
+            // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+            (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
+                $"#{R:X2}{G:X2}{B:X2}",
+
+            // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
+            ({ }, { }) => string.Format (formatProvider, CompositeFormat.Parse (formatString), R, G, B, A),
+
+            // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
+            ( ['g'], null) => ToString (),
+
+            // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
+            ( ['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
+
+            // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
+            ( ['d'], null) => $"rgb({R:D},{G:D},{B:D})",
+
+            // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
+            ( ['D'], null) => $"rgba({R:D},{G:D},{B:D},{A:D})",
+
+            // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
+            ({ }, _) => string.Format (
+                                       formatProvider ?? CultureInfo.InvariantCulture,
+                                       CompositeFormat.Parse (formatString),
+                                       R,
+                                       G,
+                                       B,
+                                       A
+                                      ),
+            _ => throw new InvalidOperationException (
+                                                      $"Unable to create string from Color with value {Argb}, using format string {formatString}"
+                                                     )
+        }
                ?? throw new InvalidOperationException (
                ?? throw new InvalidOperationException (
                                                        $"Unable to create string from Color with value {Argb}, using format string {formatString}"
                                                        $"Unable to create string from Color with value {Argb}, using format string {formatString}"
                                                       );
                                                       );
@@ -205,7 +205,7 @@ public readonly partial record struct Color
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// </param>
     /// <param name="formatProvider">
     /// <param name="formatProvider">
     ///     If specified and not <see langword="null"/>, will be passed to
     ///     If specified and not <see langword="null"/>, will be passed to
@@ -246,7 +246,7 @@ public readonly partial record struct Color
     /// </summary>
     /// </summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#RGBA", "#AARRGGBB", "rgb(r,g,b)",
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#RGBA", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// </param>
     /// <param name="formatProvider">
     /// <param name="formatProvider">
     ///     Optional <see cref="IFormatProvider"/> to provide parsing services for the input text.
     ///     Optional <see cref="IFormatProvider"/> to provide parsing services for the input text.
@@ -265,95 +265,95 @@ public readonly partial record struct Color
     public static Color Parse (ReadOnlySpan<char> text, IFormatProvider? formatProvider = null)
     public static Color Parse (ReadOnlySpan<char> text, IFormatProvider? formatProvider = null)
     {
     {
         return text switch
         return text switch
-               {
-                   // Null string or empty span provided
-                   { IsEmpty: true } when formatProvider is null => throw new ColorParseException (
-                                                                                                   in text,
-                                                                                                   "The text provided was null or empty.",
-                                                                                                   in text
-                                                                                                  ),
-
-                   // A valid ICustomColorFormatter was specified and the text wasn't null or empty
-                   { IsEmpty: false } when formatProvider is ICustomColorFormatter f => f.Parse (text),
-
-                   // Input string is only whitespace
-                   { Length: > 0 } when text.IsWhiteSpace () => throw new ColorParseException (
-                                                                                               in text,
-                                                                                               "The text provided consisted of only whitespace characters.",
-                                                                                               in text
-                                                                                              ),
-
-                   // Any string too short to possibly be any supported format.
-                   { Length: > 0 and < 3 } => throw new ColorParseException (
-                                                                             in text,
-                                                                             "Text was too short to be any possible supported format.",
-                                                                             in text
-                                                                            ),
-
-                   // The various hexadecimal cases
-                   ['#', ..] hexString => hexString switch
-                                          {
-                                              // #RGB
-                                              ['#', var rChar, var gChar, var bChar] chars when chars [1..]
-                                                      .IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([bChar, bChar], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #ARGB
-                                              ['#', var aChar, var rChar, var gChar, var bChar] chars when chars [1..]
-                                                      .IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([bChar, bChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([aChar, aChar], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #RRGGBB
-                                              [
-                                                      '#', var r1Char, var r2Char, var g1Char, var g2Char, var b1Char,
-                                                      var b2Char
-                                                  ] chars when chars [1..].IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #AARRGGBB
-                                              [
-                                                      '#', var a1Char, var a2Char, var r1Char, var r2Char, var g1Char,
-                                                      var g2Char, var b1Char, var b2Char
-                                                  ] chars when chars [1..].IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([a1Char, a2Char], NumberStyles.HexNumber)
-                                                            ),
-                                              _ => throw new ColorParseException (
-                                                                                  in hexString,
-                                                                                  $"Color hex string {hexString} was not in a supported format",
-                                                                                  in hexString
-                                                                                 )
-                                          },
-
-                   // rgb(r,g,b) or rgb(r,g,b,a)
-                   ['r', 'g', 'b', '(', .., ')'] => ParseRgbaFormat (in text, 4),
-
-                   // rgba(r,g,b,a) or rgba(r,g,b)
-                   ['r', 'g', 'b', 'a', '(', .., ')'] => ParseRgbaFormat (in text, 5),
-
-                   // Attempt to parse as a named color from the ColorName enum
-                   { } when char.IsLetter (text [0]) && Enum.TryParse (text, true, out ColorName colorName) =>
-                       new Color (colorName),
-
-                   // Any other input
-                   _ => throw new ColorParseException (in text, "Text did not match any expected format.", in text, [])
-               };
+        {
+            // Null string or empty span provided
+            { IsEmpty: true } when formatProvider is null => throw new ColorParseException (
+                                                                                            in text,
+                                                                                            "The text provided was null or empty.",
+                                                                                            in text
+                                                                                           ),
+
+            // A valid ICustomColorFormatter was specified and the text wasn't null or empty
+            { IsEmpty: false } when formatProvider is ICustomColorFormatter f => f.Parse (text),
+
+            // Input string is only whitespace
+            { Length: > 0 } when text.IsWhiteSpace () => throw new ColorParseException (
+                                                                                        in text,
+                                                                                        "The text provided consisted of only whitespace characters.",
+                                                                                        in text
+                                                                                       ),
+
+            // Any string too short to possibly be any supported format.
+            { Length: > 0 and < 3 } => throw new ColorParseException (
+                                                                      in text,
+                                                                      "Text was too short to be any possible supported format.",
+                                                                      in text
+                                                                     ),
+
+                                                                     // The various hexadecimal cases
+                                                                     ['#', ..] hexString => hexString switch
+                                                                     {
+                                                                     // #RGB
+                                                                     ['#', var rChar, var gChar, var bChar] chars when chars [1..]
+                                                                                    .IsAllAsciiHexDigits () =>
+                                                                                new Color (
+                                                                                           byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
+                                                                                           byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
+                                                                                           byte.Parse ([bChar, bChar], NumberStyles.HexNumber)
+                                                                                          ),
+
+                                                                                          // #ARGB
+                                                                                          ['#', var aChar, var rChar, var gChar, var bChar] chars when chars [1..]
+                                                                                                         .IsAllAsciiHexDigits () =>
+                                                                                                     new Color (
+                                                                                                                byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([bChar, bChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([aChar, aChar], NumberStyles.HexNumber)
+                                                                                                               ),
+
+                                                                                                               // #RRGGBB
+                                                                                                               [
+                                                                                         '#', var r1Char, var r2Char, var g1Char, var g2Char, var b1Char,
+                                                                                         var b2Char
+                                                                                     ] chars when chars [1..].IsAllAsciiHexDigits () =>
+                                                                                     new Color (
+                                                                                                byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber)
+                                                                                               ),
+
+                                                                                               // #AARRGGBB
+                                                                                               [
+                                                                                         '#', var a1Char, var a2Char, var r1Char, var r2Char, var g1Char,
+                                                                                         var g2Char, var b1Char, var b2Char
+                                                                                     ] chars when chars [1..].IsAllAsciiHexDigits () =>
+                                                                                     new Color (
+                                                                                                byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([a1Char, a2Char], NumberStyles.HexNumber)
+                                                                                               ),
+                                                                         _ => throw new ColorParseException (
+                                                                                                                    in hexString,
+                                                                                                                    $"Color hex string {hexString} was not in a supported format",
+                                                                                                                    in hexString
+                                                                                                                   )
+                                                                     },
+
+                                                                     // rgb(r,g,b) or rgb(r,g,b,a)
+                                                                     ['r', 'g', 'b', '(', .., ')'] => ParseRgbaFormat (in text, 4),
+
+                                                                     // rgba(r,g,b,a) or rgba(r,g,b)
+                                                                     ['r', 'g', 'b', 'a', '(', .., ')'] => ParseRgbaFormat (in text, 5),
+
+            // Attempt to parse as a named color from the ColorStrings resources
+            { } when char.IsLetter (text [0]) && ColorStrings.TryParseW3CColorName (text.ToString (), out Color color) =>
+                new Color (color),
+
+            // Any other input
+            _ => throw new ColorParseException (in text, "Text did not match any expected format.", in text, [])
+        };
 
 
         [Pure]
         [Pure]
         [SkipLocalsInit]
         [SkipLocalsInit]
@@ -372,44 +372,44 @@ public readonly partial record struct Color
             switch (rangeCount)
             switch (rangeCount)
             {
             {
                 case 3:
                 case 3:
-                {
-                    // rgba(r,g,b)
-                    ParseRgbValues (
-                                    in valuesSubstring,
-                                    in valueRanges,
-                                    in originalString,
-                                    out ReadOnlySpan<char> rSpan,
-                                    out ReadOnlySpan<char> gSpan,
-                                    out ReadOnlySpan<char> bSpan
-                                   );
-
-                    return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan));
-                }
+                    {
+                        // rgba(r,g,b)
+                        ParseRgbValues (
+                                        in valuesSubstring,
+                                        in valueRanges,
+                                        in originalString,
+                                        out ReadOnlySpan<char> rSpan,
+                                        out ReadOnlySpan<char> gSpan,
+                                        out ReadOnlySpan<char> bSpan
+                                       );
+
+                        return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan));
+                    }
                 case 4:
                 case 4:
-                {
-                    // rgba(r,g,b,a)
-                    ParseRgbValues (
-                                    in valuesSubstring,
-                                    in valueRanges,
-                                    in originalString,
-                                    out ReadOnlySpan<char> rSpan,
-                                    out ReadOnlySpan<char> gSpan,
-                                    out ReadOnlySpan<char> bSpan
-                                   );
-                    ReadOnlySpan<char> aSpan = valuesSubstring [valueRanges [3]];
-
-                    if (!aSpan.IsAllAsciiDigits ())
                     {
                     {
-                        throw new ColorParseException (
-                                                       in originalString,
-                                                       "Value was not composed entirely of decimal digits.",
-                                                       in aSpan,
-                                                       nameof (A)
-                                                      );
+                        // rgba(r,g,b,a)
+                        ParseRgbValues (
+                                        in valuesSubstring,
+                                        in valueRanges,
+                                        in originalString,
+                                        out ReadOnlySpan<char> rSpan,
+                                        out ReadOnlySpan<char> gSpan,
+                                        out ReadOnlySpan<char> bSpan
+                                       );
+                        ReadOnlySpan<char> aSpan = valuesSubstring [valueRanges [3]];
+
+                        if (!aSpan.IsAllAsciiDigits ())
+                        {
+                            throw new ColorParseException (
+                                                           in originalString,
+                                                           "Value was not composed entirely of decimal digits.",
+                                                           in aSpan,
+                                                           nameof (A)
+                                                          );
+                        }
+
+                        return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan), int.Parse (aSpan));
                     }
                     }
-
-                    return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan), int.Parse (aSpan));
-                }
                 default:
                 default:
                     throw new ColorParseException (
                     throw new ColorParseException (
                                                    in originalString,
                                                    in originalString,
@@ -471,7 +471,7 @@ public readonly partial record struct Color
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor (Color)"/> string
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> string
     ///     values.
     ///     values.
     /// </param>
     /// </param>
     /// <param name="formatProvider">
     /// <param name="formatProvider">
@@ -501,7 +501,7 @@ public readonly partial record struct Color
     /// </summary>
     /// </summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor (Color)"/> string
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any W3C color name."/> string
     ///     values.
     ///     values.
     /// </param>
     /// </param>
     /// <param name="formatProvider">
     /// <param name="formatProvider">
@@ -585,17 +585,20 @@ public readonly partial record struct Color
     [SkipLocalsInit]
     [SkipLocalsInit]
     public override string ToString ()
     public override string ToString ()
     {
     {
-        // If Values has an exact match with a named color (in _colorNames), use that.
-        return ColorExtensions.ColorToNameMap.TryGetValue (this, out ColorName colorName)
-                   ? Enum.GetName (typeof (ColorName), colorName) ?? $"#{R:X2}{G:X2}{B:X2}"
-                   : // Otherwise return as an RGB hex value.
-                   $"#{R:X2}{G:X2}{B:X2}";
+        string? name = ColorStrings.GetW3CColorName (this);
+
+        if (name is { })
+        {
+            return name;
+        }
+
+        return $"#{R:X2}{G:X2}{B:X2}";
     }
     }
 
 
     /// <summary>Converts the provided string to a new <see cref="Color"/> instance.</summary>
     /// <summary>Converts the provided string to a new <see cref="Color"/> instance.</summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// </param>
     /// <param name="color">The parsed value.</param>
     /// <param name="color">The parsed value.</param>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>

+ 3 - 3
Terminal.Gui/Drawing/Color.Operators.cs

@@ -53,11 +53,11 @@ public readonly partial record struct Color
     public static implicit operator Color (uint u) { return new Color (u); }
     public static implicit operator Color (uint u) { return new Color (u); }
 
 
     /// <summary>
     /// <summary>
-    ///     Implicit conversion from <see cref="GetClosestNamedColor (Color)"/> to <see cref="Color"/> via lookup from
-    ///     <see cref="ColorExtensions.ColorNameToColorMap"/>.
+    ///     Implicit conversion from <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to <see cref="Color"/> via lookup from
+    ///     <see cref="ColorExtensions.ColorName16ToColorMap"/>.
     /// </summary>
     /// </summary>
     [Pure]
     [Pure]
-    public static implicit operator Color (ColorName colorName) { return ColorExtensions.ColorNameToColorMap [colorName]; }
+    public static implicit operator Color (ColorName16 colorName) { return ColorExtensions.ColorName16ToColorMap [colorName]; }
 
 
     /// <summary>
     /// <summary>
     ///     Implicit conversion from <see cref="Vector4"/> to <see cref="Color"/>, where (<see cref="Vector4.X"/>,
     ///     Implicit conversion from <see cref="Vector4"/> to <see cref="Color"/>, where (<see cref="Vector4.X"/>,

+ 40 - 40
Terminal.Gui/Drawing/Color.cs

@@ -17,7 +17,7 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 /// <seealso cref="Attribute"/>
 /// <seealso cref="Attribute"/>
 /// <seealso cref="ColorExtensions"/>
 /// <seealso cref="ColorExtensions"/>
-/// <seealso cref="ColorName"/>
+/// <seealso cref="ColorName16"/>
 [JsonConverter (typeof (ColorJsonConverter))]
 [JsonConverter (typeof (ColorJsonConverter))]
 [StructLayout (LayoutKind.Explicit)]
 [StructLayout (LayoutKind.Explicit)]
 public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanParsable<Color>, ISpanFormattable,
 public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanParsable<Color>, ISpanFormattable,
@@ -109,7 +109,7 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 
 
     /// <summary>Initializes a new instance of the <see cref="Color"/> color from a legacy 16-color named value.</summary>
     /// <summary>Initializes a new instance of the <see cref="Color"/> color from a legacy 16-color named value.</summary>
     /// <param name="colorName">The 16-color value.</param>
     /// <param name="colorName">The 16-color value.</param>
-    public Color (in ColorName colorName) { this = ColorExtensions.ColorNameToColorMap [colorName]; }
+    public Color (in ColorName16 colorName) { this = ColorExtensions.ColorName16ToColorMap [colorName]; }
 
 
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of the <see cref="Color"/> color from string. See
     ///     Initializes a new instance of the <see cref="Color"/> color from string. See
@@ -131,26 +131,28 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// <summary>Initializes a new instance of the <see cref="Color"/> with all channels set to 0.</summary>
     /// <summary>Initializes a new instance of the <see cref="Color"/> with all channels set to 0.</summary>
     public Color () { Argb = 0u; }
     public Color () { Argb = 0u; }
 
 
+    // TODO: ColorName and AnsiColorCode are only needed when a driver is in Force16Color mode and we
+    // TODO: should be able to remove these from any non-Driver-specific usages.
     /// <summary>Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.</summary>
     /// <summary>Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
-    public static Dictionary<ColorName, string> Colors
+    public static Dictionary<ColorName16, string> Colors16
     {
     {
         get =>
         get =>
 
 
             // Transform _colorToNameMap into a Dictionary<ColorNames,string>
             // Transform _colorToNameMap into a Dictionary<ColorNames,string>
-            ColorExtensions.ColorToNameMap.ToDictionary (static kvp => kvp.Value, static kvp => kvp.Key.ToString ("g"));
+            ColorExtensions.ColorToName16Map.ToDictionary (static kvp => kvp.Value, static kvp => kvp.Key.ToString ("g"));
         set
         set
         {
         {
             // Transform Dictionary<ColorNames,string> into _colorToNameMap
             // Transform Dictionary<ColorNames,string> into _colorToNameMap
-            ColorExtensions.ColorToNameMap = value.ToFrozenDictionary (GetColorToNameMapKey, GetColorToNameMapValue);
+            ColorExtensions.ColorToName16Map = value.ToFrozenDictionary (GetColorToNameMapKey, GetColorToNameMapValue);
 
 
             return;
             return;
 
 
-            static Color GetColorToNameMapKey (KeyValuePair<ColorName, string> kvp) { return new Color (kvp.Value); }
+            static Color GetColorToNameMapKey (KeyValuePair<ColorName16, string> kvp) { return new Color (kvp.Value); }
 
 
-            static ColorName GetColorToNameMapValue (KeyValuePair<ColorName, string> kvp)
+            static ColorName16 GetColorToNameMapValue (KeyValuePair<ColorName16, string> kvp)
             {
             {
-                return Enum.TryParse (kvp.Key.ToString (), true, out ColorName colorName)
+                return Enum.TryParse (kvp.Key.ToString (), true, out ColorName16 colorName)
                            ? colorName
                            ? colorName
                            : throw new ArgumentException ($"Invalid color name: {kvp.Key}");
                            : throw new ArgumentException ($"Invalid color name: {kvp.Key}");
             }
             }
@@ -158,31 +160,31 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName"/> value. <see langword="get"/> will
+    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName16"/> value. <see langword="get"/> will
     ///     return the closest 16 color match to the true color when no exact value is found.
     ///     return the closest 16 color match to the true color when no exact value is found.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    ///     Get returns the <see cref="GetClosestNamedColor (Color)"/> of the closest 24-bit color value. Set sets the RGB
+    ///     Get returns the <see cref="GetClosestNamedColor16(Color)"/> of the closest 24-bit color value. Set sets the RGB
     ///     value using a hard-coded map.
     ///     value using a hard-coded map.
     /// </remarks>
     /// </remarks>
-    public AnsiColorCode GetAnsiColorCode () { return ColorExtensions.ColorNameToAnsiColorMap [GetClosestNamedColor ()]; }
+    public AnsiColorCode GetAnsiColorCode () { return ColorExtensions.ColorName16ToAnsiColorMap [GetClosestNamedColor16 ()]; }
 
 
     /// <summary>
     /// <summary>
-    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="Gui.ColorName"/> value. <see langword="get"/>
+    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName16"/> value. <see langword="get"/>
     ///     will return the closest 16 color match to the true color when no exact value is found.
     ///     will return the closest 16 color match to the true color when no exact value is found.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    ///     Get returns the <see cref="GetClosestNamedColor (Color)"/> of the closest 24-bit color value. Set sets the RGB
+    ///     Get returns the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> of the closest 24-bit color value. Set sets the RGB
     ///     value using a hard-coded map.
     ///     value using a hard-coded map.
     /// </remarks>
     /// </remarks>
-    public ColorName GetClosestNamedColor () { return GetClosestNamedColor (this); }
+    public ColorName16 GetClosestNamedColor16 () { return GetClosestNamedColor16 (this); }
 
 
     /// <summary>
     /// <summary>
     ///     Determines if the closest named <see cref="Color"/> to <see langword="this"/> is the provided
     ///     Determines if the closest named <see cref="Color"/> to <see langword="this"/> is the provided
     ///     <paramref name="namedColor"/>.
     ///     <paramref name="namedColor"/>.
     /// </summary>
     /// </summary>
     /// <param name="namedColor">
     /// <param name="namedColor">
-    ///     The <see cref="GetClosestNamedColor (Color)"/> to check if this <see cref="Color"/> is closer
+    ///     The <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to check if this <see cref="Color"/> is closer
     ///     to than any other configured named color.
     ///     to than any other configured named color.
     /// </param>
     /// </param>
     /// <returns>
     /// <returns>
@@ -195,18 +197,18 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// </remarks>
     /// </remarks>
     [Pure]
     [Pure]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
-    public bool IsClosestToNamedColor (in ColorName namedColor) { return GetClosestNamedColor () == namedColor; }
+    public bool IsClosestToNamedColor16 (in ColorName16 namedColor) { return GetClosestNamedColor16 () == namedColor; }
 
 
     /// <summary>
     /// <summary>
     ///     Determines if the closest named <see cref="Color"/> to <paramref name="color"/>/> is the provided
     ///     Determines if the closest named <see cref="Color"/> to <paramref name="color"/>/> is the provided
     ///     <paramref name="namedColor"/>.
     ///     <paramref name="namedColor"/>.
     /// </summary>
     /// </summary>
     /// <param name="color">
     /// <param name="color">
-    ///     The color to test against the <see cref="GetClosestNamedColor (Color)"/> value in
+    ///     The color to test against the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> value in
     ///     <paramref name="namedColor"/>.
     ///     <paramref name="namedColor"/>.
     /// </param>
     /// </param>
     /// <param name="namedColor">
     /// <param name="namedColor">
-    ///     The <see cref="GetClosestNamedColor (Color)"/> to check if this <see cref="Color"/> is closer
+    ///     The <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to check if this <see cref="Color"/> is closer
     ///     to than any other configured named color.
     ///     to than any other configured named color.
     /// </param>
     /// </param>
     /// <returns>
     /// <returns>
@@ -220,20 +222,18 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// </remarks>
     /// </remarks>
     [Pure]
     [Pure]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
-    public static bool IsColorClosestToNamedColor (in Color color, in ColorName namedColor) { return color.IsClosestToNamedColor (in namedColor); }
+    public static bool IsColorClosestToNamedColor16 (in Color color, in ColorName16 namedColor) { return color.IsClosestToNamedColor16 (in namedColor); }
 
 
     /// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
     /// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
     /// <param name="inputColor"></param>
     /// <param name="inputColor"></param>
     /// <remarks>
     /// <remarks>
     ///     Distance is defined here as the Euclidean distance between each color interpreted as a <see cref="Vector3"/>.
     ///     Distance is defined here as the Euclidean distance between each color interpreted as a <see cref="Vector3"/>.
-    ///     <para/>
-    ///     The order of the values in the passed Vector3 must be
     /// </remarks>
     /// </remarks>
     /// <returns></returns>
     /// <returns></returns>
     [SkipLocalsInit]
     [SkipLocalsInit]
-    internal static ColorName GetClosestNamedColor (Color inputColor)
+    internal static ColorName16 GetClosestNamedColor16 (Color inputColor)
     {
     {
-        return ColorExtensions.ColorToNameMap.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
+        return ColorExtensions.ColorToName16Map.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
     }
     }
 
 
     [SkipLocalsInit]
     [SkipLocalsInit]
@@ -246,7 +246,7 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     public Color GetHighlightColor ()
     public Color GetHighlightColor ()
     {
     {
         // TODO: This is a temporary implementation; just enough to show how it could work. 
         // TODO: This is a temporary implementation; just enough to show how it could work. 
-        var hsl = ColorHelper.ColorConverter.RgbToHsl(new RGB (R, G, B));
+        var hsl = ColorHelper.ColorConverter.RgbToHsl (new RGB (R, G, B));
 
 
         var amount = .7;
         var amount = .7;
         if (hsl.L <= 5)
         if (hsl.L <= 5)
@@ -284,52 +284,52 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     #region Legacy Color Names
     #region Legacy Color Names
 
 
     /// <summary>The black color.</summary>
     /// <summary>The black color.</summary>
-    public const ColorName Black = ColorName.Black;
+    public const ColorName16 Black = ColorName16.Black;
 
 
     /// <summary>The blue color.</summary>
     /// <summary>The blue color.</summary>
-    public const ColorName Blue = ColorName.Blue;
+    public const ColorName16 Blue = ColorName16.Blue;
 
 
     /// <summary>The green color.</summary>
     /// <summary>The green color.</summary>
-    public const ColorName Green = ColorName.Green;
+    public const ColorName16 Green = ColorName16.Green;
 
 
     /// <summary>The cyan color.</summary>
     /// <summary>The cyan color.</summary>
-    public const ColorName Cyan = ColorName.Cyan;
+    public const ColorName16 Cyan = ColorName16.Cyan;
 
 
     /// <summary>The red color.</summary>
     /// <summary>The red color.</summary>
-    public const ColorName Red = ColorName.Red;
+    public const ColorName16 Red = ColorName16.Red;
 
 
     /// <summary>The magenta color.</summary>
     /// <summary>The magenta color.</summary>
-    public const ColorName Magenta = ColorName.Magenta;
+    public const ColorName16 Magenta = ColorName16.Magenta;
 
 
     /// <summary>The yellow color.</summary>
     /// <summary>The yellow color.</summary>
-    public const ColorName Yellow = ColorName.Yellow;
+    public const ColorName16 Yellow = ColorName16.Yellow;
 
 
     /// <summary>The gray color.</summary>
     /// <summary>The gray color.</summary>
-    public const ColorName Gray = ColorName.Gray;
+    public const ColorName16 Gray = ColorName16.Gray;
 
 
     /// <summary>The dark gray color.</summary>
     /// <summary>The dark gray color.</summary>
-    public const ColorName DarkGray = ColorName.DarkGray;
+    public const ColorName16 DarkGray = ColorName16.DarkGray;
 
 
     /// <summary>The bright bBlue color.</summary>
     /// <summary>The bright bBlue color.</summary>
-    public const ColorName BrightBlue = ColorName.BrightBlue;
+    public const ColorName16 BrightBlue = ColorName16.BrightBlue;
 
 
     /// <summary>The bright green color.</summary>
     /// <summary>The bright green color.</summary>
-    public const ColorName BrightGreen = ColorName.BrightGreen;
+    public const ColorName16 BrightGreen = ColorName16.BrightGreen;
 
 
     /// <summary>The bright cyan color.</summary>
     /// <summary>The bright cyan color.</summary>
-    public const ColorName BrightCyan = ColorName.BrightCyan;
+    public const ColorName16 BrightCyan = ColorName16.BrightCyan;
 
 
     /// <summary>The bright red color.</summary>
     /// <summary>The bright red color.</summary>
-    public const ColorName BrightRed = ColorName.BrightRed;
+    public const ColorName16 BrightRed = ColorName16.BrightRed;
 
 
     /// <summary>The bright magenta color.</summary>
     /// <summary>The bright magenta color.</summary>
-    public const ColorName BrightMagenta = ColorName.BrightMagenta;
+    public const ColorName16 BrightMagenta = ColorName16.BrightMagenta;
 
 
     /// <summary>The bright yellow color.</summary>
     /// <summary>The bright yellow color.</summary>
-    public const ColorName BrightYellow = ColorName.BrightYellow;
+    public const ColorName16 BrightYellow = ColorName16.BrightYellow;
 
 
     /// <summary>The White color.</summary>
     /// <summary>The White color.</summary>
-    public const ColorName White = ColorName.White;
+    public const ColorName16 White = ColorName16.White;
 
 
     #endregion
     #endregion
 }
 }

+ 43 - 55
Terminal.Gui/Drawing/ColorScheme.cs

@@ -15,12 +15,6 @@ namespace Terminal.Gui;
 [JsonConverter (typeof (ColorSchemeJsonConverter))]
 [JsonConverter (typeof (ColorSchemeJsonConverter))]
 public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
 public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
 {
 {
-    private readonly Attribute _disabled;
-    private readonly Attribute _focus;
-    private readonly Attribute _hotFocus;
-    private readonly Attribute _hotNormal;
-    private readonly Attribute _normal;
-
     /// <summary>Creates a new instance set to the default colors (see <see cref="Attribute.Default"/>).</summary>
     /// <summary>Creates a new instance set to the default colors (see <see cref="Attribute.Default"/>).</summary>
     public ColorScheme () : this (Attribute.Default) { }
     public ColorScheme () : this (Attribute.Default) { }
 
 
@@ -30,22 +24,22 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
     {
     {
         ArgumentNullException.ThrowIfNull (scheme);
         ArgumentNullException.ThrowIfNull (scheme);
 
 
-        _normal = scheme.Normal;
-        _focus = scheme.Focus;
-        _hotNormal = scheme.HotNormal;
-        _disabled = scheme.Disabled;
-        _hotFocus = scheme.HotFocus;
+        Normal = scheme.Normal;
+        Focus = scheme.Focus;
+        HotNormal = scheme.HotNormal;
+        Disabled = scheme.Disabled;
+        HotFocus = scheme.HotFocus;
     }
     }
 
 
     /// <summary>Creates a new instance, initialized with the values from <paramref name="attribute"/>.</summary>
     /// <summary>Creates a new instance, initialized with the values from <paramref name="attribute"/>.</summary>
     /// <param name="attribute">The attribute to initialize the new instance with.</param>
     /// <param name="attribute">The attribute to initialize the new instance with.</param>
     public ColorScheme (Attribute attribute)
     public ColorScheme (Attribute attribute)
     {
     {
-        _normal = attribute;
-        _focus = attribute;
-        _hotNormal = attribute;
-        _disabled = attribute;
-        _hotFocus = attribute;
+        Normal = attribute;
+        Focus = attribute;
+        HotNormal = attribute;
+        Disabled = attribute;
+        HotFocus = attribute;
     }
     }
 
 
     /// <summary>Creates a new instance, initialized with the values provided.</summary>
     /// <summary>Creates a new instance, initialized with the values provided.</summary>
@@ -54,48 +48,45 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
         Attribute focus,
         Attribute focus,
         Attribute hotNormal,
         Attribute hotNormal,
         Attribute disabled,
         Attribute disabled,
-        Attribute hotFocus)
+        Attribute hotFocus
+    )
     {
     {
-        _normal = normal;
-        _focus = focus;
-        _hotNormal = hotNormal;
-        _disabled = disabled;
-        _hotFocus = hotFocus;
+        Normal = normal;
+        Focus = focus;
+        HotNormal = hotNormal;
+        Disabled = disabled;
+        HotFocus = hotFocus;
     }
     }
 
 
     /// <summary>The default foreground and background color for text when the view is disabled.</summary>
     /// <summary>The default foreground and background color for text when the view is disabled.</summary>
-    public Attribute Disabled
-    {
-        get => _disabled;
-        init => _disabled = value;
-    }
+    public Attribute Disabled { get; init; }
 
 
     /// <summary>The foreground and background color for text when the view has the focus.</summary>
     /// <summary>The foreground and background color for text when the view has the focus.</summary>
-    public Attribute Focus
-    {
-        get => _focus;
-        init => _focus = value;
-    }
+    public Attribute Focus { get; init; }
 
 
     /// <summary>The foreground and background color for text in a focused view that indicates a <see cref="View.HotKey"/>.</summary>
     /// <summary>The foreground and background color for text in a focused view that indicates a <see cref="View.HotKey"/>.</summary>
-    public Attribute HotFocus
-    {
-        get => _hotFocus;
-        init => _hotFocus = value;
-    }
+    public Attribute HotFocus { get; init; }
 
 
     /// <summary>The foreground and background color for text in a non-focused view that indicates a <see cref="View.HotKey"/>.</summary>
     /// <summary>The foreground and background color for text in a non-focused view that indicates a <see cref="View.HotKey"/>.</summary>
-    public Attribute HotNormal
-    {
-        get => _hotNormal;
-        init => _hotNormal = value;
-    }
+    public Attribute HotNormal { get; init; }
 
 
     /// <summary>The foreground and background color for text when the view is not focused, hot, or disabled.</summary>
     /// <summary>The foreground and background color for text when the view is not focused, hot, or disabled.</summary>
-    public Attribute Normal
+    public Attribute Normal { get; init; }
+
+    /// <summary>
+    ///     Gets a new <see cref="ColorScheme"/> with the same values as this instance, but with the foreground and background
+    ///     colors adjusted to be more visible.
+    /// </summary>
+    /// <returns></returns>
+    public ColorScheme GetHighlightColorScheme ()
     {
     {
-        get => _normal;
-        init => _normal = value;
+        return this with
+        {
+            Normal = new (Normal.Foreground.GetHighlightColor (), Normal.Background),
+            HotNormal = new (HotNormal.Foreground.GetHighlightColor (), HotNormal.Background),
+            Focus = new (Focus.Foreground.GetHighlightColor (), Focus.Background),
+            HotFocus = new (HotFocus.Foreground.GetHighlightColor (), HotFocus.Background)
+        };
     }
     }
 
 
     /// <summary>Compares two <see cref="ColorScheme"/> objects for equality.</summary>
     /// <summary>Compares two <see cref="ColorScheme"/> objects for equality.</summary>
@@ -104,20 +95,17 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
     public virtual bool Equals (ColorScheme? other)
     public virtual bool Equals (ColorScheme? other)
     {
     {
         return other is { }
         return other is { }
-               && EqualityComparer<Attribute>.Default.Equals (_normal, other._normal)
-               && EqualityComparer<Attribute>.Default.Equals (_focus, other._focus)
-               && EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal)
-               && EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus)
-               && EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
+               && EqualityComparer<Attribute>.Default.Equals (Normal, other.Normal)
+               && EqualityComparer<Attribute>.Default.Equals (Focus, other.Focus)
+               && EqualityComparer<Attribute>.Default.Equals (HotNormal, other.HotNormal)
+               && EqualityComparer<Attribute>.Default.Equals (HotFocus, other.HotFocus)
+               && EqualityComparer<Attribute>.Default.Equals (Disabled, other.Disabled);
     }
     }
 
 
     /// <summary>Returns a hashcode for this instance.</summary>
     /// <summary>Returns a hashcode for this instance.</summary>
     /// <returns>hashcode for this instance</returns>
     /// <returns>hashcode for this instance</returns>
-    public override int GetHashCode ()
-    {
-        return HashCode.Combine (_normal, _focus, _hotNormal, _hotFocus, _disabled);
-    }
+    public override int GetHashCode () { return HashCode.Combine (Normal, Focus, HotNormal, HotFocus, Disabled); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override string ToString () { return $"Normal: {Normal}; Focus: {Focus}; HotNormal: {HotNormal}; HotFocus: {HotFocus}; Disabled: {Disabled}"; }
     public override string ToString () { return $"Normal: {Normal}; Focus: {Focus}; HotNormal: {HotNormal}; HotFocus: {HotFocus}; Disabled: {Disabled}"; }
-}
+}

+ 30 - 27
Terminal.Gui/Drawing/ColorStrings.cs

@@ -1,6 +1,7 @@
 #nullable enable
 #nullable enable
 using System.Collections;
 using System.Collections;
 using System.Globalization;
 using System.Globalization;
+using System.Resources;
 using Terminal.Gui.Resources;
 using Terminal.Gui.Resources;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
@@ -10,6 +11,8 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 public static class ColorStrings
 public static class ColorStrings
 {
 {
+    // PERFORMANCE: See https://stackoverflow.com/a/15521524/297526 for why GlobalResources.GetString is fast.
+
     /// <summary>
     /// <summary>
     ///     Gets the W3C standard string for <paramref name="color"/>.
     ///     Gets the W3C standard string for <paramref name="color"/>.
     /// </summary>
     /// </summary>
@@ -17,7 +20,6 @@ public static class ColorStrings
     /// <returns><see langword="null"/> if there is no standard color name for the specified color.</returns>
     /// <returns><see langword="null"/> if there is no standard color name for the specified color.</returns>
     public static string? GetW3CColorName (Color color)
     public static string? GetW3CColorName (Color color)
     {
     {
-        // Fetch the color name from the resource file
         return GlobalResources.GetString ($"#{color.R:X2}{color.G:X2}{color.B:X2}", CultureInfo.CurrentUICulture);
         return GlobalResources.GetString ($"#{color.R:X2}{color.G:X2}{color.B:X2}", CultureInfo.CurrentUICulture);
     }
     }
 
 
@@ -27,18 +29,18 @@ public static class ColorStrings
     /// <returns></returns>
     /// <returns></returns>
     public static IEnumerable<string> GetW3CColorNames ()
     public static IEnumerable<string> GetW3CColorNames ()
     {
     {
-        foreach (DictionaryEntry entry in GlobalResources.GetResourceSet (
-                                                                          CultureInfo.CurrentUICulture,
-                                                                          true,
-                                                                          true,
-                                                                          e =>
-                                                                          {
-                                                                              string keyName = e.Key.ToString () ?? string.Empty;
+        ResourceSet? resourceSet = GlobalResources.GetResourceSet (CultureInfo.CurrentUICulture, true, true);
+        if (resourceSet == null)
+        {
+            yield break;
+        }
 
 
-                                                                              return e.Value is string && keyName.StartsWith ('#');
-                                                                          })!)
+        foreach (DictionaryEntry entry in resourceSet)
         {
         {
-            yield return (entry.Value as string)!;
+            if (entry is { Value: string colorName, Key: string key } && key.StartsWith ('#'))
+            {
+                yield return colorName;
+            }
         }
         }
     }
     }
 
 
@@ -50,30 +52,31 @@ public static class ColorStrings
     /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
     /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
     public static bool TryParseW3CColorName (string name, out Color color)
     public static bool TryParseW3CColorName (string name, out Color color)
     {
     {
-        // Iterate through all resource entries to find the matching color name
         foreach (DictionaryEntry entry in GlobalResources.GetResourceSet (CultureInfo.CurrentUICulture, true, true)!)
         foreach (DictionaryEntry entry in GlobalResources.GetResourceSet (CultureInfo.CurrentUICulture, true, true)!)
         {
         {
             if (entry.Value is string colorName && colorName.Equals (name, StringComparison.OrdinalIgnoreCase))
             if (entry.Value is string colorName && colorName.Equals (name, StringComparison.OrdinalIgnoreCase))
             {
             {
-                // Parse the key to extract the color components
-                string key = entry.Key.ToString () ?? string.Empty;
+                return TryParseColorKey (entry.Key.ToString (), out color);
+            }
+        }
 
 
-                if (key.StartsWith ("#") && key.Length == 7)
-                {
-                    if (int.TryParse (key.Substring (1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int r)
-                        && int.TryParse (key.Substring (3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int g)
-                        && int.TryParse (key.Substring (5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int b))
-                    {
-                        color = new (r, g, b);
+        return TryParseColorKey (name, out color);
 
 
-                        return true;
-                    }
+        bool TryParseColorKey (string? key, out Color color)
+        {
+            if (key != null && key.StartsWith ('#') && key.Length == 7)
+            {
+                if (int.TryParse (key.AsSpan (1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int r) &&
+                    int.TryParse (key.AsSpan (3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int g) &&
+                    int.TryParse (key.AsSpan (5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int b))
+                {
+                    color = new Color (r, g, b);
+                    return true;
                 }
                 }
             }
             }
-        }
-
-        color = default (Color);
 
 
-        return false;
+            color = default (Color);
+            return false;
+        }
     }
     }
 }
 }

+ 31 - 5
Terminal.Gui/Drawing/Glyphs.cs

@@ -8,11 +8,14 @@
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
 ///         The default glyphs can be changed via the <see cref="ConfigurationManager"/>. Within a <c>config.json</c>
 ///         The default glyphs can be changed via the <see cref="ConfigurationManager"/>. Within a <c>config.json</c>
-///         file The Json property name is the property name prefixed with "Glyphs.".
+///         file the Json property name is the property name prefixed with "Glyphs.".
 ///     </para>
 ///     </para>
 ///     <para>
 ///     <para>
-///         The JSon property can be either a decimal number or a string. The string may be one of: - A unicode char
-///         (e.g. "☑") - A hex value in U+ format (e.g. "U+2611") - A hex value in UTF-16 format (e.g. "\\u2611")
+///         The Json property can be one of:
+///         - unicode glyph in a string (e.g. "☑")
+///         - U+hex format in a string  (e.g. "U+2611")
+///         - \u format in a string (e.g. "\\u2611")
+///         - A decimal number (e.g. 97 for "a")
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
 public class GlyphDefinitions
 public class GlyphDefinitions
@@ -94,6 +97,9 @@ public class GlyphDefinitions
     /// <summary>Dot. Default is (U+2219) - ∙.</summary>
     /// <summary>Dot. Default is (U+2219) - ∙.</summary>
     public Rune Dot { get; set; } = (Rune)'∙';
     public Rune Dot { get; set; } = (Rune)'∙';
 
 
+    /// <summary>Dotted Square - ⬚ U+02b1a┝</summary>
+    public Rune DottedSquare { get; set; } = (Rune)'⬚';
+
     /// <summary>Black Circle . Default is (U+025cf) - ●.</summary>
     /// <summary>Black Circle . Default is (U+025cf) - ●.</summary>
     public Rune BlackCircle { get; set; } = (Rune)'●'; // Black Circle - ● U+025cf
     public Rune BlackCircle { get; set; } = (Rune)'●'; // Black Circle - ● U+025cf
 
 
@@ -106,6 +112,27 @@ public class GlyphDefinitions
     /// <summary>Identical To (U+226)</summary>
     /// <summary>Identical To (U+226)</summary>
     public Rune IdenticalTo { get; set; } = (Rune)'≡';
     public Rune IdenticalTo { get; set; } = (Rune)'≡';
 
 
+    /// <summary>Move indicator. Default is Lozenge (U+25CA) - ◊.</summary>
+    public Rune Move { get; set; } = (Rune)'◊';
+
+    /// <summary>Size Horizontally indicator. Default is ┥Left Right Arrow - ↔ U+02194</summary>
+    public Rune SizeHorizontal { get; set; } = (Rune)'↔';
+
+    /// <summary>Size Vertical indicator. Default Up Down Arrow - ↕ U+02195</summary>
+    public Rune SizeVertical { get; set; } = (Rune)'↕';
+
+    /// <summary>Size Top Left indicator. North West Arrow - ↖ U+02196</summary>
+    public Rune SizeTopLeft { get; set; } = (Rune)'↖';
+
+    /// <summary>Size Top Right indicator. North East Arrow - ↗ U+02197</summary>
+    public Rune SizeTopRight { get; set; } = (Rune)'↗';
+
+    /// <summary>Size Bottom Right indicator. South East Arrow - ↘ U+02198</summary>
+    public Rune SizeBottomRight { get; set; } = (Rune)'↘';
+
+    /// <summary>Size Bottom Left indicator. South West Arrow - ↙ U+02199</summary>
+    public Rune SizeBottomLeft { get; set; } = (Rune)'↙';
+
     /// <summary>Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610.</summary>
     /// <summary>Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610.</summary>
     public Rune Apple { get; set; } = "🍎".ToRunes () [0]; // nonBMP
     public Rune Apple { get; set; } = "🍎".ToRunes () [0]; // nonBMP
 
 
@@ -440,9 +467,8 @@ public class GlyphDefinitions
 
 
     #region ----------------- ShadowStyle -----------------
     #region ----------------- ShadowStyle -----------------
 
 
-
     /// <summary>Shadow - Vertical Start - Left Half Block - ▌ U+0258c</summary>
     /// <summary>Shadow - Vertical Start - Left Half Block - ▌ U+0258c</summary>
-    public Rune ShadowVerticalStart { get; set; } =  (Rune)'▖'; // Half: '\u2596'  ▖;
+    public Rune ShadowVerticalStart { get; set; } = (Rune)'▖'; // Half: '\u2596'  ▖;
 
 
     /// <summary>Shadow - Vertical - Left Half Block - ▌ U+0258c</summary>
     /// <summary>Shadow - Vertical - Left Half Block - ▌ U+0258c</summary>
     public Rune ShadowVertical { get; set; } = (Rune)'▌';
     public Rune ShadowVertical { get; set; } = (Rune)'▌';

+ 15 - 0
Terminal.Gui/Drawing/ISixelSupportDetector.cs

@@ -0,0 +1,15 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Interface for detecting sixel support. Either through
+///     ansi requests to terminal or config file etc.
+/// </summary>
+public interface ISixelSupportDetector
+{
+    /// <summary>
+    ///     Gets the supported sixel state e.g. by sending Ansi escape sequences
+    ///     or from a config file etc.
+    /// </summary>
+    /// <returns>Description of sixel support.</returns>
+    public SixelSupportResult Detect ();
+}

+ 1 - 1
Terminal.Gui/Drawing/LineCanvas.cs

@@ -138,7 +138,7 @@ public class LineCanvas : IDisposable
         int length,
         int length,
         Orientation orientation,
         Orientation orientation,
         LineStyle style,
         LineStyle style,
-        Attribute? attribute = default
+        Attribute? attribute = null
     )
     )
     {
     {
         _cachedViewport = Rectangle.Empty;
         _cachedViewport = Rectangle.Empty;

+ 91 - 0
Terminal.Gui/Drawing/Quant/ColorQuantizer.cs

@@ -0,0 +1,91 @@
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Translates colors in an image into a Palette of up to <see cref="MaxColors"/> colors (typically 256).
+/// </summary>
+public class ColorQuantizer
+{
+    /// <summary>
+    ///     Gets the current colors in the palette based on the last call to
+    ///     <see cref="BuildPalette"/>.
+    /// </summary>
+    public IReadOnlyCollection<Color> Palette { get; private set; } = new List<Color> ();
+
+    /// <summary>
+    ///     Gets or sets the maximum number of colors to put into the <see cref="Palette"/>.
+    ///     Defaults to 256 (the maximum for sixel images).
+    /// </summary>
+    public int MaxColors { get; set; } = 256;
+
+    /// <summary>
+    ///     Gets or sets the algorithm used to map novel colors into existing
+    ///     palette colors (closest match). Defaults to <see cref="EuclideanColorDistance"/>
+    /// </summary>
+    public IColorDistance DistanceAlgorithm { get; set; } = new EuclideanColorDistance ();
+
+    /// <summary>
+    ///     Gets or sets the algorithm used to build the <see cref="Palette"/>.
+    /// </summary>
+    public IPaletteBuilder PaletteBuildingAlgorithm { get; set; } = new PopularityPaletteWithThreshold (new EuclideanColorDistance (), 8);
+
+    private readonly ConcurrentDictionary<Color, int> _nearestColorCache = new ();
+
+    /// <summary>
+    ///     Builds a <see cref="Palette"/> of colors that most represent the colors used in <paramref name="pixels"/> image.
+    ///     This is based on the currently configured <see cref="PaletteBuildingAlgorithm"/>.
+    /// </summary>
+    /// <param name="pixels"></param>
+    public void BuildPalette (Color [,] pixels)
+    {
+        List<Color> allColors = new ();
+        int width = pixels.GetLength (0);
+        int height = pixels.GetLength (1);
+
+        for (var x = 0; x < width; x++)
+        {
+            for (var y = 0; y < height; y++)
+            {
+                allColors.Add (pixels [x, y]);
+            }
+        }
+
+        _nearestColorCache.Clear ();
+        Palette = PaletteBuildingAlgorithm.BuildPalette (allColors, MaxColors);
+    }
+
+    /// <summary>
+    /// Returns the closest color in <see cref="Palette"/> that matches <paramref name="toTranslate"/>
+    /// based on the color comparison algorithm defined by <see cref="DistanceAlgorithm"/>
+    /// </summary>
+    /// <param name="toTranslate"></param>
+    /// <returns></returns>
+    public int GetNearestColor (Color toTranslate)
+    {
+        if (_nearestColorCache.TryGetValue (toTranslate, out int cachedAnswer))
+        {
+            return cachedAnswer;
+        }
+
+        // Simple nearest color matching based on DistanceAlgorithm
+        var minDistance = double.MaxValue;
+        var nearestIndex = 0;
+
+        for (var index = 0; index < Palette.Count; index++)
+        {
+            Color color = Palette.ElementAt (index);
+            double distance = DistanceAlgorithm.CalculateDistance (color, toTranslate);
+
+            if (distance < minDistance)
+            {
+                minDistance = distance;
+                nearestIndex = index;
+            }
+        }
+
+        _nearestColorCache.TryAdd (toTranslate, nearestIndex);
+
+        return nearestIndex;
+    }
+}

+ 31 - 0
Terminal.Gui/Drawing/Quant/EuclideanColorDistance.cs

@@ -0,0 +1,31 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     <para>
+///         Calculates the distance between two colors using Euclidean distance in 3D RGB space.
+///         This measures the straight-line distance between the two points representing the colors.
+///     </para>
+///     <para>
+///         Euclidean distance in RGB space is calculated as:
+///     </para>
+///     <code>
+///      √((R2 - R1)² + (G2 - G1)² + (B2 - B1)²)
+///  </code>
+///     <remarks>Values vary from 0 to ~441.67 linearly</remarks>
+///     <remarks>
+///         This distance metric is commonly used for comparing colors in RGB space, though
+///         it doesn't account for perceptual differences in color.
+///     </remarks>
+/// </summary>
+public class EuclideanColorDistance : IColorDistance
+{
+    /// <inheritdoc/>
+    public double CalculateDistance (Color c1, Color c2)
+    {
+        int rDiff = c1.R - c2.R;
+        int gDiff = c1.G - c2.G;
+        int bDiff = c1.B - c2.B;
+
+        return Math.Sqrt (rDiff * rDiff + gDiff * gDiff + bDiff * bDiff);
+    }
+}

+ 18 - 0
Terminal.Gui/Drawing/Quant/IColorDistance.cs

@@ -0,0 +1,18 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Interface for algorithms that compute the relative distance between pairs of colors.
+///     This is used for color matching to a limited palette, such as in Sixel rendering.
+/// </summary>
+public interface IColorDistance
+{
+    /// <summary>
+    ///     Computes a similarity metric between two <see cref="Color"/> instances.
+    ///     A larger value indicates more dissimilar colors, while a smaller value indicates more similar colors.
+    ///     The metric is internally consistent for the given algorithm.
+    /// </summary>
+    /// <param name="c1">The first color.</param>
+    /// <param name="c2">The second color.</param>
+    /// <returns>A numeric value representing the distance between the two colors.</returns>
+    double CalculateDistance (Color c1, Color c2);
+}

+ 19 - 0
Terminal.Gui/Drawing/Quant/IPaletteBuilder.cs

@@ -0,0 +1,19 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Builds a palette of a given size for a given set of input colors.
+/// </summary>
+public interface IPaletteBuilder
+{
+    /// <summary>
+    ///     Reduce the number of <paramref name="colors"/> to <paramref name="maxColors"/> (or less)
+    ///     using an appropriate selection algorithm.
+    /// </summary>
+    /// <param name="colors">
+    ///     Color of every pixel in the image. Contains duplication in order
+    ///     to support algorithms that weigh how common a color is.
+    /// </param>
+    /// <param name="maxColors">The maximum number of colours that should be represented.</param>
+    /// <returns></returns>
+    List<Color> BuildPalette (List<Color> colors, int maxColors);
+}

+ 112 - 0
Terminal.Gui/Drawing/Quant/PopularityPaletteWithThreshold.cs

@@ -0,0 +1,112 @@
+using Terminal.Gui;
+using Color = Terminal.Gui.Color;
+
+/// <summary>
+///     Simple fast palette building algorithm which uses the frequency that a color is seen
+///     to determine whether it will appear in the final palette. Includes a threshold where
+///     by colors will be considered 'the same'. This reduces the chance of under represented
+///     colors being missed completely.
+/// </summary>
+public class PopularityPaletteWithThreshold : IPaletteBuilder
+{
+    private readonly IColorDistance _colorDistance;
+    private readonly double _mergeThreshold;
+
+    /// <summary>
+    ///     Creates a new instance with the given color grouping parameters.
+    /// </summary>
+    /// <param name="colorDistance">Determines which different colors can be considered the same.</param>
+    /// <param name="mergeThreshold">Threshold for merging two colors together.</param>
+    public PopularityPaletteWithThreshold (IColorDistance colorDistance, double mergeThreshold)
+    {
+        _colorDistance = colorDistance;
+        _mergeThreshold = mergeThreshold; // Set the threshold for merging similar colors
+    }
+
+    /// <inheritdoc/>
+    public List<Color> BuildPalette (List<Color> colors, int maxColors)
+    {
+        if (colors == null || colors.Count == 0 || maxColors <= 0)
+        {
+            return new ();
+        }
+
+        // Step 1: Build the histogram of colors (count occurrences)
+        Dictionary<Color, int> colorHistogram = new ();
+
+        foreach (Color color in colors)
+        {
+            if (colorHistogram.ContainsKey (color))
+            {
+                colorHistogram [color]++;
+            }
+            else
+            {
+                colorHistogram [color] = 1;
+            }
+        }
+
+        // If we already have fewer or equal colors than the limit, no need to merge
+        if (colorHistogram.Count <= maxColors)
+        {
+            return colorHistogram.Keys.ToList ();
+        }
+
+        // Step 2: Merge similar colors using the color distance threshold
+        Dictionary<Color, int> mergedHistogram = MergeSimilarColors (colorHistogram, maxColors);
+
+        // Step 3: Sort the histogram by frequency (most frequent colors first)
+        List<Color> sortedColors = mergedHistogram.OrderByDescending (c => c.Value)
+                                                  .Take (maxColors) // Keep only the top `maxColors` colors
+                                                  .Select (c => c.Key)
+                                                  .ToList ();
+
+        return sortedColors;
+    }
+
+    /// <summary>
+    ///     Merge colors in the histogram if they are within the threshold distance
+    /// </summary>
+    /// <param name="colorHistogram"></param>
+    /// <param name="maxColors"></param>
+    /// <returns></returns>
+    private Dictionary<Color, int> MergeSimilarColors (Dictionary<Color, int> colorHistogram, int maxColors)
+    {
+        Dictionary<Color, int> mergedHistogram = new ();
+
+        foreach (KeyValuePair<Color, int> entry in colorHistogram)
+        {
+            Color currentColor = entry.Key;
+            var merged = false;
+
+            // Try to merge the current color with an existing entry in the merged histogram
+            foreach (Color mergedEntry in mergedHistogram.Keys.ToList ())
+            {
+                double distance = _colorDistance.CalculateDistance (currentColor, mergedEntry);
+
+                // If the colors are similar enough (within the threshold), merge them
+                if (distance <= _mergeThreshold)
+                {
+                    mergedHistogram [mergedEntry] += entry.Value; // Add the color frequency to the existing one
+                    merged = true;
+
+                    break;
+                }
+            }
+
+            // If no similar color is found, add the current color as a new entry
+            if (!merged)
+            {
+                mergedHistogram [currentColor] = entry.Value;
+            }
+
+            // Early exit if we've reduced the colors to the maxColors limit
+            if (mergedHistogram.Count >= maxColors)
+            {
+                return mergedHistogram;
+            }
+        }
+
+        return mergedHistogram;
+    }
+}

+ 252 - 0
Terminal.Gui/Drawing/SixelEncoder.cs

@@ -0,0 +1,252 @@
+// This code is based on existing implementations of sixel algorithm in MIT licensed open source libraries
+// node-sixel (Typescript) - https://github.com/jerch/node-sixel/tree/master/src
+// Copyright (c) 2019, Joerg Breitbart @license MIT
+// libsixel (C/C++) - https://github.com/saitoha/libsixel
+// Copyright (c) 2014-2016 Hayaki Saito @license MIT
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Encodes a images into the sixel console image output format.
+/// </summary>
+public class SixelEncoder
+{
+    /*
+
+    A sixel is a column of 6 pixels - with a width of 1 pixel
+
+    Column controlled by one sixel character:
+      [ ]  - Bit 0 (top-most pixel)
+      [ ]  - Bit 1
+      [ ]  - Bit 2
+      [ ]  - Bit 3
+      [ ]  - Bit 4
+      [ ]  - Bit 5 (bottom-most pixel)
+
+   Special Characters
+       The '-' acts like '\n'. It moves the drawing cursor
+       to beginning of next line
+
+       The '$' acts like the <Home> key.  It moves drawing
+       cursor back to beginning of the current line
+       e.g. to draw more color layers.
+
+   */
+
+    /// <summary>
+    ///     Gets or sets the quantizer responsible for building a representative
+    ///     limited color palette for images and for mapping novel colors in
+    ///     images to their closest palette color
+    /// </summary>
+    public ColorQuantizer Quantizer { get; set; } = new ();
+
+    /// <summary>
+    ///     Encode the given bitmap into sixel encoding
+    /// </summary>
+    /// <param name="pixels"></param>
+    /// <returns></returns>
+    public string EncodeSixel (Color [,] pixels)
+    {
+        const string start = "\u001bP"; // Start sixel sequence
+
+        string defaultRatios = AnyHasAlphaOfZero (pixels) ? "0;1;0" : "0;0;0"; // Defaults for aspect ratio and grid size
+        const string completeStartSequence = "q"; // Signals beginning of sixel image data
+        const string noScaling = "\"1;1;"; // no scaling factors (1x1);
+
+        string fillArea = GetFillArea (pixels);
+
+        string pallette = GetColorPalette (pixels);
+
+        string pixelData = WriteSixel (pixels);
+
+        const string terminator = "\u001b\\"; // End sixel sequence
+
+        return start + defaultRatios + completeStartSequence + noScaling + fillArea + pallette + pixelData + terminator;
+    }
+
+    private string WriteSixel (Color [,] pixels)
+    {
+        var sb = new StringBuilder ();
+        int height = pixels.GetLength (1);
+        int width = pixels.GetLength (0);
+
+        // Iterate over each 'row' of the image. Because each sixel write operation
+        // outputs a screen area 6 pixels high (and 1+ across) we must process the image
+        // 6 'y' units at once (1 band)
+        for (var y = 0; y < height; y += 6)
+        {
+            sb.Append (ProcessBand (pixels, y, Math.Min (6, height - y), width));
+
+            // Line separator between bands
+            if (y + 6 < height) // Only add separator if not the last band
+            {
+                // This completes the drawing of the current line of sixel and
+                // returns the 'cursor' to beginning next line, newly drawn sixel
+                // after this will draw in the next 6 pixel high band (i.e. below).
+                sb.Append ("-");
+            }
+        }
+
+        return sb.ToString ();
+    }
+
+    private string ProcessBand (Color [,] pixels, int startY, int bandHeight, int width)
+    {
+        var last = new sbyte [Quantizer.Palette.Count + 1];
+        var code = new byte [Quantizer.Palette.Count + 1];
+        var accu = new ushort [Quantizer.Palette.Count + 1];
+        var slots = new short [Quantizer.Palette.Count + 1];
+
+        Array.Fill (last, (sbyte)-1);
+        Array.Fill (accu, (ushort)1);
+        Array.Fill (slots, (short)-1);
+
+        List<int> usedColorIdx = new List<int> ();
+        List<List<string>> targets = new List<List<string>> ();
+
+        // Process columns within the band
+        for (var x = 0; x < width; ++x)
+        {
+            Array.Clear (code, 0, usedColorIdx.Count);
+
+            // Process each row in the 6-pixel high band
+            for (var row = 0; row < bandHeight; ++row)
+            {
+                Color color = pixels [x, startY + row];
+
+                int colorIndex = Quantizer.GetNearestColor (color);
+
+                if (color.A == 0) // Skip fully transparent pixels
+                {
+                    continue;
+                }
+
+                if (slots [colorIndex] == -1)
+                {
+                    targets.Add (new ());
+
+                    if (x > 0)
+                    {
+                        last [usedColorIdx.Count] = 0;
+                        accu [usedColorIdx.Count] = (ushort)x;
+                    }
+
+                    slots [colorIndex] = (short)usedColorIdx.Count;
+                    usedColorIdx.Add (colorIndex);
+                }
+
+                code [slots [colorIndex]] |= (byte)(1 << row); // Accumulate SIXEL data
+            }
+
+            // Handle transitions between columns
+            for (var j = 0; j < usedColorIdx.Count; ++j)
+            {
+                if (code [j] == last [j])
+                {
+                    accu [j]++;
+                }
+                else
+                {
+                    if (last [j] != -1)
+                    {
+                        targets [j].Add (CodeToSixel (last [j], accu [j]));
+                    }
+
+                    last [j] = (sbyte)code [j];
+                    accu [j] = 1;
+                }
+            }
+        }
+
+        // Process remaining data for this band
+        for (var j = 0; j < usedColorIdx.Count; ++j)
+        {
+            if (last [j] != 0)
+            {
+                targets [j].Add (CodeToSixel (last [j], accu [j]));
+            }
+        }
+
+        // Build the final output for this band
+        var result = new StringBuilder ();
+
+        for (var j = 0; j < usedColorIdx.Count; ++j)
+        {
+            result.Append ($"#{usedColorIdx [j]}{string.Join ("", targets [j])}$");
+        }
+
+        return result.ToString ();
+    }
+
+    private static string CodeToSixel (int code, int repeat)
+    {
+        var c = (char)(code + 63);
+
+        if (repeat > 3)
+        {
+            return "!" + repeat + c;
+        }
+
+        if (repeat == 3)
+        {
+            return c.ToString () + c + c;
+        }
+
+        if (repeat == 2)
+        {
+            return c.ToString () + c;
+        }
+
+        return c.ToString ();
+    }
+
+    private string GetColorPalette (Color [,] pixels)
+    {
+        Quantizer.BuildPalette (pixels);
+
+        var paletteSb = new StringBuilder ();
+
+        for (var i = 0; i < Quantizer.Palette.Count; i++)
+        {
+            Color color = Quantizer.Palette.ElementAt (i);
+
+            paletteSb.AppendFormat (
+                                    "#{0};2;{1};{2};{3}",
+                                    i,
+                                    color.R * 100 / 255,
+                                    color.G * 100 / 255,
+                                    color.B * 100 / 255);
+        }
+
+        return paletteSb.ToString ();
+    }
+
+    private string GetFillArea (Color [,] pixels)
+    {
+        int widthInChars = pixels.GetLength (0);
+        int heightInChars = pixels.GetLength (1);
+
+        return $"{widthInChars};{heightInChars}";
+    }
+
+    private bool AnyHasAlphaOfZero (Color [,] pixels)
+    {
+        int width = pixels.GetLength (0);
+        int height = pixels.GetLength (1);
+
+        // Loop through each pixel in the 2D array
+        for (var x = 0; x < width; x++)
+        {
+            for (var y = 0; y < height; y++)
+            {
+                // Check if the alpha component (A) is 0
+                if (pixels [x, y].A == 0)
+                {
+                    return true; // Found a pixel with A of 0
+                }
+            }
+        }
+
+        return false; // No pixel with A of 0 was found
+    }
+}

+ 133 - 0
Terminal.Gui/Drawing/SixelSupportDetector.cs

@@ -0,0 +1,133 @@
+using System.Text.RegularExpressions;
+
+namespace Terminal.Gui;
+/* TODO : Depends on https://github.com/gui-cs/Terminal.Gui/pull/3768
+/// <summary>
+///     Uses Ansi escape sequences to detect whether sixel is supported
+///     by the terminal.
+/// </summary>
+public class SixelSupportDetector : ISixelSupportDetector
+{
+    /// <summary>
+    /// Sends Ansi escape sequences to the console to determine whether
+    /// sixel is supported (and <see cref="SixelSupportResult.Resolution"/>
+    /// etc).
+    /// </summary>
+    /// <returns>Description of sixel support, may include assumptions where
+    /// expected response codes are not returned by console.</returns>
+    public SixelSupportResult Detect ()
+    {
+        var result = new SixelSupportResult ();
+
+        result.IsSupported = IsSixelSupportedByDar ();
+
+        if (result.IsSupported)
+        {
+            if (TryGetResolutionDirectly (out var res))
+            {
+                result.Resolution = res;
+            }
+            else if(TryComputeResolution(out res))
+            {
+                result.Resolution = res;
+            }
+
+            result.SupportsTransparency = IsWindowsTerminal () || IsXtermWithTransparency ();
+        }
+
+        return result;
+    }
+
+
+    private bool TryGetResolutionDirectly (out Size resolution)
+    {
+        // Expect something like:
+        //<esc>[6;20;10t
+
+        if (AnsiEscapeSequenceRequest.TryExecuteAnsiRequest (EscSeqUtils.CSI_RequestSixelResolution, out var response))
+        {
+            // Terminal supports directly responding with resolution
+            var match = Regex.Match (response.Response, @"\[\d+;(\d+);(\d+)t$");
+
+            if (match.Success)
+            {
+                if (int.TryParse (match.Groups [1].Value, out var ry) &&
+                    int.TryParse (match.Groups [2].Value, out var rx))
+                {
+                    resolution = new Size (rx, ry);
+
+                    return true;
+                }
+            }
+        }
+
+        resolution = default;
+        return false;
+    }
+
+
+    private bool TryComputeResolution (out Size resolution)
+    {
+        // Fallback to window size in pixels and characters
+        if (AnsiEscapeSequenceRequest.TryExecuteAnsiRequest (EscSeqUtils.CSI_RequestWindowSizeInPixels, out var pixelSizeResponse)
+            && AnsiEscapeSequenceRequest.TryExecuteAnsiRequest (EscSeqUtils.CSI_ReportTerminalSizeInChars, out var charSizeResponse))
+        {
+            // Example [4;600;1200t
+            var pixelMatch = Regex.Match (pixelSizeResponse.Response, @"\[\d+;(\d+);(\d+)t$");
+
+            // Example [8;30;120t
+            var charMatch = Regex.Match (charSizeResponse.Response, @"\[\d+;(\d+);(\d+)t$");
+
+            if (pixelMatch.Success && charMatch.Success)
+            {
+                // Extract pixel dimensions
+                if (int.TryParse (pixelMatch.Groups [1].Value, out var pixelHeight)
+                    && int.TryParse (pixelMatch.Groups [2].Value, out var pixelWidth)
+                    &&
+
+                    // Extract character dimensions
+                    int.TryParse (charMatch.Groups [1].Value, out var charHeight)
+                    && int.TryParse (charMatch.Groups [2].Value, out var charWidth)
+                    && charWidth != 0
+                    && charHeight != 0) // Avoid divide by zero
+                {
+                    // Calculate the character cell size in pixels
+                    var cellWidth = (int)Math.Round ((double)pixelWidth / charWidth);
+                    var cellHeight = (int)Math.Round ((double)pixelHeight / charHeight);
+
+                    // Set the resolution based on the character cell size
+                    resolution = new Size (cellWidth, cellHeight);
+
+                    return true;
+                }
+            }
+        }
+
+        resolution = default;
+        return false;
+    }
+    private bool IsSixelSupportedByDar ()
+    {
+        return AnsiEscapeSequenceRequest.TryExecuteAnsiRequest (EscSeqUtils.CSI_SendDeviceAttributes, out AnsiEscapeSequenceResponse darResponse)
+            ? darResponse.Response.Split (';').Contains ("4")
+            : false;
+    }
+
+    private bool IsWindowsTerminal ()
+    {
+        return  !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable ("WT_SESSION"));;
+    }
+    private bool IsXtermWithTransparency ()
+    {
+        // Check if running in real xterm (XTERM_VERSION is more reliable than TERM)
+        var xtermVersionStr = Environment.GetEnvironmentVariable ("XTERM_VERSION");
+
+        // If XTERM_VERSION exists, we are in a real xterm
+        if (!string.IsNullOrWhiteSpace (xtermVersionStr) && int.TryParse (xtermVersionStr, out var xtermVersion) && xtermVersion >= 370)
+        {
+            return true;
+        }
+
+        return false;
+    }
+}*/

+ 33 - 0
Terminal.Gui/Drawing/SixelSupportResult.cs

@@ -0,0 +1,33 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Describes the discovered state of sixel support and ancillary information
+///     e.g. <see cref="Resolution"/>. You can use any <see cref="ISixelSupportDetector"/>
+///     to discover this information.
+/// </summary>
+public class SixelSupportResult
+{
+    /// <summary>
+    ///     Whether the terminal supports sixel graphic format.
+    ///     Defaults to false.
+    /// </summary>
+    public bool IsSupported { get; set; }
+
+    /// <summary>
+    ///     The number of pixels of sixel that corresponds to each Col (<see cref="Size.Width"/>)
+    ///     and each Row (<see cref="Size.Height"/>.  Defaults to 10x20.
+    /// </summary>
+    public Size Resolution { get; set; } = new (10, 20);
+
+    /// <summary>
+    ///     The maximum number of colors that can be included in a sixel image. Defaults
+    ///     to 256.
+    /// </summary>
+    public int MaxPaletteColors { get; set; } = 256;
+
+    /// <summary>
+    ///     Whether the terminal supports transparent background sixels.
+    ///     Defaults to false
+    /// </summary>
+    public bool SupportsTransparency { get; set; }
+}

+ 19 - 0
Terminal.Gui/Drawing/SixelToRender.cs

@@ -0,0 +1,19 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Describes a request to render a given <see cref="SixelData"/> at a given <see cref="ScreenPosition"/>.
+///     Requires that the terminal and <see cref="ConsoleDriver"/> both support sixel.
+/// </summary>
+public class SixelToRender
+{
+    /// <summary>
+    ///     gets or sets the encoded sixel data. Use <see cref="SixelEncoder"/> to convert bitmaps
+    ///     into encoded sixel data.
+    /// </summary>
+    public string SixelData { get; set; }
+
+    /// <summary>
+    ///     gets or sets where to move the cursor to before outputting the <see cref="SixelData"/>.
+    /// </summary>
+    public Point ScreenPosition { get; set; }
+}

+ 1 - 1
Terminal.Gui/Drawing/StraightLine.cs

@@ -16,7 +16,7 @@ public class StraightLine
         int length,
         int length,
         Orientation orientation,
         Orientation orientation,
         LineStyle style,
         LineStyle style,
-        Attribute? attribute = default
+        Attribute? attribute = null
     )
     )
     {
     {
         Start = start;
         Start = start;

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

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

+ 196 - 147
Terminal.Gui/Input/Command.cs

@@ -3,160 +3,176 @@
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Actions which can be performed by the application or bound to keys in a <see cref="View"/> control.</summary>
+/// <summary>
+///     Actions which can be performed by a <see cref="View"/>. Commands are typically invoked via
+///     <see cref="View.KeyBindings"/> and mouse events.
+/// </summary>
 public enum Command
 public enum Command
 {
 {
-    /// <summary>Invoked when the HotKey for the View has been pressed.</summary>
-    HotKey,
+    #region Base View Commands
 
 
-    /// <summary>Accepts the current state (e.g. list selection, button press, toggle, etc).</summary>
+    /// <summary>
+    ///     Accepts the current state of the View (e.g. list selection, button press, checkbox state, etc.).
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.RaiseAccepting"/>. If the event is not handled,
+    ///         the command is invoked on:
+    ///             - Any peer-view that is a <see cref="Button"/> with <see cref="Button.IsDefault"/> set to <see langword="true"/>.
+    ///             - The <see cref="View.SuperView"/>. This enables default Accept behavior.
+    ///     </para>
+    /// </summary>
     Accept,
     Accept,
 
 
-    /// <summary>Selects an item (e.g. a list item or menu item) without necessarily accepting it.</summary>
-    Select,
-
-    /// <summary>Moves down one item (cell, line, etc...).</summary>
-    LineDown,
-
-    /// <summary>Extends the selection down one (cell, line, etc...).</summary>
-    LineDownExtend,
+    /// <summary>
+    ///     Performs a hot key action (e.g. setting focus, accepting, and/or moving focus to the next View).
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.SetFocus"/> and then
+    ///         <see cref="View.RaiseHandlingHotKey"/>.
+    ///     </para>
+    /// </summary>
+    HotKey,
 
 
-    /// <summary>Moves down to the last child node of the branch that holds the current selection.</summary>
-    LineDownToLastBranch,
+    /// <summary>
+    ///     Selects the View or an item in the View (e.g. a list item or menu item) without necessarily accepting it.
+    ///     <para>
+    ///         The default implementation in <see cref="View"/> calls <see cref="View.RaiseSelecting"/>.
+    ///     </para>
+    /// </summary>
+    Select,
 
 
-    /// <summary>Scrolls down one (cell, line, etc...) (without changing the selection).</summary>
-    ScrollDown,
+    #endregion
 
 
-    // --------------------------------------------------------------------
+    #region Movement Commands
 
 
     /// <summary>Moves up one (cell, line, etc...).</summary>
     /// <summary>Moves up one (cell, line, etc...).</summary>
-    LineUp,
-
-    /// <summary>Extends the selection up one item (cell, line, etc...).</summary>
-    LineUpExtend,
-
-    /// <summary>Moves up to the first child node of the branch that holds the current selection.</summary>
-    LineUpToFirstBranch,
+    Up,
 
 
-    /// <summary>Scrolls up one item (cell, line, etc...) (without changing the selection).</summary>
-    ScrollUp,
+    /// <summary>Moves down one item (cell, line, etc...).</summary>
+    Down,
 
 
     /// <summary>
     /// <summary>
-    ///     Moves the selection left one by the minimum increment supported by the <see cref="View"/> e.g. single
-    ///     character, cell, item etc.
+    ///     Moves left one (cell, line, etc...).
     /// </summary>
     /// </summary>
     Left,
     Left,
 
 
-    /// <summary>Scrolls one item (cell, character, etc...) to the left</summary>
-    ScrollLeft,
-
-    /// <summary>
-    ///     Extends the selection left one by the minimum increment supported by the view e.g. single character, cell,
-    ///     item etc.
-    /// </summary>
-    LeftExtend,
-
     /// <summary>
     /// <summary>
-    ///     Moves the selection right one by the minimum increment supported by the view e.g. single character, cell, item
-    ///     etc.
+    ///     Moves right one (cell, line, etc...).
     /// </summary>
     /// </summary>
     Right,
     Right,
 
 
-    /// <summary>Scrolls one item (cell, character, etc...) to the right.</summary>
-    ScrollRight,
+    /// <summary>Move one page up.</summary>
+    PageUp,
 
 
-    /// <summary>
-    ///     Extends the selection right one by the minimum increment supported by the view e.g. single character, cell,
-    ///     item etc.
-    /// </summary>
-    RightExtend,
+    /// <summary>Move one page down.</summary>
+    PageDown,
 
 
-    /// <summary>Moves the caret to the start of the previous word.</summary>
-    WordLeft,
+    /// <summary>Moves to the left page.</summary>
+    PageLeft,
 
 
-    /// <summary>Extends the selection to the start of the previous word.</summary>
-    WordLeftExtend,
+    /// <summary>Moves to the right page.</summary>
+    PageRight,
 
 
-    /// <summary>Moves the caret to the start of the next word.</summary>
-    WordRight,
+    /// <summary>Moves to the top of page.</summary>
+    StartOfPage,
 
 
-    /// <summary>Extends the selection to the start of the next word.</summary>
-    WordRightExtend,
+    /// <summary>Moves to the bottom of page.</summary>
+    EndOfPage,
 
 
-    /// <summary>Cuts to the clipboard the characters from the current position to the end of the line.</summary>
-    CutToEndLine,
+    /// <summary>Moves to the start (e.g. the top or home).</summary>
+    Start,
 
 
-    /// <summary>Cuts to the clipboard the characters from the current position to the start of the line.</summary>
-    CutToStartLine,
+    /// <summary>Moves to the end (e.g. the bottom).</summary>
+    End,
 
 
-    /// <summary>Deletes the characters forwards.</summary>
-    KillWordForwards,
+    /// <summary>Moves left to the start on the current row/line.</summary>
+    LeftStart,
 
 
-    /// <summary>Deletes the characters backwards.</summary>
-    KillWordBackwards,
+    /// <summary>Moves right to the end on the current row/line.</summary>
+    RightEnd,
+
+    /// <summary>Moves to the start of the previous word.</summary>
+    WordLeft,
+
+    /// <summary>Moves the start of the next word.</summary>
+    WordRight,
+
+    #endregion
+
+    #region Movement With Extension Commands
+
+    /// <summary>Extends the selection up one item (cell, line, etc...).</summary>
+    UpExtend,
+
+    /// <summary>Extends the selection down one (cell, line, etc...).</summary>
+    DownExtend,
 
 
     /// <summary>
     /// <summary>
-    ///     Toggles overwrite mode such that newly typed text overwrites the text that is already there (typically
-    ///     associated with the Insert key).
+    ///     Extends the selection left one item (cell, line, etc...)
     /// </summary>
     /// </summary>
-    ToggleOverwrite,
+    LeftExtend,
 
 
     /// <summary>
     /// <summary>
-    ///     Enables overwrite mode such that newly typed text overwrites the text that is already there (typically
-    ///     associated with the Insert key).
+    ///     Extends the selection right one item (cell, line, etc...)
     /// </summary>
     /// </summary>
-    EnableOverwrite,
+    RightExtend,
 
 
-    /// <summary>Disables overwrite mode (<see cref="EnableOverwrite"/>)</summary>
-    DisableOverwrite,
+    /// <summary>Extends the selection to the start of the previous word.</summary>
+    WordLeftExtend,
 
 
-    /// <summary>Move one page down.</summary>
-    PageDown,
+    /// <summary>Extends the selection to the start of the next word.</summary>
+    WordRightExtend,
 
 
     /// <summary>Move one page down extending the selection to cover revealed objects/characters.</summary>
     /// <summary>Move one page down extending the selection to cover revealed objects/characters.</summary>
     PageDownExtend,
     PageDownExtend,
 
 
-    /// <summary>Move one page up.</summary>
-    PageUp,
-
     /// <summary>Move one page up extending the selection to cover revealed objects/characters.</summary>
     /// <summary>Move one page up extending the selection to cover revealed objects/characters.</summary>
     PageUpExtend,
     PageUpExtend,
 
 
-    /// <summary>Moves to the top/home.</summary>
-    TopHome,
+    /// <summary>Extends the selection to start (e.g. home or top).</summary>
+    StartExtend,
 
 
-    /// <summary>Extends the selection to the top/home.</summary>
-    TopHomeExtend,
+    /// <summary>Extends the selection to end (e.g. bottom).</summary>
+    EndExtend,
 
 
-    /// <summary>Moves to the bottom/end.</summary>
-    BottomEnd,
+    /// <summary>Extends the selection to the start on the current row/line.</summary>
+    LeftStartExtend,
 
 
-    /// <summary>Extends the selection to the bottom/end.</summary>
-    BottomEndExtend,
+    /// <summary>Extends the selection to the right on the current row/line.</summary>
+    RightEndExtend,
 
 
-    /// <summary>Open the selected item.</summary>
-    OpenSelectedItem,
+    /// <summary>Toggles the selection.</summary>
+    ToggleExtend,
 
 
-    /// <summary>Toggles the Expanded or collapsed state of a list or item (with subitems).</summary>
-    ToggleExpandCollapse,
+    #endregion
 
 
-    /// <summary>Expands a list or item (with subitems).</summary>
-    Expand,
+    #region Editing Commands
 
 
-    /// <summary>Recursively Expands all child items and their child items (if any).</summary>
-    ExpandAll,
+    /// <summary>Deletes the characters forwards.</summary>
+    KillWordForwards,
 
 
-    /// <summary>Collapses a list or item (with subitems).</summary>
-    Collapse,
+    /// <summary>Deletes the characters backwards.</summary>
+    KillWordBackwards,
 
 
-    /// <summary>Recursively collapses a list items of their children (if any).</summary>
-    CollapseAll,
+    /// <summary>
+    ///     Toggles overwrite mode such that newly typed text overwrites the text that is already there (typically
+    ///     associated with the Insert key).
+    /// </summary>
+    ToggleOverwrite,
 
 
-    /// <summary>Cancels an action or any temporary states on the control e.g. expanding a combo list.</summary>
-    Cancel,
+    // QUESTION: What is the difference between EnableOverwrite and ToggleOverwrite?
 
 
-    /// <summary>Unix emulation.</summary>
-    UnixEmulation,
+    /// <summary>
+    ///     Enables overwrite mode such that newly typed text overwrites the text that is already there (typically
+    ///     associated with the Insert key).
+    /// </summary>
+    EnableOverwrite,
+
+    /// <summary>
+    ///     Inserts a character.
+    /// </summary>
+    Insert,
+
+    /// <summary>Disables overwrite mode (<see cref="EnableOverwrite"/>)</summary>
+    DisableOverwrite,
 
 
     /// <summary>Deletes the character on the right.</summary>
     /// <summary>Deletes the character on the right.</summary>
     DeleteCharRight,
     DeleteCharRight,
@@ -170,41 +186,41 @@ public enum Command
     /// <summary>Deletes all objects.</summary>
     /// <summary>Deletes all objects.</summary>
     DeleteAll,
     DeleteAll,
 
 
-    /// <summary>Moves the cursor to the start of line.</summary>
-    StartOfLine,
+    /// <summary>Inserts a new item.</summary>
+    NewLine,
 
 
-    /// <summary>Extends the selection to the start of line.</summary>
-    StartOfLineExtend,
+    /// <summary>Unix emulation.</summary>
+    UnixEmulation,
 
 
-    /// <summary>Moves the cursor to the end of line.</summary>
-    EndOfLine,
+    #endregion
 
 
-    /// <summary>Extends the selection to the end of line.</summary>
-    EndOfLineExtend,
+    #region Tree Commands
 
 
-    /// <summary>Moves the cursor to the top of page.</summary>
-    StartOfPage,
+    /// <summary>Moves down to the last child node of the branch that holds the current selection.</summary>
+    LineDownToLastBranch,
 
 
-    /// <summary>Moves the cursor to the bottom of page.</summary>
-    EndOfPage,
+    /// <summary>Moves up to the first child node of the branch that holds the current selection.</summary>
+    LineUpToFirstBranch,
 
 
-    /// <summary>Moves to the left page.</summary>
-    PageLeft,
+    #endregion
 
 
-    /// <summary>Moves to the right page.</summary>
-    PageRight,
+    #region Scroll Commands
 
 
-    /// <summary>Moves to the left begin.</summary>
-    LeftHome,
+    /// <summary>Scrolls down one (cell, line, etc...).</summary>
+    ScrollDown,
 
 
-    /// <summary>Extends the selection to the left begin.</summary>
-    LeftHomeExtend,
+    /// <summary>Scrolls up one item (cell, line, etc...).</summary>
+    ScrollUp,
 
 
-    /// <summary>Moves to the right end.</summary>
-    RightEnd,
+    /// <summary>Scrolls one item (cell, character, etc...) to the left.</summary>
+    ScrollLeft,
 
 
-    /// <summary>Extends the selection to the right end.</summary>
-    RightEndExtend,
+    /// <summary>Scrolls one item (cell, character, etc...) to the right.</summary>
+    ScrollRight,
+
+    #endregion
+
+    #region Clipboard Commands
 
 
     /// <summary>Undo changes.</summary>
     /// <summary>Undo changes.</summary>
     Undo,
     Undo,
@@ -221,35 +237,27 @@ public enum Command
     /// <summary>Pastes the current selection.</summary>
     /// <summary>Pastes the current selection.</summary>
     Paste,
     Paste,
 
 
-    /// TODO: IRunnable: Rename to Command.Quit to make more generic.
-    /// <summary>Quit a <see cref="Toplevel"/>.</summary>
-    QuitToplevel,
-
-    /// TODO: Overlapped: Add Command.ShowHide
-
-    /// <summary>Suspend an application (Only implemented in <see cref="CursesDriver"/>).</summary>
-    Suspend,
+    /// <summary>Cuts to the clipboard the characters from the current position to the end of the line.</summary>
+    CutToEndLine,
 
 
-    /// <summary>Moves focus to the next view.</summary>
-    NextView,
+    /// <summary>Cuts to the clipboard the characters from the current position to the start of the line.</summary>
+    CutToStartLine,
 
 
-    /// <summary>Moves focus to the previous view.</summary>
-    PreviousView,
+    #endregion
 
 
-    /// <summary>Moves focus to the next view or Toplevel (case of Overlapped).</summary>
-    NextViewOrTop,
+    #region Navigation Commands
 
 
-    /// <summary>Moves focus to the next previous or Toplevel (case of Overlapped).</summary>
-    PreviousViewOrTop,
+    /// <summary>Moves focus to the next <see cref="TabBehavior.TabStop"/>.</summary>
+    NextTabStop,
 
 
-    /// <summary>Refresh.</summary>
-    Refresh,
+    /// <summary>Moves focus to the previous <see cref="TabBehavior.TabStop"/>.</summary>
+    PreviousTabStop,
 
 
-    /// <summary>Toggles the selection.</summary>
-    ToggleExtend,
+    /// <summary>Moves focus to the next <see cref="TabBehavior.TabGroup"/>.</summary>
+    NextTabGroup,
 
 
-    /// <summary>Inserts a new item.</summary>
-    NewLine,
+    /// <summary>Moves focus to the next<see cref="TabBehavior.TabGroup"/>.</summary>
+    PreviousTabGroup,
 
 
     /// <summary>Tabs to the next item.</summary>
     /// <summary>Tabs to the next item.</summary>
     Tab,
     Tab,
@@ -257,6 +265,40 @@ public enum Command
     /// <summary>Tabs back to the previous item.</summary>
     /// <summary>Tabs back to the previous item.</summary>
     BackTab,
     BackTab,
 
 
+    #endregion
+
+    #region Action Commands
+
+    /// <summary>Toggles something (e.g. the expanded or collapsed state of a list).</summary>
+    Toggle,
+
+    /// <summary>Expands a list or item (with subitems).</summary>
+    Expand,
+
+    /// <summary>Recursively Expands all child items and their child items (if any).</summary>
+    ExpandAll,
+
+    /// <summary>Collapses a list or item (with subitems).</summary>
+    Collapse,
+
+    /// <summary>Recursively collapses a list items of their children (if any).</summary>
+    CollapseAll,
+
+    /// <summary>Cancels an action or any temporary states on the control e.g. expanding a combo list.</summary>
+    Cancel,
+
+    /// <summary>Quit.</summary>
+    Quit,
+
+    /// <summary>Refresh.</summary>
+    Refresh,
+
+    /// <summary>Suspend an application (Only implemented in <see cref="CursesDriver"/>).</summary>
+    Suspend,
+
+    /// <summary>Open the selected item or invoke a UI for opening something.</summary>
+    Open,
+
     /// <summary>Saves the current document.</summary>
     /// <summary>Saves the current document.</summary>
     Save,
     Save,
 
 
@@ -267,5 +309,12 @@ public enum Command
     New,
     New,
 
 
     /// <summary>Shows context about the item (e.g. a context menu).</summary>
     /// <summary>Shows context about the item (e.g. a context menu).</summary>
-    ShowContextMenu
-}
+    Context,
+
+    /// <summary>
+    ///     Invokes a user interface for editing or configuring something.
+    /// </summary>
+    Edit,
+
+    #endregion
+}

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

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

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

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

+ 1 - 1
Terminal.Gui/Input/GrabMouseEventArgs.cs

@@ -1,6 +1,6 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Args <see cref="Application.GrabMouse"/> related events.</summary>
+/// <summary>Args GrabMouse related events.</summary>
 public class GrabMouseEventArgs : EventArgs
 public class GrabMouseEventArgs : EventArgs
 {
 {
     /// <summary>Creates a new instance of the <see cref="GrabMouseEventArgs"/> class.</summary>
     /// <summary>Creates a new instance of the <see cref="GrabMouseEventArgs"/> class.</summary>

+ 93 - 47
Terminal.Gui/Input/Key.cs

@@ -1,6 +1,5 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Globalization;
-using System.Text.Json.Serialization;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -80,7 +79,7 @@ public class Key : EventArgs, IEquatable<Key>
     public Key (KeyCode k) { KeyCode = k; }
     public Key (KeyCode k) { KeyCode = k; }
 
 
     /// <summary>
     /// <summary>
-    /// Copy constructor.
+    ///     Copy constructor.
     /// </summary>
     /// </summary>
     /// <param name="key">The Key to copy</param>
     /// <param name="key">The Key to copy</param>
     public Key (Key key)
     public Key (Key key)
@@ -155,18 +154,13 @@ public class Key : EventArgs, IEquatable<Key>
     /// </remarks>
     /// </remarks>
     public Rune AsRune => ToRune (KeyCode);
     public Rune AsRune => ToRune (KeyCode);
 
 
-    private bool _handled = false;
-
     /// <summary>
     /// <summary>
     ///     Indicates if the current Key event has already been processed and the driver should stop notifying any other
     ///     Indicates if the current Key event has already been processed and the driver should stop notifying any other
-    ///     event subscriber. It's important to set this value to true specially when updating any View's layout from inside the
+    ///     event subscriber. It's important to set this value to true specially when updating any View's layout from inside
+    ///     the
     ///     subscriber method.
     ///     subscriber method.
     /// </summary>
     /// </summary>
-    public bool Handled
-    {
-        get => _handled;
-        set => _handled = value;
-    }
+    public bool Handled { get; set; }
 
 
     /// <summary>Gets a value indicating whether the Alt key was pressed (real or synthesized)</summary>
     /// <summary>Gets a value indicating whether the Alt key was pressed (real or synthesized)</summary>
     /// <value><see langword="true"/> if is alternate; otherwise, <see langword="false"/>.</value>
     /// <value><see langword="true"/> if is alternate; otherwise, <see langword="false"/>.</value>
@@ -339,11 +333,11 @@ public class Key : EventArgs, IEquatable<Key>
         switch (baseKey)
         switch (baseKey)
         {
         {
             case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
             case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
-                return new Rune ((uint)(baseKey + 32));
+                return new ((uint)(baseKey + 32));
             case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
             case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
-                return new Rune ((uint)baseKey);
+                return new ((uint)baseKey);
             case > KeyCode.Null and < KeyCode.A:
             case > KeyCode.Null and < KeyCode.A:
-                return new Rune ((uint)baseKey);
+                return new ((uint)baseKey);
         }
         }
 
 
         if (Enum.IsDefined (typeof (KeyCode), baseKey))
         if (Enum.IsDefined (typeof (KeyCode), baseKey))
@@ -351,7 +345,7 @@ public class Key : EventArgs, IEquatable<Key>
             return default (Rune);
             return default (Rune);
         }
         }
 
 
-        return new Rune ((uint)baseKey);
+        return new ((uint)baseKey);
     }
     }
 
 
     #region Operators
     #region Operators
@@ -381,17 +375,17 @@ public class Key : EventArgs, IEquatable<Key>
 
 
     /// <summary>Cast <see cref="KeyCode"/> to a <see cref="Key"/>.</summary>
     /// <summary>Cast <see cref="KeyCode"/> to a <see cref="Key"/>.</summary>
     /// <param name="keyCode"></param>
     /// <param name="keyCode"></param>
-    public static implicit operator Key (KeyCode keyCode) { return new Key (keyCode); }
+    public static implicit operator Key (KeyCode keyCode) { return new (keyCode); }
 
 
     /// <summary>Cast <see langword="char"/> to a <see cref="Key"/>.</summary>
     /// <summary>Cast <see langword="char"/> to a <see cref="Key"/>.</summary>
     /// <remarks>See <see cref="Key(char)"/> for more information.</remarks>
     /// <remarks>See <see cref="Key(char)"/> for more information.</remarks>
     /// <param name="ch"></param>
     /// <param name="ch"></param>
-    public static implicit operator Key (char ch) { return new Key (ch); }
+    public static implicit operator Key (char ch) { return new (ch); }
 
 
     /// <summary>Cast <see langword="string"/> to a <see cref="Key"/>.</summary>
     /// <summary>Cast <see langword="string"/> to a <see cref="Key"/>.</summary>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
     /// <param name="str"></param>
     /// <param name="str"></param>
-    public static implicit operator Key (string str) { return new Key (str); }
+    public static implicit operator Key (string str) { return new (str); }
 
 
     /// <summary>Cast a <see cref="Key"/> to a <see langword="string"/>.</summary>
     /// <summary>Cast a <see cref="Key"/> to a <see langword="string"/>.</summary>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
@@ -399,10 +393,7 @@ public class Key : EventArgs, IEquatable<Key>
     public static implicit operator string (Key key) { return key.ToString (); }
     public static implicit operator string (Key key) { return key.ToString (); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool Equals (object obj)
-    {
-        return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled;
-    }
+    public override bool Equals (object obj) { return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled; }
 
 
     bool IEquatable<Key>.Equals (Key other) { return Equals (other); }
     bool IEquatable<Key>.Equals (Key other) { return Equals (other); }
 
 
@@ -568,7 +559,10 @@ public class Key : EventArgs, IEquatable<Key>
     /// <summary>Converts the provided string to a new <see cref="Key"/> instance.</summary>
     /// <summary>Converts the provided string to a new <see cref="Key"/> instance.</summary>
     /// <param name="text">
     /// <param name="text">
     ///     The text to analyze. Formats supported are "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X",
     ///     The text to analyze. Formats supported are "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X",
-    ///     "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", and "X".
+    ///     "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", "X", and "120" (Unicode codepoint).
+    ///     <para>
+    ///         The separator can be any character, not just <see cref="Key.Separator"/> (e.g. "Ctrl@Alt@X").
+    ///     </para>
     /// </param>
     /// </param>
     /// <param name="key">The parsed value.</param>
     /// <param name="key">The parsed value.</param>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>
@@ -577,38 +571,88 @@ public class Key : EventArgs, IEquatable<Key>
     {
     {
         if (string.IsNullOrEmpty (text))
         if (string.IsNullOrEmpty (text))
         {
         {
-            key = Key.Empty;
+            key = Empty;
 
 
             return true;
             return true;
         }
         }
 
 
+        switch (text)
+        {
+            case "Ctrl":
+                key = KeyCode.CtrlMask;
+
+                return true;
+            case "Alt":
+                key = KeyCode.AltMask;
+
+                return true;
+            case "Shift":
+                key = KeyCode.ShiftMask;
+
+                return true;
+        }
+
         key = null;
         key = null;
 
 
-        // Split the string into parts
-        string [] parts = text.Split ('+', '-', (char)Separator.Value);
+        Rune separator = Separator;
 
 
-        if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
+        // Perhaps the separator was written using a different Key.Separator? Does the string
+        // start with "Ctrl", "Alt" or "Shift"? If so, get the char after the modifier string and use that as the separator.
+        if (text.StartsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [4];
+        }
+        else if (text.StartsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [3];
+        }
+        else if (text.StartsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [5];
+        }
+        else if (text.EndsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
         {
         {
+            separator = (Rune)text [^5];
+        }
+        else if (text.EndsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [^4];
+        }
+        else if (text.EndsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [^6];
+        }
+
+        // Split the string into parts using the set Separator
+        string [] parts = text.Split ((char)separator.Value);
+
+        if (parts.Length is > 4)
+        {
+            // Invalid
             return false;
             return false;
         }
         }
 
 
-        // if it's just a shift key
-        if (parts.Length == 1)
+        // e.g. "Ctrl++"
+        if ((Rune)text [^1] != separator && parts.Any (string.IsNullOrEmpty))
         {
         {
-            switch (parts [0])
-            {
-                case "Ctrl":
-                    key = KeyCode.CtrlMask;
+            // Invalid
+            return false;
+        }
 
 
-                    return true;
-                case "Alt":
-                    key = KeyCode.AltMask;
+        if ((Rune)text [^1] == separator)
+        {
+            parts [^1] = separator.Value.ToString ();
+            key = (char)separator.Value;
+        }
 
 
-                    return true;
-                case "Shift":
-                    key = KeyCode.ShiftMask;
+        if (separator != Separator && (parts.Length is 1 || (key is { } && parts.Length is 2)))
+        {
+            parts = text.Split ((char)separator.Value);
 
 
-                    return true;
+            if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
+            {
+                // Invalid
+                return false;
             }
             }
         }
         }
 
 
@@ -649,12 +693,12 @@ public class Key : EventArgs, IEquatable<Key>
                 {
                 {
                     if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                     if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                     {
                     {
-                        key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+                        key = new (parsedKeyCode | KeyCode.ShiftMask);
 
 
                         return true;
                         return true;
                     }
                     }
 
 
-                    key = new Key (parsedKeyCode | modifiers);
+                    key = new (parsedKeyCode | modifiers);
 
 
                     return true;
                     return true;
                 }
                 }
@@ -664,7 +708,8 @@ public class Key : EventArgs, IEquatable<Key>
             {
             {
                 keyCode = keyCode & ~KeyCode.Space;
                 keyCode = keyCode & ~KeyCode.Space;
             }
             }
-            key = new Key (keyCode | modifiers);
+
+            key = new (keyCode | modifiers);
 
 
             return true;
             return true;
         }
         }
@@ -675,7 +720,7 @@ public class Key : EventArgs, IEquatable<Key>
             {
             {
                 if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                 if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                 {
                 {
-                    key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+                    key = new (parsedKeyCode | KeyCode.ShiftMask);
 
 
                     return true;
                     return true;
                 }
                 }
@@ -684,7 +729,8 @@ public class Key : EventArgs, IEquatable<Key>
                 {
                 {
                     parsedKeyCode = parsedKeyCode & ~KeyCode.Space;
                     parsedKeyCode = parsedKeyCode & ~KeyCode.Space;
                 }
                 }
-                key = new Key (parsedKeyCode | modifiers);
+
+                key = new (parsedKeyCode | modifiers);
 
 
                 return true;
                 return true;
             }
             }
@@ -705,12 +751,12 @@ public class Key : EventArgs, IEquatable<Key>
 
 
             if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
             if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
             {
             {
-                key = new Key ((KeyCode)parsedInt | KeyCode.ShiftMask);
+                key = new ((KeyCode)parsedInt | KeyCode.ShiftMask);
 
 
                 return true;
                 return true;
             }
             }
 
 
-            key = new Key ((KeyCode)parsedInt);
+            key = new ((KeyCode)parsedInt);
 
 
             return true;
             return true;
         }
         }
@@ -722,7 +768,7 @@ public class Key : EventArgs, IEquatable<Key>
 
 
         if (GetIsKeyCodeAtoZ (parsedKeyCode))
         if (GetIsKeyCodeAtoZ (parsedKeyCode))
         {
         {
-            key = new Key (parsedKeyCode | (modifiers & ~KeyCode.Space));
+            key = new (parsedKeyCode | (modifiers & ~KeyCode.Space));
 
 
             return true;
             return true;
         }
         }

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

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

+ 30 - 21
Terminal.Gui/Input/KeyBindingScope.cs

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

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

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

+ 101 - 0
Terminal.Gui/Input/MouseEventArgs.cs

@@ -0,0 +1,101 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Specifies the event arguments for <see cref="Terminal.Gui.MouseEventArgs"/>. This is a higher-level construct than
+///     the wrapped <see cref="Terminal.Gui.MouseEventArgs"/> class and is used for the events defined on
+///     <see cref="View"/> and subclasses
+///     of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
+/// </summary>
+public class MouseEventArgs : HandledEventArgs
+{
+    /// <summary>
+    ///     Flags indicating the state of the mouse buttons and the type of event that occurred.
+    /// </summary>
+    public MouseFlags Flags { get; set; }
+
+    /// <summary>
+    ///     The screen-relative mouse position.
+    /// </summary>
+    public Point ScreenPosition { get; set; }
+
+    /// <summary>The deepest View who's <see cref="View.Frame"/> contains <see cref="ScreenPosition"/>.</summary>
+    public View? View { get; set; }
+
+    /// <summary>
+    ///     The position of the mouse in <see cref="View"/>'s Viewport-relative coordinates. Only valid if <see cref="View"/>
+    ///     is set.
+    /// </summary>
+    public Point Position { get; set; }
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the button pressed related flags.
+    /// </summary>
+    public bool IsPressed => Flags.HasFlag (MouseFlags.Button1Pressed)
+                             || Flags.HasFlag (MouseFlags.Button2Pressed)
+                             || Flags.HasFlag (MouseFlags.Button3Pressed)
+                             || Flags.HasFlag (MouseFlags.Button4Pressed);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the button released related flags.
+    /// </summary>
+    public bool IsReleased => Flags.HasFlag (MouseFlags.Button1Released)
+                              || Flags.HasFlag (MouseFlags.Button2Released)
+                              || Flags.HasFlag (MouseFlags.Button3Released)
+                              || Flags.HasFlag (MouseFlags.Button4Released);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the single-clicked related flags.
+    /// </summary>
+    public bool IsSingleClicked => Flags.HasFlag (MouseFlags.Button1Clicked)
+                                   || Flags.HasFlag (MouseFlags.Button2Clicked)
+                                   || Flags.HasFlag (MouseFlags.Button3Clicked)
+                                   || Flags.HasFlag (MouseFlags.Button4Clicked);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the double-clicked related flags.
+    /// </summary>
+    public bool IsDoubleClicked => Flags.HasFlag (MouseFlags.Button1DoubleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button2DoubleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button3DoubleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button4DoubleClicked);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the triple-clicked related flags.
+    /// </summary>
+    public bool IsTripleClicked => Flags.HasFlag (MouseFlags.Button1TripleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button2TripleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button3TripleClicked)
+                                   || Flags.HasFlag (MouseFlags.Button4TripleClicked);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the mouse button clicked related flags.
+    /// </summary>
+    public bool IsSingleDoubleOrTripleClicked =>
+        Flags.HasFlag (MouseFlags.Button1Clicked)
+        || Flags.HasFlag (MouseFlags.Button2Clicked)
+        || Flags.HasFlag (MouseFlags.Button3Clicked)
+        || Flags.HasFlag (MouseFlags.Button4Clicked)
+        || Flags.HasFlag (MouseFlags.Button1DoubleClicked)
+        || Flags.HasFlag (MouseFlags.Button2DoubleClicked)
+        || Flags.HasFlag (MouseFlags.Button3DoubleClicked)
+        || Flags.HasFlag (MouseFlags.Button4DoubleClicked)
+        || Flags.HasFlag (MouseFlags.Button1TripleClicked)
+        || Flags.HasFlag (MouseFlags.Button2TripleClicked)
+        || Flags.HasFlag (MouseFlags.Button3TripleClicked)
+        || Flags.HasFlag (MouseFlags.Button4TripleClicked);
+
+    /// <summary>
+    ///     Gets whether <see cref="Flags"/> contains any of the mouse wheel related flags.
+    /// </summary>
+    public bool IsWheel => Flags.HasFlag (MouseFlags.WheeledDown)
+                           || Flags.HasFlag (MouseFlags.WheeledUp)
+                           || Flags.HasFlag (MouseFlags.WheeledLeft)
+                           || Flags.HasFlag (MouseFlags.WheeledRight);
+
+    /// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="Terminal.Gui.MouseEventArgs"/>.</summary>
+    /// <returns>A <see cref="T:System.String"/> that represents the current <see cref="Terminal.Gui.MouseEventArgs"/>.</returns>
+    public override string ToString () { return $"({ScreenPosition}):{Flags}:{View?.Id}:{Position}"; }
+}

+ 0 - 32
Terminal.Gui/Input/MouseEventEventArgs.cs

@@ -1,32 +0,0 @@
-namespace Terminal.Gui;
-
-/// <summary>
-///     Specifies the event arguments for <see cref="Terminal.Gui.MouseEvent"/>. This is a higher-level construct than
-///     the wrapped <see cref="MouseEvent"/> class and is used for the events defined on <see cref="View"/> and subclasses
-///     of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
-/// </summary>
-public class MouseEventEventArgs : EventArgs
-{
-    /// <summary>Constructs.</summary>
-    /// <param name="me">The mouse event.</param>
-    public MouseEventEventArgs (MouseEvent me) { MouseEvent = me; }
-
-    /// <summary>
-    ///     Indicates if the current mouse event has already been processed and the driver should stop notifying any other
-    ///     event subscriber. It's important to set this value to true specially when updating any View's layout from inside the
-    ///     subscriber method.
-    /// </summary>
-    /// <remarks>
-    ///     This property forwards to the <see cref="MouseEvent.Handled"/> property and is provided as a convenience and
-    ///     for backwards compatibility
-    /// </remarks>
-    public bool Handled
-    {
-        get => MouseEvent.Handled;
-        set => MouseEvent.Handled = value;
-    }
-
-    // TODO: Merge MouseEvent and MouseEventEventArgs into a single class.
-    /// <summary>The <see cref="Terminal.Gui.MouseEvent"/> for the event.</summary>
-    public MouseEvent MouseEvent { get; set; }
-}

+ 2 - 49
Terminal.Gui/Input/Mouse.cs → Terminal.Gui/Input/MouseFlags.cs

@@ -1,12 +1,12 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Mouse flags reported in <see cref="MouseEvent"/>.</summary>
+/// <summary>Mouse flags reported in <see cref="MouseEventArgs"/>.</summary>
 /// <remarks>They just happen to map to the ncurses ones.</remarks>
 /// <remarks>They just happen to map to the ncurses ones.</remarks>
 [Flags]
 [Flags]
 public enum MouseFlags
 public enum MouseFlags
 {
 {
     /// <summary>
     /// <summary>
-    ///    No mouse event. This is the default value for <see cref="MouseEvent.Flags"/> when no mouse event is being reported.
+    ///    No mouse event. This is the default value for <see cref="Terminal.Gui.MouseEventArgs.Flags"/> when no mouse event is being reported.
     /// </summary>
     /// </summary>
     None = 0,
     None = 0,
 
 
@@ -97,50 +97,3 @@ public enum MouseFlags
     /// <summary>Mask that captures all the events.</summary>
     /// <summary>Mask that captures all the events.</summary>
     AllEvents = 0x7ffffff
     AllEvents = 0x7ffffff
 }
 }
-
-// TODO: Merge MouseEvent and MouseEventEventArgs into a single class.
-
-/// <summary>
-///     Conveys the details of mouse events, such as coordinates and button state, from
-///     ConsoleDrivers up to <see cref="Application"/> and Views.
-/// </summary>
-/// <remarks>
-///     The <see cref="Application"/> class includes the <see cref="Application.MouseEvent"/> event which takes a
-///     MouseEvent argument.
-/// </remarks>
-public class MouseEvent
-{
-    /// <summary>Flags indicating the kind of mouse event that is being posted.</summary>
-    public MouseFlags Flags { get; set; }
-
-    /// <summary>The View at the location for the mouse event.</summary>
-    public View View { get; set; }
-
-    /// <summary>The position of the mouse in <see cref="Gui.View.Viewport"/>-relative coordinates.</summary>
-    public Point Position { get; set; }
-
-    /// <summary>
-    ///     The screen-relative mouse position.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         <see cref="Position"/> is <see cref="Gui.View.Viewport"/>-relative. When the mouse is grabbed by a view,
-    ///         <see cref="ScreenPosition"/> provides the mouse position screen-relative coordinates, enabling the grabbed view to know how much the
-    ///         mouse has moved.
-    ///     </para>
-    ///     <para>
-    ///         Calculated and processed in <see cref="Application.OnMouseEvent(MouseEvent)"/>.
-    ///     </para>
-    /// </remarks>
-    public Point ScreenPosition { get; set; }
-
-    /// <summary>
-    ///     Indicates if the current mouse event has been processed. Set this value to <see langword="true"/> to indicate the mouse
-    ///     event was handled.
-    /// </summary>
-    public bool Handled { get; set; }
-
-    /// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="MouseEvent"/>.</summary>
-    /// <returns>A <see cref="T:System.String"/> that represents the current <see cref="MouseEvent"/>.</returns>
-    public override string ToString () { return $"({Position}):{Flags}"; }
-}

+ 0 - 173
Terminal.Gui/Input/ShortcutHelper.cs

@@ -1,173 +0,0 @@
-using System.Diagnostics;
-
-namespace Terminal.Gui;
-
-// TODO: Nuke when #2975 is completed
-/// <summary>Represents a helper to manipulate shortcut keys used on views.</summary>
-public class ShortcutHelper
-{
-    // TODO: Update this to use Key, not KeyCode
-    private KeyCode shortcut;
-
-    /// <summary>This is the global setting that can be used as a global shortcut to invoke the action on the view.</summary>
-    public virtual KeyCode Shortcut
-    {
-        get => shortcut;
-        set
-        {
-            if (shortcut != value && (PostShortcutValidation (value) || value is KeyCode.Null))
-            {
-                shortcut = value;
-            }
-        }
-    }
-
-    /// <summary>The keystroke combination used in the <see cref="Shortcut"/> as string.</summary>
-    public virtual string ShortcutTag => Key.ToString (shortcut, Key.Separator);
-
-    /// <summary>Lookup for a <see cref="KeyCode"/> on range of keys.</summary>
-    /// <param name="key">The source key.</param>
-    /// <param name="first">First key in range.</param>
-    /// <param name="last">Last key in range.</param>
-    public static bool CheckKeysFlagRange (KeyCode key, KeyCode first, KeyCode last)
-    {
-        for (var i = (uint)first; i < (uint)last; i++)
-        {
-            if ((key | (KeyCode)i) == key)
-            {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /// <summary>Allows to retrieve a <see cref="KeyCode"/> from a <see cref="ShortcutTag"/></summary>
-    /// <param name="tag">The key as string.</param>
-    /// <param name="delimiter">The delimiter string.</param>
-    public static KeyCode GetShortcutFromTag (string tag, Rune delimiter = default)
-    {
-        string sCut = tag;
-
-        if (string.IsNullOrEmpty (sCut))
-        {
-            return default (KeyCode);
-        }
-
-        var key = KeyCode.Null;
-
-        //var hasCtrl = false;
-        if (delimiter == default (Rune))
-        {
-            delimiter = Key.Separator;
-        }
-
-        string [] keys = sCut.Split (delimiter.ToString ());
-
-        for (var i = 0; i < keys.Length; i++)
-        {
-            string k = keys [i];
-
-            if (k == "Ctrl")
-            {
-                //hasCtrl = true;
-                key |= KeyCode.CtrlMask;
-            }
-            else if (k == "Shift")
-            {
-                key |= KeyCode.ShiftMask;
-            }
-            else if (k == "Alt")
-            {
-                key |= KeyCode.AltMask;
-            }
-            else if (k.StartsWith ("F") && k.Length > 1)
-            {
-                int.TryParse (k.Substring (1), out int n);
-
-                for (var j = (uint)KeyCode.F1; j <= (uint)KeyCode.F12; j++)
-                {
-                    int.TryParse (((KeyCode)j).ToString ().Substring (1), out int f);
-
-                    if (f == n)
-                    {
-                        key |= (KeyCode)j;
-                    }
-                }
-            }
-            else
-            {
-                key |= (KeyCode)Enum.Parse (typeof (KeyCode), k);
-            }
-        }
-
-        return key;
-    }
-
-    /// <summary>Used at key up validation.</summary>
-    /// <param name="key">The key to validate.</param>
-    /// <returns><c>true</c> if is valid.<c>false</c>otherwise.</returns>
-    public static bool PostShortcutValidation (KeyCode key)
-    {
-        GetKeyToString (key, out KeyCode knm);
-
-        if (CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12) || ((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) != 0 && knm != KeyCode.Null))
-        {
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>Used at key down or key press validation.</summary>
-    /// <param name="key">The key to validate.</param>
-    /// <returns><c>true</c> if is valid.<c>false</c>otherwise.</returns>
-    public static bool PreShortcutValidation (KeyCode key)
-    {
-        if ((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) == 0
-            && !CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12))
-        {
-            return false;
-        }
-
-        return true;
-    }
-
-    /// <summary>Return key as string.</summary>
-    /// <param name="key">The key to extract.</param>
-    /// <param name="knm">Correspond to the non modifier key.</param>
-    private static string GetKeyToString (KeyCode key, out KeyCode knm)
-    {
-        if (key == KeyCode.Null)
-        {
-            knm = KeyCode.Null;
-
-            return "";
-        }
-
-        knm = key;
-        KeyCode mK = key & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask);
-        knm &= ~mK;
-
-        for (var i = (uint)KeyCode.F1; i < (uint)KeyCode.F12; i++)
-        {
-            if (knm == (KeyCode)i)
-            {
-                mK |= (KeyCode)i;
-            }
-        }
-
-        knm &= ~mK;
-        uint.TryParse (knm.ToString (), out uint c);
-        string s = mK == KeyCode.Null ? "" : mK.ToString ();
-
-        if (s != "" && (knm != KeyCode.Null || c > 0))
-        {
-            s += ",";
-        }
-
-        s += c == 0 ? knm == KeyCode.Null ? "" : knm.ToString () : ((char)c).ToString ();
-
-        return s;
-    }
-}

+ 72 - 0
Terminal.Gui/Resources/Strings.Designer.cs

@@ -195,6 +195,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightGreen.
+        /// </summary>
+        internal static string _16C60C {
+            get {
+                return ResourceManager.GetString("#16C60C", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to MidnightBlue.
         ///   Looks up a localized string similar to MidnightBlue.
         /// </summary>
         /// </summary>
@@ -258,6 +267,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightBlue.
+        /// </summary>
+        internal static string _3B78FF {
+            get {
+                return ResourceManager.GetString("#3B78FF", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to MediumSeaGreen.
         ///   Looks up a localized string similar to MediumSeaGreen.
         /// </summary>
         /// </summary>
@@ -339,6 +357,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightCyan.
+        /// </summary>
+        internal static string _61D6D6 {
+            get {
+                return ResourceManager.GetString("#61D6D6", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to CornflowerBlue.
         ///   Looks up a localized string similar to CornflowerBlue.
         /// </summary>
         /// </summary>
@@ -402,6 +429,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to DarkGray.
+        /// </summary>
+        internal static string _767676 {
+            get {
+                return ResourceManager.GetString("#767676", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to LightSlateGrey.
         ///   Looks up a localized string similar to LightSlateGrey.
         /// </summary>
         /// </summary>
@@ -681,6 +717,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightMagenta.
+        /// </summary>
+        internal static string _B4009E {
+            get {
+                return ResourceManager.GetString("#B4009E", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to DarkGoldenRod.
         ///   Looks up a localized string similar to DarkGoldenRod.
         /// </summary>
         /// </summary>
@@ -870,6 +915,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightRed.
+        /// </summary>
+        internal static string _E74856 {
+            get {
+                return ResourceManager.GetString("#E74856", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to DarkSalmon.
         ///   Looks up a localized string similar to DarkSalmon.
         /// </summary>
         /// </summary>
@@ -996,6 +1050,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightYellow.
+        /// </summary>
+        internal static string _F9F1A5 {
+            get {
+                return ResourceManager.GetString("#F9F1A5", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to Salmon.
         ///   Looks up a localized string similar to Salmon.
         /// </summary>
         /// </summary>
@@ -1374,6 +1437,15 @@ namespace Terminal.Gui.Resources {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   Looks up a localized string similar to Co_lors.
+        /// </summary>
+        internal static string ctxColors {
+            get {
+                return ResourceManager.GetString("ctxColors", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   Looks up a localized string similar to _Copy.
         ///   Looks up a localized string similar to _Copy.
         /// </summary>
         /// </summary>

+ 15 - 12
Terminal.Gui/Resources/Strings.fr-FR.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>Tout _sélectionner</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Tout supprimer</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copier</value>
     <value>_Copier</value>
   </data>
   </data>
   <data name="ctxCut" xml:space="preserve">
   <data name="ctxCut" xml:space="preserve">
     <value>Co_uper</value>
     <value>Co_uper</value>
   </data>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Tout supprimer</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
   <data name="ctxPaste" xml:space="preserve">
     <value>C_oller</value>
     <value>C_oller</value>
   </data>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Rétablir</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>Tout _sélectionner</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
   <data name="ctxUndo" xml:space="preserve">
     <value>_Annuler</value>
     <value>_Annuler</value>
   </data>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Rétablir</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
   <data name="fdDirectory" xml:space="preserve">
     <value>_Dossier</value>
     <value>_Dossier</value>
   </data>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
   <data name="wzNext" xml:space="preserve">
     <value>Prochai_n...</value>
     <value>Prochai_n...</value>
   </data>
   </data>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Ouvrir</value>
+  </data>
   <data name="btnSave" xml:space="preserve">
   <data name="btnSave" xml:space="preserve">
     <value>Enregistrer</value>
     <value>Enregistrer</value>
   </data>
   </data>
   <data name="btnSaveAs" xml:space="preserve">
   <data name="btnSaveAs" xml:space="preserve">
     <value>E_nregistrer sous</value>
     <value>E_nregistrer sous</value>
   </data>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Ouvrir</value>
-  </data>
   <data name="dpTitle" xml:space="preserve">
   <data name="dpTitle" xml:space="preserve">
     <value>Sélecteur de Date</value>
     <value>Sélecteur de Date</value>
   </data>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Cou_leurs</value>
+  </data>
 </root>
 </root>

+ 58 - 55
Terminal.Gui/Resources/Strings.ja-JP.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>全て選択 (_S)</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>全て削除 (_D)</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
   <data name="ctxCopy" xml:space="preserve">
     <value>コピー (_C)</value>
     <value>コピー (_C)</value>
   </data>
   </data>
   <data name="ctxCut" xml:space="preserve">
   <data name="ctxCut" xml:space="preserve">
     <value>切り取り (_T)</value>
     <value>切り取り (_T)</value>
   </data>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>全て削除 (_D)</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
   <data name="ctxPaste" xml:space="preserve">
     <value>貼り付け (_P)</value>
     <value>貼り付け (_P)</value>
   </data>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>やり直し (_R)</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>全て選択 (_S)</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
   <data name="ctxUndo" xml:space="preserve">
     <value>元に戻す (_U)</value>
     <value>元に戻す (_U)</value>
   </data>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>やり直し (_R)</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
   <data name="fdDirectory" xml:space="preserve">
     <value>ディレクトリ</value>
     <value>ディレクトリ</value>
   </data>
   </data>
@@ -171,44 +171,47 @@
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>同じ名前のディレクトリはすでに存在しました</value>
     <value>同じ名前のディレクトリはすでに存在しました</value>
   </data>
   </data>
-  <data name="fdDeleteBody" xml:space="preserve">
-    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
-  </data>
-  <data name="fdType" xml:space="preserve">
-    <value>タイプ</value>
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したディレクトリを選択してください</value>
   </data>
   </data>
-  <data name="fdSize" xml:space="preserve">
-    <value>サイズ</value>
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+    <value>同じ名前のファイルはすでに存在しました</value>
   </data>
   </data>
-  <data name="fdPathCaption" xml:space="preserve">
-    <value>パスを入力</value>
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルを選択してください</value>
   </data>
   </data>
   <data name="fdFilename" xml:space="preserve">
   <data name="fdFilename" xml:space="preserve">
     <value>ファイル名</value>
     <value>ファイル名</value>
   </data>
   </data>
-  <data name="fdNewTitle" xml:space="preserve">
-    <value>新規ディレクトリ</value>
-  </data>
-  <data name="btnNo" xml:space="preserve">
-    <value>いいえ (_N)</value>
-  </data>
-  <data name="btnYes" xml:space="preserve">
-    <value>はい (_Y)</value>
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
   </data>
   </data>
   <data name="fdModified" xml:space="preserve">
   <data name="fdModified" xml:space="preserve">
     <value>変更日時</value>
     <value>変更日時</value>
   </data>
   </data>
-  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
+  <data name="fdPathCaption" xml:space="preserve">
+    <value>パスを入力</value>
   </data>
   </data>
-  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したディレクトリを選択してください</value>
+  <data name="fdSearchCaption" xml:space="preserve">
+    <value>検索を入力</value>
   </data>
   </data>
-  <data name="fdFileMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルを選択してください</value>
+  <data name="fdSize" xml:space="preserve">
+    <value>サイズ</value>
   </data>
   </data>
-  <data name="fdRenamePrompt" xml:space="preserve">
-    <value>名前:</value>
+  <data name="fdType" xml:space="preserve">
+    <value>タイプ</value>
+  </data>
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+    <value>ファイルタイプが間違っでいます</value>
+  </data>
+  <data name="fdAnyFiles" xml:space="preserve">
+    <value>任意ファイル</value>
+  </data>
+  <data name="fdDeleteBody" xml:space="preserve">
+    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
+  </data>
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
+    <value>削除失敗</value>
   </data>
   </data>
   <data name="fdDeleteTitle" xml:space="preserve">
   <data name="fdDeleteTitle" xml:space="preserve">
     <value>{0} を削除</value>
     <value>{0} を削除</value>
@@ -216,35 +219,26 @@
   <data name="fdNewFailed" xml:space="preserve">
   <data name="fdNewFailed" xml:space="preserve">
     <value>新規失敗</value>
     <value>新規失敗</value>
   </data>
   </data>
-  <data name="fdExisting" xml:space="preserve">
-    <value>既存</value>
+  <data name="fdNewTitle" xml:space="preserve">
+    <value>新規ディレクトリ</value>
   </data>
   </data>
-  <data name="fdRenameTitle" xml:space="preserve">
-    <value>名前を変更</value>
+  <data name="btnNo" xml:space="preserve">
+    <value>いいえ (_N)</value>
   </data>
   </data>
   <data name="fdRenameFailedTitle" xml:space="preserve">
   <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>変更失敗</value>
     <value>変更失敗</value>
   </data>
   </data>
-  <data name="fdDeleteFailedTitle" xml:space="preserve">
-    <value>削除失敗</value>
-  </data>
-  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
-    <value>同じ名前のファイルはすでに存在しました</value>
-  </data>
-  <data name="fdSearchCaption" xml:space="preserve">
-    <value>検索を入力</value>
-  </data>
-  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
-    <value>ファイルタイプが間違っでいます</value>
+  <data name="fdRenamePrompt" xml:space="preserve">
+    <value>名前:</value>
   </data>
   </data>
-  <data name="fdAnyFiles" xml:space="preserve">
-    <value>任意ファイル</value>
+  <data name="fdRenameTitle" xml:space="preserve">
+    <value>名前を変更</value>
   </data>
   </data>
-  <data name="btnCancel" xml:space="preserve">
-    <value>キャンセル (_C)</value>
+  <data name="btnYes" xml:space="preserve">
+    <value>はい (_Y)</value>
   </data>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>OK (_O)</value>
+  <data name="fdExisting" xml:space="preserve">
+    <value>既存</value>
   </data>
   </data>
   <data name="btnOpen" xml:space="preserve">
   <data name="btnOpen" xml:space="preserve">
     <value>開く (_O)</value>
     <value>開く (_O)</value>
@@ -255,6 +249,12 @@
   <data name="btnSaveAs" xml:space="preserve">
   <data name="btnSaveAs" xml:space="preserve">
     <value>名前を付けて保存 (_S)</value>
     <value>名前を付けて保存 (_S)</value>
   </data>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>OK (_O)</value>
+  </data>
+  <data name="btnCancel" xml:space="preserve">
+    <value>キャンセル (_C)</value>
+  </data>
   <data name="fdCtxDelete" xml:space="preserve">
   <data name="fdCtxDelete" xml:space="preserve">
     <value>削除 (_D)</value>
     <value>削除 (_D)</value>
   </data>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
   <data name="dpTitle" xml:space="preserve">
     <value>日付ピッカー</value>
     <value>日付ピッカー</value>
   </data>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>絵の具 (_L)</value>
+  </data>
 </root>
 </root>

+ 16 - 13
Terminal.Gui/Resources/Strings.pt-PT.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>_Selecionar Tudo</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Apagar Tudo</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copiar</value>
     <value>_Copiar</value>
   </data>
   </data>
   <data name="ctxCut" xml:space="preserve">
   <data name="ctxCut" xml:space="preserve">
     <value>Cor_tar</value>
     <value>Cor_tar</value>
   </data>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Apagar Tudo</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
   <data name="ctxPaste" xml:space="preserve">
     <value>Co_lar</value>
     <value>Co_lar</value>
   </data>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Refazer</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>_Selecionar Tudo</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
   <data name="ctxUndo" xml:space="preserve">
     <value>_Desfazer</value>
     <value>_Desfazer</value>
   </data>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Refazer</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
   <data name="fdDirectory" xml:space="preserve">
     <value>Diretório</value>
     <value>Diretório</value>
   </data>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
   <data name="wzNext" xml:space="preserve">
     <value>S_eguir...</value>
     <value>S_eguir...</value>
   </data>
   </data>
-  <data name="btnSaveAs" xml:space="preserve">
-    <value>Guardar como</value>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Abrir</value>
   </data>
   </data>
   <data name="btnSave" xml:space="preserve">
   <data name="btnSave" xml:space="preserve">
     <value>Guardar</value>
     <value>Guardar</value>
   </data>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Abrir</value>
+  <data name="btnSaveAs" xml:space="preserve">
+    <value>Guardar como</value>
   </data>
   </data>
   <data name="dpTitle" xml:space="preserve">
   <data name="dpTitle" xml:space="preserve">
     <value>Seletor de Data</value>
     <value>Seletor de Data</value>
   </data>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_res</value>
+  </data>
 </root>
 </root>

+ 273 - 249
Terminal.Gui/Resources/Strings.resx

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <root>
 <root>
-    <!-- 
+  <!-- 
     Microsoft ResX Schema 
     Microsoft ResX Schema 
     
     
     Version 2.0
     Version 2.0
@@ -59,642 +59,666 @@
             : using a System.ComponentModel.TypeConverter
             : using a System.ComponentModel.TypeConverter
             : and then encoded with base64 encoding.
             : and then encoded with base64 encoding.
     -->
     -->
-    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-        <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-        <xsd:element name="root" msdata:IsDataSet="true">
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
             <xsd:complexType>
             <xsd:complexType>
-                <xsd:choice maxOccurs="unbounded">
-                    <xsd:element name="metadata">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" use="required" type="xsd:string" />
-                            <xsd:attribute name="type" type="xsd:string" />
-                            <xsd:attribute name="mimetype" type="xsd:string" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="assembly">
-                        <xsd:complexType>
-                            <xsd:attribute name="alias" type="xsd:string" />
-                            <xsd:attribute name="name" type="xsd:string" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="data">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-                            <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-                            <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="resheader">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" />
-                        </xsd:complexType>
-                    </xsd:element>
-                </xsd:choice>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
             </xsd:complexType>
-        </xsd:element>
-    </xsd:schema>
-    <resheader name="resmimetype">
-        <value>text/microsoft-resx</value>
-    </resheader>
-    <resheader name="version">
-        <value>2.0</value>
-    </resheader>
-    <resheader name="reader">
-        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <resheader name="writer">
-        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <data name="ctxSelectAll" xml:space="preserve">
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
     <value>_Select All</value>
     <value>_Select All</value>
   </data>
   </data>
-    <data name="ctxDeleteAll" xml:space="preserve">
+  <data name="ctxDeleteAll" xml:space="preserve">
     <value>_Delete All</value>
     <value>_Delete All</value>
   </data>
   </data>
-    <data name="ctxCopy" xml:space="preserve">
+  <data name="ctxCopy" xml:space="preserve">
     <value>_Copy</value>
     <value>_Copy</value>
   </data>
   </data>
-    <data name="ctxCut" xml:space="preserve">
+  <data name="ctxCut" xml:space="preserve">
     <value>Cu_t</value>
     <value>Cu_t</value>
   </data>
   </data>
-    <data name="ctxPaste" xml:space="preserve">
+  <data name="ctxPaste" xml:space="preserve">
     <value>_Paste</value>
     <value>_Paste</value>
   </data>
   </data>
-    <data name="ctxUndo" xml:space="preserve">
+  <data name="ctxUndo" xml:space="preserve">
     <value>_Undo</value>
     <value>_Undo</value>
   </data>
   </data>
-    <data name="ctxRedo" xml:space="preserve">
+  <data name="ctxRedo" xml:space="preserve">
     <value>_Redo</value>
     <value>_Redo</value>
   </data>
   </data>
-    <data name="fdDirectory" xml:space="preserve">
+  <data name="fdDirectory" xml:space="preserve">
     <value>Directory</value>
     <value>Directory</value>
   </data>
   </data>
-    <data name="fdFile" xml:space="preserve">
+  <data name="fdFile" xml:space="preserve">
     <value>File</value>
     <value>File</value>
   </data>
   </data>
-    <data name="fdSave" xml:space="preserve">
+  <data name="fdSave" xml:space="preserve">
     <value>Save</value>
     <value>Save</value>
   </data>
   </data>
-    <data name="fdSaveAs" xml:space="preserve">
+  <data name="fdSaveAs" xml:space="preserve">
     <value>Save as</value>
     <value>Save as</value>
   </data>
   </data>
-    <data name="fdOpen" xml:space="preserve">
+  <data name="fdOpen" xml:space="preserve">
     <value>Open</value>
     <value>Open</value>
   </data>
   </data>
-    <data name="fdSelectFolder" xml:space="preserve">
+  <data name="fdSelectFolder" xml:space="preserve">
     <value>Select folder</value>
     <value>Select folder</value>
   </data>
   </data>
-    <data name="fdSelectMixed" xml:space="preserve">
+  <data name="fdSelectMixed" xml:space="preserve">
     <value>Select Mixed</value>
     <value>Select Mixed</value>
   </data>
   </data>
-    <data name="wzBack" xml:space="preserve">
+  <data name="wzBack" xml:space="preserve">
     <value>_Back</value>
     <value>_Back</value>
   </data>
   </data>
-    <data name="wzFinish" xml:space="preserve">
+  <data name="wzFinish" xml:space="preserve">
     <value>Fi_nish</value>
     <value>Fi_nish</value>
   </data>
   </data>
-    <data name="wzNext" xml:space="preserve">
+  <data name="wzNext" xml:space="preserve">
     <value>_Next...</value>
     <value>_Next...</value>
   </data>
   </data>
-    <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>Directory already exists with that name</value>
     <value>Directory already exists with that name</value>
     <comment>When trying to save a file with a name already taken by a directory</comment>
     <comment>When trying to save a file with a name already taken by a directory</comment>
   </data>
   </data>
-    <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing directory</value>
     <value>Must select an existing directory</value>
   </data>
   </data>
-    <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
     <value>File already exists with that name</value>
     <value>File already exists with that name</value>
   </data>
   </data>
-    <data name="fdFileMustExistFeedback" xml:space="preserve">
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file</value>
     <value>Must select an existing file</value>
     <comment>When trying to save a directory with a name already used by a file</comment>
     <comment>When trying to save a directory with a name already used by a file</comment>
   </data>
   </data>
-    <data name="fdFilename" xml:space="preserve">
+  <data name="fdFilename" xml:space="preserve">
     <value>Filename</value>
     <value>Filename</value>
   </data>
   </data>
-    <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file or directory</value>
     <value>Must select an existing file or directory</value>
   </data>
   </data>
-    <data name="fdModified" xml:space="preserve">
+  <data name="fdModified" xml:space="preserve">
     <value>Modified</value>
     <value>Modified</value>
   </data>
   </data>
-    <data name="fdPathCaption" xml:space="preserve">
+  <data name="fdPathCaption" xml:space="preserve">
     <value>Enter Path</value>
     <value>Enter Path</value>
   </data>
   </data>
-    <data name="fdSearchCaption" xml:space="preserve">
+  <data name="fdSearchCaption" xml:space="preserve">
     <value>Enter Search</value>
     <value>Enter Search</value>
   </data>
   </data>
-    <data name="fdSize" xml:space="preserve">
+  <data name="fdSize" xml:space="preserve">
     <value>Size</value>
     <value>Size</value>
   </data>
   </data>
-    <data name="fdType" xml:space="preserve">
+  <data name="fdType" xml:space="preserve">
     <value>Type</value>
     <value>Type</value>
   </data>
   </data>
-    <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
     <value>Wrong file type</value>
     <value>Wrong file type</value>
     <comment>When trying to open/save a file that does not match the provided filter (e.g. csv)</comment>
     <comment>When trying to open/save a file that does not match the provided filter (e.g. csv)</comment>
   </data>
   </data>
-    <data name="fdAnyFiles" xml:space="preserve">
+  <data name="fdAnyFiles" xml:space="preserve">
     <value>Any Files</value>
     <value>Any Files</value>
     <comment>Describes an AllowedType that matches anything</comment>
     <comment>Describes an AllowedType that matches anything</comment>
   </data>
   </data>
-    <data name="fdDeleteBody" xml:space="preserve">
+  <data name="fdDeleteBody" xml:space="preserve">
     <value>Are you sure you want to delete '{0}'? This operation is permanent</value>
     <value>Are you sure you want to delete '{0}'? This operation is permanent</value>
   </data>
   </data>
-    <data name="fdDeleteFailedTitle" xml:space="preserve">
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
     <value>Delete Failed</value>
     <value>Delete Failed</value>
   </data>
   </data>
-    <data name="fdDeleteTitle" xml:space="preserve">
+  <data name="fdDeleteTitle" xml:space="preserve">
     <value>Delete {0}</value>
     <value>Delete {0}</value>
   </data>
   </data>
-    <data name="fdNewFailed" xml:space="preserve">
+  <data name="fdNewFailed" xml:space="preserve">
     <value>New Failed</value>
     <value>New Failed</value>
   </data>
   </data>
-    <data name="fdNewTitle" xml:space="preserve">
+  <data name="fdNewTitle" xml:space="preserve">
     <value>New Folder</value>
     <value>New Folder</value>
   </data>
   </data>
-    <data name="btnNo" xml:space="preserve">
+  <data name="btnNo" xml:space="preserve">
     <value>_No</value>
     <value>_No</value>
   </data>
   </data>
-    <data name="fdRenameFailedTitle" xml:space="preserve">
+  <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>Rename Failed</value>
     <value>Rename Failed</value>
   </data>
   </data>
-    <data name="fdRenamePrompt" xml:space="preserve">
+  <data name="fdRenamePrompt" xml:space="preserve">
     <value>Name:</value>
     <value>Name:</value>
   </data>
   </data>
-    <data name="fdRenameTitle" xml:space="preserve">
+  <data name="fdRenameTitle" xml:space="preserve">
     <value>Rename</value>
     <value>Rename</value>
   </data>
   </data>
-    <data name="btnYes" xml:space="preserve">
+  <data name="btnYes" xml:space="preserve">
     <value>_Yes</value>
     <value>_Yes</value>
   </data>
   </data>
-    <data name="fdExisting" xml:space="preserve">
+  <data name="fdExisting" xml:space="preserve">
     <value>Existing</value>
     <value>Existing</value>
   </data>
   </data>
-    <data name="btnOpen" xml:space="preserve">
+  <data name="btnOpen" xml:space="preserve">
     <value>O_pen</value>
     <value>O_pen</value>
   </data>
   </data>
-    <data name="btnSave" xml:space="preserve">
+  <data name="btnSave" xml:space="preserve">
     <value>_Save</value>
     <value>_Save</value>
   </data>
   </data>
-    <data name="btnSaveAs" xml:space="preserve">
+  <data name="btnSaveAs" xml:space="preserve">
     <value>Save _as</value>
     <value>Save _as</value>
   </data>
   </data>
-    <data name="btnOk" xml:space="preserve">
+  <data name="btnOk" xml:space="preserve">
     <value>_OK</value>
     <value>_OK</value>
   </data>
   </data>
-    <data name="btnCancel" xml:space="preserve">
+  <data name="btnCancel" xml:space="preserve">
     <value>_Cancel</value>
     <value>_Cancel</value>
   </data>
   </data>
-    <data name="fdCtxDelete" xml:space="preserve">
+  <data name="fdCtxDelete" xml:space="preserve">
     <value>_Delete</value>
     <value>_Delete</value>
   </data>
   </data>
-    <data name="fdCtxHide" xml:space="preserve">
+  <data name="fdCtxHide" xml:space="preserve">
     <value>_Hide {0}</value>
     <value>_Hide {0}</value>
   </data>
   </data>
-    <data name="fdCtxNew" xml:space="preserve">
+  <data name="fdCtxNew" xml:space="preserve">
     <value>_New</value>
     <value>_New</value>
   </data>
   </data>
-    <data name="fdCtxRename" xml:space="preserve">
+  <data name="fdCtxRename" xml:space="preserve">
     <value>_Rename</value>
     <value>_Rename</value>
   </data>
   </data>
-    <data name="fdCtxSortAsc" xml:space="preserve">
+  <data name="fdCtxSortAsc" xml:space="preserve">
     <value>_Sort {0} ASC</value>
     <value>_Sort {0} ASC</value>
   </data>
   </data>
-    <data name="fdCtxSortDesc" xml:space="preserve">
+  <data name="fdCtxSortDesc" xml:space="preserve">
     <value>_Sort {0} DESC</value>
     <value>_Sort {0} DESC</value>
   </data>
   </data>
-    <data name="dpTitle" xml:space="preserve">
+  <data name="dpTitle" xml:space="preserve">
     <value>Date Picker</value>
     <value>Date Picker</value>
   </data>
   </data>
-    <data name="#F0F8FF" xml:space="preserve">
+  <data name="#F0F8FF" xml:space="preserve">
     <value>AliceBlue</value>
     <value>AliceBlue</value>
   </data>
   </data>
-    <data name="#FAEBD7" xml:space="preserve">
+  <data name="#FAEBD7" xml:space="preserve">
     <value>AntiqueWhite</value>
     <value>AntiqueWhite</value>
   </data>
   </data>
-    <data name="#7FFFD4" xml:space="preserve">
+  <data name="#7FFFD4" xml:space="preserve">
     <value>Aquamarine</value>
     <value>Aquamarine</value>
   </data>
   </data>
-    <data name="#F0FFFF" xml:space="preserve">
+  <data name="#F0FFFF" xml:space="preserve">
     <value>Azure</value>
     <value>Azure</value>
   </data>
   </data>
-    <data name="#F5F5DC" xml:space="preserve">
+  <data name="#F5F5DC" xml:space="preserve">
     <value>Beige</value>
     <value>Beige</value>
   </data>
   </data>
-    <data name="#FFE4C4" xml:space="preserve">
+  <data name="#FFE4C4" xml:space="preserve">
     <value>Bisque</value>
     <value>Bisque</value>
   </data>
   </data>
-    <data name="#000000" xml:space="preserve">
+  <data name="#000000" xml:space="preserve">
     <value>Black</value>
     <value>Black</value>
   </data>
   </data>
-    <data name="#FFEBCD" xml:space="preserve">
+  <data name="#FFEBCD" xml:space="preserve">
     <value>BlanchedAlmond</value>
     <value>BlanchedAlmond</value>
   </data>
   </data>
-    <data name="#0000FF" xml:space="preserve">
+  <data name="#0000FF" xml:space="preserve">
     <value>Blue</value>
     <value>Blue</value>
   </data>
   </data>
-    <data name="#8A2BE2" xml:space="preserve">
+  <data name="#8A2BE2" xml:space="preserve">
     <value>BlueViolet</value>
     <value>BlueViolet</value>
   </data>
   </data>
-    <data name="#A52A2A" xml:space="preserve">
+  <data name="#A52A2A" xml:space="preserve">
     <value>Brown</value>
     <value>Brown</value>
   </data>
   </data>
-    <data name="#DEB887" xml:space="preserve">
+  <data name="#DEB887" xml:space="preserve">
     <value>BurlyWood</value>
     <value>BurlyWood</value>
   </data>
   </data>
-    <data name="#5F9EA0" xml:space="preserve">
+  <data name="#5F9EA0" xml:space="preserve">
     <value>CadetBlue</value>
     <value>CadetBlue</value>
   </data>
   </data>
-    <data name="#7FFF00" xml:space="preserve">
+  <data name="#7FFF00" xml:space="preserve">
     <value>Chartreuse</value>
     <value>Chartreuse</value>
   </data>
   </data>
-    <data name="#D2691E" xml:space="preserve">
+  <data name="#D2691E" xml:space="preserve">
     <value>Chocolate</value>
     <value>Chocolate</value>
   </data>
   </data>
-    <data name="#FF7F50" xml:space="preserve">
+  <data name="#FF7F50" xml:space="preserve">
     <value>Coral</value>
     <value>Coral</value>
   </data>
   </data>
-    <data name="#6495ED" xml:space="preserve">
+  <data name="#6495ED" xml:space="preserve">
     <value>CornflowerBlue</value>
     <value>CornflowerBlue</value>
   </data>
   </data>
-    <data name="#FFF8DC" xml:space="preserve">
+  <data name="#FFF8DC" xml:space="preserve">
     <value>Cornsilk</value>
     <value>Cornsilk</value>
   </data>
   </data>
-    <data name="#DC143C" xml:space="preserve">
+  <data name="#DC143C" xml:space="preserve">
     <value>Crimson</value>
     <value>Crimson</value>
   </data>
   </data>
-    <data name="#00FFFF" xml:space="preserve">
+  <data name="#00FFFF" xml:space="preserve">
     <value>Cyan</value>
     <value>Cyan</value>
   </data>
   </data>
-    <data name="#00008B" xml:space="preserve">
+  <data name="#00008B" xml:space="preserve">
     <value>DarkBlue</value>
     <value>DarkBlue</value>
   </data>
   </data>
-    <data name="#008B8B" xml:space="preserve">
+  <data name="#008B8B" xml:space="preserve">
     <value>DarkCyan</value>
     <value>DarkCyan</value>
   </data>
   </data>
-    <data name="#B8860B" xml:space="preserve">
+  <data name="#B8860B" xml:space="preserve">
     <value>DarkGoldenRod</value>
     <value>DarkGoldenRod</value>
   </data>
   </data>
-    <data name="#A9A9A9" xml:space="preserve">
+  <data name="#A9A9A9" xml:space="preserve">
     <value>DarkGrey</value>
     <value>DarkGrey</value>
   </data>
   </data>
-    <data name="#006400" xml:space="preserve">
+  <data name="#006400" xml:space="preserve">
     <value>DarkGreen</value>
     <value>DarkGreen</value>
   </data>
   </data>
-    <data name="#BDB76B" xml:space="preserve">
+  <data name="#BDB76B" xml:space="preserve">
     <value>DarkKhaki</value>
     <value>DarkKhaki</value>
   </data>
   </data>
-    <data name="#8B008B" xml:space="preserve">
+  <data name="#8B008B" xml:space="preserve">
     <value>DarkMagenta</value>
     <value>DarkMagenta</value>
   </data>
   </data>
-    <data name="#556B2F" xml:space="preserve">
+  <data name="#556B2F" xml:space="preserve">
     <value>DarkOliveGreen</value>
     <value>DarkOliveGreen</value>
   </data>
   </data>
-    <data name="#FF8C00" xml:space="preserve">
+  <data name="#FF8C00" xml:space="preserve">
     <value>DarkOrange</value>
     <value>DarkOrange</value>
   </data>
   </data>
-    <data name="#9932CC" xml:space="preserve">
+  <data name="#9932CC" xml:space="preserve">
     <value>DarkOrchid</value>
     <value>DarkOrchid</value>
   </data>
   </data>
-    <data name="#8B0000" xml:space="preserve">
+  <data name="#8B0000" xml:space="preserve">
     <value>DarkRed</value>
     <value>DarkRed</value>
   </data>
   </data>
-    <data name="#E9967A" xml:space="preserve">
+  <data name="#E9967A" xml:space="preserve">
     <value>DarkSalmon</value>
     <value>DarkSalmon</value>
   </data>
   </data>
-    <data name="#8FBC8F" xml:space="preserve">
+  <data name="#8FBC8F" xml:space="preserve">
     <value>DarkSeaGreen</value>
     <value>DarkSeaGreen</value>
   </data>
   </data>
-    <data name="#483D8B" xml:space="preserve">
+  <data name="#483D8B" xml:space="preserve">
     <value>DarkSlateBlue</value>
     <value>DarkSlateBlue</value>
   </data>
   </data>
-    <data name="#2F4F4F" xml:space="preserve">
+  <data name="#2F4F4F" xml:space="preserve">
     <value>DarkSlateGrey</value>
     <value>DarkSlateGrey</value>
   </data>
   </data>
-    <data name="#00CED1" xml:space="preserve">
+  <data name="#00CED1" xml:space="preserve">
     <value>DarkTurquoise</value>
     <value>DarkTurquoise</value>
   </data>
   </data>
-    <data name="#9400D3" xml:space="preserve">
+  <data name="#9400D3" xml:space="preserve">
     <value>DarkViolet</value>
     <value>DarkViolet</value>
   </data>
   </data>
-    <data name="#FF1493" xml:space="preserve">
+  <data name="#FF1493" xml:space="preserve">
     <value>DeepPink</value>
     <value>DeepPink</value>
   </data>
   </data>
-    <data name="#00BFFF" xml:space="preserve">
+  <data name="#00BFFF" xml:space="preserve">
     <value>DeepSkyBlue</value>
     <value>DeepSkyBlue</value>
   </data>
   </data>
-    <data name="#696969" xml:space="preserve">
+  <data name="#696969" xml:space="preserve">
     <value>DimGray</value>
     <value>DimGray</value>
   </data>
   </data>
-    <data name="#1E90FF" xml:space="preserve">
+  <data name="#1E90FF" xml:space="preserve">
     <value>DodgerBlue</value>
     <value>DodgerBlue</value>
   </data>
   </data>
-    <data name="#B22222" xml:space="preserve">
+  <data name="#B22222" xml:space="preserve">
     <value>FireBrick</value>
     <value>FireBrick</value>
   </data>
   </data>
-    <data name="#FFFAF0" xml:space="preserve">
+  <data name="#FFFAF0" xml:space="preserve">
     <value>FloralWhite</value>
     <value>FloralWhite</value>
   </data>
   </data>
-    <data name="#228B22" xml:space="preserve">
+  <data name="#228B22" xml:space="preserve">
     <value>ForestGreen</value>
     <value>ForestGreen</value>
   </data>
   </data>
-    <data name="#DCDCDC" xml:space="preserve">
+  <data name="#DCDCDC" xml:space="preserve">
     <value>Gainsboro</value>
     <value>Gainsboro</value>
   </data>
   </data>
-    <data name="#F8F8FF" xml:space="preserve">
+  <data name="#F8F8FF" xml:space="preserve">
     <value>GhostWhite</value>
     <value>GhostWhite</value>
   </data>
   </data>
-    <data name="#FFD700" xml:space="preserve">
+  <data name="#FFD700" xml:space="preserve">
     <value>Gold</value>
     <value>Gold</value>
   </data>
   </data>
-    <data name="#DAA520" xml:space="preserve">
+  <data name="#DAA520" xml:space="preserve">
     <value>GoldenRod</value>
     <value>GoldenRod</value>
   </data>
   </data>
-    <data name="#808080" xml:space="preserve">
+  <data name="#808080" xml:space="preserve">
     <value>Gray</value>
     <value>Gray</value>
   </data>
   </data>
-    <data name="#008000" xml:space="preserve">
+  <data name="#008000" xml:space="preserve">
     <value>Green</value>
     <value>Green</value>
   </data>
   </data>
-    <data name="#ADFF2F" xml:space="preserve">
+  <data name="#ADFF2F" xml:space="preserve">
     <value>GreenYellow</value>
     <value>GreenYellow</value>
   </data>
   </data>
-    <data name="#F0FFF0" xml:space="preserve">
+  <data name="#F0FFF0" xml:space="preserve">
     <value>HoneyDew</value>
     <value>HoneyDew</value>
   </data>
   </data>
-    <data name="#FF69B4" xml:space="preserve">
+  <data name="#FF69B4" xml:space="preserve">
     <value>HotPink</value>
     <value>HotPink</value>
   </data>
   </data>
-    <data name="#CD5C5C" xml:space="preserve">
+  <data name="#CD5C5C" xml:space="preserve">
     <value>IndianRed</value>
     <value>IndianRed</value>
   </data>
   </data>
-    <data name="#4B0082" xml:space="preserve">
+  <data name="#4B0082" xml:space="preserve">
     <value>Indigo</value>
     <value>Indigo</value>
   </data>
   </data>
-    <data name="#FFFFF0" xml:space="preserve">
+  <data name="#FFFFF0" xml:space="preserve">
     <value>Ivory</value>
     <value>Ivory</value>
   </data>
   </data>
-    <data name="#F0E68C" xml:space="preserve">
+  <data name="#F0E68C" xml:space="preserve">
     <value>Khaki</value>
     <value>Khaki</value>
   </data>
   </data>
-    <data name="#E6E6FA" xml:space="preserve">
+  <data name="#E6E6FA" xml:space="preserve">
     <value>Lavender</value>
     <value>Lavender</value>
   </data>
   </data>
-    <data name="#FFF0F5" xml:space="preserve">
+  <data name="#FFF0F5" xml:space="preserve">
     <value>LavenderBlush</value>
     <value>LavenderBlush</value>
   </data>
   </data>
-    <data name="#7CFC00" xml:space="preserve">
+  <data name="#7CFC00" xml:space="preserve">
     <value>LawnGreen</value>
     <value>LawnGreen</value>
   </data>
   </data>
-    <data name="#FFFACD" xml:space="preserve">
+  <data name="#FFFACD" xml:space="preserve">
     <value>LemonChiffon</value>
     <value>LemonChiffon</value>
   </data>
   </data>
-    <data name="#ADD8E6" xml:space="preserve">
+  <data name="#ADD8E6" xml:space="preserve">
     <value>LightBlue</value>
     <value>LightBlue</value>
   </data>
   </data>
-    <data name="#F08080" xml:space="preserve">
+  <data name="#F08080" xml:space="preserve">
     <value>LightCoral</value>
     <value>LightCoral</value>
   </data>
   </data>
-    <data name="#E0FFFF" xml:space="preserve">
+  <data name="#E0FFFF" xml:space="preserve">
     <value>LightCyan</value>
     <value>LightCyan</value>
   </data>
   </data>
-    <data name="#FAFAD2" xml:space="preserve">
+  <data name="#FAFAD2" xml:space="preserve">
     <value>LightGoldenRodYellow</value>
     <value>LightGoldenRodYellow</value>
   </data>
   </data>
-    <data name="#D3D3D3" xml:space="preserve">
+  <data name="#D3D3D3" xml:space="preserve">
     <value>LightGray</value>
     <value>LightGray</value>
   </data>
   </data>
-    <data name="#90EE90" xml:space="preserve">
+  <data name="#90EE90" xml:space="preserve">
     <value>LightGreen</value>
     <value>LightGreen</value>
   </data>
   </data>
-    <data name="#FFB6C1" xml:space="preserve">
+  <data name="#FFB6C1" xml:space="preserve">
     <value>LightPink</value>
     <value>LightPink</value>
   </data>
   </data>
-    <data name="#FFA07A" xml:space="preserve">
+  <data name="#FFA07A" xml:space="preserve">
     <value>LightSalmon</value>
     <value>LightSalmon</value>
   </data>
   </data>
-    <data name="#20B2AA" xml:space="preserve">
+  <data name="#20B2AA" xml:space="preserve">
     <value>LightSeaGreen</value>
     <value>LightSeaGreen</value>
   </data>
   </data>
-    <data name="#87CEFA" xml:space="preserve">
+  <data name="#87CEFA" xml:space="preserve">
     <value>LightSkyBlue</value>
     <value>LightSkyBlue</value>
   </data>
   </data>
-    <data name="#778899" xml:space="preserve">
+  <data name="#778899" xml:space="preserve">
     <value>LightSlateGrey</value>
     <value>LightSlateGrey</value>
   </data>
   </data>
-    <data name="#B0C4DE" xml:space="preserve">
+  <data name="#B0C4DE" xml:space="preserve">
     <value>LightSteelBlue</value>
     <value>LightSteelBlue</value>
   </data>
   </data>
-    <data name="#FFFFE0" xml:space="preserve">
+  <data name="#FFFFE0" xml:space="preserve">
     <value>LightYellow</value>
     <value>LightYellow</value>
   </data>
   </data>
-    <data name="#00FF00" xml:space="preserve">
+  <data name="#00FF00" xml:space="preserve">
     <value>Lime</value>
     <value>Lime</value>
   </data>
   </data>
-    <data name="#32CD32" xml:space="preserve">
+  <data name="#32CD32" xml:space="preserve">
     <value>LimeGreen</value>
     <value>LimeGreen</value>
   </data>
   </data>
-    <data name="#FAF0E6" xml:space="preserve">
+  <data name="#FAF0E6" xml:space="preserve">
     <value>Linen</value>
     <value>Linen</value>
   </data>
   </data>
-    <data name="#FF00FF" xml:space="preserve">
+  <data name="#FF00FF" xml:space="preserve">
     <value>Magenta</value>
     <value>Magenta</value>
   </data>
   </data>
-    <data name="#800000" xml:space="preserve">
+  <data name="#800000" xml:space="preserve">
     <value>Maroon</value>
     <value>Maroon</value>
   </data>
   </data>
-    <data name="#66CDAA" xml:space="preserve">
+  <data name="#66CDAA" xml:space="preserve">
     <value>MediumAquaMarine</value>
     <value>MediumAquaMarine</value>
   </data>
   </data>
-    <data name="#0000CD" xml:space="preserve">
+  <data name="#0000CD" xml:space="preserve">
     <value>MediumBlue</value>
     <value>MediumBlue</value>
   </data>
   </data>
-    <data name="#BA55D3" xml:space="preserve">
+  <data name="#BA55D3" xml:space="preserve">
     <value>MediumOrchid</value>
     <value>MediumOrchid</value>
   </data>
   </data>
-    <data name="#9370DB" xml:space="preserve">
+  <data name="#9370DB" xml:space="preserve">
     <value>MediumPurple</value>
     <value>MediumPurple</value>
   </data>
   </data>
-    <data name="#3CB371" xml:space="preserve">
+  <data name="#3CB371" xml:space="preserve">
     <value>MediumSeaGreen</value>
     <value>MediumSeaGreen</value>
   </data>
   </data>
-    <data name="#7B68EE" xml:space="preserve">
+  <data name="#7B68EE" xml:space="preserve">
     <value>MediumSlateBlue</value>
     <value>MediumSlateBlue</value>
   </data>
   </data>
-    <data name="#00FA9A" xml:space="preserve">
+  <data name="#00FA9A" xml:space="preserve">
     <value>MediumSpringGreen</value>
     <value>MediumSpringGreen</value>
   </data>
   </data>
-    <data name="#48D1CC" xml:space="preserve">
+  <data name="#48D1CC" xml:space="preserve">
     <value>MediumTurquoise</value>
     <value>MediumTurquoise</value>
   </data>
   </data>
-    <data name="#C71585" xml:space="preserve">
+  <data name="#C71585" xml:space="preserve">
     <value>MediumVioletRed</value>
     <value>MediumVioletRed</value>
   </data>
   </data>
-    <data name="#191970" xml:space="preserve">
+  <data name="#191970" xml:space="preserve">
     <value>MidnightBlue</value>
     <value>MidnightBlue</value>
   </data>
   </data>
-    <data name="#F5FFFA" xml:space="preserve">
+  <data name="#F5FFFA" xml:space="preserve">
     <value>MintCream</value>
     <value>MintCream</value>
   </data>
   </data>
-    <data name="#FFE4E1" xml:space="preserve">
+  <data name="#FFE4E1" xml:space="preserve">
     <value>MistyRose</value>
     <value>MistyRose</value>
   </data>
   </data>
-    <data name="#FFE4B5" xml:space="preserve">
+  <data name="#FFE4B5" xml:space="preserve">
     <value>Moccasin</value>
     <value>Moccasin</value>
   </data>
   </data>
-    <data name="#FFDEAD" xml:space="preserve">
+  <data name="#FFDEAD" xml:space="preserve">
     <value>NavajoWhite</value>
     <value>NavajoWhite</value>
   </data>
   </data>
-    <data name="#000080" xml:space="preserve">
+  <data name="#000080" xml:space="preserve">
     <value>Navy</value>
     <value>Navy</value>
   </data>
   </data>
-    <data name="#FDF5E6" xml:space="preserve">
+  <data name="#FDF5E6" xml:space="preserve">
     <value>OldLace</value>
     <value>OldLace</value>
   </data>
   </data>
-    <data name="#808000" xml:space="preserve">
+  <data name="#808000" xml:space="preserve">
     <value>Olive</value>
     <value>Olive</value>
   </data>
   </data>
-    <data name="#6B8E23" xml:space="preserve">
+  <data name="#6B8E23" xml:space="preserve">
     <value>OliveDrab</value>
     <value>OliveDrab</value>
   </data>
   </data>
-    <data name="#FFA500" xml:space="preserve">
+  <data name="#FFA500" xml:space="preserve">
     <value>Orange</value>
     <value>Orange</value>
   </data>
   </data>
-    <data name="#FF4500" xml:space="preserve">
+  <data name="#FF4500" xml:space="preserve">
     <value>OrangeRed</value>
     <value>OrangeRed</value>
   </data>
   </data>
-    <data name="#DA70D6" xml:space="preserve">
+  <data name="#DA70D6" xml:space="preserve">
     <value>Orchid</value>
     <value>Orchid</value>
   </data>
   </data>
-    <data name="#EEE8AA" xml:space="preserve">
+  <data name="#EEE8AA" xml:space="preserve">
     <value>PaleGoldenRod</value>
     <value>PaleGoldenRod</value>
   </data>
   </data>
-    <data name="#98FB98" xml:space="preserve">
+  <data name="#98FB98" xml:space="preserve">
     <value>PaleGreen</value>
     <value>PaleGreen</value>
   </data>
   </data>
-    <data name="#AFEEEE" xml:space="preserve">
+  <data name="#AFEEEE" xml:space="preserve">
     <value>PaleTurquoise</value>
     <value>PaleTurquoise</value>
   </data>
   </data>
-    <data name="#DB7093" xml:space="preserve">
+  <data name="#DB7093" xml:space="preserve">
     <value>PaleVioletRed</value>
     <value>PaleVioletRed</value>
   </data>
   </data>
-    <data name="#FFEFD5" xml:space="preserve">
+  <data name="#FFEFD5" xml:space="preserve">
     <value>PapayaWhip</value>
     <value>PapayaWhip</value>
   </data>
   </data>
-    <data name="#FFDAB9" xml:space="preserve">
+  <data name="#FFDAB9" xml:space="preserve">
     <value>PeachPuff</value>
     <value>PeachPuff</value>
   </data>
   </data>
-    <data name="#CD853F" xml:space="preserve">
+  <data name="#CD853F" xml:space="preserve">
     <value>Peru</value>
     <value>Peru</value>
   </data>
   </data>
-    <data name="#FFC0CB" xml:space="preserve">
+  <data name="#FFC0CB" xml:space="preserve">
     <value>Pink</value>
     <value>Pink</value>
   </data>
   </data>
-    <data name="#DDA0DD" xml:space="preserve">
+  <data name="#DDA0DD" xml:space="preserve">
     <value>Plum</value>
     <value>Plum</value>
   </data>
   </data>
-    <data name="#B0E0E6" xml:space="preserve">
+  <data name="#B0E0E6" xml:space="preserve">
     <value>PowderBlue</value>
     <value>PowderBlue</value>
   </data>
   </data>
-    <data name="#800080" xml:space="preserve">
+  <data name="#800080" xml:space="preserve">
     <value>Purple</value>
     <value>Purple</value>
   </data>
   </data>
-    <data name="#663399" xml:space="preserve">
+  <data name="#663399" xml:space="preserve">
     <value>RebeccaPurple</value>
     <value>RebeccaPurple</value>
   </data>
   </data>
-    <data name="#FF0000" xml:space="preserve">
+  <data name="#FF0000" xml:space="preserve">
     <value>Red</value>
     <value>Red</value>
   </data>
   </data>
-    <data name="#BC8F8F" xml:space="preserve">
+  <data name="#BC8F8F" xml:space="preserve">
     <value>RosyBrown</value>
     <value>RosyBrown</value>
   </data>
   </data>
-    <data name="#4169E1" xml:space="preserve">
+  <data name="#4169E1" xml:space="preserve">
     <value>RoyalBlue</value>
     <value>RoyalBlue</value>
   </data>
   </data>
-    <data name="#8B4513" xml:space="preserve">
+  <data name="#8B4513" xml:space="preserve">
     <value>SaddleBrown</value>
     <value>SaddleBrown</value>
   </data>
   </data>
-    <data name="#FA8072" xml:space="preserve">
+  <data name="#FA8072" xml:space="preserve">
     <value>Salmon</value>
     <value>Salmon</value>
   </data>
   </data>
-    <data name="#F4A460" xml:space="preserve">
+  <data name="#F4A460" xml:space="preserve">
     <value>SandyBrown</value>
     <value>SandyBrown</value>
   </data>
   </data>
-    <data name="#2E8B57" xml:space="preserve">
+  <data name="#2E8B57" xml:space="preserve">
     <value>SeaGreen</value>
     <value>SeaGreen</value>
   </data>
   </data>
-    <data name="#FFF5EE" xml:space="preserve">
+  <data name="#FFF5EE" xml:space="preserve">
     <value>SeaShell</value>
     <value>SeaShell</value>
   </data>
   </data>
-    <data name="#A0522D" xml:space="preserve">
+  <data name="#A0522D" xml:space="preserve">
     <value>Sienna</value>
     <value>Sienna</value>
   </data>
   </data>
-    <data name="#C0C0C0" xml:space="preserve">
+  <data name="#C0C0C0" xml:space="preserve">
     <value>Silver</value>
     <value>Silver</value>
   </data>
   </data>
-    <data name="#87CEEB" xml:space="preserve">
+  <data name="#87CEEB" xml:space="preserve">
     <value>SkyBlue</value>
     <value>SkyBlue</value>
   </data>
   </data>
-    <data name="#6A5ACD" xml:space="preserve">
+  <data name="#6A5ACD" xml:space="preserve">
     <value>SlateBlue</value>
     <value>SlateBlue</value>
   </data>
   </data>
-    <data name="#708090" xml:space="preserve">
+  <data name="#708090" xml:space="preserve">
     <value>SlateGray</value>
     <value>SlateGray</value>
   </data>
   </data>
-    <data name="#FFFAFA" xml:space="preserve">
+  <data name="#FFFAFA" xml:space="preserve">
     <value>Snow</value>
     <value>Snow</value>
   </data>
   </data>
-    <data name="#00FF7F" xml:space="preserve">
+  <data name="#00FF7F" xml:space="preserve">
     <value>SpringGreen</value>
     <value>SpringGreen</value>
   </data>
   </data>
-    <data name="#4682B4" xml:space="preserve">
+  <data name="#4682B4" xml:space="preserve">
     <value>SteelBlue</value>
     <value>SteelBlue</value>
   </data>
   </data>
-    <data name="#D2B48C" xml:space="preserve">
+  <data name="#D2B48C" xml:space="preserve">
     <value>Tan</value>
     <value>Tan</value>
   </data>
   </data>
-    <data name="#008080" xml:space="preserve">
+  <data name="#008080" xml:space="preserve">
     <value>Teal</value>
     <value>Teal</value>
   </data>
   </data>
-    <data name="#D8BFD8" xml:space="preserve">
+  <data name="#D8BFD8" xml:space="preserve">
     <value>Thistle</value>
     <value>Thistle</value>
   </data>
   </data>
-    <data name="#FF6347" xml:space="preserve">
+  <data name="#FF6347" xml:space="preserve">
     <value>Tomato</value>
     <value>Tomato</value>
   </data>
   </data>
-    <data name="#40E0D0" xml:space="preserve">
+  <data name="#40E0D0" xml:space="preserve">
     <value>Turquoise</value>
     <value>Turquoise</value>
   </data>
   </data>
-    <data name="#EE82EE" xml:space="preserve">
+  <data name="#EE82EE" xml:space="preserve">
     <value>Violet</value>
     <value>Violet</value>
   </data>
   </data>
-    <data name="#F5DEB3" xml:space="preserve">
+  <data name="#F5DEB3" xml:space="preserve">
     <value>Wheat</value>
     <value>Wheat</value>
   </data>
   </data>
-    <data name="#FFFFFF" xml:space="preserve">
+  <data name="#FFFFFF" xml:space="preserve">
     <value>White</value>
     <value>White</value>
   </data>
   </data>
-    <data name="#F5F5F5" xml:space="preserve">
+  <data name="#F5F5F5" xml:space="preserve">
     <value>WhiteSmoke</value>
     <value>WhiteSmoke</value>
   </data>
   </data>
-    <data name="#FFFF00" xml:space="preserve">
+  <data name="#FFFF00" xml:space="preserve">
     <value>Yellow</value>
     <value>Yellow</value>
   </data>
   </data>
-    <data name="#9ACD32" xml:space="preserve">
+  <data name="#9ACD32" xml:space="preserve">
     <value>YellowGreen</value>
     <value>YellowGreen</value>
   </data>
   </data>
+  <data name="#3B78FF" xml:space="preserve">
+    <value>BrightBlue</value>
+  </data>
+  <data name="#61D6D6" xml:space="preserve">
+    <value>BrightCyan</value>
+  </data>
+  <data name="#E74856" xml:space="preserve">
+    <value>BrightRed</value>
+  </data>
+  <data name="#16C60C" xml:space="preserve">
+    <value>BrightGreen</value>
+  </data>
+  <data name="#B4009E" xml:space="preserve">
+    <value>BrightMagenta</value>
+  </data>
+  <data name="#F9F1A5" xml:space="preserve">
+    <value>BrightYellow</value>
+  </data>
+  <data name="#767676" xml:space="preserve">
+    <value>DarkGray</value>
+  </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_lors</value>
+  </data>
 </root>
 </root>

+ 9 - 6
Terminal.Gui/Resources/Strings.zh-Hans.resx

@@ -153,9 +153,6 @@
   <data name="fdOpen" xml:space="preserve">
   <data name="fdOpen" xml:space="preserve">
     <value>打开</value>
     <value>打开</value>
   </data>
   </data>
-  <data name="wzNext" xml:space="preserve">
-    <value>下一步 (_N)...</value>
-  </data>
   <data name="fdSelectFolder" xml:space="preserve">
   <data name="fdSelectFolder" xml:space="preserve">
     <value>选择文件夹 (_S)</value>
     <value>选择文件夹 (_S)</value>
   </data>
   </data>
@@ -168,6 +165,9 @@
   <data name="wzFinish" xml:space="preserve">
   <data name="wzFinish" xml:space="preserve">
     <value>结束 (_N)</value>
     <value>结束 (_N)</value>
   </data>
   </data>
+  <data name="wzNext" xml:space="preserve">
+    <value>下一步 (_N)...</value>
+  </data>
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>已存在相同名称的目录</value>
     <value>已存在相同名称的目录</value>
   </data>
   </data>
@@ -240,9 +240,6 @@
   <data name="fdExisting" xml:space="preserve">
   <data name="fdExisting" xml:space="preserve">
     <value>已有</value>
     <value>已有</value>
   </data>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>确定 (_O)</value>
-  </data>
   <data name="btnOpen" xml:space="preserve">
   <data name="btnOpen" xml:space="preserve">
     <value>打开 (_O)</value>
     <value>打开 (_O)</value>
   </data>
   </data>
@@ -252,6 +249,9 @@
   <data name="btnSaveAs" xml:space="preserve">
   <data name="btnSaveAs" xml:space="preserve">
     <value>另存为 (_S)</value>
     <value>另存为 (_S)</value>
   </data>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>确定 (_O)</value>
+  </data>
   <data name="btnCancel" xml:space="preserve">
   <data name="btnCancel" xml:space="preserve">
     <value>取消 (_C)</value>
     <value>取消 (_C)</value>
   </data>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
   <data name="dpTitle" xml:space="preserve">
     <value>日期选择器</value>
     <value>日期选择器</value>
   </data>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>旗帜 (_L)</value>
+  </data>
 </root>
 </root>

+ 324 - 38
Terminal.Gui/Resources/config.json

@@ -10,8 +10,7 @@
   // note that not all values here will be recreated (e.g. the Light and Dark themes and any property initialized
   // note that not all values here will be recreated (e.g. the Light and Dark themes and any property initialized
   // null).
   // null).
   //
   //
-  // TODO: V2 - Reference via http
-  "$schema": "../../docfx/schemas/tui-config-schema.json",
+  "$schema": "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json",
 
 
   // Set this to true in a .config file to be loaded to cause JSON parsing errors
   // Set this to true in a .config file to be loaded to cause JSON parsing errors
   // to throw exceptions. 
   // to throw exceptions. 
@@ -22,6 +21,7 @@
   "Application.NextTabGroupKey": "F6",
   "Application.NextTabGroupKey": "F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.QuitKey": "Esc",
   "Application.QuitKey": "Esc",
+  "Application.ArrangeKey": "Ctrl+F5",
   "Key.Separator": "+",
   "Key.Separator": "+",
 
 
   "Theme": "Default",
   "Theme": "Default",
@@ -30,34 +30,35 @@
       "Default": {
       "Default": {
         "Dialog.DefaultButtonAlignment": "End",
         "Dialog.DefaultButtonAlignment": "End",
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
         "FrameView.DefaultBorderStyle": "Single",
         "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
-        "Dialog.DefaultBorderStyle": "Heavy",
         "MessageBox.DefaultButtonAlignment": "Center",
         "MessageBox.DefaultButtonAlignment": "Center",
         "MessageBox.DefaultBorderStyle": "Heavy",
         "MessageBox.DefaultBorderStyle": "Heavy",
-        "Button.DefaultShadow": "None",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
         "ColorSchemes": [
           {
           {
             "TopLevel": {
             "TopLevel": {
               "Normal": {
               "Normal": {
                 "Foreground": "BrightGreen",
                 "Foreground": "BrightGreen",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               },
               },
               "Focus": {
               "Focus": {
                 "Foreground": "White",
                 "Foreground": "White",
-                "Background": "Cyan"
+                "Background": "#696969" // DimGray
               },
               },
               "HotNormal": {
               "HotNormal": {
                 "Foreground": "Yellow",
                 "Foreground": "Yellow",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               },
               },
               "HotFocus": {
               "HotFocus": {
-                "Foreground": "Blue",
-                "Background": "Cyan"
+                "Foreground": "Yellow",
+                "Background": "#696969" // DimGray
               },
               },
               "Disabled": {
               "Disabled": {
                 "Foreground": "DarkGray",
                 "Foreground": "DarkGray",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               }
               }
             }
             }
           },
           },
@@ -68,8 +69,8 @@
                 "Background": "Blue"
                 "Background": "Blue"
               },
               },
               "Focus": {
               "Focus": {
-                "Foreground": "Black",
-                "Background": "Gray"
+                "Foreground": "DarkBlue",
+                "Background": "LightGray"
               },
               },
               "HotNormal": {
               "HotNormal": {
                 "Foreground": "BrightCyan",
                 "Foreground": "BrightCyan",
@@ -77,7 +78,7 @@
               },
               },
               "HotFocus": {
               "HotFocus": {
                 "Foreground": "BrightBlue",
                 "Foreground": "BrightBlue",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               },
               "Disabled": {
               "Disabled": {
                 "Foreground": "DarkGray",
                 "Foreground": "DarkGray",
@@ -89,19 +90,19 @@
             "Dialog": {
             "Dialog": {
               "Normal": {
               "Normal": {
                 "Foreground": "Black",
                 "Foreground": "Black",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               },
               "Focus": {
               "Focus": {
-                "Foreground": "White",
-                "Background": "DarkGray"
+                "Foreground": "DarkGray",
+                "Background": "LightGray"
               },
               },
               "HotNormal": {
               "HotNormal": {
                 "Foreground": "Blue",
                 "Foreground": "Blue",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               },
               "HotFocus": {
               "HotFocus": {
-                "Foreground": "BrightYellow",
-                "Background": "DarkGray"
+                "Foreground": "BrightBlue",
+                "Background": "LightGray"
               },
               },
               "Disabled": {
               "Disabled": {
                 "Foreground": "Gray",
                 "Foreground": "Gray",
@@ -113,19 +114,19 @@
             "Menu": {
             "Menu": {
               "Normal": {
               "Normal": {
                 "Foreground": "White",
                 "Foreground": "White",
-                "Background": "DarkGray"
+                "Background": "DarkBlue"
               },
               },
               "Focus": {
               "Focus": {
                 "Foreground": "White",
                 "Foreground": "White",
-                "Background": "Black"
+                "Background": "Blue"
               },
               },
               "HotNormal": {
               "HotNormal": {
-                "Foreground": "BrightYellow",
-                "Background": "DarkGray"
+                "Foreground": "Yellow",
+                "Background": "DarkBlue"
               },
               },
               "HotFocus": {
               "HotFocus": {
-                "Foreground": "BrightYellow",
-                "Background": "Black"
+                "Foreground": "Yellow",
+                "Background": "Blue"
               },
               },
               "Disabled": {
               "Disabled": {
                 "Foreground": "Gray",
                 "Foreground": "Gray",
@@ -137,18 +138,18 @@
             "Error": {
             "Error": {
               "Normal": {
               "Normal": {
                 "Foreground": "Red",
                 "Foreground": "Red",
-                "Background": "White"
+                "Background": "Pink"
               },
               },
               "Focus": {
               "Focus": {
-                "Foreground": "Black",
+                "Foreground": "White",
                 "Background": "BrightRed"
                 "Background": "BrightRed"
               },
               },
               "HotNormal": {
               "HotNormal": {
                 "Foreground": "Black",
                 "Foreground": "Black",
-                "Background": "White"
+                "Background": "Pink"
               },
               },
               "HotFocus": {
               "HotFocus": {
-                "Foreground": "White",
+                "Foreground": "Pink",
                 "Background": "BrightRed"
                 "Background": "BrightRed"
               },
               },
               "Disabled": {
               "Disabled": {
@@ -162,6 +163,15 @@
     },
     },
     {
     {
       "Dark": {
       "Dark": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
         "ColorSchemes": [
           {
           {
             "TopLevel": {
             "TopLevel": {
@@ -238,16 +248,16 @@
           {
           {
             "Menu": {
             "Menu": {
               "Normal": {
               "Normal": {
-                "Foreground": "White",
-                "Background": "DarkGray"
+                "Foreground": "LightGray",
+                "Background": "#505050" // DarkerGray
               },
               },
               "Focus": {
               "Focus": {
                 "Foreground": "White",
                 "Foreground": "White",
                 "Background": "Black"
                 "Background": "Black"
               },
               },
               "HotNormal": {
               "HotNormal": {
-                "Foreground": "Gray",
-                "Background": "DarkGray"
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
               },
               },
               "HotFocus": {
               "HotFocus": {
                 "Foreground": "White",
                 "Foreground": "White",
@@ -288,6 +298,15 @@
     },
     },
     {
     {
       "Light": {
       "Light": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
         "ColorSchemes": [
           {
           {
             "TopLevel": {
             "TopLevel": {
@@ -316,7 +335,7 @@
           {
           {
             "Base": {
             "Base": {
               "Normal": {
               "Normal": {
-                "Foreground": "DarkGray",
+                "Foreground": "#505050", // DarkerGray
                 "Background": "White"
                 "Background": "White"
               },
               },
               "Focus": {
               "Focus": {
@@ -365,19 +384,19 @@
             "Menu": {
             "Menu": {
               "Normal": {
               "Normal": {
                 "Foreground": "DarkGray",
                 "Foreground": "DarkGray",
-                "Background": "White"
+                "Background": "LightGray"
               },
               },
               "Focus": {
               "Focus": {
                 "Foreground": "DarkGray",
                 "Foreground": "DarkGray",
-                "Background": "Gray"
+                "Background": "White"
               },
               },
               "HotNormal": {
               "HotNormal": {
                 "Foreground": "BrightRed",
                 "Foreground": "BrightRed",
-                "Background": "White"
+                "Background": "LightGray"
               },
               },
               "HotFocus": {
               "HotFocus": {
                 "Foreground": "BrightRed",
                 "Foreground": "BrightRed",
-                "Background": "Gray"
+                "Background": "White"
               },
               },
               "Disabled": {
               "Disabled": {
                 "Foreground": "Gray",
                 "Foreground": "Gray",
@@ -411,6 +430,273 @@
           }
           }
         ]
         ]
       }
       }
+    },
+    {
+      "Black & White": {
+        "Dialog.DefaultShadow": "None",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "None",
+        "ColorSchemes": [
+          {
+            "TopLevel": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Base": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Dialog": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "White",
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Menu": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "White",
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Error": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "Gray Scale": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
+        "ColorSchemes": [
+          {
+            "TopLevel": {
+              "Normal": {
+                "Foreground": "#A9A9A9", // DarkGray
+                "Background": "#505050" // DarkerGray
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#696969" // DimGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "#505050" // DarkerGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "Disabled": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Base": {
+              "Normal": {
+                "Foreground": "#A9A9A9", // DarkGray
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Dialog": {
+              "Normal": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Menu": {
+              "Normal": {
+                "Foreground": "#D3D3D3", // LightGray
+                "Background": "#505050" // DarkerGray
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "#505050" // DarkerGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "Disabled": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "#505050" // DarkerGray
+              }
+            }
+          },
+          {
+            "Error": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "White"
+              }
+            }
+          }
+        ]
+      }
     }
     }
   ]
   ]
 }
 }

+ 35 - 2
Terminal.Gui/Terminal.Gui.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <!-- =================================================================== -->
   <!-- =================================================================== -->
   <!-- Version numbers -->
   <!-- Version numbers -->
   <!-- Automatically updated by gitversion (run `dotnet-gitversion /updateprojectfiles`)  -->
   <!-- Automatically updated by gitversion (run `dotnet-gitversion /updateprojectfiles`)  -->
@@ -60,7 +60,7 @@
     <!-- Enable Nuget Source Link for github -->
     <!-- Enable Nuget Source Link for github -->
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="[8,9)" PrivateAssets="all" />
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="[8,9)" PrivateAssets="all" />
     <PackageReference Include="System.IO.Abstractions" Version="[21.0.22,22)" />
     <PackageReference Include="System.IO.Abstractions" Version="[21.0.22,22)" />
-    <PackageReference Include="System.Text.Json" Version="[8.0.4,9)" />
+    <PackageReference Include="System.Text.Json" Version="[8.0.5,9)" />
     <PackageReference Include="Wcwidth" Version="[2,3)" />
     <PackageReference Include="Wcwidth" Version="[2,3)" />
   </ItemGroup>
   </ItemGroup>
   <!-- =================================================================== -->
   <!-- =================================================================== -->
@@ -141,4 +141,37 @@
     <EnableSourceLink>true</EnableSourceLink>
     <EnableSourceLink>true</EnableSourceLink>
     <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
     <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
   </PropertyGroup>
   </PropertyGroup>
+  <ProjectExtensions><VisualStudio><UserProperties resources_4config_1json__JsonSchema="../../docfx/schemas/tui-config-schema.json" /></VisualStudio></ProjectExtensions>
+
+  <Target Name="CopyNuGetPackagesToLocalPackagesFolder"
+          AfterTargets="Pack"
+          Condition="'$(Configuration)' == 'Release'">
+      <PropertyGroup>
+          <!-- Define the path for local_packages relative to the project directory -->
+          <LocalPackagesPath>$(MSBuildThisFileDirectory)..\local_packages\</LocalPackagesPath>
+          <!-- Output path without framework-specific folders -->
+          <PackageOutputPath>$(MSBuildThisFileDirectory)bin\$(Configuration)\</PackageOutputPath>
+      </PropertyGroup>
+
+      <!-- Ensure the local_packages folder exists -->
+      <Message Text="Checking if $(LocalPackagesPath) exists, creating if necessary." Importance="high" />
+      <MakeDir Directories="$(LocalPackagesPath)" />
+
+      <!-- Collect .nupkg and .snupkg files into an item group -->
+      <ItemGroup>
+          <NuGetPackages Include="$(PackageOutputPath)*.nupkg;$(PackageOutputPath)*.snupkg" />
+      </ItemGroup>
+
+      <!-- Check if any packages were found -->
+      <Message Text="Found packages: @(NuGetPackages)" Importance="high" />
+
+      <!-- Copy files only if found -->
+      <Copy SourceFiles="@(NuGetPackages)"
+            DestinationFolder="$(LocalPackagesPath)"
+            SkipUnchangedFiles="false"
+            Condition="@(NuGetPackages) != ''" />
+
+      <!-- Log success -->
+      <Message Text="Copy completed successfully." Importance="high" />
+  </Target>
 </Project>
 </Project>

+ 1 - 1
Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs

@@ -59,7 +59,7 @@ public class AppendAutocomplete : AutocompleteBase
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnMouseEvent (MouseEvent me, bool fromHost = false) { return false; }
+    public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false) { return false; }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override bool ProcessKey (Key a)
     public override bool ProcessKey (Key a)

+ 1 - 1
Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs

@@ -49,7 +49,7 @@ public abstract class AutocompleteBase : IAutocomplete
     public virtual AutocompleteContext Context { get; set; }
     public virtual AutocompleteContext Context { get; set; }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public abstract bool OnMouseEvent (MouseEvent me, bool fromHost = false);
+    public abstract bool OnMouseEvent (MouseEventArgs me, bool fromHost = false);
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public abstract bool ProcessKey (Key a);
     public abstract bool ProcessKey (Key a);

+ 2 - 2
Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui;
 public class AutocompleteContext
 public class AutocompleteContext
 {
 {
     /// <summary>Creates a new instance of the <see cref="AutocompleteContext"/> class</summary>
     /// <summary>Creates a new instance of the <see cref="AutocompleteContext"/> class</summary>
-    public AutocompleteContext (List<RuneCell> currentLine, int cursorPosition, bool canceled = false)
+    public AutocompleteContext (List<Cell> currentLine, int cursorPosition, bool canceled = false)
     {
     {
         CurrentLine = currentLine;
         CurrentLine = currentLine;
         CursorPosition = cursorPosition;
         CursorPosition = cursorPosition;
@@ -18,7 +18,7 @@ public class AutocompleteContext
     public bool Canceled { get; set; }
     public bool Canceled { get; set; }
 
 
     /// <summary>The text on the current line.</summary>
     /// <summary>The text on the current line.</summary>
-    public List<RuneCell> CurrentLine { get; set; }
+    public List<Cell> CurrentLine { get; set; }
 
 
     /// <summary>The position of the input cursor within the <see cref="CurrentLine"/>.</summary>
     /// <summary>The position of the input cursor within the <see cref="CurrentLine"/>.</summary>
     public int CursorPosition { get; set; }
     public int CursorPosition { get; set; }

+ 1 - 1
Terminal.Gui/Text/Autocomplete/IAutocomplete.cs

@@ -45,7 +45,7 @@ public interface IAutocomplete
     /// <param name="me">The mouse event.</param>
     /// <param name="me">The mouse event.</param>
     /// <param name="fromHost">If was called from the popup or from the host.</param>
     /// <param name="fromHost">If was called from the popup or from the host.</param>
     /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
     /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
-    bool OnMouseEvent (MouseEvent me, bool fromHost = false);
+    bool OnMouseEvent (MouseEventArgs me, bool fromHost = false);
 
 
     /// <summary>Gets or sets where the popup will be displayed.</summary>
     /// <summary>Gets or sets where the popup will be displayed.</summary>
     bool PopupInsideContainer { get; set; }
     bool PopupInsideContainer { get; set; }

+ 1 - 1
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs

@@ -25,6 +25,6 @@ public abstract partial class PopupAutocomplete
             _autoComplete.RenderOverlay (_autoComplete.LastPopupPos.Value);
             _autoComplete.RenderOverlay (_autoComplete.LastPopupPos.Value);
         }
         }
 
 
-        protected internal override bool OnMouseEvent (MouseEvent mouseEvent) { return _autoComplete.OnMouseEvent (mouseEvent); }
+        protected override bool OnMouseEvent (MouseEventArgs mouseEvent) { return _autoComplete.OnMouseEvent (mouseEvent); }
     }
     }
 }
 }

+ 2 - 2
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs

@@ -105,7 +105,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
     /// <param name="me">The mouse event.</param>
     /// <param name="me">The mouse event.</param>
     /// <param name="fromHost">If was called from the popup or from the host.</param>
     /// <param name="fromHost">If was called from the popup or from the host.</param>
     /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
     /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
-    public override bool OnMouseEvent (MouseEvent me, bool fromHost = false)
+    public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false)
     {
     {
         if (fromHost)
         if (fromHost)
         {
         {
@@ -488,7 +488,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
 
 
     /// <summary>Render the current selection in the Autocomplete context menu by the mouse reporting.</summary>
     /// <summary>Render the current selection in the Autocomplete context menu by the mouse reporting.</summary>
     /// <param name="me"></param>
     /// <param name="me"></param>
-    protected void RenderSelectedIdxByMouse (MouseEvent me)
+    protected void RenderSelectedIdxByMouse (MouseEventArgs me)
     {
     {
         if (SelectedIdx != me.Position.Y - ScrollOffset)
         if (SelectedIdx != me.Position.Y - ScrollOffset)
         {
         {

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

@@ -1,4 +1,5 @@
 #nullable enable
 #nullable enable
+using System.ComponentModel;
 using Terminal.Gui;
 using Terminal.Gui;
 using Attribute = Terminal.Gui.Attribute;
 using Attribute = Terminal.Gui.Attribute;
 
 
@@ -26,7 +27,9 @@ public class Adornment : View
     /// <param name="parent"></param>
     /// <param name="parent"></param>
     public Adornment (View parent)
     public Adornment (View parent)
     {
     {
-        CanFocus = true;
+        // By default Adornments can't get focus; has to be enabled specifically.
+        CanFocus = false;
+        TabStop = TabBehavior.NoStop;
         Parent = parent;
         Parent = parent;
     }
     }
 
 
@@ -220,43 +223,40 @@ public class Adornment : View
             return false;
             return false;
         }
         }
 
 
-        Rectangle frame = Frame;
-        frame.Offset (Parent.Frame.Location);
+        Rectangle outside = Frame;
+        outside.Offset (Parent.Frame.Location);
 
 
-        return Thickness.Contains (frame, location);
+        return Thickness.Contains (outside, location);
     }
     }
 
 
-    /// <inheritdoc/>
-    protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
-    {
-        // Invert Normal
-        if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-        {
-            var cs = new ColorScheme (ColorScheme)
-            {
-                Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-            };
-            ColorScheme = cs;
-        }
-
-        return base.OnMouseEnter (mouseEvent);
-    }
-
-    /// <inheritdoc/>   
-    protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
-    {
-        // Invert Normal
-        if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-        {
-            var cs = new ColorScheme (ColorScheme)
-            {
-                Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-            };
-            ColorScheme = cs;
-        }
-
-        return base.OnMouseLeave (mouseEvent);
-    }
+    ///// <inheritdoc/>
+    //protected override bool OnMouseEnter (CancelEventArgs mouseEvent)
+    //{
+    //    // Invert Normal
+    //    if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
+    //    {
+    //        var cs = new ColorScheme (ColorScheme)
+    //        {
+    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
+    //        };
+    //        ColorScheme = cs;
+    //    }
+
+    //    return false;
+    //}
 
 
+    ///// <inheritdoc/>   
+    //protected override void OnMouseLeave ()
+    //{
+    //    // Invert Normal
+    //    if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
+    //    {
+    //        var cs = new ColorScheme (ColorScheme)
+    //        {
+    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
+    //        };
+    //        ColorScheme = cs;
+    //    }
+    //}
     #endregion Mouse Support
     #endregion Mouse Support
 }
 }

+ 916 - 179
Terminal.Gui/View/Adornment/Border.cs

@@ -1,6 +1,9 @@
+#nullable enable
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>The Border for a <see cref="View"/>.</summary>
+/// <summary>The Border for a <see cref="View"/>. Accessed via <see cref="View.Border"/></summary>
 /// <remarks>
 /// <remarks>
 ///     <para>
 ///     <para>
 ///         Renders a border around the view with the <see cref="View.Title"/>. A border using <see cref="LineStyle"/>
 ///         Renders a border around the view with the <see cref="View.Title"/>. A border using <see cref="LineStyle"/>
@@ -52,8 +55,10 @@ public class Border : Adornment
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Border (View parent) : base (parent)
     public Border (View parent) : base (parent)
     {
     {
-        /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
         Parent = parent;
         Parent = parent;
+        CanFocus = false;
+        TabStop = TabBehavior.TabGroup;
+
         Application.GrabbingMouse += Application_GrabbingMouse;
         Application.GrabbingMouse += Application_GrabbingMouse;
         Application.UnGrabbingMouse += Application_UnGrabbingMouse;
         Application.UnGrabbingMouse += Application_UnGrabbingMouse;
 
 
@@ -73,14 +78,6 @@ public class Border : Adornment
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void BeginInit ()
     public override void BeginInit ()
     {
     {
-#if HOVER
-        // TOOD: Hack - make Arrangement overridable
-        if ((Parent?.Arrangement & ViewArrangement.Movable) != 0)
-        {
-            HighlightStyle |= HighlightStyle.Hover;
-        }
-#endif
-
         base.BeginInit ();
         base.BeginInit ();
 
 
 #if SUBVIEW_BASED_BORDER
 #if SUBVIEW_BASED_BORDER
@@ -131,7 +128,7 @@ public class Border : Adornment
     ///     The color scheme for the Border. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>
     ///     The color scheme for the Border. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>
     ///     scheme. color scheme.
     ///     scheme. color scheme.
     /// </summary>
     /// </summary>
-    public override ColorScheme ColorScheme
+    public override ColorScheme? ColorScheme
     {
     {
         get
         get
         {
         {
@@ -152,6 +149,7 @@ public class Border : Adornment
     internal Rectangle GetBorderRectangle ()
     internal Rectangle GetBorderRectangle ()
     {
     {
         Rectangle screenRect = ViewportToScreen (Viewport);
         Rectangle screenRect = ViewportToScreen (Viewport);
+
         return new (
         return new (
                     screenRect.X + Math.Max (0, Thickness.Left - 1),
                     screenRect.X + Math.Max (0, Thickness.Left - 1),
                     screenRect.Y + Math.Max (0, Thickness.Top - 1),
                     screenRect.Y + Math.Max (0, Thickness.Top - 1),
@@ -193,7 +191,7 @@ public class Border : Adornment
             // TODO: Make Border.LineStyle inherit from the SuperView hierarchy
             // TODO: Make Border.LineStyle inherit from the SuperView hierarchy
             // TODO: Right now, Window and FrameView use CM to set BorderStyle, which negates
             // TODO: Right now, Window and FrameView use CM to set BorderStyle, which negates
             // TODO: all this.
             // TODO: all this.
-            return Parent.SuperView?.BorderStyle ?? LineStyle.None;
+            return Parent!.SuperView?.BorderStyle ?? LineStyle.None;
         }
         }
         set => _lineStyle = value;
         set => _lineStyle = value;
     }
     }
@@ -223,9 +221,9 @@ public class Border : Adornment
 
 
     private Color? _savedForeColor;
     private Color? _savedForeColor;
 
 
-    private void Border_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
+    private void Border_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
     {
     {
-        if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
+        if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
         {
         {
             e.Cancel = true;
             e.Cancel = true;
 
 
@@ -236,31 +234,22 @@ public class Border : Adornment
         {
         {
             if (!_savedForeColor.HasValue)
             if (!_savedForeColor.HasValue)
             {
             {
-                _savedForeColor = ColorScheme.Normal.Foreground;
+                _savedForeColor = ColorScheme!.Normal.Foreground;
             }
             }
 
 
             var cs = new ColorScheme (ColorScheme)
             var cs = new ColorScheme (ColorScheme)
             {
             {
-                Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
+                Normal = new (ColorScheme!.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
             };
             };
             ColorScheme = cs;
             ColorScheme = cs;
         }
         }
-#if HOVER
-        else if (e.HighlightStyle.HasFlag (HighlightStyle.Hover))
-        {
-            if (!_savedHighlightLineStyle.HasValue)
-            {
-                _savedHighlightLineStyle = Parent?.BorderStyle ?? LineStyle;
-            }
-            LineStyle = LineStyle.Double;
-        }
-#endif
+
 
 
         if (e.NewValue == HighlightStyle.None && _savedForeColor.HasValue)
         if (e.NewValue == HighlightStyle.None && _savedForeColor.HasValue)
         {
         {
             var cs = new ColorScheme (ColorScheme)
             var cs = new ColorScheme (ColorScheme)
             {
             {
-                Normal = new (_savedForeColor.Value, ColorScheme.Normal.Background)
+                Normal = new (_savedForeColor.Value, ColorScheme!.Normal.Background)
             };
             };
             ColorScheme = cs;
             ColorScheme = cs;
         }
         }
@@ -273,53 +262,182 @@ public class Border : Adornment
     private Point _startGrabPoint;
     private Point _startGrabPoint;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
+    protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
     {
     {
-        if (base.OnMouseEvent (mouseEvent))
-        {
-            return true;
-        }
-
-        // BUGBUG: Shouldn't non-focusable views be draggable??
-        //if (!Parent.CanFocus)
-        //{
-        //    return false;
-        //}
-
-        if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
-        {
-            return false;
-        }
-
         // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
         // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
-        if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
+        if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
+                                    // HACK: Prevents Window from being draggable if it's Top
+                                    //&& Parent is Toplevel { Modal: true }
+                                    )
         {
         {
-            Parent.SetFocus ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
+            Parent!.SetFocus ();
+
+            if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
+               )
+            {
+                return false;
+            }
 
 
             // Only start grabbing if the user clicks in the Thickness area
             // Only start grabbing if the user clicks in the Thickness area
             // Adornment.Contains takes Parent SuperView=relative coords.
             // Adornment.Contains takes Parent SuperView=relative coords.
             if (Contains (new (mouseEvent.Position.X + Parent.Frame.X + Frame.X, mouseEvent.Position.Y + Parent.Frame.Y + Frame.Y)))
             if (Contains (new (mouseEvent.Position.X + Parent.Frame.X + Frame.X, mouseEvent.Position.Y + Parent.Frame.Y + Frame.Y)))
             {
             {
+                if (_arranging != ViewArrangement.Fixed)
+                {
+                    EndArrangeMode ();
+                }
+
                 // Set the start grab point to the Frame coords
                 // Set the start grab point to the Frame coords
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _dragPosition = mouseEvent.Position;
                 _dragPosition = mouseEvent.Position;
                 Application.GrabMouse (this);
                 Application.GrabMouse (this);
 
 
-                SetHighlight (HighlightStyle);
+                SetPressedHighlight (HighlightStyle);
+
+                // Arrange Mode -
+                // TODO: This code can be refactored to be more readable and maintainable.
+
+                // If not resizable, but movable: Drag anywhere is move
+                // If resizable and movable: Drag on top is move, other 3 sides are size
+                // If not movable, but resizable: Drag on any side sizes.
+
+                // Get rectangle representing Thickness.Top
+                // If mouse is in that rectangle, set _arranging to ViewArrangement.Movable
+                Rectangle sideRect;
+
+                // If mouse is in any other rectangle, set _arranging to ViewArrangement.<side>
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Y + Thickness.Top, Thickness.Left, Frame.Height - Thickness.Top - Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (
+                                    Frame.X + Frame.Width - Thickness.Right,
+                                    Frame.Y + Thickness.Top,
+                                    Thickness.Right,
+                                    Frame.Height - Thickness.Top - Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && !Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+                {
+                    sideRect = new (Frame.X + Thickness.Left, Frame.Y, Frame.Width - Thickness.Left - Thickness.Right, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable))
+                {
+                    sideRect = new (
+                                    Frame.X + Thickness.Left,
+                                    Frame.Y + Frame.Height - Thickness.Bottom,
+                                    Frame.Width - Thickness.Left - Thickness.Right,
+                                    Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Height - Thickness.Top, Thickness.Left, Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable | ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (Frame.X + Frame.Width - Thickness.Right, Frame.Height - Thickness.Top, Thickness.Right, Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable | ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (Frame.X + Frame.Width - Thickness.Right, Frame.Y, Thickness.Right, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable | ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Y, Thickness.Left, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable | ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+                {
+                    //sideRect = new (Frame.X + Thickness.Left, Frame.Y, Frame.Width - Thickness.Left - Thickness.Right, Thickness.Top);
+
+                    //if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.Movable);
+
+                        return true;
+                    }
+                }
             }
             }
 
 
             return true;
             return true;
         }
         }
 
 
-        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
+        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.MouseGrabView == this)
         {
         {
-            if (Application.MouseGrabView == this && _dragPosition.HasValue)
+            if (_dragPosition.HasValue)
             {
             {
-                if (Parent.SuperView is null)
+                if (Parent!.SuperView is null)
                 {
                 {
                     // Redraw the entire app window.
                     // Redraw the entire app window.
-                    Application.Top.SetNeedsDisplay ();
+                    Application.Top!.SetNeedsDisplay ();
                 }
                 }
                 else
                 else
                 {
                 {
@@ -331,17 +449,123 @@ public class Border : Adornment
                 Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
                 Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
                                   ?? mouseEvent.ScreenPosition;
                                   ?? mouseEvent.ScreenPosition;
 
 
-                GetLocationEnsuringFullVisibility (
-                                                   Parent,
-                                                   parentLoc.X - _startGrabPoint.X,
-                                                   parentLoc.Y - _startGrabPoint.Y,
-                                                   out int nx,
-                                                   out int ny,
-                                                   out _
-                                                  );
+                int minHeight = Thickness.Vertical + Parent!.Margin.Thickness.Bottom;
+                int minWidth = Thickness.Horizontal + Parent!.Margin.Thickness.Right;
+
+                // TODO: This code can be refactored to be more readable and maintainable.
+                switch (_arranging)
+                {
+                    case ViewArrangement.Movable:
+
+                        GetLocationEnsuringFullVisibility (
+                                                           Parent,
+                                                           parentLoc.X - _startGrabPoint.X,
+                                                           parentLoc.Y - _startGrabPoint.Y,
+                                                           out int nx,
+                                                           out int ny
+                                                          //,
+                                                          // out _
+                                                          );
+
+                        Parent.X = parentLoc.X - _startGrabPoint.X;
+                        Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+
+                        break;
+
+                    case ViewArrangement.TopResizable:
+                        // Get how much the mouse has moved since the start of the drag
+                        // and adjust the height of the parent by that amount
+                        int deltaY = parentLoc.Y - Parent.Frame.Y;
+                        int newHeight = Math.Max (minHeight, Parent.Frame.Height - deltaY);
+
+                        if (newHeight != Parent.Frame.Height)
+                        {
+                            Parent.Height = newHeight;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        break;
+
+                    case ViewArrangement.BottomResizable:
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
 
 
-                Parent.X = nx;
-                Parent.Y = ny;
+                    case ViewArrangement.LeftResizable:
+                        // Get how much the mouse has moved since the start of the drag
+                        // and adjust the height of the parent by that amount
+                        int deltaX = parentLoc.X - Parent.Frame.X;
+                        int newWidth = Math.Max (minWidth, Parent.Frame.Width - deltaX);
+
+                        if (newWidth != Parent.Frame.Width)
+                        {
+                            Parent.Width = newWidth;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
+
+                        break;
+
+                    case ViewArrangement.RightResizable:
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+
+                        break;
+
+                    case ViewArrangement.BottomResizable | ViewArrangement.RightResizable:
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
+
+                    case ViewArrangement.BottomResizable | ViewArrangement.LeftResizable:
+                        int dX = parentLoc.X - Parent.Frame.X;
+                        int newW = Math.Max (minWidth, Parent.Frame.Width - dX);
+
+                        if (newW != Parent.Frame.Width)
+                        {
+                            Parent.Width = newW;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
+
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
+
+                    case ViewArrangement.TopResizable | ViewArrangement.RightResizable:
+                        int dY = parentLoc.Y - Parent.Frame.Y;
+                        int newH = Math.Max (minHeight, Parent.Frame.Height - dY);
+
+                        if (newH != Parent.Frame.Height)
+                        {
+                            Parent.Height = newH;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+
+                        break;
+
+                    case ViewArrangement.TopResizable | ViewArrangement.LeftResizable:
+                        int dY2 = parentLoc.Y - Parent.Frame.Y;
+                        int newH2 = Math.Max (minHeight, Parent.Frame.Height - dY2);
+
+                        if (newH2 != Parent.Frame.Height)
+                        {
+                            Parent.Height = newH2;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        int dX2 = parentLoc.X - Parent.Frame.X;
+                        int newW2 = Math.Max (minWidth, Parent.Frame.Width - dX2);
+
+                        if (newW2 != Parent.Frame.Width)
+                        {
+                            Parent.Width = newW2;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
+
+                        break;
+                }
+                Application.Refresh ();
 
 
                 return true;
                 return true;
             }
             }
@@ -351,7 +575,9 @@ public class Border : Adornment
         {
         {
             _dragPosition = null;
             _dragPosition = null;
             Application.UngrabMouse ();
             Application.UngrabMouse ();
-            SetHighlight (HighlightStyle.None);
+            SetPressedHighlight (HighlightStyle.None);
+
+            EndArrangeMode ();
 
 
             return true;
             return true;
         }
         }
@@ -359,17 +585,7 @@ public class Border : Adornment
         return false;
         return false;
     }
     }
 
 
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        Application.GrabbingMouse -= Application_GrabbingMouse;
-        Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
-
-        _dragPosition = null;
-        base.Dispose (disposing);
-    }
-
-    private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
+    private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
     {
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         {
         {
@@ -377,7 +593,7 @@ public class Border : Adornment
         }
         }
     }
     }
 
 
-    private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
+    private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
     {
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         {
         {
@@ -417,7 +633,7 @@ public class Border : Adornment
         int maxTitleWidth = Math.Max (
         int maxTitleWidth = Math.Max (
                                       0,
                                       0,
                                       Math.Min (
                                       Math.Min (
-                                                Parent.TitleTextFormatter.FormatAndGetSize ().Width,
+                                                Parent!.TitleTextFormatter.FormatAndGetSize ().Width,
                                                 Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
                                                 Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
                                                )
                                                )
                                      );
                                      );
@@ -427,6 +643,8 @@ public class Border : Adornment
         int sideLineLength = borderBounds.Height;
         int sideLineLength = borderBounds.Height;
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
 
 
+        LineStyle lineStyle = LineStyle;
+
         if (Settings.FastHasFlags (BorderSettings.Title))
         if (Settings.FastHasFlags (BorderSettings.Title))
         {
         {
             if (Thickness.Top == 2)
             if (Thickness.Top == 2)
@@ -477,7 +695,7 @@ public class Border : Adornment
 
 
         if (canDrawBorder && LineStyle != LineStyle.None)
         if (canDrawBorder && LineStyle != LineStyle.None)
         {
         {
-            LineCanvas lc = Parent?.LineCanvas;
+            LineCanvas? lc = Parent?.LineCanvas;
 
 
             bool drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height >= 1;
             bool drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height >= 1;
             bool drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
             bool drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
@@ -492,7 +710,7 @@ public class Border : Adornment
             }
             }
             else
             else
             {
             {
-                Driver.SetAttribute (Parent.GetNormalColor ());
+                Driver.SetAttribute (Parent!.GetNormalColor ());
             }
             }
 
 
             if (drawTop)
             if (drawTop)
@@ -502,13 +720,13 @@ public class Border : Adornment
                 if (borderBounds.Width < 4 || !Settings.FastHasFlags (BorderSettings.Title) || string.IsNullOrEmpty (Parent?.Title))
                 if (borderBounds.Width < 4 || !Settings.FastHasFlags (BorderSettings.Title) || string.IsNullOrEmpty (Parent?.Title))
                 {
                 {
                     // ╔╡╞╗ should be ╔══╗
                     // ╔╡╞╗ should be ╔══╗
-                    lc.AddLine (
-                                new (borderBounds.Location.X, titleY),
-                                borderBounds.Width,
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.Location.X, titleY),
+                                 borderBounds.Width,
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
                 }
                 }
                 else
                 else
                 {
                 {
@@ -517,13 +735,13 @@ public class Border : Adornment
                     //│
                     //│
                     if (Thickness.Top == 2)
                     if (Thickness.Top == 2)
                     {
                     {
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
                     }
                     }
 
 
                     // ┌────┐
                     // ┌────┐
@@ -531,71 +749,71 @@ public class Border : Adornment
                     //│
                     //│
                     if (borderBounds.Width >= 4 && Thickness.Top > 2)
                     if (borderBounds.Width >= 4 && Thickness.Top > 2)
                     {
                     {
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
-
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY + 2),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
+
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY + 2),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
                     }
                     }
 
 
                     // ╔╡Title╞═════╗
                     // ╔╡Title╞═════╗
                     // Add a short horiz line for ╔╡
                     // Add a short horiz line for ╔╡
-                    lc.AddLine (
-                                new (borderBounds.Location.X, titleY),
-                                2,
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.Location.X, titleY),
+                                 2,
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
 
 
                     // Add a vert line for ╔╡
                     // Add a vert line for ╔╡
-                    lc.AddLine (
-                                new (borderBounds.X + 1, topTitleLineY),
-                                titleBarsLength,
-                                Orientation.Vertical,
-                                LineStyle.Single,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.X + 1, topTitleLineY),
+                                 titleBarsLength,
+                                 Orientation.Vertical,
+                                 LineStyle.Single,
+                                 Driver.GetAttribute ()
+                                );
 
 
                     // Add a vert line for ╞
                     // Add a vert line for ╞
-                    lc.AddLine (
-                                new (
-                                     borderBounds.X
-                                     + 1
-                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                     - 1,
-                                     topTitleLineY
-                                    ),
-                                titleBarsLength,
-                                Orientation.Vertical,
-                                LineStyle.Single,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (
+                                      borderBounds.X
+                                      + 1
+                                      + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                      - 1,
+                                      topTitleLineY
+                                     ),
+                                 titleBarsLength,
+                                 Orientation.Vertical,
+                                 LineStyle.Single,
+                                 Driver.GetAttribute ()
+                                );
 
 
                     // Add the right hand line for ╞═════╗
                     // Add the right hand line for ╞═════╗
-                    lc.AddLine (
-                                new (
-                                     borderBounds.X
-                                     + 1
-                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                     - 1,
-                                     titleY
-                                    ),
-                                borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (
+                                      borderBounds.X
+                                      + 1
+                                      + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                      - 1,
+                                      titleY
+                                     ),
+                                 borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
                 }
                 }
             }
             }
 
 
@@ -603,36 +821,36 @@ public class Border : Adornment
 
 
             if (drawLeft)
             if (drawLeft)
             {
             {
-                lc.AddLine (
-                            new (borderBounds.Location.X, titleY),
-                            sideLineLength,
-                            Orientation.Vertical,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.Location.X, titleY),
+                             sideLineLength,
+                             Orientation.Vertical,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
             }
 #endif
 #endif
 
 
             if (drawBottom)
             if (drawBottom)
             {
             {
-                lc.AddLine (
-                            new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
-                            borderBounds.Width,
-                            Orientation.Horizontal,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
+                             borderBounds.Width,
+                             Orientation.Horizontal,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
             }
 
 
             if (drawRight)
             if (drawRight)
             {
             {
-                lc.AddLine (
-                            new (borderBounds.X + borderBounds.Width - 1, titleY),
-                            sideLineLength,
-                            Orientation.Vertical,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.X + borderBounds.Width - 1, titleY),
+                             sideLineLength,
+                             Orientation.Vertical,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
             }
 
 
             Driver.SetAttribute (prevAttr);
             Driver.SetAttribute (prevAttr);
@@ -651,10 +869,10 @@ public class Border : Adornment
                 // Redraw title 
                 // Redraw title 
                 if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title))
                 if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title))
                 {
                 {
-                    Parent.TitleTextFormatter.Draw (
-                                                    new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
-                                                    Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
-                                                    Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
+                    Parent!.TitleTextFormatter.Draw (
+                                                     new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
+                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
+                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
                 }
                 }
 
 
                 //Left
                 //Left
@@ -681,11 +899,11 @@ public class Border : Adornment
             // TODO: This should not be done on each draw?
             // TODO: This should not be done on each draw?
             if (Settings.FastHasFlags (BorderSettings.Gradient))
             if (Settings.FastHasFlags (BorderSettings.Gradient))
             {
             {
-                SetupGradientLineCanvas (lc, screenBounds);
+                SetupGradientLineCanvas (lc!, screenBounds);
             }
             }
             else
             else
             {
             {
-                lc.Fill = null;
+                lc!.Fill = null;
             }
             }
         }
         }
     }
     }
@@ -705,17 +923,536 @@ public class Border : Adornment
     private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
     private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
     {
     {
         // Define the colors of the gradient stops with more appealing colors
         // Define the colors of the gradient stops with more appealing colors
-        stops = new()
-        {
+        stops =
+        [
             new (0, 128, 255), // Bright Blue
             new (0, 128, 255), // Bright Blue
             new (0, 255, 128), // Bright Green
             new (0, 255, 128), // Bright Green
             new (255, 255), // Bright Yellow
             new (255, 255), // Bright Yellow
             new (255, 128), // Bright Orange
             new (255, 128), // Bright Orange
-            new (255, 0, 128) // Bright Pink
-        };
+            new (255, 0, 128)
+        ];
 
 
         // Define the number of steps between each color for smoother transitions
         // Define the number of steps between each color for smoother transitions
         // If we pass only a single value then it will assume equal steps between all pairs
         // If we pass only a single value then it will assume equal steps between all pairs
-        steps = new() { 15 };
+        steps = [15];
+    }
+
+    private ViewArrangement _arranging;
+
+    private Button? _moveButton; // always top-left
+    private Button? _allSizeButton;
+    private Button? _leftSizeButton;
+    private Button? _rightSizeButton;
+    private Button? _topSizeButton;
+    private Button? _bottomSizeButton;
+
+    /// <summary>
+    ///     Starts "Arrange Mode" where <see cref="Adornment.Parent"/> can be moved and/or resized using the mouse
+    ///     or keyboard. If <paramref name="arrangement"/> is <see cref="ViewArrangement.Fixed"/> keyboard mode is enabled.
+    /// </summary>
+    /// <remarks>
+    ///     Arrange Mode is exited by the user pressing <see cref="Application.ArrangeKey"/>, <see cref="Key.Esc"/>, or by
+    ///     clicking
+    ///     the mouse out of the <see cref="Adornment.Parent"/>'s Frame.
+    /// </remarks>
+    /// <returns></returns>
+    public bool? EnterArrangeMode (ViewArrangement arrangement)
+    {
+        Debug.Assert (_arranging == ViewArrangement.Fixed);
+
+        if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
+           )
+        {
+            return false;
+        }
+
+        // Add Commands and Keybindigs - Note it's ok these get added each time. KeyBindings are cleared in EndArrange()
+        AddArrangeModeKeyBindings ();
+
+        Application.MouseEvent += ApplicationOnMouseEvent;
+
+        // TODO: This code can be refactored to be more readable and maintainable.
+
+        // Create buttons for resizing and moving
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+        {
+            Debug.Assert (_moveButton is null);
+
+            _moveButton = new ()
+            {
+                Id = "moveButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.Move}",
+                Visible = false,
+                Data = ViewArrangement.Movable
+            };
+            Add (_moveButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+        {
+            Debug.Assert (_allSizeButton is null);
+
+            _allSizeButton = new ()
+            {
+                Id = "allSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeBottomRight}",
+                X = Pos.AnchorEnd (),
+                Y = Pos.AnchorEnd (),
+                Visible = false,
+                Data = ViewArrangement.Resizable
+            };
+            Add (_allSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable))
+        {
+            Debug.Assert (_topSizeButton is null);
+
+            _topSizeButton = new ()
+            {
+                Id = "topSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeVertical}",
+                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
+                Y = 0,
+                Visible = false,
+                Data = ViewArrangement.TopResizable
+            };
+            Add (_topSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+        {
+            Debug.Assert (_rightSizeButton is null);
+
+            _rightSizeButton = new ()
+            {
+                Id = "rightSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeHorizontal}",
+                X = Pos.AnchorEnd (),
+                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Visible = false,
+                Data = ViewArrangement.RightResizable
+            };
+            Add (_rightSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+        {
+            Debug.Assert (_leftSizeButton is null);
+
+            _leftSizeButton = new ()
+            {
+                Id = "leftSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeHorizontal}",
+                X = 0,
+                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Visible = false,
+                Data = ViewArrangement.LeftResizable
+            };
+            Add (_leftSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable))
+        {
+            Debug.Assert (_bottomSizeButton is null);
+
+            _bottomSizeButton = new ()
+            {
+                Id = "bottomSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeVertical}",
+                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal / 2,
+                Y = Pos.AnchorEnd (),
+                Visible = false,
+                Data = ViewArrangement.BottomResizable
+            };
+            Add (_bottomSizeButton);
+        }
+
+        if (arrangement == ViewArrangement.Fixed)
+        {
+            // Keyboard mode
+            if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+            {
+                _moveButton!.Visible = true;
+            }
+
+            if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+            {
+                _allSizeButton!.Visible = true;
+            }
+
+            _arranging = ViewArrangement.Movable;
+            CanFocus = true;
+            SetFocus ();
+        }
+        else
+        {
+            // Mouse mode
+            _arranging = arrangement;
+
+            switch (_arranging)
+            {
+                case ViewArrangement.Movable:
+                    _moveButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable | ViewArrangement.BottomResizable:
+                case ViewArrangement.Resizable:
+                    _rightSizeButton!.Visible = true;
+                    _bottomSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton!.X = Pos.AnchorEnd ();
+                        _allSizeButton!.Y = Pos.AnchorEnd ();
+                        _allSizeButton!.Visible = true;
+                    }
+
+                    break;
+
+                case ViewArrangement.LeftResizable:
+                    _leftSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable:
+                    _rightSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.TopResizable:
+                    _topSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.BottomResizable:
+                    _bottomSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.LeftResizable | ViewArrangement.BottomResizable:
+                    _rightSizeButton!.Visible = true;
+                    _bottomSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton.X = 0;
+                        _allSizeButton.Y = Pos.AnchorEnd ();
+                        _allSizeButton.Visible = true;
+                    }
+
+                    break;
+
+                case ViewArrangement.LeftResizable | ViewArrangement.TopResizable:
+                    _leftSizeButton!.Visible = true;
+                    _topSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable | ViewArrangement.TopResizable:
+                    _rightSizeButton!.Visible = true;
+                    _topSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton.X = Pos.AnchorEnd ();
+                        _allSizeButton.Y = 0;
+                        _allSizeButton.Visible = true;
+                    }
+
+                    break;
+            }
+        }
+
+        if (_arranging != ViewArrangement.Fixed)
+        {
+            if (arrangement == ViewArrangement.Fixed)
+            {
+                // Keyboard mode - enable nav
+                // TODO: Keyboard mode only supports sizing from bottom/right.
+                _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+            }
+
+            return true;
+        }
+
+        // Hack for now
+        EndArrangeMode ();
+
+        return false;
+    }
+
+    private void AddArrangeModeKeyBindings ()
+    {
+        AddCommand (Command.Quit, EndArrangeMode);
+
+        AddCommand (
+                    Command.Up,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Height > 0)
+                            {
+                                Parent!.Height = Parent.Height! - 1;
+                            }
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Down,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Height = Parent.Height! + 1;
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Left,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Width > 0)
+                            {
+                                Parent!.Width = Parent.Width! - 1;
+                            }
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Right,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Width = Parent.Width! + 1;
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Tab,
+                    () =>
+                    {
+                        // BUGBUG: If an arrangable view has only arrangable subviews, it's not possible to activate
+                        // BUGBUG: ArrangeMode with keyboard for the superview.
+                        // BUGBUG: AdvanceFocus should be wise to this and when in ArrangeMode, should move across
+                        // BUGBUG: the view hierachy.
+
+                        AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+                        _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+
+                        return true; // Always eat
+                    });
+
+        AddCommand (
+                    Command.BackTab,
+                    () =>
+                    {
+                        AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
+                        _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+
+                        return true; // Always eat
+                    });
+
+        KeyBindings.Add (Key.Esc, KeyBindingScope.HotKey, Command.Quit);
+        KeyBindings.Add (Application.ArrangeKey, KeyBindingScope.HotKey, Command.Quit);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.HotKey, Command.Up);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.HotKey, Command.Down);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.HotKey, Command.Left);
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.HotKey, Command.Right);
+
+        KeyBindings.Add (Key.Tab, KeyBindingScope.HotKey, Command.Tab);
+        KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.HotKey, Command.BackTab);
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked)
+        {
+            return;
+        }
+
+        // If mouse click is outside of Border.Thickness then exit Arrange Mode
+        // e.Position is screen relative
+        Point framePos = ScreenToFrame (e.ScreenPosition);
+
+        if (!Thickness.Contains (Frame, framePos))
+        {
+            EndArrangeMode ();
+        }
+    }
+
+    private bool? EndArrangeMode ()
+    {
+        // Debug.Assert (_arranging != ViewArrangement.Fixed);
+        _arranging = ViewArrangement.Fixed;
+
+        Application.MouseEvent -= ApplicationOnMouseEvent;
+
+        if (Application.MouseGrabView == this && _dragPosition.HasValue)
+        {
+            Application.UngrabMouse ();
+        }
+
+        if (_moveButton is { })
+        {
+            Remove (_moveButton);
+            _moveButton.Dispose ();
+            _moveButton = null;
+        }
+
+        if (_allSizeButton is { })
+        {
+            Remove (_allSizeButton);
+            _allSizeButton.Dispose ();
+            _allSizeButton = null;
+        }
+
+        if (_leftSizeButton is { })
+        {
+            Remove (_leftSizeButton);
+            _leftSizeButton.Dispose ();
+            _leftSizeButton = null;
+        }
+
+        if (_rightSizeButton is { })
+        {
+            Remove (_rightSizeButton);
+            _rightSizeButton.Dispose ();
+            _rightSizeButton = null;
+        }
+
+        if (_topSizeButton is { })
+        {
+            Remove (_topSizeButton);
+            _topSizeButton.Dispose ();
+            _topSizeButton = null;
+        }
+
+        if (_bottomSizeButton is { })
+        {
+            Remove (_bottomSizeButton);
+            _bottomSizeButton.Dispose ();
+            _bottomSizeButton = null;
+        }
+
+        KeyBindings.Clear ();
+
+        if (CanFocus)
+        {
+            CanFocus = false;
+        }
+
+        return true;
+    }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        Application.GrabbingMouse -= Application_GrabbingMouse;
+        Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
+
+        _dragPosition = null;
+        base.Dispose (disposing);
     }
     }
 }
 }

+ 15 - 6
Terminal.Gui/View/Adornment/Margin.cs

@@ -2,7 +2,7 @@
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>The Margin for a <see cref="View"/>.</summary>
+/// <summary>The Margin for a <see cref="View"/>. Accessed via <see cref="View.Margin"/></summary>
 /// <remarks>
 /// <remarks>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 /// </remarks>
 /// </remarks>
@@ -18,7 +18,8 @@ public class Margin : Adornment
     {
     {
         /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
         /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
 
 
-        HighlightStyle |= HighlightStyle.Pressed;
+        // BUGBUG: We should not set HighlightStyle.Pressed here, but wherever it is actually needed
+       // HighlightStyle |= HighlightStyle.Pressed;
         Highlight += Margin_Highlight;
         Highlight += Margin_Highlight;
         LayoutStarted += Margin_LayoutStarted;
         LayoutStarted += Margin_LayoutStarted;
 
 
@@ -69,7 +70,7 @@ public class Margin : Adornment
     ///     The color scheme for the Margin. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>'s
     ///     The color scheme for the Margin. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>'s
     ///     <see cref="View.SuperView"/> scheme. color scheme.
     ///     <see cref="View.SuperView"/> scheme. color scheme.
     /// </summary>
     /// </summary>
-    public override ColorScheme ColorScheme
+    public override ColorScheme? ColorScheme
     {
     {
         get
         get
         {
         {
@@ -90,17 +91,22 @@ public class Margin : Adornment
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle viewport)
     public override void OnDrawContent (Rectangle viewport)
     {
     {
+        if (!NeedsDisplay)
+        {
+            return;
+        }
+
         Rectangle screen = ViewportToScreen (viewport);
         Rectangle screen = ViewportToScreen (viewport);
         Attribute normalAttr = GetNormalColor ();
         Attribute normalAttr = GetNormalColor ();
 
 
         Driver?.SetAttribute (normalAttr);
         Driver?.SetAttribute (normalAttr);
 
 
-        // This just draws/clears the thickness, not the insides.
         if (ShadowStyle != ShadowStyle.None)
         if (ShadowStyle != ShadowStyle.None)
         {
         {
             screen = Rectangle.Inflate (screen, -1, -1);
             screen = Rectangle.Inflate (screen, -1, -1);
         }
         }
 
 
+        // This just draws/clears the thickness, not the insides.
         Thickness.Draw (screen, ToString ());
         Thickness.Draw (screen, ToString ());
 
 
         if (Subviews.Count > 0)
         if (Subviews.Count > 0)
@@ -170,6 +176,9 @@ public class Margin : Adornment
         set => base.ShadowStyle = SetShadow (value);
         set => base.ShadowStyle = SetShadow (value);
     }
     }
 
 
+    private const int PRESS_MOVE_HORIZONTAL = 1;
+    private const int PRESS_MOVE_VERTICAL = 0;
+
     private void Margin_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
     private void Margin_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
     {
     {
         if (ShadowStyle != ShadowStyle.None)
         if (ShadowStyle != ShadowStyle.None)
@@ -179,7 +188,7 @@ public class Margin : Adornment
                 // If the view is pressed and the highlight is being removed, move the shadow back.
                 // If the view is pressed and the highlight is being removed, move the shadow back.
                 // Note, for visual effects reasons, we only move horizontally.
                 // Note, for visual effects reasons, we only move horizontally.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
-                Thickness = new (Thickness.Left - 1, Thickness.Top, Thickness.Right + 1, Thickness.Bottom);
+                Thickness = new (Thickness.Left - PRESS_MOVE_HORIZONTAL, Thickness.Top - PRESS_MOVE_VERTICAL, Thickness.Right + PRESS_MOVE_HORIZONTAL, Thickness.Bottom + PRESS_MOVE_VERTICAL);
 
 
                 if (_rightShadow is { })
                 if (_rightShadow is { })
                 {
                 {
@@ -201,7 +210,7 @@ public class Margin : Adornment
                 // If the view is not pressed and we want highlight move the shadow
                 // If the view is not pressed and we want highlight move the shadow
                 // Note, for visual effects reasons, we only move horizontally.
                 // Note, for visual effects reasons, we only move horizontally.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
-                Thickness = new (Thickness.Left + 1, Thickness.Top, Thickness.Right - 1, Thickness.Bottom);
+                Thickness = new (Thickness.Left + PRESS_MOVE_HORIZONTAL, Thickness.Top+ PRESS_MOVE_VERTICAL, Thickness.Right - PRESS_MOVE_HORIZONTAL, Thickness.Bottom - PRESS_MOVE_VERTICAL);
                 _pressed = true;
                 _pressed = true;
 
 
                 if (_rightShadow is { })
                 if (_rightShadow is { })

+ 2 - 2
Terminal.Gui/View/Adornment/Padding.cs

@@ -1,6 +1,6 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>The Padding for a <see cref="View"/>.</summary>
+/// <summary>The Padding for a <see cref="View"/>. Accessed via <see cref="View.Padding"/></summary>
 /// <remarks>
 /// <remarks>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 /// </remarks>
 /// </remarks>
@@ -50,7 +50,7 @@ public class Padding : Adornment
     /// </remarks>
     /// </remarks>
     /// <param name="mouseEvent"></param>
     /// <param name="mouseEvent"></param>
     /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
     /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
+    protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
     {
     {
         if (Parent is null)
         if (Parent is null)
         {
         {

+ 2 - 2
Terminal.Gui/View/Adornment/ShadowView.cs

@@ -109,7 +109,7 @@ internal class ShadowView : View
         Rectangle screen = ViewportToScreen (viewport);
         Rectangle screen = ViewportToScreen (viewport);
 
 
         // Fill the rest of the rectangle - note we skip the last since vertical will draw it
         // Fill the rest of the rectangle - note we skip the last since vertical will draw it
-        for (int i = screen.X + 1; i < screen.X + screen.Width - 1; i++)
+        for (int i = Math.Max(0, screen.X + 1); i < screen.X + screen.Width - 1; i++)
         {
         {
             Driver.Move (i, screen.Y);
             Driver.Move (i, screen.Y);
 
 
@@ -137,7 +137,7 @@ internal class ShadowView : View
         Rectangle screen = ViewportToScreen (viewport);
         Rectangle screen = ViewportToScreen (viewport);
 
 
         // Fill the rest of the rectangle
         // Fill the rest of the rectangle
-        for (int i = screen.Y; i < screen.Y + viewport.Height; i++)
+        for (int i = Math.Max (0, screen.Y); i < screen.Y + viewport.Height; i++)
         {
         {
             Driver.Move (screen.X, i);
             Driver.Move (screen.X, i);
 
 

+ 11 - 0
Terminal.Gui/View/CancelEventArgs.cs

@@ -27,6 +27,17 @@ public class CancelEventArgs<T> : CancelEventArgs where T : notnull
         NewValue = newValue;
         NewValue = newValue;
     }
     }
 
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="CancelEventArgs{T}"/> class.
+    /// </summary>
+    /// <param name="currentValue">The current (old) value of the property.</param>
+    /// <param name="newValue">The value the property will be set to if the event is not cancelled.</param>
+    protected CancelEventArgs (T currentValue, T newValue)
+    {
+        CurrentValue = currentValue;
+        NewValue = newValue;
+    }
+
     /// <summary>The current value of the property.</summary>
     /// <summary>The current value of the property.</summary>
     public T CurrentValue { get; }
     public T CurrentValue { get; }
 
 

+ 9 - 8
Terminal.Gui/View/HighlightStyle.cs

@@ -1,30 +1,31 @@
-namespace Terminal.Gui;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
-/// Describes the highlight style of a view.
+///     Describes the highlight style of a view when the mouse is over it.
 /// </summary>
 /// </summary>
+[JsonConverter (typeof (JsonStringEnumConverter<HighlightStyle>))]
 [Flags]
 [Flags]
 public enum HighlightStyle
 public enum HighlightStyle
 {
 {
     /// <summary>
     /// <summary>
-    /// No highlight.
+    ///     No highlight.
     /// </summary>
     /// </summary>
     None = 0,
     None = 0,
 
 
-#if HOVER
     /// <summary>
     /// <summary>
-    /// The mouse is hovering over the view.
+    ///     The mouse is hovering over the view (but not pressed). See <see cref="View.MouseEnter"/>.
     /// </summary>
     /// </summary>
     Hover = 1,
     Hover = 1,
-#endif
 
 
     /// <summary>
     /// <summary>
-    /// The mouse is pressed within the <see cref="View.Viewport"/>.
+    ///     The mouse is pressed within the <see cref="View.Viewport"/>.
     /// </summary>
     /// </summary>
     Pressed = 2,
     Pressed = 2,
 
 
     /// <summary>
     /// <summary>
-    /// The mouse is pressed but moved outside the <see cref="View.Viewport"/>.
+    ///     The mouse is pressed but moved outside the <see cref="View.Viewport"/>.
     /// </summary>
     /// </summary>
     PressedOutside = 4
     PressedOutside = 4
 }
 }

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

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

部分文件因文件數量過多而無法顯示