ThemeScope.cs 8.2 KB

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