ThemeScope.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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.Configuration.ConfigurationManager;
  9. #nullable enable
  10. namespace Terminal.Gui.Configuration {
  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 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. /// Event arguments for the <see cref="ThemeManager"/> events.
  147. /// </summary>
  148. public class ThemeManagerEventArgs : EventArgs {
  149. /// <summary>
  150. /// The name of the new active theme..
  151. /// </summary>
  152. public string NewTheme { get; set; } = string.Empty;
  153. /// <summary>
  154. /// Initializes a new instance of <see cref="ThemeManagerEventArgs"/>
  155. /// </summary>
  156. public ThemeManagerEventArgs (string newTheme)
  157. {
  158. NewTheme = newTheme;
  159. }
  160. }
  161. /// <summary>
  162. /// Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.
  163. /// </summary>
  164. internal void OnThemeChanged (string theme)
  165. {
  166. Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
  167. ThemeChanged?.Invoke (new ThemeManagerEventArgs (theme));
  168. }
  169. /// <summary>
  170. /// Event fired he selected theme has changed.
  171. /// application.
  172. /// </summary>
  173. public event Action<ThemeManagerEventArgs>? ThemeChanged;
  174. /// <summary>
  175. /// Holds the <see cref="ThemeScope"/> definitions.
  176. /// </summary>
  177. [JsonInclude, JsonConverter (typeof (DictionaryJsonConverter<ThemeScope>))]
  178. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  179. public static Dictionary<string, ThemeScope>? Themes {
  180. get => Settings? ["Themes"]?.PropertyValue as Dictionary<string, ThemeScope>; // themes ?? new Dictionary<string, ThemeScope> ();
  181. set {
  182. //if (themes == null || value == null) {
  183. // themes = value;
  184. //} else {
  185. // themes = (Dictionary<string, ThemeScope>)DeepMemberwiseCopy (value!, themes!)!;
  186. //}
  187. Settings! ["Themes"].PropertyValue = value;
  188. }
  189. }
  190. internal static void Reset ()
  191. {
  192. Debug.WriteLine ($"Themes.Reset()");
  193. Themes?.Clear ();
  194. SelectedTheme = string.Empty;
  195. }
  196. internal static void GetHardCodedDefaults ()
  197. {
  198. Debug.WriteLine ($"Themes.GetHardCodedDefaults()");
  199. var theme = new ThemeScope ();
  200. theme.RetrieveValues ();
  201. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase) { { "Default", theme } };
  202. SelectedTheme = "Default";
  203. }
  204. #region IDictionary
  205. /// <inheritdoc/>
  206. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  207. /// <inheritdoc/>
  208. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  209. /// <inheritdoc/>
  210. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  211. /// <inheritdoc/>
  212. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  213. /// <inheritdoc/>
  214. public ThemeScope this [string key] { get => ((IDictionary<string, ThemeScope>)Themes!) [key]; set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value; }
  215. /// <inheritdoc/>
  216. public void Add (string key, ThemeScope value)
  217. {
  218. ((IDictionary<string, ThemeScope>)Themes!).Add (key, value);
  219. }
  220. /// <inheritdoc/>
  221. public bool ContainsKey (string key)
  222. {
  223. return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key);
  224. }
  225. /// <inheritdoc/>
  226. public bool Remove (string key)
  227. {
  228. return ((IDictionary<string, ThemeScope>)Themes!).Remove (key);
  229. }
  230. /// <inheritdoc/>
  231. public bool TryGetValue (string key, out ThemeScope value)
  232. {
  233. return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!);
  234. }
  235. /// <inheritdoc/>
  236. public void Add (KeyValuePair<string, ThemeScope> item)
  237. {
  238. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item);
  239. }
  240. /// <inheritdoc/>
  241. public void Clear ()
  242. {
  243. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear ();
  244. }
  245. /// <inheritdoc/>
  246. public bool Contains (KeyValuePair<string, ThemeScope> item)
  247. {
  248. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item);
  249. }
  250. /// <inheritdoc/>
  251. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  252. {
  253. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  254. }
  255. /// <inheritdoc/>
  256. public bool Remove (KeyValuePair<string, ThemeScope> item)
  257. {
  258. return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item);
  259. }
  260. /// <inheritdoc/>
  261. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator ()
  262. {
  263. return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator ();
  264. }
  265. IEnumerator IEnumerable.GetEnumerator ()
  266. {
  267. return ((IEnumerable)Themes!).GetEnumerator ();
  268. }
  269. #endregion
  270. }
  271. }
  272. }