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": "Yellow",
  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. Colors.Reset ();
  126. Themes?.Clear ();
  127. SelectedTheme = string.Empty;
  128. }
  129. internal static void GetHardCodedDefaults ()
  130. {
  131. Debug.WriteLine ($"Themes.GetHardCodedDefaults()");
  132. var theme = new ThemeScope ();
  133. theme.RetrieveValues ();
  134. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase) { { "Default", theme } };
  135. SelectedTheme = "Default";
  136. }
  137. #region IDictionary
  138. #pragma warning disable 1591
  139. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  140. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  141. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  142. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  143. public ThemeScope this [string key] { get => ((IDictionary<string, ThemeScope>)Themes!) [key]; set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value; }
  144. public void Add (string key, ThemeScope value)
  145. {
  146. ((IDictionary<string, ThemeScope>)Themes!).Add (key, value);
  147. }
  148. public bool ContainsKey (string key)
  149. {
  150. return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key);
  151. }
  152. public bool Remove (string key)
  153. {
  154. return ((IDictionary<string, ThemeScope>)Themes!).Remove (key);
  155. }
  156. public bool TryGetValue (string key, out ThemeScope value)
  157. {
  158. return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!);
  159. }
  160. public void Add (KeyValuePair<string, ThemeScope> item)
  161. {
  162. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item);
  163. }
  164. public void Clear ()
  165. {
  166. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear ();
  167. }
  168. public bool Contains (KeyValuePair<string, ThemeScope> item)
  169. {
  170. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item);
  171. }
  172. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  173. {
  174. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  175. }
  176. public bool Remove (KeyValuePair<string, ThemeScope> item)
  177. {
  178. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item);
  179. }
  180. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator ()
  181. {
  182. return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator ();
  183. }
  184. IEnumerator IEnumerable.GetEnumerator ()
  185. {
  186. return ((IEnumerable)Themes!).GetEnumerator ();
  187. }
  188. #pragma warning restore 1591
  189. #endregion
  190. }