diff --git a/Directory.Packages.props b/Directory.Packages.props
index 71430a36b1..707ba9b57e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,6 +14,10 @@
+
+
+
+
diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs
index ea6ed4936e..00071b3312 100644
--- a/Terminal.Gui/App/Application.cs
+++ b/Terminal.Gui/App/Application.cs
@@ -15,13 +15,17 @@
global using Terminal.Gui.ViewBase;
global using Terminal.Gui.Views;
global using Attribute = Terminal.Gui.Drawing.Attribute;
+#pragma warning disable CS0618 // Obsolete - ConfigurationManager alias still used internally during transition
global using CM = Terminal.Gui.Configuration.ConfigurationManager;
+#pragma warning restore CS0618
global using Color = Terminal.Gui.Drawing.Color;
using System.Globalization;
using System.Reflection;
using System.Resources;
using Terminal.Gui.Tracing;
+#pragma warning disable CS0618 // Obsolete - Application still uses ConfigurationPropertyAttribute/SettingsScope during transition
+
namespace Terminal.Gui.App;
///
@@ -238,14 +242,14 @@ internal static List GetSupportedCultures ()
[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static AppModel AppModel
{
- get;
+ get => ApplicationSettings.Defaults.AppModel;
set
{
- AppModel oldValue = field;
- field = value;
- AppModelChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, field));
+ AppModel oldValue = ApplicationSettings.Defaults.AppModel;
+ ApplicationSettings.Defaults.AppModel = value;
+ AppModelChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, value));
}
- } = AppModel.FullScreen;
+ }
///
/// Gets or sets an override for the initial cursor position used in mode.
@@ -268,14 +272,14 @@ public static Point? ForceInlinePosition
[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static string ForceDriver
{
- get;
+ get => ApplicationSettings.Defaults.ForceDriver;
set
{
- string oldValue = field;
- field = value;
- ForceDriverChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, field));
+ string oldValue = ApplicationSettings.Defaults.ForceDriver;
+ ApplicationSettings.Defaults.ForceDriver = value;
+ ForceDriverChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, value));
}
- } = string.Empty;
+ }
///
/// Gets or sets the default key bindings for Application-level commands, optionally varying by platform.
diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
index c1e8a2a8ed..d5651449b4 100644
--- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
+++ b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
@@ -2,6 +2,8 @@
using Wcwidth;
using Trace = Terminal.Gui.Tracing.Trace;
+#pragma warning disable CS0618 // Obsolete - ConfigurationManager still used internally during transition
+
namespace Terminal.Gui.App;
internal partial class ApplicationImpl
diff --git a/Terminal.Gui/App/Legacy/Application.Mouse.cs b/Terminal.Gui/App/Legacy/Application.Mouse.cs
index 5aab6a8816..e09e3249de 100644
--- a/Terminal.Gui/App/Legacy/Application.Mouse.cs
+++ b/Terminal.Gui/App/Legacy/Application.Mouse.cs
@@ -9,12 +9,12 @@ public static partial class Application // Mouse handling
[Obsolete ("The legacy static Application object is going away.")]
public static bool IsMouseDisabled
{
- get;
+ get => ApplicationSettings.Defaults.IsMouseDisabled;
set
{
- bool oldValue = field;
- field = value;
- IsMouseDisabledChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, field));
+ bool oldValue = ApplicationSettings.Defaults.IsMouseDisabled;
+ ApplicationSettings.Defaults.IsMouseDisabled = value;
+ IsMouseDisabledChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, value));
}
}
diff --git a/Terminal.Gui/App/Tracing/Trace.cs b/Terminal.Gui/App/Tracing/Trace.cs
index cadb2ae6bd..2b67908aeb 100644
--- a/Terminal.Gui/App/Tracing/Trace.cs
+++ b/Terminal.Gui/App/Tracing/Trace.cs
@@ -39,9 +39,10 @@ public static class Trace
[JsonConverter (typeof (TraceCategoryJsonConverter))]
public static TraceCategory EnabledCategories
{
- get => _asyncLocalEnabledCategories.Value;
+ get => TraceSettings.Defaults.EnabledCategories;
set
{
+ TraceSettings.Defaults.EnabledCategories = value;
_asyncLocalEnabledCategories.Value = value;
EnsureBackendIfEnabled ();
}
diff --git a/Terminal.Gui/Configuration/AppSettingsScope.cs b/Terminal.Gui/Configuration/AppSettingsScope.cs
index 66c6af7f09..ed5e364030 100644
--- a/Terminal.Gui/Configuration/AppSettingsScope.cs
+++ b/Terminal.Gui/Configuration/AppSettingsScope.cs
@@ -1,5 +1,7 @@
using System.Text.Json.Serialization;
+#pragma warning disable CS0618 // Obsolete - AppSettingsScope references ConfigurationPropertyAttribute in docs
+
namespace Terminal.Gui.Configuration;
/// The class for application-defined configuration settings.
@@ -23,6 +25,7 @@ namespace Terminal.Gui.Configuration;
/// },
///
///
+[Obsolete ("Use Microsoft.Extensions.Configuration with TuiConfigurationBuilder instead. See TuiConfigurationBuilder docs for migration guide.")]
[JsonConverter (typeof (ScopeJsonConverter))]
public class AppSettingsScope : Scope
{ }
diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs
index 445ac79385..36859a3794 100644
--- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs
+++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs
@@ -71,11 +71,11 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
switch (propertyName?.ToLower ())
{
case "foreground":
- foreground = JsonSerializer.Deserialize (ref reader, ConfigurationManager.SerializerContext.Color);
+ foreground = JsonSerializer.Deserialize (ref reader, TuiSerializerContext.Instance.Color);
break;
case "background":
- background = JsonSerializer.Deserialize (ref reader, ConfigurationManager.SerializerContext.Color);
+ background = JsonSerializer.Deserialize (ref reader, TuiSerializerContext.Instance.Color);
break;
case "style":
diff --git a/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs b/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs
index d527f5058d..02fc923929 100644
--- a/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs
+++ b/Terminal.Gui/Configuration/ConcurrentDictionaryJsonConverter.cs
@@ -34,7 +34,7 @@ JsonSerializerOptions options
{
string key = reader.GetString ();
reader.Read ();
- object value = JsonSerializer.Deserialize (ref reader, typeof (T), ConfigurationManager.SerializerContext);
+ object value = JsonSerializer.Deserialize (ref reader, typeof (T), TuiSerializerContext.Instance);
if (!dictionary.TryAdd (key, (T)value))
{
@@ -60,7 +60,7 @@ public override void Write (Utf8JsonWriter writer, ConcurrentDictionary
@@ -19,6 +21,7 @@ namespace Terminal.Gui.Configuration;
/// serialization, a must be provided using the
/// attribute.
///
+[Obsolete ("Being replaced by Microsoft.Extensions.Configuration. Will be removed in a future version.")]
public class ConfigProperty
{
/// Describes the property.
diff --git a/Terminal.Gui/Configuration/ConfigPropertyHostTypes.cs b/Terminal.Gui/Configuration/ConfigPropertyHostTypes.cs
index 04ff10f051..4c88de5d1b 100644
--- a/Terminal.Gui/Configuration/ConfigPropertyHostTypes.cs
+++ b/Terminal.Gui/Configuration/ConfigPropertyHostTypes.cs
@@ -1,6 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Terminal.Gui.Tracing;
+#pragma warning disable CS0618 // Obsolete - ConfigPropertyHostTypes references ConfigurationPropertyAttribute during transition
+
namespace Terminal.Gui.Configuration;
///
@@ -26,6 +28,7 @@ namespace Terminal.Gui.Configuration;
/// consumer apps.
///
///
+[Obsolete ("Being replaced by Microsoft.Extensions.Configuration. Will be removed in a future version.")]
internal static class ConfigPropertyHostTypes
{
private const DynamicallyAccessedMemberTypes PRESERVED_MEMBERS = DynamicallyAccessedMemberTypes.PublicProperties;
diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs
index 54c132bf0c..159e78c8cf 100644
--- a/Terminal.Gui/Configuration/ConfigurationManager.cs
+++ b/Terminal.Gui/Configuration/ConfigurationManager.cs
@@ -8,6 +8,8 @@
using System.Text.Json.Serialization;
using Trace = Terminal.Gui.Tracing.Trace;
+#pragma warning disable CS0618 // Obsolete - ConfigurationManager uses ConfigProperty/SettingsScope/ThemeScope internally
+
namespace Terminal.Gui.Configuration;
///
@@ -46,6 +48,7 @@ namespace Terminal.Gui.Configuration;
/// See the UICatalog example for a complete example of how to use ConfigurationManager.
///
///
+[Obsolete ("ConfigurationManager is being replaced by Microsoft.Extensions.Configuration. Use TuiConfigurationBuilder instead. Will be removed in a future version.")]
public static class ConfigurationManager
{
private static readonly Lock _appNameLock = new ();
@@ -714,28 +717,7 @@ private static void OnApplied ()
// `Sources` - A source is a location where a configuration can be stored. Sources are defined in the `ConfigLocations` enum.
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")]
- internal static readonly SourceGenerationContext SerializerContext = new (new JsonSerializerOptions
- {
- // Be relaxed
- ReadCommentHandling = JsonCommentHandling.Skip,
- PropertyNameCaseInsensitive = true,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- WriteIndented = true,
- AllowTrailingCommas = true,
- Converters =
- {
- // We override the standard Rune converter to support specifying Glyphs in
- // a flexible way
- new RuneJsonConverter (),
-
- // Override Key to support "Ctrl+Q" format.
- new KeyJsonConverter ()
- },
-
- // Enables Key to be "Ctrl+Q" vs "Ctrl\u002BQ"
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- TypeInfoResolver = SourceGenerationContext.Default
- });
+ internal static readonly SourceGenerationContext SerializerContext = TuiSerializerContext.Instance;
private static SourcesManager? _sourcesManager = new ();
private static readonly object _sourcesManagerLock = new ();
diff --git a/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs b/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs
index 3911e9f881..334e4b1dc8 100644
--- a/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs
+++ b/Terminal.Gui/Configuration/ConfigurationPropertyAttribute.cs
@@ -5,6 +5,7 @@
/// [ConfigurationProperty(Scope = typeof(AppSettingsScope))] public static string? MyProperty { get; set; } = "MyValue";
///
[AttributeUsage (AttributeTargets.Property)]
+[Obsolete ("Use Settings POCOs with TuiConfigurationBuilder instead. Will be removed in a future version.")]
public class ConfigurationPropertyAttribute : System.Attribute
{
///
diff --git a/Terminal.Gui/Configuration/DeepCloner.cs b/Terminal.Gui/Configuration/DeepCloner.cs
index c51b2f5916..2406c78dd0 100644
--- a/Terminal.Gui/Configuration/DeepCloner.cs
+++ b/Terminal.Gui/Configuration/DeepCloner.cs
@@ -171,7 +171,7 @@ private static object CreateInstance (Type type)
// In AOT, try using the JsonSerializer if available
if (IsAotEnvironment ())
{
- JsonTypeInfo? jsonTypeInfo = ConfigurationManager.SerializerContext.GetTypeInfo (type);
+ JsonTypeInfo? jsonTypeInfo = TuiSerializerContext.Instance.GetTypeInfo (type);
if (jsonTypeInfo is not null)
{
@@ -390,30 +390,43 @@ private static IDictionary CreateDictionaryInstance (Type dictType, object? comp
return new Dictionary ();
}
+ if (dictType == typeof (Dictionary))
+ {
+ return new Dictionary ();
+ }
+
+ if (dictType == typeof (Dictionary>))
+ {
+ if (comparer is IEqualityComparer stringComparer)
+ {
+ return new Dictionary> (stringComparer);
+ }
+
+ return new Dictionary> ();
+ }
+
// AOT-safe: use the source-generated JSON serializer to create empty dictionary instances.
// This avoids Activator.CreateInstance, whose target constructors are trimmed by the AOT linker
// for closed generic dictionary types not otherwise statically reachable.
- // Only used when no comparer is needed — Deserialize("{}") always creates a default-comparer instance.
- if (comparer is null)
+ // Always attempted regardless of comparer — in AOT, a default-comparer dictionary is preferable
+ // to a crash from a trimmed constructor.
+ try
{
- try
+ JsonTypeInfo? jsonTypeInfo = TuiSerializerContext.Instance.GetTypeInfo (dictType);
+
+ if (jsonTypeInfo is not null)
{
- JsonTypeInfo? jsonTypeInfo = ConfigurationManager.SerializerContext.GetTypeInfo (dictType);
+ IDictionary? result = JsonSerializer.Deserialize ("{}", jsonTypeInfo) as IDictionary;
- if (jsonTypeInfo is not null)
+ if (result is not null)
{
- IDictionary? result = JsonSerializer.Deserialize ("{}", jsonTypeInfo) as IDictionary;
-
- if (result is not null)
- {
- return result;
- }
+ return result;
}
}
- catch (InvalidOperationException)
- {
- // JSON serializer context may not be initialized — fall through to reflective construction.
- }
+ }
+ catch (InvalidOperationException)
+ {
+ // JSON serializer context may not be initialized — fall through to reflective construction.
}
try
diff --git a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs
index daa5806fcc..247a111144 100644
--- a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs
+++ b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs
@@ -33,7 +33,7 @@ JsonSerializerOptions options
{
string key = reader.GetString ();
reader.Read ();
- object value = JsonSerializer.Deserialize (ref reader, typeof (T), ConfigurationManager.SerializerContext);
+ object value = JsonSerializer.Deserialize (ref reader, typeof (T), TuiSerializerContext.Instance);
dictionary.Add (key, (T)value);
}
}
@@ -56,7 +56,7 @@ public override void Write (Utf8JsonWriter writer, Dictionary value,
//writer.WriteString (item.Key, item.Key);
writer.WritePropertyName (item.Key);
- JsonSerializer.Serialize (writer, item.Value, typeof (T), ConfigurationManager.SerializerContext);
+ JsonSerializer.Serialize (writer, item.Value, typeof (T), TuiSerializerContext.Instance);
writer.WriteEndObject ();
}
diff --git a/Terminal.Gui/Configuration/SchemeJsonConverter.cs b/Terminal.Gui/Configuration/SchemeJsonConverter.cs
index 102f7bf04b..22e40d734f 100644
--- a/Terminal.Gui/Configuration/SchemeJsonConverter.cs
+++ b/Terminal.Gui/Configuration/SchemeJsonConverter.cs
@@ -34,7 +34,7 @@ public override Scheme Read (ref Utf8JsonReader reader, Type typeToConvert, Json
reader.Read ();
// Make sure attributes are marked as explicitly set when deserialized
- object? attrObj = JsonSerializer.Deserialize (ref reader, ConfigurationManager.SerializerContext.Attribute);
+ object? attrObj = JsonSerializer.Deserialize (ref reader, TuiSerializerContext.Instance.Attribute);
if (attrObj is not Attribute attribute)
{
diff --git a/Terminal.Gui/Configuration/SchemeManager.cs b/Terminal.Gui/Configuration/SchemeManager.cs
index bcafb8b31d..d0ff4d481c 100644
--- a/Terminal.Gui/Configuration/SchemeManager.cs
+++ b/Terminal.Gui/Configuration/SchemeManager.cs
@@ -3,6 +3,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
+#pragma warning disable CS0618 // Obsolete - SchemeManager still uses ConfigurationManager/ConfigProperty/ThemeScope internally during transition
+
namespace Terminal.Gui.Configuration;
///
diff --git a/Terminal.Gui/Configuration/Scope.cs b/Terminal.Gui/Configuration/Scope.cs
index adac046ae4..72e4b17024 100644
--- a/Terminal.Gui/Configuration/Scope.cs
+++ b/Terminal.Gui/Configuration/Scope.cs
@@ -2,6 +2,8 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+#pragma warning disable CS0618 // Obsolete - Scope uses ConfigProperty/ConfigurationPropertyAttribute internally
+
namespace Terminal.Gui.Configuration;
///
diff --git a/Terminal.Gui/Configuration/ScopeJsonConverter.cs b/Terminal.Gui/Configuration/ScopeJsonConverter.cs
index 65fd079500..de05ef45af 100644
--- a/Terminal.Gui/Configuration/ScopeJsonConverter.cs
+++ b/Terminal.Gui/Configuration/ScopeJsonConverter.cs
@@ -6,6 +6,8 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
+#pragma warning disable CS0618 // Obsolete - ScopeJsonConverter still uses ConfigurationManager internally during transition
+
namespace Terminal.Gui.Configuration;
///
@@ -131,13 +133,11 @@ public override TScopeT Read (ref Utf8JsonReader reader, Type typeToConvert, Jso
// Set the value of propertyName on the scopeT.
PropertyInfo prop = typeof (TScopeT).GetProperty (propertyName!)!;
- prop.SetValue (scope, JsonSerializer.Deserialize (ref reader, prop.PropertyType, ConfigurationManager.SerializerContext));
+ prop.SetValue (scope, JsonSerializer.Deserialize (ref reader, prop.PropertyType, TuiSerializerContext.Instance));
}
else
{
// Unknown property
- // TODO: To support forward compatibility, we should just ignore unknown properties?
- // TODO: Eg if we read an unknown property, it's possible that the property was added in a later version
throw new JsonException ($"{propertyName}: Unknown property name.");
}
}
@@ -164,7 +164,7 @@ public override void Write (Utf8JsonWriter writer, TScopeT scope, JsonSerializer
{
writer.WritePropertyName (ConfigProperty.GetJsonPropertyName (p));
object? prop = p.GetValue (scope);
- JsonSerializer.Serialize (writer, prop, p.PropertyType, ConfigurationManager.SerializerContext);
+ JsonSerializer.Serialize (writer, prop, p.PropertyType, TuiSerializerContext.Instance);
}
foreach (KeyValuePair p in from p in scope.Where (cp => cp.Value.ScopeType == typeof (TScopeT).Name)
@@ -215,7 +215,7 @@ where p.Value.HasValue
continue;
}
- JsonTypeInfo? jsonTypeInfo = ConfigurationManager.SerializerContext.GetTypeInfo (prop.GetType ());
+ JsonTypeInfo? jsonTypeInfo = TuiSerializerContext.Instance.GetTypeInfo (prop.GetType ());
if (jsonTypeInfo is null)
{
@@ -249,7 +249,7 @@ where p.Value.HasValue
}
}
- JsonTypeInfo? jsonTypeInfo = ConfigurationManager.SerializerContext.GetTypeInfo (propertyType);
+ JsonTypeInfo? jsonTypeInfo = TuiSerializerContext.Instance.GetTypeInfo (propertyType);
if (jsonTypeInfo is null)
{
diff --git a/Terminal.Gui/Configuration/Settings/ApplicationSettings.cs b/Terminal.Gui/Configuration/Settings/ApplicationSettings.cs
new file mode 100644
index 0000000000..0725b1cc44
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/ApplicationSettings.cs
@@ -0,0 +1,23 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for application-level configuration (SettingsScope).
+/// These correspond to the static properties on and related classes.
+///
+public class ApplicationSettings
+{
+ /// Gets or sets the application model (FullScreen or Inline).
+ public AppModel AppModel { get; set; } = AppModel.FullScreen;
+
+ /// Gets or sets the driver to force (e.g., "windows", "dotnet", "ansi"). Empty means auto-detect.
+ public string ForceDriver { get; set; } = string.Empty;
+
+ /// Gets or sets whether the mouse is disabled.
+ public bool IsMouseDisabled { get; set; }
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static ApplicationSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/ButtonSettings.cs b/Terminal.Gui/Configuration/Settings/ButtonSettings.cs
new file mode 100644
index 0000000000..2ec80020f8
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/ButtonSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for visual defaults (ThemeScope).
+///
+public class ButtonSettings
+{
+ /// Gets or sets the default shadow style for buttons.
+ public ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Opaque;
+
+ /// Gets or sets the default mouse highlight states for buttons.
+ public MouseState DefaultMouseHighlightStates { get; set; } = MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static ButtonSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/CharMapSettings.cs b/Terminal.Gui/Configuration/Settings/CharMapSettings.cs
new file mode 100644
index 0000000000..e476205c9e
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/CharMapSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class CharMapSettings
+{
+ /// Gets or sets the default cursor style for character map views.
+ public CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static CharMapSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/CheckBoxSettings.cs b/Terminal.Gui/Configuration/Settings/CheckBoxSettings.cs
new file mode 100644
index 0000000000..408d20ed43
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/CheckBoxSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for visual defaults (ThemeScope).
+///
+public class CheckBoxSettings
+{
+ /// Gets or sets the default mouse highlight states for checkboxes.
+ public MouseState DefaultMouseHighlightStates { get; set; } = MouseState.PressedOutside | MouseState.Pressed | MouseState.In;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static CheckBoxSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/DialogSettings.cs b/Terminal.Gui/Configuration/Settings/DialogSettings.cs
new file mode 100644
index 0000000000..60c01d6aef
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/DialogSettings.cs
@@ -0,0 +1,25 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for visual defaults (ThemeScope).
+///
+public class DialogSettings
+{
+ /// Gets or sets the default shadow style for dialogs.
+ public ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Transparent;
+
+ /// Gets or sets the default border style for dialogs.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.Heavy;
+
+ /// Gets or sets the default button alignment for dialogs.
+ public Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
+
+ /// Gets or sets the default button alignment modes for dialogs.
+ public AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static DialogSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/DriverSettings.cs b/Terminal.Gui/Configuration/Settings/DriverSettings.cs
new file mode 100644
index 0000000000..133005b7f1
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/DriverSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for driver-level configuration (SettingsScope).
+///
+public class DriverSettings
+{
+ /// Gets or sets whether to force 16-color mode.
+ public bool Force16Colors { get; set; }
+
+ /// Gets or sets the size detection mode.
+ public SizeDetectionMode SizeDetection { get; set; } = SizeDetectionMode.AnsiQuery;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static DriverSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/FileDialogSettings.cs b/Terminal.Gui/Configuration/Settings/FileDialogSettings.cs
new file mode 100644
index 0000000000..4d05b1e51e
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/FileDialogSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (SettingsScope).
+///
+public class FileDialogSettings
+{
+ /// Gets or sets the maximum number of search results in file dialogs.
+ public int MaxSearchResults { get; set; } = 10000;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static FileDialogSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/FileDialogStyleSettings.cs b/Terminal.Gui/Configuration/Settings/FileDialogStyleSettings.cs
new file mode 100644
index 0000000000..b6be6d7a13
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/FileDialogStyleSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (SettingsScope).
+///
+public class FileDialogStyleSettings
+{
+ /// Gets or sets whether file dialogs should use colors by default.
+ public bool DefaultUseColors { get; set; } = true;
+
+ /// Gets or sets whether file dialogs should use Unicode characters by default.
+ public bool DefaultUseUnicodeCharacters { get; set; } = false;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static FileDialogStyleSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/FrameViewSettings.cs b/Terminal.Gui/Configuration/Settings/FrameViewSettings.cs
new file mode 100644
index 0000000000..89748a8fe2
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/FrameViewSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class FrameViewSettings
+{
+ /// Gets or sets the default border style for frame views.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.Rounded;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static FrameViewSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/GlyphSettings.cs b/Terminal.Gui/Configuration/Settings/GlyphSettings.cs
new file mode 100644
index 0000000000..b604e95ea2
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/GlyphSettings.cs
@@ -0,0 +1,445 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class GlyphSettings
+{
+ /// Unicode replacement character; used when a wide glyph can't be output because it would be clipped.
+ public Rune WideGlyphReplacement { get; set; } = (Rune)' ';
+
+ /// File icon.
+ public Rune File { get; set; } = (Rune)'☰';
+
+ /// Folder icon.
+ public Rune Folder { get; set; } = (Rune)'꤉';
+
+ /// Horizontal Ellipsis.
+ public Rune HorizontalEllipsis { get; set; } = (Rune)'…';
+
+ /// Vertical Four Dots.
+ public Rune VerticalFourDots { get; set; } = (Rune)'⁞';
+
+ /// Null symbol.
+ public Rune Null { get; set; } = (Rune)'␀';
+
+ /// Checked indicator.
+ public Rune CheckStateChecked { get; set; } = (Rune)'☑';
+
+ /// Not Checked indicator.
+ public Rune CheckStateUnChecked { get; set; } = (Rune)'☐';
+
+ /// Null Checked indicator.
+ public Rune CheckStateNone { get; set; } = (Rune)'⬛';
+
+ /// Selected indicator.
+ public Rune Selected { get; set; } = (Rune)'◉';
+
+ /// Not Selected indicator.
+ public Rune UnSelected { get; set; } = (Rune)'○';
+
+ /// Right arrow.
+ public Rune RightArrow { get; set; } = (Rune)'►';
+
+ /// Left arrow.
+ public Rune LeftArrow { get; set; } = (Rune)'◄';
+
+ /// Down arrow.
+ public Rune DownArrow { get; set; } = (Rune)'▼';
+
+ /// Up arrow.
+ public Rune UpArrow { get; set; } = (Rune)'▲';
+
+ /// Left default indicator.
+ public Rune LeftDefaultIndicator { get; set; } = (Rune)'►';
+
+ /// Right default indicator.
+ public Rune RightDefaultIndicator { get; set; } = (Rune)'◄';
+
+ /// Left Bracket.
+ public Rune LeftBracket { get; set; } = (Rune)'⟦';
+
+ /// Right Bracket.
+ public Rune RightBracket { get; set; } = (Rune)'⟧';
+
+ /// Half block meter segment.
+ public Rune BlocksMeterSegment { get; set; } = (Rune)'▌';
+
+ /// Continuous block meter segment.
+ public Rune ContinuousMeterSegment { get; set; } = (Rune)'█';
+
+ /// Stipple pattern.
+ public Rune Stipple { get; set; } = (Rune)'░';
+
+ /// Diamond.
+ public Rune Diamond { get; set; } = (Rune)'◊';
+
+ /// Close.
+ public Rune Close { get; set; } = (Rune)'✘';
+
+ /// Minimize.
+ public Rune Minimize { get; set; } = (Rune)'❏';
+
+ /// Maximize.
+ public Rune Maximize { get; set; } = (Rune)'✽';
+
+ /// Dot.
+ public Rune Dot { get; set; } = (Rune)'∙';
+
+ /// Dotted Square.
+ public Rune DottedSquare { get; set; } = (Rune)'⬚';
+
+ /// Black Circle.
+ public Rune BlackCircle { get; set; } = (Rune)'●';
+
+ /// Expand.
+ public Rune Expand { get; set; } = (Rune)'+';
+
+ /// Collapse.
+ public Rune Collapse { get; set; } = (Rune)'-';
+
+ /// Identical To.
+ public Rune IdenticalTo { get; set; } = (Rune)'≡';
+
+ /// Move indicator.
+ public Rune Move { get; set; } = (Rune)'◊';
+
+ /// Size Horizontally indicator.
+ public Rune SizeHorizontal { get; set; } = (Rune)'↔';
+
+ /// Size Vertical indicator.
+ public Rune SizeVertical { get; set; } = (Rune)'↕';
+
+ /// Size Top Left indicator.
+ public Rune SizeTopLeft { get; set; } = (Rune)'↖';
+
+ /// Size Top Right indicator.
+ public Rune SizeTopRight { get; set; } = (Rune)'↗';
+
+ /// Size Bottom Right indicator.
+ public Rune SizeBottomRight { get; set; } = (Rune)'↘';
+
+ /// Size Bottom Left indicator.
+ public Rune SizeBottomLeft { get; set; } = (Rune)'↙';
+
+ /// Apple (non-BMP).
+ public Rune Apple { get; set; } = "🍎".ToRunes () [0];
+
+ /// Apple (BMP).
+ public Rune AppleBMP { get; set; } = (Rune)'❦';
+
+ /// Copy indicator.
+ public Rune Copy { get; set; } = (Rune)'⧉';
+
+ /// Box Drawings Horizontal Line - Light.
+ public Rune HLine { get; set; } = (Rune)'─';
+
+ /// Box Drawings Vertical Line - Light.
+ public Rune VLine { get; set; } = (Rune)'│';
+
+ /// Box Drawings Double Horizontal.
+ public Rune HLineDbl { get; set; } = (Rune)'═';
+
+ /// Box Drawings Double Vertical.
+ public Rune VLineDbl { get; set; } = (Rune)'║';
+
+ /// Box Drawings Heavy Double Dash Horizontal.
+ public Rune HLineHvDa2 { get; set; } = (Rune)'╍';
+
+ /// Box Drawings Heavy Triple Dash Vertical.
+ public Rune VLineHvDa3 { get; set; } = (Rune)'┇';
+
+ /// Box Drawings Heavy Triple Dash Horizontal.
+ public Rune HLineHvDa3 { get; set; } = (Rune)'┅';
+
+ /// Box Drawings Heavy Quadruple Dash Horizontal.
+ public Rune HLineHvDa4 { get; set; } = (Rune)'┉';
+
+ /// Box Drawings Heavy Double Dash Vertical.
+ public Rune VLineHvDa2 { get; set; } = (Rune)'╏';
+
+ /// Box Drawings Heavy Quadruple Dash Vertical.
+ public Rune VLineHvDa4 { get; set; } = (Rune)'┋';
+
+ /// Box Drawings Light Double Dash Horizontal.
+ public Rune HLineDa2 { get; set; } = (Rune)'╌';
+
+ /// Box Drawings Light Triple Dash Vertical.
+ public Rune VLineDa3 { get; set; } = (Rune)'┆';
+
+ /// Box Drawings Light Triple Dash Horizontal.
+ public Rune HLineDa3 { get; set; } = (Rune)'┄';
+
+ /// Box Drawings Light Quadruple Dash Horizontal.
+ public Rune HLineDa4 { get; set; } = (Rune)'┈';
+
+ /// Box Drawings Light Double Dash Vertical.
+ public Rune VLineDa2 { get; set; } = (Rune)'╎';
+
+ /// Box Drawings Light Quadruple Dash Vertical.
+ public Rune VLineDa4 { get; set; } = (Rune)'┊';
+
+ /// Box Drawings Heavy Horizontal.
+ public Rune HLineHv { get; set; } = (Rune)'━';
+
+ /// Box Drawings Heavy Vertical.
+ public Rune VLineHv { get; set; } = (Rune)'┃';
+
+ /// Box Drawings Light Left.
+ public Rune HalfLeftLine { get; set; } = (Rune)'╴';
+
+ /// Box Drawings Light Up.
+ public Rune HalfTopLine { get; set; } = (Rune)'╵';
+
+ /// Box Drawings Light Right.
+ public Rune HalfRightLine { get; set; } = (Rune)'╶';
+
+ /// Box Drawings Light Down.
+ public Rune HalfBottomLine { get; set; } = (Rune)'╷';
+
+ /// Box Drawings Heavy Left.
+ public Rune HalfLeftLineHv { get; set; } = (Rune)'╸';
+
+ /// Box Drawings Heavy Up.
+ public Rune HalfTopLineHv { get; set; } = (Rune)'╹';
+
+ /// Box Drawings Heavy Right.
+ public Rune HalfRightLineHv { get; set; } = (Rune)'╺';
+
+ /// Box Drawings Light Down Heavy.
+ public Rune HalfBottomLineLt { get; set; } = (Rune)'╻';
+
+ /// Box Drawings Light Horizontal and Heavy Horizontal.
+ public Rune RightSideLineLtHv { get; set; } = (Rune)'╼';
+
+ /// Box Drawings Light Vertical and Heavy Horizontal.
+ public Rune BottomSideLineLtHv { get; set; } = (Rune)'╽';
+
+ /// Box Drawings Heavy Left and Light Horizontal.
+ public Rune LeftSideLineHvLt { get; set; } = (Rune)'╾';
+
+ /// Box Drawings Heavy Vertical and Light Horizontal.
+ public Rune TopSideLineHvLt { get; set; } = (Rune)'╿';
+
+ /// Box Drawings Upper Left Corner - Light.
+ public Rune ULCorner { get; set; } = (Rune)'┌';
+
+ /// Box Drawings Upper Left Corner - Double.
+ public Rune ULCornerDbl { get; set; } = (Rune)'╔';
+
+ /// Box Drawings Upper Left Corner - Rounded.
+ public Rune ULCornerR { get; set; } = (Rune)'╭';
+
+ /// Box Drawings Upper Left Corner - Heavy.
+ public Rune ULCornerHv { get; set; } = (Rune)'┏';
+
+ /// Box Drawings Upper Left Corner - Heavy Vertical Light Horizontal.
+ public Rune ULCornerHvLt { get; set; } = (Rune)'┎';
+
+ /// Box Drawings Upper Left Corner - Light Vertical Heavy Horizontal.
+ public Rune ULCornerLtHv { get; set; } = (Rune)'┍';
+
+ /// Box Drawings Upper Left Corner - Double Down Single Horizontal.
+ public Rune ULCornerDblSingle { get; set; } = (Rune)'╓';
+
+ /// Box Drawings Upper Left Corner - Single Down Double Horizontal.
+ public Rune ULCornerSingleDbl { get; set; } = (Rune)'╒';
+
+ /// Box Drawings Lower Left Corner - Light.
+ public Rune LLCorner { get; set; } = (Rune)'└';
+
+ /// Box Drawings Lower Left Corner - Heavy.
+ public Rune LLCornerHv { get; set; } = (Rune)'┗';
+
+ /// Box Drawings Lower Left Corner - Heavy Vertical Light Horizontal.
+ public Rune LLCornerHvLt { get; set; } = (Rune)'┖';
+
+ /// Box Drawings Lower Left Corner - Light Vertical Heavy Horizontal.
+ public Rune LLCornerLtHv { get; set; } = (Rune)'┕';
+
+ /// Box Drawings Lower Left Corner - Double.
+ public Rune LLCornerDbl { get; set; } = (Rune)'╚';
+
+ /// Box Drawings Lower Left Corner - Single Vertical Double Horizontal.
+ public Rune LLCornerSingleDbl { get; set; } = (Rune)'╘';
+
+ /// Box Drawings Lower Left Corner - Double Vertical Single Horizontal.
+ public Rune LLCornerDblSingle { get; set; } = (Rune)'╙';
+
+ /// Box Drawings Lower Left Corner - Rounded.
+ public Rune LLCornerR { get; set; } = (Rune)'╰';
+
+ /// Box Drawings Upper Right Corner - Light.
+ public Rune URCorner { get; set; } = (Rune)'┐';
+
+ /// Box Drawings Upper Right Corner - Double.
+ public Rune URCornerDbl { get; set; } = (Rune)'╗';
+
+ /// Box Drawings Upper Right Corner - Rounded.
+ public Rune URCornerR { get; set; } = (Rune)'╮';
+
+ /// Box Drawings Upper Right Corner - Heavy.
+ public Rune URCornerHv { get; set; } = (Rune)'┓';
+
+ /// Box Drawings Upper Right Corner - Heavy Vertical Light Horizontal.
+ public Rune URCornerHvLt { get; set; } = (Rune)'┑';
+
+ /// Box Drawings Upper Right Corner - Light Vertical Heavy Horizontal.
+ public Rune URCornerLtHv { get; set; } = (Rune)'┒';
+
+ /// Box Drawings Upper Right Corner - Double Vertical Single Horizontal.
+ public Rune URCornerDblSingle { get; set; } = (Rune)'╖';
+
+ /// Box Drawings Upper Right Corner - Single Vertical Double Horizontal.
+ public Rune URCornerSingleDbl { get; set; } = (Rune)'╕';
+
+ /// Box Drawings Lower Right Corner - Light.
+ public Rune LRCorner { get; set; } = (Rune)'┘';
+
+ /// Box Drawings Lower Right Corner - Double.
+ public Rune LRCornerDbl { get; set; } = (Rune)'╝';
+
+ /// Box Drawings Lower Right Corner - Rounded.
+ public Rune LRCornerR { get; set; } = (Rune)'╯';
+
+ /// Box Drawings Lower Right Corner - Heavy.
+ public Rune LRCornerHv { get; set; } = (Rune)'┛';
+
+ /// Box Drawings Lower Right Corner - Double Vertical Single Horizontal.
+ public Rune LRCornerDblSingle { get; set; } = (Rune)'╜';
+
+ /// Box Drawings Lower Right Corner - Single Vertical Double Horizontal.
+ public Rune LRCornerSingleDbl { get; set; } = (Rune)'╛';
+
+ /// Box Drawings Lower Right Corner - Light Vertical Heavy Horizontal.
+ public Rune LRCornerLtHv { get; set; } = (Rune)'┙';
+
+ /// Box Drawings Lower Right Corner - Heavy Vertical Light Horizontal.
+ public Rune LRCornerHvLt { get; set; } = (Rune)'┚';
+
+ /// Box Drawings Left Tee - Light.
+ public Rune LeftTee { get; set; } = (Rune)'├';
+
+ /// Box Drawings Left Tee - Single Vertical Double Horizontal.
+ public Rune LeftTeeDblH { get; set; } = (Rune)'╞';
+
+ /// Box Drawings Left Tee - Double Vertical Single Horizontal.
+ public Rune LeftTeeDblV { get; set; } = (Rune)'╟';
+
+ /// Box Drawings Left Tee - Double.
+ public Rune LeftTeeDbl { get; set; } = (Rune)'╠';
+
+ /// Box Drawings Left Tee - Heavy Horizontal Light Vertical.
+ public Rune LeftTeeHvH { get; set; } = (Rune)'┝';
+
+ /// Box Drawings Left Tee - Light Horizontal Heavy Vertical.
+ public Rune LeftTeeHvV { get; set; } = (Rune)'┠';
+
+ /// Box Drawings Left Tee - Heavy.
+ public Rune LeftTeeHvDblH { get; set; } = (Rune)'┣';
+
+ /// Box Drawings Right Tee - Light.
+ public Rune RightTee { get; set; } = (Rune)'┤';
+
+ /// Box Drawings Right Tee - Single Vertical Double Horizontal.
+ public Rune RightTeeDblH { get; set; } = (Rune)'╡';
+
+ /// Box Drawings Right Tee - Double Vertical Single Horizontal.
+ public Rune RightTeeDblV { get; set; } = (Rune)'╢';
+
+ /// Box Drawings Right Tee - Double.
+ public Rune RightTeeDbl { get; set; } = (Rune)'╣';
+
+ /// Box Drawings Right Tee - Heavy Horizontal Light Vertical.
+ public Rune RightTeeHvH { get; set; } = (Rune)'┥';
+
+ /// Box Drawings Right Tee - Light Horizontal Heavy Vertical.
+ public Rune RightTeeHvV { get; set; } = (Rune)'┨';
+
+ /// Box Drawings Right Tee - Heavy.
+ public Rune RightTeeHvDblH { get; set; } = (Rune)'┫';
+
+ /// Box Drawings Top Tee - Light.
+ public Rune TopTee { get; set; } = (Rune)'┬';
+
+ /// Box Drawings Top Tee - Single Vertical Double Horizontal.
+ public Rune TopTeeDblH { get; set; } = (Rune)'╤';
+
+ /// Box Drawings Top Tee - Double Vertical Single Horizontal.
+ public Rune TopTeeDblV { get; set; } = (Rune)'╥';
+
+ /// Box Drawings Top Tee - Double.
+ public Rune TopTeeDbl { get; set; } = (Rune)'╦';
+
+ /// Box Drawings Top Tee - Heavy Horizontal Light Vertical.
+ public Rune TopTeeHvH { get; set; } = (Rune)'┯';
+
+ /// Box Drawings Top Tee - Light Horizontal Heavy Vertical.
+ public Rune TopTeeHvV { get; set; } = (Rune)'┰';
+
+ /// Box Drawings Top Tee - Heavy.
+ public Rune TopTeeHvDblH { get; set; } = (Rune)'┳';
+
+ /// Box Drawings Bottom Tee - Light.
+ public Rune BottomTee { get; set; } = (Rune)'┴';
+
+ /// Box Drawings Bottom Tee - Single Vertical Double Horizontal.
+ public Rune BottomTeeDblH { get; set; } = (Rune)'╧';
+
+ /// Box Drawings Bottom Tee - Double Vertical Single Horizontal.
+ public Rune BottomTeeDblV { get; set; } = (Rune)'╨';
+
+ /// Box Drawings Bottom Tee - Double.
+ public Rune BottomTeeDbl { get; set; } = (Rune)'╩';
+
+ /// Box Drawings Bottom Tee - Heavy Horizontal Light Vertical.
+ public Rune BottomTeeHvH { get; set; } = (Rune)'┷';
+
+ /// Box Drawings Bottom Tee - Light Horizontal Heavy Vertical.
+ public Rune BottomTeeHvV { get; set; } = (Rune)'┸';
+
+ /// Box Drawings Bottom Tee - Heavy.
+ public Rune BottomTeeHvDblH { get; set; } = (Rune)'┻';
+
+ /// Box Drawings Cross - Light.
+ public Rune Cross { get; set; } = (Rune)'┼';
+
+ /// Box Drawings Cross - Single Vertical Double Horizontal.
+ public Rune CrossDblH { get; set; } = (Rune)'╪';
+
+ /// Box Drawings Cross - Double Vertical Single Horizontal.
+ public Rune CrossDblV { get; set; } = (Rune)'╫';
+
+ /// Box Drawings Cross - Double.
+ public Rune CrossDbl { get; set; } = (Rune)'╬';
+
+ /// Box Drawings Cross - Heavy Horizontal Light Vertical.
+ public Rune CrossHvH { get; set; } = (Rune)'┿';
+
+ /// Box Drawings Cross - Light Horizontal Heavy Vertical.
+ public Rune CrossHvV { get; set; } = (Rune)'╂';
+
+ /// Box Drawings Cross - Heavy.
+ public Rune CrossHv { get; set; } = (Rune)'╋';
+
+ /// Shadow - Vertical Start.
+ public Rune ShadowVerticalStart { get; set; } = (Rune)'▖';
+
+ /// Shadow - Vertical.
+ public Rune ShadowVertical { get; set; } = (Rune)'▌';
+
+ /// Shadow - Horizontal Start.
+ public Rune ShadowHorizontalStart { get; set; } = (Rune)'▝';
+
+ /// Shadow - Horizontal.
+ public Rune ShadowHorizontal { get; set; } = (Rune)'▀';
+
+ /// Shadow - Horizontal End.
+ public Rune ShadowHorizontalEnd { get; set; } = (Rune)'▘';
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static GlyphSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/HexViewSettings.cs b/Terminal.Gui/Configuration/Settings/HexViewSettings.cs
new file mode 100644
index 0000000000..10c7721a3e
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/HexViewSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class HexViewSettings
+{
+ /// Gets or sets the default cursor style for hex views.
+ public CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static HexViewSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/ISchemeManager.cs b/Terminal.Gui/Configuration/Settings/ISchemeManager.cs
new file mode 100644
index 0000000000..99a1c59491
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/ISchemeManager.cs
@@ -0,0 +1,20 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Abstracts color scheme management. Provides access to named schemes.
+///
+public interface ISchemeManager
+{
+ /// Gets the names of all registered schemes.
+ IReadOnlyList SchemeNames { get; }
+
+ /// Gets a scheme by name.
+ /// The scheme name (case-insensitive).
+ /// The scheme, or if not found.
+ Scheme? GetScheme (string name);
+
+ /// Adds or updates a named scheme.
+ /// The scheme name.
+ /// The scheme to add/update.
+ void AddScheme (string name, Scheme scheme);
+}
diff --git a/Terminal.Gui/Configuration/Settings/IThemeManager.cs b/Terminal.Gui/Configuration/Settings/IThemeManager.cs
new file mode 100644
index 0000000000..75d758b197
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/IThemeManager.cs
@@ -0,0 +1,31 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Abstracts theme management. Allows switching themes and querying available themes.
+///
+public interface IThemeManager
+{
+ /// Gets the name of the currently active theme.
+ string CurrentThemeName { get; }
+
+ /// Gets the names of all available themes.
+ IReadOnlyList ThemeNames { get; }
+
+ ///
+ /// Switches the active theme. This updates all ThemeScope Settings POCOs
+ /// and triggers redraw on affected views.
+ ///
+ /// The name of the theme to activate.
+ /// if the theme was found and activated; otherwise .
+ bool SwitchTheme (string themeName);
+
+ ///
+ /// Raised after the active theme has changed. The
+ /// is the name of the newly-active theme.
+ ///
+ ///
+ /// For static (non-DI) consumers such as subclasses, see
+ /// , which raises in lockstep with this event.
+ ///
+ event EventHandler>? ThemeChanged;
+}
diff --git a/Terminal.Gui/Configuration/Settings/KeySettings.cs b/Terminal.Gui/Configuration/Settings/KeySettings.cs
new file mode 100644
index 0000000000..14846553f0
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/KeySettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (SettingsScope).
+///
+public class KeySettings
+{
+ /// Gets or sets the separator character used when parsing and printing Keys.
+ public Rune Separator { get; set; } = new Rune ('+');
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static KeySettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/LinearRangeSettings.cs b/Terminal.Gui/Configuration/Settings/LinearRangeSettings.cs
new file mode 100644
index 0000000000..5c155dc3f9
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/LinearRangeSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class LinearRangeSettings
+{
+ /// Gets or sets the default cursor style for linear range views.
+ public CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static LinearRangeSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/MecSchemeManager.cs b/Terminal.Gui/Configuration/Settings/MecSchemeManager.cs
new file mode 100644
index 0000000000..b8dd80a3ec
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/MecSchemeManager.cs
@@ -0,0 +1,44 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// MEC-backed implementation of .
+/// During the transition period, this delegates to the existing static
+/// for scheme data.
+///
+public class MecSchemeManager : ISchemeManager
+{
+ ///
+ public IReadOnlyList SchemeNames
+ {
+ get
+ {
+ try
+ {
+ return SchemeManager.GetSchemeNames ().ToList ();
+ }
+ catch
+ {
+ return [];
+ }
+ }
+ }
+
+ ///
+ public Scheme? GetScheme (string name)
+ {
+ try
+ {
+ return SchemeManager.GetScheme (name);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ public void AddScheme (string name, Scheme scheme)
+ {
+ SchemeManager.AddScheme (name, scheme);
+ }
+}
diff --git a/Terminal.Gui/Configuration/Settings/MecThemeManager.cs b/Terminal.Gui/Configuration/Settings/MecThemeManager.cs
new file mode 100644
index 0000000000..84a80fb9a3
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/MecThemeManager.cs
@@ -0,0 +1,87 @@
+#pragma warning disable CS0618 // Obsolete - MecThemeManager forwards from legacy ThemeManager during transition
+
+namespace Terminal.Gui.Configuration;
+
+///
+/// MEC-backed implementation of .
+/// During the transition period (PR #5411), this delegates writes to the legacy static
+/// because the runtime theme/scheme dictionary is still owned by .
+/// The Phase A2 work in #5416 will let this type own the theme/scheme data directly.
+///
+public class MecThemeManager : IThemeManager
+{
+ private readonly TuiConfigurationBuilder _builder;
+
+ /// Initializes a new instance of .
+ public MecThemeManager (TuiConfigurationBuilder builder)
+ {
+ _builder = builder;
+
+ // Forward legacy ThemeManager.ThemeChanged into the IThemeManager.ThemeChanged event so
+ // consumers of the new API see every theme change, regardless of which API triggered it.
+ ThemeManager.ThemeChanged += OnLegacyThemeChanged;
+ }
+
+ private void OnLegacyThemeChanged (object? sender, App.EventArgs e) => ThemeChanged?.Invoke (this, e);
+
+ ///
+ public string CurrentThemeName => ThemeSettings.Defaults.Theme;
+
+ ///
+ public IReadOnlyList ThemeNames
+ {
+ get
+ {
+ // During transition, delegate to existing ThemeManager
+ try
+ {
+ return ThemeManager.GetThemeNames ();
+ }
+ catch (InvalidOperationException)
+ {
+ return [ThemeManager.DEFAULT_THEME_NAME];
+ }
+ }
+ }
+
+ ///
+ public event EventHandler>? ThemeChanged;
+
+ ///
+ public bool SwitchTheme (string themeName)
+ {
+ if (string.IsNullOrEmpty (themeName))
+ {
+ return false;
+ }
+
+ // Verify the theme exists before switching
+ if (!ThemeNames.Contains (themeName))
+ {
+ return false;
+ }
+
+ // During transition, also update the existing ThemeManager. Its setter raises
+ // ThemeManager.ThemeChanged, which is forwarded via OnLegacyThemeChanged above.
+ try
+ {
+ ThemeManager.Theme = themeName;
+ }
+ catch (InvalidOperationException)
+ {
+ return false;
+ }
+ catch (KeyNotFoundException)
+ {
+ return false;
+ }
+
+ // Update the settings POCO only on success
+ ThemeSettings.Defaults.Theme = themeName;
+
+ // Re-apply all ThemeScope POCOs from configuration
+ _builder.ApplyToStaticFacades ();
+
+ return true;
+ }
+}
diff --git a/Terminal.Gui/Configuration/Settings/MenuBarSettings.cs b/Terminal.Gui/Configuration/Settings/MenuBarSettings.cs
new file mode 100644
index 0000000000..342497936a
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/MenuBarSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults.
+///
+public class MenuBarSettings
+{
+ /// Gets or sets the default border style for menu bars.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.None;
+
+ /// Gets or sets the default activation key for menu bars.
+ public Key DefaultKey { get; set; } = Key.F10;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static MenuBarSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/MenuSettings.cs b/Terminal.Gui/Configuration/Settings/MenuSettings.cs
new file mode 100644
index 0000000000..65fdc8f7ec
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/MenuSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class MenuSettings
+{
+ /// Gets or sets the default border style for menus.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.None;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static MenuSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/MessageBoxSettings.cs b/Terminal.Gui/Configuration/Settings/MessageBoxSettings.cs
new file mode 100644
index 0000000000..4e5ff7af13
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/MessageBoxSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for visual defaults (ThemeScope).
+///
+public class MessageBoxSettings
+{
+ /// Gets or sets the default border style for message boxes.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.Heavy;
+
+ /// Gets or sets the default button alignment for message boxes.
+ public Alignment DefaultButtonAlignment { get; set; } = Alignment.Center;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static MessageBoxSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/NerdFontsSettings.cs b/Terminal.Gui/Configuration/Settings/NerdFontsSettings.cs
new file mode 100644
index 0000000000..b5a4a287c3
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/NerdFontsSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class NerdFontsSettings
+{
+ /// Gets or sets whether Nerd Fonts glyphs are enabled.
+ public bool Enable { get; set; } = false;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static NerdFontsSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/PopoverMenuSettings.cs b/Terminal.Gui/Configuration/Settings/PopoverMenuSettings.cs
new file mode 100644
index 0000000000..6cb41b741d
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/PopoverMenuSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (SettingsScope).
+///
+public class PopoverMenuSettings
+{
+ /// Gets or sets the default activation key for popover menus.
+ public Key DefaultKey { get; set; } = Key.F10.WithShift;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static PopoverMenuSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/SelectorBaseSettings.cs b/Terminal.Gui/Configuration/Settings/SelectorBaseSettings.cs
new file mode 100644
index 0000000000..9c00011337
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/SelectorBaseSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class SelectorBaseSettings
+{
+ /// Gets or sets the default mouse highlight states for selectors.
+ public MouseState DefaultMouseHighlightStates { get; set; } = MouseState.In;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static SelectorBaseSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/StatusBarSettings.cs b/Terminal.Gui/Configuration/Settings/StatusBarSettings.cs
new file mode 100644
index 0000000000..51932645ce
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/StatusBarSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class StatusBarSettings
+{
+ /// Gets or sets the default separator line style for status bars.
+ public LineStyle DefaultSeparatorLineStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static StatusBarSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/TextFieldSettings.cs b/Terminal.Gui/Configuration/Settings/TextFieldSettings.cs
new file mode 100644
index 0000000000..c0caf63bf7
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/TextFieldSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class TextFieldSettings
+{
+ /// Gets or sets the default cursor style for text fields.
+ public CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBar;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static TextFieldSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/TextViewSettings.cs b/Terminal.Gui/Configuration/Settings/TextViewSettings.cs
new file mode 100644
index 0000000000..5505dedb2b
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/TextViewSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (ThemeScope).
+///
+public class TextViewSettings
+{
+ /// Gets or sets the default cursor style for text views.
+ public CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBar;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static TextViewSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/ThemeChanges.cs b/Terminal.Gui/Configuration/Settings/ThemeChanges.cs
new file mode 100644
index 0000000000..cef9969c42
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/ThemeChanges.cs
@@ -0,0 +1,41 @@
+#pragma warning disable CS0618 // Obsolete - bridges legacy ConfigurationManager.Applied and ThemeManager.ThemeChanged into the MEC-facing facade
+
+namespace Terminal.Gui.Configuration;
+
+///
+/// Static facade that raises when the active theme — or any setting that affects views
+/// reading from *Settings.Defaults — has changed. Provided for consumers that cannot
+/// take a dependency (typically subclasses
+/// constructed before an is available).
+///
+///
+///
+/// This class is the MEC-friendly replacement for the obsolete
+/// ConfigurationManager.Applied event subscriptions in subclasses.
+/// It raises after any settings re-application (theme switch, runtime config reload,
+/// ConfigurationManager.Reset(), etc.) so that handlers re-read their static defaults.
+///
+///
+/// The bridge to the legacy events is established the first time this class is referenced;
+/// the subscription persists for the lifetime of the process and never needs explicit teardown.
+///
+///
+public static class ThemeChanges
+{
+ static ThemeChanges ()
+ {
+ // ConfigurationManager.Apply() writes ConfigProperty values directly (bypassing the
+ // ThemeManager.Theme setter), so the broad Applied event is required to catch the
+ // most common change path (Init, Reset, RuntimeConfig).
+ ConfigurationManager.Applied += (_, _) => ThemeChanged?.Invoke (null, new App.EventArgs (ThemeManager.GetCurrentThemeName ()));
+
+ // The narrow event also forwards, so explicit ThemeManager.Theme = X calls still surface here.
+ ThemeManager.ThemeChanged += (_, e) => ThemeChanged?.Invoke (null, e);
+ }
+
+ ///
+ /// Raised after the active theme — or any setting affecting *Settings.Defaults — has changed.
+ /// The is the name of the currently-active theme.
+ ///
+ public static event EventHandler>? ThemeChanged;
+}
diff --git a/Terminal.Gui/Configuration/Settings/ThemeSettings.cs b/Terminal.Gui/Configuration/Settings/ThemeSettings.cs
new file mode 100644
index 0000000000..a423f470d6
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/ThemeSettings.cs
@@ -0,0 +1,17 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for theme selection (SettingsScope).
+/// Controls which theme is active and what themes are available.
+///
+public class ThemeSettings
+{
+ /// Gets or sets the name of the active theme.
+ public string Theme { get; set; } = ThemeManager.DEFAULT_THEME_NAME;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static ThemeSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/TraceSettings.cs b/Terminal.Gui/Configuration/Settings/TraceSettings.cs
new file mode 100644
index 0000000000..90c9ea6715
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/TraceSettings.cs
@@ -0,0 +1,16 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for defaults (SettingsScope).
+///
+public class TraceSettings
+{
+ /// Gets or sets the enabled trace categories.
+ public TraceCategory EnabledCategories { get; set; } = TraceCategory.None;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static TraceSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs b/Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs
new file mode 100644
index 0000000000..831b5e3d9d
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs
@@ -0,0 +1,174 @@
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace Terminal.Gui.Configuration;
+
+///
+/// Builds and manages a Terminal.Gui MEC-based configuration, loading from all standard sources
+/// in the correct precedence order.
+///
+///
+///
+/// This is the MEC-based replacement for the legacy .
+/// It provides the same multi-source precedence (library defaults → app defaults → user files
+/// → environment variables → runtime config) using standard Microsoft.Extensions.Configuration.
+///
+/// App Developer Usage:
+///
+/// // Define your app settings POCO:
+/// public class MyAppSettings
+/// {
+/// public string Title { get; set; } = "My App";
+/// public bool DarkMode { get; set; }
+/// public static MyAppSettings Defaults { get; set; } = new ();
+/// }
+///
+/// // In your app startup:
+/// var builder = new TuiConfigurationBuilder ("MyApp");
+/// builder.BindAppSettings<MyAppSettings> ("MyApp", s => MyAppSettings.Defaults = s);
+/// builder.ApplyToStaticFacades ();
+///
+/// // Access settings:
+/// string title = MyAppSettings.Defaults.Title;
+///
+///
+/// To add custom configuration sources, use the MEC extension methods directly:
+///
+///
+/// IConfigurationBuilder configBuilder = new ConfigurationBuilder ()
+/// .AddTuiLibraryDefaults ()
+/// .AddTuiUserFiles ("MyApp")
+/// .AddJsonFile ("custom-settings.json", optional: true);
+/// IConfiguration config = configBuilder.Build ();
+///
+///
+public class TuiConfigurationBuilder
+{
+ private readonly string? _appName;
+ private string? _runtimeConfig;
+ private IConfiguration? _configuration;
+
+ /// Initializes a new instance of .
+ /// The application name for app-specific config file discovery. If null, uses entry assembly name.
+ public TuiConfigurationBuilder (string? appName = null)
+ {
+ _appName = appName ?? System.Reflection.Assembly.GetEntryAssembly ()?.GetName ().Name;
+ }
+
+ ///
+ /// Gets or sets the runtime configuration JSON string (highest priority).
+ /// Setting this invalidates the cached configuration, causing a rebuild on next access.
+ ///
+ public string? RuntimeConfig
+ {
+ get => _runtimeConfig;
+ set
+ {
+ _runtimeConfig = value;
+ _configuration = null; // force rebuild
+ }
+ }
+
+ ///
+ /// Gets the built instance. Lazily built on first access.
+ /// Rebuilt when changes.
+ ///
+ public IConfiguration Configuration => _configuration ??= Build ();
+
+ ///
+ /// Builds the configuration from all sources in precedence order.
+ ///
+ /// The built configuration root.
+ public IConfiguration Build ()
+ {
+ IConfigurationBuilder builder = new ConfigurationBuilder ()
+ .AddTuiLibraryDefaults ()
+ .AddTuiAppDefaults (_appName)
+ .AddTuiUserFiles (_appName)
+ .AddTuiEnvironmentVariable ()
+ .AddTuiRuntimeConfig (_runtimeConfig);
+
+ _configuration = builder.Build ();
+
+ return _configuration;
+ }
+
+ ///
+ /// Gets the MEC-backed theme manager instance for this builder.
+ ///
+ public IThemeManager ThemeManager => _themeManager ??= new MecThemeManager (this);
+ private IThemeManager? _themeManager;
+
+ ///
+ /// Gets the MEC-backed scheme manager instance for this builder.
+ ///
+ public ISchemeManager SchemeManager => _schemeManager ??= new MecSchemeManager ();
+ private ISchemeManager? _schemeManager;
+
+ ///
+ /// Applies the current configuration to all static settings facades.
+ /// Call this after building or rebuilding to push MEC values to the static Defaults properties.
+ ///
+ public void ApplyToStaticFacades ()
+ {
+ IConfiguration config = Configuration;
+
+ // SettingsScope POCOs
+ BindSection (config, "Theme", s => ThemeSettings.Defaults = s);
+ BindSection (config, "Application", s => ApplicationSettings.Defaults = s);
+ BindSection (config, "Driver", s => DriverSettings.Defaults = s);
+ BindSection (config, "FileDialog", s => FileDialogSettings.Defaults = s);
+ BindSection (config, "FileDialogStyle", s => FileDialogStyleSettings.Defaults = s);
+ BindSection (config, "Key", s => KeySettings.Defaults = s);
+ BindSection (config, "Trace", s => TraceSettings.Defaults = s);
+
+ // ThemeScope POCOs
+ BindSection (config, "Button", s => ButtonSettings.Defaults = s);
+ BindSection (config, "CheckBox", s => CheckBoxSettings.Defaults = s);
+ BindSection (config, "CharMap", s => CharMapSettings.Defaults = s);
+ BindSection (config, "Dialog", s => DialogSettings.Defaults = s);
+ BindSection (config, "FrameView", s => FrameViewSettings.Defaults = s);
+ BindSection (config, "HexView", s => HexViewSettings.Defaults = s);
+ BindSection (config, "LinearRange", s => LinearRangeSettings.Defaults = s);
+ BindSection (config, "MenuBar", s => MenuBarSettings.Defaults = s);
+ BindSection (config, "Menu", s => MenuSettings.Defaults = s);
+ BindSection (config, "MessageBox", s => MessageBoxSettings.Defaults = s);
+ BindSection (config, "NerdFonts", s => NerdFontsSettings.Defaults = s);
+ BindSection (config, "PopoverMenu", s => PopoverMenuSettings.Defaults = s);
+ BindSection (config, "SelectorBase", s => SelectorBaseSettings.Defaults = s);
+ BindSection (config, "StatusBar", s => StatusBarSettings.Defaults = s);
+ BindSection (config, "TextField", s => TextFieldSettings.Defaults = s);
+ BindSection (config, "TextView", s => TextViewSettings.Defaults = s);
+ BindSection (config, "Window", s => WindowSettings.Defaults = s);
+ BindSection (config, "Glyphs", s => GlyphSettings.Defaults = s);
+ }
+
+ ///
+ /// Binds a custom application settings section from the configuration to a POCO instance.
+ /// This is the MEC replacement for the legacy .
+ ///
+ /// The settings POCO type.
+ /// The JSON section name to bind from.
+ /// Action to apply the bound settings (typically update a static Defaults property).
+ /// This builder for chaining.
+ [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
+ [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Settings POCOs are simple types; no generic instantiation needed at runtime.")]
+ public TuiConfigurationBuilder BindAppSettings (string sectionName, Action apply) where T : new ()
+ {
+ T settings = new ();
+ Configuration.GetSection (sectionName).Bind (settings);
+ apply (settings);
+
+ return this;
+ }
+
+ [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
+ [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Settings POCOs are simple types; no generic instantiation needed at runtime.")]
+ private static void BindSection (IConfiguration config, string sectionName, Action apply) where T : new ()
+ {
+ T settings = new ();
+ config.GetSection (sectionName).Bind (settings);
+ apply (settings);
+ }
+}
diff --git a/Terminal.Gui/Configuration/Settings/TuiConfigurationExtensions.cs b/Terminal.Gui/Configuration/Settings/TuiConfigurationExtensions.cs
new file mode 100644
index 0000000000..9d2b5f8a3a
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/TuiConfigurationExtensions.cs
@@ -0,0 +1,152 @@
+using System.Reflection;
+using Microsoft.Extensions.Configuration;
+
+namespace Terminal.Gui.Configuration;
+
+///
+/// Extension methods for that add Terminal.Gui configuration sources
+/// in the correct precedence order (matching the existing behavior).
+///
+public static class TuiConfigurationExtensions
+{
+ /// The name of the TUI configuration folder.
+ public const string TUI_CONFIG_FOLDER = ".tui";
+
+ /// The name of the TUI configuration environment variable.
+ public const string TUI_CONFIG_ENV = "TUI_CONFIG";
+
+ /// The default config filename.
+ public const string CONFIG_FILENAME = "config.json";
+
+ ///
+ /// Adds the Terminal.Gui library's embedded config.json as a configuration source.
+ /// This is the lowest-priority file-based source (above hard-coded POCO defaults).
+ ///
+ /// The configuration builder.
+ /// The builder for chaining.
+ public static IConfigurationBuilder AddTuiLibraryDefaults (this IConfigurationBuilder builder)
+ {
+ Assembly libraryAssembly = typeof (TuiConfigurationExtensions).Assembly;
+ string resourceName = $"Terminal.Gui.Resources.{CONFIG_FILENAME}";
+
+ Stream? stream = libraryAssembly.GetManifestResourceStream (resourceName);
+
+ if (stream is not null)
+ {
+ builder.AddJsonStream (stream);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Adds the entry assembly's embedded config.json as a configuration source.
+ ///
+ /// The configuration builder.
+ /// The application name (used for app-specific config files).
+ /// The builder for chaining.
+ public static IConfigurationBuilder AddTuiAppDefaults (this IConfigurationBuilder builder, string? appName = null)
+ {
+ Assembly? entryAssembly = Assembly.GetEntryAssembly ();
+
+ if (entryAssembly is null)
+ {
+ return builder;
+ }
+
+ string? resourceName = entryAssembly
+ .GetManifestResourceNames ()
+ .FirstOrDefault (x => x.EndsWith (CONFIG_FILENAME, StringComparison.Ordinal));
+
+ if (string.IsNullOrEmpty (resourceName))
+ {
+ return builder;
+ }
+
+ Stream? stream = entryAssembly.GetManifestResourceStream (resourceName);
+
+ if (stream is not null)
+ {
+ builder.AddJsonStream (stream);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Adds user-level configuration files from the standard locations:
+ ///
+ /// - ~/.tui/config.json (GlobalHome)
+ /// - ./.tui/config.json (GlobalCurrent)
+ /// - ~/.tui/{appName}.config.json (AppHome)
+ /// - ./.tui/{appName}.config.json (AppCurrent)
+ ///
+ /// Files are optional — missing files are silently skipped. Later files override earlier ones.
+ ///
+ /// The configuration builder.
+ /// The application name for app-specific config files. If null, app-specific files are skipped.
+ /// The builder for chaining.
+ public static IConfigurationBuilder AddTuiUserFiles (this IConfigurationBuilder builder, string? appName = null)
+ {
+ string homeDir = Environment.GetFolderPath (Environment.SpecialFolder.UserProfile);
+ string globalHomePath = Path.Combine (homeDir, TUI_CONFIG_FOLDER, CONFIG_FILENAME);
+ string globalCurrentPath = Path.Combine (".", TUI_CONFIG_FOLDER, CONFIG_FILENAME);
+
+ builder.AddJsonFile (globalHomePath, optional: true, reloadOnChange: false);
+ builder.AddJsonFile (globalCurrentPath, optional: true, reloadOnChange: false);
+
+ if (!string.IsNullOrEmpty (appName))
+ {
+ string appHomePath = Path.Combine (homeDir, TUI_CONFIG_FOLDER, $"{appName}.{CONFIG_FILENAME}");
+ string appCurrentPath = Path.Combine (".", TUI_CONFIG_FOLDER, $"{appName}.{CONFIG_FILENAME}");
+
+ builder.AddJsonFile (appHomePath, optional: true, reloadOnChange: false);
+ builder.AddJsonFile (appCurrentPath, optional: true, reloadOnChange: false);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Adds the TUI_CONFIG environment variable as a JSON configuration source.
+ /// The environment variable value is treated as inline JSON content.
+ ///
+ /// The configuration builder.
+ /// The builder for chaining.
+ public static IConfigurationBuilder AddTuiEnvironmentVariable (this IConfigurationBuilder builder)
+ {
+ string? envConfig = Environment.GetEnvironmentVariable (TUI_CONFIG_ENV);
+
+ if (string.IsNullOrEmpty (envConfig))
+ {
+ return builder;
+ }
+
+ byte[] bytes = System.Text.Encoding.UTF8.GetBytes (envConfig);
+ MemoryStream stream = new (bytes);
+ builder.AddJsonStream (stream);
+
+ return builder;
+ }
+
+ ///
+ /// Adds an in-memory JSON string as the highest-priority configuration source.
+ /// Equivalent to the legacy ConfigurationManager.RuntimeConfig property.
+ ///
+ /// The configuration builder.
+ /// A JSON string containing configuration overrides.
+ /// The builder for chaining.
+ public static IConfigurationBuilder AddTuiRuntimeConfig (this IConfigurationBuilder builder, string? json)
+ {
+ if (string.IsNullOrEmpty (json))
+ {
+ return builder;
+ }
+
+ byte[] bytes = System.Text.Encoding.UTF8.GetBytes (json);
+ MemoryStream stream = new (bytes);
+ builder.AddJsonStream (stream);
+
+ return builder;
+ }
+}
diff --git a/Terminal.Gui/Configuration/Settings/WindowSettings.cs b/Terminal.Gui/Configuration/Settings/WindowSettings.cs
new file mode 100644
index 0000000000..13ace188ff
--- /dev/null
+++ b/Terminal.Gui/Configuration/Settings/WindowSettings.cs
@@ -0,0 +1,19 @@
+namespace Terminal.Gui.Configuration;
+
+///
+/// Settings POCO for visual defaults (ThemeScope).
+///
+public class WindowSettings
+{
+ /// Gets or sets the default shadow style for windows.
+ public ShadowStyles DefaultShadow { get; set; } = ShadowStyles.None;
+
+ /// Gets or sets the default border style for windows.
+ public LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// The static facade instance. Always contains the current effective values.
+ /// Updated by the MEC binding at initialization.
+ ///
+ public static WindowSettings Defaults { get; set; } = new ();
+}
diff --git a/Terminal.Gui/Configuration/SettingsScope.cs b/Terminal.Gui/Configuration/SettingsScope.cs
index de9dbdeb8a..870a7858af 100644
--- a/Terminal.Gui/Configuration/SettingsScope.cs
+++ b/Terminal.Gui/Configuration/SettingsScope.cs
@@ -1,5 +1,7 @@
using System.Text.Json.Serialization;
+#pragma warning disable CS0618 // Obsolete - SettingsScope uses ConfigProperty internally
+
namespace Terminal.Gui.Configuration;
// TODO: Change to internal to prevent app usage
@@ -21,6 +23,7 @@ namespace Terminal.Gui.Configuration;
///
///
[JsonConverter (typeof (ScopeJsonConverter))]
+[Obsolete ("Being replaced by Microsoft.Extensions.Configuration. Will be removed in a future version.")]
public class SettingsScope : Scope
{
///
diff --git a/Terminal.Gui/Configuration/SourceGenerationContext.cs b/Terminal.Gui/Configuration/SourceGenerationContext.cs
index c39f254390..800dc4dad7 100644
--- a/Terminal.Gui/Configuration/SourceGenerationContext.cs
+++ b/Terminal.Gui/Configuration/SourceGenerationContext.cs
@@ -2,6 +2,8 @@
using System.Text.Json.Serialization;
using Terminal.Gui.Input;
+#pragma warning disable CS0618 // Obsolete - SourceGenerationContext registers serialization types used during transition
+
namespace Terminal.Gui.Configuration;
///
diff --git a/Terminal.Gui/Configuration/SourcesManager.cs b/Terminal.Gui/Configuration/SourcesManager.cs
index 415a45aeba..75e5163f78 100644
--- a/Terminal.Gui/Configuration/SourcesManager.cs
+++ b/Terminal.Gui/Configuration/SourcesManager.cs
@@ -5,6 +5,8 @@
using System.Text.Json;
using Trace = Terminal.Gui.Tracing.Trace;
+#pragma warning disable CS0618 // Obsolete - SourcesManager still uses ConfigurationManager internally during transition
+
namespace Terminal.Gui.Configuration;
///
@@ -154,7 +156,7 @@ internal bool Load (SettingsScope? settingsScope, Stream stream, string source,
stream.Position = 0;
Debug.Assert (json != null);
#endif
- SettingsScope? scope = JsonSerializer.Deserialize (stream, ConfigurationManager.SerializerContext.SettingsScope);
+ SettingsScope? scope = JsonSerializer.Deserialize (stream, TuiSerializerContext.Instance.SettingsScope);
if (scope is null)
{
@@ -301,7 +303,7 @@ internal bool Load (SettingsScope? settingsScope, Assembly assembly, string reso
///
internal string ToJson (SettingsScope? scope)
{
- return JsonSerializer.Serialize (scope, ConfigurationManager.SerializerContext.SettingsScope);
+ return JsonSerializer.Serialize (scope, TuiSerializerContext.Instance.SettingsScope);
}
///
@@ -310,7 +312,7 @@ internal string ToJson (SettingsScope? scope)
///
internal Stream ToStream (SettingsScope? scope)
{
- string json = JsonSerializer.Serialize (scope, ConfigurationManager.SerializerContext.SettingsScope);
+ string json = JsonSerializer.Serialize (scope, TuiSerializerContext.Instance.SettingsScope);
// turn it into a stream
var stream = new MemoryStream ();
diff --git a/Terminal.Gui/Configuration/ThemeManager.cs b/Terminal.Gui/Configuration/ThemeManager.cs
index 2f0c1dcc9f..7f969ea35f 100644
--- a/Terminal.Gui/Configuration/ThemeManager.cs
+++ b/Terminal.Gui/Configuration/ThemeManager.cs
@@ -3,6 +3,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
+#pragma warning disable CS0618 // Obsolete - ThemeManager still uses ConfigurationManager/ConfigProperty/ThemeScope internally during transition
+
namespace Terminal.Gui.Configuration;
/// Manages Themes.
diff --git a/Terminal.Gui/Configuration/ThemeScope.cs b/Terminal.Gui/Configuration/ThemeScope.cs
index f5f0cdcf7b..cfd6f907f7 100644
--- a/Terminal.Gui/Configuration/ThemeScope.cs
+++ b/Terminal.Gui/Configuration/ThemeScope.cs
@@ -1,5 +1,7 @@
using System.Text.Json.Serialization;
+#pragma warning disable CS0618 // Obsolete - ThemeScope references Scope internally
+
namespace Terminal.Gui.Configuration;
// TODO: Change to internal to prevent app usage
@@ -43,6 +45,7 @@ namespace Terminal.Gui.Configuration;
///
///
[JsonConverter (typeof (ScopeJsonConverter))]
+[Obsolete ("Being replaced by Microsoft.Extensions.Configuration. Will be removed in a future version.")]
public class ThemeScope : Scope
{
}
diff --git a/Terminal.Gui/Configuration/TuiSerializerContext.cs b/Terminal.Gui/Configuration/TuiSerializerContext.cs
new file mode 100644
index 0000000000..83f4cb6b88
--- /dev/null
+++ b/Terminal.Gui/Configuration/TuiSerializerContext.cs
@@ -0,0 +1,48 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui.Configuration;
+
+///
+/// The configured instance used by Terminal.Gui's JSON
+/// converters and serializers. Wraps with the
+/// options required by Terminal.Gui (comment-handling, case-insensitivity, custom converters
+/// for and , and
+/// ).
+///
+///
+/// Internal: this is the non-obsolete home for what was historically
+/// ConfigurationManager.SerializerContext. Converters and consumers that previously
+/// referenced the obsolete static should reference here instead.
+///
+[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "Internal serializer context")]
+internal static class TuiSerializerContext
+{
+ ///
+ /// The shared instance, pre-configured for Terminal.Gui's
+ /// JSON conventions.
+ ///
+ internal static readonly SourceGenerationContext Instance = new (new JsonSerializerOptions
+ {
+ // Be relaxed
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ PropertyNameCaseInsensitive = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ WriteIndented = true,
+ AllowTrailingCommas = true,
+ Converters =
+ {
+ // Custom Rune converter so Glyphs can be specified flexibly.
+ new RuneJsonConverter (),
+
+ // Custom Key converter so "Ctrl+Q" parses as expected.
+ new KeyJsonConverter ()
+ },
+
+ // Enables Key to be "Ctrl+Q" vs "Ctrl\u002BQ"
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ TypeInfoResolver = SourceGenerationContext.Default
+ });
+}
diff --git a/Terminal.Gui/Drawing/Color/Color.cs b/Terminal.Gui/Drawing/Color/Color.cs
index 65c31143ac..abe63fcd05 100644
--- a/Terminal.Gui/Drawing/Color/Color.cs
+++ b/Terminal.Gui/Drawing/Color/Color.cs
@@ -7,6 +7,8 @@
using ColorHelper;
using ColorConverter = ColorHelper.ColorConverter;
+#pragma warning disable CS0618 // Obsolete - Color still uses ConfigurationPropertyAttribute during transition
+
namespace Terminal.Gui.Drawing;
///
diff --git a/Terminal.Gui/Drawing/Glyphs.cs b/Terminal.Gui/Drawing/Glyphs.cs
index 23a6b55976..d39e7fde09 100644
--- a/Terminal.Gui/Drawing/Glyphs.cs
+++ b/Terminal.Gui/Drawing/Glyphs.cs
@@ -1,4 +1,4 @@
-
+
namespace Terminal.Gui.Drawing;
/// Defines the standard set of glyphs used to draw checkboxes, lines, borders, etc...
@@ -13,7 +13,7 @@ namespace Terminal.Gui.Drawing;
///
///
/// The Json property can be one of:
-/// - unicode glyph in a string (e.g. "☑")
+/// - unicode glyph in a string (e.g. "☑")
/// - U+hex format in a string (e.g. "U+2611")
/// - \u format in a string (e.g. "\\u2611")
/// - A decimal number (e.g. 97 for "a")
@@ -29,515 +29,1679 @@ public class Glyphs
/// Unicode replacement character; used by Drivers when rendering in cases where a wide glyph can't
/// be output because it would be clipped. Defaults to ' ' (Space).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune WideGlyphReplacement { get; set; } = (Rune)' ';
+ public static Rune WideGlyphReplacement
+ {
+ get => GlyphSettings.Defaults.WideGlyphReplacement;
+ set => GlyphSettings.Defaults.WideGlyphReplacement = value;
+ }
- /// File icon. Defaults to ☰ (Trigram For Heaven)
+ /// File icon. Defaults to ☰ (Trigram For Heaven)
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune File { get; set; } = (Rune)'☰';
+ public static Rune File
+ {
+ get => GlyphSettings.Defaults.File;
+ set => GlyphSettings.Defaults.File = value;
+ }
- /// Folder icon. Defaults to ꤉ (Kayah Li Digit Nine)
+ /// Folder icon. Defaults to ꤉ (Kayah Li Digit Nine)
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Folder { get; set; } = (Rune)'꤉';
+ public static Rune Folder
+ {
+ get => GlyphSettings.Defaults.Folder;
+ set => GlyphSettings.Defaults.Folder = value;
+ }
- /// Horizontal Ellipsis - … U+2026
+ /// Horizontal Ellipsis - … U+2026
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune HorizontalEllipsis { get; set; } = (Rune)'…';
+ public static Rune HorizontalEllipsis
+ {
+ get => GlyphSettings.Defaults.HorizontalEllipsis;
+ set => GlyphSettings.Defaults.HorizontalEllipsis = value;
+ }
- /// Vertical Four Dots - ⁞ U+205e
+ /// Vertical Four Dots - âž U+205e
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune VerticalFourDots { get; set; } = (Rune)'⁞';
+ public static Rune VerticalFourDots
+ {
+ get => GlyphSettings.Defaults.VerticalFourDots;
+ set => GlyphSettings.Defaults.VerticalFourDots = value;
+ }
#region ----------------- Single Glyphs -----------------
- /// Null symbol ('␀')
+ /// Null symbol ('â€')
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Null { get; set; } = (Rune)'␀';
+ public static Rune Null
+ {
+ get => GlyphSettings.Defaults.Null;
+ set => GlyphSettings.Defaults.Null = value;
+ }
/// Checked indicator (e.g. for and ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune CheckStateChecked { get; set; } = (Rune)'☑';
+ public static Rune CheckStateChecked
+ {
+ get => GlyphSettings.Defaults.CheckStateChecked;
+ set => GlyphSettings.Defaults.CheckStateChecked = value;
+ }
/// Not Checked indicator (e.g. for and ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune CheckStateUnChecked { get; set; } = (Rune)'☐';
+ public static Rune CheckStateUnChecked
+ {
+ get => GlyphSettings.Defaults.CheckStateUnChecked;
+ set => GlyphSettings.Defaults.CheckStateUnChecked = value;
+ }
/// Null Checked indicator (e.g. for and ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune CheckStateNone { get; set; } = (Rune)'⬛';
+ public static Rune CheckStateNone
+ {
+ get => GlyphSettings.Defaults.CheckStateNone;
+ set => GlyphSettings.Defaults.CheckStateNone = value;
+ }
/// Selected indicator (e.g. for and ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Selected { get; set; } = (Rune)'◉';
+ public static Rune Selected
+ {
+ get => GlyphSettings.Defaults.Selected;
+ set => GlyphSettings.Defaults.Selected = value;
+ }
/// Not Selected indicator (e.g. for and ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune UnSelected { get; set; } = (Rune)'○';
+ public static Rune UnSelected
+ {
+ get => GlyphSettings.Defaults.UnSelected;
+ set => GlyphSettings.Defaults.UnSelected = value;
+ }
/// Horizontal arrow.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune RightArrow { get; set; } = (Rune)'►';
+ public static Rune RightArrow
+ {
+ get => GlyphSettings.Defaults.RightArrow;
+ set => GlyphSettings.Defaults.RightArrow = value;
+ }
/// Left arrow.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune LeftArrow { get; set; } = (Rune)'◄';
+ public static Rune LeftArrow
+ {
+ get => GlyphSettings.Defaults.LeftArrow;
+ set => GlyphSettings.Defaults.LeftArrow = value;
+ }
/// Down arrow.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune DownArrow { get; set; } = (Rune)'▼';
+ public static Rune DownArrow
+ {
+ get => GlyphSettings.Defaults.DownArrow;
+ set => GlyphSettings.Defaults.DownArrow = value;
+ }
/// Vertical arrow.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune UpArrow { get; set; } = (Rune)'▲';
+ public static Rune UpArrow
+ {
+ get => GlyphSettings.Defaults.UpArrow;
+ set => GlyphSettings.Defaults.UpArrow = value;
+ }
/// Left default indicator (e.g. for .
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune LeftDefaultIndicator { get; set; } = (Rune)'►';
+ public static Rune LeftDefaultIndicator
+ {
+ get => GlyphSettings.Defaults.LeftDefaultIndicator;
+ set => GlyphSettings.Defaults.LeftDefaultIndicator = value;
+ }
/// Horizontal default indicator (e.g. for .
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune RightDefaultIndicator { get; set; } = (Rune)'◄';
+ public static Rune RightDefaultIndicator
+ {
+ get => GlyphSettings.Defaults.RightDefaultIndicator;
+ set => GlyphSettings.Defaults.RightDefaultIndicator = value;
+ }
/// Left Bracket (e.g. for . Default is (U+005B) - [.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune LeftBracket { get; set; } = (Rune)'⟦';
+ public static Rune LeftBracket
+ {
+ get => GlyphSettings.Defaults.LeftBracket;
+ set => GlyphSettings.Defaults.LeftBracket = value;
+ }
/// Horizontal Bracket (e.g. for . Default is (U+005D) - ].
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune RightBracket { get; set; } = (Rune)'⟧';
+ public static Rune RightBracket
+ {
+ get => GlyphSettings.Defaults.RightBracket;
+ set => GlyphSettings.Defaults.RightBracket = value;
+ }
/// Half block meter segment (e.g. for ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune BlocksMeterSegment { get; set; } = (Rune)'▌';
+ public static Rune BlocksMeterSegment
+ {
+ get => GlyphSettings.Defaults.BlocksMeterSegment;
+ set => GlyphSettings.Defaults.BlocksMeterSegment = value;
+ }
/// Continuous block meter segment (e.g. for ).
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune ContinuousMeterSegment { get; set; } = (Rune)'█';
+ public static Rune ContinuousMeterSegment
+ {
+ get => GlyphSettings.Defaults.ContinuousMeterSegment;
+ set => GlyphSettings.Defaults.ContinuousMeterSegment = value;
+ }
- /// Stipple pattern (e.g. for ). Default is Light Shade (U+2591) - ░.
+ /// Stipple pattern (e.g. for ). Default is Light Shade (U+2591) - â–‘.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Stipple { get; set; } = (Rune)'░';
+ public static Rune Stipple
+ {
+ get => GlyphSettings.Defaults.Stipple;
+ set => GlyphSettings.Defaults.Stipple = value;
+ }
- /// Diamond. Default is Lozenge (U+25CA) - ◊.
+ /// Diamond. Default is Lozenge (U+25CA) - â—Š.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Diamond { get; set; } = (Rune)'◊';
+ public static Rune Diamond
+ {
+ get => GlyphSettings.Defaults.Diamond;
+ set => GlyphSettings.Defaults.Diamond = value;
+ }
- /// Close. Default is Heavy Ballot X (U+2718) - ✘.
+ /// Close. Default is Heavy Ballot X (U+2718) - ✘.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Close { get; set; } = (Rune)'✘';
+ public static Rune Close
+ {
+ get => GlyphSettings.Defaults.Close;
+ set => GlyphSettings.Defaults.Close = value;
+ }
- /// Minimize. Default is Lower Horizontal Shadowed White Circle (U+274F) - ❏.
+ /// Minimize. Default is Lower Horizontal Shadowed White Circle (U+274F) - â.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Minimize { get; set; } = (Rune)'❏';
+ public static Rune Minimize
+ {
+ get => GlyphSettings.Defaults.Minimize;
+ set => GlyphSettings.Defaults.Minimize = value;
+ }
- /// Maximize. Default is Upper Horizontal Shadowed White Circle (U+273D) - ✽.
+ /// Maximize. Default is Upper Horizontal Shadowed White Circle (U+273D) - ✽.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Maximize { get; set; } = (Rune)'✽';
+ public static Rune Maximize
+ {
+ get => GlyphSettings.Defaults.Maximize;
+ set => GlyphSettings.Defaults.Maximize = value;
+ }
- /// Dot. Default is (U+2219) - ∙.
+ /// Dot. Default is (U+2219) - ∙.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Dot { get; set; } = (Rune)'∙';
+ public static Rune Dot
+ {
+ get => GlyphSettings.Defaults.Dot;
+ set => GlyphSettings.Defaults.Dot = value;
+ }
- /// Dotted Square - ⬚ U+02b1a┝
+ /// Dotted Square - ⬚ U+02b1aâ”
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune DottedSquare { get; set; } = (Rune)'⬚';
+ public static Rune DottedSquare
+ {
+ get => GlyphSettings.Defaults.DottedSquare;
+ set => GlyphSettings.Defaults.DottedSquare = value;
+ }
- /// Black Circle . Default is (U+025cf) - ●.
+ /// Black Circle . Default is (U+025cf) - â—.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune BlackCircle { get; set; } = (Rune)'●'; // Black Circle - ● U+025cf
+ public static Rune BlackCircle // Black Circle - â— U+025cf
+ {
+ get => GlyphSettings.Defaults.BlackCircle;
+ set => GlyphSettings.Defaults.BlackCircle = value;
+ }
/// Expand (e.g. for .
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Expand { get; set; } = (Rune)'+';
+ public static Rune Expand
+ {
+ get => GlyphSettings.Defaults.Expand;
+ set => GlyphSettings.Defaults.Expand = value;
+ }
/// Expand (e.g. for .
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Collapse { get; set; } = (Rune)'-';
+ public static Rune Collapse
+ {
+ get => GlyphSettings.Defaults.Collapse;
+ set => GlyphSettings.Defaults.Collapse = value;
+ }
/// Identical To (U+226)
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune IdenticalTo { get; set; } = (Rune)'≡';
+ public static Rune IdenticalTo
+ {
+ get => GlyphSettings.Defaults.IdenticalTo;
+ set => GlyphSettings.Defaults.IdenticalTo = value;
+ }
- /// Move indicator. Default is Lozenge (U+25CA) - ◊.
+ /// Move indicator. Default is Lozenge (U+25CA) - â—Š.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Move { get; set; } = (Rune)'◊';
+ public static Rune Move
+ {
+ get => GlyphSettings.Defaults.Move;
+ set => GlyphSettings.Defaults.Move = value;
+ }
- /// Size Horizontally indicator. Default is ┥Left Right Arrow - ↔ U+02194
+ /// Size Horizontally indicator. Default is ┥Left Right Arrow - ↔ U+02194
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeHorizontal { get; set; } = (Rune)'↔';
+ public static Rune SizeHorizontal
+ {
+ get => GlyphSettings.Defaults.SizeHorizontal;
+ set => GlyphSettings.Defaults.SizeHorizontal = value;
+ }
- /// Size Vertical indicator. Default Up Down Arrow - ↕ U+02195
+ /// Size Vertical indicator. Default Up Down Arrow - ↕ U+02195
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeVertical { get; set; } = (Rune)'↕';
+ public static Rune SizeVertical
+ {
+ get => GlyphSettings.Defaults.SizeVertical;
+ set => GlyphSettings.Defaults.SizeVertical = value;
+ }
- /// Size Top Left indicator. North West Arrow - ↖ U+02196
+ /// Size Top Left indicator. North West Arrow - ↖ U+02196
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeTopLeft { get; set; } = (Rune)'↖';
+ public static Rune SizeTopLeft
+ {
+ get => GlyphSettings.Defaults.SizeTopLeft;
+ set => GlyphSettings.Defaults.SizeTopLeft = value;
+ }
- /// Size Top Right indicator. North East Arrow - ↗ U+02197
+ /// Size Top Right indicator. North East Arrow - ↗ U+02197
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeTopRight { get; set; } = (Rune)'↗';
+ public static Rune SizeTopRight
+ {
+ get => GlyphSettings.Defaults.SizeTopRight;
+ set => GlyphSettings.Defaults.SizeTopRight = value;
+ }
- /// Size Bottom Right indicator. South East Arrow - ↘ U+02198
+ /// Size Bottom Right indicator. South East Arrow - ↘ U+02198
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeBottomRight { get; set; } = (Rune)'↘';
+ public static Rune SizeBottomRight
+ {
+ get => GlyphSettings.Defaults.SizeBottomRight;
+ set => GlyphSettings.Defaults.SizeBottomRight = value;
+ }
- /// Size Bottom Left indicator. South West Arrow - ↙ U+02199
+ /// Size Bottom Left indicator. South West Arrow - ↙ U+02199
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune SizeBottomLeft { get; set; } = (Rune)'↙';
+ public static Rune SizeBottomLeft
+ {
+ get => GlyphSettings.Defaults.SizeBottomLeft;
+ set => GlyphSettings.Defaults.SizeBottomLeft = value;
+ }
/// Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Apple { get; set; } = "🍎".ToRunes () [0]; // nonBMP
+ public static Rune Apple // nonBMP
+ {
+ get => GlyphSettings.Defaults.Apple;
+ set => GlyphSettings.Defaults.Apple = value;
+ }
/// Apple (BMP). Because snek. See Issue #2610.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune AppleBMP { get; set; } = (Rune)'❦';
+ public static Rune AppleBMP
+ {
+ get => GlyphSettings.Defaults.AppleBMP;
+ set => GlyphSettings.Defaults.AppleBMP = value;
+ }
- /// Copy indicator. Two Joined Squares - ⧉ U+29C9. Used for code block copy buttons.
+ /// Copy indicator. Two Joined Squares - ⧉ U+29C9. Used for code block copy buttons.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune Copy { get; set; } = (Rune)'⧉';
+ public static Rune Copy
+ {
+ get => GlyphSettings.Defaults.Copy;
+ set => GlyphSettings.Defaults.Copy = value;
+ }
#endregion
#region ----------------- Lines -----------------
- /// Box Drawings Horizontal Line - Light (U+2500) - ─
+ /// Box Drawings Horizontal Line - Light (U+2500) - ─
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLine
+ {
+ get => GlyphSettings.Defaults.HLine;
+ set => GlyphSettings.Defaults.HLine = value;
+ }
+
+ /// Box Drawings Vertical Line - Light (U+2502) - │
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLine
+ {
+ get => GlyphSettings.Defaults.VLine;
+ set => GlyphSettings.Defaults.VLine = value;
+ }
+
+ /// Box Drawings Double Horizontal (U+2550) - â•
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineDbl
+ {
+ get => GlyphSettings.Defaults.HLineDbl;
+ set => GlyphSettings.Defaults.HLineDbl = value;
+ }
+
+ /// Box Drawings Double Vertical (U+2551) - â•‘
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineDbl
+
+ {
+
+ get => GlyphSettings.Defaults.VLineDbl;
+
+ set => GlyphSettings.Defaults.VLineDbl = value;
+
+ }
+
+ /// Box Drawings Heavy Double Dash Horizontal (U+254D) - â•
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineHvDa2
+
+ {
+
+ get => GlyphSettings.Defaults.HLineHvDa2;
+
+ set => GlyphSettings.Defaults.HLineHvDa2 = value;
+
+ }
+
+ /// Box Drawings Heavy Triple Dash Vertical (U+2507) - ┇
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineHvDa3
+ {
+ get => GlyphSettings.Defaults.VLineHvDa3;
+ set => GlyphSettings.Defaults.VLineHvDa3 = value;
+ }
+
+ /// Box Drawings Heavy Triple Dash Horizontal (U+2505) - â”…
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineHvDa3
+
+ {
+
+ get => GlyphSettings.Defaults.HLineHvDa3;
+
+ set => GlyphSettings.Defaults.HLineHvDa3 = value;
+
+ }
+
+ /// Box Drawings Heavy Quadruple Dash Horizontal (U+2509) - ┉
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineHvDa4
+
+ {
+
+ get => GlyphSettings.Defaults.HLineHvDa4;
+
+ set => GlyphSettings.Defaults.HLineHvDa4 = value;
+
+ }
+
+ /// Box Drawings Heavy Double Dash Vertical (U+254F) - â•
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineHvDa2
+
+ {
+
+ get => GlyphSettings.Defaults.VLineHvDa2;
+
+ set => GlyphSettings.Defaults.VLineHvDa2 = value;
+
+ }
+
+ /// Box Drawings Heavy Quadruple Dash Vertical (U+250B) - ┋
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineHvDa4
+
+ {
+
+ get => GlyphSettings.Defaults.VLineHvDa4;
+
+ set => GlyphSettings.Defaults.VLineHvDa4 = value;
+
+ }
+
+ /// Box Drawings Light Double Dash Horizontal (U+254C) - ╌
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineDa2
+
+ {
+
+ get => GlyphSettings.Defaults.HLineDa2;
+
+ set => GlyphSettings.Defaults.HLineDa2 = value;
+
+ }
+
+ /// Box Drawings Light Triple Dash Vertical (U+2506) - ┆
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineDa3
+
+ {
+
+ get => GlyphSettings.Defaults.VLineDa3;
+
+ set => GlyphSettings.Defaults.VLineDa3 = value;
+
+ }
+
+ /// Box Drawings Light Triple Dash Horizontal (U+2504) - ┄
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineDa3
+
+ {
+
+ get => GlyphSettings.Defaults.HLineDa3;
+
+ set => GlyphSettings.Defaults.HLineDa3 = value;
+
+ }
+
+ /// Box Drawings Light Quadruple Dash Horizontal (U+2508) - ┈
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineDa4
+
+ {
+
+ get => GlyphSettings.Defaults.HLineDa4;
+
+ set => GlyphSettings.Defaults.HLineDa4 = value;
+
+ }
+
+ /// Box Drawings Light Double Dash Vertical (U+254E) - ╎
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineDa2
+
+ {
+
+ get => GlyphSettings.Defaults.VLineDa2;
+
+ set => GlyphSettings.Defaults.VLineDa2 = value;
+
+ }
+
+ /// Box Drawings Light Quadruple Dash Vertical (U+250A) - ┊
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineDa4
+
+ {
+
+ get => GlyphSettings.Defaults.VLineDa4;
+
+ set => GlyphSettings.Defaults.VLineDa4 = value;
+
+ }
+
+ /// Box Drawings Heavy Horizontal (U+2501) - â”
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HLineHv
+
+ {
+
+ get => GlyphSettings.Defaults.HLineHv;
+
+ set => GlyphSettings.Defaults.HLineHv = value;
+
+ }
+
+ /// Box Drawings Heavy Vertical (U+2503) - ┃
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune VLineHv
+
+ {
+
+ get => GlyphSettings.Defaults.VLineHv;
+
+ set => GlyphSettings.Defaults.VLineHv = value;
+
+ }
+
+ /// Box Drawings Light Left (U+2574) - â•´
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HalfLeftLine
+
+ {
+
+ get => GlyphSettings.Defaults.HalfLeftLine;
+
+ set => GlyphSettings.Defaults.HalfLeftLine = value;
+
+ }
+
+ /// Box Drawings Light Vertical (U+2575) - ╵
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HalfTopLine
+
+ {
+
+ get => GlyphSettings.Defaults.HalfTopLine;
+
+ set => GlyphSettings.Defaults.HalfTopLine = value;
+
+ }
+
+ /// Box Drawings Light Horizontal (U+2576) - â•¶
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HalfRightLine
+
+ {
+
+ get => GlyphSettings.Defaults.HalfRightLine;
+
+ set => GlyphSettings.Defaults.HalfRightLine = value;
+
+ }
+
+ /// Box Drawings Light Down (U+2577) - â•·
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune HalfBottomLine
+
+ {
+
+ get => GlyphSettings.Defaults.HalfBottomLine;
+
+ set => GlyphSettings.Defaults.HalfBottomLine = value;
+
+ }
+
+ /// Box Drawings Heavy Left (U+2578) - ╸
+
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune HLine { get; set; } = (Rune)'─';
+ public static Rune HalfLeftLineHv
+
+ {
+
+ get => GlyphSettings.Defaults.HalfLeftLineHv;
+
+ set => GlyphSettings.Defaults.HalfLeftLineHv = value;
+
+ }
+
+ /// Box Drawings Heavy Vertical (U+2579) - ╹
- /// Box Drawings Vertical Line - Light (U+2502) - │
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune VLine { get; set; } = (Rune)'│';
+ public static Rune HalfTopLineHv
+
+ {
+
+ get => GlyphSettings.Defaults.HalfTopLineHv;
+
+ set => GlyphSettings.Defaults.HalfTopLineHv = value;
+
+ }
+
+ /// Box Drawings Heavy Horizontal (U+257A) - ╺
- /// Box Drawings Double Horizontal (U+2550) - ═
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune HLineDbl { get; set; } = (Rune)'═';
+ public static Rune HalfRightLineHv
+
+ {
+
+ get => GlyphSettings.Defaults.HalfRightLineHv;
- /// Box Drawings Double Vertical (U+2551) - ║
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineDbl { get; set; } = (Rune)'║';
+ set => GlyphSettings.Defaults.HalfRightLineHv = value;
- /// Box Drawings Heavy Double Dash Horizontal (U+254D) - ╍
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineHvDa2 { get; set; } = (Rune)'╍';
+ }
+
+ /// Box Drawings Light Vertical and Horizontal (U+257B) - â•»
- /// Box Drawings Heavy Triple Dash Vertical (U+2507) - ┇
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Rune VLineHvDa3 { get; set; } = (Rune)'┇';
+ public static Rune HalfBottomLineLt
+
+ {
+
+ get => GlyphSettings.Defaults.HalfBottomLineLt;
+
+ set => GlyphSettings.Defaults.HalfBottomLineLt = value;
- /// Box Drawings Heavy Triple Dash Horizontal (U+2505) - ┅
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineHvDa3 { get; set; } = (Rune)'┅';
+ }
- /// Box Drawings Heavy Quadruple Dash Horizontal (U+2509) - ┉
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineHvDa4 { get; set; } = (Rune)'┉';
+ /// Box Drawings Light Horizontal and Heavy Horizontal (U+257C) - ╼
- /// Box Drawings Heavy Double Dash Vertical (U+254F) - ╏
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineHvDa2 { get; set; } = (Rune)'╏';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightSideLineLtHv
+
+ {
- /// Box Drawings Heavy Quadruple Dash Vertical (U+250B) - ┋
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineHvDa4 { get; set; } = (Rune)'┋';
+ get => GlyphSettings.Defaults.RightSideLineLtHv;
- /// Box Drawings Light Double Dash Horizontal (U+254C) - ╌
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineDa2 { get; set; } = (Rune)'╌';
+ set => GlyphSettings.Defaults.RightSideLineLtHv = value;
- /// Box Drawings Light Triple Dash Vertical (U+2506) - ┆
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineDa3 { get; set; } = (Rune)'┆';
+ }
- /// Box Drawings Light Triple Dash Horizontal (U+2504) - ┄
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineDa3 { get; set; } = (Rune)'┄';
+ /// Box Drawings Light Vertical and Heavy Horizontal (U+257D) - ╽
- /// Box Drawings Light Quadruple Dash Horizontal (U+2508) - ┈
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineDa4 { get; set; } = (Rune)'┈';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomSideLineLtHv
- /// Box Drawings Light Double Dash Vertical (U+254E) - ╎
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineDa2 { get; set; } = (Rune)'╎';
+ {
- /// Box Drawings Light Quadruple Dash Vertical (U+250A) - ┊
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineDa4 { get; set; } = (Rune)'┊';
+ get => GlyphSettings.Defaults.BottomSideLineLtHv;
- /// Box Drawings Heavy Horizontal (U+2501) - ━
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HLineHv { get; set; } = (Rune)'━';
+ set => GlyphSettings.Defaults.BottomSideLineLtHv = value;
- /// Box Drawings Heavy Vertical (U+2503) - ┃
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune VLineHv { get; set; } = (Rune)'┃';
+ }
- /// Box Drawings Light Left (U+2574) - ╴
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfLeftLine { get; set; } = (Rune)'╴';
+ /// Box Drawings Heavy Left and Light Horizontal (U+257E) - ╾
- /// Box Drawings Light Vertical (U+2575) - ╵
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfTopLine { get; set; } = (Rune)'╵';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftSideLineHvLt
- /// Box Drawings Light Horizontal (U+2576) - ╶
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfRightLine { get; set; } = (Rune)'╶';
+ {
- /// Box Drawings Light Down (U+2577) - ╷
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfBottomLine { get; set; } = (Rune)'╷';
+ get => GlyphSettings.Defaults.LeftSideLineHvLt;
- /// Box Drawings Heavy Left (U+2578) - ╸
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfLeftLineHv { get; set; } = (Rune)'╸';
+ set => GlyphSettings.Defaults.LeftSideLineHvLt = value;
- /// Box Drawings Heavy Vertical (U+2579) - ╹
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfTopLineHv { get; set; } = (Rune)'╹';
+ }
- /// Box Drawings Heavy Horizontal (U+257A) - ╺
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfRightLineHv { get; set; } = (Rune)'╺';
+ /// Box Drawings Heavy Vertical and Light Horizontal (U+257F) - â•¿
- /// Box Drawings Light Vertical and Horizontal (U+257B) - ╻
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune HalfBottomLineLt { get; set; } = (Rune)'╻';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopSideLineHvLt
- /// Box Drawings Light Horizontal and Heavy Horizontal (U+257C) - ╼
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightSideLineLtHv { get; set; } = (Rune)'╼';
+ {
- /// Box Drawings Light Vertical and Heavy Horizontal (U+257D) - ╽
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomSideLineLtHv { get; set; } = (Rune)'╽';
+ get => GlyphSettings.Defaults.TopSideLineHvLt;
- /// Box Drawings Heavy Left and Light Horizontal (U+257E) - ╾
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftSideLineHvLt { get; set; } = (Rune)'╾';
+ set => GlyphSettings.Defaults.TopSideLineHvLt = value;
- /// Box Drawings Heavy Vertical and Light Horizontal (U+257F) - ╿
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopSideLineHvLt { get; set; } = (Rune)'╿';
+ }
#endregion
#region ----------------- Upper Left Corners -----------------
- /// Box Drawings Upper Left Corner - Light Vertical and Light Horizontal (U+250C) - ┌
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCorner { get; set; } = (Rune)'┌';
+ /// Box Drawings Upper Left Corner - Light Vertical and Light Horizontal (U+250C) - ┌
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCorner
+
+ {
+
+ get => GlyphSettings.Defaults.ULCorner;
+
+ set => GlyphSettings.Defaults.ULCorner = value;
+
+ }
+
+ /// Box Drawings Upper Left Corner - Double (U+2554) - â•”
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerDbl
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerDbl;
+
+ set => GlyphSettings.Defaults.ULCornerDbl = value;
+
+ }
+
+ /// Box Drawings Upper Left Corner - Light Arc Down and Horizontal (U+256D) - â•
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerR
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerR;
+
+ set => GlyphSettings.Defaults.ULCornerR = value;
+
+ }
+
+ /// Box Drawings Heavy Down and Horizontal (U+250F) - â”
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerHv
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerHv;
+
+ set => GlyphSettings.Defaults.ULCornerHv = value;
+
+ }
+
+ /// Box Drawings Down Heavy and Horizontal Light (U+251E) - ┎
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerHvLt
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerHvLt;
+
+ set => GlyphSettings.Defaults.ULCornerHvLt = value;
+
+ }
+
+ /// Box Drawings Down Light and Horizontal Heavy (U+250D) - ┎
- /// Box Drawings Upper Left Corner - Double (U+2554) - ╔
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerDbl { get; set; } = (Rune)'╔';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerLtHv
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerLtHv;
+
+ set => GlyphSettings.Defaults.ULCornerLtHv = value;
+
+ }
+
+ /// Box Drawings Double Down and Single Horizontal (U+2553) - â•“
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerDblSingle
- /// Box Drawings Upper Left Corner - Light Arc Down and Horizontal (U+256D) - ╭
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerR { get; set; } = (Rune)'╭';
+ {
- /// Box Drawings Heavy Down and Horizontal (U+250F) - ┏
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerHv { get; set; } = (Rune)'┏';
+ get => GlyphSettings.Defaults.ULCornerDblSingle;
- /// Box Drawings Down Heavy and Horizontal Light (U+251E) - ┎
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerHvLt { get; set; } = (Rune)'┎';
+ set => GlyphSettings.Defaults.ULCornerDblSingle = value;
- /// Box Drawings Down Light and Horizontal Heavy (U+250D) - ┎
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerLtHv { get; set; } = (Rune)'┍';
+ }
- /// Box Drawings Double Down and Single Horizontal (U+2553) - ╓
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerDblSingle { get; set; } = (Rune)'╓';
+ /// Box Drawings Single Down and Double Horizontal (U+2552) - â•’
- /// Box Drawings Single Down and Double Horizontal (U+2552) - ╒
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ULCornerSingleDbl { get; set; } = (Rune)'╒';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ULCornerSingleDbl
+
+ {
+
+ get => GlyphSettings.Defaults.ULCornerSingleDbl;
+
+ set => GlyphSettings.Defaults.ULCornerSingleDbl = value;
+
+ }
#endregion
#region ----------------- Lower Left Corners -----------------
- /// Box Drawings Lower Left Corner - Light Vertical and Light Horizontal (U+2514) - └
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCorner { get; set; } = (Rune)'└';
+ /// Box Drawings Lower Left Corner - Light Vertical and Light Horizontal (U+2514) - â””
- /// Box Drawings Heavy Vertical and Horizontal (U+2517) - ┗
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerHv { get; set; } = (Rune)'┗';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCorner
- /// Box Drawings Heavy Vertical and Horizontal Light (U+2516) - ┖
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerHvLt { get; set; } = (Rune)'┖';
+ {
- /// Box Drawings Vertical Light and Horizontal Heavy (U+2511) - ┕
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerLtHv { get; set; } = (Rune)'┕';
+ get => GlyphSettings.Defaults.LLCorner;
- /// Box Drawings Double Vertical and Double Left (U+255A) - ╚
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerDbl { get; set; } = (Rune)'╚';
+ set => GlyphSettings.Defaults.LLCorner = value;
- /// Box Drawings Single Vertical and Double Left (U+2558) - ╘
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerSingleDbl { get; set; } = (Rune)'╘';
+ }
- /// Box Drawings Double Down and Single Left (U+2559) - ╙
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerDblSingle { get; set; } = (Rune)'╙';
+ /// Box Drawings Heavy Vertical and Horizontal (U+2517) - â”—
- /// Box Drawings Upper Left Corner - Light Arc Down and Left (U+2570) - ╰
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LLCornerR { get; set; } = (Rune)'╰';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerHv
- #endregion
+ {
- #region ----------------- Upper Right Corners -----------------
+ get => GlyphSettings.Defaults.LLCornerHv;
- /// Box Drawings Upper Horizontal Corner - Light Vertical and Light Horizontal (U+2510) - ┐
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCorner { get; set; } = (Rune)'┐';
+ set => GlyphSettings.Defaults.LLCornerHv = value;
- /// Box Drawings Upper Horizontal Corner - Double Vertical and Double Horizontal (U+2557) - ╗
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerDbl { get; set; } = (Rune)'╗';
+ }
- /// Box Drawings Upper Horizontal Corner - Light Arc Vertical and Horizontal (U+256E) - ╮
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerR { get; set; } = (Rune)'╮';
+ /// Box Drawings Heavy Vertical and Horizontal Light (U+2516) - â”–
- /// Box Drawings Heavy Down and Left (U+2513) - ┓
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerHv { get; set; } = (Rune)'┓';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerHvLt
- /// Box Drawings Heavy Vertical and Left Down Light (U+2511) - ┑
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerHvLt { get; set; } = (Rune)'┑';
+ {
- /// Box Drawings Down Light and Horizontal Heavy (U+2514) - ┒
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerLtHv { get; set; } = (Rune)'┒';
+ get => GlyphSettings.Defaults.LLCornerHvLt;
- /// Box Drawings Double Vertical and Single Left (U+2556) - ╖
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerDblSingle { get; set; } = (Rune)'╖';
+ set => GlyphSettings.Defaults.LLCornerHvLt = value;
- /// Box Drawings Single Vertical and Double Left (U+2555) - ╕
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune URCornerSingleDbl { get; set; } = (Rune)'╕';
+ }
- #endregion
+ /// Box Drawings Vertical Light and Horizontal Heavy (U+2511) - ┕
- #region ----------------- Lower Right Corners -----------------
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerLtHv
+
+ {
+
+ get => GlyphSettings.Defaults.LLCornerLtHv;
+
+ set => GlyphSettings.Defaults.LLCornerLtHv = value;
+
+ }
+
+ /// Box Drawings Double Vertical and Double Left (U+255A) - ╚
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerDbl
+
+ {
+
+ get => GlyphSettings.Defaults.LLCornerDbl;
+
+ set => GlyphSettings.Defaults.LLCornerDbl = value;
+
+ }
- /// Box Drawings Lower Right Corner - Light (U+2518) - ┘
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCorner { get; set; } = (Rune)'┘';
+ /// Box Drawings Single Vertical and Double Left (U+2558) - ╘
- /// Box Drawings Lower Right Corner - Double (U+255D) - ╝
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerDbl { get; set; } = (Rune)'╝';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerSingleDbl
+
+ {
+
+ get => GlyphSettings.Defaults.LLCornerSingleDbl;
+
+ set => GlyphSettings.Defaults.LLCornerSingleDbl = value;
+
+ }
+
+ /// Box Drawings Double Down and Single Left (U+2559) - â•™
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerDblSingle
- /// Box Drawings Lower Right Corner - Rounded (U+256F) - ╯
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerR { get; set; } = (Rune)'╯';
+ {
- /// Box Drawings Lower Right Corner - Heavy (U+251B) - ┛
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerHv { get; set; } = (Rune)'┛';
+ get => GlyphSettings.Defaults.LLCornerDblSingle;
- /// Box Drawings Lower Right Corner - Double Vertical and Single Horizontal (U+255C) - ╜
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerDblSingle { get; set; } = (Rune)'╜';
+ set => GlyphSettings.Defaults.LLCornerDblSingle = value;
- /// Box Drawings Lower Right Corner - Single Vertical and Double Horizontal (U+255B) - ╛
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerSingleDbl { get; set; } = (Rune)'╛';
+ }
- /// Box Drawings Lower Right Corner - Light Vertical and Heavy Horizontal (U+2519) - ┙
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerLtHv { get; set; } = (Rune)'┙';
+ /// Box Drawings Upper Left Corner - Light Arc Down and Left (U+2570) - â•°
- /// Box Drawings Lower Right Corner - Heavy Vertical and Light Horizontal (U+251A) - ┚
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LRCornerHvLt { get; set; } = (Rune)'┚';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LLCornerR
+
+ {
+
+ get => GlyphSettings.Defaults.LLCornerR;
+
+ set => GlyphSettings.Defaults.LLCornerR = value;
+
+ }
#endregion
- #region ----------------- Tees -----------------
+ #region ----------------- Upper Right Corners -----------------
+
+ /// Box Drawings Upper Horizontal Corner - Light Vertical and Light Horizontal (U+2510) - â”
- /// Box Drawings Left Tee - Single Vertical and Single Horizontal (U+251C) - ├
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTee { get; set; } = (Rune)'├';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCorner
+
+ {
- /// Box Drawings Left Tee - Single Vertical and Double Horizontal (U+255E) - ╞
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeDblH { get; set; } = (Rune)'╞';
+ get => GlyphSettings.Defaults.URCorner;
- /// Box Drawings Left Tee - Double Vertical and Single Horizontal (U+255F) - ╟
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeDblV { get; set; } = (Rune)'╟';
+ set => GlyphSettings.Defaults.URCorner = value;
- /// Box Drawings Left Tee - Double Vertical and Double Horizontal (U+2560) - ╠
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeDbl { get; set; } = (Rune)'╠';
+ }
- /// Box Drawings Left Tee - Heavy Horizontal and Light Vertical (U+2523) - ┝
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeHvH { get; set; } = (Rune)'┝';
+ /// Box Drawings Upper Horizontal Corner - Double Vertical and Double Horizontal (U+2557) - â•—
- /// Box Drawings Left Tee - Light Horizontal and Heavy Vertical (U+252B) - ┠
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeHvV { get; set; } = (Rune)'┠';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerDbl
- /// Box Drawings Left Tee - Heavy Vertical and Heavy Horizontal (U+2527) - ┣
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune LeftTeeHvDblH { get; set; } = (Rune)'┣';
+ {
- /// Box Drawings Right Tee - Single Vertical and Single Horizontal (U+2524) - ┤
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTee { get; set; } = (Rune)'┤';
+ get => GlyphSettings.Defaults.URCornerDbl;
- /// Box Drawings Right Tee - Single Vertical and Double Horizontal (U+2561) - ╡
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeDblH { get; set; } = (Rune)'╡';
+ set => GlyphSettings.Defaults.URCornerDbl = value;
- /// Box Drawings Right Tee - Double Vertical and Single Horizontal (U+2562) - ╢
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeDblV { get; set; } = (Rune)'╢';
+ }
- /// Box Drawings Right Tee - Double Vertical and Double Horizontal (U+2563) - ╣
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeDbl { get; set; } = (Rune)'╣';
+ /// Box Drawings Upper Horizontal Corner - Light Arc Vertical and Horizontal (U+256E) - â•®
- /// Box Drawings Right Tee - Heavy Horizontal and Light Vertical (U+2528) - ┥
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeHvH { get; set; } = (Rune)'┥';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerR
- /// Box Drawings Right Tee - Light Horizontal and Heavy Vertical (U+2530) - ┨
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeHvV { get; set; } = (Rune)'┨';
+ {
- /// Box Drawings Right Tee - Heavy Vertical and Heavy Horizontal (U+252C) - ┫
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune RightTeeHvDblH { get; set; } = (Rune)'┫';
+ get => GlyphSettings.Defaults.URCornerR;
- /// Box Drawings Top Tee - Single Vertical and Single Horizontal (U+252C) - ┬
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTee { get; set; } = (Rune)'┬';
+ set => GlyphSettings.Defaults.URCornerR = value;
- /// Box Drawings Top Tee - Single Vertical and Double Horizontal (U+2564) - ╤
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeDblH { get; set; } = (Rune)'╤';
+ }
- /// Box Drawings Top Tee - Double Vertical and Single Horizontal (U+2565) - ╥
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeDblV { get; set; } = (Rune)'╥';
+ /// Box Drawings Heavy Down and Left (U+2513) - ┓
- /// Box Drawings Top Tee - Double Vertical and Double Horizontal (U+2566) - ╦
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeDbl { get; set; } = (Rune)'╦';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerHv
- /// Box Drawings Top Tee - Heavy Horizontal and Light Vertical (U+252F) - ┯
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeHvH { get; set; } = (Rune)'┯';
+ {
- /// Box Drawings Top Tee - Light Horizontal and Heavy Vertical (U+2537) - ┰
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeHvV { get; set; } = (Rune)'┰';
+ get => GlyphSettings.Defaults.URCornerHv;
- /// Box Drawings Top Tee - Heavy Vertical and Heavy Horizontal (U+2533) - ┳
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune TopTeeHvDblH { get; set; } = (Rune)'┳';
+ set => GlyphSettings.Defaults.URCornerHv = value;
- /// Box Drawings Bottom Tee - Single Vertical and Single Horizontal (U+2534) - ┴
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTee { get; set; } = (Rune)'┴';
+ }
- /// Box Drawings Bottom Tee - Single Vertical and Double Horizontal (U+2567) - ╧
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeDblH { get; set; } = (Rune)'╧';
+ /// Box Drawings Heavy Vertical and Left Down Light (U+2511) - ┑
- /// Box Drawings Bottom Tee - Double Vertical and Single Horizontal (U+2568) - ╨
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeDblV { get; set; } = (Rune)'╨';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerHvLt
- /// Box Drawings Bottom Tee - Double Vertical and Double Horizontal (U+2569) - ╩
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeDbl { get; set; } = (Rune)'╩';
+ {
- /// Box Drawings Bottom Tee - Heavy Horizontal and Light Vertical (U+2535) - ┷
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeHvH { get; set; } = (Rune)'┷';
+ get => GlyphSettings.Defaults.URCornerHvLt;
- /// Box Drawings Bottom Tee - Light Horizontal and Heavy Vertical (U+253D) - ┸
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeHvV { get; set; } = (Rune)'┸';
+ set => GlyphSettings.Defaults.URCornerHvLt = value;
- /// Box Drawings Bottom Tee - Heavy Vertical and Heavy Horizontal (U+2539) - ┻
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune BottomTeeHvDblH { get; set; } = (Rune)'┻';
+ }
- #endregion
+ /// Box Drawings Down Light and Horizontal Heavy (U+2514) - â”’
- #region ----------------- Crosses -----------------
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerLtHv
- /// Box Drawings Cross - Single Vertical and Single Horizontal (U+253C) - ┼
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune Cross { get; set; } = (Rune)'┼';
+ {
- /// Box Drawings Cross - Single Vertical and Double Horizontal (U+256A) - ╪
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossDblH { get; set; } = (Rune)'╪';
+ get => GlyphSettings.Defaults.URCornerLtHv;
- /// Box Drawings Cross - Double Vertical and Single Horizontal (U+256B) - ╫
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossDblV { get; set; } = (Rune)'╫';
+ set => GlyphSettings.Defaults.URCornerLtHv = value;
- /// Box Drawings Cross - Double Vertical and Double Horizontal (U+256C) - ╬
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossDbl { get; set; } = (Rune)'╬';
+ }
- /// Box Drawings Cross - Heavy Horizontal and Light Vertical (U+253F) - ┿
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossHvH { get; set; } = (Rune)'┿';
+ /// Box Drawings Double Vertical and Single Left (U+2556) - â•–
- /// Box Drawings Cross - Light Horizontal and Heavy Vertical (U+2541) - ╂
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossHvV { get; set; } = (Rune)'╂';
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerDblSingle
- /// Box Drawings Cross - Heavy Vertical and Heavy Horizontal (U+254B) - ╋
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune CrossHv { get; set; } = (Rune)'╋';
+ {
- #endregion
+ get => GlyphSettings.Defaults.URCornerDblSingle;
- #region ----------------- ShadowStyle -----------------
+ set => GlyphSettings.Defaults.URCornerDblSingle = value;
+
+ }
+
+ /// Box Drawings Single Vertical and Double Left (U+2555) - â••
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune URCornerSingleDbl
- /// Shadow - Vertical Start - Left Half Block - ▌ U+0258c
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ShadowVerticalStart { get; set; } = (Rune)'▖'; // Half: '\u2596' ▖;
+ {
+
+ get => GlyphSettings.Defaults.URCornerSingleDbl;
+
+ set => GlyphSettings.Defaults.URCornerSingleDbl = value;
+
+ }
+
+ #endregion
+
+ #region ----------------- Lower Right Corners -----------------
+
+ /// Box Drawings Lower Right Corner - Light (U+2518) - ┘
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCorner
+
+ {
+
+ get => GlyphSettings.Defaults.LRCorner;
+
+ set => GlyphSettings.Defaults.LRCorner = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Double (U+255D) - â•
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerDbl
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerDbl;
+
+ set => GlyphSettings.Defaults.LRCornerDbl = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Rounded (U+256F) - ╯
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerR
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerR;
+
+ set => GlyphSettings.Defaults.LRCornerR = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Heavy (U+251B) - â”›
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerHv
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerHv;
+
+ set => GlyphSettings.Defaults.LRCornerHv = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Double Vertical and Single Horizontal (U+255C) - ╜
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerDblSingle
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerDblSingle;
+
+ set => GlyphSettings.Defaults.LRCornerDblSingle = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Single Vertical and Double Horizontal (U+255B) - â•›
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerSingleDbl
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerSingleDbl;
+
+ set => GlyphSettings.Defaults.LRCornerSingleDbl = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Light Vertical and Heavy Horizontal (U+2519) - â”™
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerLtHv
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerLtHv;
+
+ set => GlyphSettings.Defaults.LRCornerLtHv = value;
+
+ }
+
+ /// Box Drawings Lower Right Corner - Heavy Vertical and Light Horizontal (U+251A) - ┚
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LRCornerHvLt
+
+ {
+
+ get => GlyphSettings.Defaults.LRCornerHvLt;
+
+ set => GlyphSettings.Defaults.LRCornerHvLt = value;
+
+ }
+
+ #endregion
+
+ #region ----------------- Tees -----------------
+
+ /// Box Drawings Left Tee - Single Vertical and Single Horizontal (U+251C) - ├
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTee
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTee;
+
+ set => GlyphSettings.Defaults.LeftTee = value;
+
+ }
+
+ /// Box Drawings Left Tee - Single Vertical and Double Horizontal (U+255E) - ╞
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeDblH
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeDblH;
+
+ set => GlyphSettings.Defaults.LeftTeeDblH = value;
+
+ }
+
+ /// Box Drawings Left Tee - Double Vertical and Single Horizontal (U+255F) - ╟
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeDblV
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeDblV;
+
+ set => GlyphSettings.Defaults.LeftTeeDblV = value;
+
+ }
+
+ /// Box Drawings Left Tee - Double Vertical and Double Horizontal (U+2560) - â•
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeDbl
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeDbl;
+
+ set => GlyphSettings.Defaults.LeftTeeDbl = value;
+
+ }
+
+ /// Box Drawings Left Tee - Heavy Horizontal and Light Vertical (U+2523) - â”
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeHvH
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeHvH;
+
+ set => GlyphSettings.Defaults.LeftTeeHvH = value;
+
+ }
+
+ /// Box Drawings Left Tee - Light Horizontal and Heavy Vertical (U+252B) - â”
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeHvV
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeHvV;
+
+ set => GlyphSettings.Defaults.LeftTeeHvV = value;
+
+ }
+
+ /// Box Drawings Left Tee - Heavy Vertical and Heavy Horizontal (U+2527) - ┣
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune LeftTeeHvDblH
+
+ {
+
+ get => GlyphSettings.Defaults.LeftTeeHvDblH;
+
+ set => GlyphSettings.Defaults.LeftTeeHvDblH = value;
+
+ }
+
+ /// Box Drawings Right Tee - Single Vertical and Single Horizontal (U+2524) - ┤
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTee
+
+ {
+
+ get => GlyphSettings.Defaults.RightTee;
+
+ set => GlyphSettings.Defaults.RightTee = value;
+
+ }
+
+ /// Box Drawings Right Tee - Single Vertical and Double Horizontal (U+2561) - â•¡
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeDblH
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeDblH;
+
+ set => GlyphSettings.Defaults.RightTeeDblH = value;
+
+ }
+
+ /// Box Drawings Right Tee - Double Vertical and Single Horizontal (U+2562) - â•¢
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeDblV
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeDblV;
+
+ set => GlyphSettings.Defaults.RightTeeDblV = value;
+
+ }
+
+ /// Box Drawings Right Tee - Double Vertical and Double Horizontal (U+2563) - â•£
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeDbl
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeDbl;
+
+ set => GlyphSettings.Defaults.RightTeeDbl = value;
+
+ }
+
+ /// Box Drawings Right Tee - Heavy Horizontal and Light Vertical (U+2528) - ┥
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeHvH
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeHvH;
+
+ set => GlyphSettings.Defaults.RightTeeHvH = value;
+
+ }
+
+ /// Box Drawings Right Tee - Light Horizontal and Heavy Vertical (U+2530) - ┨
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeHvV
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeHvV;
+
+ set => GlyphSettings.Defaults.RightTeeHvV = value;
+
+ }
+
+ /// Box Drawings Right Tee - Heavy Vertical and Heavy Horizontal (U+252C) - ┫
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune RightTeeHvDblH
+
+ {
+
+ get => GlyphSettings.Defaults.RightTeeHvDblH;
+
+ set => GlyphSettings.Defaults.RightTeeHvDblH = value;
+
+ }
+
+ /// Box Drawings Top Tee - Single Vertical and Single Horizontal (U+252C) - ┬
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTee
+
+ {
+
+ get => GlyphSettings.Defaults.TopTee;
+
+ set => GlyphSettings.Defaults.TopTee = value;
+
+ }
+
+ /// Box Drawings Top Tee - Single Vertical and Double Horizontal (U+2564) - ╤
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeDblH
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeDblH;
+
+ set => GlyphSettings.Defaults.TopTeeDblH = value;
+
+ }
+
+ /// Box Drawings Top Tee - Double Vertical and Single Horizontal (U+2565) - â•¥
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeDblV
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeDblV;
+
+ set => GlyphSettings.Defaults.TopTeeDblV = value;
+
+ }
+
+ /// Box Drawings Top Tee - Double Vertical and Double Horizontal (U+2566) - ╦
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeDbl
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeDbl;
+
+ set => GlyphSettings.Defaults.TopTeeDbl = value;
+
+ }
+
+ /// Box Drawings Top Tee - Heavy Horizontal and Light Vertical (U+252F) - ┯
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeHvH
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeHvH;
+
+ set => GlyphSettings.Defaults.TopTeeHvH = value;
+
+ }
+
+ /// Box Drawings Top Tee - Light Horizontal and Heavy Vertical (U+2537) - â”°
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeHvV
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeHvV;
+
+ set => GlyphSettings.Defaults.TopTeeHvV = value;
+
+ }
+
+ /// Box Drawings Top Tee - Heavy Vertical and Heavy Horizontal (U+2533) - ┳
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune TopTeeHvDblH
+
+ {
+
+ get => GlyphSettings.Defaults.TopTeeHvDblH;
+
+ set => GlyphSettings.Defaults.TopTeeHvDblH = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Single Vertical and Single Horizontal (U+2534) - â”´
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTee
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTee;
+
+ set => GlyphSettings.Defaults.BottomTee = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Single Vertical and Double Horizontal (U+2567) - â•§
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeDblH
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeDblH;
+
+ set => GlyphSettings.Defaults.BottomTeeDblH = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Double Vertical and Single Horizontal (U+2568) - ╨
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeDblV
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeDblV;
+
+ set => GlyphSettings.Defaults.BottomTeeDblV = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Double Vertical and Double Horizontal (U+2569) - â•©
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeDbl
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeDbl;
+
+ set => GlyphSettings.Defaults.BottomTeeDbl = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Heavy Horizontal and Light Vertical (U+2535) - â”·
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeHvH
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeHvH;
+
+ set => GlyphSettings.Defaults.BottomTeeHvH = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Light Horizontal and Heavy Vertical (U+253D) - ┸
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeHvV
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeHvV;
+
+ set => GlyphSettings.Defaults.BottomTeeHvV = value;
+
+ }
+
+ /// Box Drawings Bottom Tee - Heavy Vertical and Heavy Horizontal (U+2539) - â”»
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune BottomTeeHvDblH
+
+ {
+
+ get => GlyphSettings.Defaults.BottomTeeHvDblH;
+
+ set => GlyphSettings.Defaults.BottomTeeHvDblH = value;
+
+ }
+
+ #endregion
+
+ #region ----------------- Crosses -----------------
+
+ /// Box Drawings Cross - Single Vertical and Single Horizontal (U+253C) - ┼
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune Cross
+
+ {
+
+ get => GlyphSettings.Defaults.Cross;
+
+ set => GlyphSettings.Defaults.Cross = value;
+
+ }
+
+ /// Box Drawings Cross - Single Vertical and Double Horizontal (U+256A) - ╪
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossDblH
+
+ {
+
+ get => GlyphSettings.Defaults.CrossDblH;
+
+ set => GlyphSettings.Defaults.CrossDblH = value;
+
+ }
+
+ /// Box Drawings Cross - Double Vertical and Single Horizontal (U+256B) - â•«
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossDblV
+
+ {
+
+ get => GlyphSettings.Defaults.CrossDblV;
+
+ set => GlyphSettings.Defaults.CrossDblV = value;
+
+ }
+
+ /// Box Drawings Cross - Double Vertical and Double Horizontal (U+256C) - ╬
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossDbl
+
+ {
+
+ get => GlyphSettings.Defaults.CrossDbl;
+
+ set => GlyphSettings.Defaults.CrossDbl = value;
+
+ }
+
+ /// Box Drawings Cross - Heavy Horizontal and Light Vertical (U+253F) - ┿
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossHvH
+
+ {
+
+ get => GlyphSettings.Defaults.CrossHvH;
+
+ set => GlyphSettings.Defaults.CrossHvH = value;
+
+ }
+
+ /// Box Drawings Cross - Light Horizontal and Heavy Vertical (U+2541) - â•‚
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossHvV
+
+ {
+
+ get => GlyphSettings.Defaults.CrossHvV;
+
+ set => GlyphSettings.Defaults.CrossHvV = value;
+
+ }
+
+ /// Box Drawings Cross - Heavy Vertical and Heavy Horizontal (U+254B) - â•‹
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune CrossHv
+
+ {
+
+ get => GlyphSettings.Defaults.CrossHv;
+
+ set => GlyphSettings.Defaults.CrossHv = value;
+
+ }
+
+ #endregion
+
+ #region ----------------- ShadowStyle -----------------
+
+ /// Shadow - Vertical Start - Left Half Block - ▌ U+0258c
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ShadowVerticalStart // Half: '\u2596' â––;
+
+ {
+
+ get => GlyphSettings.Defaults.ShadowVerticalStart;
+
+ set => GlyphSettings.Defaults.ShadowVerticalStart = value;
+
+ }
+
+ /// Shadow - Vertical - Left Half Block - ▌ U+0258c
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ShadowVertical
+
+ {
+
+ get => GlyphSettings.Defaults.ShadowVertical;
+
+ set => GlyphSettings.Defaults.ShadowVertical = value;
+
+ }
+
+ /// Shadow - Horizontal Start - Upper Half Block - â–€ U+02580
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ShadowHorizontalStart // Half: â– U+0259d;
+
+ {
+
+ get => GlyphSettings.Defaults.ShadowHorizontalStart;
+
+ set => GlyphSettings.Defaults.ShadowHorizontalStart = value;
+
+ }
+
+ /// Shadow - Horizontal - Upper Half Block - â–€ U+02580
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ShadowHorizontal
+
+ {
+
+ get => GlyphSettings.Defaults.ShadowHorizontal;
+
+ set => GlyphSettings.Defaults.ShadowHorizontal = value;
+
+ }
+
+ /// Shadow - Horizontal End - Quadrant Upper Left - â–˜ U+02598
+
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune ShadowHorizontalEnd
- /// Shadow - Vertical - Left Half Block - ▌ U+0258c
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ShadowVertical { get; set; } = (Rune)'▌';
+ {
- /// Shadow - Horizontal Start - Upper Half Block - ▀ U+02580
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ShadowHorizontalStart { get; set; } = (Rune)'▝'; // Half: ▝ U+0259d;
+ get => GlyphSettings.Defaults.ShadowHorizontalEnd;
- /// Shadow - Horizontal - Upper Half Block - ▀ U+02580
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ShadowHorizontal { get; set; } = (Rune)'▀';
+ set => GlyphSettings.Defaults.ShadowHorizontalEnd = value;
- /// Shadow - Horizontal End - Quadrant Upper Left - ▘ U+02598
- [ConfigurationProperty (Scope = typeof (ThemeScope))] public static Rune ShadowHorizontalEnd { get; set; } = (Rune)'▘';
+ }
#endregion
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs
index 580b624b93..a67ee8dd16 100644
--- a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs
+++ b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs
@@ -62,10 +62,8 @@ public class LineCanvas : IDisposable
/// Creates a new instance.
public LineCanvas () =>
- // TODO: Refactor ConfigurationManager to not use an event handler for this.
- // Instead, have it call a method on any class appropriately attributed
- // to update the cached values. See Issue #2871
- ConfigurationManager.Applied += ConfigurationManager_Applied;
+ // Subscribes to theme changes so cached glyphs/attributes can be refreshed. See Issue #2871.
+ ThemeChanges.ThemeChanged += OnThemeChanged;
private readonly List _lines = [];
@@ -701,7 +699,7 @@ private static bool All (ReadOnlySpan intersects, Orient
return true;
}
- private void ConfigurationManager_Applied (object? sender, ConfigurationManagerEventArgs e)
+ private void OnThemeChanged (object? sender, App.EventArgs e)
{
foreach (KeyValuePair irr in _runeResolvers)
{
@@ -1256,7 +1254,7 @@ public static LineDirections GetLineDirections (string? grapheme)
///
public void Dispose ()
{
- ConfigurationManager.Applied -= ConfigurationManager_Applied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
GC.SuppressFinalize (this);
}
}
diff --git a/Terminal.Gui/Drivers/Driver.cs b/Terminal.Gui/Drivers/Driver.cs
index 870aa79154..27f960479e 100644
--- a/Terminal.Gui/Drivers/Driver.cs
+++ b/Terminal.Gui/Drivers/Driver.cs
@@ -15,12 +15,12 @@ public sealed class Driver
[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static bool Force16Colors
{
- get;
+ get => DriverSettings.Defaults.Force16Colors;
set
{
- bool oldValue = field;
- field = value;
- Force16ColorsChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, field));
+ bool oldValue = DriverSettings.Defaults.Force16Colors;
+ DriverSettings.Defaults.Force16Colors = value;
+ Force16ColorsChanged?.Invoke (null, new ValueChangedEventArgs (oldValue, value));
}
}
@@ -40,7 +40,11 @@ public static bool Force16Colors
///
///
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static SizeDetectionMode SizeDetection { get; set; } = SizeDetectionMode.AnsiQuery;
+ public static SizeDetectionMode SizeDetection
+ {
+ get => DriverSettings.Defaults.SizeDetection;
+ set => DriverSettings.Defaults.SizeDetection = value;
+ }
///
/// Determines whether the process has a controlling terminal usable for TUI rendering and input.
diff --git a/Terminal.Gui/Input/Keyboard/Key.cs b/Terminal.Gui/Input/Keyboard/Key.cs
index e48e571b62..21fcdfca31 100644
--- a/Terminal.Gui/Input/Keyboard/Key.cs
+++ b/Terminal.Gui/Input/Keyboard/Key.cs
@@ -1335,13 +1335,13 @@ public static bool TryParse (string text, out Key key)
[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static Rune Separator
{
- get;
+ get => KeySettings.Defaults.Separator;
set
{
- if (field != value)
+ if (KeySettings.Defaults.Separator != value)
{
- field = value == default (Rune) ? new Rune ('+') : value;
+ KeySettings.Defaults.Separator = value == default (Rune) ? new Rune ('+') : value;
}
}
- } = new ('+');
+ }
}
diff --git a/Terminal.Gui/ModuleInitializers.cs b/Terminal.Gui/ModuleInitializers.cs
index fbce733cf0..d7e4c86077 100644
--- a/Terminal.Gui/ModuleInitializers.cs
+++ b/Terminal.Gui/ModuleInitializers.cs
@@ -1,4 +1,7 @@
using System.Runtime.CompilerServices;
+using Terminal.Gui.Configuration;
+
+#pragma warning disable CS0618 // Obsolete - ConfigurationManager still used internally during transition
namespace Terminal.Gui;
@@ -21,8 +24,15 @@ internal static void InitializeConfigurationManager ()
// are loaded deterministically
ConfigurationManager.Initialize ();
+ // After CM initialization, also apply MEC-based settings.
+ // MEC values (from the same config files) take precedence over CM defaults.
+ TuiConfigurationBuilder mecBuilder = new ();
+ mecBuilder.ApplyToStaticFacades ();
+
// Note: We're only initializing the config properties structure here,
// not loading settings from files. That still happens during Application.Init()
// via InitializeConfigurationManagement()
}
}
+
+#pragma warning restore CS0618
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index 6f026ccc7c..a79f687669 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -31,8 +31,8 @@
false
true
true
-
- $(NoWarn);IL2026
+
+ $(NoWarn);IL2026;IL3050
true
@@ -55,7 +55,11 @@
+
+
+
+
diff --git a/Terminal.Gui/Text/NerdFonts.cs b/Terminal.Gui/Text/NerdFonts.cs
index c6da806976..2253a3ecef 100644
--- a/Terminal.Gui/Text/NerdFonts.cs
+++ b/Terminal.Gui/Text/NerdFonts.cs
@@ -16,7 +16,11 @@ internal class NerdFonts
/// installed on the users machine to work correctly. Defaults to .
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static bool Enable { get; set; } = false;
+ public static bool Enable
+ {
+ get => NerdFontsSettings.Defaults.Enable;
+ set => NerdFontsSettings.Defaults.Enable = value;
+ }
/// Mapping of file extension to name.
public Dictionary ExtensionToIcon { get; set; } = new ()
diff --git a/Terminal.Gui/ViewBase/View.Keyboard.cs b/Terminal.Gui/ViewBase/View.Keyboard.cs
index bbe5ba49d8..cc68dd04a4 100644
--- a/Terminal.Gui/ViewBase/View.Keyboard.cs
+++ b/Terminal.Gui/ViewBase/View.Keyboard.cs
@@ -1,5 +1,7 @@
using Terminal.Gui.Tracing;
+#pragma warning disable CS0618 // Obsolete - View.Keyboard still uses ConfigurationPropertyAttribute during transition
+
namespace Terminal.Gui.ViewBase;
public partial class View // Keyboard APIs
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index 04e3d8d2b5..3e05362cc2 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -54,13 +54,21 @@ public class Button : View, IDesignable, IAcceptTarget
/// Gets or sets whether s are shown with a shadow effect by default.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Opaque;
+ public static ShadowStyles DefaultShadow
+ {
+ get => ButtonSettings.Defaults.DefaultShadow;
+ set => ButtonSettings.Defaults.DefaultShadow = value;
+ }
///
/// Gets or sets the default Highlight Style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static MouseState DefaultMouseHighlightStates { get; set; } = MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
+ public static MouseState DefaultMouseHighlightStates
+ {
+ get => ButtonSettings.Defaults.DefaultMouseHighlightStates;
+ set => ButtonSettings.Defaults.DefaultMouseHighlightStates = value;
+ }
/// Initializes a new instance of .
public Button ()
diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs
index 292e27276b..69133b56bc 100644
--- a/Terminal.Gui/Views/CharMap/CharMap.cs
+++ b/Terminal.Gui/Views/CharMap/CharMap.cs
@@ -55,7 +55,11 @@ public class CharMap : View, IDesignable, IValue
/// Gets or sets the default cursor style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+ public static CursorStyle DefaultCursorStyle
+ {
+ get => CharMapSettings.Defaults.DefaultCursorStyle;
+ set => CharMapSettings.Defaults.DefaultCursorStyle = value;
+ }
private const int COLUMN_WIDTH = 3; // Width of each column of glyphs
private const int HEADER_HEIGHT = 1; // Height of the header
diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs
index 5fc1bb4cc2..df991b4cc5 100644
--- a/Terminal.Gui/Views/CheckBox.cs
+++ b/Terminal.Gui/Views/CheckBox.cs
@@ -25,7 +25,11 @@ public class CheckBox : View, IValue
/// Gets or sets the default Highlight Style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static MouseState DefaultMouseHighlightStates { get; set; } = MouseState.PressedOutside | MouseState.Pressed | MouseState.In;
+ public static MouseState DefaultMouseHighlightStates
+ {
+ get => CheckBoxSettings.Defaults.DefaultMouseHighlightStates;
+ set => CheckBoxSettings.Defaults.DefaultMouseHighlightStates = value;
+ }
///
/// Initializes a new instance of .
diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs
index 56732bb01c..5da9e0e0c9 100644
--- a/Terminal.Gui/Views/Dialog.cs
+++ b/Terminal.Gui/Views/Dialog.cs
@@ -65,25 +65,41 @@ public class Dialog : Dialog
/// and theme files.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Heavy;
+ public static LineStyle DefaultBorderStyle
+ {
+ get => DialogSettings.Defaults.DefaultBorderStyle;
+ set => DialogSettings.Defaults.DefaultBorderStyle = value;
+ }
///
/// The default button alignment for new instances. Can be configured via theme files.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
+ public static Alignment DefaultButtonAlignment
+ {
+ get => DialogSettings.Defaults.DefaultButtonAlignment;
+ set => DialogSettings.Defaults.DefaultButtonAlignment = value;
+ }
///
/// The default button alignment modes for new instances. Can be configured via theme files.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
+ public static AlignmentModes DefaultButtonAlignmentModes
+ {
+ get => DialogSettings.Defaults.DefaultButtonAlignmentModes;
+ set => DialogSettings.Defaults.DefaultButtonAlignmentModes = value;
+ }
///
/// The default shadow style for new instances. Can be configured via theme files.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Transparent;
+ public static ShadowStyles DefaultShadow
+ {
+ get => DialogSettings.Defaults.DefaultShadow;
+ set => DialogSettings.Defaults.DefaultShadow = value;
+ }
///
/// Helper property that gets whether the dialog was canceled (Result is or 1).
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
index 4402d4955b..d4425cfe3f 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
@@ -370,7 +370,11 @@ public override void EndInit ()
/// The maximum number of results that will be collected when searching before stopping.
/// This prevents performance issues e.g. when searching root of file system for a common letter (e.g. 'e').
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static int MaxSearchResults { get; set; } = 10000;
+ public static int MaxSearchResults
+ {
+ get => FileDialogSettings.Defaults.MaxSearchResults;
+ set => FileDialogSettings.Defaults.MaxSearchResults = value;
+ }
///
/// Gets all files/directories selected or an empty collection is
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs b/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs
index e97f166de5..d66e0fa3c4 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialogStyle.cs
@@ -45,14 +45,22 @@ public FileDialogStyle (IFileSystem? fileSystem)
/// files via
///
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static bool DefaultUseColors { get; set; } = true;
+ public static bool DefaultUseColors
+ {
+ get => FileDialogStyleSettings.Defaults.DefaultUseColors;
+ set => FileDialogStyleSettings.Defaults.DefaultUseColors = value;
+ }
///
/// Gets or sets the default value to use for . This can be populated from .tui
/// config files via
///
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static bool DefaultUseUnicodeCharacters { get; set; }
+ public static bool DefaultUseUnicodeCharacters
+ {
+ get => FileDialogStyleSettings.Defaults.DefaultUseUnicodeCharacters;
+ set => FileDialogStyleSettings.Defaults.DefaultUseUnicodeCharacters = value;
+ }
///
/// Gets or sets error message when user is and user enters the
diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs
index da7bf42a25..68ad2940fd 100644
--- a/Terminal.Gui/Views/FrameView.cs
+++ b/Terminal.Gui/Views/FrameView.cs
@@ -18,8 +18,6 @@ namespace Terminal.Gui.Views;
///
public class FrameView : View
{
- private static LineStyle _defaultBorderStyle = LineStyle.Rounded; // Resources/config.json overrides
-
///
/// Initializes a new instance of the class.
/// layout.
@@ -45,7 +43,7 @@ public FrameView ()
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static LineStyle DefaultBorderStyle
{
- get => _defaultBorderStyle;
- set => _defaultBorderStyle = value;
+ get => FrameViewSettings.Defaults.DefaultBorderStyle;
+ set => FrameViewSettings.Defaults.DefaultBorderStyle = value;
}
}
diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs
index 511b23f037..e772bf72f0 100644
--- a/Terminal.Gui/Views/HexView.cs
+++ b/Terminal.Gui/Views/HexView.cs
@@ -81,7 +81,11 @@ public class HexView : View, IDesignable
/// Gets or sets the default cursor style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+ public static CursorStyle DefaultCursorStyle
+ {
+ get => HexViewSettings.Defaults.DefaultCursorStyle;
+ set => HexViewSettings.Defaults.DefaultCursorStyle = value;
+ }
///
/// Gets or sets the view-specific default key bindings for . Contains only bindings
diff --git a/Terminal.Gui/Views/LinearRange/LinearRangeDefaults.cs b/Terminal.Gui/Views/LinearRange/LinearRangeDefaults.cs
index 445c3f749d..04f5b7e145 100644
--- a/Terminal.Gui/Views/LinearRange/LinearRangeDefaults.cs
+++ b/Terminal.Gui/Views/LinearRange/LinearRangeDefaults.cs
@@ -9,5 +9,9 @@ public static class LinearRangeDefaults
{
/// Gets or sets the default cursor style applied to a new linear range view.
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBlock;
+ public static CursorStyle DefaultCursorStyle
+ {
+ get => LinearRangeSettings.Defaults.DefaultCursorStyle;
+ set => LinearRangeSettings.Defaults.DefaultCursorStyle = value;
+ }
}
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs
index d89170d6bf..2bcdf9b8d2 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -1,5 +1,7 @@
using Terminal.Gui.Tracing;
+#pragma warning disable CS0618 // Obsolete - Menu uses [ConfigurationProperty] for DefaultBorderStyle during transition
+
namespace Terminal.Gui.Views;
///
@@ -51,7 +53,11 @@ public class Menu : Bar, IValue
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.None;
+ public static LineStyle DefaultBorderStyle
+ {
+ get => MenuSettings.Defaults.DefaultBorderStyle;
+ set => MenuSettings.Defaults.DefaultBorderStyle = value;
+ }
///
public Menu () : this ([]) { }
@@ -79,10 +85,10 @@ public Menu (IEnumerable? shortcuts) : base (shortcuts)
KeyBindings.Clear ();
MouseBindings.Clear ();
- ConfigurationManager.Applied += OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged += OnThemeChanged;
}
- private void OnConfigurationManagerApplied (object? sender, ConfigurationManagerEventArgs e)
+ private void OnThemeChanged (object? sender, App.EventArgs e)
{
if (SuperView is { })
{
@@ -507,7 +513,7 @@ protected override void Dispose (bool disposing)
if (disposing)
{
- ConfigurationManager.Applied -= OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
}
}
}
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index 491ff8779d..278f132fa9 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -1,6 +1,8 @@
using System.ComponentModel;
using Terminal.Gui.Tracing;
+#pragma warning disable CS0618 // Obsolete - MenuBar uses [ConfigurationProperty] for DefaultBorderStyle/DefaultKey during transition
+
namespace Terminal.Gui.Views;
///
@@ -118,7 +120,7 @@ public MenuBar (IEnumerable
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.None;
+ public new static LineStyle DefaultBorderStyle
+ {
+ get => MenuBarSettings.Defaults.DefaultBorderStyle;
+ set => MenuBarSettings.Defaults.DefaultBorderStyle = value;
+ }
/// The default key for activating menu bars.
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static Key DefaultKey { get; set; } = Key.F10;
+ public static Key DefaultKey
+ {
+ get => MenuBarSettings.Defaults.DefaultKey;
+ set => MenuBarSettings.Defaults.DefaultKey = value;
+ }
///
public override void EndInit ()
@@ -586,7 +596,7 @@ protected override void Dispose (bool disposing)
if (disposing)
{
- ConfigurationManager.Applied -= OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
}
}
@@ -780,7 +790,7 @@ protected override void OnSuperViewChanged (ValueChangedEventArgs e)
// BUGBUG: For some reason in some unit tests, when Top is disposed, MenuBar.Dispose does not get called.
// BUGBUG: Yet, the MenuBar does get Removed from Top (and it's SuperView set to null).
// BUGBUG: Related: https://github.com/gui-cs/Terminal.Gui/issues/4021
- ConfigurationManager.Applied -= OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
}
}
@@ -862,7 +872,7 @@ private bool IsInlineSubMenu (View view)
return false;
}
- private void OnConfigurationManagerApplied (object? sender, ConfigurationManagerEventArgs e) => BorderStyle = DefaultBorderStyle;
+ private void OnThemeChanged (object? sender, App.EventArgs e) => BorderStyle = DefaultBorderStyle;
private void OnEntryMenuOpenChanged (object? sender, ValueChangedEventArgs e)
{
diff --git a/Terminal.Gui/Views/Menu/PopoverMenu.cs b/Terminal.Gui/Views/Menu/PopoverMenu.cs
index 8297d79130..45df4e0cd3 100644
--- a/Terminal.Gui/Views/Menu/PopoverMenu.cs
+++ b/Terminal.Gui/Views/Menu/PopoverMenu.cs
@@ -293,7 +293,11 @@ public Key Key
/// This is a configuration property that affects all new instances.
///
[ConfigurationProperty (Scope = typeof (SettingsScope))]
- public static Key DefaultKey { get; set; } = Key.F10.WithShift;
+ public static Key DefaultKey
+ {
+ get => PopoverMenuSettings.Defaults.DefaultKey;
+ set => PopoverMenuSettings.Defaults.DefaultKey = value;
+ }
///
/// The mouse flags that will cause the popover menu to be visible. The default is
diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs
index 50b0aef8a6..56fb3f873c 100644
--- a/Terminal.Gui/Views/MessageBox.cs
+++ b/Terminal.Gui/Views/MessageBox.cs
@@ -53,9 +53,6 @@ namespace Terminal.Gui.Views;
///
public static class MessageBox
{
- private static LineStyle _defaultBorderStyle = LineStyle.Heavy; // Resources/config.json overrides
- private static Alignment _defaultButtonAlignment = Alignment.Center; // Resources/config.json overrides
-
///
/// Defines the default border styling for . Can be configured via
/// .
@@ -63,8 +60,8 @@ public static class MessageBox
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static LineStyle DefaultBorderStyle
{
- get => _defaultBorderStyle;
- set => _defaultBorderStyle = value;
+ get => MessageBoxSettings.Defaults.DefaultBorderStyle;
+ set => MessageBoxSettings.Defaults.DefaultBorderStyle = value;
}
/// The default for .
@@ -72,8 +69,8 @@ public static LineStyle DefaultBorderStyle
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static Alignment DefaultButtonAlignment
{
- get => _defaultButtonAlignment;
- set => _defaultButtonAlignment = value;
+ get => MessageBoxSettings.Defaults.DefaultButtonAlignment;
+ set => MessageBoxSettings.Defaults.DefaultButtonAlignment = value;
}
///
diff --git a/Terminal.Gui/Views/Selectors/SelectorBase.cs b/Terminal.Gui/Views/Selectors/SelectorBase.cs
index 23ec2180a9..e962e9a38e 100644
--- a/Terminal.Gui/Views/Selectors/SelectorBase.cs
+++ b/Terminal.Gui/Views/Selectors/SelectorBase.cs
@@ -26,7 +26,11 @@ public abstract class SelectorBase : View, IOrientation, IValue
/// Gets or sets the default Highlight Style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static MouseState DefaultMouseHighlightStates { get; set; } = MouseState.In;
+ public static MouseState DefaultMouseHighlightStates
+ {
+ get => SelectorBaseSettings.Defaults.DefaultMouseHighlightStates;
+ set => SelectorBaseSettings.Defaults.DefaultMouseHighlightStates = value;
+ }
///
/// Initializes a new instance of the class.
diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs
index 4bf42c180e..76331d459e 100644
--- a/Terminal.Gui/Views/StatusBar.cs
+++ b/Terminal.Gui/Views/StatusBar.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS0618 // Obsolete - StatusBar uses [ConfigurationProperty] for DefaultSeparatorLineStyle during transition
+
namespace Terminal.Gui.Views;
///
@@ -12,8 +14,6 @@ namespace Terminal.Gui.Views;
///
public class StatusBar : Bar, IDesignable
{
- private static LineStyle _defaultSeparatorLineStyle = LineStyle.Single; // Resources/config.json overrides
-
///
public StatusBar () : this ([]) { }
@@ -33,7 +33,7 @@ public StatusBar (IEnumerable shortcuts) : base (shortcuts)
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Menu);
- ConfigurationManager.Applied += OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged += OnThemeChanged;
}
///
@@ -45,10 +45,10 @@ protected override void OnSuperViewChanged (ValueChangedEventArgs e)
// BUGBUG: For some reason in some unit tests, when Top is disposed, MenuBar.Dispose does not get called.
// BUGBUG: Yet, the MenuBar does get Removed from Top (and it's SuperView set to null).
// BUGBUG: Related: https://github.com/gui-cs/Terminal.Gui/issues/4021
- ConfigurationManager.Applied -= OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
}
}
- private void OnConfigurationManagerApplied (object? sender, ConfigurationManagerEventArgs e)
+ private void OnThemeChanged (object? sender, App.EventArgs e)
{
if (Border is { })
{
@@ -62,8 +62,8 @@ private void OnConfigurationManagerApplied (object? sender, ConfigurationManager
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static LineStyle DefaultSeparatorLineStyle
{
- get => _defaultSeparatorLineStyle;
- set => _defaultSeparatorLineStyle = value;
+ get => StatusBarSettings.Defaults.DefaultSeparatorLineStyle;
+ set => StatusBarSettings.Defaults.DefaultSeparatorLineStyle = value;
}
///
@@ -180,6 +180,6 @@ protected override void Dispose (bool disposing)
{
base.Dispose (disposing);
- ConfigurationManager.Applied -= OnConfigurationManagerApplied;
+ ThemeChanges.ThemeChanged -= OnThemeChanged;
}
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/TextInput/TextField/TextField.cs b/Terminal.Gui/Views/TextInput/TextField/TextField.cs
index 38b73afcb1..a4212617e7 100644
--- a/Terminal.Gui/Views/TextInput/TextField/TextField.cs
+++ b/Terminal.Gui/Views/TextInput/TextField/TextField.cs
@@ -80,7 +80,11 @@ public partial class TextField : View, IDesignable, IValue
/// Gets or sets the default cursor style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBar;
+ public static CursorStyle DefaultCursorStyle
+ {
+ get => TextFieldSettings.Defaults.DefaultCursorStyle;
+ set => TextFieldSettings.Defaults.DefaultCursorStyle = value;
+ }
///
/// Initializes a new instance of the class.
diff --git a/Terminal.Gui/Views/TextInput/TextView/TextView.cs b/Terminal.Gui/Views/TextInput/TextView/TextView.cs
index e517449708..6879118f29 100644
--- a/Terminal.Gui/Views/TextInput/TextView/TextView.cs
+++ b/Terminal.Gui/Views/TextInput/TextView/TextView.cs
@@ -105,7 +105,11 @@ public partial class TextView : View, IDesignable
/// Gets or sets the default cursor style.
///
[ConfigurationProperty (Scope = typeof (ThemeScope))]
- public static CursorStyle DefaultCursorStyle { get; set; } = CursorStyle.BlinkingBar;
+ public static CursorStyle DefaultCursorStyle
+ {
+ get => TextViewSettings.Defaults.DefaultCursorStyle;
+ set => TextViewSettings.Defaults.DefaultCursorStyle = value;
+ }
private CultureInfo? _currentCulture;
diff --git a/Terminal.Gui/Views/Window.cs b/Terminal.Gui/Views/Window.cs
index 0277b85d5c..a93b1a3301 100644
--- a/Terminal.Gui/Views/Window.cs
+++ b/Terminal.Gui/Views/Window.cs
@@ -17,9 +17,6 @@ namespace Terminal.Gui.Views;
///
public class Window : Runnable
{
- private static ShadowStyles _defaultShadow = ShadowStyles.None; // Resources/config.json overrides
- private static LineStyle _defaultBorderStyle = LineStyle.Single; // Resources/config.json overrides
-
///
/// Initializes a new instance of the class.
///
@@ -39,8 +36,8 @@ public Window ()
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static ShadowStyles DefaultShadow
{
- get => _defaultShadow;
- set => _defaultShadow = value;
+ get => WindowSettings.Defaults.DefaultShadow;
+ set => WindowSettings.Defaults.DefaultShadow = value;
}
// TODO: enable this
@@ -64,7 +61,7 @@ public static ShadowStyles DefaultShadow
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static LineStyle DefaultBorderStyle
{
- get => _defaultBorderStyle;
- set => _defaultBorderStyle = value;
+ get => WindowSettings.Defaults.DefaultBorderStyle;
+ set => WindowSettings.Defaults.DefaultBorderStyle = value;
}
}
diff --git a/Tests/UnitTestsParallelizable/Configuration/KeyJsonConverterTests.cs b/Tests/UnitTestsParallelizable/Configuration/KeyJsonConverterTests.cs
index ce9151b7c6..5eee47425c 100644
--- a/Tests/UnitTestsParallelizable/Configuration/KeyJsonConverterTests.cs
+++ b/Tests/UnitTestsParallelizable/Configuration/KeyJsonConverterTests.cs
@@ -45,8 +45,8 @@ public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo)
// Arrange
// Act
- string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager.SerializerContext.Options);
- var deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize ((Key)key, TuiSerializerContext.Instance.Options);
+ var deserializedKey = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal (expectedStringTo, deserializedKey!.ToString ());
@@ -60,7 +60,7 @@ public void Deserialized_Key_Equals ()
// Act
string json = "\"Ctrl+Q\"";
- Key? deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ Key? deserializedKey = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal (key, deserializedKey);
@@ -70,7 +70,7 @@ public void Deserialized_Key_Equals ()
public void Separator_Property_Serializes_As_Glyph ()
{
// Act
- string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize (Key.Separator, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ($"\"{Key.Separator}\"", json);
diff --git a/Tests/UnitTestsParallelizable/Configuration/MecAppSettingsTests.cs b/Tests/UnitTestsParallelizable/Configuration/MecAppSettingsTests.cs
new file mode 100644
index 0000000000..369e4d43f8
--- /dev/null
+++ b/Tests/UnitTestsParallelizable/Configuration/MecAppSettingsTests.cs
@@ -0,0 +1,90 @@
+// Copilot
+
+using Terminal.Gui.Configuration;
+
+namespace ConfigurationTests;
+
+/// Tests for the app-developer workflow.
+public class MecAppSettingsTests
+{
+ /// Example app settings POCO for testing.
+ private class SampleAppSettings
+ {
+ public string Title { get; set; } = "Default Title";
+ public int MaxItems { get; set; } = 100;
+ public bool EnableFeatureX { get; set; }
+ public static SampleAppSettings Defaults { get; set; } = new ();
+ }
+
+ [Fact]
+ public void BindAppSettings_BindsFromJson ()
+ {
+ // Arrange
+ string json = """{"SampleApp": {"Title": "My App", "MaxItems": 50, "EnableFeatureX": true}}""";
+ TuiConfigurationBuilder builder = new () { RuntimeConfig = json };
+
+ // Act
+ builder.BindAppSettings ("SampleApp", s => SampleAppSettings.Defaults = s);
+
+ // Assert
+ Assert.Equal ("My App", SampleAppSettings.Defaults.Title);
+ Assert.Equal (50, SampleAppSettings.Defaults.MaxItems);
+ Assert.True (SampleAppSettings.Defaults.EnableFeatureX);
+
+ // Cleanup
+ SampleAppSettings.Defaults = new ();
+ }
+
+ [Fact]
+ public void BindAppSettings_UsesDefaults_WhenSectionMissing ()
+ {
+ // Arrange - empty config
+ TuiConfigurationBuilder builder = new () { RuntimeConfig = "{}" };
+
+ // Act
+ builder.BindAppSettings ("SampleApp", s => SampleAppSettings.Defaults = s);
+
+ // Assert - defaults from POCO
+ Assert.Equal ("Default Title", SampleAppSettings.Defaults.Title);
+ Assert.Equal (100, SampleAppSettings.Defaults.MaxItems);
+ Assert.False (SampleAppSettings.Defaults.EnableFeatureX);
+
+ // Cleanup
+ SampleAppSettings.Defaults = new ();
+ }
+
+ [Fact]
+ public void BindAppSettings_PartialJson_MergesWithDefaults ()
+ {
+ // Arrange - only Title specified
+ string json = """{"SampleApp": {"Title": "Custom"}}""";
+ TuiConfigurationBuilder builder = new () { RuntimeConfig = json };
+
+ // Act
+ builder.BindAppSettings ("SampleApp", s => SampleAppSettings.Defaults = s);
+
+ // Assert
+ Assert.Equal ("Custom", SampleAppSettings.Defaults.Title);
+ Assert.Equal (100, SampleAppSettings.Defaults.MaxItems);
+ Assert.False (SampleAppSettings.Defaults.EnableFeatureX);
+
+ // Cleanup
+ SampleAppSettings.Defaults = new ();
+ }
+
+ [Fact]
+ public void BindAppSettings_ReturnsBuilder_ForChaining ()
+ {
+ // Arrange
+ TuiConfigurationBuilder builder = new () { RuntimeConfig = "{}" };
+
+ // Act
+ TuiConfigurationBuilder result = builder.BindAppSettings ("SampleApp", s => SampleAppSettings.Defaults = s);
+
+ // Assert
+ Assert.Same (builder, result);
+
+ // Cleanup
+ SampleAppSettings.Defaults = new ();
+ }
+}
diff --git a/Tests/UnitTestsParallelizable/Configuration/MecSettingsTests.cs b/Tests/UnitTestsParallelizable/Configuration/MecSettingsTests.cs
new file mode 100644
index 0000000000..6dae2cc2e8
--- /dev/null
+++ b/Tests/UnitTestsParallelizable/Configuration/MecSettingsTests.cs
@@ -0,0 +1,204 @@
+// Copilot - Claude Opus 4.6
+
+using Terminal.Gui.Configuration;
+using Microsoft.Extensions.Configuration;
+
+namespace ConfigurationTests;
+
+/// Tests for the MEC-based settings POCOs and .
+public class MecSettingsTests
+{
+ [Fact]
+ public void ApplicationSettings_Defaults_HasCorrectValues ()
+ {
+ ApplicationSettings settings = new ();
+
+ Assert.Equal (AppModel.FullScreen, settings.AppModel);
+ Assert.Equal (string.Empty, settings.ForceDriver);
+ Assert.False (settings.IsMouseDisabled);
+ }
+
+ [Fact]
+ public void DriverSettings_Defaults_HasCorrectValues ()
+ {
+ DriverSettings settings = new ();
+
+ Assert.False (settings.Force16Colors);
+ Assert.Equal (SizeDetectionMode.AnsiQuery, settings.SizeDetection);
+ }
+
+ [Fact]
+ public void ButtonSettings_Defaults_HasCorrectValues ()
+ {
+ ButtonSettings settings = new ();
+
+ Assert.Equal (ShadowStyles.Opaque, settings.DefaultShadow);
+ Assert.Equal (MouseState.In | MouseState.Pressed | MouseState.PressedOutside, settings.DefaultMouseHighlightStates);
+ }
+
+ [Fact]
+ public void DialogSettings_Defaults_HasCorrectValues ()
+ {
+ DialogSettings settings = new ();
+
+ Assert.Equal (ShadowStyles.Transparent, settings.DefaultShadow);
+ Assert.Equal (LineStyle.Heavy, settings.DefaultBorderStyle);
+ Assert.Equal (Alignment.End, settings.DefaultButtonAlignment);
+ Assert.Equal (AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems, settings.DefaultButtonAlignmentModes);
+ }
+
+ [Fact]
+ public void WindowSettings_Defaults_HasCorrectValues ()
+ {
+ WindowSettings settings = new ();
+
+ Assert.Equal (ShadowStyles.None, settings.DefaultShadow);
+ Assert.Equal (LineStyle.Single, settings.DefaultBorderStyle);
+ }
+
+ [Fact]
+ public void MessageBoxSettings_Defaults_HasCorrectValues ()
+ {
+ MessageBoxSettings settings = new ();
+
+ Assert.Equal (LineStyle.Heavy, settings.DefaultBorderStyle);
+ Assert.Equal (Alignment.Center, settings.DefaultButtonAlignment);
+ }
+
+ [Fact]
+ public void CheckBoxSettings_Defaults_HasCorrectValues ()
+ {
+ CheckBoxSettings settings = new ();
+
+ Assert.Equal (MouseState.PressedOutside | MouseState.Pressed | MouseState.In, settings.DefaultMouseHighlightStates);
+ }
+
+ [Fact]
+ public void StaticFacade_CanBeOverridden ()
+ {
+ // Save original
+ ButtonSettings original = ButtonSettings.Defaults;
+
+ try
+ {
+ ButtonSettings custom = new () { DefaultShadow = ShadowStyles.None };
+ ButtonSettings.Defaults = custom;
+
+ Assert.Equal (ShadowStyles.None, ButtonSettings.Defaults.DefaultShadow);
+ }
+ finally
+ {
+ ButtonSettings.Defaults = original;
+ }
+ }
+
+ [Fact]
+ public void TuiConfigurationExtensions_AddTuiRuntimeConfig_BindsJsonToSection ()
+ {
+ // Arrange
+ string json = """
+ {
+ "Application": {
+ "ForceDriver": "ansi",
+ "IsMouseDisabled": true
+ }
+ }
+ """;
+
+ IConfigurationBuilder builder = new ConfigurationBuilder ()
+ .AddTuiRuntimeConfig (json);
+
+ IConfiguration config = builder.Build ();
+
+ // Act
+ ApplicationSettings settings = new ();
+ config.GetSection ("Application").Bind (settings);
+
+ // Assert
+ Assert.Equal ("ansi", settings.ForceDriver);
+ Assert.True (settings.IsMouseDisabled);
+ }
+
+ [Fact]
+ public void TuiConfigurationExtensions_AddTuiRuntimeConfig_NullJson_DoesNotThrow ()
+ {
+ IConfigurationBuilder builder = new ConfigurationBuilder ()
+ .AddTuiRuntimeConfig (null);
+
+ IConfiguration config = builder.Build ();
+
+ // Should not throw, and section should be empty
+ string? value = config.GetSection ("Application") ["ForceDriver"];
+ Assert.Null (value);
+ }
+
+ [Fact]
+ public void TuiConfigurationBuilder_Build_LoadsLibraryDefaults ()
+ {
+ // The library's embedded config.json should be loadable
+ TuiConfigurationBuilder tuiBuilder = new ();
+ IConfiguration config = tuiBuilder.Configuration;
+
+ // The config should have at least the Themes section from the embedded config.json
+ IConfigurationSection themesSection = config.GetSection ("Themes");
+ Assert.NotNull (themesSection);
+ }
+
+ [Fact]
+ public void TuiConfigurationBuilder_RuntimeConfig_InvalidatesCache ()
+ {
+ TuiConfigurationBuilder tuiBuilder = new ();
+ IConfiguration first = tuiBuilder.Configuration;
+
+ tuiBuilder.RuntimeConfig = """{ "Driver": { "Force16Colors": true } }""";
+ IConfiguration second = tuiBuilder.Configuration;
+
+ Assert.NotSame (first, second);
+ }
+
+ [Fact]
+ public void TuiConfigurationBuilder_ApplyToStaticFacades_UpdatesDefaults ()
+ {
+ // Save originals
+ ApplicationSettings originalApp = ApplicationSettings.Defaults;
+ DriverSettings originalDriver = DriverSettings.Defaults;
+
+ try
+ {
+ TuiConfigurationBuilder tuiBuilder = new ();
+
+ tuiBuilder.RuntimeConfig = """
+ {
+ "Application": { "ForceDriver": "dotnet" },
+ "Driver": { "Force16Colors": true }
+ }
+ """;
+ tuiBuilder.ApplyToStaticFacades ();
+
+ Assert.Equal ("dotnet", ApplicationSettings.Defaults.ForceDriver);
+ Assert.True (DriverSettings.Defaults.Force16Colors);
+ }
+ finally
+ {
+ ApplicationSettings.Defaults = originalApp;
+ DriverSettings.Defaults = originalDriver;
+ }
+ }
+
+ [Fact]
+ public void TuiConfigurationExtensions_Precedence_LaterSourceOverridesEarlier ()
+ {
+ string lowPriority = """{ "Driver": { "Force16Colors": false } }""";
+ string highPriority = """{ "Driver": { "Force16Colors": true } }""";
+
+ IConfiguration config = new ConfigurationBuilder ()
+ .AddTuiRuntimeConfig (lowPriority)
+ .AddTuiRuntimeConfig (highPriority)
+ .Build ();
+
+ DriverSettings settings = new ();
+ config.GetSection ("Driver").Bind (settings);
+
+ Assert.True (settings.Force16Colors);
+ }
+}
diff --git a/Tests/UnitTestsParallelizable/Configuration/MecThemeTests.cs b/Tests/UnitTestsParallelizable/Configuration/MecThemeTests.cs
new file mode 100644
index 0000000000..5b34a39856
--- /dev/null
+++ b/Tests/UnitTestsParallelizable/Configuration/MecThemeTests.cs
@@ -0,0 +1,125 @@
+// Copilot
+
+using Terminal.Gui.App;
+using Terminal.Gui.Configuration;
+
+namespace ConfigurationTests;
+
+/// Tests for the MEC-backed theme and scheme manager interfaces.
+public class MecThemeTests
+{
+ [Fact]
+ public void ThemeSettings_Defaults_HasDefaultThemeName ()
+ {
+ Assert.Equal ("Default", ThemeSettings.Defaults.Theme);
+ }
+
+ [Fact]
+ public void MecThemeManager_CurrentThemeName_ReturnsDefault ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ MecThemeManager manager = new (builder);
+ Assert.Equal ("Default", manager.CurrentThemeName);
+ }
+
+ [Fact]
+ public void MecThemeManager_ThemeNames_ContainsDefault ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ MecThemeManager manager = new (builder);
+ Assert.Contains ("Default", manager.ThemeNames);
+ }
+
+ [Fact]
+ public void MecThemeManager_SwitchTheme_NullOrEmpty_ReturnsFalse ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ MecThemeManager manager = new (builder);
+
+ Assert.False (manager.SwitchTheme (""));
+ Assert.False (manager.SwitchTheme (null!));
+ }
+
+ [Fact]
+ public void MecThemeManager_SwitchTheme_NonExistent_ReturnsFalse ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ MecThemeManager manager = new (builder);
+
+ // A non-existent theme name should return false without corrupting state
+ bool result = manager.SwitchTheme ("NonExistent");
+ Assert.False (result);
+ }
+
+ [Fact]
+ public void MecSchemeManager_SchemeNames_DoesNotThrow ()
+ {
+ MecSchemeManager manager = new ();
+ IReadOnlyList names = manager.SchemeNames;
+
+ // Should return a list without throwing (may be empty if CM state is odd in parallel tests)
+ Assert.NotNull (names);
+ }
+
+ [Fact]
+ public void MecSchemeManager_GetScheme_ReturnsNull_ForInvalid ()
+ {
+ MecSchemeManager manager = new ();
+ Scheme? scheme = manager.GetScheme ("NonExistentScheme12345");
+ Assert.Null (scheme);
+ }
+
+ [Fact]
+ public void IThemeManager_Interface_IsImplementedByMecThemeManager ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ IThemeManager manager = builder.ThemeManager;
+ Assert.IsType (manager);
+ }
+
+ [Fact]
+ public void ISchemeManager_Interface_IsImplementedByMecSchemeManager ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ ISchemeManager manager = builder.SchemeManager;
+ Assert.IsType (manager);
+ }
+
+ // Copilot - Claude Opus 4.7
+ [Fact]
+ public void IThemeManager_ThemeChanged_EventCanBeSubscribedAndUnsubscribed ()
+ {
+ TuiConfigurationBuilder builder = new ();
+ IThemeManager manager = builder.ThemeManager;
+ bool fired = false;
+ EventHandler> handler = (_, _) => fired = true;
+
+ manager.ThemeChanged += handler;
+ manager.ThemeChanged -= handler;
+
+ Assert.False (fired);
+ }
+
+ // Copilot - Claude Opus 4.7
+ [Fact]
+ public void ThemeChanges_ThemeChanged_EventCanBeSubscribedAndUnsubscribed ()
+ {
+ bool fired = false;
+ EventHandler> handler = (_, _) => fired = true;
+
+ ThemeChanges.ThemeChanged += handler;
+ ThemeChanges.ThemeChanged -= handler;
+
+ Assert.False (fired);
+ }
+
+ // Copilot - Claude Opus 4.7
+ [Fact]
+ public void ThemeChanges_ThemeChanged_StaticClassIsPublic ()
+ {
+ // Phase B contract: views can reach a static facade without DI.
+ Type t = typeof (ThemeChanges);
+ Assert.True (t.IsAbstract && t.IsSealed, "ThemeChanges must be a static class");
+ Assert.NotNull (t.GetEvent ("ThemeChanged"));
+ }
+}
diff --git a/Tests/UnitTestsParallelizable/Configuration/RuneJsonConverterTests.cs b/Tests/UnitTestsParallelizable/Configuration/RuneJsonConverterTests.cs
index c57189389d..222b29924e 100644
--- a/Tests/UnitTestsParallelizable/Configuration/RuneJsonConverterTests.cs
+++ b/Tests/UnitTestsParallelizable/Configuration/RuneJsonConverterTests.cs
@@ -31,13 +31,13 @@ public class RuneJsonConverterTests
public void RoundTripConversion_Negative (string rune)
{
// Act
- string json = JsonSerializer.Serialize (rune, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize (rune, TuiSerializerContext.Instance.Options);
// Assert
Assert.Throws (
() => JsonSerializer.Deserialize (
json,
- ConfigurationManager.SerializerContext.Options
+ TuiSerializerContext.Instance.Options
)
);
}
@@ -61,8 +61,8 @@ public void RoundTripConversion_Positive (string rune, string expected)
// Arrange
// Act
- string json = JsonSerializer.Serialize (rune, ConfigurationManager.SerializerContext.Options);
- var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize (rune, TuiSerializerContext.Instance.Options);
+ var deserialized = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal (expected, deserialized.ToString ());
@@ -74,7 +74,7 @@ public void Printable_Rune_Is_Serialized_As_Glyph ()
// Arrange
// Act
- string json = JsonSerializer.Serialize ((Rune)'a', ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize ((Rune)'a', TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("\"a\"", json);
@@ -86,7 +86,7 @@ public void Non_Printable_Rune_Is_Serialized_As_u_Encoded_Value ()
// Arrange
// Act
- string json = JsonSerializer.Serialize ((Rune)0x01, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize ((Rune)0x01, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("\"\\u0001\"", json);
@@ -99,7 +99,7 @@ public void Json_With_Glyph_Works ()
var json = "\"a\"";
// Act
- var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ var deserialized = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("a", deserialized.ToString ());
@@ -112,7 +112,7 @@ public void Json_With_u_Encoded_Works ()
var json = "\"\\u0061\"";
// Act
- var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ var deserialized = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("a", deserialized.ToString ());
@@ -125,7 +125,7 @@ public void Json_With_U_Encoded_Works ()
var json = "\"U+0061\"";
// Act
- var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ var deserialized = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("a", deserialized.ToString ());
@@ -138,7 +138,7 @@ public void Json_With_Decimal_Works ()
var json = "97";
// Act
- var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ var deserialized = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal ("a", deserialized.ToString ());
diff --git a/Tests/UnitTestsParallelizable/Configuration/SchemeJsonConverterTests.cs b/Tests/UnitTestsParallelizable/Configuration/SchemeJsonConverterTests.cs
index 3fa653fa85..e972a38e25 100644
--- a/Tests/UnitTestsParallelizable/Configuration/SchemeJsonConverterTests.cs
+++ b/Tests/UnitTestsParallelizable/Configuration/SchemeJsonConverterTests.cs
@@ -50,11 +50,11 @@ public void TestSchemesSerialization_Equality ()
};
string serializedScheme =
- JsonSerializer.Serialize (expectedScheme, ConfigurationManager.SerializerContext.Options);
+ JsonSerializer.Serialize (expectedScheme, TuiSerializerContext.Instance.Options);
// Act
var actualScheme =
- JsonSerializer.Deserialize (serializedScheme, ConfigurationManager.SerializerContext.Options);
+ JsonSerializer.Deserialize (serializedScheme, TuiSerializerContext.Instance.Options);
// Assert
Assert.Equal (expectedScheme, actualScheme);
@@ -77,8 +77,8 @@ public void TestSchemesSerialization ()
Disabled = new (Color.Gray, Color.Black),
};
- string json = JsonSerializer.Serialize (expected, ConfigurationManager.SerializerContext.Options);
- Scheme? actual = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize (expected, TuiSerializerContext.Instance.Options);
+ Scheme? actual = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
Assert.NotNull (actual);
@@ -109,7 +109,7 @@ public void Deserialized_Attributes_AreExplicitlySet ()
}
""";
- Scheme scheme = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options)!;
+ Scheme scheme = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options)!;
Assert.True (scheme.TryGetExplicitlySetAttributeForRole (VisualRole.Normal, out _));
Assert.True (scheme.TryGetExplicitlySetAttributeForRole (VisualRole.HotNormal, out _));
@@ -132,7 +132,7 @@ public void Deserialized_Attributes_NotSpecified_AreImplicit ()
}
""";
- Scheme scheme = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options)!;
+ Scheme scheme = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options)!;
// explicitly set
Assert.True (scheme.TryGetExplicitlySetAttributeForRole (VisualRole.Normal, out _));
@@ -159,8 +159,8 @@ public void CodeToken_Attributes_RoundTrip ()
CodeKeyword = new (Color.Blue, Color.Black, TextStyle.Bold)
};
- string json = JsonSerializer.Serialize (expected, ConfigurationManager.SerializerContext.Options);
- Scheme? actual = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerContext.Options);
+ string json = JsonSerializer.Serialize (expected, TuiSerializerContext.Instance.Options);
+ Scheme? actual = JsonSerializer.Deserialize (json, TuiSerializerContext.Instance.Options);
Assert.NotNull (actual);
Assert.True (actual.TryGetExplicitlySetAttributeForRole (VisualRole.CodeKeyword, out Attribute? attr));
diff --git a/Tests/UnitTestsParallelizable/Configuration/ScopeJsonConverterTests.cs b/Tests/UnitTestsParallelizable/Configuration/ScopeJsonConverterTests.cs
index 36760eedfd..b6c907962a 100644
--- a/Tests/UnitTestsParallelizable/Configuration/ScopeJsonConverterTests.cs
+++ b/Tests/UnitTestsParallelizable/Configuration/ScopeJsonConverterTests.cs
@@ -17,9 +17,9 @@ public void RoundTripConversion_Property_Positive (string configPropertyJson)
string scopeJson = $"{{{configPropertyJson}}}";
// Act
- SettingsScope? deserialized = JsonSerializer.Deserialize (scopeJson, ConfigurationManager.SerializerContext.Options);
+ SettingsScope? deserialized = JsonSerializer.Deserialize (scopeJson, TuiSerializerContext.Instance.Options);
- string? json = JsonSerializer.Serialize (deserialized!, ConfigurationManager.SerializerContext.Options);
+ string? json = JsonSerializer.Serialize (deserialized!, TuiSerializerContext.Instance.Options);
// Strip all whitespace
json = json.Replace (" ", string.Empty);
@@ -39,7 +39,7 @@ public void RoundTripConversion_JsonIncludeProperty_WithNullValue_WritesNull ()
SettingsScope settingsScope = new () { Schema = null! };
// Act
- string json = JsonSerializer.Serialize (settingsScope, ConfigurationManager.SerializerContext.SettingsScope);
+ string json = JsonSerializer.Serialize (settingsScope, TuiSerializerContext.Instance.SettingsScope);
JsonDocument document = JsonDocument.Parse (json);
// Assert
diff --git a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs
index 1e13c5fd41..bad5ec8d3b 100644
--- a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs
+++ b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs
@@ -29,11 +29,11 @@ public void Load_WithValidStream_UpdatesSettingsScope ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = false;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = false;
var json = """
{
- "Driver.Force16Colors": true
+ "ConfigurationManager.ThrowOnJsonErrors": true
}
""";
var location = ConfigLocations.HardCoded;
@@ -50,7 +50,7 @@ public void Load_WithValidStream_UpdatesSettingsScope ()
// Assert
Assert.True (result);
- Assert.Equal (true, settingsScope ["Driver.Force16Colors"].PropertyValue);
+ Assert.Equal (true, settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue);
Assert.Contains (source, sourcesManager.Sources.Values);
}
@@ -83,11 +83,11 @@ public void Load_WithValidFile_UpdatesSettingsScope ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = false;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = false;
var json = """
{
- "Driver.Force16Colors": true
+ "ConfigurationManager.ThrowOnJsonErrors": true
}
""";
string source = Path.GetTempFileName ();
@@ -102,7 +102,7 @@ public void Load_WithValidFile_UpdatesSettingsScope ()
// Assert
Assert.True (result);
- Assert.Equal (true, settingsScope ["Driver.Force16Colors"].PropertyValue);
+ Assert.Equal (true, settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue);
Assert.Contains (source, sourcesManager.Sources.Values);
}
finally
@@ -172,11 +172,11 @@ public void Load_WithValidJson_UpdatesSettingsScope ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = false;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = false;
var json = """
{
- "Driver.Force16Colors": true
+ "ConfigurationManager.ThrowOnJsonErrors": true
}
""";
var source = "Load_WithValidJson_UpdatesSettingsScope";
@@ -187,7 +187,7 @@ public void Load_WithValidJson_UpdatesSettingsScope ()
// Assert
Assert.True (result);
- Assert.Equal (true, settingsScope ["Driver.Force16Colors"].PropertyValue);
+ Assert.Equal (true, settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue);
Assert.Contains (source, sourcesManager.Sources.Values);
}
@@ -228,7 +228,7 @@ public void Load_WithNullResourceName_ReturnsFalse ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = false;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = false;
var assembly = Assembly.GetExecutingAssembly ();
var location = ConfigLocations.AppResources;
@@ -272,11 +272,11 @@ public void Load_Runtime_Overrides ()
var location = ConfigLocations.LibraryResources;
sourcesManager.Load (settingsScope, assembly!, resourceName, location);
- Assert.Equal (false, settingsScope ["Driver.Force16Colors"].PropertyValue);
+ Assert.Equal (false, settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue);
var runtimeJson = """
{
- "Driver.Force16Colors": true
+ "ConfigurationManager.ThrowOnJsonErrors": true
}
""";
var runtimeSource = "runtime.json";
@@ -294,7 +294,7 @@ public void Load_Runtime_Overrides ()
Assert.True (result);
// Verify settingsScope is updated as expected
- Assert.Equal (true, settingsScope ["Driver.Force16Colors"].PropertyValue);
+ Assert.Equal (true, settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue);
}
#endregion
@@ -308,13 +308,13 @@ public void ToJson_WithValidScope_ReturnsJsonString ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = true;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = true;
// Act
string json = sourcesManager.ToJson (settingsScope);
// Assert
- Assert.Contains ("\"Driver.Force16Colors\": true", json);
+ Assert.Contains ("\"ConfigurationManager.ThrowOnJsonErrors\": true", json);
}
[Fact]
@@ -324,7 +324,7 @@ public void ToStream_WithValidScope_ReturnsStream ()
var sourcesManager = new SourcesManager ();
var settingsScope = new SettingsScope ();
settingsScope.LoadHardCodedDefaults ();
- settingsScope ["Driver.Force16Colors"].PropertyValue = true;
+ settingsScope ["ConfigurationManager.ThrowOnJsonErrors"].PropertyValue = true;
// Act
var stream = sourcesManager.ToStream (settingsScope);
@@ -334,7 +334,7 @@ public void ToStream_WithValidScope_ReturnsStream ()
stream.Position = 0;
StreamReader reader = new (stream);
string json = reader.ReadToEnd ();
- Assert.Contains ("\"Driver.Force16Colors\": true", json);
+ Assert.Contains ("\"ConfigurationManager.ThrowOnJsonErrors\": true", json);
}
#endregion
@@ -361,11 +361,11 @@ public void Load_WhenCalledMultipleTimes_MaintainsLastSourceForLocation ()
// Act - Update with first source for location
var firstSource = "first.json";
- sourcesManager.Load (settingsScope, """{"Driver.Force16Colors": true}""", firstSource, ConfigLocations.Runtime);
+ sourcesManager.Load (settingsScope, """{"ConfigurationManager.ThrowOnJsonErrors": true}""", firstSource, ConfigLocations.Runtime);
// Update with second source for same location
var secondSource = "second.json";
- sourcesManager.Load (settingsScope, """{"Driver.Force16Colors": false}""", secondSource, ConfigLocations.Runtime);
+ sourcesManager.Load (settingsScope, """{"ConfigurationManager.ThrowOnJsonErrors": false}""", secondSource, ConfigLocations.Runtime);
// Assert - Only the last source should be stored for the location
Assert.Single (sourcesManager.Sources);
@@ -385,7 +385,7 @@ public void Load_WithDifferentLocations_AddsAllSourcesToCollection ()
foreach (ConfigLocations location in locations)
{
var source = $"config-{location}.json";
- sourcesManager.Load (settingsScope, """{"Driver.Force16Colors": true}""", source, location);
+ sourcesManager.Load (settingsScope, """{"ConfigurationManager.ThrowOnJsonErrors": true}""", source, location);
}
// Assert
@@ -462,14 +462,14 @@ public void Sources_IsPreservedAcrossOperations ()
// Second operation - json string update
var jsonSource = "jsonstring";
var location2 = ConfigLocations.Runtime;
- sourcesManager.Load (settingsScope, """{"Driver.Force16Colors": true}""", jsonSource, location2);
+ sourcesManager.Load (settingsScope, """{"ConfigurationManager.ThrowOnJsonErrors": true}""", jsonSource, location2);
// Perform a stream operation
var streamSource = "streamdata";
var location3 = ConfigLocations.GlobalCurrent;
var stream = new MemoryStream ();
var writer = new StreamWriter (stream);
- writer.Write ("""{"Driver.Force16Colors": true}""");
+ writer.Write ("""{"ConfigurationManager.ThrowOnJsonErrors": true}""");
writer.Flush ();
stream.Position = 0;
sourcesManager.Load (settingsScope, stream, streamSource, location3);
diff --git a/specs/breaking-changes-cm-mec.md b/specs/breaking-changes-cm-mec.md
new file mode 100644
index 0000000000..24d712a996
--- /dev/null
+++ b/specs/breaking-changes-cm-mec.md
@@ -0,0 +1,175 @@
+# Breaking-Change Analysis: CM → MEC Migration (#5411 vs #5416)
+
+> Companion to [replace-cm-with-mec.md](./replace-cm-with-mec.md).
+> Tracks the umbrella issue [#4943](https://github.com/gui-cs/Terminal.Gui/issues/4943).
+
+This document records what is **actually** breaking in
+[#5411](https://github.com/gui-cs/Terminal.Gui/pull/5411)
+("Refactors `ConfigurationManager` to be based on `MEC`") versus what is deferred
+to its stacked follow-on
+[#5416](https://github.com/gui-cs/Terminal.Gui/pull/5416)
+("Remove legacy `ConfigurationManager` after MEC migration").
+
+The two PRs are intentionally split so the **functional migration** (#5411) and the
+**legacy removal** (#5416) carry different risk profiles.
+
+## TL;DR
+
+| | #5411 | #5416 |
+|---|---|---|
+| Public types removed | **None** | Yes (CM machinery) |
+| Public members removed / renamed / re-signatured | **None** | Yes |
+| Public types newly `[Obsolete]` | **6** (see below) | n/a (they get deleted) |
+| New transitive package dependencies | **4** (Microsoft.Extensions.*) | — |
+| Behavioral change to runtime config | Intended **none** (shims preserved) | Yes (CM-backed paths removed) |
+| Net source-break for an external consumer | Only if they treat **CS0618** as an error | Hard compile breaks |
+
+**Bottom line:** Despite the `BREAKING CHANGE` label, #5411 is **source-compatible**.
+It removes nothing public and changes no public signature. Its only consumer-facing
+surface is **`[Obsolete]` (CS0618) deprecation warnings** plus four new package
+dependencies. All hard, compile-breaking removals live in **#5416**.
+
+---
+
+## What #5411 actually does (verified against the merge with `develop`)
+
+### 1. No public API removed, renamed, or re-signatured
+
+Verified by diffing #5411's net contribution over `develop`:
+
+- **0 files deleted** (`--diff-filter=D`).
+- **0 files renamed** (`--diff-filter=R`).
+- **0 public/protected methods removed.**
+- The `-public ...` lines in the diff are all **auto-properties converted to
+ delegating properties** — e.g. `Glyphs`, `Button.DefaultShadow`,
+ `Driver.SizeDetection`. The signature (`public static Rune File { get; set; }`)
+ is preserved; only the backing implementation moves to a Settings POCO
+ (`GlyphSettings.Defaults.File`, etc.). Get/set behavior is unchanged.
+
+So nothing a consumer *calls* disappears or changes shape in #5411.
+
+### 2. Six public types marked `[Obsolete]`
+
+These remain present and functional, but emit **CS0618** when referenced:
+
+| Type | Replacement guidance in the attribute |
+|---|---|
+| `ConfigurationManager` | Use `TuiConfigurationBuilder` |
+| `ConfigurationPropertyAttribute` | Use Settings POCOs with `TuiConfigurationBuilder` |
+| `ConfigProperty` | Being replaced by MEC |
+| `SettingsScope` | Being replaced by MEC |
+| `ThemeScope` | Being replaced by MEC |
+| `AppSettingsScope` | Use MEC with `TuiConfigurationBuilder` |
+
+`ConfigPropertyHostTypes` is also `[Obsolete]` but is **`internal`** → no consumer impact.
+
+**Still public and NOT obsolete** (the common theming/scheme entry points keep working
+without warnings):
+
+- `ThemeManager` (incl. `Theme`, `Themes`, `ThemeChanged`)
+- `SchemeManager`
+
+#### Important: the in-repo build hides these warnings; external consumers will see them
+
+`.editorconfig` (line ~86) sets `dotnet_diagnostic.cs0618.severity = none`
+**repo-wide** (added Oct 2025 in #4362, *not* by #5411). That is why building
+`Terminal.Gui`, `UICatalog`, and the test projects produces **zero** CS0618 warnings
+even though `UICatalog`/`Runner.cs` calls `ConfigurationManager.Enable/Load/Apply`
+directly.
+
+That suppression only applies to projects **inside this repository**. A downstream
+app consuming the published NuGet package is **not** covered by it and **will** see
+CS0618 deprecation warnings for any use of the six types above. For consumers that
+build with `TreatWarningsAsErrors`, that is effectively a **source break** — the only
+one #5411 can produce. It is easily worked around (suppress CS0618, or migrate to
+`TuiConfigurationBuilder`).
+
+### 3. Four new transitive package dependencies
+
+Added to `Directory.Packages.props` and referenced by `Terminal.Gui`:
+
+- `Microsoft.Extensions.Configuration` (10.0.7)
+- `Microsoft.Extensions.Configuration.Binder` (10.0.7)
+- `Microsoft.Extensions.Configuration.Json` (10.0.7)
+- `Microsoft.Extensions.Options` (10.0.7)
+
+This is not a source break, but it **does** change the dependency closure of anyone
+referencing Terminal.Gui (relevant for size-sensitive / NativeAOT consumers; #5416
+targets AOT-size reduction as follow-up). The MEC binder is reflection-based, so
+#5411 adds `IL2026`/`IL3050` to `NoWarn` and `[UnconditionalSuppressMessage]` on the
+binder paths to keep AOT/trim builds quiet.
+
+### 4. Behavior: intended to be preserved
+
+#5411 keeps the legacy CM paths alive as shims and routes effective configuration
+through MEC. Notable behavior-neutral plumbing:
+
+- `ConfigurationManager.SerializerContext` now delegates to the new non-obsolete
+ `TuiSerializerContext.Instance` (literally the same readonly instance).
+- A new `Terminal.Gui.Configuration.ThemeChanges` facade bridges
+ `ConfigurationManager.Applied` and `ThemeManager.ThemeChanged` into one event;
+ `Menu`, `MenuBar`, `StatusBar`, and `LineCanvas` subscribe to it instead of
+ `ConfigurationManager.Applied` (which stays public + obsolete for external use).
+- `IThemeManager`/`ISchemeManager` interfaces + `Mec*` implementations are added
+ (additive).
+
+Validated locally: full solution build clean; the `Configuration` (230) and
+`CheckBox` (36) parallelizable suites pass, including develop's
+`Default_CheckState_Glyphs_Are_Distinct`.
+
+> ⚠️ One thing that *looks* like a #5411 behavior change but is **not**: the default
+> checkbox glyphs changed `☒→☑` and `□→⬛`. That came from `develop` commit
+> `bbd16ad1e` ("Update default checkbox state glyphs"), surfaced here only because the
+> merge had to reconcile it with #5411's POCO delegation. See the merge notes below.
+
+---
+
+## What is deferred to #5416 (the real removals)
+
+#5416 is stacked on #5411's branch and is where the hard breaks land:
+
+- **Deletes / strips the legacy CM machinery** — e.g. `Glyphs.cs` loses ~1260 lines
+ (the `[ConfigurationProperty]` attribute surface), `ConfigPropertyHostTypes` is
+ removed, and `[ConfigurationProperty]` attributes are stripped from the Settings
+ POCOs and view defaults.
+- Reworks theme/scheme **data ownership** into MEC (`ThemeDefinition`, theme overlay
+ merge), which requires the `config.json` shape decision that #5411 deliberately did
+ not make.
+- Ships a `Tools/MigrateConfig` utility to convert existing `config.json` files —
+ acknowledgement that #5416 is where the **on-disk config format** can break.
+- Targets NativeAOT size reduction (#4367).
+
+In short: **#5416 removes the obsolete types and the `[ConfigurationProperty]`
+contract** that #5411 only deprecated. Code that merely produced CS0618 warnings under
+#5411 will **fail to compile** under #5416, and custom `config.json` files may need the
+migration tool.
+
+---
+
+## Recommended messaging for the #5411 changelog
+
+- "Marks `ConfigurationManager` and its scope/attribute types `[Obsolete]`; introduces
+ `TuiConfigurationBuilder` and Settings POCOs as the replacement. **No public APIs are
+ removed in this release** — existing code keeps compiling (you may see CS0618
+ deprecation warnings). Adds `Microsoft.Extensions.Configuration` dependencies.
+ Legacy `ConfigurationManager` removal and `config.json` format changes follow in a
+ later release (#5416)."
+
+---
+
+## Appendix: merge-conflict resolution with `develop`
+
+Merging `develop` (101 commits ahead) into #5411 produced three conflicts, all
+resolved in favor of #5411's architecture while preserving develop's intent:
+
+1. **`App/Application.cs`** — kept develop's reordered global usings; re-wrapped the
+ now-obsolete `global using CM = ConfigurationManager` alias in
+ `#pragma warning disable/restore CS0618`.
+2. **`Configuration/ConcurrentDictionaryJsonConverter.cs`** — kept #5411's
+ `TuiSerializerContext.Instance` (Phase C extraction) with develop's correct spacing
+ (dropped the bot-introduced no-space formatting).
+3. **`Drawing/Glyphs.cs`** — kept #5411's POCO-delegation for `CheckStateChecked` /
+ `CheckStateNone`, and **moved develop's new glyph values (`☑`, `⬛`) into
+ `GlyphSettings.cs`** so the static defaults — and develop's
+ `Default_CheckState_Glyphs_Are_Distinct` test — produce the new glyphs.
+ `config.json` (the runtime source of truth) already carried `☑`/`⬛` from develop.
diff --git a/specs/replace-cm-with-mec.md b/specs/replace-cm-with-mec.md
new file mode 100644
index 0000000000..67d5d7af18
--- /dev/null
+++ b/specs/replace-cm-with-mec.md
@@ -0,0 +1,1352 @@
+# Spec: Replace ConfigurationManager (CM) with Microsoft.Extensions.Configuration (MEC)
+
+> **Status:** In Progress — MEC migration is implemented; current focus is finishing complex-type migration in PR #5411.
+> **Tracking Issue:** [#4943](https://github.com/gui-cs/Terminal.Gui/issues/4943)
+> **Related analysis:** See [@tig's AOT binary-size comment](https://github.com/gui-cs/Terminal.Gui/issues/4943#issuecomment-XXXXXX)
+
+---
+
+## Table of Contents
+
+1. [Background and Motivation](#1-background-and-motivation)
+2. [Constitution Alignment](#2-constitution-alignment)
+3. [Current CM — Complete Use Case Inventory](#3-current-cm--complete-use-case-inventory)
+4. [Current CM — Architecture Summary](#4-current-cm--architecture-summary)
+5. [Proposed MEC-Based Architecture](#5-proposed-mec-based-architecture)
+6. [Use Case → MEC Mapping](#6-use-case--mec-mapping)
+7. [Functionality Changes Requiring Explicit Debate](#7-functionality-changes-requiring-explicit-debate)
+8. [Implementation Phases](#8-implementation-phases)
+9. [Open Questions](#9-open-questions)
+10. [Out of Scope](#10-out-of-scope)
+
+---
+
+## 1. Background and Motivation
+
+### 1.1 What Is ConfigurationManager (CM)?
+
+`ConfigurationManager` is a static, process-wide configuration subsystem introduced in Terminal.Gui v1 and carried forward into v2. It allows Terminal.Gui library components and application developers to:
+
+- Define configurable properties via the `[ConfigurationProperty]` attribute on static properties.
+- Load layered JSON configuration from up to 9 sources (hard-coded defaults → embedded resources → home-directory files → current-directory files → environment variable → in-memory runtime string).
+- Apply loaded configuration back to those static properties.
+- Define and switch named **Themes**, each containing **Schemes** (color/attribute sets) and per-component visual defaults.
+- Subscribe to `Updated` and `Applied` events to react to live configuration changes.
+
+### 1.2 Why Replace CM?
+
+Three concrete drivers, ordered by urgency:
+
+#### Driver 1 — AOT Binary Size (Highest Urgency)
+
+Analysis of the NativeAot example (PR #5243) shows that a simple login-form app produces an **11.8 MB** native binary. CM is the primary culprit:
+
+| Anti-trimming pattern | Location | Impact |
+|---|---|---|
+| `[DynamicDependency]` on 29 types | `ConfigPropertyHostTypes.cs:66-94` | Forces linker to preserve all public properties of all 29 types, even those unused by the app |
+| `Assembly.GetTypes()` reflection scan | `ConfigProperty.cs` | Prevents any type from being trimmed; worst-case AOT pattern |
+| `Activator.CreateInstance` | `DeepCloner.cs` | Prevents trimming of cloned types |
+| `MakeGenericType` | `DeepCloner.cs`, `ScopeJsonConverter.cs` | Prevents trimming of generic instantiations |
+| ~188 `[ConfigurationProperty]` attributes | Throughout codebase (144 on `Glyphs` alone) | Each roots a property |
+
+Disabling CM at runtime (`ConfigurationManager.IsEnabled == false`) has **zero impact on binary size** because the rooting annotations are compile-time, not runtime.
+
+#### Driver 2 — Test Parallelization (High Urgency)
+
+CM uses static state (`_settings`, `_enabled`, `ThemeManager.Theme`, `SchemeManager.Schemes`, etc.). Tests that touch CM cannot run in parallel. This limits `Tests/UnitTestsParallelizable` coverage significantly. Every component whose behavior is controlled by a `[ConfigurationProperty]` static property is a hidden parallelization barrier.
+
+#### Driver 3 — Modern .NET Alignment (Moderate Urgency)
+
+The .NET ecosystem has converged on `Microsoft.Extensions.Configuration` + `Microsoft.Extensions.Options` (`IOptions`, `IOptionsMonitor`) as the standard configuration pattern since .NET Core 2.0. Terminal.Gui's bespoke CM is unfamiliar to modern .NET developers and duplicates a mature system already in the ecosystem.
+
+---
+
+## 2. Constitution Alignment
+
+This proposal must be evaluated against the Terminal.Gui [constitution](./constitution.md). Key tenet checks:
+
+### ✅ Testability First
+
+> *Views must be testable in isolation without global state.*
+
+**Current CM violates this tenet.** Static `[ConfigurationProperty]` properties make all 29 host types (and all views they affect) globally stateful. Replacing CM with `IOptions` / `IOptionsMonitor` passed via constructor or property injection allows each test to create an isolated configuration snapshot.
+
+### ✅ Performance Is a Feature
+
+> *We never accept regressions in the hot path.*
+
+CM adds reflection overhead at module-init time (`Assembly.GetTypes()`, deep-clone of all properties). MEC configuration is loaded once into POCOs and accessed via lightweight interface calls. The hot rendering path reads a simple `Rune` field or `ShadowStyle` enum — this **must not change**.
+
+### ✅ Users Have Final Control
+
+> *Everything configurable must be configurable.*
+
+MEC retains full configurability. The same JSON files remain valid. The precedence model survives. No configuration capability is removed (see §7 for the one debatable exception).
+
+### ✅ Separation of Concerns
+
+> *Layout, focus, input, and drawing are cleanly decoupled.*
+
+Currently, View subclasses have static `[ConfigurationProperty]` properties entangled with CM lifecycle. MEC decouples them: views hold a value and receive updates; configuration loading and persistence is someone else's job.
+
+### ✅ Respect What Came Before
+
+> *Appreciate existing systems; learn from the past.*
+
+CM served Terminal.Gui well for multiple years. This spec proposes preserving the **external JSON format** unchanged and the **same 9-level precedence model** so that users' existing config files continue to work without modification.
+
+### 🔴 Documentation Is the Spec
+
+> *API documentation is the contract. When docs and code conflict, the code is wrong.*
+
+This spec **is** the new contract. `docfx/docs/config.md` must be updated to match before any implementation ships.
+
+---
+
+## 3. Current CM — Complete Use Case Inventory
+
+This section is the authoritative catalog of everything CM does. Every use case listed here must be supported by the MEC replacement — either equivalently or with an explicit decision (documented in §7) to change or remove it.
+
+---
+
+### UC-01: Library Developer Defines a SettingsScope Property
+
+**What:** A Terminal.Gui library developer marks a static property with `[ConfigurationProperty(Scope = typeof(SettingsScope))]` to make it configurable via `config.json`.
+
+**Who does this:** Library developers only (not app developers).
+
+**Current API:**
+```csharp
+// In Application.cs
+[ConfigurationProperty(Scope = typeof(SettingsScope))]
+public static string ForceDriver { get; set; } = string.Empty;
+
+// In Driver.cs
+[ConfigurationProperty(Scope = typeof(SettingsScope))]
+public static bool Force16Colors { get; set; } = false;
+
+// In View.Keyboard.cs
+[ConfigurationProperty(Scope = typeof(SettingsScope))]
+public static Dictionary? DefaultKeyBindings { get; set; }
+```
+
+**JSON representation:**
+```json
+{
+ "Application.ForceDriver": "ansi",
+ "Driver.Force16Colors": true
+}
+```
+
+**Current behavior:** CM discovers these at module-init via `ConfigProperty.Initialize()`, stores their current values as hard-coded defaults, and applies loaded JSON values back to the static properties during `Apply()`.
+
+**Full list of SettingsScope properties (as of writing):**
+- `Application.AppModel`
+- `Application.ForceDriver`
+- `Application.DefaultKeyBindings`
+- `Application.IsMouseDisabled`
+- `Color.Colors16` *(OmitClassName — maps ColorName16 to string)*
+- `ConfigurationManager.ThrowOnJsonErrors`
+- `Driver.Force16Colors`
+- `Driver.SizeDetection`
+- `Key.Separator`
+- `MenuBar.DefaultKey` *(F10)*
+- `PopoverMenu.DefaultKey`
+- `FileDialog.DefaultOpenMode`
+- `FileDialogStyle.DefaultSearchMatcher`
+- `FileDialogStyle.PreferResultFromOpenMode`
+- `Trace.EnabledCategories`
+- `View.DefaultKeyBindings` *(keyboard bindings for all Views)*
+- `View.ViewKeyBindings` *(per-View-type keyboard bindings)*
+
+---
+
+### UC-02: Library Developer Defines a ThemeScope Property
+
+**What:** A Terminal.Gui library developer marks a static property with `[ConfigurationProperty(Scope = typeof(ThemeScope))]` to make it part of the theme system — its value can vary per named theme.
+
+**Who does this:** Library developers only (not app developers).
+
+**Current API:**
+```csharp
+// In Button.cs
+[ConfigurationProperty(Scope = typeof(ThemeScope))]
+public static ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Opaque;
+
+[ConfigurationProperty(Scope = typeof(ThemeScope))]
+public static MouseState DefaultMouseHighlightStates { get; set; } = MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
+
+// In Glyphs.cs (144 of these)
+[ConfigurationProperty(Scope = typeof(ThemeScope))]
+public static Rune CheckStateChecked { get; set; } = (Rune)'☑';
+```
+
+**JSON representation (inside a theme):**
+```json
+{
+ "Themes": [
+ {
+ "Default": {
+ "Button.DefaultShadow": "Opaque",
+ "Glyphs.CheckStateChecked": "☑"
+ }
+ }
+ ]
+}
+```
+
+**Full list of ThemeScope property host types (as of writing):**
+- `Glyphs` (~130+ glyph Runes: CheckStateChecked, CheckStateUnChecked, CheckStateNone, all border/line drawing runes, all scrollbar runes, all arrow glyphs, FileOpen, HorizontalEllipsis, etc.)
+- `Button.DefaultShadow`, `Button.DefaultMouseHighlightStates`
+- `CheckBox.DefaultMouseHighlightStates`
+- `Dialog.DefaultShadow`, `Dialog.DefaultBorderStyle`, `Dialog.DefaultButtonAlignment`, `Dialog.DefaultButtonAlignmentModes`
+- `FrameView.DefaultBorderStyle`
+- `HexView.DefaultBorderStyle`
+- `Menu.DefaultBorderStyle`
+- `MenuBar.DefaultBorderStyle`
+- `MessageBox.DefaultBorderStyle`, `MessageBox.DefaultButtonAlignment`
+- `NerdFonts.Enable`
+- `SelectorBase.DefaultBorderStyle`
+- `StatusBar.DefaultBorderStyle`
+- `TextField.DefaultBorderStyle`
+- `TextView.DefaultBorderStyle`
+- `Window.DefaultBorderStyle`, `Window.DefaultShadow`
+- `CharMap.DefaultBorderStyle`
+- `LinearRangeDefaults.*`
+- `SchemeManager.Schemes` *(the color/attribute dictionary for the current theme)*
+
+---
+
+### UC-03: App Developer Defines an AppSettingsScope Property
+
+**What:** An application developer marks a static property with `[ConfigurationProperty]` (defaulting to `AppSettingsScope`) so that users can configure it via `AppName.config.json`.
+
+**Who does this:** App developers.
+
+**Current API:**
+```csharp
+// In a UICatalog class
+[ConfigurationProperty] // implies AppSettingsScope
+public static bool ShowStatusBar { get; set; } = true;
+```
+
+**JSON representation:**
+```json
+{
+ "AppSettings": {
+ "UICatalog.ShowStatusBar": true
+ }
+}
+```
+
+**Constraint:** OmitClassName is **not** allowed for `AppSettingsScope` to ensure globally unique keys. The property name in JSON is always `ClassName.PropertyName`.
+
+---
+
+### UC-04: Enable Configuration Loading
+
+**What:** CM is disabled by default. Hard-coded defaults are always in effect. An app must explicitly call `Enable()` to load from any source.
+
+**Current API:**
+```csharp
+// Typical usage (before Application.Init):
+ConfigurationManager.Enable(ConfigLocations.All);
+
+// Enable without loading any sources (just enables the system):
+ConfigurationManager.Enable(ConfigLocations.None);
+
+// Enable with only hard-coded defaults (same as hard-coded):
+ConfigurationManager.Enable(ConfigLocations.HardCoded);
+```
+
+**Notes:**
+- `IsEnabled` returns `false` until `Enable()` is called.
+- Most framework behaviors are available even when CM is disabled (via hard-coded defaults).
+- `Apply()` and `Load()` throw `ConfigurationManagerNotEnabledException` if CM is not enabled.
+
+---
+
+### UC-05: Load from 9-Level Precedence Stack
+
+**What:** Load configuration from all standard sources in a defined precedence order. Higher-precedence sources override lower ones.
+
+**Current API:**
+```csharp
+ConfigurationManager.Load(ConfigLocations.All); // Load all 9 levels
+ConfigurationManager.Load(ConfigLocations.GlobalHome | ConfigLocations.AppCurrent); // Selective
+```
+
+**Precedence order (lowest to highest):**
+
+| Level | `ConfigLocations` flag | Source | Example path |
+|-------|------------------------|--------|--------------|
+| 1 | `HardCoded` | Static property initializers in code | n/a |
+| 2 | `LibraryResources` | Embedded in `Terminal.Gui.dll` | `Terminal.Gui.Resources.config.json` |
+| 3 | `AppResources` | Embedded in the app assembly | `MyApp.Resources.config.json` |
+| 4 | `GlobalHome` | Global file in user home | `~/.tui/config.json` |
+| 5 | `GlobalCurrent` | Global file in current dir | `./.tui/config.json` |
+| 6 | `AppHome` | App file in user home | `~/.tui/MyApp.config.json` |
+| 7 | `AppCurrent` | App file in current dir | `./.tui/MyApp.config.json` |
+| 8 | `Env` | `TUI_CONFIG` environment variable (JSON string) | `export TUI_CONFIG='{"Key.Separator":"+"}'` |
+| 9 | `Runtime` | `ConfigurationManager.RuntimeConfig` in-memory string | Set programmatically |
+
+**Notes:**
+- Sources at the same precedence level that don't exist (missing file) are silently skipped.
+- JSON parsing errors are collected in `_jsonErrors` by default (not thrown) unless `ThrowOnJsonErrors` is set.
+- `AppName` (defaults to entry assembly name) is used to construct per-app file names.
+- The `TUI_CONFIG` environment variable value is a raw JSON fragment (same format as a config file).
+
+---
+
+### UC-06: Apply Configuration to Static Properties
+
+**What:** After loading, apply the merged configuration to the static `[ConfigurationProperty]` properties.
+
+**Current API:**
+```csharp
+ConfigurationManager.Apply(); // throws if not enabled
+```
+
+**Notes:**
+- `Apply()` copies the loaded values back to the static properties.
+- Fires `Applied` event when any value changed.
+- Calls `ThemeManager.Themes[Theme].Apply()` and `AppSettings.Apply()` internally.
+
+---
+
+### UC-07: Reset to Hard-Coded Defaults
+
+**What:** Restore all `[ConfigurationProperty]` static properties to their initial hard-coded values (as they were when the module loaded).
+
+**Current API:**
+```csharp
+ConfigurationManager.Disable(resetToHardCodedDefaults: true);
+// OR:
+ConfigurationManager.Enable(ConfigLocations.HardCoded);
+```
+
+**Notes:**
+- Hard-coded defaults are captured once at module initialization via `ConfigProperty.GetAllConfigProperties()`.
+- A deep clone of each property's initial value is preserved in `_hardCodedConfigPropertyCache`.
+- Resetting clears `RuntimeConfig` and `SourcesManager.Sources`.
+
+---
+
+### UC-08: Runtime / Programmatic Configuration Override
+
+**What:** Override configuration at runtime via an in-memory JSON string, without touching any files. This is the highest-precedence source (level 9).
+
+**Current API:**
+```csharp
+// Override a setting programmatically (used in UICatalog's Runner for force-driver):
+ConfigurationManager.RuntimeConfig = """
+ {
+ "Application.ForceDriver": "ansi",
+ "Driver.Force16Colors": true
+ }
+ """;
+
+ConfigurationManager.Load(ConfigLocations.All);
+ConfigurationManager.Apply();
+```
+
+**Use cases in the codebase:**
+- `UICatalog/Runner.cs`: Sets `ForceDriver` and `Force16Colors` from command-line arguments.
+- Tests: Used to configure specific settings without requiring files on disk.
+
+---
+
+### UC-09: Define and Apply a Theme
+
+**What:** A theme is a named collection of `ThemeScope` properties (visual settings + color schemes). Terminal.Gui ships several built-in themes in its embedded `config.json`.
+
+**Current API:**
+```csharp
+// Get current theme
+ThemeScope currentTheme = ThemeManager.GetCurrentTheme();
+
+// Get all theme names
+ImmutableList names = ThemeManager.GetThemeNames();
+
+// Switch themes (apply immediately after)
+ThemeManager.Theme = "Dark";
+ConfigurationManager.Apply();
+
+// Access themes dictionary
+ConcurrentDictionary allThemes = ThemeManager.Themes!;
+```
+
+**Built-in themes (from `Terminal.Gui/Resources/config.json`):**
+- Default
+- Dark
+- Light
+- TurboPascal 5
+- Anders
+- Green Phosphor
+- Amber Phosphor
+
+**JSON format:**
+```json
+{
+ "Theme": "Dark",
+ "Themes": [
+ {
+ "Dark": {
+ "Button.DefaultShadow": "None",
+ "Window.DefaultBorderStyle": "Heavy",
+ "Schemes": [
+ { "Base": { "Normal": { "Foreground": "White", "Background": "Black" } } }
+ ]
+ }
+ }
+ ]
+}
+```
+
+---
+
+### UC-10: Subscribe to Theme Change Events
+
+**What:** Components and applications can subscribe to events that fire when the theme changes or when configuration is applied.
+
+**Current API:**
+```csharp
+// Fires when a new theme has been selected
+ThemeManager.ThemeChanged += (sender, e) => {
+ // e.Value is the new theme name
+ RefreshUI();
+};
+
+// Fires when configuration has been loaded (not yet applied)
+ConfigurationManager.Updated += (sender, e) => {
+ LogMessage("Config updated");
+};
+
+// Fires when configuration has been applied to static properties
+ConfigurationManager.Applied += (sender, e) => {
+ // In UICatalogRunnable: re-read all static properties and refresh
+ RefreshUI();
+};
+```
+
+---
+
+### UC-11: Define Color Schemes
+
+**What:** A Scheme maps `VisualRole`s (Normal, Focus, HotNormal, HotFocus, Disabled, Code) to `Attribute`s (Foreground + Background + Style). Schemes are part of the current theme.
+
+**Current API:**
+```csharp
+// Access built-in scheme
+Scheme baseScheme = SchemeManager.GetScheme(Schemes.Base);
+Scheme menuScheme = SchemeManager.GetScheme("Menu");
+
+// Try-get (safe, no exceptions)
+if (SchemeManager.TryGetScheme("MyCustomScheme", out Scheme? scheme)) { }
+
+// Get all scheme names
+ImmutableList names = SchemeManager.GetSchemeNames();
+
+// Get all schemes for current theme
+Dictionary all = SchemeManager.GetSchemesForCurrentTheme();
+
+// Add custom scheme
+SchemeManager.AddScheme("MyScheme", new Scheme {
+ Normal = new Attribute(Color.White, Color.Black),
+ Focus = new Attribute(Color.Black, Color.White),
+ // ...
+});
+
+// Remove custom scheme (built-ins cannot be removed)
+SchemeManager.RemoveScheme("MyScheme");
+```
+
+**Built-in scheme names (from `Schemes` enum):**
+- Base, Dialog, Menu, Error, Accent, Toplevel, View, Highlight
+
+**JSON representation:**
+```json
+{
+ "Schemes": [
+ {
+ "Base": {
+ "Normal": { "Foreground": "White", "Background": "Black" },
+ "Focus": { "Foreground": "Black", "Background": "White" },
+ "HotNormal": { "Foreground": "Yellow", "Background": "Black" },
+ "HotFocus": { "Foreground": "Yellow", "Background": "White" },
+ "Disabled": { "Foreground": "Gray", "Background": "Black" },
+ "Code": { "Foreground": "Green", "Background": "Black" }
+ }
+ }
+ ]
+}
+```
+
+---
+
+### UC-12: Assign Scheme to a View
+
+**What:** Each View has a `SchemeName` property. When `SchemeName` is set, the View uses the named Scheme for rendering.
+
+**Current API:**
+```csharp
+// Assign scheme by name
+myDialog.SchemeName = SchemeManager.SchemesToSchemeName(Schemes.Dialog);
+
+// Assign via enum (resolves to "Dialog")
+myMenuBar.SchemeName = SchemeManager.SchemesToSchemeName(Schemes.Menu);
+
+// Views inherit scheme from SuperView if SchemeName is null
+```
+
+---
+
+### UC-13: Access Hard-Coded Configuration
+
+**What:** Get the hard-coded default config values as a JSON string, or get an empty config template.
+
+**Current API:**
+```csharp
+// Get empty config (just the schema URL)
+string emptyJson = ConfigurationManager.GetEmptyConfig();
+
+// Get hard-coded defaults as JSON
+string hardCodedJson = ConfigurationManager.GetHardCodedConfig();
+```
+
+**Notes:**
+- `GetHardCodedConfig()` is used by UICatalog's "Save Defaults" test to regenerate `config.json`.
+- Used in tooling to scaffold configuration files.
+
+---
+
+### UC-14: Embedded Library Resource Configuration
+
+**What:** Terminal.Gui ships its own `Terminal.Gui.Resources.config.json` embedded in the DLL. This file defines the built-in themes and sets the source-of-truth defaults for all `[ConfigurationProperty]` static properties.
+
+**Notes:**
+- This file is **the canonical definition** of built-in themes like "Dark", "Light", "TurboPascal 5", etc.
+- It is 1,501 lines of JSON and contains all scheme definitions for all built-in themes.
+- Loading this file is `ConfigLocations.LibraryResources` (precedence level 2).
+- Changes to this file must be reflected in `config.json` and vice versa — the `SaveDefaults` test enforces this.
+
+---
+
+### UC-15: Embedded App Resource Configuration
+
+**What:** Application developers can embed a `config.json` (or `Resources/config.json`) in their app assembly to provide app-specific defaults. This is `ConfigLocations.AppResources` (precedence level 3).
+
+**Notes:**
+- Discovered by scanning the entry assembly's manifest resource names for anything ending in `config.json`.
+- Loaded before any user-level files, so user preferences can still override app defaults.
+
+---
+
+### UC-16: Global User Configuration Files
+
+**What:** Users place global configuration in `~/.tui/config.json` (`GlobalHome`) or `./.tui/config.json` (`GlobalCurrent`). These affect all Terminal.Gui apps the user runs.
+
+**Current behavior:**
+- `SourcesManager` expands `~` to `Environment.GetFolderPath(SpecialFolder.UserProfile)`.
+- Missing files are silently skipped.
+- Cross-platform: Works on Windows, macOS, Linux.
+
+---
+
+### UC-17: Per-App User Configuration Files
+
+**What:** Users can place app-specific configuration in `~/.tui/AppName.config.json` (`AppHome`) or `./.tui/AppName.config.json` (`AppCurrent`). `AppName` defaults to the entry assembly's simple name.
+
+**Notes:**
+- `ConfigurationManager.AppName` can be set before `Enable()` to override the default.
+- Useful for multi-runnable apps (e.g. UICatalog hosting sub-scenarios with distinct names).
+
+---
+
+### UC-18: Environment Variable Configuration
+
+**What:** The `TUI_CONFIG` environment variable can contain a JSON fragment that overrides configuration. Useful for CI/CD, containers, and test harnesses.
+
+**Current behavior:**
+```bash
+export TUI_CONFIG='{"Application.ForceDriver": "NetDriver", "Driver.Force16Colors": true}'
+```
+
+- The environment variable value is treated as a raw JSON config document.
+- It is at precedence level 8 (overrides all file-based sources, but can be overridden by `RuntimeConfig`).
+
+---
+
+### UC-19: JSON Error Handling
+
+**What:** By default, JSON parsing errors are collected (not thrown) and can be reported after the fact. In test/debug mode, errors can be set to throw exceptions.
+
+**Current API:**
+```csharp
+// Default: collect errors, print them when Application shuts down
+ConfigurationManager.PrintJsonErrors();
+
+// Test/debug: throw on first error
+ConfigurationManager.ThrowOnJsonErrors = true;
+
+// Check if any errors occurred
+bool hasErrors = ConfigurationManager._jsonErrors.Length > 0;
+```
+
+---
+
+### UC-20: Custom JSON Converters for Terminal.Gui Types
+
+**What:** CM uses custom `System.Text.Json` converters for Terminal.Gui types that don't map directly to JSON primitives.
+
+**Converters:**
+| Converter | Handles | Example JSON |
+|---|---|---|
+| `RuneJsonConverter` | `Rune` (Unicode code point) | `"☑"`, `"U+2611"`, `97` |
+| `KeyJsonConverter` | `Key` (key binding) | `"Ctrl+Q"`, `"F1"` |
+| `ColorJsonConverter` | `Color` | `"White"`, `"#RRGGBB"`, `42` |
+| `AttributeJsonConverter` | `Attribute` (fg+bg pair) | `{ "Foreground": "White", "Background": "Black" }` |
+| `SchemeJsonConverter` | `Scheme` | Nested scheme object |
+| `DictionaryJsonConverter` | Dictionaries | Theme scheme arrays |
+| `ConcurrentDictionaryJsonConverter` | Themes dict | Theme array → dict |
+| `ScopeJsonConverter` | All Scope types | Flat key-value to typed properties |
+| `KeyArrayJsonConverter` | `Key[]` | Array of key strings |
+| `KeyCodeJsonConverter` | `KeyCode` | Numeric key code |
+| `TraceCategoryJsonConverter` | `TraceCategory` | Trace category flags |
+
+---
+
+### UC-21: AOT / Trim Compatibility
+
+**What:** CM must work in `PublishTrimmed=true` and `PublishAot=true` builds, which is Terminal.Gui's current requirement.
+
+**Current mechanism:**
+- `ConfigPropertyHostTypes.GetTypes()` enumerates all 29 host types.
+- Each type is decorated with `[DynamicDependency(PublicProperties, typeof(...))]` to prevent trimming.
+- `SourceGenerationContext` provides STJ source-generation for all known types.
+- `DeepCloner` is the residual reflection hotspot (multiple `[UnconditionalSuppressMessage]` annotations are required).
+
+---
+
+### UC-22: Disable CM Without Resetting
+
+**What:** CM can be disabled without resetting to hard-coded defaults (e.g., to prevent further loads while preserving the currently applied state).
+
+**Current API:**
+```csharp
+ConfigurationManager.Disable(); // disable, keep current static property values
+ConfigurationManager.Disable(resetToHardCodedDefaults: true); // disable and reset
+```
+
+---
+
+### UC-23: Get/Set `AppName`
+
+**What:** `ConfigurationManager.AppName` is the name used for per-app config files. Defaults to entry assembly name. Can be overridden before `Enable()`.
+
+**Current API:**
+```csharp
+ConfigurationManager.AppName = "MyCustomAppName";
+ConfigurationManager.Enable(ConfigLocations.All);
+```
+
+---
+
+### UC-24: App Developer `Updated` / `Applied` Event Subscriptions
+
+**What:** Application developers subscribe to CM events to react to configuration changes (e.g., after theme switch, re-read all theme-affected views).
+
+**Current API:**
+```csharp
+// UICatalogRunnable:
+ConfigurationManager.Applied += ConfigAppliedHandler;
+// ...
+ConfigurationManager.Applied -= ConfigAppliedHandler;
+```
+
+---
+
+### UC-25: Reload Configuration (Live Reload)
+
+**What:** After `Enable()`, an app can call `Load()` again to reload from sources and then `Apply()` to push changes to the application. Used in UICatalog's theme switcher.
+
+**Current API:**
+```csharp
+// After user selects "Dark" theme:
+ThemeManager.Theme = "Dark";
+ConfigurationManager.Load(ConfigLocations.All);
+ConfigurationManager.Apply();
+```
+
+---
+
+### UC-26: Custom Schemes (App-Developer Defined)
+
+**What:** App developers can add custom named Schemes beyond the built-in ones.
+
+**Current API:**
+```csharp
+SchemeManager.AddScheme("MyCustomScheme", new Scheme {
+ Normal = new Attribute(Color.BrightWhite, Color.DarkBlue),
+ Focus = new Attribute(Color.Black, Color.BrightYellow),
+ HotNormal = new Attribute(Color.BrightYellow, Color.DarkBlue),
+ HotFocus = new Attribute(Color.BrightYellow, Color.BrightYellow),
+ Disabled = new Attribute(Color.Gray, Color.DarkBlue)
+});
+
+// Assign to view
+myView.SchemeName = "MyCustomScheme";
+```
+
+---
+
+### UC-27: Access `SourcesManager.Sources` (Diagnostics)
+
+**What:** `SourcesManager.Sources` is a `ConcurrentDictionary` showing which files/resources were actually loaded. Used for diagnostics and "config editor" scenarios.
+
+**Current API:**
+```csharp
+foreach (var (location, path) in ConfigurationManager.SourcesManager!.Sources)
+{
+ Console.WriteLine($"{location}: {path}");
+}
+```
+
+---
+
+### UC-28: Serialization / Export Configuration
+
+**What:** The current configuration (or a specific scope) can be serialized to JSON for export, editing, or display.
+
+**Current API:**
+```csharp
+// Serialize the current settings scope to JSON
+string json = ConfigurationManager.SourcesManager!.ToJson(ConfigurationManager.Settings);
+
+// Get hard-coded defaults as JSON
+string defaults = ConfigurationManager.GetHardCodedConfig();
+```
+
+---
+
+## 4. Current CM — Architecture Summary
+
+```
+Module Init (once, process-wide)
+ └─ ConfigProperty.Initialize()
+ └─ Scan all types in ConfigPropertyHostTypes.GetTypes()
+ └─ Reflect on all [ConfigurationProperty] static properties
+ └─ Cache hard-coded values (deep-cloned)
+
+Enable(locations)
+ └─ SourcesManager.LoadFromLocations(Settings, locations)
+ └─ Load LibraryResources ──┐
+ └─ Load AppResources │ Each is JSON → SettingsScope merge
+ └─ Load GlobalHome │ Higher precedence overwrites lower
+ └─ Load GlobalCurrent │
+ └─ Load AppHome │
+ └─ Load AppCurrent │
+ └─ Load Env │
+ └─ Load Runtime ─┘
+ └─ InternalApply()
+ └─ Settings.Apply() → writes to all [CP(SettingsScope)] static props
+ └─ ThemeManager.Themes[Theme].Apply() → writes to all [CP(ThemeScope)] static props
+ └─ AppSettings.Apply() → writes to all [CP(AppSettingsScope)] static props
+ └─ OnApplied() → fires Applied event
+```
+
+**Key structural characteristics:**
+1. **Process-wide global state** — all state is in static fields.
+2. **Attribute-based discovery** — `[ConfigurationProperty]` decorates static properties; CM finds them all via reflection at init.
+3. **Push model** — loaded values are "pushed" to static properties during `Apply()`.
+4. **Scope model** — three orthogonal scopes: SettingsScope, ThemeScope, AppSettingsScope.
+5. **Theme layering** — ThemeScope properties are per-theme; switching themes means applying a different `ThemeScope` instance to the same static properties.
+
+---
+
+## 5. Proposed MEC-Based Architecture
+
+This section describes the target architecture. It is a **proposal requiring review**, not a final decision.
+
+### 5.1 Core Concepts
+
+#### A. Replace Static Properties with POCOs + Options
+
+Each group of related configuration properties becomes a POCO ("Settings class"):
+
+```csharp
+// Terminal.Gui/Configuration/Settings/ButtonSettings.cs
+public class ButtonSettings
+{
+ public ShadowStyles DefaultShadow { get; set; } = ShadowStyles.Opaque;
+ public MouseState DefaultMouseHighlightStates { get; set; } =
+ MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
+}
+
+// Terminal.Gui/Configuration/Settings/GlyphsSettings.cs
+public class GlyphsSettings
+{
+ public Rune CheckStateChecked { get; set; } = (Rune)'☑';
+ public Rune CheckStateUnChecked { get; set; } = (Rune)'☐';
+ // ... all 144 glyph properties
+}
+
+// Terminal.Gui/Configuration/Settings/ApplicationSettings.cs
+public class ApplicationSettings
+{
+ public AppModel AppModel { get; set; } = AppModel.FullScreen;
+ public string ForceDriver { get; set; } = string.Empty;
+ public bool IsMouseDisabled { get; set; } = false;
+ // ...
+}
+```
+
+#### B. MEC Replaces SourcesManager
+
+`Microsoft.Extensions.Configuration` (`IConfiguration`) provides the same 9-level precedence stack through composable configuration providers:
+
+| Current `ConfigLocations` | MEC Provider / Extension |
+|---|---|
+| `HardCoded` | POCO default property values (no provider needed) |
+| `LibraryResources` | Custom `EmbeddedResourceConfigurationProvider` (Terminal.Gui.dll resource) |
+| `AppResources` | Custom `EmbeddedResourceConfigurationProvider` (entry assembly resource) |
+| `GlobalHome` | `JsonConfigurationExtensions.AddJsonFile("~/.tui/config.json", optional: true)` |
+| `GlobalCurrent` | `JsonConfigurationExtensions.AddJsonFile("./.tui/config.json", optional: true)` |
+| `AppHome` | `AddJsonFile("~/.tui/AppName.config.json", optional: true)` |
+| `AppCurrent` | `AddJsonFile("./.tui/AppName.config.json", optional: true)` |
+| `Env` | Custom `EnvironmentVariableChunkConfigurationProvider` for `TUI_CONFIG` |
+| `Runtime` | `MemoryConfigurationProvider` or `AddInMemoryCollection(...)` |
+
+#### C. `IOptions` and `IOptionsMonitor` for Consumption
+
+Components access configuration through:
+- `IOptions` — a snapshot that does not change after the app starts.
+- `IOptionsMonitor` — subscribes to change notifications (live-update support, equivalent to the current `Applied` event).
+
+#### D. Theme System Stays; InstanceSettings POCO Becomes the Vehicle
+
+The theme system (named themes, scheme dictionaries) does not disappear. It becomes an `IOptionsMonitor` where `ThemeSettings` includes:
+- `string ActiveTheme` — the currently selected theme name.
+- `Dictionary Themes` — all defined themes.
+- `Dictionary Schemes` — schemes for the active theme (computed by `ThemeManager`).
+
+#### E. Static Properties Remain as a Compatibility Facade (During Transition)
+
+Static properties decorated with `[ConfigurationProperty]` do not disappear overnight. The transition plan (§8) keeps them but changes how they are populated: `IOptionsMonitor.OnChange(...)` updates the static property rather than CM reflection.
+
+#### F. `ThemeManager` and `SchemeManager` Become Services
+
+```csharp
+public interface IThemeManager
+{
+ string ActiveTheme { get; set; }
+ ImmutableList ThemeNames { get; }
+ ThemeDefinition GetTheme(string name);
+ void SwitchTheme(string themeName);
+ event EventHandler ThemeChanged;
+}
+
+public interface ISchemeManager
+{
+ void AddScheme(string name, Scheme scheme);
+ void RemoveScheme(string name);
+ Scheme GetScheme(string name);
+ bool TryGetScheme(string name, out Scheme? scheme);
+ ImmutableList SchemeNames { get; }
+}
+```
+
+#### G. Application-Level DI Registration Extension
+
+```csharp
+// Application startup
+IConfigurationBuilder builder = new ConfigurationBuilder()
+ .AddTuiLibraryDefaults() // Loads Terminal.Gui.Resources.config.json
+ .AddTuiAppDefaults("MyApp") // Loads MyApp.Resources.config.json
+ .AddTuiUserFiles("MyApp") // Loads ~/.tui/*.json and ./.tui/*.json
+ .AddTuiEnvironmentVariable() // Loads TUI_CONFIG env var
+ .AddTuiRuntimeConfig(myJsonStr); // Loads in-memory override
+
+IConfiguration config = builder.Build();
+
+// Bind settings sections to POCOs
+services.Configure(config.GetSection("Application"));
+services.Configure(config.GetSection("Button"));
+services.Configure(config.GetSection("Glyphs"));
+services.Configure(config.GetSection("Themes"));
+// ...
+```
+
+#### H. View Property Access Pattern
+
+The static properties are replaced with instance fields, populated during construction:
+
+```csharp
+public class Button : View
+{
+ // Instance value beats the configured default
+ private ShadowStyles? _shadowStyle;
+
+ public ShadowStyles ShadowStyle
+ {
+ get => _shadowStyle ?? _settings.DefaultShadow;
+ set
+ {
+ _shadowStyle = value;
+ SetNeedsDisplay();
+ }
+ }
+
+ // Construction via DI host
+ public Button(IOptionsMonitor settings)
+ {
+ _settings = settings;
+ _settingsSubscription = settings.OnChange(OnSettingsChanged);
+ }
+
+ // OR: Construction without DI (for tests, direct use)
+ public Button() : this(new StaticOptionsMonitor(new ButtonSettings())) { }
+}
+```
+
+> **Note on parameterless constructors:** See §7, Debate Item D-01. Requiring constructor injection for all views is a significant breaking change.
+
+---
+
+## 6. Use Case → MEC Mapping
+
+| UC | Current API | MEC Equivalent | Notes |
+|----|-------------|----------------|-------|
+| UC-01 SettingsScope property | `[ConfigurationProperty(Scope = typeof(SettingsScope))]` on static prop | `services.Configure(config.GetSection("Application"))` | POCO replaces attribute |
+| UC-02 ThemeScope property | `[ConfigurationProperty(Scope = typeof(ThemeScope))]` on static prop | `services.Configure(...)`, `IOptionsMonitor` | Per-component Settings POCOs |
+| UC-03 AppSettingsScope property | `[ConfigurationProperty]` on app static prop | App-developer registers own `Configure(...)` | Same pattern, more explicit |
+| UC-04 Enable loading | `ConfigurationManager.Enable(ConfigLocations.All)` | `IConfigurationBuilder.AddTuiUserFiles(...)` | Explicit builder, no opt-in flag |
+| UC-05 9-level precedence | `ConfigLocations` flags | MEC provider chain ordering | See §5.1B for mapping |
+| UC-06 Apply | `ConfigurationManager.Apply()` | `IOptionsMonitor.OnChange(...)` auto-push | Push replaced by pull-on-change |
+| UC-07 Reset to defaults | `ConfigurationManager.Disable(resetToHardCodedDefaults: true)` | Rebuild config without user files; or set POCO defaults | **Debate item D-06** |
+| UC-08 Runtime override | `ConfigurationManager.RuntimeConfig = json` | `services.Configure` or `AddInMemoryCollection(...)` | Still supported; different API |
+| UC-09 Themes | `ThemeManager.Theme = "Dark"; ConfigurationManager.Apply()` | `IThemeManager.SwitchTheme("Dark")` | `IThemeManager` service |
+| UC-10 Theme change events | `ThemeManager.ThemeChanged`, `ConfigurationManager.Applied` | `IThemeManager.ThemeChanged`, `IOptionsMonitor.OnChange` | Equivalent |
+| UC-11 Color schemes | `SchemeManager.GetScheme(Schemes.Base)` | `ISchemeManager.GetScheme("Base")` | Equivalent |
+| UC-12 Assign scheme to View | `view.SchemeName = "Dialog"` | No change — `SchemeName` property retained | No change needed |
+| UC-13 Hard-coded config JSON | `ConfigurationManager.GetHardCodedConfig()` | Serialize default POCO values to JSON | **Debate item D-07** |
+| UC-14 Library resource config | `ConfigLocations.LibraryResources` | `AddTuiLibraryDefaults()` | Equivalent |
+| UC-15 App resource config | `ConfigLocations.AppResources` | `AddTuiAppDefaults(appName)` | Equivalent |
+| UC-16 Global user files | `ConfigLocations.GlobalHome/Current` | `AddTuiUserFiles(appName)` | Equivalent |
+| UC-17 Per-app user files | `ConfigLocations.AppHome/Current` | Part of `AddTuiUserFiles(appName)` | Equivalent |
+| UC-18 Env variable | `ConfigLocations.Env` | `AddTuiEnvironmentVariable()` | Equivalent |
+| UC-19 JSON error handling | `ThrowOnJsonErrors`, `PrintJsonErrors()` | MEC built-in error handling + `ILogger` | Improved |
+| UC-20 Custom JSON converters | Bespoke `JsonConverter` classes | Same converters, registered with STJ via `JsonSerializerOptions` in MEC JSON provider | Reuse existing converters |
+| UC-21 AOT/Trim compat | `[DynamicDependency]` on 29 types | POCOs use source-generated STJ; no reflection discovery | **The primary improvement** |
+| UC-22 Disable CM | `ConfigurationManager.Disable()` | N/A — MEC is always "enabled"; omit providers to restrict | **Debate item D-08** |
+| UC-23 AppName | `ConfigurationManager.AppName` | Parameter to `AddTuiUserFiles(appName)` | Equivalent |
+| UC-24 Applied event | `ConfigurationManager.Applied += handler` | `IOptionsMonitor.OnChange(handler)` | More granular (per-type) |
+| UC-25 Live reload | `ConfigurationManager.Load(All); Apply()` | `IOptionsMonitor` auto-notifies | Handled automatically |
+| UC-26 Custom schemes | `SchemeManager.AddScheme(...)` | `ISchemeManager.AddScheme(...)` | No change in capability |
+| UC-27 Sources diagnostics | `SourcesManager.Sources` | MEC does not expose loaded sources by default | **Debate item D-09** |
+| UC-28 Serialize current config | `SourcesManager.ToJson(settings)` | Enumerate `IOptions` values and serialize | Different API, same result |
+
+---
+
+## 7. Functionality Changes Requiring Explicit Debate
+
+> **IMPORTANT:** These are not decisions. Each item below is a **debate question**. No implementation work may begin on any item marked here until the team has discussed it and recorded a resolution in this section.
+
+---
+
+### D-01: Constructor Injection vs. Parameterless Construction
+
+**The question:** Terminal.Gui Views are currently created with `new Button()`. DI injection requires `new Button(IOptionsMonitor)`. These two forms cannot coexist without providing a parameterless overload that uses a fallback (e.g., default POCOs, or a static `ApplicationServices` resolver).
+
+**Options:**
+1. **Dual constructors** — Parameterless constructor creates a `StaticOptionsMonitor` backed by default values. DI constructor injects the real monitor. This preserves backward compatibility at the cost of complexity.
+2. **Static facade for defaults** — `ButtonSettings.Defaults` is a static singleton POCO. The parameterless constructor reads from it. The DI path injects a live monitor. The facade is updated by the MEC binding.
+3. **Ambient DI resolution** — Use `Application.Services.GetService>()` internally when no explicit injection is provided. Familiar to ASP.NET Core developers; an antipattern in library code.
+
+**Decision: Option 2 (Static facade).**
+
+**Rationale:** Views are independent objects that can be constructed before `Application.Create()` and without any DI container. Option 1 breaks this contract for all app developers. Option 3 also breaks it — if no `Application` exists yet, there is no service provider to resolve from, so it degrades to a static fallback anyway. Option 2 preserves the existing `new Button()` contract, is AOT-safe, and requires no ceremony in tests. Future multi-instance support (#4366) can be addressed by having each `IApplication.Init()` swap the static facade to its own values.
+
+---
+
+### D-02: JSON Schema Compatibility
+
+**The question:** The current config.json format uses flat keys like `"Button.DefaultShadow": "Opaque"` for ThemeScope properties. MEC's standard JSON provider uses nested sections like `"Button": { "DefaultShadow": "Opaque" }`.
+
+**Options:**
+1. **Preserve flat keys** — Write a custom MEC JSON provider that translates flat CM-style keys to MEC-style nested keys.
+2. **Adopt nested format** — Break backward compatibility with existing user config files; provide a migration tool.
+3. **Support both** — Custom provider accepts both formats; flat keys are deprecated.
+
+**Decision: Option 3 (Support both; flat keys deprecated).**
+
+**Rationale:** Existing user config files continue to work without modification. The custom provider accepts both flat (`"Button.DefaultShadow"`) and nested (`"Button": { "DefaultShadow": ... }`) formats. Flat keys emit a deprecation warning at load time. New documentation and generated defaults use the nested MEC-native format. This avoids a breaking change while guiding users toward the standard format.
+
+---
+
+### D-03: ThemeScope Remains vs. Merges Into App-Level Themes
+
+**The question:** ThemeScope is currently a global per-process concept. Every Button reads `Button.DefaultShadow` from the one active theme. With MEC and instance-based config, could different application "sessions" (if Terminal.Gui ever supports multiple concurrent `IApplication` instances) have different active themes?
+
+**Options:**
+1. **Scoped themes per IApplication** — `IThemeManager` is scoped to the `IApplication` instance. Two concurrent apps could have different themes.
+2. **Process-wide themes** — Theme remains a process-wide concept (simplest, matches current behavior).
+
+**Recommendation:** Defer to the instance-based application proposal (issue #4366). For the initial CM→MEC replacement, keep themes process-wide (option 2). Revisit when multi-instance becomes a concrete requirement.
+
+---
+
+### D-04: AppSettingsScope — Breaking Change for App Developers
+
+**The question:** App developers currently use `[ConfigurationProperty]` on their own static properties, and CM discovers them via `ConfigPropertyHostTypes`. In MEC, app developers would instead register their own `services.Configure(config.GetSection("MySettings"))`.
+
+**User impact:** Every app that currently has custom `[ConfigurationProperty]` properties must be updated. This is a **breaking change for all app developers**.
+
+**Mitigation:** Provide a compatibility shim period (one major version) where both approaches work. Document the migration path clearly.
+
+---
+
+### D-05: `ThrowOnJsonErrors` / `PrintJsonErrors` Behavior
+
+**The question:** CM currently collects JSON parsing errors and either throws or logs them lazily. MEC throws immediately on parse failure by default.
+
+**Options:**
+1. **Adopt MEC behavior** — Errors throw at `Build()` time. This is safer and more predictable.
+2. **Wrap MEC with try/catch** — Maintain the "collect errors, report at shutdown" behavior via a wrapper.
+
+**Recommendation:** Adopt MEC behavior (option 1) with an `ILogger` integration. Remove `PrintJsonErrors()` which currently writes to `Console.WriteLine`.
+
+---
+
+### D-06: "Disable" Concept
+
+**The question:** CM has `Enable()` and `Disable()`. MEC does not have an on/off switch; it is always active. Omitting providers is equivalent to "disabled."
+
+**Options:**
+1. **Remove the Enable/Disable API** — Apps that want no configuration simply don't call `AddTuiUserFiles()`. Library defaults are always in POCOs.
+2. **Keep a compatibility wrapper** — A `TuiConfigurationManager` class wraps the MEC builder and provides `Enable(locations)`-style API.
+
+**Recommendation:** Option 1. The "disabled by default" design of CM was necessary because CM's reflection scan is expensive and breaks AOT. With MEC + POCOs, the cost of "configuration" is near zero. There is no reason to opt in.
+
+---
+
+### D-07: `GetHardCodedConfig()` and `GetEmptyConfig()`
+
+**The question:** These methods generate JSON from the hard-coded defaults. They are used by:
+1. The `SaveDefaults` unit test to regenerate `config.json`.
+2. Potentially by a "config editor" scenario in UICatalog.
+
+With POCOs, "hard-coded defaults" are simply the default property values of each POCO. Serializing them to JSON is straightforward (`JsonSerializer.Serialize(new ButtonSettings())`).
+
+**Resolution needed:** Decide whether a `TuiConfigurationSerializer` utility class is needed for tooling, or whether each POCO's defaults are sufficient.
+
+---
+
+### D-08: Sources Diagnostics (`SourcesManager.Sources`)
+
+**The question:** `SourcesManager.Sources` shows exactly which files were loaded and from which location. MEC does not expose this natively. It is used in diagnostic scenarios (the UICatalog "Config" menu shows which config files are loaded).
+
+**Options:**
+1. **Drop the diagnostics API** — UICatalog's config editor is simplified or removed.
+2. **Implement a `ITuiConfigurationSources` service** — Records which providers loaded successfully during `Build()`.
+
+**Recommendation:** Option 2 is preferable for transparency to end users. It is not required for the initial replacement but should be in scope for Phase 2.
+
+---
+
+### D-09: `RuntimeConfig` String API
+
+**The question:** `ConfigurationManager.RuntimeConfig` is a raw JSON string property that can be set at any time before `Load()`. This is used in UICatalog and tests. MEC's equivalent is `AddInMemoryCollection()` or re-building the configuration. Re-building requires that all subscribers (IOptionsMonitor) be notified.
+
+**Options:**
+1. **MEC in-memory provider** — `AddInMemoryCollection(dictionary)` is equivalent but requires the key-value format rather than JSON.
+2. **Custom JSON in-memory provider** — Write an `InMemoryJsonConfigurationProvider` that accepts a JSON string and can be updated after `Build()`. MEC supports reloading via `IConfigurationSource.ReloadOnChange`.
+
+**Recommendation:** Option 2. Existing tests and UICatalog code that sets `RuntimeConfig` to a JSON string should continue to work.
+
+---
+
+### D-10: Rune / Key / Color Custom JSON Converters
+
+**The question:** The existing custom converters for `Rune`, `Key`, `Color`, `Attribute`, etc. are STJ `JsonConverter` classes. They must be preserved because:
+1. The JSON config files use the Rune-as-character format (`"☑"`) not the .NET default.
+2. Keys are `"Ctrl+Q"` strings, not numeric codes.
+
+**Options:**
+1. **Reuse existing converters as-is** — Register them with `JsonSerializerOptions` in the MEC JSON configuration provider.
+2. **Migrate to STJ source generation** — Generate converters at compile time.
+
+**Recommendation:** Option 1 initially (reuse), then option 2 as a follow-on AOT improvement.
+
+---
+
+## 8. Implementation Phases
+
+> **Status note:** Implementation is already underway. This section now serves as the execution plan and remaining-work tracker.
+
+### Phase 0 — Spec Review and Debate Resolution (Current Phase)
+
+- [ ] Community review of this spec document.
+- [ ] Resolution of all debate items in §7 (D-01 through D-10).
+- [ ] Update this spec with decisions.
+- [ ] Create a formal backwards-compatibility migration guide outline.
+
+### Phase 1 — Foundation: POCOs and MEC Provider Chain
+
+**Goal:** Create the MEC infrastructure without changing any View APIs or removing CM.
+
+1. Add `Microsoft.Extensions.Configuration.Json` and `Microsoft.Extensions.Options` NuGet dependencies.
+2. Create all settings POCOs (`ButtonSettings`, `GlyphsSettings`, `ApplicationSettings`, `ThemeSettings`, etc.).
+3. Implement `TuiConfigurationBuilder` (the MEC-based equivalent of `SourcesManager`).
+4. Implement custom MEC providers for embedded resources, `TUI_CONFIG` env var, and in-memory JSON.
+5. Implement `IThemeManager` and `ISchemeManager` as services backed by `IOptionsMonitor`.
+6. Implement `TuiConfigurationExtensions` with `AddTuiLibraryDefaults()`, `AddTuiUserFiles()`, etc.
+7. **Tests:** Write unit tests for POCOs and the builder. All tests in `Tests/UnitTestsParallelizable`.
+
+### Phase 2 — Wire Views to IOptionsMonitor
+
+**Goal:** Views read from POCOs rather than static properties; existing static properties become a facade.
+
+1. Add `IOptionsMonitor` fields to each View that has `[ConfigurationProperty(Scope = typeof(ThemeScope))]` properties.
+2. Create parameterless constructor overloads backed by default POCOs (resolve D-01).
+3. Static `[ConfigurationProperty]` properties remain as setters that forward to the POCO.
+4. The `IOptionsMonitor.OnChange(...)` callback updates the static properties (to maintain compatibility) and calls `SetNeedsDisplay()`.
+5. **Tests:** Verify that a View created with default constructor has the correct default values. Verify that changing an `IOptionsMonitor` fires redraw.
+
+### Phase 3 — Theme and Scheme Refactor
+
+**Goal:** Themes and schemes are managed by `IThemeManager` / `ISchemeManager`; the ThemeScope/SettingsScope POCO infrastructure is the source of truth.
+
+1. Migrate `ThemeManager` to `IThemeManager`.
+2. Migrate `SchemeManager` to `ISchemeManager`.
+3. Theme switching becomes `IThemeManager.SwitchTheme(name)`, which updates `IOptionsMonitor`, which triggers `OnChange` on all registered View subscriptions.
+4. Preserve JSON schema compatibility for built-in `config.json` (resolve D-02).
+5. **Tests:** Verify theme switching triggers `SetNeedsDisplay()` on affected views.
+
+### Phase 3A — Finish Complex-Type Migration (Current #5411 focus)
+
+**Goal:** Complete the functional CM→MEC migration for complex CM-owned types so effective runtime behavior is MEC-based before CM removal.
+
+1. **Theme graph migration**
+ - Ensure `"Theme"` scalar binding and reset semantics are correct (no stale state across rebuilds).
+ - Ensure `"Themes"` definitions bind correctly for active theme selection and fallback behavior.
+2. **Scheme dictionary migration**
+ - Ensure current-theme schemes are sourced from MEC-bound data.
+ - Preserve existing built-in scheme names and lookup behavior.
+3. **Key-binding migration**
+ - Ensure `Application.DefaultKeyBindings`, `View.DefaultKeyBindings`, and `View.ViewKeyBindings` load from MEC config.
+ - Preserve command/key parsing behavior with existing key converters.
+4. **Color dictionary migration**
+ - Ensure `Color.Colors16` is loaded from MEC with existing key/value semantics.
+5. **Mixed-format compatibility**
+ - Support both nested MEC sections and legacy flat dotted keys during migration.
+ - In mixed configs, preserve provider precedence (higher-precedence keys must win).
+6. **Tests and acceptance criteria**
+ - Add focused tests for themes, schemes, key-binding dictionaries, and `Colors16`.
+ - Add regression tests for mixed nested+dotted config overlays and scalar reset behavior.
+ - Keep full UnitTestsParallelizable and CI green.
+
+**Definition of done for PR #5411:** complex-type behavior is fully MEC-driven (with compatibility shims still present), and all migration tests pass.
+
+#### Phase 3A.x — Internal subscriber rewiring (landed in #5411)
+
+The following items were pulled forward from the planned post-#5411 removal work because they make the MEC story coherent without requiring CM deletion:
+
+- **A1 — `IThemeManager.ThemeChanged` event.** Added an `EventHandler>` to `IThemeManager`. `MecThemeManager` subscribes to legacy `ThemeManager.ThemeChanged` in its constructor and forwards. The runtime theme/scheme dictionary still lives in legacy `ConfigurationManager.Settings["Themes"]`; Mec presents the API + event surface. Full Mec ownership of theme/scheme data (Phase A2) is deferred to #5416, where deletion of `ScopeJsonConverter` and the array-of-single-key-object JSON shape can be addressed together.
+- **B — `ThemeChanges` static facade.** New `Terminal.Gui.Configuration.ThemeChanges` static class exposes a single `ThemeChanged` event that bridges both `ConfigurationManager.Applied` and `ThemeManager.ThemeChanged`. The four internal view subscribers — `Menu`, `MenuBar`, `StatusBar`, `LineCanvas` — now subscribe to `ThemeChanges.ThemeChanged` instead of `ConfigurationManager.Applied`. `ConfigurationManager.Applied` remains public and `[Obsolete]` for external consumers; #5416 can delete it once external migration windows close.
+- **C — `TuiSerializerContext` extraction.** The configured `SourceGenerationContext` instance (with `RuneJsonConverter`, `KeyJsonConverter`, `JavaScriptEncoder.UnsafeRelaxedJsonEscaping`, comment-skip, trailing commas, etc.) now lives in a non-obsolete `internal static class TuiSerializerContext` with a single `Instance` field. The obsolete `ConfigurationManager.SerializerContext` is preserved as a thin delegator (`= TuiSerializerContext.Instance`) for back-compat. All internal JSON-converter consumers and the four affected test classes (`Key`/`Rune`/`Scheme`/`ScopeJsonConverterTests`) reference `TuiSerializerContext.Instance` directly, eliminating the `#pragma warning disable CS0618` blocks in `AttributeJsonConverter`, `SchemeJsonConverter`, `DictionaryJsonConverter`, `ConcurrentDictionaryJsonConverter`, and `DeepCloner`. The remaining two pragmas (`ScopeJsonConverter`, `SourcesManager`) stay narrowed for *other* obsolete CM uses unrelated to the serializer context; they are removed wholesale in #5416 step D.
+
+The following items were considered for #5411 but deferred to #5416 with documented reasons:
+
+- **A2 — Mec actually owns theme/scheme runtime data.** Requires either D-02 (config.json nested-shape migration) or a MEC-compatible parser for the array-of-single-key-object format. Out of scope for #5411 because it would force a JSON schema decision; that decision belongs in the deletion PR where `ScopeJsonConverter` is also being removed.
+- **Phase D — `config.json` flat → nested migration.** CM's read path still exists in #5411 and would break on a nested-format resource file. Must be paired with `ScopeJsonConverter` deletion in #5416.
+- **Phase E — Delete CM types.** The whole point of marking them `[Obsolete]` in #5411 is to provide a one-release shim window.
+- **`PrintJsonErrors()` removal / `OnLoadException` wiring.** Behavior-preserving replacement is possible via `JsonConfigurationSource.OnLoadException` (sets `ctx.Ignored = true` to swallow parse errors and aggregate them for deferred display). Out of scope for #5411 to avoid expanding the diff further; folded into #5416's scope with the explicit note that fail-fast vs. deferred-error parity is preservable.
+
+### Phase 4 — Remove [ConfigurationProperty] Static Statics
+
+**Goal:** Remove the reflection-based CM machinery. Static properties become plain statics that are only updated by MEC.
+
+1. Remove `[ConfigurationProperty]` attribute from all library properties.
+2. Remove `ConfigProperty`, `ConfigPropertyHostTypes`, `Scope`, `ScopeJsonConverter`, `DeepCloner`.
+3. Remove `Assembly.GetTypes()` scan from `ConfigProperty.Initialize()`.
+4. Remove `[DynamicDependency]` annotations from `ConfigPropertyHostTypes`.
+5. Retain `SourcesManager` as a compatibility class if needed (resolve D-09).
+6. **Tests:** Verify AOT build size reduces. Verify tests still pass.
+
+### Phase 5 — App Developer Migration (AppSettingsScope)
+
+**Goal:** Migrate the AppSettingsScope pattern to MEC `services.Configure(...)`.
+
+1. Document the migration guide for app developers.
+2. Remove or deprecate `AppSettingsScope` and `[ConfigurationProperty]` for app use.
+3. Update UICatalog to use the new API.
+4. **Tests:** Write tests that demonstrate the app-developer workflow.
+
+### Phase 6 — Backward Compatibility Cleanup
+
+**Goal:** Remove the CM shim layer; CM is fully replaced.
+
+1. Mark `ConfigurationManager` class as `[Obsolete]`.
+2. Remove in the next major version.
+3. Final binary size measurement on the NativeAot example.
+
+**Planned follow-up:** This cleanup/removal work is tracked in stacked PR #5416 so #5411 can complete functional migration first.
+
+---
+
+## 9. Open Questions
+
+These questions are not debates about functionality changes — they are unresolved design questions that need answers before Phase 1 work can begin.
+
+### Q-01: NuGet Dependency Footprint
+
+Adding `Microsoft.Extensions.Configuration.Json` and `Microsoft.Extensions.Options` to Terminal.Gui's package dependencies affects all consumers. The packages are small and ubiquitous in .NET, but:
+- Does this conflict with any consumer's version constraints?
+- Do the new packages themselves have AOT/trim issues?
+
+**Action:** Check [NuGet.org advisory database](https://advisories.nuget.org) and NativeAOT compatibility notes for each dependency before Phase 1.
+
+### Q-02: Generic Host vs. Just MEC
+
+The issue proposal describes using `IHostBuilder` / `Host.CreateDefaultBuilder(args)`. However, adopting the Generic Host is a much larger change than just adopting MEC.
+
+**Recommendation:** Phase 1 through 5 use only `IConfigurationBuilder` + `IOptions` (no Generic Host). The Generic Host integration (if desired) is a separate, later proposal.
+
+### Q-03: JSON Schema URL
+
+The current `config.json` includes `"$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"`. The schema must be updated if the JSON format changes (D-02). Who maintains the hosted schema?
+
+### Q-04: `Glyphs` — 144 Properties
+
+`Glyphs` has 144 `[ConfigurationProperty(Scope = typeof(ThemeScope))]` properties. Migrating each to a `GlyphsSettings` POCO property is mechanical but voluminous. Is there a better structure for the POCO (e.g., a `Dictionary` for extensibility)?
+
+### Q-05: Custom Converter AOT
+
+The `RuneJsonConverter`, `KeyJsonConverter`, etc. must be verified to work with STJ source generation. They use polymorphism (`JsonConverter`) which *is* compatible, but they must be registered with `SourceGenerationContext`. This needs a proof-of-concept before Phase 1.
+
+### Q-06: `IOptionsMonitor` for AOT
+
+`IOptionsMonitor` is in `Microsoft.Extensions.Options`. It uses generics but not dynamic code. It *should* be AOT-safe. This needs verification with the `PublishAot=true` build before Phase 1 is committed.
+
+---
+
+## 10. Out of Scope
+
+The following are explicitly **not** part of this proposal:
+
+1. **Generic Host / IHostBuilder adoption** — See Q-02. This is a separate, larger proposal.
+2. **Constructor injection mandate for all Views** — Depends on D-01 resolution. If DI injection is not made mandatory, Views do not require DI at all.
+3. **View-level DI** — Individual views resolving services from `IServiceProvider` is not proposed here.
+4. **Multiple concurrent application instances** — Follows from the separate instance-based `IApplication` proposal (#4366).
+5. **TextMate/Markdig conditional compilation** — Mentioned in the AOT analysis; separate issue.
+6. **Lazy driver registration** — Also mentioned in the AOT analysis; separate issue.
+7. **Assembly splitting** — Also mentioned in the AOT analysis; will be more impactful after this work is complete.
+
+---
+
+## Appendix A: Current [ConfigurationProperty] Property Full Inventory
+
+### SettingsScope (process-wide)
+
+| Class | Property | Type | Hard-coded Default |
+|---|---|---|---|
+| `Application` | `AppModel` | `AppModel` | `FullScreen` |
+| `Application` | `ForceDriver` | `string` | `""` |
+| `Application` | `DefaultKeyBindings` | `Dictionary?` | Platform-specific |
+| `Application` | `IsMouseDisabled` | `bool` | `false` |
+| `ConfigurationManager` | `ThrowOnJsonErrors` | `bool?` | `false` |
+| `Driver` | `Force16Colors` | `bool` | `false` |
+| `Driver` | `SizeDetection` | `SizeDetectionMode` | `AnsiQuery` |
+| `Key` | `Separator` | `char` | `'+'` |
+| `MenuBar` | `DefaultKey` | `Key` | `F10` |
+| `PopoverMenu` | `DefaultKey` | `Key` | `Shift+F10` |
+| `FileDialog` | `DefaultOpenMode` | `OpenMode` | `Mixed` |
+| `FileDialogStyle` | `DefaultSearchMatcher` | `ISearchMatcher?` | `DefaultSearchMatcher` |
+| `FileDialogStyle` | `PreferResultFromOpenMode` | `bool` | `true` |
+| `Trace` | `EnabledCategories` | `TraceCategory` | `None` |
+| `View` | `DefaultKeyBindings` | `Dictionary<...>?` | Platform-specific |
+| `View` | `ViewKeyBindings` | `Dictionary>?` | `null` |
+| `Color` | `Colors16` *(OmitClassName)* | `Dictionary` | *(platform defaults)* |
+
+### ThemeScope (per-theme)
+
+| Class | Property | Type | Hard-coded Default |
+|---|---|---|---|
+| `Button` | `DefaultShadow` | `ShadowStyles` | `Opaque` |
+| `Button` | `DefaultMouseHighlightStates` | `MouseState` | `In\|Pressed\|PressedOutside` |
+| `CheckBox` | `DefaultMouseHighlightStates` | `MouseState` | `PressedOutside\|Pressed\|In` |
+| `Dialog` | `DefaultShadow` | `ShadowStyles` | `Transparent` |
+| `Dialog` | `DefaultBorderStyle` | `LineStyle` | `Heavy` |
+| `Dialog` | `DefaultButtonAlignment` | `Alignment` | `End` |
+| `Dialog` | `DefaultButtonAlignmentModes` | `AlignmentModes` | `StartToEnd\|AddSpaceBetweenItems` |
+| `FrameView` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `HexView` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `Menu` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `MenuBar` | `DefaultBorderStyle` | `LineStyle` | `None` |
+| `MessageBox` | `DefaultBorderStyle` | `LineStyle` | `Heavy` |
+| `MessageBox` | `DefaultButtonAlignment` | `Alignment` | `Center` |
+| `NerdFonts` | `Enable` | `bool` | `false` |
+| `SelectorBase` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `StatusBar` | `DefaultBorderStyle` | `LineStyle` | `None` |
+| `TextField` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `TextView` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `Window` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `Window` | `DefaultShadow` | `ShadowStyles` | `None` |
+| `CharMap` | `DefaultBorderStyle` | `LineStyle` | `Single` |
+| `LinearRangeDefaults` | *(multiple)* | *(various)* | *(see source)* |
+| `NerdFonts` | `Enable` | `bool` | `false` |
+| `SchemeManager` | `Schemes` | `Dictionary?` | Built-in schemes |
+| `Glyphs` | `WideGlyphReplacement` | `Rune` | `(Rune)' '` |
+| `Glyphs` | `File` | `Rune` | `(Rune)'☰'` |
+| `Glyphs` | `Folder` | `Rune` | `(Rune)'꤉'` |
+| `Glyphs` | *(~141 more)* | `Rune` | *(various)* |
+
+### AppSettingsScope (app-specific)
+
+Defined by app developers. Terminal.Gui itself does not own any AppSettingsScope properties (as of writing).
+
+---
+
+## Appendix B: External Config Files
+
+### Terminal.Gui Library Resource: `config.json`
+
+Located at `Terminal.Gui/Resources/config.json`. Embedded in `Terminal.Gui.dll`. 1,501 lines. Defines:
+- All built-in themes (Default, Dark, Light, TurboPascal 5, Anders, Green Phosphor, Amber Phosphor)
+- Each theme defines its Schemes (color/attribute pairs for Base, Dialog, Menu, Error, Accent, etc.)
+- Each theme optionally overrides ThemeScope properties (glyph sets, border styles, shadow styles)
+
+**This file's format must be preserved** unless the team decides otherwise in D-02.
+
+### App Config File Format
+
+```json
+{
+ "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
+ "Theme": "Dark",
+ "Application.ForceDriver": "NetDriver",
+ "Driver.Force16Colors": false,
+ "AppSettings": {
+ "MyApp.ShowStatusBar": true
+ },
+ "Themes": [
+ {
+ "Default": {
+ "Button.DefaultShadow": "None",
+ "Schemes": [
+ {
+ "Base": {
+ "Normal": { "Foreground": "Yellow", "Background": "DarkBlue" }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
+```
+
+---
+
+*Last updated: 2026-05-25. Authors: @copilot (specification), reviewed by team.*