#nullable enable using System.Collections; using System.Diagnostics; using System.Text.Json.Serialization; namespace Terminal.Gui; /// Contains a dictionary of the s for a Terminal.Gui application. /// /// A Theme is a collection of settings that are named. The default theme is named "Default". /// The property is used to detemrine the currently active theme. /// /// /// is a singleton class. It is created when the first property /// is accessed. Accessing is the same as accessing /// . /// /// /// /// "Themes": [ /// { /// "Default": { /// "ColorSchemes": [ /// { /// "TopLevel": { /// "Normal": { /// "Foreground": "BrightGreen", /// "Background": "Black" /// }, /// "Focus": { /// "Foreground": "White", /// "Background": "Cyan" /// /// }, /// "HotNormal": { /// "Foreground": "Yellow", /// "Background": "Black" /// /// }, /// "HotFocus": { /// "Foreground": "Blue", /// "Background": "Cyan" /// }, /// "Disabled": { /// "Foreground": "DarkGray", /// "Background": "Black" /// /// } /// } /// } /// /// public class ThemeManager : IDictionary { private static string _theme = string.Empty; static ThemeManager () { } // Make sure it's truly lazy private ThemeManager () { } // Prevent instantiation outside /// Class is a singleton... public static ThemeManager Instance { get; } = new (); /// Gets or sets the currently selected theme. The value is persisted to the "Theme" property. [JsonIgnore] public string Theme { get => SelectedTheme; set => SelectedTheme = value; } /// Holds the definitions. [JsonInclude] [JsonConverter (typeof (DictionaryJsonConverter))] [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)] public static Dictionary? Themes { get => Settings? ["Themes"] ?.PropertyValue as Dictionary; // themes ?? new Dictionary (); set => //if (themes is null || value is null) { // themes = value; //} else { // themes = (Dictionary)DeepMemberwiseCopy (value!, themes!)!; //} Settings! ["Themes"].PropertyValue = value; } /// The currently selected theme. This is the internal version; see . [JsonInclude] [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)] [JsonPropertyName ("Theme")] internal static string SelectedTheme { get => _theme; set { string oldTheme = _theme; _theme = value; if (oldTheme != _theme && Settings! ["Themes"]?.PropertyValue is Dictionary themes && themes.ContainsKey (_theme)) { Settings! ["Theme"].PropertyValue = _theme; Instance.OnThemeChanged (oldTheme); } } } /// Event fired he selected theme has changed. application. public event EventHandler? ThemeChanged; internal static void GetHardCodedDefaults () { Debug.WriteLine ("Themes.GetHardCodedDefaults()"); var theme = new ThemeScope (); theme.RetrieveValues (); Themes = new Dictionary (StringComparer.InvariantCultureIgnoreCase) { { "Default", theme } }; SelectedTheme = "Default"; } /// Called when the selected theme has changed. Fires the event. internal void OnThemeChanged (string theme) { Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}"); ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme)); } internal static void Reset () { Debug.WriteLine ("Themes.Reset()"); Colors.Reset (); Themes?.Clear (); SelectedTheme = string.Empty; } #region IDictionary #pragma warning disable 1591 public ICollection Keys => ((IDictionary)Themes!).Keys; public ICollection Values => ((IDictionary)Themes!).Values; public int Count => ((ICollection>)Themes!).Count; public bool IsReadOnly => ((ICollection>)Themes!).IsReadOnly; public ThemeScope this [string key] { get => ((IDictionary)Themes!) [key]; set => ((IDictionary)Themes!) [key] = value; } public void Add (string key, ThemeScope value) { ((IDictionary)Themes!).Add (key, value); } public bool ContainsKey (string key) { return ((IDictionary)Themes!).ContainsKey (key); } public bool Remove (string key) { return ((IDictionary)Themes!).Remove (key); } public bool TryGetValue (string key, out ThemeScope value) { return ((IDictionary)Themes!).TryGetValue (key, out value!); } public void Add (KeyValuePair item) { ((ICollection>)Themes!).Add (item); } public void Clear () { ((ICollection>)Themes!).Clear (); } public bool Contains (KeyValuePair item) { return ((ICollection>)Themes!).Contains (item); } public void CopyTo (KeyValuePair [] array, int arrayIndex) { ((ICollection>)Themes!).CopyTo (array, arrayIndex); } public bool Remove (KeyValuePair item) { return ((ICollection>)Themes!).Remove (item); } public IEnumerator> GetEnumerator () { return ((IEnumerable>)Themes!).GetEnumerator (); } IEnumerator IEnumerable.GetEnumerator () { return ((IEnumerable)Themes!).GetEnumerator (); } #pragma warning restore 1591 #endregion }