ThemeManager.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Text.Json.Serialization;
  6. #nullable enable
  7. namespace Terminal.Gui;
  8. /// <summary>
  9. /// Contains a dictionary of the <see cref="ThemeManager.Theme"/>s for a Terminal.Gui application.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>
  13. /// A Theme is a collection of settings that are named. The default theme is named "Default".
  14. /// </para>
  15. /// <para>
  16. /// The <see cref="ThemeManager.Theme"/> property is used to detemrine the currently active theme.
  17. /// </para>
  18. /// </remarks>
  19. /// <para>
  20. /// <see cref="ThemeManager"/> is a singleton class. It is created when the first <see cref="ThemeManager"/> property is accessed.
  21. /// Accessing <see cref="ThemeManager.Instance"/> is the same as accessing <see cref="ConfigurationManager.Themes"/>.
  22. /// </para>
  23. /// <example><code>
  24. /// "Themes": [
  25. /// {
  26. /// "Default": {
  27. /// "ColorSchemes": [
  28. /// {
  29. /// "TopLevel": {
  30. /// "Normal": {
  31. /// "Foreground": "BrightGreen",
  32. /// "Background": "Black"
  33. /// },
  34. /// "Focus": {
  35. /// "Foreground": "White",
  36. /// "Background": "Cyan"
  37. ///
  38. /// },
  39. /// "HotNormal": {
  40. /// "Foreground": "Brown",
  41. /// "Background": "Black"
  42. ///
  43. /// },
  44. /// "HotFocus": {
  45. /// "Foreground": "Blue",
  46. /// "Background": "Cyan"
  47. /// },
  48. /// "Disabled": {
  49. /// "Foreground": "DarkGray",
  50. /// "Background": "Black"
  51. ///
  52. /// }
  53. /// }
  54. /// }
  55. /// </code></example>
  56. public class ThemeManager : IDictionary<string, ThemeScope> {
  57. private static readonly ThemeManager _instance = new ThemeManager ();
  58. static ThemeManager () { } // Make sure it's truly lazy
  59. private ThemeManager () { } // Prevent instantiation outside
  60. /// <summary>
  61. /// Class is a singleton...
  62. /// </summary>
  63. public static ThemeManager Instance { get { return _instance; } }
  64. private static string _theme = string.Empty;
  65. /// <summary>
  66. /// The currently selected theme. This is the internal version; see <see cref="Theme"/>.
  67. /// </summary>
  68. [JsonInclude, SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true), JsonPropertyName ("Theme")]
  69. internal static string SelectedTheme {
  70. get => _theme;
  71. set {
  72. var oldTheme = _theme;
  73. _theme = value;
  74. if (oldTheme != _theme &&
  75. ConfigurationManager.Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes &&
  76. themes.ContainsKey (_theme)) {
  77. ConfigurationManager.Settings! ["Theme"].PropertyValue = _theme;
  78. Instance.OnThemeChanged (oldTheme);
  79. }
  80. }
  81. }
  82. /// <summary>
  83. /// Gets or sets the currently selected theme. The value is persisted to the "Theme"
  84. /// property.
  85. /// </summary>
  86. [JsonIgnore]
  87. public string Theme {
  88. get => ThemeManager.SelectedTheme;
  89. set {
  90. ThemeManager.SelectedTheme = value;
  91. }
  92. }
  93. /// <summary>
  94. /// Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.
  95. /// </summary>
  96. internal void OnThemeChanged (string theme)
  97. {
  98. Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
  99. ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme));
  100. }
  101. /// <summary>
  102. /// Event fired he selected theme has changed.
  103. /// application.
  104. /// </summary>
  105. public event EventHandler<ThemeManagerEventArgs>? ThemeChanged;
  106. /// <summary>
  107. /// Holds the <see cref="ThemeScope"/> definitions.
  108. /// </summary>
  109. [JsonInclude, JsonConverter (typeof (DictionaryJsonConverter<ThemeScope>))]
  110. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  111. public static Dictionary<string, ThemeScope>? Themes {
  112. get => ConfigurationManager.Settings? ["Themes"]?.PropertyValue as Dictionary<string, ThemeScope>; // themes ?? new Dictionary<string, ThemeScope> ();
  113. set {
  114. //if (themes == null || value == null) {
  115. // themes = value;
  116. //} else {
  117. // themes = (Dictionary<string, ThemeScope>)DeepMemberwiseCopy (value!, themes!)!;
  118. //}
  119. ConfigurationManager.Settings! ["Themes"].PropertyValue = value;
  120. }
  121. }
  122. internal static void Reset ()
  123. {
  124. Debug.WriteLine ($"Themes.Reset()");
  125. Themes?.Clear ();
  126. SelectedTheme = string.Empty;
  127. }
  128. internal static void GetHardCodedDefaults ()
  129. {
  130. Debug.WriteLine ($"Themes.GetHardCodedDefaults()");
  131. var theme = new ThemeScope ();
  132. theme.RetrieveValues ();
  133. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase) { { "Default", theme } };
  134. SelectedTheme = "Default";
  135. }
  136. #region IDictionary
  137. #pragma warning disable 1591
  138. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  139. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  140. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  141. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  142. public ThemeScope this [string key] { get => ((IDictionary<string, ThemeScope>)Themes!) [key]; set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value; }
  143. public void Add (string key, ThemeScope value)
  144. {
  145. ((IDictionary<string, ThemeScope>)Themes!).Add (key, value);
  146. }
  147. public bool ContainsKey (string key)
  148. {
  149. return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key);
  150. }
  151. public bool Remove (string key)
  152. {
  153. return ((IDictionary<string, ThemeScope>)Themes!).Remove (key);
  154. }
  155. public bool TryGetValue (string key, out ThemeScope value)
  156. {
  157. return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!);
  158. }
  159. public void Add (KeyValuePair<string, ThemeScope> item)
  160. {
  161. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item);
  162. }
  163. public void Clear ()
  164. {
  165. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear ();
  166. }
  167. public bool Contains (KeyValuePair<string, ThemeScope> item)
  168. {
  169. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item);
  170. }
  171. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  172. {
  173. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  174. }
  175. public bool Remove (KeyValuePair<string, ThemeScope> item)
  176. {
  177. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item);
  178. }
  179. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator ()
  180. {
  181. return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator ();
  182. }
  183. IEnumerator IEnumerable.GetEnumerator ()
  184. {
  185. return ((IEnumerable)Themes!).GetEnumerator ();
  186. }
  187. #pragma warning restore 1591
  188. #endregion
  189. }