Browse Source

Merge branch 'v2_develop' into v2_2491-Arrangement-Overlapped

Tig 11 months ago
parent
commit
a4d1990e2a
78 changed files with 2351 additions and 1088 deletions
  1. 14 10
      GitVersion.yml
  2. 22 0
      NativeAot/NativeAot.csproj
  3. 113 0
      NativeAot/Program.cs
  4. 18 0
      NativeAot/Properties/PublishProfiles/FolderProfile_net8.0_win-x64_Debug.pubxml
  5. 18 0
      NativeAot/Properties/PublishProfiles/FolderProfile_net8.0_win-x64_Release.pubxml
  6. 5 0
      NativeAot/Publish_linux-x64_Debug.sh
  7. 5 0
      NativeAot/Publish_linux-x64_Release.sh
  8. 5 0
      NativeAot/Publish_osx-x64_Debug.sh
  9. 5 0
      NativeAot/Publish_osx-x64_Release.sh
  10. 10 0
      NativeAot/README.md
  11. 0 5
      Terminal.Gui/Application/Application.Keyboard.cs
  12. 6 6
      Terminal.Gui/Configuration/SourceGenerationContext.cs
  13. 16 1
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  14. 2 1
      Terminal.Gui/Drawing/Alignment.cs
  15. 2 1
      Terminal.Gui/Drawing/AlignmentModes.cs
  16. 1 1
      Terminal.Gui/Drawing/Color.Formatting.cs
  17. 3 0
      Terminal.Gui/Drawing/LineStyle.cs
  18. 21 4
      Terminal.Gui/Input/Key.cs
  19. 2 2
      Terminal.Gui/Input/ShortcutHelper.cs
  20. 1 0
      Terminal.Gui/Resources/config.json
  21. 4 1
      Terminal.Gui/View/Adornment/ShadowStyle.cs
  22. 0 4
      Terminal.Gui/Views/Button.cs
  23. 36 28
      Terminal.Gui/Views/CheckBox.cs
  24. 18 15
      Terminal.Gui/Views/Dialog.cs
  25. 1 4
      Terminal.Gui/Views/FrameView.cs
  26. 23 14
      Terminal.Gui/Views/Menu/Menu.cs
  27. 27 24
      Terminal.Gui/Views/Menu/MenuBar.cs
  28. 74 5
      Terminal.Gui/Views/Menu/MenuBarItem.cs
  29. 170 76
      Terminal.Gui/Views/Menu/MenuItem.cs
  30. 1 16
      Terminal.Gui/Views/MessageBox.cs
  31. 251 0
      Terminal.Gui/Views/NumericUpDown.cs
  32. 1 4
      Terminal.Gui/Views/Window.cs
  33. 7 0
      Terminal.sln
  34. 9 5
      UICatalog/Scenarios/AdornmentEditor.cs
  35. 4 4
      UICatalog/Scenarios/AdornmentsEditor.cs
  36. 6 6
      UICatalog/Scenarios/BorderEditor.cs
  37. 17 159
      UICatalog/Scenarios/Buttons.cs
  38. 14 14
      UICatalog/Scenarios/ContentScrolling.cs
  39. 1 1
      UICatalog/Scenarios/ContextMenus.cs
  40. 4 3
      UICatalog/Scenarios/Dialogs.cs
  41. 328 366
      UICatalog/Scenarios/DynamicMenuBar.cs
  42. 10 81
      UICatalog/Scenarios/DynamicStatusBar.cs
  43. 8 8
      UICatalog/Scenarios/Editor.cs
  44. 16 16
      UICatalog/Scenarios/FileDialogExamples.cs
  45. 1 1
      UICatalog/Scenarios/GraphViewExample.cs
  46. 3 3
      UICatalog/Scenarios/Images.cs
  47. 6 6
      UICatalog/Scenarios/ListViewWithSelection.cs
  48. 2 2
      UICatalog/Scenarios/Localization.cs
  49. 3 3
      UICatalog/Scenarios/MessageBoxes.cs
  50. 3 3
      UICatalog/Scenarios/Mouse.cs
  51. 291 0
      UICatalog/Scenarios/NumericUpDownDemo.cs
  52. 11 11
      UICatalog/Scenarios/PosAlignDemo.cs
  53. 3 3
      UICatalog/Scenarios/ProgressBarStyles.cs
  54. 14 14
      UICatalog/Scenarios/Scrolling.cs
  55. 3 3
      UICatalog/Scenarios/SendKeys.cs
  56. 2 2
      UICatalog/Scenarios/Shortcuts.cs
  57. 2 2
      UICatalog/Scenarios/Sliders.cs
  58. 10 10
      UICatalog/Scenarios/SpinnerStyles.cs
  59. 10 10
      UICatalog/Scenarios/Text.cs
  60. 4 4
      UICatalog/Scenarios/TextAlignmentAndDirection.cs
  61. 1 1
      UICatalog/Scenarios/TextEffectsScenario.cs
  62. 2 2
      UICatalog/Scenarios/TextFormatterDemo.cs
  63. 13 13
      UICatalog/Scenarios/TileViewNesting.cs
  64. 3 3
      UICatalog/Scenarios/TrueColors.cs
  65. 1 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  66. 7 7
      UICatalog/Scenarios/Wizards.cs
  67. 14 14
      UICatalog/UICatalog.cs
  68. 40 40
      UnitTests/Configuration/ConfigurationMangerTests.cs
  69. 108 0
      UnitTests/Configuration/SerializableConfigurationPropertyTests.cs
  70. 9 4
      UnitTests/Configuration/ThemeScopeTests.cs
  71. 3 0
      UnitTests/Configuration/ThemeTests.cs
  72. 17 1
      UnitTests/Input/KeyTests.cs
  73. 72 0
      UnitTests/TestHelpers.cs
  74. 32 32
      UnitTests/Views/CheckBoxTests.cs
  75. 91 6
      UnitTests/Views/MenuBarTests.cs
  76. 2 2
      UnitTests/Views/MenuTests.cs
  77. 239 0
      UnitTests/Views/NumericUpDownTests.cs
  78. 30 0
      docfx/docs/migratingfromv1.md

+ 14 - 10
GitVersion.yml

@@ -2,14 +2,6 @@ mode: ContinuousDeployment
 tag-prefix: '[vV]'
 tag-prefix: '[vV]'
 continuous-delivery-fallback-tag: dev
 continuous-delivery-fallback-tag: dev
 branches:
 branches:
-  v1_develop:
-    mode: ContinuousDeployment
-    tag: dev
-    regex: v1_develop
-    source-branches:
-    - v1_release
-    pre-release-weight: 100
-
   v2_develop:
   v2_develop:
     mode: ContinuousDeployment
     mode: ContinuousDeployment
     tag: dev
     tag: dev
@@ -25,6 +17,20 @@ branches:
     is-release-branch: true
     is-release-branch: true
     source-branches: ['v2_develop']
     source-branches: ['v2_develop']
 
 
+  v1_develop:
+    mode: ContinuousDeployment
+    tag: dev
+    regex: v1_develop
+    source-branches:
+    - v1_release
+    pre-release-weight: 100
+
+  v1_release:
+    mode: ContinuousDeployment
+    regex: v1_release
+    is-release-branch: true
+    source-branches: ['v1_develop']
+
   pull-request:
   pull-request:
     mode: ContinuousDeployment
     mode: ContinuousDeployment
     tag: PullRequest.{BranchName}
     tag: PullRequest.{BranchName}
@@ -32,8 +38,6 @@ branches:
     tag-number-pattern: '[/-](?<number>\d+)'
     tag-number-pattern: '[/-](?<number>\d+)'
     regex: ^(pull|pull\-requests|pr)[/-]
     regex: ^(pull|pull\-requests|pr)[/-]
     source-branches:
     source-branches:
-    - v1_develop
-    - v1_release
     - v2_develop
     - v2_develop
     - v2_release
     - v2_release
     - feature
     - feature

+ 22 - 0
NativeAot/NativeAot.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <PublishAot>true</PublishAot>
+    <InvariantGlobalization>false</InvariantGlobalization>
+  </PropertyGroup>
+
+  <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
+    <TrimmerRootAssembly Include="Terminal.Gui" />
+  </ItemGroup>
+
+  <ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <PackageReference Include="Terminal.Gui" Version="[2.0.0-pre.1788,3)" />
+    <TrimmerRootAssembly Include="Terminal.Gui" />
+  </ItemGroup>
+
+</Project>

+ 113 - 0
NativeAot/Program.cs

@@ -0,0 +1,113 @@
+// This is a test application for a native Aot file.
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using Terminal.Gui;
+
+namespace NativeAot;
+
+public static class Program
+{
+    [RequiresUnreferencedCode ("Calls Terminal.Gui.Application.Init(ConsoleDriver, String)")]
+    [RequiresDynamicCode ("Calls Terminal.Gui.Application.Init(ConsoleDriver, String)")]
+    private static void Main (string [] args)
+    {
+        Application.Init ();
+
+        #region The code in this region is not intended for use in a native Aot self-contained. It's just here to make sure there is no functionality break with localization in Terminal.Gui using self-contained
+
+        if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0)
+        {
+            // Only happens if the project has <InvariantGlobalization>true</InvariantGlobalization>
+            Debug.Assert (Application.SupportedCultures.Count == 0);
+        }
+        else
+        {
+            Debug.Assert (Application.SupportedCultures.Count > 0);
+            Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
+        }
+
+        #endregion
+
+        ExampleWindow app = new ();
+        Application.Run (app);
+
+        // Dispose the app object before shutdown
+        app.Dispose ();
+
+        // Before the application exits, reset Terminal.Gui for clean shutdown
+        Application.Shutdown ();
+
+        // To see this output on the screen it must be done after shutdown,
+        // which restores the previous screen.
+        Console.WriteLine ($@"Username: {ExampleWindow.UserName}");
+    }
+}
+
+// Defines a top-level window with border and title
+public class ExampleWindow : Window
+{
+    public static string? UserName;
+
+    public ExampleWindow ()
+    {
+        Title = $"Example App ({Application.QuitKey} to quit)";
+
+        // Create input components and labels
+        var usernameLabel = new Label { Text = "Username:" };
+
+        var userNameText = new TextField
+        {
+            // Position text field adjacent to the label
+            X = Pos.Right (usernameLabel) + 1,
+
+            // Fill remaining horizontal space
+            Width = Dim.Fill ()
+        };
+
+        var passwordLabel = new Label
+        {
+            Text = "Password:", X = Pos.Left (usernameLabel), Y = Pos.Bottom (usernameLabel) + 1
+        };
+
+        var passwordText = new TextField
+        {
+            Secret = true,
+
+            // align with the text box above
+            X = Pos.Left (userNameText),
+            Y = Pos.Top (passwordLabel),
+            Width = Dim.Fill ()
+        };
+
+        // Create login button
+        var btnLogin = new Button
+        {
+            Text = "Login",
+            Y = Pos.Bottom (passwordLabel) + 1,
+
+            // center the login button horizontally
+            X = Pos.Center (),
+            IsDefault = true
+        };
+
+        // When login button is clicked display a message popup
+        btnLogin.Accept += (s, e) =>
+        {
+            if (userNameText.Text == "admin" && passwordText.Text == "password")
+            {
+                MessageBox.Query ("Logging In", "Login Successful", "Ok");
+                UserName = userNameText.Text;
+                Application.RequestStop ();
+            }
+            else
+            {
+                MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+            }
+        };
+
+        // Add the views to the Window
+        Add (usernameLabel, userNameText, passwordLabel, passwordText, btnLogin);
+    }
+}

+ 18 - 0
NativeAot/Properties/PublishProfiles/FolderProfile_net8.0_win-x64_Debug.pubxml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+https://go.microsoft.com/fwlink/?LinkID=208121.
+-->
+<Project>
+  <PropertyGroup>
+    <Configuration>Debug</Configuration>
+    <Platform>Any CPU</Platform>
+    <PublishDir>bin\Debug\net8.0\publish\win-x64\</PublishDir>
+    <PublishProtocol>FileSystem</PublishProtocol>
+    <_TargetId>Folder</_TargetId>
+    <TargetFramework>net8.0</TargetFramework>
+    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
+    <SelfContained>true</SelfContained>
+    <PublishSingleFile>false</PublishSingleFile>
+    <PublishReadyToRun>false</PublishReadyToRun>
+  </PropertyGroup>
+</Project>

+ 18 - 0
NativeAot/Properties/PublishProfiles/FolderProfile_net8.0_win-x64_Release.pubxml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+https://go.microsoft.com/fwlink/?LinkID=208121.
+-->
+<Project>
+  <PropertyGroup>
+    <Configuration>Release</Configuration>
+    <Platform>Any CPU</Platform>
+    <PublishDir>bin\Release\net8.0\publish\win-x64\</PublishDir>
+    <PublishProtocol>FileSystem</PublishProtocol>
+    <_TargetId>Folder</_TargetId>
+    <TargetFramework>net8.0</TargetFramework>
+    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
+    <SelfContained>true</SelfContained>
+    <PublishSingleFile>false</PublishSingleFile>
+    <PublishReadyToRun>false</PublishReadyToRun>
+  </PropertyGroup>
+</Project>

+ 5 - 0
NativeAot/Publish_linux-x64_Debug.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+dotnet clean
+dotnet build
+dotnet publish -c Debug -r linux-x64 --self-contained

+ 5 - 0
NativeAot/Publish_linux-x64_Release.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+dotnet clean
+dotnet build
+dotnet publish -c Release -r linux-x64 --self-contained

+ 5 - 0
NativeAot/Publish_osx-x64_Debug.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+dotnet clean
+dotnet build
+dotnet publish -c Debug -r osx-x64 --self-contained

+ 5 - 0
NativeAot/Publish_osx-x64_Release.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+dotnet clean
+dotnet build
+dotnet publish -c Release -r osx-x64 --self-contained

+ 10 - 0
NativeAot/README.md

@@ -0,0 +1,10 @@
+# Terminal.Gui C# SelfContained
+
+This project aims to test the `Terminal.Gui` library to create a simple `native AOT` `self-container` GUI application in C#, ensuring that all its features are available.
+
+With `Debug` the `.csproj` is used and with `Release` the latest `nuget package` is used, either in `Solution Configurations` or in `Profile Publish` or in the `Publish_linux-x64` or in the `Publish_osx-x64` files.
+Unlike self-contained single-file publishing, native AOT publishing must be generated on the same platform as the target execution version. Therefore, if the target execution is Linux, then the publishing must be generated on a Linux operating system. Attempting to generate on Windows for the Linux target will throw an exception.
+
+To publish the `native AOT` file in `Debug` or `Release` mode, it is not necessary to select it in the `Solution Configurations`, just choose the `Debug` or `Release` configuration in the `Publish Profile` or the `*.sh` files.
+
+When executing the file directly from the `native AOT` file and needing to debug it, it will be necessary to attach it to the debugger, just like any other standalone application and selecting `Native Code`.

+ 0 - 5
Terminal.Gui/Application/Application.Keyboard.cs

@@ -9,7 +9,6 @@ public static partial class Application // Keyboard handling
 
 
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    [JsonConverter (typeof (KeyJsonConverter))]
     public static Key NextTabKey
     public static Key NextTabKey
     {
     {
         get => _nextTabKey;
         get => _nextTabKey;
@@ -27,7 +26,6 @@ public static partial class Application // Keyboard handling
 
 
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    [JsonConverter (typeof (KeyJsonConverter))]
     public static Key PrevTabKey
     public static Key PrevTabKey
     {
     {
         get => _prevTabKey;
         get => _prevTabKey;
@@ -45,7 +43,6 @@ public static partial class Application // Keyboard handling
 
 
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    [JsonConverter (typeof (KeyJsonConverter))]
     public static Key NextTabGroupKey
     public static Key NextTabGroupKey
     {
     {
         get => _nextTabGroupKey;
         get => _nextTabGroupKey;
@@ -63,7 +60,6 @@ public static partial class Application // Keyboard handling
 
 
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    [JsonConverter (typeof (KeyJsonConverter))]
     public static Key PrevTabGroupKey
     public static Key PrevTabGroupKey
     {
     {
         get => _prevTabGroupKey;
         get => _prevTabGroupKey;
@@ -81,7 +77,6 @@ public static partial class Application // Keyboard handling
 
 
     /// <summary>Gets or sets the key to quit the application.</summary>
     /// <summary>Gets or sets the key to quit the application.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    [JsonConverter (typeof (KeyJsonConverter))]
     public static Key QuitKey
     public static Key QuitKey
     {
     {
         get => _quitKey;
         get => _quitKey;

+ 6 - 6
Terminal.Gui/Configuration/SourceGenerationContext.cs

@@ -7,17 +7,17 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 [JsonSerializable (typeof (Attribute))]
 [JsonSerializable (typeof (Attribute))]
 [JsonSerializable (typeof (Color))]
 [JsonSerializable (typeof (Color))]
-[JsonSerializable (typeof (ThemeScope))]
-[JsonSerializable (typeof (ColorScheme))]
-[JsonSerializable (typeof (SettingsScope))]
 [JsonSerializable (typeof (AppScope))]
 [JsonSerializable (typeof (AppScope))]
+[JsonSerializable (typeof (SettingsScope))]
 [JsonSerializable (typeof (Key))]
 [JsonSerializable (typeof (Key))]
 [JsonSerializable (typeof (GlyphDefinitions))]
 [JsonSerializable (typeof (GlyphDefinitions))]
-[JsonSerializable (typeof (ConfigProperty))]
+[JsonSerializable (typeof (Alignment))]
+[JsonSerializable (typeof (AlignmentModes))]
+[JsonSerializable (typeof (LineStyle))]
 [JsonSerializable (typeof (ShadowStyle))]
 [JsonSerializable (typeof (ShadowStyle))]
-[JsonSerializable (typeof (string))]
-[JsonSerializable (typeof (bool))]
 [JsonSerializable (typeof (bool?))]
 [JsonSerializable (typeof (bool?))]
 [JsonSerializable (typeof (Dictionary<ColorName, string>))]
 [JsonSerializable (typeof (Dictionary<ColorName, string>))]
+[JsonSerializable (typeof (Dictionary<string, ThemeScope>))]
+[JsonSerializable (typeof (Dictionary<string, ColorScheme>))]
 internal partial class SourceGenerationContext : JsonSerializerContext
 internal partial class SourceGenerationContext : JsonSerializerContext
 { }
 { }

+ 16 - 1
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -2343,7 +2343,22 @@ internal class WindowsClipboard : ClipboardBase
 {
 {
     private const uint CF_UNICODE_TEXT = 13;
     private const uint CF_UNICODE_TEXT = 13;
 
 
-    public override bool IsSupported { get; } = IsClipboardFormatAvailable (CF_UNICODE_TEXT);
+    public override bool IsSupported { get; } = CheckClipboardIsAvailable ();
+
+    private static bool CheckClipboardIsAvailable ()
+    {
+        // Attempt to open the clipboard
+        if (OpenClipboard (nint.Zero))
+        {
+            // Clipboard is available
+            // Close the clipboard after use
+            CloseClipboard ();
+
+            return true;
+        }
+        // Clipboard is not available
+        return false;
+    }
 
 
     protected override string GetClipboardDataImpl ()
     protected override string GetClipboardDataImpl ()
     {
     {

+ 2 - 1
Terminal.Gui/Drawing/Alignment.cs

@@ -1,10 +1,11 @@
-
+using System.Text.Json.Serialization;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     Determines the position of items when arranged in a container.
 ///     Determines the position of items when arranged in a container.
 /// </summary>
 /// </summary>
+[JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
 public enum Alignment
 public enum Alignment
 {
 {
     /// <summary>
     /// <summary>

+ 2 - 1
Terminal.Gui/Drawing/AlignmentModes.cs

@@ -1,10 +1,11 @@
-
+using System.Text.Json.Serialization;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     Determines alignment modes for <see cref="Alignment"/>.
 ///     Determines alignment modes for <see cref="Alignment"/>.
 /// </summary>
 /// </summary>
+[JsonConverter (typeof (JsonStringEnumConverter<AlignmentModes>))]
 [Flags]
 [Flags]
 public enum AlignmentModes
 public enum AlignmentModes
 {
 {

+ 1 - 1
Terminal.Gui/Drawing/Color.Formatting.cs

@@ -284,7 +284,7 @@ public readonly partial record struct Color
                                                                                               ),
                                                                                               ),
 
 
                    // Any string too short to possibly be any supported format.
                    // Any string too short to possibly be any supported format.
-                   { Length: > 0 and < 4 } => throw new ColorParseException (
+                   { Length: > 0 and < 3 } => throw new ColorParseException (
                                                                              in text,
                                                                              in text,
                                                                              "Text was too short to be any possible supported format.",
                                                                              "Text was too short to be any possible supported format.",
                                                                              in text
                                                                              in text

+ 3 - 0
Terminal.Gui/Drawing/LineStyle.cs

@@ -1,7 +1,10 @@
 #nullable enable
 #nullable enable
+using System.Text.Json.Serialization;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Defines the style of lines for a <see cref="LineCanvas"/>.</summary>
 /// <summary>Defines the style of lines for a <see cref="LineCanvas"/>.</summary>
+[JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
 public enum LineStyle
 public enum LineStyle
 {
 {
     /// <summary>No border is drawn.</summary>
     /// <summary>No border is drawn.</summary>

+ 21 - 4
Terminal.Gui/Input/Key.cs

@@ -1,5 +1,6 @@
 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;
 
 
@@ -448,9 +449,9 @@ public class Key : EventArgs, IEquatable<Key>
 
 
     #region String conversion
     #region String conversion
 
 
-    /// <summary>Pretty prints the KeyEvent</summary>
+    /// <summary>Pretty prints the Key.</summary>
     /// <returns></returns>
     /// <returns></returns>
-    public override string ToString () { return ToString (KeyCode, (Rune)'+'); }
+    public override string ToString () { return ToString (KeyCode, Separator); }
 
 
     private static string GetKeyString (KeyCode key)
     private static string GetKeyString (KeyCode key)
     {
     {
@@ -483,7 +484,7 @@ public class Key : EventArgs, IEquatable<Key>
     ///     The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key
     ///     The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key
     ///     name will be returned.
     ///     name will be returned.
     /// </returns>
     /// </returns>
-    public static string ToString (KeyCode key) { return ToString (key, (Rune)'+'); }
+    public static string ToString (KeyCode key) { return ToString (key, Separator); }
 
 
     /// <summary>Formats a <see cref="KeyCode"/> as a string.</summary>
     /// <summary>Formats a <see cref="KeyCode"/> as a string.</summary>
     /// <param name="key">The key to format.</param>
     /// <param name="key">The key to format.</param>
@@ -584,7 +585,7 @@ public class Key : EventArgs, IEquatable<Key>
         key = null;
         key = null;
 
 
         // Split the string into parts
         // Split the string into parts
-        string [] parts = text.Split ('+', '-');
+        string [] parts = text.Split ('+', '-', (char)Separator.Value);
 
 
         if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
         if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
         {
         {
@@ -971,4 +972,20 @@ public class Key : EventArgs, IEquatable<Key>
     public static Key F24 => new (KeyCode.F24);
     public static Key F24 => new (KeyCode.F24);
 
 
     #endregion
     #endregion
+
+    private static Rune _separator = new ('+');
+
+    /// <summary>Gets or sets the separator character used when parsing and printing Keys. E.g. Ctrl+A. The default is '+'.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Rune Separator
+    {
+        get => _separator;
+        set
+        {
+            if (_separator != value)
+            {
+                _separator = value == default (Rune) ? new ('+') : value;
+            }
+        }
+    }
 }
 }

+ 2 - 2
Terminal.Gui/Input/ShortcutHelper.cs

@@ -23,7 +23,7 @@ public class ShortcutHelper
     }
     }
 
 
     /// <summary>The keystroke combination used in the <see cref="Shortcut"/> as string.</summary>
     /// <summary>The keystroke combination used in the <see cref="Shortcut"/> as string.</summary>
-    public virtual string ShortcutTag => Key.ToString (shortcut, MenuBar.ShortcutDelimiter);
+    public virtual string ShortcutTag => Key.ToString (shortcut, Key.Separator);
 
 
     /// <summary>Lookup for a <see cref="KeyCode"/> on range of keys.</summary>
     /// <summary>Lookup for a <see cref="KeyCode"/> on range of keys.</summary>
     /// <param name="key">The source key.</param>
     /// <param name="key">The source key.</param>
@@ -59,7 +59,7 @@ public class ShortcutHelper
         //var hasCtrl = false;
         //var hasCtrl = false;
         if (delimiter == default (Rune))
         if (delimiter == default (Rune))
         {
         {
-            delimiter = MenuBar.ShortcutDelimiter;
+            delimiter = Key.Separator;
         }
         }
 
 
         string [] keys = sCut.Split (delimiter.ToString ());
         string [] keys = sCut.Split (delimiter.ToString ());

+ 1 - 0
Terminal.Gui/Resources/config.json

@@ -22,6 +22,7 @@
   "Application.NextTabGroupKey": "F6",
   "Application.NextTabGroupKey": "F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.QuitKey": "Esc",
   "Application.QuitKey": "Esc",
+  "Key.Separator": "+",
 
 
   "Theme": "Default",
   "Theme": "Default",
   "Themes": [
   "Themes": [

+ 4 - 1
Terminal.Gui/View/Adornment/ShadowStyle.cs

@@ -1,8 +1,11 @@
-namespace Terminal.Gui;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     Defines the style of shadow to be drawn on the right and bottom sides of the <see cref="View"/>.
 ///     Defines the style of shadow to be drawn on the right and bottom sides of the <see cref="View"/>.
 /// </summary>
 /// </summary>
+[JsonConverter (typeof (JsonStringEnumConverter<ShadowStyle>))]
 public enum ShadowStyle
 public enum ShadowStyle
 {
 {
     /// <summary>
     /// <summary>

+ 0 - 4
Terminal.Gui/Views/Button.cs

@@ -5,8 +5,6 @@
 //   Miguel de Icaza ([email protected])
 //   Miguel de Icaza ([email protected])
 //
 //
 
 
-using System.Text.Json.Serialization;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Button is a <see cref="View"/> that provides an item that invokes raises the <see cref="View.Accept"/> event.</summary>
 /// <summary>Button is a <see cref="View"/> that provides an item that invokes raises the <see cref="View.Accept"/> event.</summary>
@@ -39,8 +37,6 @@ public class Button : View, IDesignable
     /// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
     /// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<ShadowStyle>))]
-
     public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
     public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
 
 
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>

+ 36 - 28
Terminal.Gui/Views/CheckBox.cs

@@ -1,12 +1,9 @@
 #nullable enable
 #nullable enable
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Shows a check box that can be toggled.</summary>
+/// <summary>Shows a check box that can be cycled between three states.</summary>
 public class CheckBox : View
 public class CheckBox : View
 {
 {
-    private bool _allowNone;
-    private CheckState _checked = CheckState.UnChecked;
-
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of <see cref="CheckBox"/>.
     ///     Initializes a new instance of <see cref="CheckBox"/>.
     /// </summary>
     /// </summary>
@@ -18,8 +15,8 @@ public class CheckBox : View
         CanFocus = true;
         CanFocus = true;
 
 
         // Things this view knows how to do
         // Things this view knows how to do
-        AddCommand (Command.Accept, OnToggle);
-        AddCommand (Command.HotKey, OnToggle);
+        AddCommand (Command.Accept, AdvanceCheckState);
+        AddCommand (Command.HotKey, AdvanceCheckState);
 
 
         // Default keybindings for this view
         // Default keybindings for this view
         KeyBindings.Add (Key.Space, Command.Accept);
         KeyBindings.Add (Key.Space, Command.Accept);
@@ -32,7 +29,7 @@ public class CheckBox : View
 
 
     private void CheckBox_MouseClick (object? sender, MouseEventEventArgs e)
     private void CheckBox_MouseClick (object? sender, MouseEventEventArgs e)
     {
     {
-        e.Handled = OnToggle () == true;
+        e.Handled = AdvanceCheckState () == true;
     }
     }
 
 
     private void Checkbox_TitleChanged (object? sender, EventArgs<string> e)
     private void Checkbox_TitleChanged (object? sender, EventArgs<string> e)
@@ -55,8 +52,10 @@ public class CheckBox : View
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
         set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
     }
     }
 
 
+    private bool _allowNone = false;
+
     /// <summary>
     /// <summary>
-    ///     If <see langword="true"/> allows <see cref="State"/> to be <see cref="CheckState.None"/>.
+    ///     If <see langword="true"/> allows <see cref="CheckedState"/> to be <see cref="CheckState.None"/>. The default is <see langword="false"/>.
     /// </summary>
     /// </summary>
     public bool AllowCheckStateNone
     public bool AllowCheckStateNone
     {
     {
@@ -69,13 +68,15 @@ public class CheckBox : View
             }
             }
             _allowNone = value;
             _allowNone = value;
 
 
-            if (State == CheckState.None)
+            if (CheckedState == CheckState.None)
             {
             {
-                State = CheckState.UnChecked;
+                CheckedState = CheckState.UnChecked;
             }
             }
         }
         }
     }
     }
 
 
+    private CheckState _checkedState = CheckState.UnChecked;
+
     /// <summary>
     /// <summary>
     ///     The state of the <see cref="CheckBox"/>.
     ///     The state of the <see cref="CheckBox"/>.
     /// </summary>
     /// </summary>
@@ -93,35 +94,42 @@ public class CheckBox : View
     ///        will display the <c>ConfigurationManager.Glyphs.CheckStateChecked</c> character (☑).
     ///        will display the <c>ConfigurationManager.Glyphs.CheckStateChecked</c> character (☑).
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public CheckState State
+    public CheckState CheckedState
     {
     {
-        get => _checked;
+        get => _checkedState;
         set
         set
         {
         {
-            if (_checked == value || (value is CheckState.None && !AllowCheckStateNone))
+            if (_checkedState == value || (value is CheckState.None && !AllowCheckStateNone))
             {
             {
                 return;
                 return;
             }
             }
 
 
-            _checked = value;
+            _checkedState = value;
             UpdateTextFormatterText ();
             UpdateTextFormatterText ();
             OnResizeNeeded ();
             OnResizeNeeded ();
         }
         }
     }
     }
 
 
-    /// <summary>Called when the <see cref="State"/> property changes. Invokes the cancelable <see cref="Toggle"/> event.</summary>
+    /// <summary>
+    ///     Advances <see cref="CheckedState"/> to the next value. Invokes the cancelable <see cref="CheckedStateChanging"/> event.
+    /// </summary>
     /// <remarks>
     /// <remarks>
     /// </remarks>
     /// </remarks>
-    /// <returns>If <see langword="true"/> the <see cref="Toggle"/> event was canceled.</returns>
+    /// <returns>If <see langword="true"/> the <see cref="CheckedStateChanging"/> event was canceled.</returns>
     /// <remarks>
     /// <remarks>
-    ///     Toggling cycles through the states <see cref="CheckState.None"/>, <see cref="CheckState.Checked"/>, and <see cref="CheckState.UnChecked"/>.
+    /// <para>
+    ///     Cycles through the states <see cref="CheckState.None"/>, <see cref="CheckState.Checked"/>, and <see cref="CheckState.UnChecked"/>.
+    /// </para>
+    /// <para>
+    ///     If the <see cref="CheckedStateChanging"/> event is not canceled, the <see cref="CheckedState"/> will be updated and the <see cref="Command.Accept"/> event will be raised.
+    /// </para>
     /// </remarks>
     /// </remarks>
-    public bool? OnToggle ()
+    public bool? AdvanceCheckState ()
     {
     {
-        CheckState oldValue = State;
-        CancelEventArgs<CheckState> e = new (ref _checked, ref oldValue);
+        CheckState oldValue = CheckedState;
+        CancelEventArgs<CheckState> e = new (in _checkedState, ref oldValue);
 
 
-        switch (State)
+        switch (CheckedState)
         {
         {
             case CheckState.None:
             case CheckState.None:
                 e.NewValue = CheckState.Checked;
                 e.NewValue = CheckState.Checked;
@@ -144,35 +152,35 @@ public class CheckBox : View
                 break;
                 break;
         }
         }
 
 
-        Toggle?.Invoke (this, e);
+        CheckedStateChanging?.Invoke (this, e);
         if (e.Cancel)
         if (e.Cancel)
         {
         {
             return e.Cancel;
             return e.Cancel;
         }
         }
 
 
-        // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired.
+        // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the Accept event is fired.
         if (OnAccept () == true)
         if (OnAccept () == true)
         {
         {
             return true;
             return true;
         }
         }
 
 
-        State = e.NewValue;
+        CheckedState = e.NewValue;
 
 
         return true;
         return true;
     }
     }
 
 
-    /// <summary>Toggle event, raised when the <see cref="CheckBox"/> is toggled.</summary>
+    /// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>
     /// <remarks>
     /// <remarks>
     /// <para>
     /// <para>
     ///    This event can be cancelled. If cancelled, the <see cref="CheckBox"/> will not change its state.
     ///    This event can be cancelled. If cancelled, the <see cref="CheckBox"/> will not change its state.
     /// </para>
     /// </para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<CancelEventArgs<CheckState>>? Toggle;
+    public event EventHandler<CancelEventArgs<CheckState>>? CheckedStateChanging;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void UpdateTextFormatterText ()
     protected override void UpdateTextFormatterText ()
     {
     {
-        base.UpdateTextFormatterText();
+        base.UpdateTextFormatterText ();
         switch (TextAlignment)
         switch (TextAlignment)
         {
         {
             case Alignment.Start:
             case Alignment.Start:
@@ -190,7 +198,7 @@ public class CheckBox : View
 
 
     private Rune GetCheckedGlyph ()
     private Rune GetCheckedGlyph ()
     {
     {
-        return State switch
+        return CheckedState switch
         {
         {
             CheckState.Checked => Glyphs.CheckStateChecked,
             CheckState.Checked => Glyphs.CheckStateChecked,
             CheckState.UnChecked => Glyphs.CheckStateUnChecked,
             CheckState.UnChecked => Glyphs.CheckStateUnChecked,

+ 18 - 15
Terminal.Gui/Views/Dialog.cs

@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     The <see cref="Dialog"/> <see cref="View"/> is a <see cref="Window"/> that by default is centered and contains
 ///     The <see cref="Dialog"/> <see cref="View"/> is a <see cref="Window"/> that by default is centered and contains
@@ -19,13 +17,11 @@ public class Dialog : Window
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     /// <remarks>This property can be set in a Theme.</remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
     public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End; // Default is set in config.json
     public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End; // Default is set in config.json
 
 
-    /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
+    /// <summary>The default <see cref="AlignmentModes"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     /// <remarks>This property can be set in a Theme.</remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<AlignmentModes>))]
     public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
     public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
 
 
     /// <summary>
     /// <summary>
@@ -47,7 +43,6 @@ public class Dialog : Window
     /// Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
     /// Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<ShadowStyle>))]
     public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None; // Default is set in config.json
     public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None; // Default is set in config.json
 
 
     /// <summary>
     /// <summary>
@@ -56,7 +51,6 @@ public class Dialog : Window
     /// </summary>
     /// </summary>
 
 
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
     public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
 
 
     private readonly List<Button> _buttons = new ();
     private readonly List<Button> _buttons = new ();
@@ -99,6 +93,22 @@ public class Dialog : Window
         KeyBindings.Add (Key.Esc, Command.QuitToplevel);
         KeyBindings.Add (Key.Esc, Command.QuitToplevel);
     }
     }
 
 
+    // BUGBUG: We override GetNormal/FocusColor because "Dialog" ColorScheme is goofy.
+    // BUGBUG: By defn, a Dialog is Modal, and thus HasFocus is always true. OnDrawContent
+    // BUGBUG: Calls these methods.
+    // TODO: Fix this in https://github.com/gui-cs/Terminal.Gui/issues/2381
+    /// <inheritdoc />
+    public override Attribute GetNormalColor ()
+    {
+        return ColorScheme.Normal;
+    }
+
+    /// <inheritdoc />
+    public override Attribute GetFocusColor ()
+    {
+        return ColorScheme.Normal;
+    }
+
     private bool _canceled;
     private bool _canceled;
 
 
     /// <summary>Gets a value indicating whether the <see cref="Dialog"/> was canceled.</summary>
     /// <summary>Gets a value indicating whether the <see cref="Dialog"/> was canceled.</summary>
@@ -172,12 +182,5 @@ public class Dialog : Window
 
 
         _buttons.Add (button);
         _buttons.Add (button);
         Add (button);
         Add (button);
-
-        SetNeedsDisplay ();
-
-        if (IsInitialized)
-        {
-            LayoutSubviews ();
-        }
     }
     }
 }
 }

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

@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     The FrameView is a container View with a border around it. 
 ///     The FrameView is a container View with a border around it. 
@@ -38,6 +36,5 @@ public class FrameView : View
     ///     <see cref="FrameView"/>s.
     ///     <see cref="FrameView"/>s.
     /// </remarks>
     /// </remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 }
 }

+ 23 - 14
Terminal.Gui/Views/Menu/Menu.cs

@@ -120,7 +120,7 @@ internal sealed class Menu : View
                     }
                     }
                    );
                    );
 
 
-        AddKeyBindings (_barItems);
+        AddKeyBindingsHotKey (_barItems);
     }
     }
 
 
     public Menu ()
     public Menu ()
@@ -179,7 +179,7 @@ internal sealed class Menu : View
         KeyBindings.Add (Key.Enter, Command.Accept);
         KeyBindings.Add (Key.Enter, Command.Accept);
     }
     }
 
 
-    private void AddKeyBindings (MenuBarItem menuBarItem)
+    private void AddKeyBindingsHotKey (MenuBarItem menuBarItem)
     {
     {
         if (menuBarItem is null || menuBarItem.Children is null)
         if (menuBarItem is null || menuBarItem.Children is null)
         {
         {
@@ -190,23 +190,30 @@ internal sealed class Menu : View
         {
         {
             KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuItem);
             KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuItem);
 
 
-            if ((KeyCode)menuItem.HotKey.Value != KeyCode.Null)
+            if (menuItem.HotKey != Key.Empty)
             {
             {
-                KeyBindings.Remove ((KeyCode)menuItem.HotKey.Value);
-                KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, keyBinding);
-                KeyBindings.Remove ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask);
-                KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, keyBinding);
+                KeyBindings.Remove (menuItem.HotKey);
+                KeyBindings.Add (menuItem.HotKey, keyBinding);
+                KeyBindings.Remove (menuItem.HotKey.WithAlt);
+                KeyBindings.Add (menuItem.HotKey.WithAlt, keyBinding);
             }
             }
+        }
+    }
+
+    private void RemoveKeyBindingsHotKey (MenuBarItem menuBarItem)
+    {
+        if (menuBarItem is null || menuBarItem.Children is null)
+        {
+            return;
+        }
 
 
-            if (menuItem.Shortcut != KeyCode.Null)
+        foreach (MenuItem menuItem in menuBarItem.Children.Where (m => m is { }))
+        {
+            if (menuItem.HotKey != Key.Empty)
             {
             {
-                keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
-                KeyBindings.Remove (menuItem.Shortcut);
-                KeyBindings.Add (menuItem.Shortcut, keyBinding);
+                KeyBindings.Remove (menuItem.HotKey);
+                KeyBindings.Remove (menuItem.HotKey.WithAlt);
             }
             }
-
-            MenuBarItem subMenu = menuBarItem.SubMenu (menuItem);
-            AddKeyBindings (subMenu);
         }
         }
     }
     }
 
 
@@ -910,6 +917,8 @@ internal sealed class Menu : View
 
 
     protected override void Dispose (bool disposing)
     protected override void Dispose (bool disposing)
     {
     {
+        RemoveKeyBindingsHotKey (_barItems);
+
         if (Application.Current is { })
         if (Application.Current is { })
         {
         {
             Application.Current.DrawContentComplete -= Current_DrawContentComplete;
             Application.Current.DrawContentComplete -= Current_DrawContentComplete;

+ 27 - 24
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -66,6 +66,8 @@ public class MenuBar : View, IDesignable
     /// <summary>Initializes a new instance of the <see cref="MenuBar"/>.</summary>
     /// <summary>Initializes a new instance of the <see cref="MenuBar"/>.</summary>
     public MenuBar ()
     public MenuBar ()
     {
     {
+        MenuItem._menuBar = this;
+
         TabStop = TabBehavior.NoStop;
         TabStop = TabBehavior.NoStop;
         X = 0;
         X = 0;
         Y = 0;
         Y = 0;
@@ -122,7 +124,7 @@ public class MenuBar : View, IDesignable
                         return true;
                         return true;
                     }
                     }
                    );
                    );
-        AddCommand (Command.ToggleExpandCollapse, ctx => Select ((int)ctx.KeyBinding?.Context!));
+        AddCommand (Command.ToggleExpandCollapse, ctx => Select (Menus.IndexOf (ctx.KeyBinding?.Context)));
         AddCommand (Command.Select, ctx => Run ((ctx.KeyBinding?.Context as MenuItem)?.Action));
         AddCommand (Command.Select, ctx => Run ((ctx.KeyBinding?.Context as MenuItem)?.Action));
 
 
         // Default key bindings for this view
         // Default key bindings for this view
@@ -172,19 +174,23 @@ public class MenuBar : View, IDesignable
             {
             {
                 MenuBarItem menuBarItem = Menus [i];
                 MenuBarItem menuBarItem = Menus [i];
 
 
-                if (menuBarItem?.HotKey != default (Rune))
+                if (menuBarItem?.HotKey != Key.Empty)
                 {
                 {
-                    KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.Focused, i);
-                    KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value, keyBinding);
-                    keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, i);
-                    KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value | KeyCode.AltMask, keyBinding);
+                    KeyBindings.Remove (menuBarItem!.HotKey);
+                    KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.Focused, menuBarItem);
+                    KeyBindings.Add (menuBarItem!.HotKey, keyBinding);
+                    KeyBindings.Remove (menuBarItem.HotKey.WithAlt);
+                    keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuBarItem);
+                    KeyBindings.Add (menuBarItem.HotKey.WithAlt, keyBinding);
                 }
                 }
 
 
-                if (menuBarItem?.Shortcut != KeyCode.Null)
+                if (menuBarItem?.ShortcutKey != Key.Empty)
                 {
                 {
                     // Technically this will never run because MenuBarItems don't have shortcuts
                     // Technically this will never run because MenuBarItems don't have shortcuts
-                    KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, i);
-                    KeyBindings.Add (menuBarItem.Shortcut, keyBinding);
+                    // unless the IsTopLevel is true
+                    KeyBindings.Remove (menuBarItem.ShortcutKey);
+                    KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuBarItem);
+                    KeyBindings.Add (menuBarItem.ShortcutKey, keyBinding);
                 }
                 }
 
 
                 menuBarItem?.AddShortcutKeyBindings (this);
                 menuBarItem?.AddShortcutKeyBindings (this);
@@ -1255,21 +1261,6 @@ public class MenuBar : View, IDesignable
         }
         }
     }
     }
 
 
-    private static Rune _shortcutDelimiter = new ('+');
-
-    /// <summary>Sets or gets the shortcut delimiter separator. The default is "+".</summary>
-    public static Rune ShortcutDelimiter
-    {
-        get => _shortcutDelimiter;
-        set
-        {
-            if (_shortcutDelimiter != value)
-            {
-                _shortcutDelimiter = value == default (Rune) ? new ('+') : value;
-            }
-        }
-    }
-
     /// <summary>The specifier character for the hot keys.</summary>
     /// <summary>The specifier character for the hot keys.</summary>
     public new static Rune HotKeySpecifier => (Rune)'_';
     public new static Rune HotKeySpecifier => (Rune)'_';
 
 
@@ -1321,6 +1312,10 @@ public class MenuBar : View, IDesignable
         {
         {
             OpenMenu ();
             OpenMenu ();
         }
         }
+        else if (Menus [index].IsTopLevel)
+        {
+            Run (Menus [index].Action);
+        }
         else
         else
         {
         {
             Activate (index);
             Activate (index);
@@ -1766,4 +1761,12 @@ public class MenuBar : View, IDesignable
         ];
         ];
         return true;
         return true;
     }
     }
+
+    /// <inheritdoc />
+    protected override void Dispose (bool disposing)
+    {
+        MenuItem._menuBar = null;
+
+        base.Dispose (disposing);
+    }
 }
 }

+ 74 - 5
Terminal.Gui/Views/Menu/MenuBarItem.cs

@@ -2,7 +2,7 @@ namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     <see cref="MenuBarItem"/> is a menu item on  <see cref="MenuBar"/>. MenuBarItems do not support
 ///     <see cref="MenuBarItem"/> is a menu item on  <see cref="MenuBar"/>. MenuBarItems do not support
-///     <see cref="MenuItem.Shortcut"/>.
+///     <see cref="MenuItem.ShortcutKey"/>.
 /// </summary>
 /// </summary>
 public class MenuBarItem : MenuItem
 public class MenuBarItem : MenuItem
 {
 {
@@ -100,11 +100,9 @@ public class MenuBarItem : MenuItem
         {
         {
             // For MenuBar only add shortcuts for submenus
             // For MenuBar only add shortcuts for submenus
 
 
-            if (menuItem.Shortcut != KeyCode.Null)
+            if (menuItem.ShortcutKey != Key.Empty)
             {
             {
-                KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
-                menuBar.KeyBindings.Remove (menuItem.Shortcut);
-                menuBar.KeyBindings.Add (menuItem.Shortcut, keyBinding);
+                menuItem.UpdateShortcutKeyBinding (Key.Empty);
             }
             }
 
 
             SubMenu (menuItem)?.AddShortcutKeyBindings (menuBar);
             SubMenu (menuItem)?.AddShortcutKeyBindings (menuBar);
@@ -176,4 +174,75 @@ public class MenuBarItem : MenuItem
         title ??= string.Empty;
         title ??= string.Empty;
         Title = title;
         Title = title;
     }
     }
+
+    /// <summary>
+    /// Add a <see cref="MenuBarItem"/> dynamically into the <see cref="MenuBar"/><c>.Menus</c>.
+    /// </summary>
+    /// <param name="menuItem"></param>
+    public void AddMenuBarItem (MenuItem menuItem = null)
+    {
+        if (menuItem is null)
+        {
+            MenuBarItem [] menus = _menuBar.Menus;
+            Array.Resize (ref menus, menus.Length + 1);
+            menus [^1] = this;
+            _menuBar.Menus = menus;
+        }
+        else
+        {
+            MenuItem [] childrens = Children ?? [];
+            Array.Resize (ref childrens, childrens.Length + 1);
+            childrens [^1] = menuItem;
+            Children = childrens;
+        }
+    }
+
+    /// <inheritdoc />
+    public override void RemoveMenuItem ()
+    {
+        if (Children is { })
+        {
+            foreach (MenuItem menuItem in Children)
+            {
+                if (menuItem.ShortcutKey != Key.Empty)
+                {
+                    // Remove an existent ShortcutKey
+                    _menuBar?.KeyBindings.Remove (menuItem.ShortcutKey);
+                }
+            }
+        }
+
+        if (ShortcutKey != Key.Empty)
+        {
+            // Remove an existent ShortcutKey
+            _menuBar?.KeyBindings.Remove (ShortcutKey);
+        }
+
+        var index = _menuBar!.Menus.IndexOf (this);
+        if (index > -1)
+        {
+            if (_menuBar!.Menus [index].HotKey != Key.Empty)
+            {
+                // Remove an existent HotKey
+                _menuBar?.KeyBindings.Remove (HotKey.WithAlt);
+            }
+
+            _menuBar!.Menus [index] = null;
+        }
+
+        var i = 0;
+
+        foreach (MenuBarItem m in _menuBar.Menus)
+        {
+            if (m != null)
+            {
+                _menuBar.Menus [i] = m;
+                i++;
+            }
+        }
+
+        MenuBarItem [] menus = _menuBar.Menus;
+        Array.Resize (ref menus, menus.Length - 1);
+        _menuBar.Menus = menus;
+    }
 }
 }

+ 170 - 76
Terminal.Gui/Views/Menu/MenuItem.cs

@@ -6,31 +6,25 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 public class MenuItem
 public class MenuItem
 {
 {
-    private readonly ShortcutHelper _shortcutHelper;
-    private bool _allowNullChecked;
-    private MenuItemCheckStyle _checkType;
+    internal static MenuBar _menuBar;
 
 
-    private string _title;
-
-    // TODO: Update to use Key instead of KeyCode
     /// <summary>Initializes a new instance of <see cref="MenuItem"/></summary>
     /// <summary>Initializes a new instance of <see cref="MenuItem"/></summary>
-    public MenuItem (KeyCode shortcut = KeyCode.Null) : this ("", "", null, null, null, shortcut) { }
+    public MenuItem (Key shortcutKey = null) : this ("", "", null, null, null, shortcutKey) { }
 
 
-    // TODO: Update to use Key instead of KeyCode
     /// <summary>Initializes a new instance of <see cref="MenuItem"/>.</summary>
     /// <summary>Initializes a new instance of <see cref="MenuItem"/>.</summary>
     /// <param name="title">Title for the menu item.</param>
     /// <param name="title">Title for the menu item.</param>
     /// <param name="help">Help text to display.</param>
     /// <param name="help">Help text to display.</param>
     /// <param name="action">Action to invoke when the menu item is activated.</param>
     /// <param name="action">Action to invoke when the menu item is activated.</param>
     /// <param name="canExecute">Function to determine if the action can currently be executed.</param>
     /// <param name="canExecute">Function to determine if the action can currently be executed.</param>
     /// <param name="parent">The <see cref="Parent"/> of this menu item.</param>
     /// <param name="parent">The <see cref="Parent"/> of this menu item.</param>
-    /// <param name="shortcut">The <see cref="Shortcut"/> keystroke combination.</param>
+    /// <param name="shortcutKey">The <see cref="ShortcutKey"/> keystroke combination.</param>
     public MenuItem (
     public MenuItem (
         string title,
         string title,
         string help,
         string help,
         Action action,
         Action action,
         Func<bool> canExecute = null,
         Func<bool> canExecute = null,
         MenuItem parent = null,
         MenuItem parent = null,
-        KeyCode shortcut = KeyCode.Null
+        Key shortcutKey = null
     )
     )
     {
     {
         Title = title ?? "";
         Title = title ?? "";
@@ -38,14 +32,20 @@ public class MenuItem
         Action = action;
         Action = action;
         CanExecute = canExecute;
         CanExecute = canExecute;
         Parent = parent;
         Parent = parent;
-        _shortcutHelper = new ();
 
 
-        if (shortcut != KeyCode.Null)
+        if (Parent is { } && Parent.ShortcutKey != Key.Empty)
         {
         {
-            Shortcut = shortcut;
+            Parent.ShortcutKey = Key.Empty;
         }
         }
+        // Setter will ensure Key.Empty if it's null
+        ShortcutKey = shortcutKey;
     }
     }
 
 
+    private bool _allowNullChecked;
+    private MenuItemCheckStyle _checkType;
+
+    private string _title;
+
     /// <summary>Gets or sets the action to be invoked when the menu item is triggered.</summary>
     /// <summary>Gets or sets the action to be invoked when the menu item is triggered.</summary>
     /// <value>Method to invoke.</value>
     /// <value>Method to invoke.</value>
     public Action Action { get; set; }
     public Action Action { get; set; }
@@ -104,6 +104,12 @@ public class MenuItem
     /// <value>The help text.</value>
     /// <value>The help text.</value>
     public string Help { get; set; }
     public string Help { get; set; }
 
 
+    /// <summary>
+    ///     Returns <see langword="true"/> if the menu item is enabled. This method is a wrapper around
+    ///     <see cref="CanExecute"/>.
+    /// </summary>
+    public bool IsEnabled () { return CanExecute?.Invoke () ?? true; }
+
     /// <summary>Gets the parent for this <see cref="MenuItem"/>.</summary>
     /// <summary>Gets the parent for this <see cref="MenuItem"/>.</summary>
     /// <value>The parent.</value>
     /// <value>The parent.</value>
     public MenuItem Parent { get; set; }
     public MenuItem Parent { get; set; }
@@ -125,46 +131,6 @@ public class MenuItem
         }
         }
     }
     }
 
 
-    /// <summary>Gets if this <see cref="MenuItem"/> is from a sub-menu.</summary>
-    internal bool IsFromSubMenu => Parent != null;
-
-    internal int TitleLength => GetMenuBarItemLength (Title);
-
-    // 
-    // ┌─────────────────────────────┐
-    // │ Quit  Quit UI Catalog  Ctrl+Q │
-    // └─────────────────────────────┘
-    // ┌─────────────────┐
-    // │ ◌ TopLevel Alt+T │
-    // └─────────────────┘
-    // TODO: Replace the `2` literals with named constants 
-    internal int Width => 1
-                          + // space before Title
-                          TitleLength
-                          + 2
-                          + // space after Title - BUGBUG: This should be 1 
-                          (Checked == true || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio)
-                               ? 2
-                               : 0)
-                          + // check glyph + space 
-                          (Help.GetColumns () > 0 ? 2 + Help.GetColumns () : 0)
-                          + // Two spaces before Help
-                          (ShortcutTag.GetColumns () > 0
-                               ? 2 + ShortcutTag.GetColumns ()
-                               : 0); // Pad two spaces before shortcut tag (which are also aligned right)
-
-    /// <summary>Merely a debugging aid to see the interaction with main.</summary>
-    internal bool GetMenuBarItem () { return IsFromSubMenu; }
-
-    /// <summary>Merely a debugging aid to see the interaction with main.</summary>
-    internal MenuItem GetMenuItem () { return this; }
-
-    /// <summary>
-    ///     Returns <see langword="true"/> if the menu item is enabled. This method is a wrapper around
-    ///     <see cref="CanExecute"/>.
-    /// </summary>
-    public bool IsEnabled () { return CanExecute?.Invoke () ?? true; }
-
     /// <summary>
     /// <summary>
     ///     Toggle the <see cref="Checked"/> between three states if <see cref="AllowNullChecked"/> is
     ///     Toggle the <see cref="Checked"/> between three states if <see cref="AllowNullChecked"/> is
     ///     <see langword="true"/> or between two states if <see cref="AllowNullChecked"/> is <see langword="false"/>.
     ///     <see langword="true"/> or between two states if <see cref="AllowNullChecked"/> is <see langword="false"/>.
@@ -193,6 +159,40 @@ public class MenuItem
         }
         }
     }
     }
 
 
+    /// <summary>Merely a debugging aid to see the interaction with main.</summary>
+    internal bool GetMenuBarItem () { return IsFromSubMenu; }
+
+    /// <summary>Merely a debugging aid to see the interaction with main.</summary>
+    internal MenuItem GetMenuItem () { return this; }
+
+    /// <summary>Gets if this <see cref="MenuItem"/> is from a sub-menu.</summary>
+    internal bool IsFromSubMenu => Parent != null;
+
+    internal int TitleLength => GetMenuBarItemLength (Title);
+
+    // 
+    // ┌─────────────────────────────┐
+    // │ Quit  Quit UI Catalog  Ctrl+Q │
+    // └─────────────────────────────┘
+    // ┌─────────────────┐
+    // │ ◌ TopLevel Alt+T │
+    // └─────────────────┘
+    // TODO: Replace the `2` literals with named constants
+    internal int Width => 1
+                          + // space before Title
+                          TitleLength
+                          + 2
+                          + // space after Title - BUGBUG: This should be 1
+                          (Checked == true || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio)
+                               ? 2
+                               : 0)
+                          + // check glyph + space 
+                          (Help.GetColumns () > 0 ? 2 + Help.GetColumns () : 0)
+                          + // Two spaces before Help
+                          (ShortcutTag.GetColumns () > 0
+                               ? 2 + ShortcutTag.GetColumns ()
+                               : 0); // Pad two spaces before shortcut tag (which are also aligned right)
+
     private static int GetMenuBarItemLength (string title)
     private static int GetMenuBarItemLength (string title)
     {
     {
         return title.EnumerateRunes ()
         return title.EnumerateRunes ()
@@ -202,21 +202,32 @@ public class MenuItem
 
 
     #region Keyboard Handling
     #region Keyboard Handling
 
 
-    // TODO: Update to use Key instead of Rune
+    private Key _hotKey = Key.Empty;
+
     /// <summary>
     /// <summary>
     ///     The HotKey is used to activate a <see cref="MenuItem"/> with the keyboard. HotKeys are defined by prefixing the
     ///     The HotKey is used to activate a <see cref="MenuItem"/> with the keyboard. HotKeys are defined by prefixing the
     ///     <see cref="Title"/> of a MenuItem with an underscore ('_').
     ///     <see cref="Title"/> of a MenuItem with an underscore ('_').
     ///     <para>
     ///     <para>
     ///         Pressing Alt-Hotkey for a <see cref="MenuBarItem"/> (menu items on the menu bar) works even if the menu is
     ///         Pressing Alt-Hotkey for a <see cref="MenuBarItem"/> (menu items on the menu bar) works even if the menu is
-    ///         not active). Once a menu has focus and is active, pressing just the HotKey will activate the MenuItem.
+    ///         not active. Once a menu has focus and is active, pressing just the HotKey will activate the MenuItem.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         For example for a MenuBar with a "_File" MenuBarItem that contains a "_New" MenuItem, Alt-F will open the
     ///         For example for a MenuBar with a "_File" MenuBarItem that contains a "_New" MenuItem, Alt-F will open the
     ///         File menu. Pressing the N key will then activate the New MenuItem.
     ///         File menu. Pressing the N key will then activate the New MenuItem.
     ///     </para>
     ///     </para>
-    ///     <para>See also <see cref="Shortcut"/> which enable global key-bindings to menu items.</para>
+    ///     <para>See also <see cref="ShortcutKey"/> which enable global key-bindings to menu items.</para>
     /// </summary>
     /// </summary>
-    public Rune HotKey { get; set; }
+    public Key HotKey
+    {
+        get => _hotKey;
+        private set
+        {
+            var oldKey = _hotKey ?? Key.Empty;
+            _hotKey = value ?? Key.Empty;
+            UpdateHotKeyBinding (oldKey);
+        }
+    }
+
     private void GetHotKey ()
     private void GetHotKey ()
     {
     {
         var nextIsHot = false;
         var nextIsHot = false;
@@ -227,47 +238,130 @@ public class MenuItem
             {
             {
                 nextIsHot = true;
                 nextIsHot = true;
             }
             }
-            else
+            else if (nextIsHot)
             {
             {
-                if (nextIsHot)
-                {
-                    HotKey = (Rune)char.ToUpper (x);
+                    HotKey = char.ToLower (x);
 
 
-                    break;
-                }
-
-                nextIsHot = false;
-                HotKey = default (Rune);
+                    return;
             }
             }
         }
         }
+
+        HotKey = Key.Empty;
     }
     }
 
 
-    // TODO: Update to use Key instead of KeyCode
+    private Key _shortcutKey = Key.Empty;
+
     /// <summary>
     /// <summary>
     ///     Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the
     ///     Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the
     ///     <see cref="View"/> that is the parent of the <see cref="MenuBar"/> or <see cref="ContextMenu"/> this
     ///     <see cref="View"/> that is the parent of the <see cref="MenuBar"/> or <see cref="ContextMenu"/> this
     ///     <see cref="MenuItem"/>.
     ///     <see cref="MenuItem"/>.
     ///     <para>
     ///     <para>
-    ///         The <see cref="KeyCode"/> will be drawn on the MenuItem to the right of the <see cref="Title"/> and
+    ///         The <see cref="Key"/> will be drawn on the MenuItem to the right of the <see cref="Title"/> and
     ///         <see cref="Help"/> text. See <see cref="ShortcutTag"/>.
     ///         <see cref="Help"/> text. See <see cref="ShortcutTag"/>.
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
-    public KeyCode Shortcut
+    public Key ShortcutKey
     {
     {
-        get => _shortcutHelper.Shortcut;
+        get => _shortcutKey;
         set
         set
         {
         {
-            if (_shortcutHelper.Shortcut != value && (ShortcutHelper.PostShortcutValidation (value) || value == KeyCode.Null))
+            var oldKey = _shortcutKey ?? Key.Empty;
+            _shortcutKey = value ?? Key.Empty;
+            UpdateShortcutKeyBinding (oldKey);
+        }
+    }
+
+    /// <summary>Gets the text describing the keystroke combination defined by <see cref="ShortcutKey"/>.</summary>
+    public string ShortcutTag => ShortcutKey != Key.Empty ? ShortcutKey.ToString () : string.Empty;
+
+    private void UpdateHotKeyBinding (Key oldKey)
+    {
+        if (_menuBar is null || _menuBar?.IsInitialized == false)
+        {
+            return;
+        }
+
+        if (oldKey != Key.Empty)
+        {
+            var index = _menuBar.Menus?.IndexOf (this);
+
+            if (index > -1)
+            {
+                _menuBar.KeyBindings.Remove (oldKey.WithAlt);
+            }
+        }
+
+        if (HotKey != Key.Empty)
+        {
+            var index = _menuBar.Menus?.IndexOf (this);
+
+            if (index > -1)
             {
             {
-                _shortcutHelper.Shortcut = value;
+                _menuBar.KeyBindings.Remove (HotKey.WithAlt);
+                KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, this);
+                _menuBar.KeyBindings.Add (HotKey.WithAlt, keyBinding);
             }
             }
         }
         }
     }
     }
 
 
-    /// <summary>Gets the text describing the keystroke combination defined by <see cref="Shortcut"/>.</summary>
-    public string ShortcutTag => _shortcutHelper.Shortcut == KeyCode.Null
-                                     ? string.Empty
-                                     : Key.ToString (_shortcutHelper.Shortcut, MenuBar.ShortcutDelimiter);
+    internal void UpdateShortcutKeyBinding (Key oldKey)
+    {
+        if (_menuBar is null)
+        {
+            return;
+        }
+
+        if (oldKey != Key.Empty)
+        {
+            _menuBar.KeyBindings.Remove (oldKey);
+        }
+
+        if (ShortcutKey != Key.Empty)
+        {
+            KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, this);
+            // Remove an existent ShortcutKey
+            _menuBar?.KeyBindings.Remove (ShortcutKey);
+            _menuBar?.KeyBindings.Add (ShortcutKey, keyBinding);
+        }
+    }
 
 
     #endregion Keyboard Handling
     #endregion Keyboard Handling
+
+    /// <summary>
+    /// Removes a <see cref="MenuItem"/> dynamically from the <see cref="Parent"/>.
+    /// </summary>
+    public virtual void RemoveMenuItem ()
+    {
+        if (Parent is { })
+        {
+            MenuItem [] childrens = ((MenuBarItem)Parent).Children;
+            var i = 0;
+
+            foreach (MenuItem c in childrens)
+            {
+                if (c != this)
+                {
+                    childrens [i] = c;
+                    i++;
+                }
+            }
+
+            Array.Resize (ref childrens, childrens.Length - 1);
+
+            if (childrens.Length == 0)
+            {
+                ((MenuBarItem)Parent).Children = null;
+            }
+            else
+            {
+                ((MenuBarItem)Parent).Children = childrens;
+            }
+        }
+
+        if (ShortcutKey != Key.Empty)
+        {
+            // Remove an existent ShortcutKey
+            _menuBar?.KeyBindings.Remove (ShortcutKey);
+        }
+    }
 }
 }

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

@@ -1,7 +1,4 @@
-using System.Diagnostics;
-using System.Text.Json.Serialization;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     MessageBox displays a modal message to the user, with a title, a message and a series of options that the user
 ///     MessageBox displays a modal message to the user, with a title, a message and a series of options that the user
@@ -32,13 +29,11 @@ public static class MessageBox
     ///     <see cref="ConfigurationManager"/>.
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single; // Default is set in config.json
 
 
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     /// <remarks>This property can be set in a Theme.</remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<Alignment>))]
     public static Alignment DefaultButtonAlignment { get; set; } = Alignment.Center; // Default is set in config.json
     public static Alignment DefaultButtonAlignment { get; set; } = Alignment.Center; // Default is set in config.json
 
 
     /// <summary>
     /// <summary>
@@ -405,11 +400,6 @@ public static class MessageBox
         d.TextFormatter.WordWrap = wrapMessage;
         d.TextFormatter.WordWrap = wrapMessage;
         d.TextFormatter.MultiLine = !wrapMessage;
         d.TextFormatter.MultiLine = !wrapMessage;
 
 
-        d.ColorScheme = new ColorScheme (d.ColorScheme)
-        {
-            Focus = d.ColorScheme.Normal
-        };
-
         // Setup actions
         // Setup actions
         Clicked = -1;
         Clicked = -1;
 
 
@@ -423,11 +413,6 @@ public static class MessageBox
                              Clicked = buttonId;
                              Clicked = buttonId;
                              Application.RequestStop ();
                              Application.RequestStop ();
                          };
                          };
-
-            if (b.IsDefault)
-            {
-                b.SetFocus ();
-            }
         }
         }
 
 
         // Run the modal; do not shut down the mainloop driver when done
         // Run the modal; do not shut down the mainloop driver when done

+ 251 - 0
Terminal.Gui/Views/NumericUpDown.cs

@@ -0,0 +1,251 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui;
+
+/// <summary>
+///     Enables the user to increase or decrease a value with the mouse or keyboard.
+/// </summary>
+/// <remarks>
+///     Supports the following types: <see cref="int"/>, <see cref="long"/>, <see cref="double"/>, <see cref="double"/>,
+///     <see cref="decimal"/>. Attempting to use any other type will result in an <see cref="InvalidOperationException"/>.
+/// </remarks>
+public class NumericUpDown<T> : View where T : notnull
+{
+    private readonly Button _down;
+
+    // TODO: Use a TextField instead of a Label
+    private readonly View _number;
+    private readonly Button _up;
+
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="NumericUpDown{T}"/> class.
+    /// </summary>
+    /// <exception cref="InvalidOperationException"></exception>
+    public NumericUpDown ()
+    {
+        Type type = typeof (T);
+
+        if (!(type == typeof (object)
+              || type == typeof (int)
+              || type == typeof (long)
+              || type == typeof (double)
+              || type == typeof (float)
+              || type == typeof (double)
+              || type == typeof (decimal)))
+        {
+            throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
+        }
+
+        // `object` is supported only for AllViewsTester
+        if (type != typeof (object))
+        {
+            Increment = (dynamic)1;
+            Value = (dynamic)0;
+        }
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        _down = new ()
+        {
+            Height = 1,
+            Width = 1,
+            NoPadding = true,
+            NoDecorations = true,
+            Title = $"{Glyphs.DownArrow}",
+            WantContinuousButtonPressed = true,
+            CanFocus = false,
+            ShadowStyle = ShadowStyle.None
+        };
+
+        _number = new ()
+        {
+            Text = Value?.ToString () ?? "Err",
+            X = Pos.Right (_down),
+            Y = Pos.Top (_down),
+            Width = Dim.Auto (minimumContentDim: Dim.Func (() => string.Format (Format, Value).Length)),
+            Height = 1,
+            TextAlignment = Alignment.Center,
+            CanFocus = true
+        };
+
+        _up = new ()
+        {
+            X = Pos.Right (_number),
+            Y = Pos.Top (_number),
+            Height = 1,
+            Width = 1,
+            NoPadding = true,
+            NoDecorations = true,
+            Title = $"{Glyphs.UpArrow}",
+            WantContinuousButtonPressed = true,
+            CanFocus = false,
+            ShadowStyle = ShadowStyle.None
+        };
+
+        CanFocus = true;
+
+        _down.Accept += OnDownButtonOnAccept;
+        _up.Accept += OnUpButtonOnAccept;
+
+        Add (_down, _number, _up);
+
+        AddCommand (
+                    Command.ScrollUp,
+                    () =>
+                    {
+                        if (type == typeof (object))
+                        {
+                            return false;
+                        }
+
+                        if (Value is { })
+                        {
+                            Value = (dynamic)Value + (dynamic)Increment;
+                        }
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.ScrollDown,
+                    () =>
+                    {
+                        if (type == typeof (object))
+                        {
+                            return false;
+                        }
+
+                        if (Value is { })
+                        {
+                            Value = (dynamic)Value - (dynamic)Increment;
+                        }
+
+                        return true;
+                    });
+
+        KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
+        KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
+
+        SetText ();
+
+        return;
+
+        void OnDownButtonOnAccept (object? s, HandledEventArgs e) { InvokeCommand (Command.ScrollDown); }
+
+        void OnUpButtonOnAccept (object? s, HandledEventArgs e) { InvokeCommand (Command.ScrollUp); }
+    }
+
+    private T _value = default!;
+
+    /// <summary>
+    ///     Gets or sets the value that will be incremented or decremented.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         <see cref="ValueChanging"/> and <see cref="ValueChanged"/> events are raised when the value changes.
+    ///         The <see cref="ValueChanging"/> event can be canceled the change setting
+    ///         <see cref="CancelEventArgs{T}"/><c>.Cancel</c> to <see langword="true"/>.
+    ///     </para>
+    /// </remarks>
+    public T Value
+    {
+        get => _value;
+        set
+        {
+            if (_value.Equals (value))
+            {
+                return;
+            }
+
+            T oldValue = value;
+            CancelEventArgs<T> args = new (in _value, ref value);
+            ValueChanging?.Invoke (this, args);
+
+            if (args.Cancel)
+            {
+                return;
+            }
+
+            _value = value;
+            SetText ();
+            ValueChanged?.Invoke (this, new (in value));
+        }
+    }
+
+    /// <summary>
+    ///     Raised when the value is about to change. Set <see cref="CancelEventArgs{T}"/><c>.Cancel</c> to true to prevent the
+    ///     change.
+    /// </summary>
+    public event EventHandler<CancelEventArgs<T>>? ValueChanging;
+
+    /// <summary>
+    ///     Raised when the value has changed.
+    /// </summary>
+    public event EventHandler<EventArgs<T>>? ValueChanged;
+
+    private string _format = "{0}";
+
+    /// <summary>
+    ///     Gets or sets the format string used to display the value. The default is "{0}".
+    /// </summary>
+    public string Format
+    {
+        get => _format;
+        set
+        {
+            if (_format == value)
+            {
+                return;
+            }
+
+            _format = value;
+
+            FormatChanged?.Invoke (this, new (value));
+            SetText ();
+        }
+    }
+
+    /// <summary>
+    ///     Raised when <see cref="Format"/> has changed.
+    /// </summary>
+    public event EventHandler<EventArgs<string>>? FormatChanged;
+
+    private void SetText ()
+    {
+        _number.Text = string.Format (Format, _value);
+        Text = _number.Text;
+    }
+
+    private T _increment;
+
+    /// <summary>
+    /// </summary>
+    public T Increment
+    {
+        get => _increment;
+        set
+        {
+            if ((dynamic)_increment == (dynamic)value)
+            {
+                return;
+            }
+
+            _increment = value;
+
+            IncrementChanged?.Invoke (this, new (value));
+        }
+    }
+
+    /// <summary>
+    ///     Raised when <see cref="Increment"/> has changed.
+    /// </summary>
+    public event EventHandler<EventArgs<T>>? IncrementChanged;
+}
+
+/// <summary>
+///     Enables the user to increase or decrease an <see langword="int"/> by clicking on the up or down buttons.
+/// </summary>
+public class NumericUpDown : NumericUpDown<int>
+{ }

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

@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     A <see cref="Toplevel"/> <see cref="View"/> with <see cref="View.BorderStyle"/> set to
 ///     A <see cref="Toplevel"/> <see cref="View"/> with <see cref="View.BorderStyle"/> set to
@@ -75,6 +73,5 @@ public class Window : Toplevel
     ///     s.
     ///     s.
     /// </remarks>
     /// </remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    [JsonConverter (typeof (JsonStringEnumConverter<LineStyle>))]
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 }
 }

+ 7 - 0
Terminal.sln

@@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{B8
 		.editorconfig = .editorconfig
 		.editorconfig = .editorconfig
 		.filenesting.json = .filenesting.json
 		.filenesting.json = .filenesting.json
 		.gitignore = .gitignore
 		.gitignore = .gitignore
+		GitVersion.yml = GitVersion.yml
 		global.json = global.json
 		global.json = global.json
 		nuget.config = nuget.config
 		nuget.config = nuget.config
 		Terminal.sln.DotSettings = Terminal.sln.DotSettings
 		Terminal.sln.DotSettings = Terminal.sln.DotSettings
@@ -45,6 +46,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{C7A51224-5
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SelfContained", "SelfContained\SelfContained.csproj", "{524DEA78-7E7C-474D-B42D-52ED4C04FF14}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SelfContained", "SelfContained\SelfContained.csproj", "{524DEA78-7E7C-474D-B42D-52ED4C04FF14}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeAot", "NativeAot\NativeAot.csproj", "{E6D716C6-AC94-4150-B10A-44AE13F79344}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -79,6 +82,10 @@ Global
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Release|Any CPU.Build.0 = Release|Any CPU
 		{524DEA78-7E7C-474D-B42D-52ED4C04FF14}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E6D716C6-AC94-4150-B10A-44AE13F79344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E6D716C6-AC94-4150-B10A-44AE13F79344}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E6D716C6-AC94-4150-B10A-44AE13F79344}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E6D716C6-AC94-4150-B10A-44AE13F79344}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

+ 9 - 5
UICatalog/Scenarios/AdornmentEditor.cs

@@ -78,10 +78,10 @@ public class AdornmentEditor : View
         AdornmentChanged?.Invoke (this, EventArgs.Empty);
         AdornmentChanged?.Invoke (this, EventArgs.Empty);
     }
     }
 
 
-    private Buttons.NumericUpDown<int> _topEdit;
-    private Buttons.NumericUpDown<int> _leftEdit;
-    private Buttons.NumericUpDown<int> _bottomEdit;
-    private Buttons.NumericUpDown<int> _rightEdit;
+    private NumericUpDown<int> _topEdit;
+    private NumericUpDown<int> _leftEdit;
+    private NumericUpDown<int> _bottomEdit;
+    private NumericUpDown<int> _rightEdit;
 
 
     public AdornmentEditor ()
     public AdornmentEditor ()
     {
     {
@@ -102,6 +102,7 @@ public class AdornmentEditor : View
         _topEdit = new ()
         _topEdit = new ()
         {
         {
             X = Pos.Center (), Y = 0,
             X = Pos.Center (), Y = 0,
+            Format = "{0, 2}",
             Enabled = false
             Enabled = false
         };
         };
 
 
@@ -110,7 +111,8 @@ public class AdornmentEditor : View
 
 
         _leftEdit = new ()
         _leftEdit = new ()
         {
         {
-            X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Digits) - 2, Y = Pos.Bottom (_topEdit),
+            X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Text.Length) - 2, Y = Pos.Bottom (_topEdit),
+            Format = _topEdit.Format,
             Enabled = false
             Enabled = false
         };
         };
 
 
@@ -120,6 +122,7 @@ public class AdornmentEditor : View
         _rightEdit = new ()
         _rightEdit = new ()
         {
         {
             X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit),
             X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit),
+            Format = _topEdit.Format,
             Enabled = false
             Enabled = false
         };
         };
 
 
@@ -129,6 +132,7 @@ public class AdornmentEditor : View
         _bottomEdit = new ()
         _bottomEdit = new ()
         {
         {
             X = Pos.Center (), Y = Pos.Bottom (_leftEdit),
             X = Pos.Center (), Y = Pos.Bottom (_leftEdit),
+            Format = _topEdit.Format,
             Enabled = false
             Enabled = false
         };
         };
 
 

+ 4 - 4
UICatalog/Scenarios/AdornmentsEditor.cs

@@ -120,9 +120,9 @@ public class AdornmentsEditor : View
         Add (_paddingEditor);
         Add (_paddingEditor);
 
 
         _diagPaddingCheckBox = new () { Text = "_Diagnostic Padding" };
         _diagPaddingCheckBox = new () { Text = "_Diagnostic Padding" };
-        _diagPaddingCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding) ? CheckState.Checked : CheckState.UnChecked;
+        _diagPaddingCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding) ? CheckState.Checked : CheckState.UnChecked;
 
 
-        _diagPaddingCheckBox.Toggle += (s, e) =>
+        _diagPaddingCheckBox.CheckedStateChanging += (s, e) =>
                                        {
                                        {
                                            if (e.NewValue == CheckState.Checked)
                                            if (e.NewValue == CheckState.Checked)
                                            {
                                            {
@@ -138,9 +138,9 @@ public class AdornmentsEditor : View
         _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
         _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
 
 
         _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
         _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
-        _diagRulerCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
+        _diagRulerCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
 
 
-        _diagRulerCheckBox.Toggle += (s, e) =>
+        _diagRulerCheckBox.CheckedStateChanging += (s, e) =>
                                      {
                                      {
                                          if (e.NewValue == CheckState.Checked)
                                          if (e.NewValue == CheckState.Checked)
                                          {
                                          {

+ 6 - 6
UICatalog/Scenarios/BorderEditor.cs

@@ -20,9 +20,9 @@ public class BorderEditor : AdornmentEditor
 
 
     private void BorderEditor_AdornmentChanged (object sender, EventArgs e)
     private void BorderEditor_AdornmentChanged (object sender, EventArgs e)
     {
     {
-        _ckbTitle.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked;
+        _ckbTitle.CheckedState = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked;
         _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
         _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
-        _ckbGradient.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked;
+        _ckbGradient.CheckedState = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked;
     }
     }
 
 
     private void BorderEditor_Initialized (object sender, EventArgs e)
     private void BorderEditor_Initialized (object sender, EventArgs e)
@@ -51,13 +51,13 @@ public class BorderEditor : AdornmentEditor
             X = 0,
             X = 0,
             Y = Pos.Bottom (_rbBorderStyle),
             Y = Pos.Bottom (_rbBorderStyle),
 
 
-            State = CheckState.Checked,
+            CheckedState = CheckState.Checked,
             SuperViewRendersLineCanvas = true,
             SuperViewRendersLineCanvas = true,
             Text = "Title",
             Text = "Title",
             Enabled = AdornmentToEdit is { }
             Enabled = AdornmentToEdit is { }
         };
         };
 
 
-        _ckbTitle.Toggle += OnCkbTitleOnToggle;
+        _ckbTitle.CheckedStateChanging += OnCkbTitleOnToggle;
         Add (_ckbTitle);
         Add (_ckbTitle);
 
 
         _ckbGradient = new ()
         _ckbGradient = new ()
@@ -65,13 +65,13 @@ public class BorderEditor : AdornmentEditor
             X = 0,
             X = 0,
             Y = Pos.Bottom (_ckbTitle),
             Y = Pos.Bottom (_ckbTitle),
 
 
-            State = CheckState.Checked,
+            CheckedState = CheckState.Checked,
             SuperViewRendersLineCanvas = true,
             SuperViewRendersLineCanvas = true,
             Text = "Gradient",
             Text = "Gradient",
             Enabled = AdornmentToEdit is { }
             Enabled = AdornmentToEdit is { }
         };
         };
 
 
-        _ckbGradient.Toggle += OnCkbGradientOnToggle;
+        _ckbGradient.CheckedStateChanging += OnCkbGradientOnToggle;
         Add (_ckbGradient);
         Add (_ckbGradient);
 
 
         return;
         return;

+ 17 - 159
UICatalog/Scenarios/Buttons.cs

@@ -336,8 +336,6 @@ public class Buttons : Scenario
             Value = 69,
             Value = 69,
             X = Pos.Right (label) + 1,
             X = Pos.Right (label) + 1,
             Y = Pos.Top (label),
             Y = Pos.Top (label),
-            Width = 5,
-            Height = 1
         };
         };
         numericUpDown.ValueChanged += NumericUpDown_ValueChanged;
         numericUpDown.ValueChanged += NumericUpDown_ValueChanged;
 
 
@@ -385,169 +383,29 @@ public class Buttons : Scenario
             X = Pos.Right (repeatButton) + 1,
             X = Pos.Right (repeatButton) + 1,
             Y = Pos.Top (repeatButton),
             Y = Pos.Top (repeatButton),
             Title = "Enabled",
             Title = "Enabled",
-            State = CheckState.Checked
+            CheckedState = CheckState.Checked
         };
         };
-        enableCB.Toggle += (s, e) => { repeatButton.Enabled = !repeatButton.Enabled; };
+        enableCB.CheckedStateChanging += (s, e) => { repeatButton.Enabled = !repeatButton.Enabled; };
         main.Add (label, repeatButton, enableCB);
         main.Add (label, repeatButton, enableCB);
 
 
-        main.Ready += (s, e) => radioGroup.Refresh ();
-        Application.Run (main);
-        main.Dispose ();
-        Application.Shutdown ();
-    }
-
-    /// <summary>
-    /// Enables the user to increase or decrease a value by clicking on the up or down buttons.
-    /// </summary>
-    /// <remarks>
-    ///     Supports the following types: <see cref="int"/>, <see cref="long"/>, <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>.
-    ///     Supports only one digit of precision.
-    /// </remarks>
-    public class NumericUpDown<T> : View
-    {
-        private readonly Button _down;
-        // TODO: Use a TextField instead of a Label
-        private readonly View _number;
-        private readonly Button _up;
-
-        public NumericUpDown ()
+        var decNumericUpDown = new NumericUpDown<int>
         {
         {
-            Type type = typeof (T);
-            if (!(type == typeof (int) || type == typeof (long) || type == typeof (float) || type == typeof (double) || type == typeof (decimal)))
-            {
-                throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
-            }
-
-            Width = Dim.Auto (DimAutoStyle.Content); //Dim.Function (() => Digits + 2); // button + 3 for number + button
-            Height = Dim.Auto (DimAutoStyle.Content);
-
-            _down = new ()
-            {
-                Height = 1,
-                Width = 1,
-                NoPadding = true,
-                NoDecorations = true,
-                Title = $"{CM.Glyphs.DownArrow}",
-                WantContinuousButtonPressed = true,
-                CanFocus = false,
-                ShadowStyle = ShadowStyle.None,
-            };
-
-            _number = new ()
-            {
-                Text = Value.ToString (),
-                X = Pos.Right (_down),
-                Y = Pos.Top (_down),
-                Width = Dim.Func (() => Digits),
-                Height = 1,
-                TextAlignment = Alignment.Center,
-                CanFocus = true
-            };
-
-            _up = new ()
-            {
-                X = Pos.AnchorEnd (),
-                Y = Pos.Top (_number),
-                Height = 1,
-                Width = 1,
-                NoPadding = true,
-                NoDecorations = true,
-                Title = $"{CM.Glyphs.UpArrow}",
-                WantContinuousButtonPressed = true,
-                CanFocus = false,
-                ShadowStyle = ShadowStyle.None,
-            };
-
-            CanFocus = true;
-
-            _down.Accept += OnDownButtonOnAccept;
-            _up.Accept += OnUpButtonOnAccept;
-
-            Add (_down, _number, _up);
-
-
-            AddCommand (Command.ScrollUp, () =>
-                                          {
-                                              Value = (dynamic)Value + 1;
-                                              _number.Text = Value.ToString ();
-
-                                              return true;
-                                          });
-            AddCommand (Command.ScrollDown, () =>
-                                            {
-                                                Value = (dynamic)Value - 1;
-                                                _number.Text = Value.ToString ();
-
-                                                return true;
-                                            });
-
-            KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
-            KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
-
-            return;
-
-            void OnDownButtonOnAccept (object s, HandledEventArgs e)
-            {
-                InvokeCommand (Command.ScrollDown);
-            }
-
-            void OnUpButtonOnAccept (object s, HandledEventArgs e)
-            {
-                InvokeCommand (Command.ScrollUp);
-            }
-        }
-
-        private void _up_Enter (object sender, FocusEventArgs e)
-        {
-            throw new NotImplementedException ();
-        }
-
-        private T _value;
+            Value = 911,
+            Increment = 1,
+            Format = "{0:X}",
+            X = 0,
+            Y = Pos.Bottom (enableCB) + 1,
+        };
 
 
-        /// <summary>
-        /// The value that will be incremented or decremented.
-        /// </summary>
-        public T Value
-        {
-            get => _value;
-            set
-            {
-                if (_value.Equals (value))
-                {
-                    return;
-                }
-
-                T oldValue = value;
-                CancelEventArgs<T> args = new (ref _value, ref value);
-                ValueChanging?.Invoke (this, args);
-
-                if (args.Cancel)
-                {
-                    return;
-                }
-
-                _value = value;
-                _number.Text = _value.ToString ()!;
-                ValueChanged?.Invoke (this, new (in _value));
-            }
-        }
+        main.Add (decNumericUpDown);
 
 
-        /// <summary>
-        /// Fired when the value is about to change. Set <see cref="CancelEventArgs{T}.Cancel"/> to true to prevent the change.
-        /// </summary>
-        [CanBeNull]
-        public event EventHandler<CancelEventArgs<T>> ValueChanging;
-
-        /// <summary>
-        /// Fired when the value has changed.
-        /// </summary>
-        [CanBeNull]
-        public event EventHandler<EventArgs<T>> ValueChanged;
-
-        /// <summary>
-        /// The number of digits to display. The <see cref="View.Viewport"/> will be resized to fit this number of characters plus the buttons. The default is 3.
-        /// </summary>
-        public int Digits { get; set; } = 3;
+        main.Ready += (s, e) =>
+                      {
+                          radioGroup.Refresh ();
+                      };
+        Application.Run (main);
+        main.Dispose ();
+        Application.Shutdown ();
     }
     }
 }
 }
 
 

+ 14 - 14
UICatalog/Scenarios/ContentScrolling.cs

@@ -135,8 +135,8 @@ public class ContentScrolling : Scenario
             Y = 0,
             Y = 0,
             CanFocus = false
             CanFocus = false
         };
         };
-        cbAllowNegativeX.State = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowNegativeX.Toggle += AllowNegativeX_Toggle;
+        cbAllowNegativeX.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowNegativeX.CheckedStateChanging += AllowNegativeX_Toggle;
 
 
         void AllowNegativeX_Toggle (object sender, CancelEventArgs<CheckState> e)
         void AllowNegativeX_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {
@@ -159,8 +159,8 @@ public class ContentScrolling : Scenario
             Y = 0,
             Y = 0,
             CanFocus = false
             CanFocus = false
         };
         };
-        cbAllowNegativeY.State = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowNegativeY.Toggle += AllowNegativeY_Toggle;
+        cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowNegativeY.CheckedStateChanging += AllowNegativeY_Toggle;
 
 
         void AllowNegativeY_Toggle (object sender, CancelEventArgs<CheckState> e)
         void AllowNegativeY_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {
@@ -182,8 +182,8 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = false
             CanFocus = false
         };
         };
-        cbAllowXGreaterThanContentWidth.State = view.ViewportSettings.HasFlag(ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowXGreaterThanContentWidth.Toggle += AllowXGreaterThanContentWidth_Toggle;
+        cbAllowXGreaterThanContentWidth.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowXGreaterThanContentWidth.CheckedStateChanging += AllowXGreaterThanContentWidth_Toggle;
 
 
         void AllowXGreaterThanContentWidth_Toggle (object sender, CancelEventArgs<CheckState> e)
         void AllowXGreaterThanContentWidth_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {
@@ -206,8 +206,8 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = false
             CanFocus = false
         };
         };
-        cbAllowYGreaterThanContentHeight.State = view.ViewportSettings.HasFlag(ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowYGreaterThanContentHeight.Toggle += AllowYGreaterThanContentHeight_Toggle;
+        cbAllowYGreaterThanContentHeight.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowYGreaterThanContentHeight.CheckedStateChanging += AllowYGreaterThanContentHeight_Toggle;
 
 
         void AllowYGreaterThanContentHeight_Toggle (object sender, CancelEventArgs<CheckState> e)
         void AllowYGreaterThanContentHeight_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {
@@ -229,7 +229,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowYGreaterThanContentHeight)
             Y = Pos.Bottom (cbAllowYGreaterThanContentHeight)
         };
         };
 
 
-        Buttons.NumericUpDown<int> contentSizeWidth = new Buttons.NumericUpDown<int>
+        NumericUpDown<int> contentSizeWidth = new NumericUpDown<int>
         {
         {
             Value = view.GetContentSize ().Width,
             Value = view.GetContentSize ().Width,
             X = Pos.Right (labelContentSize) + 1,
             X = Pos.Right (labelContentSize) + 1,
@@ -256,7 +256,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize)
             Y = Pos.Top (labelContentSize)
         };
         };
 
 
-        Buttons.NumericUpDown<int> contentSizeHeight = new Buttons.NumericUpDown<int>
+        NumericUpDown<int> contentSizeHeight = new NumericUpDown<int>
         {
         {
             Value = view.GetContentSize ().Height,
             Value = view.GetContentSize ().Height,
             X = Pos.Right (labelComma) + 1,
             X = Pos.Right (labelComma) + 1,
@@ -284,8 +284,8 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize),
             Y = Pos.Top (labelContentSize),
             CanFocus = false
             CanFocus = false
         };
         };
-        cbClearOnlyVisible.State = view.ViewportSettings.HasFlag(ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
-        cbClearOnlyVisible.Toggle += ClearVisibleContentOnly_Toggle;
+        cbClearOnlyVisible.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
+        cbClearOnlyVisible.CheckedStateChanging += ClearVisibleContentOnly_Toggle;
 
 
         void ClearVisibleContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)
         void ClearVisibleContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {
@@ -306,8 +306,8 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize),
             Y = Pos.Top (labelContentSize),
             CanFocus = false
             CanFocus = false
         };
         };
-        cbDoNotClipContent.State = view.ViewportSettings.HasFlag (ViewportSettings.ClipContentOnly) ? CheckState.Checked : CheckState.UnChecked;
-        cbDoNotClipContent.Toggle += ClipVisibleContentOnly_Toggle;
+        cbDoNotClipContent.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.ClipContentOnly) ? CheckState.Checked : CheckState.UnChecked;
+        cbDoNotClipContent.CheckedStateChanging += ClipVisibleContentOnly_Toggle;
 
 
         void ClipVisibleContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)
         void ClipVisibleContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
         {

+ 1 - 1
UICatalog/Scenarios/ContextMenus.cs

@@ -179,7 +179,7 @@ public class ContextMenus : Scenario
                                                                        "This would open setup dialog",
                                                                        "This would open setup dialog",
                                                                        "Ok"
                                                                        "Ok"
                                                                       ),
                                                                       ),
-                                                           shortcut: KeyCode.T
+                                                           shortcutKey: KeyCode.T
                                                                      | KeyCode
                                                                      | KeyCode
                                                                          .CtrlMask
                                                                          .CtrlMask
                                                           ),
                                                           ),

+ 4 - 3
UICatalog/Scenarios/Dialogs.cs

@@ -136,7 +136,7 @@ public class Dialogs : Scenario
             Y = Pos.Bottom (numButtonsLabel),
             Y = Pos.Bottom (numButtonsLabel),
             TextAlignment = Alignment.End,
             TextAlignment = Alignment.End,
             Text = $"_Add {char.ConvertFromUtf32 (CODE_POINT)} to button text to stress wide char support",
             Text = $"_Add {char.ConvertFromUtf32 (CODE_POINT)} to button text to stress wide char support",
-            State = CheckState.UnChecked
+            CheckedState = CheckState.UnChecked
         };
         };
         frame.Add (glyphsNotWords);
         frame.Add (glyphsNotWords);
 
 
@@ -235,7 +235,7 @@ public class Dialogs : Scenario
                 int buttonId = i;
                 int buttonId = i;
                 Button button = null;
                 Button button = null;
 
 
-                if (glyphsNotWords.State == CheckState.Checked)
+                if (glyphsNotWords.CheckedState == CheckState.Checked)
                 {
                 {
                     buttonId = i;
                     buttonId = i;
 
 
@@ -263,6 +263,7 @@ public class Dialogs : Scenario
             dialog = new ()
             dialog = new ()
             {
             {
                 Title = titleEdit.Text,
                 Title = titleEdit.Text,
+                Text = "Dialog Text",
                 ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentRadioGroup.RadioLabels [alignmentRadioGroup.SelectedItem]),
                 ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentRadioGroup.RadioLabels [alignmentRadioGroup.SelectedItem]),
 
 
                 Buttons = buttons.ToArray ()
                 Buttons = buttons.ToArray ()
@@ -289,7 +290,7 @@ public class Dialogs : Scenario
                               int buttonId = buttons.Count;
                               int buttonId = buttons.Count;
                               Button button;
                               Button button;
 
 
-                              if (glyphsNotWords.State == CheckState.Checked)
+                              if (glyphsNotWords.CheckedState == CheckState.Checked)
                               {
                               {
                                   button = new ()
                                   button = new ()
                                   {
                                   {

File diff suppressed because it is too large
+ 328 - 366
UICatalog/Scenarios/DynamicMenuBar.cs


+ 10 - 81
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -15,7 +15,7 @@ public class DynamicStatusBar : Scenario
 {
 {
     public override void Main ()
     public override void Main ()
     {
     {
-
+        Application.Init ();
         Application.Run<DynamicStatusBarSample> ().Dispose ();
         Application.Run<DynamicStatusBarSample> ().Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
     }
     }
@@ -125,58 +125,9 @@ public class DynamicStatusBar : Scenario
 
 
             TextShortcut.KeyDown += (s, e) =>
             TextShortcut.KeyDown += (s, e) =>
                                     {
                                     {
-                                        if (!ProcessKey (e))
-                                        {
-                                            return;
-                                        }
+                                        TextShortcut.Text = e.ToString ();
 
 
-                                        if (CheckShortcut (e.KeyCode, true))
-                                        {
-                                            e.Handled = true;
-                                        }
                                     };
                                     };
-
-            bool ProcessKey (Key ev)
-            {
-                switch (ev.KeyCode)
-                {
-                    case KeyCode.CursorUp:
-                    case KeyCode.CursorDown:
-                    case KeyCode.Tab:
-                    case KeyCode.Tab | KeyCode.ShiftMask:
-                        return false;
-                }
-
-                return true;
-            }
-
-            bool CheckShortcut (KeyCode k, bool pre)
-            {
-                Shortcut m = _statusItem != null ? _statusItem : new Shortcut (k, "", null);
-
-                if (pre && !ShortcutHelper.PreShortcutValidation (k))
-                {
-                    TextShortcut.Text = "";
-
-                    return false;
-                }
-
-                if (!pre)
-                {
-                    return true;
-                }
-
-                TextShortcut.Text = k.ToString ();
-                return true;
-            }
-
-            TextShortcut.KeyUp += (s, e) =>
-                                  {
-                                      if (CheckShortcut (e.KeyCode, true))
-                                      {
-                                          e.Handled = true;
-                                      }
-                                  };
             Add (TextShortcut);
             Add (TextShortcut);
 
 
             var _btnShortcut = new Button
             var _btnShortcut = new Button
@@ -210,7 +161,7 @@ public class DynamicStatusBar : Scenario
                                   ? GetTargetAction (statusItem.Action)
                                   ? GetTargetAction (statusItem.Action)
                                   : string.Empty;
                                   : string.Empty;
 
 
-            TextShortcut.Text = statusItem.CommandView.Text;
+            TextShortcut.Text = statusItem.Key;
         }
         }
 
 
         public DynamicStatusItem EnterStatusItem ()
         public DynamicStatusItem EnterStatusItem ()
@@ -238,13 +189,6 @@ public class DynamicStatusBar : Scenario
                                   }
                                   }
                                   else
                                   else
                                   {
                                   {
-                                      if (!string.IsNullOrEmpty (TextShortcut.Text))
-                                      {
-                                          TextTitle.Text = DynamicStatusBarSample.SetTitleText (
-                                                                                                TextTitle.Text,
-                                                                                                TextShortcut.Text
-                                                                                               );
-                                      }
 
 
                                       valid = true;
                                       valid = true;
                                       Application.RequestStop ();
                                       Application.RequestStop ();
@@ -433,10 +377,6 @@ public class DynamicStatusBar : Scenario
                                   }
                                   }
                                   else if (_currentEditStatusItem != null)
                                   else if (_currentEditStatusItem != null)
                                   {
                                   {
-                                      _frmStatusBarDetails.TextTitle.Text = SetTitleText (
-                                                                                          _frmStatusBarDetails.TextTitle.Text,
-                                                                                          _frmStatusBarDetails.TextShortcut.Text
-                                                                                         );
 
 
                                       var statusItem = new DynamicStatusItem
                                       var statusItem = new DynamicStatusItem
                                       {
                                       {
@@ -487,6 +427,7 @@ public class DynamicStatusBar : Scenario
                                       if (statusItem != null)
                                       if (statusItem != null)
                                       {
                                       {
                                           _statusBar.RemoveShortcut (_currentSelectedStatusBar);
                                           _statusBar.RemoveShortcut (_currentSelectedStatusBar);
+                                          statusItem.Dispose ();
                                           DataContext.Items.RemoveAt (_lstItems.SelectedItem);
                                           DataContext.Items.RemoveAt (_lstItems.SelectedItem);
 
 
                                           if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1)
                                           if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1)
@@ -526,6 +467,7 @@ public class DynamicStatusBar : Scenario
                                                }
                                                }
 
 
                                                Remove (_statusBar);
                                                Remove (_statusBar);
+                                               _statusBar.Dispose ();
                                                _statusBar = null;
                                                _statusBar = null;
                                                DataContext.Items = [];
                                                DataContext.Items = [];
                                                _currentStatusItem = null;
                                                _currentStatusItem = null;
@@ -588,7 +530,7 @@ public class DynamicStatusBar : Scenario
 
 
             Shortcut CreateNewStatusBar (DynamicStatusItem item)
             Shortcut CreateNewStatusBar (DynamicStatusItem item)
             {
             {
-                var newStatusItem = new Shortcut (Key.Empty, item.Title, null);
+                var newStatusItem = new Shortcut (item.Shortcut, item.Title, _frmStatusBarDetails.CreateAction (item));
 
 
                 return newStatusItem;
                 return newStatusItem;
             }
             }
@@ -599,8 +541,9 @@ public class DynamicStatusBar : Scenario
                 int index
                 int index
             )
             )
             {
             {
-                _currentEditStatusItem = CreateNewStatusBar (statusItem);
-                //_statusBar.Items [index] = _currentEditStatusItem;
+                _statusBar.Subviews [index].Title = statusItem.Title;
+                ((Shortcut)_statusBar.Subviews [index]).Action = _frmStatusBarDetails.CreateAction (statusItem);
+                ((Shortcut)_statusBar.Subviews [index]).Key = statusItem.Shortcut;
 
 
                 if (DataContext.Items.Count == 0)
                 if (DataContext.Items.Count == 0)
                 {
                 {
@@ -624,23 +567,9 @@ public class DynamicStatusBar : Scenario
 
 
         public DynamicStatusItemModel DataContext { get; set; }
         public DynamicStatusItemModel DataContext { get; set; }
 
 
-        public static string SetTitleText (string title, string shortcut)
-        {
-            string txt = title;
-            string [] split = title.Split ('~');
 
 
-            if (split.Length > 1)
-            {
-                txt = split [2].Trim ();
-            }
 
 
-            if (string.IsNullOrEmpty (shortcut) || shortcut == "Null")
-            {
-                return txt;
-            }
 
 
-            return $"~{shortcut}~ {txt}";
-        }
     }
     }
 
 
     public class DynamicStatusItem
     public class DynamicStatusItem
@@ -662,7 +591,7 @@ public class DynamicStatusBar : Scenario
 
 
         public Shortcut Shortcut { get; set; }
         public Shortcut Shortcut { get; set; }
         public string Title { get; set; }
         public string Title { get; set; }
-        public override string ToString () { return $"{Title}, {Shortcut}"; }
+        public override string ToString () { return $"{Title}, {Shortcut.Key}"; }
     }
     }
 
 
     public class DynamicStatusItemModel : INotifyPropertyChanged
     public class DynamicStatusItemModel : INotifyPropertyChanged

+ 8 - 8
UICatalog/Scenarios/Editor.cs

@@ -895,16 +895,16 @@ public class Editor : Scenario
 
 
         var ckbMatchCase = new CheckBox
         var ckbMatchCase = new CheckBox
         {
         {
-            X = 0, Y = Pos.Top (txtToFind) + 2, State = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
+            X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
         };
         };
-        ckbMatchCase.Toggle += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
+        ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
         d.Add (ckbMatchCase);
         d.Add (ckbMatchCase);
 
 
         var ckbMatchWholeWord = new CheckBox
         var ckbMatchWholeWord = new CheckBox
         {
         {
-            X = 0, Y = Pos.Top (ckbMatchCase) + 1, State = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
+            X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
         };
         };
-        ckbMatchWholeWord.Toggle += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
+        ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
         d.Add (ckbMatchWholeWord);
         d.Add (ckbMatchWholeWord);
         return d;
         return d;
     }
     }
@@ -1153,16 +1153,16 @@ public class Editor : Scenario
 
 
         var ckbMatchCase = new CheckBox
         var ckbMatchCase = new CheckBox
         {
         {
-            X = 0, Y = Pos.Top (txtToFind) + 2, State = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
+            X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
         };
         };
-        ckbMatchCase.Toggle += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
+        ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
         d.Add (ckbMatchCase);
         d.Add (ckbMatchCase);
 
 
         var ckbMatchWholeWord = new CheckBox
         var ckbMatchWholeWord = new CheckBox
         {
         {
-            X = 0, Y = Pos.Top (ckbMatchCase) + 1, State = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
+            X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
         };
         };
-        ckbMatchWholeWord.Toggle += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
+        ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
         d.Add (ckbMatchWholeWord);
         d.Add (ckbMatchWholeWord);
 
 
         return d;
         return d;

+ 16 - 16
UICatalog/Scenarios/FileDialogExamples.cs

@@ -33,25 +33,25 @@ public class FileDialogExamples : Scenario
         var x = 1;
         var x = 1;
         var win = new Window { Title = GetQuitKeyAndName () };
         var win = new Window { Title = GetQuitKeyAndName () };
 
 
-        _cbMustExist = new CheckBox { State = CheckState.Checked, Y = y++, X = x, Text = "Must Exist" };
+        _cbMustExist = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Must Exist" };
         win.Add (_cbMustExist);
         win.Add (_cbMustExist);
 
 
-        _cbUseColors = new CheckBox { State = FileDialogStyle.DefaultUseColors ? CheckState.Checked : CheckState.UnChecked, Y = y++, X = x, Text = "Use Colors" };
+        _cbUseColors = new CheckBox { CheckedState = FileDialogStyle.DefaultUseColors ? CheckState.Checked : CheckState.UnChecked, Y = y++, X = x, Text = "Use Colors" };
         win.Add (_cbUseColors);
         win.Add (_cbUseColors);
 
 
-        _cbCaseSensitive = new CheckBox { State = CheckState.UnChecked, Y = y++, X = x, Text = "Case Sensitive Search" };
+        _cbCaseSensitive = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Case Sensitive Search" };
         win.Add (_cbCaseSensitive);
         win.Add (_cbCaseSensitive);
 
 
-        _cbAllowMultipleSelection = new CheckBox { State = CheckState.UnChecked, Y = y++, X = x, Text = "Multiple" };
+        _cbAllowMultipleSelection = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Multiple" };
         win.Add (_cbAllowMultipleSelection);
         win.Add (_cbAllowMultipleSelection);
 
 
-        _cbShowTreeBranchLines = new CheckBox { State = CheckState.Checked, Y = y++, X = x, Text = "Tree Branch Lines" };
+        _cbShowTreeBranchLines = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Tree Branch Lines" };
         win.Add (_cbShowTreeBranchLines);
         win.Add (_cbShowTreeBranchLines);
 
 
-        _cbAlwaysTableShowHeaders = new CheckBox { State = CheckState.Checked, Y = y++, X = x, Text = "Always Show Headers" };
+        _cbAlwaysTableShowHeaders = new CheckBox { CheckedState = CheckState.Checked, Y = y++, X = x, Text = "Always Show Headers" };
         win.Add (_cbAlwaysTableShowHeaders);
         win.Add (_cbAlwaysTableShowHeaders);
 
 
-        _cbDrivesOnlyInTree = new CheckBox { State = CheckState.UnChecked, Y = y++, X = x, Text = "Only Show Drives" };
+        _cbDrivesOnlyInTree = new CheckBox { CheckedState = CheckState.UnChecked, Y = y++, X = x, Text = "Only Show Drives" };
         win.Add (_cbDrivesOnlyInTree);
         win.Add (_cbDrivesOnlyInTree);
 
 
         y = 0;
         y = 0;
@@ -151,8 +151,8 @@ public class FileDialogExamples : Scenario
             OpenMode = Enum.Parse<OpenMode> (
             OpenMode = Enum.Parse<OpenMode> (
                                              _rgOpenMode.RadioLabels [_rgOpenMode.SelectedItem]
                                              _rgOpenMode.RadioLabels [_rgOpenMode.SelectedItem]
                                             ),
                                             ),
-            MustExist = _cbMustExist.State == CheckState.Checked,
-            AllowsMultipleSelection = _cbAllowMultipleSelection.State == CheckState.Checked
+            MustExist = _cbMustExist.CheckedState == CheckState.Checked,
+            AllowsMultipleSelection = _cbAllowMultipleSelection.CheckedState == CheckState.Checked
         };
         };
 
 
         fd.Style.OkButtonText = _rgCaption.RadioLabels [_rgCaption.SelectedItem];
         fd.Style.OkButtonText = _rgCaption.RadioLabels [_rgCaption.SelectedItem];
@@ -166,19 +166,19 @@ public class FileDialogExamples : Scenario
         fd.Style.IconProvider.UseUnicodeCharacters = _rgIcons.SelectedItem == 1;
         fd.Style.IconProvider.UseUnicodeCharacters = _rgIcons.SelectedItem == 1;
         fd.Style.IconProvider.UseNerdIcons = _rgIcons.SelectedItem == 2;
         fd.Style.IconProvider.UseNerdIcons = _rgIcons.SelectedItem == 2;
 
 
-        if (_cbCaseSensitive.State == CheckState.Checked)
+        if (_cbCaseSensitive.CheckedState == CheckState.Checked)
         {
         {
             fd.SearchMatcher = new CaseSensitiveSearchMatcher ();
             fd.SearchMatcher = new CaseSensitiveSearchMatcher ();
         }
         }
 
 
-        fd.Style.UseColors = _cbUseColors.State == CheckState.Checked;
+        fd.Style.UseColors = _cbUseColors.CheckedState == CheckState.Checked;
 
 
-        fd.Style.TreeStyle.ShowBranchLines = _cbShowTreeBranchLines.State == CheckState.Checked;
-        fd.Style.TableStyle.AlwaysShowHeaders = _cbAlwaysTableShowHeaders.State == CheckState.Checked;
+        fd.Style.TreeStyle.ShowBranchLines = _cbShowTreeBranchLines.CheckedState == CheckState.Checked;
+        fd.Style.TableStyle.AlwaysShowHeaders = _cbAlwaysTableShowHeaders.CheckedState == CheckState.Checked;
 
 
         IDirectoryInfoFactory dirInfoFactory = new FileSystem ().DirectoryInfo;
         IDirectoryInfoFactory dirInfoFactory = new FileSystem ().DirectoryInfo;
 
 
-        if (_cbDrivesOnlyInTree.State == CheckState.Checked)
+        if (_cbDrivesOnlyInTree.CheckedState == CheckState.Checked)
         {
         {
             fd.Style.TreeRootGetter = () => { return Environment.GetLogicalDrives ().ToDictionary (dirInfoFactory.New, k => k); };
             fd.Style.TreeRootGetter = () => { return Environment.GetLogicalDrives ().ToDictionary (dirInfoFactory.New, k => k); };
         }
         }
@@ -203,7 +203,7 @@ public class FileDialogExamples : Scenario
             fd.Style.CancelButtonText = _tbCancelButton.Text;
             fd.Style.CancelButtonText = _tbCancelButton.Text;
         }
         }
 
 
-        if (_cbFlipButtonOrder.State == CheckState.Checked)
+        if (_cbFlipButtonOrder.CheckedState == CheckState.Checked)
         {
         {
             fd.Style.FlipOkCancelButtonLayoutOrder = true;
             fd.Style.FlipOkCancelButtonLayoutOrder = true;
         }
         }
@@ -225,7 +225,7 @@ public class FileDialogExamples : Scenario
                               "Ok"
                               "Ok"
                              );
                              );
         }
         }
-        else if (_cbAllowMultipleSelection.State == CheckState.Checked)
+        else if (_cbAllowMultipleSelection.CheckedState == CheckState.Checked)
         {
         {
             MessageBox.Query (
             MessageBox.Query (
                               "Chosen!",
                               "Chosen!",

+ 1 - 1
UICatalog/Scenarios/GraphViewExample.cs

@@ -202,7 +202,7 @@ public class GraphViewExample : Scenario
 
 
         if (sender is Shortcut shortcut && shortcut.CommandView is CheckBox checkBox)
         if (sender is Shortcut shortcut && shortcut.CommandView is CheckBox checkBox)
         {
         {
-            checkBox.State = _miDiags.Checked ?? false ? CheckState.Checked : CheckState.UnChecked;
+            checkBox.CheckedState = _miDiags.Checked ?? false ? CheckState.Checked : CheckState.UnChecked;
         }
         }
     }
     }
 
 

+ 3 - 3
UICatalog/Scenarios/Images.cs

@@ -29,7 +29,7 @@ public class Images : Scenario
         {
         {
             X = Pos.Right (lblDriverName) + 2,
             X = Pos.Right (lblDriverName) + 2,
             Y = 0,
             Y = 0,
-            State = canTrueColor ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = canTrueColor ? CheckState.Checked : CheckState.UnChecked,
             CanFocus = false,
             CanFocus = false,
             Text = "supports true color "
             Text = "supports true color "
         };
         };
@@ -39,11 +39,11 @@ public class Images : Scenario
         {
         {
             X = Pos.Right (cbSupportsTrueColor) + 2,
             X = Pos.Right (cbSupportsTrueColor) + 2,
             Y = 0,
             Y = 0,
-            State = !Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = !Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
             Enabled = canTrueColor,
             Enabled = canTrueColor,
             Text = "Use true color"
             Text = "Use true color"
         };
         };
-        cbUseTrueColor.Toggle += (_, evt) => Application.Force16Colors = evt.NewValue == CheckState.UnChecked;
+        cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.NewValue == CheckState.UnChecked;
         win.Add (cbUseTrueColor);
         win.Add (cbUseTrueColor);
 
 
         var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };
         var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };

+ 6 - 6
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -34,24 +34,24 @@ public class ListViewWithSelection : Scenario
 
 
         _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom rendering" };
         _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom rendering" };
         _appWindow.Add (_customRenderCB);
         _appWindow.Add (_customRenderCB);
-        _customRenderCB.Toggle += _customRenderCB_Toggle;
+        _customRenderCB.CheckedStateChanging += _customRenderCB_Toggle;
 
 
         _allowMarkingCB = new CheckBox
         _allowMarkingCB = new CheckBox
         {
         {
             X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow Marking", AllowCheckStateNone = false
             X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow Marking", AllowCheckStateNone = false
         };
         };
         _appWindow.Add (_allowMarkingCB);
         _appWindow.Add (_allowMarkingCB);
-        _allowMarkingCB.Toggle += AllowMarkingCB_Toggle;
+        _allowMarkingCB.CheckedStateChanging += AllowMarkingCB_Toggle;
 
 
         _allowMultipleCB = new CheckBox
         _allowMultipleCB = new CheckBox
         {
         {
             X = Pos.Right (_allowMarkingCB) + 1,
             X = Pos.Right (_allowMarkingCB) + 1,
             Y = 0,
             Y = 0,
-            Visible = _allowMarkingCB.State == CheckState.Checked,
+            Visible = _allowMarkingCB.CheckedState == CheckState.Checked,
             Text = "Allow Multi-Select"
             Text = "Allow Multi-Select"
         };
         };
         _appWindow.Add (_allowMultipleCB);
         _appWindow.Add (_allowMultipleCB);
-        _allowMultipleCB.Toggle += AllowMultipleCB_Toggle;
+        _allowMultipleCB.CheckedStateChanging += AllowMultipleCB_Toggle;
 
 
         _listView = new ListView
         _listView = new ListView
         {
         {
@@ -108,9 +108,9 @@ public class ListViewWithSelection : Scenario
 
 
         var keepCheckBox = new CheckBox
         var keepCheckBox = new CheckBox
         {
         {
-            X = Pos.AnchorEnd (k.Length + 3), Y = 0, Text = k, State = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
+            X = Pos.AnchorEnd (k.Length + 3), Y = 0, Text = k, CheckedState = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
         };
         };
-        keepCheckBox.Toggle += (s, e) => scrollBar.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
+        keepCheckBox.CheckedStateChanging += (s, e) => scrollBar.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
         _appWindow.Add (keepCheckBox);
         _appWindow.Add (keepCheckBox);
 
 
         Application.Run (_appWindow);
         Application.Run (_appWindow);

+ 2 - 2
UICatalog/Scenarios/Localization.cs

@@ -143,7 +143,7 @@ public class Localization : Scenario
         {
         {
             X = Pos.Right (textField) + 1,
             X = Pos.Right (textField) + 1,
             Y = Pos.Bottom (textAndFileDialogLabel) + 1,
             Y = Pos.Bottom (textAndFileDialogLabel) + 1,
-            State = CheckState.UnChecked,
+            CheckedState = CheckState.UnChecked,
             Text = "Allow any"
             Text = "Allow any"
         };
         };
         win.Add (_allowAnyCheckBox);
         win.Add (_allowAnyCheckBox);
@@ -190,7 +190,7 @@ public class Localization : Scenario
 
 
         dialog.AllowedTypes =
         dialog.AllowedTypes =
         [
         [
-            _allowAnyCheckBox.State == CheckState.Checked
+            _allowAnyCheckBox.CheckedState == CheckState.Checked
                 ? new AllowedTypeAny ()
                 ? new AllowedTypeAny ()
                 : new AllowedType ("Dynamic link library", ".dll"),
                 : new AllowedType ("Dynamic link library", ".dll"),
             new AllowedType ("Json", ".json"),
             new AllowedType ("Json", ".json"),

+ 3 - 3
UICatalog/Scenarios/MessageBoxes.cs

@@ -190,7 +190,7 @@ public class MessageBoxes : Scenario
 
 
         var ckbWrapMessage = new CheckBox
         var ckbWrapMessage = new CheckBox
         {
         {
-            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup), Text = "_Wrap Message", State = CheckState.Checked
+            X = Pos.Right (label) + 1, Y = Pos.Bottom (styleRadioGroup), Text = "_Wrap Message", CheckedState = CheckState.Checked
         };
         };
         frame.Add (ckbWrapMessage);
         frame.Add (ckbWrapMessage);
 
 
@@ -241,7 +241,7 @@ public class MessageBoxes : Scenario
                                                                              titleEdit.Text,
                                                                              titleEdit.Text,
                                                                              messageEdit.Text,
                                                                              messageEdit.Text,
                                                                              defaultButton,
                                                                              defaultButton,
-                                                                             ckbWrapMessage.State == CheckState.Checked,
+                                                                             ckbWrapMessage.CheckedState == CheckState.Checked,
                                                                              btns.ToArray ()
                                                                              btns.ToArray ()
                                                                             )}";
                                                                             )}";
                                                }
                                                }
@@ -254,7 +254,7 @@ public class MessageBoxes : Scenario
                                                                                   titleEdit.Text,
                                                                                   titleEdit.Text,
                                                                                   messageEdit.Text,
                                                                                   messageEdit.Text,
                                                                                   defaultButton,
                                                                                   defaultButton,
-                                                                                  ckbWrapMessage.State == CheckState.Checked,
+                                                                                  ckbWrapMessage.CheckedState == CheckState.Checked,
                                                                                   btns.ToArray ()
                                                                                   btns.ToArray ()
                                                                                  )}";
                                                                                  )}";
                                                }
                                                }

+ 3 - 3
UICatalog/Scenarios/Mouse.cs

@@ -67,7 +67,7 @@ public class Mouse : Scenario
             Y = Pos.Bottom (ml),
             Y = Pos.Bottom (ml),
             Title = "_Want Continuous Button Pressed"
             Title = "_Want Continuous Button Pressed"
         };
         };
-        cbWantContinuousPresses.Toggle += (s, e) => { win.WantContinuousButtonPressed = !win.WantContinuousButtonPressed; };
+        cbWantContinuousPresses.CheckedStateChanging += (s, e) => { win.WantContinuousButtonPressed = !win.WantContinuousButtonPressed; };
 
 
         win.Add (cbWantContinuousPresses);
         win.Add (cbWantContinuousPresses);
 
 
@@ -77,9 +77,9 @@ public class Mouse : Scenario
             Y = Pos.Bottom (cbWantContinuousPresses),
             Y = Pos.Bottom (cbWantContinuousPresses),
             Title = "_Highlight on Press"
             Title = "_Highlight on Press"
         };
         };
-        cbHighlightOnPress.State = win.HighlightStyle == (HighlightStyle.Pressed | HighlightStyle.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
+        cbHighlightOnPress.CheckedState = win.HighlightStyle == (HighlightStyle.Pressed | HighlightStyle.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
 
 
-        cbHighlightOnPress.Toggle += (s, e) =>
+        cbHighlightOnPress.CheckedStateChanging += (s, e) =>
                                       {
                                       {
                                           if (e.NewValue == CheckState.Checked)
                                           if (e.NewValue == CheckState.Checked)
                                           {
                                           {

+ 291 - 0
UICatalog/Scenarios/NumericUpDownDemo.cs

@@ -0,0 +1,291 @@
+#nullable enable 
+using System;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("NumericUpDown", "Demonstrates the NumericUpDown View")]
+[ScenarioCategory ("Controls")]
+public class NumericUpDownDemo : Scenario
+{
+    public override void Main ()
+    {
+        Application.Init ();
+
+        Window app = new ()
+        {
+            Title = GetQuitKeyAndName (),
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var editor = new AdornmentsEditor
+        {
+            X = 0,
+            Y = 0,
+            AutoSelectViewToEdit = true,
+            TabStop = TabBehavior.NoStop
+        };
+        app.Add (editor);
+
+        NumericUpDownEditor<int> intEditor = new ()
+        {
+            X = Pos.Right (editor),
+            Y = 0,
+            Title = "int",
+        };
+
+        app.Add (intEditor);
+
+        NumericUpDownEditor<float> floatEditor = new ()
+        {
+            X = Pos.Right (intEditor),
+            Y = 0,
+            Title = "float",
+        };
+        app.Add (floatEditor);
+
+        app.Initialized += AppInitialized;
+
+        void AppInitialized (object? sender, EventArgs e)
+        {
+            floatEditor!.NumericUpDown!.Increment = 0.1F;
+            floatEditor!.NumericUpDown!.Format = "{0:0.0}";
+
+        }
+
+        Application.Run (app);
+        app.Dispose ();
+        Application.Shutdown ();
+
+    }
+
+}
+
+internal class NumericUpDownEditor<T> : View where T : notnull
+{
+    private NumericUpDown<T>? _numericUpDown;
+
+    internal NumericUpDown<T>? NumericUpDown
+    {
+        get => _numericUpDown;
+        set
+        {
+            if (value == _numericUpDown)
+            {
+                return;
+            }
+            _numericUpDown = value;
+
+            if (_numericUpDown is { } && _value is { })
+            {
+                _value.Text = _numericUpDown.Text;
+            }
+        }
+    }
+
+    private TextField? _value;
+    private TextField? _format;
+    private TextField? _increment;
+
+    internal NumericUpDownEditor ()
+    {
+        _numericUpDown = null;
+        Title = "NumericUpDownEditor";
+        BorderStyle = LineStyle.Single;
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+        TabStop = TabBehavior.TabGroup;
+
+        Initialized += NumericUpDownEditorInitialized;
+
+        return;
+
+        void NumericUpDownEditorInitialized (object? sender, EventArgs e)
+        {
+            Label label = new ()
+            {
+                Title = "_Value: ",
+                Width = 12,
+            };
+            label.TextFormatter.Alignment = Alignment.End;
+            _value = new ()
+            {
+                X = Pos.Right (label),
+                Y = Pos.Top (label),
+                Width = 8,
+                Title = "Value",
+            };
+            _value.Accept += ValuedOnAccept;
+
+            void ValuedOnAccept (object? sender, EventArgs e)
+            {
+                if (_numericUpDown is null)
+                {
+                    return;
+                }
+
+                try
+                {
+                    if (string.IsNullOrEmpty (_value.Text))
+                    {
+                        // Handle empty or null text if needed
+                        _numericUpDown.Value = default!;
+                    }
+                    else
+                    {
+                        // Parse _value.Text and then convert to type T
+                        _numericUpDown.Value = (T)Convert.ChangeType (_value.Text, typeof (T));
+                    }
+
+                    _value.ColorScheme = SuperView.ColorScheme;
+
+                }
+                catch (System.FormatException)
+                {
+                    _value.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                catch (InvalidCastException)
+                {
+                    _value.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                finally
+                {
+                }
+
+            }
+            Add (label, _value);
+
+            label = new ()
+            {
+                Y = Pos.Bottom (_value),
+                Width = 12,
+                Title = "_Format: ",
+            };
+            label.TextFormatter.Alignment = Alignment.End;
+
+            _format = new ()
+            {
+                X = Pos.Right (label),
+                Y = Pos.Top (label),
+                Title = "Format",
+                Width = Dim.Width (_value),
+            };
+            _format.Accept += FormatOnAccept;
+
+            void FormatOnAccept (object? o, EventArgs eventArgs)
+            {
+                if (_numericUpDown is null)
+                {
+                    return;
+                }
+
+                try
+                {
+                    // Test format to ensure it's valid
+                    _ = string.Format (_format.Text, _value);
+                    _numericUpDown.Format = _format.Text;
+                    
+                    _format.ColorScheme = SuperView.ColorScheme;
+
+                }
+                catch (System.FormatException)
+                {
+                    _format.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                catch (InvalidCastException)
+                {
+                    _format.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                finally
+                {
+                }
+            }
+
+            Add (label, _format);
+
+            label = new ()
+            {
+                Y = Pos.Bottom (_format),
+                Width = 12,
+                Title = "_Increment: ",
+            };
+            label.TextFormatter.Alignment = Alignment.End;
+            _increment = new ()
+            {
+                X = Pos.Right (label),
+                Y = Pos.Top (label),
+                Title = "Increment",
+                Width = Dim.Width (_value),
+            };
+
+            _increment.Accept += IncrementOnAccept;
+
+            void IncrementOnAccept (object? o, EventArgs eventArgs)
+            {
+                if (_numericUpDown is null)
+                {
+                    return;
+                }
+
+                try
+                {
+                    if (string.IsNullOrEmpty (_value.Text))
+                    {
+                        // Handle empty or null text if needed
+                        _numericUpDown.Increment = default!;
+                    }
+                    else
+                    {
+                        // Parse _value.Text and then convert to type T
+                        _numericUpDown.Increment = (T)Convert.ChangeType (_increment.Text, typeof (T));
+                    }
+
+                    _increment.ColorScheme = SuperView.ColorScheme;
+
+                }
+                catch (System.FormatException)
+                {
+                    _increment.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                catch (InvalidCastException)
+                {
+                    _increment.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+                finally
+                {
+                }
+            }
+
+            Add (label, _increment);
+
+            _numericUpDown = new ()
+            {
+                X = Pos.Center (),
+                Y = Pos.Bottom (_increment) + 1,
+                Increment = (dynamic)1,
+            };
+
+            _numericUpDown.ValueChanged += NumericUpDownOnValueChanged;
+
+            void NumericUpDownOnValueChanged (object? o, EventArgs<T> eventArgs)
+            {
+                _value.Text = _numericUpDown.Text;
+            }
+
+            _numericUpDown.IncrementChanged += NumericUpDownOnIncrementChanged;
+
+            void NumericUpDownOnIncrementChanged (object? o, EventArgs<T> eventArgs)
+            {
+                _increment.Text = _numericUpDown.Increment.ToString ();
+            }
+
+            Add (_numericUpDown);
+
+            _value.Text = _numericUpDown.Text;
+            _format.Text = _numericUpDown.Format;
+            _increment.Text = _numericUpDown.Increment.ToString ();
+        }
+    }
+
+
+}

+ 11 - 11
UICatalog/Scenarios/PosAlignDemo.cs

@@ -87,18 +87,18 @@ public sealed class PosAlignDemo : Scenario
 
 
         if (dimension == Dimension.Width)
         if (dimension == Dimension.Width)
         {
         {
-            endToStartCheckBox.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
+            endToStartCheckBox.CheckedState = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
             endToStartCheckBox.X = Pos.Align (_horizAligner.Alignment);
             endToStartCheckBox.X = Pos.Align (_horizAligner.Alignment);
             endToStartCheckBox.Y = Pos.Top (alignRadioGroup);
             endToStartCheckBox.Y = Pos.Top (alignRadioGroup);
         }
         }
         else
         else
         {
         {
-            endToStartCheckBox.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
+            endToStartCheckBox.CheckedState = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.EndToStart) ? CheckState.Checked : CheckState.UnChecked;
             endToStartCheckBox.X = Pos.Left (alignRadioGroup);
             endToStartCheckBox.X = Pos.Left (alignRadioGroup);
             endToStartCheckBox.Y = Pos.Align (_vertAligner.Alignment);
             endToStartCheckBox.Y = Pos.Align (_vertAligner.Alignment);
         }
         }
 
 
-        endToStartCheckBox.Toggle += (s, e) =>
+        endToStartCheckBox.CheckedStateChanging += (s, e) =>
                                       {
                                       {
                                           if (dimension == Dimension.Width)
                                           if (dimension == Dimension.Width)
                                           {
                                           {
@@ -125,18 +125,18 @@ public sealed class PosAlignDemo : Scenario
 
 
         if (dimension == Dimension.Width)
         if (dimension == Dimension.Width)
         {
         {
-            ignoreFirstOrLast.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
+            ignoreFirstOrLast.CheckedState = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
             ignoreFirstOrLast.X = Pos.Align (_horizAligner.Alignment);
             ignoreFirstOrLast.X = Pos.Align (_horizAligner.Alignment);
             ignoreFirstOrLast.Y = Pos.Top (alignRadioGroup);
             ignoreFirstOrLast.Y = Pos.Top (alignRadioGroup);
         }
         }
         else
         else
         {
         {
-            ignoreFirstOrLast.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
+            ignoreFirstOrLast.CheckedState = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.IgnoreFirstOrLast) ? CheckState.Checked : CheckState.UnChecked;
             ignoreFirstOrLast.X = Pos.Left (alignRadioGroup);
             ignoreFirstOrLast.X = Pos.Left (alignRadioGroup);
             ignoreFirstOrLast.Y = Pos.Align (_vertAligner.Alignment);
             ignoreFirstOrLast.Y = Pos.Align (_vertAligner.Alignment);
         }
         }
 
 
-        ignoreFirstOrLast.Toggle += (s, e) =>
+        ignoreFirstOrLast.CheckedStateChanging += (s, e) =>
                                      {
                                      {
                                          if (dimension == Dimension.Width)
                                          if (dimension == Dimension.Width)
                                          {
                                          {
@@ -163,18 +163,18 @@ public sealed class PosAlignDemo : Scenario
 
 
         if (dimension == Dimension.Width)
         if (dimension == Dimension.Width)
         {
         {
-            addSpacesBetweenItems.State = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
+            addSpacesBetweenItems.CheckedState = _horizAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
             addSpacesBetweenItems.X = Pos.Align (_horizAligner.Alignment);
             addSpacesBetweenItems.X = Pos.Align (_horizAligner.Alignment);
             addSpacesBetweenItems.Y = Pos.Top (alignRadioGroup);
             addSpacesBetweenItems.Y = Pos.Top (alignRadioGroup);
         }
         }
         else
         else
         {
         {
-            addSpacesBetweenItems.State = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
+            addSpacesBetweenItems.CheckedState = _vertAligner.AlignmentModes.HasFlag (AlignmentModes.AddSpaceBetweenItems) ? CheckState.Checked : CheckState.UnChecked;
             addSpacesBetweenItems.X = Pos.Left (alignRadioGroup);
             addSpacesBetweenItems.X = Pos.Left (alignRadioGroup);
             addSpacesBetweenItems.Y = Pos.Align (_vertAligner.Alignment);
             addSpacesBetweenItems.Y = Pos.Align (_vertAligner.Alignment);
         }
         }
 
 
-        addSpacesBetweenItems.Toggle += (s, e) =>
+        addSpacesBetweenItems.CheckedStateChanging += (s, e) =>
                                          {
                                          {
                                              if (dimension == Dimension.Width)
                                              if (dimension == Dimension.Width)
                                              {
                                              {
@@ -211,7 +211,7 @@ public sealed class PosAlignDemo : Scenario
             margin.Y = Pos.Align (_vertAligner.Alignment);
             margin.Y = Pos.Align (_vertAligner.Alignment);
         }
         }
 
 
-        margin.Toggle += (s, e) =>
+        margin.CheckedStateChanging += (s, e) =>
                           {
                           {
                               if (dimension == Dimension.Width)
                               if (dimension == Dimension.Width)
                               {
                               {
@@ -236,7 +236,7 @@ public sealed class PosAlignDemo : Scenario
             }
             }
         ];
         ];
 
 
-        Buttons.NumericUpDown<int> addedViewsUpDown = new()
+        NumericUpDown<int> addedViewsUpDown = new()
         {
         {
             Width = 9,
             Width = 9,
             Title = "Added",
             Title = "Added",

+ 3 - 3
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -236,7 +236,7 @@ public class ProgressBarStyles : Scenario
             X = Pos.Center (),
             X = Pos.Center (),
             Y = Pos.Bottom (continuousPB),
             Y = Pos.Bottom (continuousPB),
             Text = "BidirectionalMarquee", 
             Text = "BidirectionalMarquee", 
-            State = CheckState.Checked
+            CheckedState = CheckState.Checked
         };
         };
         container.Add (ckbBidirectional);
         container.Add (ckbBidirectional);
 
 
@@ -289,9 +289,9 @@ public class ProgressBarStyles : Scenario
                                               marqueesContinuousPB.ProgressBarFormat = (ProgressBarFormat)e.SelectedItem;
                                               marqueesContinuousPB.ProgressBarFormat = (ProgressBarFormat)e.SelectedItem;
                                           };
                                           };
 
 
-        ckbBidirectional.Toggle += (s, e) =>
+        ckbBidirectional.CheckedStateChanging += (s, e) =>
                                    {
                                    {
-                                       ckbBidirectional.State = e.NewValue;
+                                       ckbBidirectional.CheckedState = e.NewValue;
                                        marqueesBlocksPB.BidirectionalMarquee =
                                        marqueesBlocksPB.BidirectionalMarquee =
                                                                   marqueesContinuousPB.BidirectionalMarquee = e.NewValue == CheckState.Checked;
                                                                   marqueesContinuousPB.BidirectionalMarquee = e.NewValue == CheckState.Checked;
                                    };
                                    };

+ 14 - 14
UICatalog/Scenarios/Scrolling.cs

@@ -148,7 +148,7 @@ public class Scrolling : Scenario
             X = Pos.X (scrollView),
             X = Pos.X (scrollView),
             Y = Pos.Bottom (scrollView),
             Y = Pos.Bottom (scrollView),
             Text = "Horizontal Scrollbar",
             Text = "Horizontal Scrollbar",
-            State = scrollView.ShowHorizontalScrollIndicator ? CheckState.Checked : CheckState.UnChecked
+            CheckedState = scrollView.ShowHorizontalScrollIndicator ? CheckState.Checked : CheckState.UnChecked
         };
         };
         app.Add (hCheckBox);
         app.Add (hCheckBox);
 
 
@@ -157,7 +157,7 @@ public class Scrolling : Scenario
             X = Pos.Right (hCheckBox) + 3,
             X = Pos.Right (hCheckBox) + 3,
             Y = Pos.Bottom (scrollView),
             Y = Pos.Bottom (scrollView),
             Text = "Vertical Scrollbar",
             Text = "Vertical Scrollbar",
-            State = scrollView.ShowVerticalScrollIndicator ? CheckState.Checked : CheckState.UnChecked
+            CheckedState = scrollView.ShowVerticalScrollIndicator ? CheckState.Checked : CheckState.UnChecked
         };
         };
         app.Add (vCheckBox);
         app.Add (vCheckBox);
 
 
@@ -165,50 +165,50 @@ public class Scrolling : Scenario
 
 
         var ahCheckBox = new CheckBox
         var ahCheckBox = new CheckBox
         {
         {
-            X = Pos.Left (scrollView), Y = Pos.Bottom (hCheckBox), Text = t, State = scrollView.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
+            X = Pos.Left (scrollView), Y = Pos.Bottom (hCheckBox), Text = t, CheckedState = scrollView.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
         };
         };
         var k = "Keep Content Always In Viewport";
         var k = "Keep Content Always In Viewport";
 
 
         var keepCheckBox = new CheckBox
         var keepCheckBox = new CheckBox
         {
         {
-            X = Pos.Left (scrollView), Y = Pos.Bottom (ahCheckBox), Text = k, State = scrollView.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
+            X = Pos.Left (scrollView), Y = Pos.Bottom (ahCheckBox), Text = k, CheckedState = scrollView.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
         };
         };
 
 
-        hCheckBox.Toggle += (s, e) =>
+        hCheckBox.CheckedStateChanging += (s, e) =>
                              {
                              {
-                                 if (ahCheckBox.State == CheckState.UnChecked)
+                                 if (ahCheckBox.CheckedState == CheckState.UnChecked)
                                  {
                                  {
                                      scrollView.ShowHorizontalScrollIndicator = e.NewValue == CheckState.Checked;
                                      scrollView.ShowHorizontalScrollIndicator = e.NewValue == CheckState.Checked;
                                  }
                                  }
                                  else
                                  else
                                  {
                                  {
-                                     hCheckBox.State = CheckState.Checked;
+                                     hCheckBox.CheckedState = CheckState.Checked;
                                      MessageBox.Query ("Message", "Disable Auto Hide Scrollbars first.", "Ok");
                                      MessageBox.Query ("Message", "Disable Auto Hide Scrollbars first.", "Ok");
                                  }
                                  }
                              };
                              };
 
 
-        vCheckBox.Toggle += (s, e) =>
+        vCheckBox.CheckedStateChanging += (s, e) =>
                              {
                              {
-                                 if (ahCheckBox.State == CheckState.UnChecked)
+                                 if (ahCheckBox.CheckedState == CheckState.UnChecked)
                                  {
                                  {
                                      scrollView.ShowVerticalScrollIndicator = e.NewValue == CheckState.Checked;
                                      scrollView.ShowVerticalScrollIndicator = e.NewValue == CheckState.Checked;
                                  }
                                  }
                                  else
                                  else
                                  {
                                  {
-                                     vCheckBox.State = CheckState.Checked;
+                                     vCheckBox.CheckedState = CheckState.Checked;
                                      MessageBox.Query ("Message", "Disable Auto Hide Scrollbars first.", "Ok");
                                      MessageBox.Query ("Message", "Disable Auto Hide Scrollbars first.", "Ok");
                                  }
                                  }
                              };
                              };
 
 
-        ahCheckBox.Toggle += (s, e) =>
+        ahCheckBox.CheckedStateChanging += (s, e) =>
                               {
                               {
                                   scrollView.AutoHideScrollBars = e.NewValue == CheckState.Checked;
                                   scrollView.AutoHideScrollBars = e.NewValue == CheckState.Checked;
-                                  hCheckBox.State = CheckState.Checked;
-                                  vCheckBox.State = CheckState.Checked;
+                                  hCheckBox.CheckedState = CheckState.Checked;
+                                  vCheckBox.CheckedState = CheckState.Checked;
                               };
                               };
         app.Add (ahCheckBox);
         app.Add (ahCheckBox);
 
 
-        keepCheckBox.Toggle += (s, e) => scrollView.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
+        keepCheckBox.CheckedStateChanging += (s, e) => scrollView.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
         app.Add (keepCheckBox);
         app.Add (keepCheckBox);
 
 
         var count = 0;
         var count = 0;

+ 3 - 3
UICatalog/Scenarios/SendKeys.cs

@@ -89,9 +89,9 @@ public class SendKeys : Scenario
                 Application.Driver?.SendKeys (
                 Application.Driver?.SendKeys (
                                              r,
                                              r,
                                              ck,
                                              ck,
-                                             ckbShift.State == CheckState.Checked,
-                                             ckbAlt.State == CheckState.Checked,
-                                             ckbControl.State == CheckState.Checked
+                                             ckbShift.CheckedState == CheckState.Checked,
+                                             ckbAlt.CheckedState == CheckState.Checked,
+                                             ckbControl.CheckedState == CheckState.Checked
                                             );
                                             );
             }
             }
 
 

+ 2 - 2
UICatalog/Scenarios/Shortcuts.cs

@@ -107,7 +107,7 @@ public class Shortcuts : Scenario
             KeyBindingScope = KeyBindingScope.HotKey,
             KeyBindingScope = KeyBindingScope.HotKey,
         };
         };
 
 
-        ((CheckBox)vShortcut3.CommandView).Toggle += (s, e) =>
+        ((CheckBox)vShortcut3.CommandView).CheckedStateChanging += (s, e) =>
                                                       {
                                                       {
                                                           if (vShortcut3.CommandView is CheckBox cb)
                                                           if (vShortcut3.CommandView is CheckBox cb)
                                                           {
                                                           {
@@ -166,7 +166,7 @@ public class Shortcuts : Scenario
             CommandView = new CheckBox { Text = "_CanFocus" },
             CommandView = new CheckBox { Text = "_CanFocus" },
         };
         };
 
 
-        ((CheckBox)vShortcut5.CommandView).Toggle += (s, e) =>
+        ((CheckBox)vShortcut5.CommandView).CheckedStateChanging += (s, e) =>
                                                      {
                                                      {
                                                          if (vShortcut5.CommandView is CheckBox cb)
                                                          if (vShortcut5.CommandView is CheckBox cb)
                                                          {
                                                          {

+ 2 - 2
UICatalog/Scenarios/Sliders.cs

@@ -236,7 +236,7 @@ public class Sliders : Scenario
             Y = Pos.Bottom (optionsSlider)
             Y = Pos.Bottom (optionsSlider)
         };
         };
 
 
-        dimAutoUsesMin.Toggle += (sender, e) =>
+        dimAutoUsesMin.CheckedStateChanging += (sender, e) =>
                                   {
                                   {
                                       foreach (Slider s in app.Subviews.OfType<Slider> ())
                                       foreach (Slider s in app.Subviews.OfType<Slider> ())
                                       {
                                       {
@@ -407,7 +407,7 @@ public class Sliders : Scenario
             Text = "Min _Inner Spacing:",
             Text = "Min _Inner Spacing:",
         };
         };
 
 
-        Buttons.NumericUpDown<int> innerSpacingUpDown = new ()
+        NumericUpDown<int> innerSpacingUpDown = new ()
         {
         {
             X = Pos.Right (label) + 1
             X = Pos.Right (label) + 1
         };
         };

+ 10 - 10
UICatalog/Scenarios/SpinnerStyles.cs

@@ -53,7 +53,7 @@ public class SpinnerViewStyles : Scenario
             X = Pos.Center () - 7,
             X = Pos.Center () - 7,
             Y = Pos.Bottom (preview),
             Y = Pos.Bottom (preview),
             Enabled = false,
             Enabled = false,
-            State = CheckState.Checked,
+            CheckedState = CheckState.Checked,
             Text = "Ascii Only"
             Text = "Ascii Only"
         };
         };
         app.Add (ckbAscii);
         app.Add (ckbAscii);
@@ -63,20 +63,20 @@ public class SpinnerViewStyles : Scenario
             X = Pos.Center () + 7,
             X = Pos.Center () + 7,
             Y = Pos.Bottom (preview),
             Y = Pos.Bottom (preview),
             Enabled = false,
             Enabled = false,
-            State = CheckState.Checked,
+            CheckedState = CheckState.Checked,
             Text = "No Special"
             Text = "No Special"
         };
         };
         app.Add (ckbNoSpecial);
         app.Add (ckbNoSpecial);
 
 
         var ckbReverse = new CheckBox
         var ckbReverse = new CheckBox
         {
         {
-            X = Pos.Center () - 22, Y = Pos.Bottom (preview) + 1, State = CheckState.UnChecked, Text = "Reverse"
+            X = Pos.Center () - 22, Y = Pos.Bottom (preview) + 1, CheckedState = CheckState.UnChecked, Text = "Reverse"
         };
         };
         app.Add (ckbReverse);
         app.Add (ckbReverse);
 
 
         var ckbBounce = new CheckBox
         var ckbBounce = new CheckBox
         {
         {
-            X = Pos.Right (ckbReverse) + 2, Y = Pos.Bottom (preview) + 1, State = CheckState.UnChecked, Text = "Bounce"
+            X = Pos.Right (ckbReverse) + 2, Y = Pos.Bottom (preview) + 1, CheckedState = CheckState.UnChecked, Text = "Bounce"
         };
         };
         app.Add (ckbBounce);
         app.Add (ckbBounce);
 
 
@@ -157,16 +157,16 @@ public class SpinnerViewStyles : Scenario
                                               spinner.Visible = true;
                                               spinner.Visible = true;
                                               spinner.Style = (SpinnerStyle)Activator.CreateInstance (styleDict [e.Item].Value);
                                               spinner.Style = (SpinnerStyle)Activator.CreateInstance (styleDict [e.Item].Value);
                                               delayField.Text = spinner.SpinDelay.ToString ();
                                               delayField.Text = spinner.SpinDelay.ToString ();
-                                              ckbBounce.State = spinner.SpinBounce ? CheckState.Checked : CheckState.UnChecked;
-                                              ckbNoSpecial.State = !spinner.HasSpecialCharacters ? CheckState.Checked : CheckState.UnChecked;
-                                              ckbAscii.State = spinner.IsAsciiOnly ? CheckState.Checked : CheckState.UnChecked;
-                                              ckbReverse.State = CheckState.UnChecked;
+                                              ckbBounce.CheckedState = spinner.SpinBounce ? CheckState.Checked : CheckState.UnChecked;
+                                              ckbNoSpecial.CheckedState = !spinner.HasSpecialCharacters ? CheckState.Checked : CheckState.UnChecked;
+                                              ckbAscii.CheckedState = spinner.IsAsciiOnly ? CheckState.Checked : CheckState.UnChecked;
+                                              ckbReverse.CheckedState = CheckState.UnChecked;
                                           }
                                           }
                                       };
                                       };
 
 
-        ckbReverse.Toggle += (s, e) => { spinner.SpinReverse = e.NewValue == CheckState.Checked; };
+        ckbReverse.CheckedStateChanging += (s, e) => { spinner.SpinReverse = e.NewValue == CheckState.Checked; };
 
 
-        ckbBounce.Toggle += (s, e) => { spinner.SpinBounce = e.NewValue == CheckState.Checked; };
+        ckbBounce.CheckedStateChanging += (s, e) => { spinner.SpinBounce = e.NewValue == CheckState.Checked; };
 
 
         app.Unloaded += App_Unloaded;
         app.Unloaded += App_Unloaded;
 
 

+ 10 - 10
UICatalog/Scenarios/Text.cs

@@ -107,7 +107,7 @@ public class Text : Scenario
         // single-line mode.
         // single-line mode.
         var chxMultiline = new CheckBox
         var chxMultiline = new CheckBox
         {
         {
-            X = Pos.Left (textView), Y = Pos.Bottom (textView), State = textView.Multiline ? CheckState.Checked : CheckState.UnChecked, Text = "_Multiline"
+            X = Pos.Left (textView), Y = Pos.Bottom (textView), CheckedState = textView.Multiline ? CheckState.Checked : CheckState.UnChecked, Text = "_Multiline"
         };
         };
         win.Add (chxMultiline);
         win.Add (chxMultiline);
 
 
@@ -115,10 +115,10 @@ public class Text : Scenario
         {
         {
             X = Pos.Right (chxMultiline) + 2,
             X = Pos.Right (chxMultiline) + 2,
             Y = Pos.Top (chxMultiline),
             Y = Pos.Top (chxMultiline),
-            State = textView.WordWrap ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = textView.WordWrap ? CheckState.Checked : CheckState.UnChecked,
             Text = "_Word Wrap"
             Text = "_Word Wrap"
         };
         };
-        chxWordWrap.Toggle += (s, e) => textView.WordWrap = e.NewValue == CheckState.Checked;
+        chxWordWrap.CheckedStateChanging += (s, e) => textView.WordWrap = e.NewValue == CheckState.Checked;
         win.Add (chxWordWrap);
         win.Add (chxWordWrap);
 
 
         // TextView captures Tabs (so users can enter /t into text) by default;
         // TextView captures Tabs (so users can enter /t into text) by default;
@@ -128,29 +128,29 @@ public class Text : Scenario
         {
         {
             X = Pos.Right (chxWordWrap) + 2,
             X = Pos.Right (chxWordWrap) + 2,
             Y = Pos.Top (chxWordWrap),
             Y = Pos.Top (chxWordWrap),
-            State = textView.AllowsTab ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = textView.AllowsTab ? CheckState.Checked : CheckState.UnChecked,
             Text = "_Capture Tabs"
             Text = "_Capture Tabs"
         };
         };
 
 
-        chxMultiline.Toggle += (s, e) =>
+        chxMultiline.CheckedStateChanging += (s, e) =>
                                 {
                                 {
                                     textView.Multiline = e.NewValue == CheckState.Checked;
                                     textView.Multiline = e.NewValue == CheckState.Checked;
 
 
-                                    if (!textView.Multiline && chxWordWrap.State == CheckState.Checked)
+                                    if (!textView.Multiline && chxWordWrap.CheckedState == CheckState.Checked)
                                     {
                                     {
-                                        chxWordWrap.State = CheckState.UnChecked;
+                                        chxWordWrap.CheckedState = CheckState.UnChecked;
                                     }
                                     }
 
 
-                                    if (!textView.Multiline && chxCaptureTabs.State == CheckState.Checked)
+                                    if (!textView.Multiline && chxCaptureTabs.CheckedState == CheckState.Checked)
                                     {
                                     {
-                                        chxCaptureTabs.State = CheckState.UnChecked;
+                                        chxCaptureTabs.CheckedState = CheckState.UnChecked;
                                     }
                                     }
                                 };
                                 };
 
 
         Key keyTab = textView.KeyBindings.GetKeyFromCommands (Command.Tab);
         Key keyTab = textView.KeyBindings.GetKeyFromCommands (Command.Tab);
         Key keyBackTab = textView.KeyBindings.GetKeyFromCommands (Command.BackTab);
         Key keyBackTab = textView.KeyBindings.GetKeyFromCommands (Command.BackTab);
 
 
-        chxCaptureTabs.Toggle += (s, e) =>
+        chxCaptureTabs.CheckedStateChanging += (s, e) =>
                                   {
                                   {
                                       if (e.NewValue == CheckState.Checked)
                                       if (e.NewValue == CheckState.Checked)
                                       {
                                       {

+ 4 - 4
UICatalog/Scenarios/TextAlignmentAndDirection.cs

@@ -484,7 +484,7 @@ public class TextAlignmentAndDirection : Scenario
             Enabled = false
             Enabled = false
         };
         };
 
 
-        justifyCheckbox.Toggle += (s, e) => ToggleJustify (e.NewValue != CheckState.Checked);
+        justifyCheckbox.CheckedStateChanging += (s, e) => ToggleJustify (e.NewValue != CheckState.Checked);
 
 
         justifyOptions.SelectedItemChanged += (s, e) => { ToggleJustify (false, true); };
         justifyOptions.SelectedItemChanged += (s, e) => { ToggleJustify (false, true); };
 
 
@@ -500,9 +500,9 @@ public class TextAlignmentAndDirection : Scenario
             Height = 1,
             Height = 1,
             Text = "Word Wrap"
             Text = "Word Wrap"
         };
         };
-        wrapCheckbox.State = wrapCheckbox.TextFormatter.WordWrap ? CheckState.Checked : CheckState.UnChecked;
+        wrapCheckbox.CheckedState = wrapCheckbox.TextFormatter.WordWrap ? CheckState.Checked : CheckState.UnChecked;
 
 
-        wrapCheckbox.Toggle += (s, e) =>
+        wrapCheckbox.CheckedStateChanging += (s, e) =>
                                 {
                                 {
                                     if (e.CurrentValue == CheckState.Checked)
                                     if (e.CurrentValue == CheckState.Checked)
                                     {
                                     {
@@ -536,7 +536,7 @@ public class TextAlignmentAndDirection : Scenario
 
 
         directionOptions.SelectedItemChanged += (s, ev) =>
         directionOptions.SelectedItemChanged += (s, ev) =>
                                                 {
                                                 {
-                                                    bool justChecked = justifyCheckbox.State == CheckState.Checked;
+                                                    bool justChecked = justifyCheckbox.CheckedState == CheckState.Checked;
 
 
                                                     if (justChecked)
                                                     if (justChecked)
                                                     {
                                                     {

+ 1 - 1
UICatalog/Scenarios/TextEffectsScenario.cs

@@ -70,7 +70,7 @@ public class TextEffectsScenario : Scenario
             Y = Pos.AnchorEnd (1)
             Y = Pos.AnchorEnd (1)
         };
         };
 
 
-        cbLooping.Toggle += (s, e) =>
+        cbLooping.CheckedStateChanging += (s, e) =>
                             {
                             {
                                 LoopingGradient = e.NewValue == CheckState.Checked;
                                 LoopingGradient = e.NewValue == CheckState.Checked;
                                 SetupGradientLineCanvas (w, w.Frame.Size);
                                 SetupGradientLineCanvas (w, w.Frame.Size);

+ 2 - 2
UICatalog/Scenarios/TextFormatterDemo.cs

@@ -58,7 +58,7 @@ public class TextFormatterDemo : Scenario
             X = 0,
             X = 0,
             Y = Pos.Bottom (blockText) + 1,
             Y = Pos.Bottom (blockText) + 1,
             Text = "Unicode",
             Text = "Unicode",
-            State = app.HotKeySpecifier == (Rune)' ' ? CheckState.Checked : CheckState.UnChecked
+            CheckedState = app.HotKeySpecifier == (Rune)' ' ? CheckState.Checked : CheckState.UnChecked
         };
         };
 
 
         app.Add (unicodeCheckBox);
         app.Add (unicodeCheckBox);
@@ -121,7 +121,7 @@ public class TextFormatterDemo : Scenario
             label = multipleLines [i];
             label = multipleLines [i];
         }
         }
 
 
-        unicodeCheckBox.Toggle += (s, e) =>
+        unicodeCheckBox.CheckedStateChanging += (s, e) =>
                                    {
                                    {
                                        for (int i = 0; i < alignments.Count; i++)
                                        for (int i = 0; i < alignments.Count; i++)
                                        {
                                        {

+ 13 - 13
UICatalog/Scenarios/TileViewNesting.cs

@@ -35,16 +35,16 @@ public class TileViewNesting : Scenario
         _textField.TextChanged += (s, e) => SetupTileView ();
         _textField.TextChanged += (s, e) => SetupTileView ();
 
 
         _cbHorizontal = new() { X = Pos.Right (_textField) + 1, Text = "Horizontal" };
         _cbHorizontal = new() { X = Pos.Right (_textField) + 1, Text = "Horizontal" };
-        _cbHorizontal.Toggle += (s, e) => SetupTileView ();
+        _cbHorizontal.CheckedStateChanging += (s, e) => SetupTileView ();
 
 
         _cbBorder = new() { X = Pos.Right (_cbHorizontal) + 1, Text = "Border" };
         _cbBorder = new() { X = Pos.Right (_cbHorizontal) + 1, Text = "Border" };
-        _cbBorder.Toggle += (s, e) => SetupTileView ();
+        _cbBorder.CheckedStateChanging += (s, e) => SetupTileView ();
 
 
         _cbTitles = new() { X = Pos.Right (_cbBorder) + 1, Text = "Titles" };
         _cbTitles = new() { X = Pos.Right (_cbBorder) + 1, Text = "Titles" };
-        _cbTitles.Toggle += (s, e) => SetupTileView ();
+        _cbTitles.CheckedStateChanging += (s, e) => SetupTileView ();
 
 
         _cbUseLabels = new() { X = Pos.Right (_cbTitles) + 1, Text = "Use Labels" };
         _cbUseLabels = new() { X = Pos.Right (_cbTitles) + 1, Text = "Use Labels" };
-        _cbUseLabels.Toggle += (s, e) => SetupTileView ();
+        _cbUseLabels.CheckedStateChanging += (s, e) => SetupTileView ();
 
 
         _workArea = new() { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () };
         _workArea = new() { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () };
 
 
@@ -101,7 +101,7 @@ public class TileViewNesting : Scenario
         }
         }
     }
     }
 
 
-    private View CreateContentControl (int number) { return _cbUseLabels.State == CheckState.Checked ? CreateLabelView (number) : CreateTextView (number); }
+    private View CreateContentControl (int number) { return _cbUseLabels.CheckedState == CheckState.Checked ? CreateLabelView (number) : CreateTextView (number); }
 
 
     private View CreateLabelView (int number)
     private View CreateLabelView (int number)
     {
     {
@@ -136,8 +136,8 @@ public class TileViewNesting : Scenario
             Orientation = orientation
             Orientation = orientation
         };
         };
 
 
-        toReturn.Tiles.ElementAt (0).Title = _cbTitles.State == CheckState.Checked ? $"View {titleNumber}" : string.Empty;
-        toReturn.Tiles.ElementAt (1).Title = _cbTitles.State == CheckState.Checked ? $"View {titleNumber + 1}" : string.Empty;
+        toReturn.Tiles.ElementAt (0).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {titleNumber}" : string.Empty;
+        toReturn.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {titleNumber + 1}" : string.Empty;
 
 
         return toReturn;
         return toReturn;
     }
     }
@@ -158,9 +158,9 @@ public class TileViewNesting : Scenario
     {
     {
         int numberOfViews = GetNumberOfViews ();
         int numberOfViews = GetNumberOfViews ();
 
 
-        CheckState titles = _cbTitles.State;
-        CheckState border = _cbBorder.State;
-        CheckState startHorizontal = _cbHorizontal.State;
+        CheckState titles = _cbTitles.CheckedState;
+        CheckState border = _cbBorder.CheckedState;
+        CheckState startHorizontal = _cbHorizontal.CheckedState;
 
 
         foreach (View sub in _workArea.Subviews)
         foreach (View sub in _workArea.Subviews)
         {
         {
@@ -177,9 +177,9 @@ public class TileViewNesting : Scenario
         TileView root = CreateTileView (1, startHorizontal == CheckState.Checked ? Orientation.Horizontal : Orientation.Vertical);
         TileView root = CreateTileView (1, startHorizontal == CheckState.Checked ? Orientation.Horizontal : Orientation.Vertical);
 
 
         root.Tiles.ElementAt (0).ContentView.Add (CreateContentControl (1));
         root.Tiles.ElementAt (0).ContentView.Add (CreateContentControl (1));
-        root.Tiles.ElementAt (0).Title = _cbTitles.State == CheckState.Checked ? "View 1" : string.Empty;
+        root.Tiles.ElementAt (0).Title = _cbTitles.CheckedState == CheckState.Checked ? "View 1" : string.Empty;
         root.Tiles.ElementAt (1).ContentView.Add (CreateContentControl (2));
         root.Tiles.ElementAt (1).ContentView.Add (CreateContentControl (2));
-        root.Tiles.ElementAt (1).Title = _cbTitles.State == CheckState.Checked ? "View 2" : string.Empty;
+        root.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? "View 2" : string.Empty;
 
 
         root.LineStyle = border  == CheckState.Checked? LineStyle.Rounded : LineStyle.None;
         root.LineStyle = border  == CheckState.Checked? LineStyle.Rounded : LineStyle.None;
 
 
@@ -225,7 +225,7 @@ public class TileViewNesting : Scenario
 
 
         // During splitting the old Title will have been migrated to View1 so we only need
         // During splitting the old Title will have been migrated to View1 so we only need
         // to set the Title on View2 (the one that gets our new TextView)
         // to set the Title on View2 (the one that gets our new TextView)
-        newView.Tiles.ElementAt (1).Title = _cbTitles.State == CheckState.Checked ? $"View {_viewsCreated}" : string.Empty;
+        newView.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {_viewsCreated}" : string.Empty;
 
 
         // Flip orientation
         // Flip orientation
         newView.Orientation = to.Orientation == Orientation.Vertical
         newView.Orientation = to.Orientation == Orientation.Vertical

+ 3 - 3
UICatalog/Scenarios/TrueColors.cs

@@ -32,7 +32,7 @@ public class TrueColors : Scenario
         {
         {
             X = x,
             X = x,
             Y = y++,
             Y = y++,
-            State = canTrueColor ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = canTrueColor ? CheckState.Checked : CheckState.UnChecked,
             CanFocus = false,
             CanFocus = false,
             Enabled = false,
             Enabled = false,
             Text = "Driver supports true color "
             Text = "Driver supports true color "
@@ -43,11 +43,11 @@ public class TrueColors : Scenario
         {
         {
             X = x,
             X = x,
             Y = y++,
             Y = y++,
-            State = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
             Enabled = canTrueColor,
             Enabled = canTrueColor,
             Text = "Force 16 colors"
             Text = "Force 16 colors"
         };
         };
-        cbUseTrueColor.Toggle += (_, evt) => { Application.Force16Colors = evt.NewValue == CheckState.Checked; };
+        cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Force16Colors = evt.NewValue == CheckState.Checked; };
         app.Add (cbUseTrueColor);
         app.Add (cbUseTrueColor);
 
 
         y += 2;
         y += 2;

+ 1 - 1
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -119,7 +119,7 @@ public class VkeyPacketSimulator : Scenario
                                                                                         "Keys",
                                                                                         "Keys",
                                                                                         $"'{Key.ToString (
                                                                                         $"'{Key.ToString (
                                                                                                           e.KeyCode,
                                                                                                           e.KeyCode,
-                                                                                                          MenuBar.ShortcutDelimiter
+                                                                                                          Key.Separator
                                                                                                          )}' pressed!",
                                                                                                          )}' pressed!",
                                                                                         "Ok"
                                                                                         "Ok"
                                                                                        )
                                                                                        )

+ 7 - 7
UICatalog/Scenarios/Wizards.cs

@@ -198,7 +198,7 @@ public class Wizards : Scenario
                                            var thirdStepEnabledCeckBox = new CheckBox
                                            var thirdStepEnabledCeckBox = new CheckBox
                                            {
                                            {
                                                Text = "Enable Step _3",
                                                Text = "Enable Step _3",
-                                               State = CheckState.UnChecked,
+                                               CheckedState = CheckState.UnChecked,
                                                X = Pos.Left (lastNameField),
                                                X = Pos.Left (lastNameField),
                                                Y = Pos.Bottom (lastNameField)
                                                Y = Pos.Bottom (lastNameField)
                                            };
                                            };
@@ -245,8 +245,8 @@ public class Wizards : Scenario
                                                X = Pos.Right (progLbl), Y = Pos.Top (progLbl), Width = 40, Fraction = 0.42F
                                                X = Pos.Right (progLbl), Y = Pos.Top (progLbl), Width = 40, Fraction = 0.42F
                                            };
                                            };
                                            thirdStep.Add (progLbl, progressBar);
                                            thirdStep.Add (progLbl, progressBar);
-                                           thirdStep.Enabled = thirdStepEnabledCeckBox.State == CheckState.Checked;
-                                           thirdStepEnabledCeckBox.Toggle += (s, e) => { thirdStep.Enabled = thirdStepEnabledCeckBox.State == CheckState.Checked; };
+                                           thirdStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
+                                           thirdStepEnabledCeckBox.CheckedStateChanging += (s, e) => { thirdStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked; };
 
 
                                            // Add 4th step
                                            // Add 4th step
                                            var fourthStep = new WizardStep { Title = "Step Four" };
                                            var fourthStep = new WizardStep { Title = "Step Four" };
@@ -323,7 +323,7 @@ public class Wizards : Scenario
                                                "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing ESC will cancel the wizard.";
                                                "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing ESC will cancel the wizard.";
 
 
                                            var finalFinalStepEnabledCeckBox =
                                            var finalFinalStepEnabledCeckBox =
-                                               new CheckBox { Text = "Enable _Final Final Step", State = CheckState.UnChecked, X = 0, Y = 1 };
+                                               new CheckBox { Text = "Enable _Final Final Step", CheckedState = CheckState.UnChecked, X = 0, Y = 1 };
                                            lastStep.Add (finalFinalStepEnabledCeckBox);
                                            lastStep.Add (finalFinalStepEnabledCeckBox);
 
 
                                            // Add an optional FINAL last step
                                            // Add an optional FINAL last step
@@ -332,11 +332,11 @@ public class Wizards : Scenario
 
 
                                            finalFinalStep.HelpText =
                                            finalFinalStep.HelpText =
                                                "This step only shows if it was enabled on the other last step.";
                                                "This step only shows if it was enabled on the other last step.";
-                                           finalFinalStep.Enabled = thirdStepEnabledCeckBox.State == CheckState.Checked;
+                                           finalFinalStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
 
 
-                                           finalFinalStepEnabledCeckBox.Toggle += (s, e) =>
+                                           finalFinalStepEnabledCeckBox.CheckedStateChanging += (s, e) =>
                                                                                    {
                                                                                    {
-                                                                                       finalFinalStep.Enabled = finalFinalStepEnabledCeckBox.State == CheckState.Checked;
+                                                                                       finalFinalStep.Enabled = finalFinalStepEnabledCeckBox.CheckedState == CheckState.Checked;
                                                                                    };
                                                                                    };
 
 
                                            Application.Run (wizard);
                                            Application.Run (wizard);

+ 14 - 14
UICatalog/UICatalog.cs

@@ -492,14 +492,14 @@ public class UICatalogApp
                     CommandView = new CheckBox
                     CommandView = new CheckBox
                     {
                     {
                         Title = "16 color mode",
                         Title = "16 color mode",
-                        State = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+                        CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
                         CanFocus = false
                         CanFocus = false
                     },
                     },
                     HelpText = "",
                     HelpText = "",
                     Key = Key.F6
                     Key = Key.F6
                 };
                 };
 
 
-                ((CheckBox)ShForce16Colors.CommandView).Toggle += (sender, args) =>
+                ((CheckBox)ShForce16Colors.CommandView).CheckedStateChanging += (sender, args) =>
                                                                   {
                                                                   {
                                                                       Application.Force16Colors = args.NewValue == CheckState.Checked;
                                                                       Application.Force16Colors = args.NewValue == CheckState.Checked;
                                                                       MiForce16Colors!.Checked = Application.Force16Colors;
                                                                       MiForce16Colors!.Checked = Application.Force16Colors;
@@ -676,7 +676,7 @@ public class UICatalogApp
 
 
             ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
             ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
 
 
-            MenuBar!.Menus [0].Children [0].Shortcut = (KeyCode)Application.QuitKey;
+            MenuBar!.Menus [0].Children [0].ShortcutKey = Application.QuitKey;
 
 
             if (StatusBar is { })
             if (StatusBar is { })
             {
             {
@@ -700,8 +700,8 @@ public class UICatalogApp
             {
             {
                 var item = new MenuItem
                 var item = new MenuItem
                 {
                 {
-                    Title = $"_{theme.Key}",
-                    Shortcut = (KeyCode)new Key ((KeyCode)((uint)KeyCode.D1 + schemeCount++))
+                    Title = theme.Key == "Dark" ? $"{theme.Key.Substring (0, 3)}_{theme.Key.Substring (3, 1)}" : $"_{theme.Key}",
+                    ShortcutKey = new Key ((KeyCode)((uint)KeyCode.D1 + schemeCount++))
                         .WithCtrl
                         .WithCtrl
                 };
                 };
                 item.CheckType |= MenuItemCheckStyle.Checked;
                 item.CheckType |= MenuItemCheckStyle.Checked;
@@ -735,6 +735,7 @@ public class UICatalogApp
                                    ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
                                    ColorScheme = Colors.ColorSchemes [_topLevelColorScheme];
                                    Application.Top!.SetNeedsDisplay ();
                                    Application.Top!.SetNeedsDisplay ();
                                };
                                };
+                item.ShortcutKey = ((Key)sc.Key [0].ToString ().ToLower ()).WithCtrl;
                 schemeMenuItems.Add (item);
                 schemeMenuItems.Add (item);
             }
             }
 
 
@@ -796,7 +797,7 @@ public class UICatalogApp
             {
             {
                 var item = new MenuItem
                 var item = new MenuItem
                 {
                 {
-                    Title = GetDiagnosticsTitle (diag), Shortcut = (KeyCode)new Key (index.ToString () [0]).WithAlt
+                    Title = GetDiagnosticsTitle (diag), ShortcutKey = new Key (index.ToString () [0]).WithAlt
                 };
                 };
                 index++;
                 index++;
                 item.CheckType |= MenuItemCheckStyle.Checked;
                 item.CheckType |= MenuItemCheckStyle.Checked;
@@ -951,9 +952,8 @@ public class UICatalogApp
             List<MenuItem> menuItems = new ();
             List<MenuItem> menuItems = new ();
             MiIsMenuBorderDisabled = new () { Title = "Disable Menu _Border" };
             MiIsMenuBorderDisabled = new () { Title = "Disable Menu _Border" };
 
 
-            MiIsMenuBorderDisabled.Shortcut =
-                (KeyCode)new Key (MiIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt
-                                                                                       .WithCtrl.NoShift;
+            MiIsMenuBorderDisabled.ShortcutKey =
+                new Key (MiIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt.WithCtrl.NoShift;
             MiIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
             MiIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
 
 
             MiIsMenuBorderDisabled.Action += () =>
             MiIsMenuBorderDisabled.Action += () =>
@@ -974,8 +974,8 @@ public class UICatalogApp
             List<MenuItem> menuItems = new ();
             List<MenuItem> menuItems = new ();
             MiIsMouseDisabled = new () { Title = "_Disable Mouse" };
             MiIsMouseDisabled = new () { Title = "_Disable Mouse" };
 
 
-            MiIsMouseDisabled.Shortcut =
-                (KeyCode)new Key (MiIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl.NoShift;
+            MiIsMouseDisabled.ShortcutKey =
+                new Key (MiIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl.NoShift;
             MiIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
             MiIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
 
 
             MiIsMouseDisabled.Action += () =>
             MiIsMouseDisabled.Action += () =>
@@ -994,7 +994,7 @@ public class UICatalogApp
             List<MenuItem> menuItems = new ();
             List<MenuItem> menuItems = new ();
             MiUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
             MiUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
 
 
-            MiUseSubMenusSingleFrame.Shortcut = KeyCode.CtrlMask
+            MiUseSubMenusSingleFrame.ShortcutKey = KeyCode.CtrlMask
                                                 | KeyCode.AltMask
                                                 | KeyCode.AltMask
                                                 | (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
                                                 | (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
                                                  0];
                                                  0];
@@ -1017,7 +1017,7 @@ public class UICatalogApp
             MiForce16Colors = new ()
             MiForce16Colors = new ()
             {
             {
                 Title = "Force _16 Colors",
                 Title = "Force _16 Colors",
-                Shortcut = (KeyCode)Key.F6,
+                ShortcutKey = Key.F6,
                 Checked = Application.Force16Colors,
                 Checked = Application.Force16Colors,
                 CanExecute = () => Application.Driver?.SupportsTrueColor ?? false
                 CanExecute = () => Application.Driver?.SupportsTrueColor ?? false
             };
             };
@@ -1027,7 +1027,7 @@ public class UICatalogApp
                                       {
                                       {
                                           MiForce16Colors.Checked = Application.Force16Colors = (bool)!MiForce16Colors.Checked!;
                                           MiForce16Colors.Checked = Application.Force16Colors = (bool)!MiForce16Colors.Checked!;
 
 
-                                          ((CheckBox)ShForce16Colors!.CommandView!).State =
+                                          ((CheckBox)ShForce16Colors!.CommandView!).CheckedState =
                                               Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
                                               Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
                                           Application.Refresh ();
                                           Application.Refresh ();
                                       };
                                       };

+ 40 - 40
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -38,7 +38,7 @@ public class ConfigurationManagerTests
         }
         }
 
 
         // act
         // act
-        Settings ["Application.QuitKey"].PropertyValue = Key.Q;
+        Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
@@ -130,7 +130,7 @@ public class ConfigurationManagerTests
             { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
             { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
         };
         };
         dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
         dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
-        Assert.Equal (2, dictCopy.Count);
+        Assert.Equal (2, dictCopy!.Count);
         Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
         Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
         Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]);
         Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]);
 
 
@@ -141,7 +141,7 @@ public class ConfigurationManagerTests
         };
         };
         dictSrc = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.White) } };
         dictSrc = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.White) } };
         dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
         dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
-        Assert.Equal (2, dictCopy.Count);
+        Assert.Equal (2, dictCopy!.Count);
         Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
         Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
         Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]);
         Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]);
     }
     }
@@ -151,7 +151,7 @@ public class ConfigurationManagerTests
     {
     {
         Reset ();
         Reset ();
 
 
-        Settings ["Application.QuitKey"].PropertyValue = Key.Q;
+        Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
@@ -163,16 +163,16 @@ public class ConfigurationManagerTests
             fired = true;
             fired = true;
 
 
             // assert
             // assert
-            Assert.Equal (Key.Esc, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
+            Assert.Equal (Key.Esc, (((Key)Settings! ["Application.QuitKey"].PropertyValue)!).KeyCode);
 
 
             Assert.Equal (
             Assert.Equal (
                           KeyCode.F6,
                           KeyCode.F6,
-                          ((Key)Settings ["Application.NextTabGroupKey"].PropertyValue).KeyCode
+                          (((Key)Settings ["Application.NextTabGroupKey"].PropertyValue)!).KeyCode
                          );
                          );
 
 
             Assert.Equal (
             Assert.Equal (
                           KeyCode.F6 | KeyCode.ShiftMask,
                           KeyCode.F6 | KeyCode.ShiftMask,
-                          ((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode
+                          (((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue)!).KeyCode
                          );
                          );
         }
         }
 
 
@@ -228,7 +228,7 @@ public class ConfigurationManagerTests
 
 
         // arrange
         // arrange
         Reset ();
         Reset ();
-        Settings ["Application.QuitKey"].PropertyValue = Key.Q;
+        Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         Settings.Apply ();
         Settings.Apply ();
@@ -242,7 +242,7 @@ public class ConfigurationManagerTests
         Reset ();
         Reset ();
 
 
         // assert
         // assert
-        Assert.NotEmpty (Themes);
+        Assert.NotEmpty (Themes!);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal (Key.Esc, Application.QuitKey);
         Assert.Equal (Key.Esc, Application.QuitKey);
         Assert.Equal (Key.F6, Application.NextTabGroupKey);
         Assert.Equal (Key.F6, Application.NextTabGroupKey);
@@ -274,7 +274,7 @@ public class ConfigurationManagerTests
     {
     {
         Locations = ConfigLocations.DefaultOnly;
         Locations = ConfigLocations.DefaultOnly;
         Reset ();
         Reset ();
-        Assert.NotEmpty (Themes);
+        Assert.NotEmpty (Themes!);
         Assert.Equal ("Default", Themes.Theme);
         Assert.Equal ("Default", Themes.Theme);
     }
     }
 
 
@@ -367,7 +367,7 @@ public class ConfigurationManagerTests
         // Serialize to a JSON string
         // Serialize to a JSON string
         string json = ToJson ();
         string json = ToJson ();
 
 
-        // Write the JSON string to the file 
+        // Write the JSON string to the file
         File.WriteAllText ("config.json", json);
         File.WriteAllText ("config.json", json);
     }
     }
 
 
@@ -377,23 +377,23 @@ public class ConfigurationManagerTests
         Locations = ConfigLocations.All;
         Locations = ConfigLocations.All;
         Reset ();
         Reset ();
 
 
-        Assert.NotEmpty (Settings);
+        Assert.NotEmpty (Settings!);
 
 
         // test that all ConfigProperties have our attribute
         // test that all ConfigProperties have our attribute
         Assert.All (
         Assert.All (
                     Settings,
                     Settings,
                     item => Assert.NotEmpty (
                     item => Assert.NotEmpty (
-                                             item.Value.PropertyInfo.CustomAttributes.Where (
-                                                                                             a => a.AttributeType == typeof (SerializableConfigurationProperty)
-                                                                                            )
+                                             item.Value.PropertyInfo!.CustomAttributes.Where (
+                                                                                              a => a.AttributeType == typeof (SerializableConfigurationProperty)
+                                                                                             )
                                             )
                                             )
                    );
                    );
 
 
         Assert.Empty (
         Assert.Empty (
                       Settings.Where (
                       Settings.Where (
-                                      cp => cp.Value.PropertyInfo.GetCustomAttribute (
-                                                                                      typeof (SerializableConfigurationProperty)
-                                                                                     )
+                                      cp => cp.Value.PropertyInfo!.GetCustomAttribute (
+                                                                                       typeof (SerializableConfigurationProperty)
+                                                                                      )
                                             == null
                                             == null
                                      )
                                      )
                      );
                      );
@@ -401,12 +401,12 @@ public class ConfigurationManagerTests
         // Application is a static class
         // Application is a static class
         PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
         PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
         Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
         Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
-        
 
 
         // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
         // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
         pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");
         pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");
         Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle"));
         Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle"));
-        Assert.True (Themes ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
+        Assert.True (Themes! ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
+        Assert.Equal (pi, Themes! ["Default"] ["FrameView.DefaultBorderStyle"].PropertyInfo);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -414,31 +414,31 @@ public class ConfigurationManagerTests
     {
     {
         // Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
         // Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
         PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
         PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
-        var scp = (SerializableConfigurationProperty)pi.GetCustomAttribute (typeof (SerializableConfigurationProperty));
-        Assert.True (scp.Scope == typeof (ThemeScope));
+        var scp = (SerializableConfigurationProperty)pi!.GetCustomAttribute (typeof (SerializableConfigurationProperty));
+        Assert.True (scp!.Scope == typeof (ThemeScope));
         Assert.True (scp.OmitClassName);
         Assert.True (scp.OmitClassName);
 
 
         Reset ();
         Reset ();
-        Assert.Equal (pi, Themes ["Default"] ["ColorSchemes"].PropertyInfo);
+        Assert.Equal (pi, Themes! ["Default"] ["ColorSchemes"].PropertyInfo);
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void TestConfigurationManagerInitDriver ()
     public void TestConfigurationManagerInitDriver ()
     {
     {
-        Assert.Equal ("Default", Themes.Theme);
+        Assert.Equal ("Default", Themes!.Theme);
 
 
-        Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
+        Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
         Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
         Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
 
 
         // Change Base
         // Change Base
         Stream json = ToStream ();
         Stream json = ToStream ();
 
 
-        Settings.Update (json, "TestConfigurationManagerInitDriver");
+        Settings!.Update (json, "TestConfigurationManagerInitDriver");
 
 
         Dictionary<string, ColorScheme> colorSchemes =
         Dictionary<string, ColorScheme> colorSchemes =
             (Dictionary<string, ColorScheme>)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue;
             (Dictionary<string, ColorScheme>)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue;
-        Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes ["Base"]);
+        Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes! ["Base"]);
         Assert.Equal (Colors.ColorSchemes ["TopLevel"], colorSchemes ["TopLevel"]);
         Assert.Equal (Colors.ColorSchemes ["TopLevel"], colorSchemes ["TopLevel"]);
         Assert.Equal (Colors.ColorSchemes ["Error"], colorSchemes ["Error"]);
         Assert.Equal (Colors.ColorSchemes ["Error"], colorSchemes ["Error"]);
         Assert.Equal (Colors.ColorSchemes ["Dialog"], colorSchemes ["Dialog"]);
         Assert.Equal (Colors.ColorSchemes ["Dialog"], colorSchemes ["Dialog"]);
@@ -489,7 +489,7 @@ public class ConfigurationManagerTests
 				}
 				}
 			}";
 			}";
 
 
-        Settings.Update (json, "test");
+        Settings!.Update (json, "test");
 
 
         // AbNormal is not a ColorScheme attribute
         // AbNormal is not a ColorScheme attribute
         json = @"
         json = @"
@@ -514,7 +514,7 @@ public class ConfigurationManagerTests
 
 
         Settings.Update (json, "test");
         Settings.Update (json, "test");
 
 
-        // Modify hotNormal background only 
+        // Modify hotNormal background only
         json = @"
         json = @"
 			{
 			{
 				""Themes"" :  [ 
 				""Themes"" :  [ 
@@ -572,7 +572,7 @@ public class ConfigurationManagerTests
 				]
 				]
 			}";
 			}";
 
 
-        var jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
+        var jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
         Assert.Equal ("Unexpected color name: brown.", jsonException.Message);
         Assert.Equal ("Unexpected color name: brown.", jsonException.Message);
 
 
         // AbNormal is not a ColorScheme attribute
         // AbNormal is not a ColorScheme attribute
@@ -596,10 +596,10 @@ public class ConfigurationManagerTests
 				]
 				]
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
+        jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
         Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message);
         Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message);
 
 
-        // Modify hotNormal background only 
+        // Modify hotNormal background only
         json = @"
         json = @"
 			{
 			{
 				""Themes"" : [ 
 				""Themes"" : [ 
@@ -619,7 +619,7 @@ public class ConfigurationManagerTests
 				]
 				]
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
+        jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
         Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message);
         Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message);
 
 
         // Unknown property
         // Unknown property
@@ -628,7 +628,7 @@ public class ConfigurationManagerTests
 				""Unknown"" : ""Not known""
 				""Unknown"" : ""Not known""
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
+        jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
         Assert.StartsWith ("Unknown property", jsonException.Message);
         Assert.StartsWith ("Unknown property", jsonException.Message);
 
 
         Assert.Equal (0, _jsonErrors.Length);
         Assert.Equal (0, _jsonErrors.Length);
@@ -644,7 +644,7 @@ public class ConfigurationManagerTests
         GetHardCodedDefaults ();
         GetHardCodedDefaults ();
         Stream stream = ToStream ();
         Stream stream = ToStream ();
 
 
-        Settings.Update (stream, "TestConfigurationManagerToJson");
+        Settings!.Update (stream, "TestConfigurationManagerToJson");
     }
     }
 
 
     [Fact]
     [Fact]
@@ -790,19 +790,19 @@ public class ConfigurationManagerTests
         Reset ();
         Reset ();
         ThrowOnJsonErrors = true;
         ThrowOnJsonErrors = true;
 
 
-        Settings.Update (json, "TestConfigurationManagerUpdateFromJson");
+        Settings!.Update (json, "TestConfigurationManagerUpdateFromJson");
 
 
         Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
-        Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
+        Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue)!.KeyCode);
 
 
-        Assert.Equal ("Default", Themes.Theme);
+        Assert.Equal ("Default", Themes!.Theme);
 
 
-        Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
+        Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
         Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
         Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
 
 
         Dictionary<string, ColorScheme> colorSchemes =
         Dictionary<string, ColorScheme> colorSchemes =
             (Dictionary<string, ColorScheme>)Themes.First ().Value ["ColorSchemes"].PropertyValue;
             (Dictionary<string, ColorScheme>)Themes.First ().Value ["ColorSchemes"].PropertyValue;
-        Assert.Equal (new Color (Color.White), colorSchemes ["Base"].Normal.Foreground);
+        Assert.Equal (new Color (Color.White), colorSchemes! ["Base"].Normal.Foreground);
         Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background);
         Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background);
 
 
         // Now re-apply
         // Now re-apply

+ 108 - 0
UnitTests/Configuration/SerializableConfigurationPropertyTests.cs

@@ -0,0 +1,108 @@
+#nullable enable
+
+using System.Reflection;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class SerializableConfigurationPropertyTests
+{
+    [Fact]
+    public void Test_SerializableConfigurationProperty_Types_Added_To_JsonSerializerContext ()
+    {
+        // The assembly containing the types to inspect
+        var assembly = Assembly.GetAssembly (typeof (SourceGenerationContext));
+
+        // Get all types from the assembly
+        var types = assembly!.GetTypes ();
+
+        // Find all properties with the SerializableConfigurationProperty attribute
+        var properties = new List<PropertyInfo> ();
+        foreach (var type in types)
+        {
+            properties.AddRange (type.GetProperties ().Where (p =>
+                p.GetCustomAttributes (typeof (SerializableConfigurationProperty), false).Any ()));
+        }
+
+        // Get the types of the properties
+        var propertyTypes = properties.Select (p => p.PropertyType).Distinct ();
+
+        // Get the types registered in the JsonSerializerContext derived class
+        var contextType = typeof (SourceGenerationContext);
+        var contextTypes = GetRegisteredTypes (contextType);
+
+        // Ensure all property types are included in the JsonSerializerContext derived class
+        IEnumerable<Type> collection = contextTypes as Type [] ?? contextTypes.ToArray ();
+
+        foreach (var type in propertyTypes)
+        {
+            Assert.Contains (type, collection);
+        }
+
+        // Ensure no property has the generic JsonStringEnumConverter<>
+        EnsureNoSpecifiedConverters (properties, new [] { typeof (JsonStringEnumConverter<>) });
+        // Ensure no property has the type RuneJsonConverter
+        EnsureNoSpecifiedConverters (properties, new [] { typeof (RuneJsonConverter) });
+        // Ensure no property has the type KeyJsonConverter
+        EnsureNoSpecifiedConverters (properties, new [] { typeof (KeyJsonConverter) });
+
+        // Find all classes with the JsonConverter attribute of type ScopeJsonConverter<>
+        var classesWithScopeJsonConverter = types.Where (t =>
+            t.GetCustomAttributes (typeof (JsonConverterAttribute), false)
+            .Any (attr => ((JsonConverterAttribute)attr).ConverterType!.IsGenericType &&
+                         ((JsonConverterAttribute)attr).ConverterType!.GetGenericTypeDefinition () == typeof (ScopeJsonConverter<>)));
+
+        // Ensure all these classes are included in the JsonSerializerContext derived class
+        foreach (var type in classesWithScopeJsonConverter)
+        {
+            Assert.Contains (type, collection);
+        }
+    }
+
+    private static IEnumerable<Type> GetRegisteredTypes (Type contextType)
+    {
+        // Use reflection to find which types are registered in the JsonSerializerContext
+        var registeredTypes = new List<Type> ();
+
+        var properties = contextType.GetProperties (BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
+        foreach (var property in properties)
+        {
+            if (property.PropertyType.IsGenericType &&
+                property.PropertyType.GetGenericTypeDefinition () == typeof (JsonTypeInfo<>))
+            {
+                registeredTypes.Add (property.PropertyType.GetGenericArguments () [0]);
+            }
+        }
+
+        return registeredTypes.Distinct ();
+    }
+
+    private static void EnsureNoSpecifiedConverters (List<PropertyInfo> properties, IEnumerable<Type> converterTypes)
+    {
+        // Ensure no property has any of the specified converter types
+        foreach (var property in properties)
+        {
+            var jsonConverterAttributes = property.GetCustomAttributes (typeof (JsonConverterAttribute), false)
+                                                  .Cast<JsonConverterAttribute> ();
+
+            foreach (var attribute in jsonConverterAttributes)
+            {
+                foreach (var converterType in converterTypes)
+                {
+                    if (attribute.ConverterType!.IsGenericType &&
+                        attribute.ConverterType.GetGenericTypeDefinition () == converterType)
+                    {
+                        Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
+                    }
+
+                    if (!attribute.ConverterType!.IsGenericType &&
+                        attribute.ConverterType == converterType)
+                    {
+                        Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
+                    }
+                }
+            }
+        }
+    }
+}

+ 9 - 4
UnitTests/Configuration/ThemeScopeTests.cs

@@ -29,13 +29,18 @@ public class ThemeScopeTests
     {
     {
         Reset ();
         Reset ();
         Assert.NotEmpty (Themes);
         Assert.NotEmpty (Themes);
-        Assert.Equal (Alignment.End, Dialog.DefaultButtonAlignment);
+        Alignment savedValue = Dialog.DefaultButtonAlignment;
+        Alignment newValue = Alignment.Center != savedValue ? Alignment.Center : Alignment.Start;
 
 
-        Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.Center;
+        Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = newValue;
 
 
         ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
         ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
-        Assert.Equal (Alignment.Center, Dialog.DefaultButtonAlignment);
-        Reset ();
+        Assert.Equal (newValue, Dialog.DefaultButtonAlignment);
+
+        // Replace with the savedValue to avoid failures on other unit tests that rely on the default value
+        Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = savedValue;
+        ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
+        Assert.Equal (savedValue, Dialog.DefaultButtonAlignment);
     }
     }
 
 
     [Fact]
     [Fact]

+ 3 - 0
UnitTests/Configuration/ThemeTests.cs

@@ -76,6 +76,9 @@ public class ThemeTests
     [Fact]
     [Fact]
     public void TestSerialize_RoundTrip ()
     public void TestSerialize_RoundTrip ()
     {
     {
+        // This is needed to test only this alone
+        Reset ();
+
         var theme = new ThemeScope ();
         var theme = new ThemeScope ();
         theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;
         theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;
 
 

+ 17 - 1
UnitTests/Input/KeyTests.cs

@@ -533,7 +533,7 @@ public class KeyTests
         var b = Key.A;
         var b = Key.A;
         Assert.True (a.Equals (b));
         Assert.True (a.Equals (b));
     }
     }
-    
+
     [Fact]
     [Fact]
     public void Equals_Handled_Changed_ShouldReturnTrue_WhenEqual ()
     public void Equals_Handled_Changed_ShouldReturnTrue_WhenEqual ()
     {
     {
@@ -552,4 +552,20 @@ public class KeyTests
         var b = Key.A;
         var b = Key.A;
         Assert.False (a.Equals (b));
         Assert.False (a.Equals (b));
     }
     }
+
+    [Fact]
+    public void Set_Key_Separator_With_Rune_Default_Ensure_Using_The_Default_Plus ()
+    {
+        Key key = new (Key.A.WithCtrl);
+        Assert.Equal ((Rune)'+', Key.Separator);
+        Assert.Equal ("Ctrl+A", key.ToString ());
+
+        Key.Separator = new ('-');
+        Assert.Equal ((Rune)'-', Key.Separator);
+        Assert.Equal ("Ctrl-A", key.ToString ());
+
+        Key.Separator = new ();
+        Assert.Equal ((Rune)'+', Key.Separator);
+        Assert.Equal ("Ctrl+A", key.ToString ());
+    }
 }
 }

+ 72 - 0
UnitTests/TestHelpers.cs

@@ -86,6 +86,11 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
             }
             }
 #endif
 #endif
             ConfigurationManager.Reset ();
             ConfigurationManager.Reset ();
+
+            if (CM.Locations != CM.ConfigLocations.None)
+            {
+                SetCurrentConfig (_savedValues);
+            }
         }
         }
     }
     }
 
 
@@ -110,10 +115,77 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
             }
             }
 #endif
 #endif
             Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType));
             Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType));
+
+            if (CM.Locations != CM.ConfigLocations.None)
+            {
+                _savedValues = GetCurrentConfig ();
+            }
         }
         }
     }
     }
 
 
     private bool AutoInit { get; }
     private bool AutoInit { get; }
+
+    private List<object> _savedValues;
+
+    private List<object> GetCurrentConfig ()
+    {
+        CM.Reset ();
+
+        List<object> savedValues =
+        [
+            Dialog.DefaultButtonAlignment,
+            Dialog.DefaultButtonAlignmentModes,
+            MessageBox.DefaultBorderStyle
+        ];
+        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;
+        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = AlignmentModes.AddSpaceBetweenItems;
+        CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = LineStyle.Double;
+        ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
+
+        return savedValues;
+    }
+
+    private void SetCurrentConfig (List<object> values)
+    {
+        CM.Reset ();
+        bool needApply = false;
+
+        foreach (object value in values)
+        {
+            switch (value)
+            {
+                case Alignment alignment:
+                    if ((Alignment)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != alignment)
+                    {
+                        needApply = true;
+                        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = alignment;
+                    }
+
+                    break;
+                case AlignmentModes alignmentModes:
+                    if ((AlignmentModes)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue! != alignmentModes)
+                    {
+                        needApply = true;
+                        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = alignmentModes;
+                    }
+
+                    break;
+                case LineStyle lineStyle:
+                    if ((LineStyle)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != lineStyle)
+                    {
+                        needApply = true;
+                        CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = lineStyle;
+                    }
+
+                    break;
+            }
+        }
+
+        if (needApply)
+        {
+            ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
+        }
+    }
 }
 }
 
 
 [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
 [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]

+ 32 - 32
UnitTests/Views/CheckBoxTests.cs

@@ -98,15 +98,15 @@ public class CheckBoxTests (ITestOutputHelper output)
     {
     {
         var checkBox = new CheckBox { Text = "Check this out 你" };
         var checkBox = new CheckBox { Text = "Check this out 你" };
 
 
-        Assert.Equal (CheckState.UnChecked, checkBox.State);
+        Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
-        Assert.Equal (CheckState.Checked, checkBox.State);
+        Assert.Equal (CheckState.Checked, checkBox.CheckedState);
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
-        Assert.Equal (CheckState.UnChecked, checkBox.State);
+        Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
 
 
         checkBox.AllowCheckStateNone = true;
         checkBox.AllowCheckStateNone = true;
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
-        Assert.Equal (CheckState.None, checkBox.State);
+        Assert.Equal (CheckState.None, checkBox.CheckedState);
         checkBox.Draw ();
         checkBox.Draw ();
 
 
         TestHelpers.AssertDriverContentsWithFrameAre (
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -115,14 +115,14 @@ public class CheckBoxTests (ITestOutputHelper output)
                                                       output
                                                       output
                                                      );
                                                      );
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
-        Assert.Equal (CheckState.Checked, checkBox.State);
+        Assert.Equal (CheckState.Checked, checkBox.CheckedState);
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
         Assert.True (checkBox.NewKeyDownEvent (Key.Space));
-        Assert.Equal (CheckState.UnChecked, checkBox.State);
+        Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
         Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
-        Assert.Equal (CheckState.None, checkBox.State);
+        Assert.Equal (CheckState.None, checkBox.CheckedState);
 
 
         checkBox.AllowCheckStateNone = false;
         checkBox.AllowCheckStateNone = false;
-        Assert.Equal (CheckState.UnChecked, checkBox.State);
+        Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -131,17 +131,17 @@ public class CheckBoxTests (ITestOutputHelper output)
         var ckb = new CheckBox ();
         var ckb = new CheckBox ();
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
         Assert.True (ckb.Height is DimAuto);
-        Assert.Equal (CheckState.UnChecked, ckb.State);
+        Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal (string.Empty, ckb.Text);
         Assert.Equal (string.Empty, ckb.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} ", ckb.TextFormatter.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} ", ckb.TextFormatter.Text);
         Assert.True (ckb.CanFocus);
         Assert.True (ckb.CanFocus);
         Assert.Equal (new (0, 0, 2, 1), ckb.Frame);
         Assert.Equal (new (0, 0, 2, 1), ckb.Frame);
 
 
-        ckb = new () { Text = "Test", State = CheckState.Checked };
+        ckb = new () { Text = "Test", CheckedState = CheckState.Checked };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
         Assert.True (ckb.Height is DimAuto);
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
@@ -151,17 +151,17 @@ public class CheckBoxTests (ITestOutputHelper output)
         ckb = new () { Text = "Test", X = 1, Y = 2 };
         ckb = new () { Text = "Test", X = 1, Y = 2 };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
         Assert.True (ckb.Height is DimAuto);
-        Assert.Equal (CheckState.UnChecked, ckb.State);
+        Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} Test", ckb.TextFormatter.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} Test", ckb.TextFormatter.Text);
         Assert.True (ckb.CanFocus);
         Assert.True (ckb.CanFocus);
         Assert.Equal (new (1, 2, 6, 1), ckb.Frame);
         Assert.Equal (new (1, 2, 6, 1), ckb.Frame);
 
 
-        ckb = new () { Text = "Test", X = 3, Y = 4, State = CheckState.Checked };
+        ckb = new () { Text = "Test", X = 3, Y = 4, CheckedState = CheckState.Checked };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
         Assert.True (ckb.Height is DimAuto);
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ("Test", ckb.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
         Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
@@ -174,16 +174,16 @@ public class CheckBoxTests (ITestOutputHelper output)
     {
     {
         var toggled = false;
         var toggled = false;
         var ckb = new CheckBox ();
         var ckb = new CheckBox ();
-        ckb.Toggle += (s, e) => toggled = true;
+        ckb.CheckedStateChanging += (s, e) => toggled = true;
 
 
-        Assert.Equal (CheckState.UnChecked, ckb.State);
+        Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
         Assert.False (toggled);
         Assert.False (toggled);
         Assert.Equal (Key.Empty, ckb.HotKey);
         Assert.Equal (Key.Empty, ckb.HotKey);
 
 
         ckb.Text = "_Test";
         ckb.Text = "_Test";
         Assert.Equal (Key.T, ckb.HotKey);
         Assert.Equal (Key.T, ckb.HotKey);
         Assert.True (ckb.NewKeyDownEvent (Key.T));
         Assert.True (ckb.NewKeyDownEvent (Key.T));
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
         Assert.True (toggled);
         Assert.True (toggled);
 
 
         ckb.Text = "T_est";
         ckb.Text = "T_est";
@@ -191,28 +191,28 @@ public class CheckBoxTests (ITestOutputHelper output)
         Assert.Equal (Key.E, ckb.HotKey);
         Assert.Equal (Key.E, ckb.HotKey);
         Assert.True (ckb.NewKeyDownEvent (Key.E.WithAlt));
         Assert.True (ckb.NewKeyDownEvent (Key.E.WithAlt));
         Assert.True (toggled);
         Assert.True (toggled);
-        Assert.Equal (CheckState.UnChecked, ckb.State);
+        Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
 
 
         toggled = false;
         toggled = false;
         Assert.Equal (Key.E, ckb.HotKey);
         Assert.Equal (Key.E, ckb.HotKey);
         Assert.True (ckb.NewKeyDownEvent (Key.E));
         Assert.True (ckb.NewKeyDownEvent (Key.E));
         Assert.True (toggled);
         Assert.True (toggled);
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
 
 
         toggled = false;
         toggled = false;
         Assert.True (ckb.NewKeyDownEvent (Key.Space));
         Assert.True (ckb.NewKeyDownEvent (Key.Space));
         Assert.True (toggled);
         Assert.True (toggled);
-        Assert.Equal (CheckState.UnChecked, ckb.State);
+        Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
 
 
         toggled = false;
         toggled = false;
         Assert.True (ckb.NewKeyDownEvent (Key.Space));
         Assert.True (ckb.NewKeyDownEvent (Key.Space));
         Assert.True (toggled);
         Assert.True (toggled);
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
 
 
         toggled = false;
         toggled = false;
         Assert.False (ckb.NewKeyDownEvent (Key.Enter));
         Assert.False (ckb.NewKeyDownEvent (Key.Enter));
         Assert.False (toggled);
         Assert.False (toggled);
-        Assert.Equal (CheckState.Checked, ckb.State);
+        Assert.Equal (CheckState.Checked, ckb.CheckedState);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -271,7 +271,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Assert.Equal (new (0, 0, 30, 5), pos);
         Assert.Equal (new (0, 0, 30, 5), pos);
 
 
-        checkBox.State = CheckState.Checked;
+        checkBox.CheckedState = CheckState.Checked;
         Application.Refresh ();
         Application.Refresh ();
 
 
         expected = @$"
         expected = @$"
@@ -333,10 +333,10 @@ public class CheckBoxTests (ITestOutputHelper output)
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Assert.Equal (new (0, 0, 30, 6), pos);
         Assert.Equal (new (0, 0, 30, 6), pos);
 
 
-        checkBox1.State = CheckState.Checked;
+        checkBox1.CheckedState = CheckState.Checked;
         Assert.Equal (new (1, 1, 25, 1), checkBox1.Frame);
         Assert.Equal (new (1, 1, 25, 1), checkBox1.Frame);
         Assert.Equal (_size25x1, checkBox1.TextFormatter.ConstrainToSize);
         Assert.Equal (_size25x1, checkBox1.TextFormatter.ConstrainToSize);
-        checkBox2.State = CheckState.Checked;
+        checkBox2.CheckedState = CheckState.Checked;
         Assert.Equal (new (1, 2, 25, 1), checkBox2.Frame);
         Assert.Equal (new (1, 2, 25, 1), checkBox2.Frame);
         Assert.Equal (_size25x1, checkBox2.TextFormatter.ConstrainToSize);
         Assert.Equal (_size25x1, checkBox2.TextFormatter.ConstrainToSize);
         Application.Refresh ();
         Application.Refresh ();
@@ -389,7 +389,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Assert.Equal (new (0, 0, 30, 5), pos);
         Assert.Equal (new (0, 0, 30, 5), pos);
 
 
-        checkBox.State = CheckState.Checked;
+        checkBox.CheckedState = CheckState.Checked;
         Application.Refresh ();
         Application.Refresh ();
 
 
         expected = @$"
         expected = @$"
@@ -440,7 +440,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         Assert.Equal (new (0, 0, 30, 5), pos);
         Assert.Equal (new (0, 0, 30, 5), pos);
 
 
-        checkBox.State = CheckState.Checked;
+        checkBox.CheckedState = CheckState.Checked;
         Application.Refresh ();
         Application.Refresh ();
 
 
         expected = @$"
         expected = @$"
@@ -481,14 +481,14 @@ public class CheckBoxTests (ITestOutputHelper output)
         var ckb = new CheckBox { AllowCheckStateNone = true };
         var ckb = new CheckBox { AllowCheckStateNone = true };
         var checkedInvoked = false;
         var checkedInvoked = false;
 
 
-        ckb.Toggle += CheckBoxToggle;
+        ckb.CheckedStateChanging += CheckBoxToggle;
 
 
-        ckb.State = initialState;
-        Assert.Equal (initialState, ckb.State);
-        bool? ret = ckb.OnToggle ();
+        ckb.CheckedState = initialState;
+        Assert.Equal (initialState, ckb.CheckedState);
+        bool? ret = ckb.AdvanceCheckState ();
         Assert.True (ret);
         Assert.True (ret);
         Assert.True (checkedInvoked);
         Assert.True (checkedInvoked);
-        Assert.Equal (initialState, ckb.State);
+        Assert.Equal (initialState, ckb.CheckedState);
 
 
         return;
         return;
 
 

+ 91 - 6
UnitTests/Views/MenuBarTests.cs

@@ -1,4 +1,5 @@
-using Xunit.Abstractions;
+using System.Text;
+using Xunit.Abstractions;
 
 
 namespace Terminal.Gui.ViewsTests;
 namespace Terminal.Gui.ViewsTests;
 
 
@@ -217,7 +218,7 @@ public class MenuBarTests (ITestOutputHelper output)
         Assert.Null (menuBarItem.Action);
         Assert.Null (menuBarItem.Action);
         Assert.Null (menuBarItem.CanExecute);
         Assert.Null (menuBarItem.CanExecute);
         Assert.Null (menuBarItem.Parent);
         Assert.Null (menuBarItem.Parent);
-        Assert.Equal (KeyCode.Null, menuBarItem.Shortcut);
+        Assert.Equal (Key.Empty, menuBarItem.ShortcutKey);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -1229,7 +1230,7 @@ wo
                 )]
                 )]
     [InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)]
     [InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)]
 
 
-    // Hotkeys
+    //// Hotkeys
     [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)]
     [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)]
     [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)]
     [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)]
     [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)]
     [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)]
@@ -1245,9 +1246,10 @@ wo
     [InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)]
     [InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)]
     [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)]
     [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)]
     [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)]
     [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)]
-    [InlineData ("_Edit", "_3rd Level", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
+    [InlineData ("Closed", "None", "2", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D2)]
+    [InlineData ("_Edit", "_5th", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
     [InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)]
     [InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)]
-    [InlineData ("_About", "_About", "", KeyCode.AltMask | KeyCode.A)]
+    [InlineData ("Closed", "None", "About", KeyCode.AltMask | KeyCode.A)]
     public void KeyBindings_Navigation_Commands (
     public void KeyBindings_Navigation_Commands (
         string expectedBarTitle,
         string expectedBarTitle,
         string expectedItemTitle,
         string expectedItemTitle,
@@ -1283,7 +1285,7 @@ wo
         top.Add (menu);
         top.Add (menu);
         Application.Begin (top);
         Application.Begin (top);
 
 
-        foreach (KeyCode key in keys)
+        foreach (Key key in keys)
         {
         {
             top.NewKeyDownEvent (new (key));
             top.NewKeyDownEvent (new (key));
             Application.MainLoop.RunIteration ();
             Application.MainLoop.RunIteration ();
@@ -3725,4 +3727,87 @@ Edit
         Assert.True (btnClicked);
         Assert.True (btnClicked);
         top.Dispose ();
         top.Dispose ();
     }
     }
+
+    [Fact]
+    public void Update_ShortcutKey_KeyBindings_Old_ShortcutKey_Is_Removed ()
+    {
+        var menuBar = new MenuBar ()
+        {
+            Menus =
+            [
+                new MenuBarItem (
+                                 "_File",
+                                 new MenuItem []
+                                 {
+                                     new MenuItem ("New", "Create New", null, null, null, Key.A.WithCtrl)
+                                 }
+                                )
+            ]
+        };
+
+        Assert.Contains (Key.A.WithCtrl, menuBar.KeyBindings.Bindings);
+
+        menuBar.Menus [0].Children [0].ShortcutKey = Key.B.WithCtrl;
+
+        Assert.DoesNotContain (Key.A.WithCtrl, menuBar.KeyBindings.Bindings);
+        Assert.Contains (Key.B.WithCtrl, menuBar.KeyBindings.Bindings);
+    }
+
+    [Fact]
+    public void SetMenus_With_Same_HotKey_Does_Not_Throws ()
+    {
+        var mb = new MenuBar ();
+
+        var i1 = new MenuBarItem ("_heey", "fff", () => { }, () => true);
+
+        mb.Menus = new MenuBarItem [] { i1 };
+        mb.Menus = new MenuBarItem [] { i1 };
+
+        Assert.Equal (Key.H, mb.Menus [0].HotKey);
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void AddMenuBarItem_RemoveMenuItem_Dynamically ()
+    {
+        var menuBar = new MenuBar ();
+        var menuBarItem = new MenuBarItem { Title = "_New" };
+        var action = "";
+        var menuItem = new MenuItem { Title = "_Item", Action = () => action = "I", Parent = menuBarItem };
+        Assert.Equal ("n", menuBarItem.HotKey);
+        Assert.Equal ("i", menuItem.HotKey);
+        Assert.Empty (menuBar.Menus);
+        menuBarItem.AddMenuBarItem (menuItem);
+        menuBar.Menus = [menuBarItem];
+        Assert.Single (menuBar.Menus);
+        Assert.Single (menuBar.Menus [0].Children);
+        Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
+        Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
+
+        var top = new Toplevel ();
+        top.Add (menuBar);
+        Application.Begin (top);
+
+        top.NewKeyDownEvent (Key.N.WithAlt);
+        Application.MainLoop.RunIteration ();
+        Assert.True (menuBar.IsMenuOpen);
+        Assert.Equal ("", action);
+
+        top.NewKeyDownEvent (Key.I);
+        Application.MainLoop.RunIteration ();
+        Assert.False (menuBar.IsMenuOpen);
+        Assert.Equal ("I", action);
+
+        menuItem.RemoveMenuItem ();
+        Assert.Single (menuBar.Menus);
+        Assert.Equal (null, menuBar.Menus [0].Children);
+        Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
+        Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
+
+        menuBarItem.RemoveMenuItem ();
+        Assert.Empty (menuBar.Menus);
+        Assert.DoesNotContain (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
+
+        top.Dispose ();
+    }
 }
 }

+ 2 - 2
UnitTests/Views/MenuTests.cs

@@ -31,7 +31,7 @@ public class MenuTests
         Assert.Null (menuItem.Action);
         Assert.Null (menuItem.Action);
         Assert.Null (menuItem.CanExecute);
         Assert.Null (menuItem.CanExecute);
         Assert.Null (menuItem.Parent);
         Assert.Null (menuItem.Parent);
-        Assert.Equal (KeyCode.Null, menuItem.Shortcut);
+        Assert.Equal (Key.Empty, menuItem.ShortcutKey);
 
 
         menuItem = new MenuItem ("Test", "Help", Run, () => { return true; }, new MenuItem (), KeyCode.F1);
         menuItem = new MenuItem ("Test", "Help", Run, () => { return true; }, new MenuItem (), KeyCode.F1);
         Assert.Equal ("Test", menuItem.Title);
         Assert.Equal ("Test", menuItem.Title);
@@ -39,7 +39,7 @@ public class MenuTests
         Assert.Equal (Run, menuItem.Action);
         Assert.Equal (Run, menuItem.Action);
         Assert.NotNull (menuItem.CanExecute);
         Assert.NotNull (menuItem.CanExecute);
         Assert.NotNull (menuItem.Parent);
         Assert.NotNull (menuItem.Parent);
-        Assert.Equal (KeyCode.F1, menuItem.Shortcut);
+        Assert.Equal (KeyCode.F1, menuItem.ShortcutKey);
 
 
         void Run () { }
         void Run () { }
     }
     }

+ 239 - 0
UnitTests/Views/NumericUpDownTests.cs

@@ -0,0 +1,239 @@
+using System.Globalization;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class NumericUpDownTests (ITestOutputHelper _output)
+{
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultValues_int ()
+    {
+        NumericUpDown<int> numericUpDown = new ();
+
+        Assert.Equal (0, numericUpDown.Value);
+        Assert.Equal (1, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultValues_long ()
+    {
+        NumericUpDown<long> numericUpDown = new ();
+
+        Assert.Equal (0, numericUpDown.Value);
+        Assert.Equal (1, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultValues_float ()
+    {
+        NumericUpDown<float> numericUpDown = new ();
+
+        Assert.Equal (0F, numericUpDown.Value);
+        Assert.Equal (1.0F, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultValues_double ()
+    {
+        NumericUpDown<double> numericUpDown = new ();
+
+        Assert.Equal (0F, numericUpDown.Value);
+        Assert.Equal (1.0F, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultValues_decimal ()
+    {
+        NumericUpDown<decimal> numericUpDown = new ();
+
+        Assert.Equal (0, numericUpDown.Value);
+        Assert.Equal (1, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_int ()
+    {
+        NumericUpDown<int> numericUpDown = new()
+        {
+            Value = 10,
+            Increment = 2
+        };
+
+        Assert.Equal (10, numericUpDown.Value);
+        Assert.Equal (2, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_float ()
+    {
+        NumericUpDown<float> numericUpDown = new()
+        {
+            Value = 10.5F,
+            Increment = 2.5F
+        };
+
+        Assert.Equal (10.5F, numericUpDown.Value);
+        Assert.Equal (2.5F, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_decimal ()
+    {
+        NumericUpDown<decimal> numericUpDown = new ()
+        {
+            Value = 10.5m,
+            Increment = 2.5m
+        };
+
+        Assert.Equal (10.5m, numericUpDown.Value);
+        Assert.Equal (2.5m, numericUpDown.Increment);
+    }
+
+    [Fact]
+    public void WhenCreatedWithInvalidType_ShouldThrowInvalidOperationException ()
+    {
+        Assert.Throws<InvalidOperationException> (() => new NumericUpDown<string> ());
+    }
+
+    [Fact]
+    public void WhenCreatedWithInvalidTypeObject_ShouldNotThrowInvalidOperationException ()
+    {
+        Exception exception = Record.Exception (() => new NumericUpDown<object> ());
+        Assert.Null (exception);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultWidthAndHeight_int ()
+    {
+        NumericUpDown<int> numericUpDown = new ();
+        numericUpDown.SetRelativeLayout (Application.Screen.Size);
+
+        Assert.Equal (3, numericUpDown.Frame.Width);
+        Assert.Equal (1, numericUpDown.Frame.Height);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultWidthAndHeight_float ()
+    {
+        NumericUpDown<float> numericUpDown = new ();
+        numericUpDown.SetRelativeLayout (Application.Screen.Size);
+
+        Assert.Equal (3, numericUpDown.Frame.Width);
+        Assert.Equal (1, numericUpDown.Frame.Height);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultWidthAndHeight_double ()
+    {
+        NumericUpDown<double> numericUpDown = new ();
+        numericUpDown.SetRelativeLayout (Application.Screen.Size);
+
+        Assert.Equal (3, numericUpDown.Frame.Width);
+        Assert.Equal (1, numericUpDown.Frame.Height);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultWidthAndHeight_long ()
+    {
+        NumericUpDown<long> numericUpDown = new ();
+        numericUpDown.SetRelativeLayout (Application.Screen.Size);
+
+        Assert.Equal (3, numericUpDown.Frame.Width);
+        Assert.Equal (1, numericUpDown.Frame.Height);
+    }
+
+    [Fact]
+    public void WhenCreated_ShouldHaveDefaultWidthAndHeight_decimal ()
+    {
+        NumericUpDown<decimal> numericUpDown = new ();
+        numericUpDown.SetRelativeLayout (Application.Screen.Size);
+
+        Assert.Equal (3, numericUpDown.Frame.Width);
+        Assert.Equal (1, numericUpDown.Frame.Height);
+    }
+
+    [Fact]
+    public void WhenCreated_Text_Should_Be_Correct_int ()
+    {
+        NumericUpDown<int> numericUpDown = new ();
+
+        Assert.Equal ("0", numericUpDown.Text);
+    }
+
+    [Fact]
+    public void WhenCreated_Text_Should_Be_Correct_float ()
+    {
+        NumericUpDown<float> numericUpDown = new ();
+
+        Assert.Equal ("0", numericUpDown.Text);
+    }
+
+    [Fact]
+    public void Format_Default ()
+    {
+        NumericUpDown<float> numericUpDown = new ();
+
+        Assert.Equal ("{0}", numericUpDown.Format);
+    }
+
+    [Theory]
+    [InlineData (0F, "{0}", "0")]
+    [InlineData (1.1F, "{0}", "1.1")]
+    [InlineData (0F, "{0:0%}", "0%")]
+    [InlineData (.75F, "{0:0%}", "75%")]
+    public void Format_decimal (float value, string format, string expectedText)
+    {
+        CultureInfo currentCulture = CultureInfo.CurrentCulture;
+        CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+
+        NumericUpDown<float> numericUpDown = new ();
+
+        numericUpDown.Format = format;
+        numericUpDown.Value = value;
+
+        Assert.Equal (expectedText, numericUpDown.Text);
+
+        CultureInfo.CurrentCulture = currentCulture;
+    }
+
+    [Theory]
+    [InlineData (0, "{0}", "0")]
+    [InlineData (11, "{0}", "11")]
+    [InlineData (-1, "{0}", "-1")]
+    [InlineData (911, "{0:X}", "38F")]
+    [InlineData (911, "0x{0:X04}", "0x038F")]
+    public void Format_int (int value, string format, string expectedText)
+    {
+        CultureInfo currentCulture = CultureInfo.CurrentCulture;
+        CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+
+        NumericUpDown<int> numericUpDown = new ();
+
+        numericUpDown.Format = format;
+        numericUpDown.Value = value;
+
+        Assert.Equal (expectedText, numericUpDown.Text);
+
+        CultureInfo.CurrentCulture = currentCulture;
+    }
+
+    [Fact]
+    public void KeyDown_CursorUp_Increments ()
+    {
+        NumericUpDown<int> numericUpDown = new ();
+
+        numericUpDown.NewKeyDownEvent (Key.CursorUp);
+
+        Assert.Equal (1, numericUpDown.Value);
+    }
+
+    [Fact]
+    public void KeyDown_CursorDown_Decrements ()
+    {
+        NumericUpDown<int> numericUpDown = new ();
+
+        numericUpDown.NewKeyDownEvent (Key.CursorDown);
+
+        Assert.Equal (-1, numericUpDown.Value);
+    }
+}

+ 30 - 0
docfx/docs/migratingfromv1.md

@@ -296,3 +296,33 @@ The [Aligner](~/api/Terminal.Gui.Aligner.yml) class makes it easy to align eleme
 -                                      );
 -                                      );
 + var statusBar = new StatusBar (new Shortcut [] { new (Application.QuitKey, "Quit", Quit) });
 + var statusBar = new StatusBar (new Shortcut [] { new (Application.QuitKey, "Quit", Quit) });
 ```
 ```
+
+## `CheckBox` - API renamed and simplified
+
+In v1 `CheckBox` used `bool?` to represent the 3 states. To support consistent behavior for the `Accept` event, `CheckBox` was refactored to use the new `CheckState` enum instead of `bool?`.
+
+Additionally the `Toggle` event was renamed `CheckStateChanging` and made cancelable. The `Toggle` method was renamed to `AdvanceCheckState`.
+
+### How to Fix
+
+```diff
+-var cb = new CheckBox ("_Checkbox", true); {
+-				X = Pos.Right (label) + 1,
+-				Y = Pos.Top (label) + 2
+-			};
+-			cb.Toggled += (e) => {
+-			};
+-           cb.Toggle ();
++
++var cb = new CheckBox ()
++{ 
++	Title = "_Checkbox",
++	CheckState = CheckState.Checked
++}
++cb.CheckStateChanging += (s, e) =>
++{	
++	e.Cancel = preventChange;
++}
++preventChange = false;
++cb.AdvanceCheckState ();
+```

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