Преглед на файлове

Merge branch 'v2_develop' into copilot/port-parallelizable-unit-tests

Tig преди 1 месец
родител
ревизия
a35f09eff0
променени са 33 файла, в които са добавени 1427 реда и са изтрити 311 реда
  1. 1 1
      Examples/UICatalog/Scenarios/CombiningMarks.cs
  2. 2 0
      Examples/UICatalog/Scenarios/Shortcuts.cs
  3. 1 1
      Examples/UICatalog/Scenarios/Themes.cs
  4. 2 2
      Terminal.Gui/App/Application.Screen.cs
  5. 3 0
      Terminal.Gui/Configuration/ConfigProperty.cs
  6. 30 20
      Terminal.Gui/Configuration/ConfigurationManager.cs
  7. 25 15
      Terminal.Gui/Configuration/SchemeManager.cs
  8. 22 26
      Terminal.Gui/Configuration/Scope.cs
  9. 8 9
      Terminal.Gui/Configuration/ScopeJsonConverter.cs
  10. 38 26
      Terminal.Gui/Configuration/ThemeManager.cs
  11. 1 1
      Terminal.Gui/Drawing/Color/Color.cs
  12. 24 4
      Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs
  13. 3 3
      Terminal.Gui/ViewBase/View.Drawing.Scheme.cs
  14. 19 3
      Terminal.Gui/ViewBase/View.Layout.cs
  15. 1 1
      Terminal.Gui/ViewBase/View.cs
  16. 3 2
      Tests/IntegrationTests/UICatalog/ScenarioTests.cs
  17. 1 1
      Tests/StressTests/ScenariosStressTests.cs
  18. 15 1
      Tests/UnitTests/Application/ApplicationScreenTests.cs
  19. 1 1
      Tests/UnitTests/Application/SynchronizatonContextTests.cs
  20. 2 0
      Tests/UnitTests/AutoInitShutdownAttribute.cs
  21. 3 3
      Tests/UnitTests/Configuration/AppScopeTests.cs
  22. 426 41
      Tests/UnitTests/Configuration/ConfigurationMangerTests.cs
  23. 41 34
      Tests/UnitTests/Configuration/GlyphTests.cs
  24. 502 32
      Tests/UnitTests/Configuration/SchemeManagerTests.cs
  25. 56 55
      Tests/UnitTests/Configuration/SettingsScopeTests.cs
  26. 18 12
      Tests/UnitTests/Configuration/ThemeManagerTests.cs
  27. 95 7
      Tests/UnitTests/Configuration/ThemeScopeTests.cs
  28. 1 1
      Tests/UnitTests/Views/ComboBoxTests.cs
  29. 27 0
      Tests/UnitTestsParallelizable/Drawing/Color/ColorStandardColorTests.cs
  30. 0 1
      Tests/UnitTestsParallelizable/Drawing/SchemeTests.GetAttributeForRoleAlgorithmTests.cs
  31. 34 0
      Tests/UnitTestsParallelizable/Drawing/SchemeTests.cs
  32. 12 8
      Tests/UnitTestsParallelizable/View/SchemeTests.cs
  33. 10 0
      docfx/docs/drivers.md

+ 1 - 1
Examples/UICatalog/Scenarios/CombiningMarks.cs

@@ -13,7 +13,7 @@ public class CombiningMarks : Scenario
         top.DrawComplete += (s, e) =>
         {
             // Forces reset _lineColsOffset because we're dealing with direct draw
-            Application.ClearScreenNextIteration = true;
+            Application.Top!.SetNeedsDraw ();
 
             var i = -1;
             top.AddStr ("Terminal.Gui only supports combining marks that normalize. See Issue #2616.");

+ 2 - 0
Examples/UICatalog/Scenarios/Shortcuts.cs

@@ -12,6 +12,7 @@ public class Shortcuts : Scenario
     public override void Main ()
     {
         Application.Init ();
+        var quitKey = Application.QuitKey;
         Window app = new ();
 
         app.Loaded += App_Loaded;
@@ -19,6 +20,7 @@ public class Shortcuts : Scenario
         Application.Run (app);
         app.Dispose ();
         Application.Shutdown ();
+        Application.QuitKey = quitKey;
     }
 
     // Setting everything up in Loaded handler because we change the

+ 1 - 1
Examples/UICatalog/Scenarios/Themes.cs

@@ -172,7 +172,7 @@ public sealed class Themes : Scenario
                                                     else
                                                     {
                                                         appWindow.Remove (allViewsView);
-                                                        allViewsView.Dispose ();
+                                                        allViewsView!.Dispose ();
                                                         allViewsView = null;
 
                                                         appWindow.Add (viewFrame);

+ 2 - 2
Terminal.Gui/App/Application.Screen.cs

@@ -82,8 +82,8 @@ public static partial class Application // Screen related stuff
     ///     Gets or sets whether the screen will be cleared, and all Views redrawn, during the next Application iteration.
     /// </summary>
     /// <remarks>
-    ///     This is typicall set to true when a View's <see cref="View.Frame"/> changes and that view has no
+    ///     This is typical set to true when a View's <see cref="View.Frame"/> changes and that view has no
     ///     SuperView (e.g. when <see cref="Application.Top"/> is moved or resized.
     /// </remarks>
-    public static bool ClearScreenNextIteration { get; set; }
+    internal static bool ClearScreenNextIteration { get; set; }
 }

+ 3 - 0
Terminal.Gui/Configuration/ConfigProperty.cs

@@ -1,6 +1,7 @@
 #nullable enable
 using System.Collections.Concurrent;
 using System.Collections.Immutable;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Text.Json;
@@ -74,6 +75,8 @@ public class ConfigProperty
             {
                 // Use DeepCloner to create a deep copy of PropertyValue
                 object? val = DeepCloner.DeepClone (PropertyValue);
+
+                Debug.Assert (!Immutable);
                 PropertyInfo.SetValue (null, val);
 
             }

+ 30 - 20
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -51,7 +51,7 @@ public static class ConfigurationManager
 {
     /// <summary>The backing property for <see cref="Settings"/> (config settings of <see cref="SettingsScope"/>).</summary>
     /// <remarks>
-    ///     Is <see langword="null"/> until <see cref="ResetToCurrentValues"/> is called. Gets set to a new instance by
+    ///     Is <see langword="null"/> until <see cref="UpdateToCurrentValues"/> is called. Gets set to a new instance by
     ///     deserialization
     ///     (see <see cref="Load"/>).
     /// </remarks>
@@ -117,15 +117,19 @@ public static class ConfigurationManager
         }
     }
 
+    // TODO: Find a way to make this cache truly read-only at the leaf node level. 
+    // TODO: Right now, the dictionary is frozen, but the ConfigProperty instances can still be modified   
+    // TODO: if the PropertyValue is a reference type.
+    // TODO: See https://github.com/gui-cs/Terminal.Gui/issues/4288
     /// <summary>
     ///     A cache of all<see cref="ConfigurationPropertyAttribute"/> properties and their hard coded values.
     /// </summary>
     /// <remarks>Is <see langword="null"/> until <see cref="Initialize"/> is called.</remarks>
 #pragma warning disable IDE1006 // Naming Styles
     internal static FrozenDictionary<string, ConfigProperty>? _hardCodedConfigPropertyCache;
+
     private static readonly object _hardCodedConfigPropertyCacheLock = new ();
 #pragma warning restore IDE1006 // Naming Styles
-
     internal static FrozenDictionary<string, ConfigProperty>? GetHardCodedConfigPropertyCache ()
     {
         lock (_hardCodedConfigPropertyCacheLock)
@@ -183,7 +187,7 @@ public static class ConfigurationManager
         lock (_uninitializedConfigPropertiesCacheCacheLock)
         {
             // _allConfigProperties: for ordered, iterable access (LINQ-friendly)
-            // _frozenConfigPropertyCache: for high-speed key lookup (frozen)
+            // _hardCodedConfigPropertyCache: for high-speed key lookup (frozen)
 
             // Note GetAllConfigProperties returns a new instance and all the properties !HasValue and Immutable.
             _uninitializedConfigPropertiesCache = ConfigProperty.GetAllConfigProperties ();
@@ -209,6 +213,11 @@ public static class ConfigurationManager
         }
 
         LoadHardCodedDefaults ();
+
+        // BUGBUG: ThemeScope is broken and needs to be fixed to not have the hard coded schemes get overwritten.
+        // BUGBUG: This a partial workaround.
+        // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/4288
+        ThemeManager.Themes? [ThemeManager.Theme]?.Apply ();
     }
 
     #endregion Initialization
@@ -291,6 +300,7 @@ public static class ConfigurationManager
 
         if (resetToHardCodedDefaults)
         {
+            // Calls Apply
             ResetToHardCodedDefaults ();
         }
     }
@@ -299,16 +309,17 @@ public static class ConfigurationManager
 
     #region Reset
 
-    // `Reset` - Reset the configuration to either the current values or the hard-coded defaults.
-    // Resetting does not load the configuration; it only resets the configuration to the default values.
+    // `Update` - Updates the configuration from either the current values or the hard-coded defaults.
+    // Updating does not load the configuration; it only updates the configuration to the values currently
+    // in the static ConfigProperties.
 
     /// <summary>
-    ///     INTERNAL: Resets <see cref="ConfigurationManager"/>. Loads settings from the current
+    ///     INTERNAL: Updates <see cref="ConfigurationManager"/> to the settings from the current
     ///     values of the static <see cref="ConfigurationPropertyAttribute"/> properties.
     /// </summary>
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
-    internal static void ResetToCurrentValues ()
+    internal static void UpdateToCurrentValues ()
     {
         if (!IsInitialized ())
         {
@@ -327,13 +338,13 @@ public static class ConfigurationManager
             _settingsLockSlim.ExitWriteLock ();
         }
 
-        Settings!.LoadCurrentValues ();
+        Settings!.UpdateToCurrentValues ();
         ThemeManager.UpdateToCurrentValues ();
-        AppSettings!.LoadCurrentValues ();
+        AppSettings!.UpdateToCurrentValues ();
     }
 
     /// <summary>
-    ///     INTERNAL: Resets <see cref="ConfigurationManager"/>. Loads the hard-coded values of the
+    ///     INTERNAL: Loads the hard-coded values of the
     ///     <see cref="ConfigurationPropertyAttribute"/> properties and applies them.
     /// </summary>
     [RequiresUnreferencedCode ("AOT")]
@@ -374,7 +385,7 @@ public static class ConfigurationManager
 
         Settings = new ();
         Settings!.LoadHardCodedDefaults ();
-        ThemeManager.ResetToHardCodedDefaults ();
+        ThemeManager.LoadHardCodedDefaults ();
         AppSettings!.LoadHardCodedDefaults ();
     }
 
@@ -447,10 +458,6 @@ public static class ConfigurationManager
         {
             SourcesManager?.Load (Settings, $"~/.tui/{AppName}.{_configFilename}", ConfigLocations.AppHome);
         }
-
-        Settings!.Validate ();
-        ThemeManager.Validate ();
-        AppSettings!.Validate ();
     }
 
     // TODO: Rename to Loaded?
@@ -566,7 +573,7 @@ public static class ConfigurationManager
 
     [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
     internal static readonly SourceGenerationContext SerializerContext = new (
-                                                                              new JsonSerializerOptions
+                                                                              new()
                                                                               {
                                                                                   // Be relaxed
                                                                                   ReadCommentHandling = JsonCommentHandling.Skip,
@@ -638,7 +645,7 @@ public static class ConfigurationManager
                 if (!appSettingsConfigProperty.HasValue)
                 {
                     var appSettings = new AppSettingsScope ();
-                    appSettings.LoadCurrentValues ();
+                    appSettings.UpdateToCurrentValues ();
 
                     return appSettings;
                 }
@@ -710,8 +717,9 @@ public static class ConfigurationManager
         {
             if (_jsonErrors.Length > 0)
             {
-                Console.WriteLine (@"Terminal.Gui ConfigurationManager encountered these errors while reading configuration files" +
-                                   @"(set ThrowOnJsonErrors to have these caught during execution):");
+                Console.WriteLine (
+                                   @"Terminal.Gui ConfigurationManager encountered these errors while reading configuration files"
+                                   + @"(set ThrowOnJsonErrors to have these caught during execution):");
                 Console.WriteLine (_jsonErrors.ToString ());
             }
         }
@@ -783,8 +791,10 @@ public static class ConfigurationManager
 
             Debug.Assert (filtered is { });
 
-            IEnumerable<KeyValuePair<string, ConfigProperty>> configPropertiesByScope = filtered as KeyValuePair<string, ConfigProperty> [] ?? filtered.ToArray ();
+            IEnumerable<KeyValuePair<string, ConfigProperty>> configPropertiesByScope =
+                filtered as KeyValuePair<string, ConfigProperty> [] ?? filtered.ToArray ();
             Debug.Assert (configPropertiesByScope.All (v => !v.Value.HasValue));
+
             return configPropertiesByScope;
         }
     }

+ 25 - 15
Terminal.Gui/Configuration/SchemeManager.cs

@@ -7,25 +7,29 @@ using System.Text.Json.Serialization;
 namespace Terminal.Gui.Configuration;
 
 /// <summary>
-///     Holds the <see cref="Drawing.Scheme"/>s that define the <see cref="System.Attribute"/>s that are used by views to render
-///     themselves. A Scheme is a mapping from <see cref="Drawing.VisualRole"/>s (such as <see cref="Drawing.VisualRole.Focus"/>) to <see cref="System.Attribute"/>s.
+///     Holds the <see cref="Drawing.Scheme"/>s that define the <see cref="System.Attribute"/>s that are used by views to
+///     render
+///     themselves. A Scheme is a mapping from <see cref="Drawing.VisualRole"/>s (such as
+///     <see cref="Drawing.VisualRole.Focus"/>) to <see cref="System.Attribute"/>s.
 ///     A Scheme defines how a `View` should look based on its purpose (e.g. Menu or Dialog).
 /// </summary>
-public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<string, Scheme?>
+public sealed class SchemeManager // : INotifyCollectionChanged, IDictionary<string, Scheme?>
 {
 #pragma warning disable IDE1006 // Naming Styles
     private static readonly object _schemesLock = new ();
 #pragma warning restore IDE1006 // Naming Styles
 
     /// <summary>
-    ///     INTERNAL: Gets the hard-coded schemes defined by <see cref="View"/>. These are not loaded from the configuration files,
+    ///     INTERNAL: Gets the hard-coded schemes defined by <see cref="View"/>. These are not loaded from the configuration
+    ///     files,
     ///     but are hard-coded in the source code. Used for unit testing when ConfigurationManager is not initialized.
     /// </summary>
     /// <returns></returns>
     internal static ImmutableSortedDictionary<string, Scheme?>? GetHardCodedSchemes () { return Scheme.GetHardCodedSchemes ()!; }
 
     /// <summary>
-    ///     Use <see cref="AddScheme"/>, <see cref="GetScheme(Drawing.Schemes)"/>, <see cref="GetSchemeNames"/>, <see cref="GetSchemesForCurrentTheme"/>, etc... instead.
+    ///     Use <see cref="AddScheme"/>, <see cref="GetScheme(Drawing.Schemes)"/>, <see cref="GetSchemeNames"/>,
+    ///     <see cref="GetSchemesForCurrentTheme"/>, etc... instead.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
     [JsonConverter (typeof (DictionaryJsonConverter<Scheme?>))]
@@ -54,7 +58,7 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
     /// <summary>INTERNAL: The set method for <see cref="Schemes"/>.</summary>
     [RequiresUnreferencedCode ("Calls Terminal.Gui.ConfigProperty.UpdateFrom(Object)")]
     [RequiresDynamicCode ("Calls Terminal.Gui.ConfigProperty.UpdateFrom(Object)")]
-    private static void SetSchemes (Dictionary<string, Scheme?>? value)
+    internal static void SetSchemes (Dictionary<string, Scheme?>? value)
     {
         lock (_schemesLock)
         {
@@ -117,6 +121,7 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
     {
         // Convert schemeName to string via Enum api
         string? schemeNameString = SchemesToSchemeName (schemeName);
+
         if (schemeNameString is null)
         {
             throw new ArgumentException ($"Invalid scheme name: {schemeName}");
@@ -131,10 +136,7 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
     /// <param name="schemeName"></param>
     /// <returns></returns>
     /// <exception cref="ArgumentException"></exception>
-    public static Scheme GetScheme (string schemeName)
-    {
-        return GetSchemesForCurrentTheme ()! [schemeName]!;
-    }
+    public static Scheme GetScheme (string schemeName) { return GetSchemesForCurrentTheme ()! [schemeName]!; }
 
     /// <summary>
     ///     Gets the name of the specified <see cref="Schemes"/>. Will throw an exception if <paramref name="schemeName"/>
@@ -142,10 +144,7 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
     /// </summary>
     /// <param name="schemeName"></param>
     /// <returns>The name of scheme.</returns>
-    public static string? SchemesToSchemeName (Schemes schemeName)
-    {
-        return Enum.GetName (typeof (Schemes), schemeName);
-    }
+    public static string? SchemesToSchemeName (Schemes schemeName) { return Enum.GetName (typeof (Schemes), schemeName); }
 
     /// <summary>
     ///     Converts a string to a <see cref="Schemes"/> enum value.
@@ -158,11 +157,12 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
         {
             return value?.ToString ();
         }
+
         return null;
     }
 
     /// <summary>
-    ///     Get the dictionary schemes from the selected theme loaded from configuration.
+    ///     Get the dictionary of schemes from the current theme. Current means active.
     /// </summary>
     /// <returns></returns>
     public static Dictionary<string, Scheme?> GetSchemesForCurrentTheme ()
@@ -195,4 +195,14 @@ public sealed class SchemeManager// : INotifyCollectionChanged, IDictionary<stri
             return GetSchemes ()!.Keys.ToImmutableList ();
         }
     }
+
+    [RequiresUnreferencedCode ("Calls SetSchemes")]
+    [RequiresDynamicCode ("Calls SetSchemes")]
+    internal static void LoadToHardCodedDefaults ()
+    {
+        // BUGBUG: SchemeManager is broken and needs to be fixed to not have the hard coded schemes get overwritten.
+        // BUGBUG: This is a partial workaround
+        // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/4288
+        SetSchemes (GetHardCodedSchemes ()!.ToDictionary ());
+    }
 }

+ 22 - 26
Terminal.Gui/Configuration/Scope.cs

@@ -1,7 +1,7 @@
 #nullable enable
 using System.Collections.Concurrent;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
-using System.Text.Json;
 
 namespace Terminal.Gui.Configuration;
 
@@ -20,13 +20,12 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
     ///     will be <see langword="false"/>).
     /// </summary>
     [RequiresUnreferencedCode (
-        "Uses cached configuration properties filtered by type T. This is AOT-safe as long as T is one of the known scope types (SettingsScope, ThemeScope, AppSettingsScope).")]
-    public Scope () : base (StringComparer.InvariantCultureIgnoreCase)
-    {
-    }
+                                  "Uses cached configuration properties filtered by type T. This is AOT-safe as long as T is one of the known scope types (SettingsScope, ThemeScope, AppSettingsScope).")]
+    public Scope () : base (StringComparer.InvariantCultureIgnoreCase) { }
 
     /// <summary>
-    ///     INTERNAL: Adds a new ConfigProperty given a <paramref name="value"/>. Determines the correct PropertyInfo etc... by retrieving the
+    ///     INTERNAL: Adds a new ConfigProperty given a <paramref name="value"/>. Determines the correct PropertyInfo etc... by
+    ///     retrieving the
     ///     hard coded value for <paramref name="name"/>.
     /// </summary>
     /// <param name="name"></param>
@@ -42,20 +41,20 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
 
         TryAdd (name, ConfigProperty.CreateCopy (configProperty));
         this [name].PropertyValue = configProperty.PropertyValue;
-
     }
 
     internal ConfigProperty? GetHardCodedProperty (string name)
     {
         ConfigProperty? configProperty = ConfigurationManager.GetHardCodedConfigPropertiesByScope (typeof (T).Name)!
-                                                             .FirstOrDefault (hardCodedKeyValuePair => hardCodedKeyValuePair.Key == name).Value;
+                                                             .FirstOrDefault (hardCodedKeyValuePair => hardCodedKeyValuePair.Key == name)
+                                                             .Value;
 
         if (configProperty is null)
         {
             return null;
         }
 
-        ConfigProperty copy = ConfigProperty.CreateCopy (configProperty);
+        var copy = ConfigProperty.CreateCopy (configProperty);
         copy.PropertyValue = configProperty.PropertyValue;
 
         return copy;
@@ -64,17 +63,18 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
     internal ConfigProperty GetUninitializedProperty (string name)
     {
         ConfigProperty? configProperty = ConfigurationManager.GetUninitializedConfigPropertiesByScope (typeof (T).Name)!
-                                                             .FirstOrDefault (hardCodedKeyValuePair => hardCodedKeyValuePair.Key == name).Value;
+                                                             .FirstOrDefault (hardCodedKeyValuePair => hardCodedKeyValuePair.Key == name)
+                                                             .Value;
 
         if (configProperty is null)
         {
             throw new InvalidOperationException ($@"{name} is not a ConfigProperty.");
         }
-        ConfigProperty  copy = ConfigProperty.CreateCopy (configProperty);
+
+        var copy = ConfigProperty.CreateCopy (configProperty);
         copy.PropertyValue = configProperty.PropertyValue;
 
         return copy;
-
     }
 
     /// <summary>
@@ -82,7 +82,7 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
     ///     <see cref="ConfigurationPropertyAttribute"/> properties.
     /// </summary>
     [RequiresDynamicCode ("Uses reflection to retrieve property values")]
-    internal void LoadCurrentValues ()
+    internal void UpdateToCurrentValues ()
     {
         foreach (KeyValuePair<string, ConfigProperty> validProperties in this.Where (cp => cp.Value.PropertyInfo is { }))
         {
@@ -97,7 +97,7 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
     {
         foreach (KeyValuePair<string, ConfigProperty> hardCodedKeyValuePair in ConfigurationManager.GetHardCodedConfigPropertiesByScope (typeof (T).Name)!)
         {
-            ConfigProperty copy = ConfigProperty.CreateCopy (hardCodedKeyValuePair.Value);
+            var copy = ConfigProperty.CreateCopy (hardCodedKeyValuePair.Value);
             TryAdd (hardCodedKeyValuePair.Key, copy);
             this [hardCodedKeyValuePair.Key].PropertyValue = hardCodedKeyValuePair.Value.PropertyValue;
         }
@@ -127,7 +127,7 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
                 }
 
                 // Add an empty (HasValue = false) property to this scope
-                ConfigProperty copy = ConfigProperty.CreateCopy (prop.Value);
+                var copy = ConfigProperty.CreateCopy (prop.Value);
                 copy.PropertyValue = prop.Value.PropertyValue;
                 TryAdd (prop.Key, copy);
             }
@@ -160,32 +160,28 @@ public class Scope<T> : ConcurrentDictionary<string, ConfigProperty>
             if (propWithValue.Value.PropertyInfo != null)
             {
                 object? currentValue = propWithValue.Value.PropertyInfo.GetValue (null);
+                object? newValue = null;
 
                 // QUESTION: Should we avoid setting if currentValue == newValue?
 
                 if (propWithValue.Value.PropertyValue is Scope<T> scopeSource && currentValue is Scope<T> scopeDest)
                 {
-                    propWithValue.Value.PropertyInfo.SetValue (null, scopeDest.UpdateFrom (scopeSource));
+                    newValue = scopeDest.UpdateFrom (scopeSource);
                 }
                 else
                 {
                     // Use DeepCloner to create a deep copy of the property value
-                    object? val = DeepCloner.DeepClone (propWithValue.Value.PropertyValue);
-                    propWithValue.Value.PropertyInfo.SetValue (null, val);
+                    newValue = DeepCloner.DeepClone (propWithValue.Value.PropertyValue);
                 }
 
+                // Logging.Debug($"{propWithValue.Key}: {currentValue} -> {newValue}");
+                Debug.Assert (!propWithValue.Value.Immutable);
+                propWithValue.Value.PropertyInfo.SetValue (null, newValue);
+
                 set = true;
             }
         }
 
         return set;
     }
-
-    internal virtual void Validate ()
-    {
-        if (IsEmpty)
-        {
-            //throw new JsonException ($@"Empty!");
-        }
-    }
 }

+ 8 - 9
Terminal.Gui/Configuration/ScopeJsonConverter.cs

@@ -107,13 +107,12 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
             {
                 // It is not a config property. Maybe it's just a property on the Scope with [JsonInclude]
                 // like ScopeSettings.$schema.
-                // If so, don't add it to the dictionary but apply it to the underlying property on 
-                // the scopeT. 
-                // BUGBUG: This is a really bad design. The only time it's used is for $schema though.
+                // If so, don't add it to the dictionary but apply it to the underlying property on
+                // the scopeT.
+                // BUGBUG: This is terrible design. The only time it's used is for $schema though.
                 PropertyInfo? property = scope!.GetType ()
                                                .GetProperties ()
-                                               .Where (
-                                                       p =>
+                                               .Where (p =>
                                                        {
                                                            if (p.GetCustomAttribute (typeof (JsonIncludeAttribute)) is JsonIncludeAttribute { } jia)
                                                            {
@@ -143,6 +142,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
                 {
                     // Set the value of propertyName on the scopeT.
                     PropertyInfo prop = scope.GetType ().GetProperty (propertyName!)!;
+
                     prop.SetValue (scope, JsonSerializer.Deserialize (ref reader, prop.PropertyType, ConfigurationManager.SerializerContext));
                 }
                 else
@@ -168,8 +168,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
 
         IEnumerable<PropertyInfo> properties = scope!.GetType ()
                                                      .GetProperties ()
-                                                     .Where (
-                                                             p => p.GetCustomAttribute (typeof (JsonIncludeAttribute))
+                                                     .Where (p => p.GetCustomAttribute (typeof (JsonIncludeAttribute))
                                                                   != null
                                                             );
 
@@ -181,8 +180,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
         }
 
         foreach (KeyValuePair<string, ConfigProperty> p in from p in scope
-                                                               .Where (
-                                                                       cp =>
+                                                               .Where (cp =>
                                                                            cp.Value.PropertyInfo?.GetCustomAttribute (
                                                                                     typeof (
                                                                                         ConfigurationPropertyAttribute)
@@ -223,6 +221,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
             else
             {
                 object? prop = p.Value.PropertyValue;
+
                 if (prop == null)
                 {
                     writer.WriteNullValue ();

+ 38 - 26
Terminal.Gui/Configuration/ThemeManager.cs

@@ -22,18 +22,19 @@ public static class ThemeManager
     public static ThemeScope GetCurrentTheme () { return Themes! [Theme]; }
 
     /// <summary>
+    ///     INTERNAL: Getter for <see cref="Themes"/>.
     ///     Convenience method to get the themes dictionary. The themes dictionary is a dictionary of <see cref="ThemeScope"/>
     ///     objects, with the key being the name of the theme.
     /// </summary>
     /// <returns></returns>
     /// <exception cref="InvalidOperationException"></exception>
-    public static ConcurrentDictionary<string, ThemeScope> GetThemes ()
+    private static ConcurrentDictionary<string, ThemeScope> GetThemes ()
     {
         if (!ConfigurationManager.IsInitialized ())
         {
             // We're being called from the module initializer.
             // We need to provide a dictionary of themes containing the hard-coded theme.
-            return HardCodedThemes ()!;
+            return GetHardCodedThemes ()!;
         }
 
         if (ConfigurationManager.Settings is null)
@@ -48,14 +49,14 @@ public static class ThemeManager
                 return (themes.PropertyValue as ConcurrentDictionary<string, ThemeScope>)!;
             }
 
-            return HardCodedThemes ()!;
+            return GetHardCodedThemes ()!;
         }
 
         throw new InvalidOperationException ("Settings has no Themes property.");
     }
 
     /// <summary>
-    ///     Convenience method to get a list of theme names.
+    ///    INTERNAL: Convenience method to get a list of theme names.
     /// </summary>
     /// <returns></returns>
     /// <exception cref="InvalidOperationException"></exception>
@@ -65,7 +66,7 @@ public static class ThemeManager
         {
             // We're being called from the module initializer.
             // We need to provide a dictionary of themes containing the hard-coded theme.
-            return HardCodedThemes ()!.Keys.ToImmutableList ();
+            return GetHardCodedThemes ()!.Keys.ToImmutableList ();
         }
 
         if (ConfigurationManager.Settings is null)
@@ -86,7 +87,7 @@ public static class ThemeManager
         }
         else
         {
-            returnConcurrentDictionary = HardCodedThemes ();
+            returnConcurrentDictionary = GetHardCodedThemes ();
         }
 
         return returnConcurrentDictionary!.Keys
@@ -121,6 +122,11 @@ public static class ThemeManager
         internal set => SetThemes (value);
     }
 
+    /// <summary>
+    ///     INTERNAL: Setter for <see cref="Themes"/>.
+    /// </summary>
+    /// <param name="dictionary"></param>
+    /// <exception cref="InvalidOperationException"></exception>
     private static void SetThemes (ConcurrentDictionary<string, ThemeScope>? dictionary)
     {
         if (dictionary is { } && !dictionary.ContainsKey (DEFAULT_THEME_NAME))
@@ -138,7 +144,12 @@ public static class ThemeManager
         throw new InvalidOperationException ("Settings is null.");
     }
 
-    private static ConcurrentDictionary<string, ThemeScope>? HardCodedThemes ()
+    /// <summary>
+    ///     INTERNAL: Returns the hard-coded Themes dictionary.
+    /// </summary>
+    /// <returns></returns>
+    /// <exception cref="InvalidOperationException"></exception>
+    private static ConcurrentDictionary<string, ThemeScope>? GetHardCodedThemes ()
     {
         ThemeScope? hardCodedThemeScope = GetHardCodedThemeScope ();
 
@@ -151,10 +162,10 @@ public static class ThemeManager
     }
 
     /// <summary>
-    ///     Returns a dictionary of hard-coded ThemeScope properties.
+    ///     INTERNAL: Returns the ThemeScope containing the hard-coded Themes.
     /// </summary>
     /// <returns></returns>
-    private static ThemeScope? GetHardCodedThemeScope ()
+    private static ThemeScope GetHardCodedThemeScope ()
     {
         IEnumerable<KeyValuePair<string, ConfigProperty>>? hardCodedThemeProperties = ConfigurationManager.GetHardCodedConfigPropertiesByScope ("ThemeScope");
 
@@ -173,9 +184,9 @@ public static class ThemeManager
     }
 
     /// <summary>
-    ///     Since Theme is a dynamic property, we need to cache the value of the selected theme for when CM is not enabled.
+    ///     The name of the default theme ("Default").
     /// </summary>
-    internal const string DEFAULT_THEME_NAME = "Default";
+    public const string DEFAULT_THEME_NAME = "Default";
 
     /// <summary>
     ///     The currently selected theme. The backing store is <c><see cref="ConfigurationManager.Settings"/> ["Theme"]</c>.
@@ -256,13 +267,19 @@ public static class ThemeManager
     /// </summary>
     [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
     [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
-    internal static void UpdateToCurrentValues () { Themes! [Theme].LoadCurrentValues (); }
+    internal static void UpdateToCurrentValues ()
+    {
+        // BUGBUG: This corrupts _hardCodedDefaults. See #4288
+        Themes! [Theme].UpdateToCurrentValues ();
+    }
 
     /// <summary>
-    ///     INTERNAL: Resets all themes to the values the <see cref="ConfigurationPropertyAttribute"/> properties contained
-    ///     when the module was initialized.
+    ///     INTERNAL: Loads all Themes to their hard-coded default values.
     /// </summary>
-    internal static void ResetToHardCodedDefaults ()
+    [RequiresUnreferencedCode ("Calls SchemeManager.LoadToHardCodedDefaults")]
+    [RequiresDynamicCode ("Calls SchemeManager.LoadToHardCodedDefaults")]
+
+    internal static void LoadHardCodedDefaults ()
     {
         if (!ConfigurationManager.IsInitialized ())
         {
@@ -288,8 +305,14 @@ public static class ThemeManager
                                                                         },
                                                                         StringComparer.InvariantCultureIgnoreCase);
 
+        // BUGBUG: SchemeManager is broken and needs to be fixed to not have the hard coded schemes get overwritten.
+        // BUGBUG: This is a partial workaround
+        // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/4288
+        SchemeManager.LoadToHardCodedDefaults ();
+
         ConfigurationManager.Settings ["Themes"].PropertyValue = hardCodedThemes;
         ConfigurationManager.Settings ["Theme"].PropertyValue = DEFAULT_THEME_NAME;
+
     }
 
     /// <summary>Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.</summary>
@@ -302,15 +325,4 @@ public static class ThemeManager
 
     /// <summary>Raised when the selected theme has changed.</summary>
     public static event EventHandler<EventArgs<string>>? ThemeChanged;
-
-    /// <summary>
-    ///     Validates all themes in the <see cref="Themes"/> dictionary.
-    /// </summary>
-    public static void Validate ()
-    {
-        foreach (ThemeScope theme in Themes!.Values)
-        {
-            theme.Validate ();
-        }
-    }
 }

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

@@ -113,7 +113,7 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 
     /// <summary>Initializes a new instance of the <see cref="Color"/> color from a value in the <see cref="StandardColor"/> enum.</summary>
     /// <param name="colorName">The 16-color value.</param>
-    public Color (in StandardColor colorName) : this ((int)colorName) { }
+    public Color (in StandardColor colorName) : this (StandardColors.GetArgb (colorName)) { }
 
     /// <summary>
     ///     Initializes a new instance of the <see cref="Color"/> color from string. See

+ 24 - 4
Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs

@@ -100,6 +100,8 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput
     private readonly nint _outputHandle;
     private nint _screenBuffer;
     private readonly bool _isVirtualTerminal;
+    private readonly ConsoleColor _foreground;
+    private readonly ConsoleColor _background;
 
     public WindowsOutput ()
     {
@@ -117,8 +119,16 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput
 
         if (_isVirtualTerminal)
         {
-            //Enable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
+            if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
+            {
+                //Enable alternative screen buffer.
+                Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
+            }
+            else
+            {
+                _foreground = Console.ForegroundColor;
+                _background = Console.BackgroundColor;
+            }
         }
         else
         {
@@ -502,8 +512,18 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput
 
         if (_isVirtualTerminal)
         {
-            //Disable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
+            if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
+            {
+                //Disable alternative screen buffer.
+                Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
+            }
+            else
+            {
+                // Simulate restoring the color and clearing the screen.
+                Console.ForegroundColor = _foreground;
+                Console.BackgroundColor = _background;
+                Console.Clear ();
+            }
         }
         else
         {

+ 3 - 3
Terminal.Gui/ViewBase/View.Drawing.Scheme.cs

@@ -125,16 +125,16 @@ public partial class View
         ResultEventArgs<Scheme?> args = new ();
 
         return CWPWorkflowHelper.ExecuteWithResult (
-                                                    args =>
+                                                    resultEventArgs =>
                                                     {
                                                         bool cancelled = OnGettingScheme (out Scheme? newScheme);
-                                                        args.Result = newScheme;
+                                                        resultEventArgs.Result = newScheme;
 
                                                         return cancelled;
                                                     },
                                                     GettingScheme,
                                                     args,
-                                                    DefaultAction);
+                                                    DefaultAction)!;
 
         Scheme DefaultAction ()
         {

+ 19 - 3
Terminal.Gui/ViewBase/View.Layout.cs

@@ -239,6 +239,8 @@ public partial class View // Layout APIs
             _x = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (X)} cannot be null");
 
             PosDimSet ();
+
+            NeedsClearScreenNextIteration ();
         }
     }
 
@@ -281,6 +283,8 @@ public partial class View // Layout APIs
 
             _y = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Y)} cannot be null");
             PosDimSet ();
+
+            NeedsClearScreenNextIteration ();
         }
     }
 
@@ -339,6 +343,8 @@ public partial class View // Layout APIs
                                               OnHeightChanged,
                                               HeightChanged,
                                               out Dim _);
+
+            NeedsClearScreenNextIteration ();
         }
     }
 
@@ -425,6 +431,17 @@ public partial class View // Layout APIs
                                               OnWidthChanged,
                                               WidthChanged,
                                               out Dim _);
+
+            NeedsClearScreenNextIteration ();
+        }
+    }
+
+    private void NeedsClearScreenNextIteration ()
+    {
+        if (Application.Top is { } && Application.Top == this && Application.TopLevels.Count == 1)
+        {
+            // If this is the only TopLevel, we need to redraw the screen
+            Application.ClearScreenNextIteration = true;
         }
     }
 
@@ -653,10 +670,9 @@ public partial class View // Layout APIs
             {
                 SuperView?.SetNeedsDraw ();
             }
-            else if (Application.TopLevels.Count == 1)
+            else
             {
-                // If this is the only TopLevel, we need to redraw the screen
-                Application.ClearScreenNextIteration = true;
+                NeedsClearScreenNextIteration ();
             }
         }
 

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

@@ -378,7 +378,7 @@ public partial class View : IDisposable, ISupportInitializeNotification
             }
             else
             {
-                Application.ClearScreenNextIteration = true;
+                NeedsClearScreenNextIteration ();
             }
         }
     }

+ 3 - 2
Tests/IntegrationTests/UICatalog/ScenarioTests.cs

@@ -41,8 +41,9 @@ public class ScenarioTests : TestsAllViews
 
         _output.WriteLine ($"Running Scenario '{scenarioType}'");
         var scenario = Activator.CreateInstance (scenarioType) as Scenario;
+        var scenarioName = scenario!.GetName ();
 
-        uint abortTime = 2000;
+        uint abortTime = 2200;
         object? timeout = null;
         var initialized = false;
         var shutdownGracefully = false;
@@ -70,7 +71,7 @@ public class ScenarioTests : TestsAllViews
         Assert.True (initialized);
 
 
-        Assert.True (shutdownGracefully, $"Scenario Failed to Quit with {quitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
+        Assert.True (shutdownGracefully, $"Scenario '{scenarioName}' Failed to Quit with {quitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
 
 #if DEBUG_IDISPOSABLE
         Assert.Empty (View.Instances);

+ 1 - 1
Tests/StressTests/ScenariosStressTests.cs

@@ -33,7 +33,7 @@ public class ScenariosStressTests : TestsAllViews
         Assert.Null (_timeoutLock);
         _timeoutLock = new ();
 
-        ConfigurationManager.Disable();
+        ConfigurationManager.Disable(true);
 
         // If a previous test failed, this will ensure that the Application is in a clean state
         Application.ResetState (true);

+ 15 - 1
Tests/UnitTests/Application/ApplicationScreenTests.cs

@@ -47,7 +47,7 @@ public class ApplicationScreenTests
         Assert.Equal (0, clearedContentsRaised);
 
         // Act
-        Application.Top.SetNeedsLayout ();
+        Application.Top!.SetNeedsLayout ();
         Application.LayoutAndDraw ();
 
         // Assert
@@ -67,6 +67,20 @@ public class ApplicationScreenTests
         // Assert
         Assert.Equal (2, clearedContentsRaised);
 
+        // Act
+        Application.Top.Y = 1;
+        Application.LayoutAndDraw ();
+
+        // Assert
+        Assert.Equal (3, clearedContentsRaised);
+
+        // Act
+        Application.Top.Height = 10;
+        Application.LayoutAndDraw ();
+
+        // Assert
+        Assert.Equal (4, clearedContentsRaised);
+
         Application.End (rs);
 
         return;

+ 1 - 1
Tests/UnitTests/Application/SynchronizatonContextTests.cs

@@ -10,7 +10,7 @@ public class SyncrhonizationContextTests
     public void SynchronizationContext_CreateCopy ()
     {
         ConsoleDriver.RunningUnitTests = true;
-        Application.Init ();
+        Application.Init (null, "fake");
         SynchronizationContext context = SynchronizationContext.Current;
         Assert.NotNull (context);
 

+ 2 - 0
Tests/UnitTests/AutoInitShutdownAttribute.cs

@@ -114,6 +114,8 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
     {
         Debug.WriteLine ($"Before: {methodUnderTest?.Name ?? "Unknown Test"}");
 
+        Debug.Assert (!CM.IsEnabled, "A previous test left ConfigurationManager enabled!");
+
         // Disable & force the ConfigurationManager to reset to its hardcoded defaults
         CM.Disable (true);
 

+ 3 - 3
Tests/UnitTests/Configuration/AppScopeTests.cs

@@ -44,7 +44,7 @@ public class AppSettingsScopeTests
         Assert.Null (AppSettings! ["AppSettingsTestClass.NullableValueProperty"].PropertyValue);
 
         AppSettingsTestClass.NullableValueProperty = true;
-        ResetToCurrentValues ();
+        UpdateToCurrentValues ();
         Assert.True (AppSettingsTestClass.NullableValueProperty);
         Assert.NotEmpty (AppSettings);
         Assert.True (AppSettings ["AppSettingsTestClass.NullableValueProperty"].PropertyValue as bool?);
@@ -74,7 +74,7 @@ public class AppSettingsScopeTests
         AppSettingsTestClass.NullableValueProperty = null;
         Assert.Null (AppSettingsTestClass.NullableValueProperty);
 
-        ResetToCurrentValues ();
+        ResetToHardCodedDefaults ();
         Assert.Null (AppSettings! ["AppSettingsTestClass.NullableValueProperty"].PropertyValue);
 
         Apply ();
@@ -82,7 +82,7 @@ public class AppSettingsScopeTests
         Assert.Null (AppSettingsTestClass.NullableValueProperty);
 
         AppSettingsTestClass.NullableValueProperty = true;
-        ResetToCurrentValues ();
+        UpdateToCurrentValues ();
         Assert.True ((bool)AppSettings! ["AppSettingsTestClass.NullableValueProperty"].PropertyValue!);
         Assert.True (AppSettingsTestClass.NullableValueProperty);
         Assert.NotNull (AppSettingsTestClass.NullableValueProperty);

+ 426 - 41
Tests/UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -1,9 +1,9 @@
 using System.Collections.Frozen;
+using System.Collections.Immutable;
 using System.Diagnostics;
 using System.Reflection;
 using System.Text;
 using System.Text.Json;
-using ColorHelper;
 using Xunit.Abstractions;
 using static Terminal.Gui.Configuration.ConfigurationManager;
 using File = System.IO.File;
@@ -49,6 +49,25 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
         Disable (true);
     }
 
+    [Fact]
+    public void GetHardCodedDefaultCache_Always_Returns_Same_Ref ()
+    {
+        // It's important it always returns the same cache ref, so no copies are made
+        // Otherwise it's a big performance hit
+        Assert.False (IsEnabled);
+
+        try
+        {
+            FrozenDictionary<string, ConfigProperty> initialCache = GetHardCodedConfigPropertyCache ();
+            FrozenDictionary<string, ConfigProperty> cache = GetHardCodedConfigPropertyCache ();
+            Assert.Equal (initialCache, cache);
+        }
+        finally
+        {
+            Disable (true);
+        }
+    }
+
     [Fact]
     public void HardCodedDefaultCache_Properties_Are_Immutable ()
     {
@@ -74,7 +93,6 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 
             // Assert
             FrozenDictionary<string, ConfigProperty> cache = GetHardCodedConfigPropertyCache ();
-            Assert.Equal (initialCache, cache);
             Assert.True (initialCache ["Application.QuitKey"].Immutable);
             Assert.Equal (Key.Esc, (Key)initialCache ["Application.QuitKey"].PropertyValue);
         }
@@ -94,12 +112,11 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
         Assert.NotNull (Settings);
     }
 
-
     [Fact]
     public void Disable_With_ResetToHardCodedDefaults_True_Works_When_Disabled ()
     {
-        Assert.False (ConfigurationManager.IsEnabled);
-        ConfigurationManager.Disable (true);
+        Assert.False (IsEnabled);
+        Disable (true);
     }
 
     [Fact]
@@ -111,9 +128,144 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 
         Assert.NotNull (Settings);
 
-        Disable ();
+        Disable (true);
     }
 
+    [Fact]
+    public void Enable_HardCoded_Resets_Schemes_After_Runtime_Config ()
+    {
+        Assert.False (IsEnabled);
+
+        try
+        {
+            // Arrange: Start from hard-coded defaults and capture baseline scheme values.
+            Enable (ConfigLocations.HardCoded);
+            Dictionary<string, Scheme> schemes = SchemeManager.GetSchemes ();
+            Assert.NotNull (schemes);
+            Assert.NotEmpty (schemes);
+            Color baselineFg = schemes ["Base"].Normal.Foreground;
+            Color baselineBg = schemes ["Base"].Normal.Background;
+
+            // Sanity: defaults should be stable
+            Assert.NotEqual (default (Color), baselineFg);
+            Assert.NotEqual (default (Color), baselineBg);
+
+            // Act: Override the Base scheme via runtime JSON and apply
+            ThrowOnJsonErrors = true;
+
+            RuntimeConfig = """
+                            {
+                              "Themes": [
+                                {
+                                  "Default": {
+                                    "Schemes": [
+                                      {
+                                        "Base": {
+                                          "Normal": {
+                                            "Foreground": "Black",
+                                            "Background": "Gray"
+                                          }
+                                        }
+                                      }
+                                    ]
+                                  }
+                                }
+                              ]
+                            }
+                            """;
+            Load (ConfigLocations.Runtime);
+            Apply ();
+
+            // Verify override took effect
+            Dictionary<string, Scheme> overridden = SchemeManager.GetSchemes ();
+            Assert.Equal (Color.Black, overridden ["Base"].Normal.Foreground);
+            Assert.Equal (Color.Gray, overridden ["Base"].Normal.Background);
+
+            // Now simulate "CM.Enable(true)" semantics: re-enable with HardCoded to reset
+            Disable ();
+            Enable (ConfigLocations.HardCoded);
+
+            // Assert: schemes are reset to the original hard-coded baseline
+            Dictionary<string, Scheme> reset = SchemeManager.GetSchemes ();
+            Assert.Equal (baselineFg, reset ["Base"].Normal.Foreground);
+            Assert.Equal (baselineBg, reset ["Base"].Normal.Background);
+        }
+        finally
+        {
+            Disable (true);
+            Application.ResetState (true);
+        }
+    }
+
+    [Fact]
+    public void Enable_HardCoded_Resets_Theme_Dictionary_And_Selection ()
+    {
+        Assert.False (IsEnabled);
+
+        try
+        {
+            // Arrange: Enable defaults
+            Enable (ConfigLocations.HardCoded);
+            Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
+            Assert.Single (ThemeManager.Themes!);
+            Assert.True (ThemeManager.Themes.ContainsKey (ThemeManager.DEFAULT_THEME_NAME));
+
+            // Act: Load a runtime config that introduces a custom theme and selects it
+            ThrowOnJsonErrors = true;
+
+            RuntimeConfig = """
+                            {
+                              "Theme": "Custom",
+                              "Themes": [
+                                {
+                                  "Custom": {
+                                    "Schemes": [
+                                      {
+                                        "Base": {
+                                          "Normal": {
+                                            "Foreground": "Yellow",
+                                            "Background": "Black"
+                                          }
+                                        }
+                                      }
+                                    ]
+                                  }
+                                }
+                              ]
+                            }
+                            """;
+
+            // Capture dynamically created hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!;
+
+            Color hardCodedBaseNormalFg = hardCodedSchemes ["Base"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ());
+
+            Load (ConfigLocations.Runtime);
+            Apply ();
+
+            // Verify the runtime selection took effect
+            Assert.Equal ("Custom", ThemeManager.Theme);
+
+            // Now simulate "CM.Enable(true)" semantics: re-enable with HardCoded to reset
+            Disable ();
+            Enable (ConfigLocations.HardCoded);
+
+            // Assert: selection and dictionary have been reset to hard-coded defaults
+            Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
+            Assert.Single (ThemeManager.Themes!);
+            Assert.True (ThemeManager.Themes.ContainsKey (ThemeManager.DEFAULT_THEME_NAME));
+
+            // Also assert the Base scheme is back to defaults (sanity check)
+            Scheme baseScheme = SchemeManager.GetSchemes () ["Base"];
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), SchemeManager.GetSchemes () ["Base"]!.Normal.Foreground.ToString ());
+        }
+        finally
+        {
+            Disable (true);
+            Application.ResetState (true);
+        }
+    }
 
     [Fact]
     public void Apply_Applies_Theme ()
@@ -132,11 +284,11 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
         theme ["FrameView.DefaultBorderStyle"].PropertyValue = LineStyle.Double;
 
         ThemeManager.Theme = "testTheme";
-        ConfigurationManager.Apply ();
+        Apply ();
 
         Assert.Equal (LineStyle.Double, FrameView.DefaultBorderStyle);
 
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 
     [Fact]
@@ -171,7 +323,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 
         Applied -= ConfigurationManagerApplied;
 
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
         Application.ResetState (true);
     }
 
@@ -254,7 +406,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 
             // act
             RuntimeConfig = """
-                            
+
                                     {
                                           "Application.QuitKey": "Ctrl-Q"
                                     }
@@ -288,7 +440,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             Updated += ConfigurationManagerUpdated;
 
             // Act
-            ResetToCurrentValues ();
+            UpdateToCurrentValues ();
 
             // assert
             Assert.True (fired);
@@ -361,30 +513,260 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void ResetToCurrentValues_Enabled_Resets ()
+    public void ResetToHardCodedDefaults_Resets ()
     {
         Assert.False (IsEnabled);
 
-        // Act
-        Enable (ConfigLocations.HardCoded);
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
 
-        Application.QuitKey = Key.A;
+            // Capture dynamically created hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemesViaSchemeManager = SchemeManager.GetHardCodedSchemes ()!;
 
-        ResetToCurrentValues ();
+            Dictionary<string, Scheme> hardCodedSchemes =
+                GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary<string, Scheme>;
 
-        Assert.Equal (Key.A, (Key)Settings! ["Application.QuitKey"].PropertyValue);
-        Assert.NotNull (Settings);
-        Assert.NotNull (AppSettings);
-        Assert.NotNull (ThemeManager.Themes);
+            Color hardCodedBaseNormalFg = hardCodedSchemesViaSchemeManager ["Base"].Normal.Foreground;
 
-        // Default Theme should be "Default"
-        Assert.Single (ThemeManager.Themes);
-        Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
+            Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ());
 
-        ResetToHardCodedDefaults ();
-        Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue);
-        Disable ();
-        Application.ResetState (true);
+            // Capture current scheme colors
+            Dictionary<string, Scheme> currentSchemes = SchemeManager.GetSchemes ()!;
+
+            Color currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground;
+
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), currentBaseNormalFg.ToString ());
+
+            // Arrange
+            var json = @"
+{
+  ""$schema"": ""https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"",
+  ""Application.QuitKey"": ""Alt-Z"",
+  ""Theme"": ""Default"",
+  ""Themes"": [
+    {
+      ""Default"": {
+        ""MessageBox.DefaultButtonAlignment"": ""End"",
+        ""Schemes"": [
+          {
+            ""TopLevel"": {
+              ""Normal"": {
+                ""Foreground"": ""BrightGreen"",
+                ""Background"": ""Black""
+              },
+              ""Focus"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""Cyan""
+              },
+              ""HotNormal"": {
+                ""Foreground"": ""Yellow"",
+                ""Background"": ""Black""
+              },
+              ""HotFocus"": {
+                ""Foreground"": ""Blue"",
+                ""Background"": ""Cyan""
+              },
+              ""Disabled"": {
+                ""Foreground"": ""DarkGray"",
+                ""Background"": ""Black""
+              }
+            }
+          },
+          {
+            ""Base"": {
+              ""Normal"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""Blue""
+              },
+              ""Focus"": {
+                ""Foreground"": ""Black"",
+                ""Background"": ""Gray""
+              },
+              ""HotNormal"": {
+                ""Foreground"": ""BrightCyan"",
+                ""Background"": ""Blue""
+              },
+              ""HotFocus"": {
+                ""Foreground"": ""BrightBlue"",
+                ""Background"": ""Gray""
+              },
+              ""Disabled"": {
+                ""Foreground"": ""DarkGray"",
+                ""Background"": ""Blue""
+              }
+            }
+          },
+          {
+            ""Dialog"": {
+              ""Normal"": {
+                ""Foreground"": ""Black"",
+                ""Background"": ""Gray""
+              },
+              ""Focus"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""DarkGray""
+              },
+              ""HotNormal"": {
+                ""Foreground"": ""Blue"",
+                ""Background"": ""Gray""
+              },
+              ""HotFocus"": {
+                ""Foreground"": ""BrightYellow"",
+                ""Background"": ""DarkGray""
+              },
+              ""Disabled"": {
+                ""Foreground"": ""Gray"",
+                ""Background"": ""DarkGray""
+              }
+            }
+          },
+          {
+            ""Menu"": {
+              ""Normal"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""DarkGray""
+              },
+              ""Focus"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""Black""
+              },
+              ""HotNormal"": {
+                ""Foreground"": ""BrightYellow"",
+                ""Background"": ""DarkGray""
+              },
+              ""HotFocus"": {
+                ""Foreground"": ""BrightYellow"",
+                ""Background"": ""Black""
+              },
+              ""Disabled"": {
+                ""Foreground"": ""Gray"",
+                ""Background"": ""DarkGray""
+              }
+            }
+          },
+          {
+            ""Error"": {
+              ""Normal"": {
+                ""Foreground"": ""Red"",
+                ""Background"": ""White""
+              },
+              ""Focus"": {
+                ""Foreground"": ""Black"",
+                ""Background"": ""BrightRed""
+              },
+              ""HotNormal"": {
+                ""Foreground"": ""Black"",
+                ""Background"": ""White""
+              },
+              ""HotFocus"": {
+                ""Foreground"": ""White"",
+                ""Background"": ""BrightRed""
+              },
+              ""Disabled"": {
+                ""Foreground"": ""DarkGray"",
+                ""Background"": ""White""
+              }
+            }
+          }
+        ]
+      }
+    }
+  ]
+}					
+			";
+
+           // ResetToCurrentValues ();
+
+            ThrowOnJsonErrors = true;
+            ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
+
+            Assert.Equal ("Default", ThemeManager.Theme);
+            Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
+            Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings! ["Application.QuitKey"].PropertyValue)!.KeyCode);
+            Assert.Equal (Alignment.Center, MessageBox.DefaultButtonAlignment);
+
+            // Get current scheme colors again
+            currentSchemes = SchemeManager.GetSchemes ()!;
+
+            currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground;
+
+            Assert.Equal (Color.White.ToString (), currentBaseNormalFg.ToString ());
+
+            // Now Apply
+            Apply ();
+
+            Assert.Equal ("Default", ThemeManager.Theme);
+            Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode);
+            Assert.Equal (Alignment.End, MessageBox.DefaultButtonAlignment);
+
+            Assert.Equal (Color.White.ToString (), currentBaseNormalFg.ToString ());
+
+            // Reset
+            ResetToHardCodedDefaults ();
+
+            hardCodedSchemes =
+                GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary<string, Scheme>;
+            hardCodedBaseNormalFg = hardCodedSchemes! ["Base"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ());
+
+            FrozenDictionary<string, ConfigProperty> hardCodedCache = GetHardCodedConfigPropertyCache ()!;
+
+            Assert.Equal (hardCodedCache ["Theme"].PropertyValue, ThemeManager.Theme);
+            Assert.Equal (hardCodedCache ["Application.QuitKey"].PropertyValue, Application.QuitKey);
+
+            // Themes
+            Assert.Equal (hardCodedCache ["MessageBox.DefaultButtonAlignment"].PropertyValue, MessageBox.DefaultButtonAlignment);
+
+            Assert.Equal (GetHardCodedConfigPropertyCache ()! ["MessageBox.DefaultButtonAlignment"].PropertyValue, MessageBox.DefaultButtonAlignment);
+
+            // Schemes
+            currentSchemes = SchemeManager.GetSchemes ()!;
+            currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground;
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), currentBaseNormalFg.ToString ());
+
+            Scheme baseScheme = SchemeManager.GetScheme ("Base");
+
+            Attribute attr = baseScheme.Normal;
+
+            // Use ToString so Assert.Equal shows the actual vs expected values on failure
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), attr.Foreground.ToString ());
+        }
+        finally
+        {
+            output.WriteLine ("Disabling CM to clean up.");
+
+            Disable (true);
+        }
+    }
+
+    [Fact (Skip = "ResetToCurrentValues corrupts hard coded cache")]
+    public void ResetToCurrentValues_Enabled_Resets ()
+    {
+        Assert.False (IsEnabled);
+
+        try
+        {
+            // Act
+            Enable (ConfigLocations.HardCoded);
+
+            Application.QuitKey = Key.A;
+
+            UpdateToCurrentValues ();
+
+            Assert.Equal (Key.A, (Key)Settings! ["Application.QuitKey"].PropertyValue);
+            Assert.NotNull (Settings);
+            Assert.NotNull (AppSettings);
+            Assert.NotNull (ThemeManager.Themes);
+
+            // Default Theme should be "Default"
+            Assert.Single (ThemeManager.Themes);
+            Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
+        }
+        finally
+        {
+            Disable (true);
+        }
     }
 
     [Fact]
@@ -421,7 +803,6 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 
             // Assert - the runtime config should win due to precedence
             Assert.Equal (Key.Q.WithAlt, (Key)Settings! ["Application.QuitKey"].PropertyValue);
-
         }
         finally
         {
@@ -504,16 +885,20 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             // test that all ConfigProperties have our attribute
             Assert.All (
                         Settings,
-                        item => Assert.Contains (item.Value.PropertyInfo!.CustomAttributes, a => a.AttributeType
-                                                                                                       == typeof (ConfigurationPropertyAttribute)
-));
+                        item => Assert.Contains (
+                                                 item.Value.PropertyInfo!.CustomAttributes,
+                                                 a => a.AttributeType
+                                                      == typeof (ConfigurationPropertyAttribute)
+                                                ));
 
 #pragma warning disable xUnit2030
-            Assert.DoesNotContain (Settings, cp => cp.Value.PropertyInfo!.GetCustomAttribute (
-                                                                                           typeof (ConfigurationPropertyAttribute)
-                                                                                          )
-                                                == null
-);
+            Assert.DoesNotContain (
+                                   Settings,
+                                   cp => cp.Value.PropertyInfo!.GetCustomAttribute (
+                                                                                    typeof (ConfigurationPropertyAttribute)
+                                                                                   )
+                                         == null
+                                  );
 #pragma warning restore xUnit2030
 
             // Application is a static class
@@ -840,7 +1225,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void UpdateFromJson ()
+    public void SourcesManager_Load_FromJson_Loads ()
     {
         Assert.False (IsEnabled);
 
@@ -986,7 +1371,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 }					
 			";
 
-            ResetToCurrentValues ();
+            //ResetToCurrentValues ();
             ThrowOnJsonErrors = true;
 
             ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
@@ -997,7 +1382,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings! ["Application.QuitKey"].PropertyValue)!.KeyCode);
             Assert.Equal (Alignment.Center, MessageBox.DefaultButtonAlignment);
 
-            // Now re-apply
+            // Now Apply
             Apply ();
 
             Assert.Equal ("Default", ThemeManager.Theme);
@@ -1009,9 +1394,9 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
         }
         finally
         {
-            Disable (resetToHardCodedDefaults: true);
+            output.WriteLine ("Disabling CM to clean up.");
 
+            Disable (true);
         }
     }
-
 }

+ 41 - 34
Tests/UnitTests/Configuration/GlyphTests.cs

@@ -10,39 +10,46 @@ public class GlyphTests
     [Fact]
     public void Apply_Applies_Over_Defaults ()
     {
-        // arrange
-        Enable (ConfigLocations.HardCoded);
-
-        Assert.Equal ((Rune)'⟦', Glyphs.LeftBracket);
-
-        var glyph = (Rune)ThemeManager.GetCurrentTheme () ["Glyphs.LeftBracket"].PropertyValue!;
-        Assert.Equal ((Rune)'⟦', glyph);
-
-        ThrowOnJsonErrors = true;
-
-        // act
-        RuntimeConfig = """
-                   {
-                       "Themes": [
-                           {
-                             "Default": 
-                                {
-                                    "Glyphs.LeftBracket": "["
-                                }
-                           }
-                       ]
-                   }
-                   """;
-
-        Load (ConfigLocations.Runtime);
-        Apply ();
-
-        // assert
-        glyph = (Rune)ThemeManager.GetCurrentTheme () ["Glyphs.LeftBracket"].PropertyValue!;
-        Assert.Equal ((Rune)'[', glyph);
-        Assert.Equal ((Rune)'[', Glyphs.LeftBracket);
-
-        // clean up
-        Disable (resetToHardCodedDefaults: true);
+        try
+        {
+            // arrange
+            Enable (ConfigLocations.HardCoded);
+
+            Assert.Equal ((Rune)'⟦', Glyphs.LeftBracket);
+
+            var glyph = (Rune)ThemeManager.GetCurrentTheme () ["Glyphs.LeftBracket"].PropertyValue!;
+            Assert.Equal ((Rune)'⟦', glyph);
+
+            ThrowOnJsonErrors = true;
+
+            // act
+            RuntimeConfig = """
+                            {
+                                "Themes": [
+                                    {
+                                      "Default": 
+                                         {
+                                             "Glyphs.LeftBracket": "["
+                                         }
+                                    }
+                                ]
+                            }
+                            """;
+
+            Load (ConfigLocations.Runtime);
+
+            Apply ();
+
+            // assert
+            glyph = (Rune)ThemeManager.GetCurrentTheme () ["Glyphs.LeftBracket"].PropertyValue!;
+            Assert.Equal ((Rune)'[', glyph);
+            Assert.Equal ((Rune)'[', Glyphs.LeftBracket);
+        }
+        finally
+        {
+            // clean up
+            Disable (resetToHardCodedDefaults: true);
+
+        }
     }
 }

+ 502 - 32
Tests/UnitTests/Configuration/SchemeManagerTests.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.Collections.Concurrent;
+using System.Collections.Frozen;
 using System.Collections.Immutable;
 using System.Text.Json;
 using static Terminal.Gui.Configuration.ConfigurationManager;
@@ -25,27 +26,40 @@ public class SchemeManagerTests
     [Fact]
     public void GetSchemes_Enabled_Gets_Current ()
     {
-        Enable (ConfigLocations.HardCoded);
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
 
-        Dictionary<string, Scheme?>? schemes = SchemeManager.GetSchemesForCurrentTheme ();
-        Assert.NotNull (schemes);
-        Assert.NotNull (schemes ["Base"]);
-        Assert.True (schemes!.ContainsKey ("Base"));
-        Assert.True (schemes.ContainsKey ("base"));
+            Dictionary<string, Scheme?>? schemes = SchemeManager.GetSchemesForCurrentTheme ();
+            Assert.NotNull (schemes);
+            Assert.NotNull (schemes ["Base"]);
+            Assert.True (schemes!.ContainsKey ("Base"));
+            Assert.True (schemes.ContainsKey ("base"));
 
-        Assert.Equal (SchemeManager.GetSchemes (), schemes);
+            Assert.Equal (SchemeManager.GetSchemes (), schemes);
 
-        Disable (true);
+        }
+        finally
+        {
+            Disable (true);
+        }
     }
 
     [Fact]
     public void GetSchemes_Get_Schemes_After_Load ()
     {
-        Enable (ConfigLocations.HardCoded);
-        Load (ConfigLocations.All);
-        Apply ();
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
+            Load (ConfigLocations.All);
+            Apply ();
 
-        Assert.Equal (SchemeManager.GetSchemes (), SchemeManager.GetSchemesForCurrentTheme ());
+            Assert.Equal (SchemeManager.GetSchemes (), SchemeManager.GetSchemesForCurrentTheme ());
+        }
+        finally
+        {
+            Disable (true);
+        }
     }
 
 
@@ -57,6 +71,72 @@ public class SchemeManagerTests
         Assert.Equal (Scheme.GetHardCodedSchemes (), actual: hardCoded!);
     }
 
+    [Fact]
+    public void GetHardCodedSchemes_Have_Expected_Normal_Attributes ()
+    {
+        var schemes = SchemeManager.GetHardCodedSchemes ();
+        Assert.NotNull (schemes);
+
+        // Base
+        var baseScheme = schemes! ["Base"];
+        Assert.NotNull (baseScheme);
+        Assert.Equal (new Attribute (StandardColor.LightBlue, StandardColor.RaisinBlack), baseScheme!.Normal);
+
+        // Dialog
+        var dialogScheme = schemes ["Dialog"];
+        Assert.NotNull (dialogScheme);
+        Assert.Equal (new Attribute (StandardColor.LightSkyBlue, StandardColor.OuterSpace), dialogScheme!.Normal);
+
+        // Error
+        var errorScheme = schemes ["Error"];
+        Assert.NotNull (errorScheme);
+        Assert.Equal (new Attribute (StandardColor.IndianRed, StandardColor.RaisinBlack), errorScheme!.Normal);
+
+        // Menu (Bold style)
+        var menuScheme = schemes ["Menu"];
+        Assert.NotNull (menuScheme);
+        Assert.Equal (new Attribute (StandardColor.Charcoal, StandardColor.LightBlue, TextStyle.Bold), menuScheme!.Normal);
+
+        // Toplevel
+        var toplevelScheme = schemes ["Toplevel"];
+        Assert.NotNull (toplevelScheme);
+        Assert.Equal (new Attribute (StandardColor.CadetBlue, StandardColor.Charcoal).ToString (), toplevelScheme!.Normal.ToString ());
+    }
+
+
+    [Fact]
+    public void GetHardCodedSchemes_Have_Expected_Normal_Attributes_LoadHardCodedDefaults ()
+    {
+        LoadHardCodedDefaults ();
+        var schemes = SchemeManager.GetHardCodedSchemes ();
+
+        Assert.NotNull (schemes);
+
+        // Base
+        var baseScheme = schemes! ["Base"];
+        Assert.NotNull (baseScheme);
+        Assert.Equal (new Attribute (StandardColor.LightBlue, StandardColor.RaisinBlack), baseScheme!.Normal);
+
+        // Dialog
+        var dialogScheme = schemes ["Dialog"];
+        Assert.NotNull (dialogScheme);
+        Assert.Equal (new Attribute (StandardColor.LightSkyBlue, StandardColor.OuterSpace), dialogScheme!.Normal);
+
+        // Error
+        var errorScheme = schemes ["Error"];
+        Assert.NotNull (errorScheme);
+        Assert.Equal (new Attribute (StandardColor.IndianRed, StandardColor.RaisinBlack), errorScheme!.Normal);
+
+        // Menu (Bold style)
+        var menuScheme = schemes ["Menu"];
+        Assert.NotNull (menuScheme);
+        Assert.Equal (new Attribute (StandardColor.Charcoal, StandardColor.LightBlue, TextStyle.Bold), menuScheme!.Normal);
+
+        // Toplevel
+        var toplevelScheme = schemes ["Toplevel"];
+        Assert.NotNull (toplevelScheme);
+        Assert.Equal (new Attribute (StandardColor.CadetBlue, StandardColor.Charcoal).ToString (), toplevelScheme!.Normal.ToString ());
+    }
     [Fact]
     public void Not_Case_Sensitive_Disabled ()
     {
@@ -72,19 +152,25 @@ public class SchemeManagerTests
     public void Not_Case_Sensitive_Enabled ()
     {
         Assert.False (IsEnabled);
-        Enable (ConfigLocations.HardCoded);
 
-        Assert.True (SchemeManager.GetSchemesForCurrentTheme ()!.ContainsKey ("Base"));
-        Assert.True (SchemeManager.GetSchemesForCurrentTheme ()!.ContainsKey ("base"));
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
 
-        ResetToHardCodedDefaults ();
-        Dictionary<string, Scheme?>? current = SchemeManager.GetSchemesForCurrentTheme ();
-        Assert.NotNull (current);
+            Assert.True (SchemeManager.GetSchemesForCurrentTheme ()!.ContainsKey ("Base"));
+            Assert.True (SchemeManager.GetSchemesForCurrentTheme ()!.ContainsKey ("base"));
 
-        Assert.True (current!.ContainsKey ("Base"));
-        Assert.True (current.ContainsKey ("base"));
+            ResetToHardCodedDefaults ();
+            Dictionary<string, Scheme?>? current = SchemeManager.GetSchemesForCurrentTheme ();
+            Assert.NotNull (current);
 
-        Disable (true);
+            Assert.True (current!.ContainsKey ("Base"));
+            Assert.True (current.ContainsKey ("base"));
+        }
+        finally
+        {
+            Disable (true);
+        }
     }
 
     [Fact]
@@ -217,16 +303,11 @@ public class SchemeManagerTests
 
             // Load the test theme
             // TODO: This should throw an exception!
-            Assert.Throws< JsonException > (() => Load (ConfigLocations.Runtime));
+            Assert.Throws<JsonException> (() => Load (ConfigLocations.Runtime));
             Assert.Contains ("TestTheme", ThemeManager.Themes!);
             Assert.Equal ("TestTheme", ThemeManager.Theme);
             Assert.Throws<System.Collections.Generic.KeyNotFoundException> (SchemeManager.GetSchemes);
 
-            // Now reset everything and reload
-            ResetToCurrentValues ();
-
-            // Verify we're back to default
-            Assert.Equal ("Default", ThemeManager.Theme);
         }
         finally
         {
@@ -261,7 +342,7 @@ public class SchemeManagerTests
             Assert.Equal ("TestTheme", ThemeManager.Theme);
 
             // Now reset everything and reload
-            ResetToCurrentValues ();
+            ResetToHardCodedDefaults ();
 
             // Verify we're back to default
             Assert.Equal ("Default", ThemeManager.Theme);
@@ -367,7 +448,7 @@ public class SchemeManagerTests
                                             "Normal": {
                                               "Foreground": "White",
                                               "Background": "DarkBlue",
-                                              "Style": "Bold"
+                                              "Style": "Reverse" // Not default Bold
                                             },
                                             "Focus": {
                                             "Foreground": "White",
@@ -422,20 +503,409 @@ public class SchemeManagerTests
                             }
                             """;
 
+            // Capture hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!;
+
+            Color hardCodedTopLevelNormalFg = hardCodedSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), hardCodedTopLevelNormalFg.ToString ());
+
+            Assert.Equal (hardCodedSchemes ["Menu"].Normal.Style, SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style);
+
+            // Capture current scheme colors
+            Dictionary<string, Scheme> currentSchemes = SchemeManager.GetSchemes ()!;
+
+            Color currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), currentTopLevelNormalFg.ToString ());
+
             // Load the test theme
             Load (ConfigLocations.Runtime);
             Assert.Equal ("TestTheme", ThemeManager.Theme);
+            Assert.Equal (TextStyle.Reverse, SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style);
 
-            TextStyle style = SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style;
+            currentSchemes = SchemeManager.GetSchemesForCurrentTheme ()!;
+            currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+            Assert.NotEqual (hardCodedTopLevelNormalFg.ToString (), currentTopLevelNormalFg.ToString ());
 
-            Assert.Equal (TextStyle.Bold, style);
+            // Now reset everything and reload
+            ResetToHardCodedDefaults ();
+
+            // Verify we're back to default
+            Assert.Equal ("Default", ThemeManager.Theme);
+
+            currentSchemes = SchemeManager.GetSchemes ()!;
+            currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (hardCodedTopLevelNormalFg.ToString (), currentTopLevelNormalFg.ToString ());
+
+        }
+        finally
+        {
+            Disable (true);
+        }
+    }
+
+    [Fact]
+    public void Load_Modified_Default_Scheme_Loads ()
+    {
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
+            ThrowOnJsonErrors = true;
+
+            // Create a test theme
+            RuntimeConfig = """
+                            {
+                                 "Theme": "Default",
+                                 "Themes": [
+                                   {
+                                     "Default": {
+                                       "Schemes": [
+                                                   {
+                                          "TopLevel": {
+                                            "Normal": {
+                                              "Foreground": "AntiqueWhite",
+                                              "Background": "DimGray"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "White",
+                                              "Background": "DarkGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Wheat",
+                                              "Background": "DarkGray",
+                                              "Style": "Underline"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "LightYellow",
+                                              "Background": "DimGray",
+                                              "Style": "Underline"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Black",
+                                              "Background": "DimGray"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Base": {
+                                            "Normal": {
+                                              "Foreground": "White",
+                                              "Background": "Blue"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "DarkBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "BrightCyan",
+                                              "Background": "Blue"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "BrightBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "Blue"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Dialog": {
+                                            "Normal": {
+                                              "Foreground": "Black",
+                                              "Background": "LightGray"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Blue",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "BrightBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Gray",
+                                              "Background": "DarkGray"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Menu": {
+                                            "Normal": {
+                                              "Foreground": "White",
+                                              "Background": "DarkBlue",
+                                              "Style": "Reverse" // Not default Bold
+                                            },
+                                            "Focus": {
+                                            "Foreground": "White",
+                                            "Background": "DarkBlue",
+                                              "Style": "Bold,Reverse"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "BrightYellow",
+                                              "Background": "DarkBlue",
+                                              "Style": "Bold,Underline"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "Blue",
+                                              "Background": "White",
+                                              "Style": "Bold,Underline"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Gray",
+                                              "Background": "DarkGray",
+                                              "Style": "Faint"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Error": {
+                                            "Normal": {
+                                              "Foreground": "Red",
+                                              "Background": "Pink"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "White",
+                                              "Background": "BrightRed"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Black",
+                                              "Background": "Pink"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "Pink",
+                                              "Background": "BrightRed"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "White"
+                                            }
+                                          }
+                                        }
+                                       ]
+                                     }
+                                   }
+                                 ]
+                            }
+                            """;
+
+            // Capture hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!;
+
+            Color hardCodedTopLevelNormalFg = hardCodedSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), hardCodedTopLevelNormalFg.ToString ());
+
+            Assert.Equal (hardCodedSchemes ["Menu"].Normal.Style, SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style);
+
+            // Capture current scheme colors
+            Dictionary<string, Scheme> currentSchemes = SchemeManager.GetSchemes ()!;
+
+            Color currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), currentTopLevelNormalFg.ToString ());
+
+            // Load the test theme
+            Load (ConfigLocations.Runtime);
+            Assert.Equal ("Default", ThemeManager.Theme);
+            // BUGBUG: We did not Apply after loading, so schemes should NOT have been updated
+            Assert.Equal (TextStyle.Reverse, SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style);
+
+            currentSchemes = SchemeManager.GetSchemesForCurrentTheme ()!;
+            currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+            // BUGBUG: We did not Apply after loading, so schemes should NOT have been updated
+            //Assert.Equal (hardCodedTopLevelNormalFg.ToString (), currentTopLevelNormalFg.ToString ());
 
             // Now reset everything and reload
-            ResetToCurrentValues ();
+            ResetToHardCodedDefaults ();
 
             // Verify we're back to default
             Assert.Equal ("Default", ThemeManager.Theme);
 
+            currentSchemes = SchemeManager.GetSchemes ()!;
+            currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (hardCodedTopLevelNormalFg.ToString (), currentTopLevelNormalFg.ToString ());
+
+        }
+        finally
+        {
+            Disable (true);
+        }
+    }
+
+
+    [Fact]
+    public void Load_From_Json_Does_Not_Corrupt_HardCodedSchemes ()
+    {
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
+
+            // Create a test theme
+            string json = """
+                            {
+                                 "Theme": "TestTheme",
+                                 "Themes": [
+                                   {
+                                     "TestTheme": {
+                                       "Schemes": [
+                                                   {
+                                          "TopLevel": {
+                                            "Normal": {
+                                              "Foreground": "AntiqueWhite",
+                                              "Background": "DimGray"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "White",
+                                              "Background": "DarkGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Wheat",
+                                              "Background": "DarkGray",
+                                              "Style": "Underline"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "LightYellow",
+                                              "Background": "DimGray",
+                                              "Style": "Underline"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Black",
+                                              "Background": "DimGray"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Base": {
+                                            "Normal": {
+                                              "Foreground": "White",
+                                              "Background": "Blue"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "DarkBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "BrightCyan",
+                                              "Background": "Blue"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "BrightBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "Blue"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Dialog": {
+                                            "Normal": {
+                                              "Foreground": "Black",
+                                              "Background": "LightGray"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Blue",
+                                              "Background": "LightGray"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "BrightBlue",
+                                              "Background": "LightGray"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Gray",
+                                              "Background": "DarkGray"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Menu": {
+                                            "Normal": {
+                                              "Foreground": "White",
+                                              "Background": "DarkBlue",
+                                              "Style": "Reverse" // Not default Bold
+                                            },
+                                            "Focus": {
+                                            "Foreground": "White",
+                                            "Background": "DarkBlue",
+                                              "Style": "Bold,Reverse"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "BrightYellow",
+                                              "Background": "DarkBlue",
+                                              "Style": "Bold,Underline"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "Blue",
+                                              "Background": "White",
+                                              "Style": "Bold,Underline"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "Gray",
+                                              "Background": "DarkGray",
+                                              "Style": "Faint"
+                                            }
+                                          }
+                                        },
+                                        {
+                                          "Error": {
+                                            "Normal": {
+                                              "Foreground": "Red",
+                                              "Background": "Pink"
+                                            },
+                                            "Focus": {
+                                              "Foreground": "White",
+                                              "Background": "BrightRed"
+                                            },
+                                            "HotNormal": {
+                                              "Foreground": "Black",
+                                              "Background": "Pink"
+                                            },
+                                            "HotFocus": {
+                                              "Foreground": "Pink",
+                                              "Background": "BrightRed"
+                                            },
+                                            "Disabled": {
+                                              "Foreground": "DarkGray",
+                                              "Background": "White"
+                                            }
+                                          }
+                                        }
+                                       ]
+                                     }
+                                   }
+                                 ]
+                            }
+                            """;
+
+            // Capture dynamically created hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!;
+
+            Color hardCodedTopLevelNormalFg = hardCodedSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), hardCodedTopLevelNormalFg.ToString ());
+
+            // Capture current scheme colors
+            Dictionary<string, Scheme> currentSchemes = SchemeManager.GetSchemes ()!;
+            Color currentTopLevelNormalFg = currentSchemes ["TopLevel"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.CadetBlue).ToString (), currentTopLevelNormalFg.ToString ());
+
+            // Load the test theme
+            ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
+
+            Assert.Equal ("TestTheme", ThemeManager.Theme);
+            Assert.Equal (TextStyle.Reverse, SchemeManager.GetSchemesForCurrentTheme () ["Menu"]!.Normal.Style);
+            Dictionary<string, Scheme>? hardCodedSchemesViaScope = GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary<string, Scheme>;
+            Assert.Equal (hardCodedTopLevelNormalFg.ToString (), hardCodedSchemesViaScope! ["TopLevel"].Normal.Foreground.ToString ());
+
         }
         finally
         {

+ 56 - 55
Tests/UnitTests/Configuration/SettingsScopeTests.cs

@@ -1,5 +1,8 @@
 #nullable enable
 using System.Collections.Concurrent;
+using System.Collections.Frozen;
+using System.Collections.Immutable;
+using System.Text.Json;
 using static Terminal.Gui.Configuration.ConfigurationManager;
 
 namespace Terminal.Gui.ConfigurationTests;
@@ -18,11 +21,11 @@ public class SettingsScopeTests
 
         // act
         RuntimeConfig = """
-                   
-                           {
-                                 "Application.QuitKey": "Ctrl-Q"
-                           }
-                   """;
+
+                                {
+                                      "Application.QuitKey": "Ctrl-Q"
+                                }
+                        """;
 
         Load (ConfigLocations.Runtime);
 
@@ -30,11 +33,9 @@ public class SettingsScopeTests
         Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue!);
 
         // clean up
-        Disable (resetToHardCodedDefaults: true);
-
+        Disable (true);
     }
 
-
     [Fact]
     public void Load_Dictionary_Property_Overrides_Defaults ()
     {
@@ -53,7 +54,6 @@ public class SettingsScopeTests
         Assert.NotNull (scope);
         Assert.Equal (MouseState.In | MouseState.Pressed | MouseState.PressedOutside, scope ["Button.DefaultHighlightStates"].PropertyValue);
 
-
         RuntimeConfig = """
                         {
                             "Themes": [
@@ -76,9 +76,9 @@ public class SettingsScopeTests
         Load (ConfigLocations.Runtime);
 
         // assert
-        Assert.Equal (2, ThemeManager.GetThemes ().Count);
+        Assert.Equal (2, ThemeManager.Themes!.Count);
         Assert.Equal (MouseState.None, (MouseState)ThemeManager.GetCurrentTheme () ["Button.DefaultHighlightStates"].PropertyValue!);
-        Assert.Equal (MouseState.In, (MouseState)ThemeManager.GetThemes () ["NewTheme"] ["Button.DefaultHighlightStates"].PropertyValue!);
+        Assert.Equal (MouseState.In, (MouseState)ThemeManager.Themes ["NewTheme"] ["Button.DefaultHighlightStates"].PropertyValue!);
 
         RuntimeConfig = """
                         {
@@ -95,13 +95,12 @@ public class SettingsScopeTests
         Load (ConfigLocations.Runtime);
 
         // assert
-        Assert.Equal (2, ThemeManager.GetThemes ().Count);
+        Assert.Equal (2, ThemeManager.Themes.Count);
         Assert.Equal (MouseState.Pressed, (MouseState)ThemeManager.Themes! [ThemeManager.DEFAULT_THEME_NAME] ["Button.DefaultHighlightStates"].PropertyValue!);
         Assert.Equal (MouseState.In, (MouseState)ThemeManager.Themes! ["NewTheme"] ["Button.DefaultHighlightStates"].PropertyValue!);
 
         // clean up
-        Disable (resetToHardCodedDefaults: true);
-
+        Disable (true);
     }
 
     [Fact]
@@ -111,16 +110,16 @@ public class SettingsScopeTests
         Load (ConfigLocations.LibraryResources);
 
         // arrange
-        Assert.Equal (Key.Esc, (Key)Settings!["Application.QuitKey"].PropertyValue!);
+        Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue!);
 
         Assert.Equal (
                       Key.F6,
-                      (Key)Settings["Application.NextTabGroupKey"].PropertyValue!
+                      (Key)Settings ["Application.NextTabGroupKey"].PropertyValue!
                      );
 
         Assert.Equal (
                       Key.F6.WithShift,
-                      (Key)Settings["Application.PrevTabGroupKey"].PropertyValue!
+                      (Key)Settings ["Application.PrevTabGroupKey"].PropertyValue!
                      );
 
         // act
@@ -135,14 +134,14 @@ public class SettingsScopeTests
         Assert.Equal (Key.F, Application.NextTabGroupKey);
         Assert.Equal (Key.B, Application.PrevTabGroupKey);
 
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 
     [Fact]
     public void CopyUpdatedPropertiesFrom_ShouldCopyChangedPropertiesOnly ()
     {
         Enable (ConfigLocations.HardCoded);
-        Settings ["Application.QuitKey"].PropertyValue = Key.End;
+        Settings! ["Application.QuitKey"].PropertyValue = Key.End;
 
         var updatedSettings = new SettingsScope ();
         updatedSettings.LoadHardCodedDefaults ();
@@ -154,33 +153,37 @@ public class SettingsScopeTests
         updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
         Settings.UpdateFrom (updatedSettings);
-        Assert.Equal (KeyCode.End, ((Key)Settings["Application.QuitKey"].PropertyValue!).KeyCode);
-        Assert.Equal (KeyCode.F, ((Key)updatedSettings["Application.NextTabGroupKey"].PropertyValue!).KeyCode);
-        Assert.Equal (KeyCode.B, ((Key)updatedSettings["Application.PrevTabGroupKey"].PropertyValue!).KeyCode);
-        Disable (resetToHardCodedDefaults: true);
+        Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue!).KeyCode);
+        Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.NextTabGroupKey"].PropertyValue!).KeyCode);
+        Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.PrevTabGroupKey"].PropertyValue!).KeyCode);
+        Disable (true);
     }
 
     [Fact]
     public void ResetToHardCodedDefaults_Resets_Config_And_Applies ()
     {
-        Enable (ConfigLocations.HardCoded);
-        Load (ConfigLocations.LibraryResources);
-
-        Assert.True (Settings! ["Application.QuitKey"].PropertyValue is Key);
-        Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
-        Settings ["Application.QuitKey"].PropertyValue = Key.Q;
-        Apply ();
-        Assert.Equal (Key.Q, Application.QuitKey);
-
-        // Act
-        ResetToHardCodedDefaults ();
-        Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
-        Assert.Equal (Key.Esc, Application.QuitKey);
-
-        Disable ();
+        try
+        {
+            Enable (ConfigLocations.HardCoded);
+            Load (ConfigLocations.LibraryResources);
+
+            Assert.True (Settings! ["Application.QuitKey"].PropertyValue is Key);
+            Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
+            Settings ["Application.QuitKey"].PropertyValue = Key.Q;
+            Apply ();
+            Assert.Equal (Key.Q, Application.QuitKey);
+
+            // Act
+            ResetToHardCodedDefaults ();
+            Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
+            Assert.Equal (Key.Esc, Application.QuitKey);
+        }
+        finally
+        {
+            Disable (true);
+        }
     }
 
-
     [Fact]
     public void Themes_Property_Exists ()
     {
@@ -191,12 +194,11 @@ public class SettingsScopeTests
         // Themes exists, but is not initialized
         Assert.Null (settingsScope ["Themes"].PropertyValue);
 
-        settingsScope.LoadCurrentValues ();
+        //settingsScope.UpdateToCurrentValues ();
 
-        Assert.NotEmpty (settingsScope);
+        //Assert.NotEmpty (settingsScope);
     }
 
-
     [Fact]
     public void LoadHardCodedDefaults_Resets ()
     {
@@ -216,9 +218,9 @@ public class SettingsScopeTests
         // Assert
         Assert.Equal (Key.Esc, Application.QuitKey);
 
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
-    
+
     private class ConfigPropertyMock
     {
         public object? PropertyValue { get; init; }
@@ -230,7 +232,6 @@ public class SettingsScopeTests
         public string? Theme { get; set; }
     }
 
-
     [Fact]
     public void SettingsScopeMockWithKey_CreatesDeepCopy ()
     {
@@ -263,18 +264,18 @@ public class SettingsScopeTests
         Assert.Equal ("Dark", source.Theme);
         Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled);
         Assert.Single ((Dictionary<string, int>)source ["Counts"].PropertyValue!);
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 
     [Fact /*(Skip = "This test randomly fails due to a concurrent change to something. Needs to be moved to non-parallel tests.")*/]
     public void ThemeScopeList_WithThemes_ClonesSuccessfully ()
     {
         // Arrange: Create a ThemeScope and verify a property exists
-        ThemeScope defaultThemeScope = new ThemeScope ();
+        var defaultThemeScope = new ThemeScope ();
         defaultThemeScope.LoadHardCodedDefaults ();
         Assert.True (defaultThemeScope.ContainsKey ("Button.DefaultHighlightStates"));
 
-        ThemeScope darkThemeScope = new ThemeScope ();
+        var darkThemeScope = new ThemeScope ();
         darkThemeScope.LoadHardCodedDefaults ();
         Assert.True (darkThemeScope.ContainsKey ("Button.DefaultHighlightStates"));
 
@@ -286,7 +287,7 @@ public class SettingsScopeTests
         ];
 
         // Create a SettingsScope and set the Themes property
-        SettingsScope settingsScope = new SettingsScope ();
+        var settingsScope = new SettingsScope ();
         settingsScope.LoadHardCodedDefaults ();
         Assert.True (settingsScope.ContainsKey ("Themes"));
         settingsScope ["Themes"].PropertyValue = themesList;
@@ -297,14 +298,14 @@ public class SettingsScopeTests
         // Assert
         Assert.NotNull (result);
         Assert.IsType<SettingsScope> (result);
-        SettingsScope resultScope = (SettingsScope)result;
+        var resultScope = result;
         Assert.True (resultScope.ContainsKey ("Themes"));
 
         Assert.NotNull (resultScope ["Themes"].PropertyValue);
 
         List<Dictionary<string, ThemeScope>> clonedThemes = (List<Dictionary<string, ThemeScope>>)resultScope ["Themes"].PropertyValue!;
         Assert.Equal (2, clonedThemes.Count);
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 
     [Fact]
@@ -322,7 +323,7 @@ public class SettingsScopeTests
         Assert.IsType<SettingsScope> (result);
 
         Assert.True (result.ContainsKey ("Themes"));
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 
     [Fact]
@@ -334,8 +335,8 @@ public class SettingsScopeTests
 
         settingsScope ["Themes"].PropertyValue = new List<Dictionary<string, ThemeScope>>
         {
-            new() { { "Default", new () } },
-            new() { { "Dark", new () } }
+            new () { { "Default", new () } },
+            new () { { "Dark", new () } }
         };
 
         // Act
@@ -346,6 +347,6 @@ public class SettingsScopeTests
         Assert.IsType<SettingsScope> (result);
         Assert.True (result.ContainsKey ("Themes"));
         Assert.NotNull (result ["Themes"].PropertyValue);
-        Disable (resetToHardCodedDefaults: true);
+        Disable (true);
     }
 }

+ 18 - 12
Tests/UnitTests/Configuration/ThemeManagerTests.cs

@@ -78,20 +78,26 @@ public class ThemeManagerTests (ITestOutputHelper output)
     [Fact]
     public void Theme_ResetToHardCodedDefaults_Sets_To_Default ()
     {
-        Assert.False (IsEnabled);
-        Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
+        try
+        {
+            Assert.False (IsEnabled);
+            Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
 
-        Enable (ConfigLocations.HardCoded);
-        Assert.Equal ("Default", ThemeManager.Theme);
+            Enable (ConfigLocations.HardCoded);
+            Assert.Equal ("Default", ThemeManager.Theme);
 
-        ThemeManager.Theme = "Test";
-        Assert.Equal ("Test", ThemeManager.Theme);
-        Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
-        Assert.Equal ("Test", Settings! ["Theme"].PropertyValue);
+            ThemeManager.Theme = "Test";
+            Assert.Equal ("Test", ThemeManager.Theme);
+            Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
+            Assert.Equal ("Test", Settings! ["Theme"].PropertyValue);
 
-        ResetToHardCodedDefaults ();
-        Assert.Equal ("Default", ThemeManager.Theme);
-        Disable ();
+            ResetToHardCodedDefaults ();
+            Assert.Equal ("Default", ThemeManager.Theme);
+        }
+        finally
+        {
+            Disable(true);
+        }
     }
 
     #endregion Tests Settings["Theme"] and ThemeManager.Theme
@@ -231,7 +237,7 @@ public class ThemeManagerTests (ITestOutputHelper output)
             Assert.Equal ("TestTheme", ThemeManager.Theme);
 
             // Now reset everything and reload
-            ResetToCurrentValues ();
+            ResetToHardCodedDefaults ();
 
             // Verify we're back to default
             Assert.Equal ("Default", ThemeManager.Theme);

+ 95 - 7
Tests/UnitTests/Configuration/ThemeScopeTests.cs

@@ -1,4 +1,7 @@
-using System.Collections.Concurrent;
+#nullable enable
+using System.Collections.Concurrent;
+using System.Collections.Frozen;
+using System.Collections.Immutable;
 using System.Text.Json;
 using static Terminal.Gui.Configuration.ConfigurationManager;
 
@@ -29,7 +32,7 @@ public class ThemeScopeTests
         ThemeManager.GetCurrentTheme () ["Dialog.DefaultButtonAlignment"].PropertyValue = newButtonAlignment;
 
         LineStyle savedBorderStyle = Dialog.DefaultBorderStyle;
-        LineStyle newBorderStyle = LineStyle.HeavyDotted;
+        var newBorderStyle = LineStyle.HeavyDotted;
         ThemeManager.GetCurrentTheme () ["Dialog.DefaultBorderStyle"].PropertyValue = newBorderStyle;
 
         ThemeManager.Themes! [ThemeManager.Theme]!.Apply ();
@@ -59,7 +62,7 @@ public class ThemeScopeTests
         Assert.Equal ("Dark", ThemeManager.Theme);
 
         // Act
-        ThemeManager.ResetToHardCodedDefaults ();
+        ThemeManager.LoadHardCodedDefaults ();
         Assert.Equal ("Default", ThemeManager.Theme);
 
         Disable (true);
@@ -70,15 +73,15 @@ public class ThemeScopeTests
     {
         Enable (ConfigLocations.HardCoded);
 
-        IDictionary<string, ThemeScope> initial = ThemeManager.Themes;
+        IDictionary<string, ThemeScope> initial = ThemeManager.Themes!;
 
         string serialized = JsonSerializer.Serialize (ThemeManager.Themes, SerializerContext.Options);
 
-        ConcurrentDictionary<string, ThemeScope> deserialized =
+        ConcurrentDictionary<string, ThemeScope>? deserialized =
             JsonSerializer.Deserialize<ConcurrentDictionary<string, ThemeScope>> (serialized, SerializerContext.Options);
 
         Assert.NotEqual (initial, deserialized);
-        Assert.Equal (deserialized.Count, initial!.Count);
+        Assert.Equal (deserialized!.Count, initial!.Count);
 
         Disable (true);
     }
@@ -98,9 +101,94 @@ public class ThemeScopeTests
 
         Assert.Equal (
                       Alignment.End,
-                      (Alignment)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue!
+                      (Alignment)deserialized! ["Dialog.DefaultButtonAlignment"].PropertyValue!
                      );
 
         Disable (true);
     }
+
+    [Fact (Skip = "Temp work arounds for #4288 prevent corruption.")]
+    public void UpdateFrom_Corrupts_Schemes_HardCodeDefaults ()
+    {
+        // BUGBUG: ThemeScope is broken and needs to be fixed to not have the hard coded schemes get overwritten.
+        // BUGBUG: This test demonstrates the problem.
+        // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/4288
+
+        // Create a test theme
+        var json = """
+                   {
+                      "Schemes": [
+                       {
+                         "Base": {
+                           "Normal": {
+                             "Foreground": "White",
+                             "Background": "Blue"
+                           }
+                         }
+                       }
+                      ]
+                   }
+                   """;
+
+        //var json = """
+        //           {
+        //                "Themes": [
+        //                  {
+        //                    "Default": {
+        //                      "Schemes": [
+        //                           {
+        //                             "Base": {
+        //                               "Normal": {
+        //                                 "Foreground": "White",
+        //                                 "Background": "Blue"
+        //                               }
+        //                             }
+        //                           }
+        //                          ]
+        //                        }
+        //                    }
+        //            ]
+        //           }
+        //           """;
+
+        try
+        {
+            Assert.False (IsEnabled);
+            ThrowOnJsonErrors = true;
+           // Enable (ConfigLocations.HardCoded);
+            //ResetToCurrentValues ();
+
+            // Capture dynamically created hardCoded hard-coded scheme colors
+            ImmutableSortedDictionary<string, Scheme> hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!;
+            Color hardCodedBaseNormalFg = hardCodedSchemes ["Base"].Normal.Foreground;
+            Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ());
+
+            // Capture hard-coded scheme colors via cache
+            Dictionary<string, Scheme>? hardCodedSchemesViaCache =
+                GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary<string, Scheme>;
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), hardCodedSchemesViaCache! ["Base"].Normal.Foreground.ToString ());
+
+            // (ConfigLocations.HardCoded);
+
+            // Capture current scheme 
+            Dictionary<string, Scheme> currentSchemes = SchemeManager.GetSchemes ()!;
+            Color currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground;
+            Assert.Equal (hardCodedBaseNormalFg.ToString (), currentBaseNormalFg.ToString ());
+
+            //ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
+
+            ThemeScope scope = (JsonSerializer.Deserialize (json, typeof (ThemeScope), SerializerContext.Options) as ThemeScope)!;
+
+            ThemeScope defaultTheme = ThemeManager.Themes! ["Default"]!;
+            Dictionary<string, Scheme?> schemesScope = (defaultTheme ["Schemes"].PropertyValue as Dictionary<string, Scheme?>)!;
+            defaultTheme ["Schemes"].UpdateFrom (scope ["Schemes"].PropertyValue!);
+            defaultTheme.UpdateFrom (scope);
+
+            Assert.Equal (Color.White.ToString (), hardCodedSchemesViaCache! ["Base"].Normal.Foreground.ToString ());
+        }
+        finally
+        {
+            ResetToHardCodedDefaults ();
+        }
+    }
 }

+ 1 - 1
Tests/UnitTests/Views/ComboBoxTests.cs

@@ -1,4 +1,5 @@
 using System.Collections.ObjectModel;
+using Terminal.Gui.ConfigurationTests;
 using UnitTests;
 using Xunit.Abstractions;
 
@@ -525,7 +526,6 @@ public class ComboBoxTests (ITestOutputHelper output)
         Assert.True (cb.IsShow);
         Assert.Equal (-1, cb.SelectedItem);
         Assert.Equal ("", cb.Text);
-
         cb.Layout ();
 
         cb.Draw ();

+ 27 - 0
Tests/UnitTestsParallelizable/Drawing/Color/ColorStandardColorTests.cs

@@ -0,0 +1,27 @@
+using Xunit;
+
+namespace Terminal.Gui.DrawingTests;
+
+public class ColorStandardColorTests
+{
+    [Fact]
+    public void ToString_Returns_Standard_Name_For_StandardColor_CadetBlue()
+    {
+        // Without the fix, this uses Color(in StandardColor) -> this((int)colorName),
+        // which sets A=0x00 and prevents name resolution (expects A=0xFF).
+        var c = new Terminal.Gui.Drawing.Color(Terminal.Gui.Drawing.StandardColor.CadetBlue);
+
+        // Expected: named color
+        Assert.Equal("CadetBlue", c.ToString());
+    }
+
+    [Fact]
+    public void ToString_G_Prints_Opaque_ARGB_For_StandardColor_CadetBlue()
+    {
+        // Without the fix, A=0x00, so "G" prints "#005F9EA0" instead of "#FF5F9EA0".
+        var c = new Terminal.Gui.Drawing.Color(Terminal.Gui.Drawing.StandardColor.CadetBlue);
+
+        // Expected: #AARRGGBB with A=FF (opaque)
+        Assert.Equal("#FF5F9EA0", c.ToString("G", null));
+    }
+}

+ 0 - 1
Tests/UnitTestsParallelizable/Drawing/SchemeTests.GetAttributeForRoleAlgorithmTests.cs

@@ -10,7 +10,6 @@ public class SchemeGetAttributeForRoleAlgorithmTests
         Attribute normal = new ("Red", "Blue");
         Scheme scheme = new (normal);
 
-        Assert.NotNull (scheme.Normal);
         Assert.Equal (normal, scheme.GetAttributeForRole (VisualRole.Normal));
     }
 

+ 34 - 0
Tests/UnitTestsParallelizable/Drawing/SchemeTests.cs

@@ -39,6 +39,40 @@ public class SchemeTests
         Assert.True (schemes.ContainsKey ("TopLevel"));
     }
 
+
+    [Fact]
+    public void GetHardCodedSchemes_Have_Expected_Normal_Attributes ()
+    {
+        var schemes = Scheme.GetHardCodedSchemes ();
+        Assert.NotNull (schemes);
+
+        // Base
+        var baseScheme = schemes! ["Base"];
+        Assert.NotNull (baseScheme);
+        Assert.Equal (new Attribute (StandardColor.LightBlue, StandardColor.RaisinBlack), baseScheme!.Normal);
+
+        // Dialog
+        var dialogScheme = schemes ["Dialog"];
+        Assert.NotNull (dialogScheme);
+        Assert.Equal (new Attribute (StandardColor.LightSkyBlue, StandardColor.OuterSpace), dialogScheme!.Normal);
+
+        // Error
+        var errorScheme = schemes ["Error"];
+        Assert.NotNull (errorScheme);
+        Assert.Equal (new Attribute (StandardColor.IndianRed, StandardColor.RaisinBlack), errorScheme!.Normal);
+
+        // Menu (Bold style)
+        var menuScheme = schemes ["Menu"];
+        Assert.NotNull (menuScheme);
+        Assert.Equal (new Attribute (StandardColor.Charcoal, StandardColor.LightBlue, TextStyle.Bold), menuScheme!.Normal);
+
+        // Toplevel
+        var toplevelScheme = schemes ["Toplevel"];
+        Assert.NotNull (toplevelScheme);
+        Assert.Equal (new Attribute (StandardColor.CadetBlue, StandardColor.Charcoal).ToString (), toplevelScheme!.Normal.ToString ());
+    }
+
+
     [Fact]
     public void Built_Ins_Are_Implicit ()
     {

+ 12 - 8
Tests/UnitTestsParallelizable/View/SchemeTests.cs

@@ -142,6 +142,7 @@ public class SchemeTests
         var customScheme = SchemeManager.GetHardCodedSchemes ()? ["Error"]! with { Normal = Attribute.Default };
 
         Assert.NotEqual (Attribute.Default, view.GetScheme ().Normal);
+
         view.GettingScheme += (sender, args) =>
                               {
                                   args.Result = customScheme;
@@ -174,13 +175,13 @@ public class SchemeTests
         var customAttribute = new Attribute (Color.BrightRed, Color.BrightYellow);
 
         view.GettingAttributeForRole += (sender, args) =>
-        {
-            if (args.Role == VisualRole.Focus)
-            {
-                args.Result = customAttribute;
-                args.Handled = true;
-            }
-        };
+                                        {
+                                            if (args.Role == VisualRole.Focus)
+                                            {
+                                                args.Result = customAttribute;
+                                                args.Handled = true;
+                                            }
+                                        };
 
         Assert.Equal (customAttribute, view.GetAttributeForRole (VisualRole.Focus));
         view.Dispose ();
@@ -199,6 +200,7 @@ public class SchemeTests
         Assert.Contains ("Toplevel", schemes.Keys);
     }
 
+
     [Fact]
     public void SchemeName_OverridesSuperViewScheme ()
     {
@@ -243,6 +245,7 @@ public class SchemeTests
         protected override bool OnGettingScheme (out Scheme? scheme)
         {
             scheme = SchemeManager.GetHardCodedSchemes ()? ["Error"];
+
             return true;
         }
 
@@ -265,4 +268,5 @@ public class SchemeTests
 
         view.Dispose ();
     }
-}
+
+}

+ 10 - 0
docfx/docs/drivers.md

@@ -189,6 +189,16 @@ This interface allows advanced scenarios and testing.
 - Supports Windows-specific features and better performance
 - Automatically selected on Windows platforms
 
+#### Visual Studio Debug Console Support
+
+When running in Visual Studio's debug console (`VSDebugConsole.exe`), WindowsDriver detects the `VSAPPIDNAME` environment variable and automatically adjusts its behavior:
+
+- Disables the alternative screen buffer (which is not supported in VS debug console)
+- Preserves the original console colors on startup
+- Restores the original colors and clears the screen on shutdown
+
+This ensures Terminal.Gui applications can be debugged directly in Visual Studio without rendering issues.
+
 ### UnixDriver (UnixComponentFactory)
 
 - Uses Unix/Linux terminal APIs