ThemeManager.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #nullable enable
  2. using System.Collections;
  3. using System.Diagnostics;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Text.Json.Serialization;
  6. namespace Terminal.Gui;
  7. /// <summary>Contains a dictionary of the <see cref="ThemeManager.Theme"/>s for a Terminal.Gui application.</summary>
  8. /// <remarks>
  9. /// <para>A Theme is a collection of settings that are named. The default theme is named "Default".</para>
  10. /// <para>The <see cref="ThemeManager.Theme"/> property is used to determine the currently active theme.</para>
  11. /// </remarks>
  12. /// <para>
  13. /// <see cref="ThemeManager"/> is a singleton class. It is created when the first <see cref="ThemeManager"/> property
  14. /// is accessed. Accessing <see cref="ThemeManager.Instance"/> is the same as accessing
  15. /// <see cref="ConfigurationManager.Themes"/>.
  16. /// </para>
  17. /// <example>
  18. /// <code>
  19. /// "Themes": [
  20. /// {
  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": "Yellow",
  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. /// }
  50. /// </code>
  51. /// </example>
  52. public class ThemeManager : IDictionary<string, ThemeScope>
  53. {
  54. private static string _theme = string.Empty;
  55. static ThemeManager () { } // Make sure it's truly lazy
  56. private ThemeManager () { } // Prevent instantiation outside
  57. /// <summary>Class is a singleton...</summary>
  58. public static ThemeManager Instance { get; } = new ();
  59. /// <summary>Gets or sets the currently selected theme. The value is persisted to the "Theme" property.</summary>
  60. [JsonIgnore]
  61. public string Theme
  62. {
  63. get => SelectedTheme;
  64. [RequiresUnreferencedCode ("AOT")]
  65. [RequiresDynamicCode ("AOT")]
  66. set => SelectedTheme = value;
  67. }
  68. /// <summary>Holds the <see cref="ThemeScope"/> definitions.</summary>
  69. [JsonInclude]
  70. [JsonConverter (typeof (DictionaryJsonConverter<ThemeScope>))]
  71. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  72. public static Dictionary<string, ThemeScope>? Themes
  73. {
  74. [RequiresUnreferencedCode ("AOT")]
  75. [RequiresDynamicCode ("AOT")]
  76. get => Settings? ["Themes"]
  77. ?.PropertyValue as
  78. Dictionary<string, ThemeScope>; // themes ?? new Dictionary<string, ThemeScope> ();
  79. [RequiresUnreferencedCode ("AOT")]
  80. [RequiresDynamicCode ("AOT")]
  81. set =>
  82. //if (themes is null || value is null) {
  83. // themes = value;
  84. //} else {
  85. // themes = (Dictionary<string, ThemeScope>)DeepMemberwiseCopy (value!, themes!)!;
  86. //}
  87. Settings! ["Themes"].PropertyValue = value;
  88. }
  89. /// <summary>The currently selected theme. This is the internal version; see <see cref="Theme"/>.</summary>
  90. [JsonInclude]
  91. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  92. [JsonPropertyName ("Theme")]
  93. internal static string SelectedTheme
  94. {
  95. get => _theme;
  96. [RequiresUnreferencedCode ("Calls Terminal.Gui.ConfigurationManager.Settings")]
  97. [RequiresDynamicCode ("Calls Terminal.Gui.ConfigurationManager.Settings")]
  98. set
  99. {
  100. string oldTheme = _theme;
  101. _theme = value;
  102. if ((oldTheme != _theme || oldTheme != Settings! ["Theme"].PropertyValue as string) && Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes && themes.ContainsKey (_theme))
  103. {
  104. Settings! ["Theme"].PropertyValue = _theme;
  105. Instance.OnThemeChanged (oldTheme);
  106. }
  107. }
  108. }
  109. /// <summary>Event fired he selected theme has changed. application.</summary>
  110. public event EventHandler<ThemeManagerEventArgs>? ThemeChanged;
  111. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  112. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  113. internal static void GetHardCodedDefaults ()
  114. {
  115. //Debug.WriteLine ("Themes.GetHardCodedDefaults()");
  116. var theme = new ThemeScope ();
  117. theme.RetrieveValues ();
  118. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase)
  119. {
  120. { "Default", theme }
  121. };
  122. SelectedTheme = "Default";
  123. }
  124. /// <summary>Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.</summary>
  125. internal void OnThemeChanged (string theme)
  126. {
  127. //Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
  128. ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme));
  129. }
  130. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  131. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  132. internal static void Reset ()
  133. {
  134. Debug.WriteLine ("Themes.Reset()");
  135. Colors.Reset ();
  136. Themes?.Clear ();
  137. SelectedTheme = string.Empty;
  138. }
  139. #region IDictionary
  140. #pragma warning disable 1591
  141. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  142. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  143. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  144. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  145. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  146. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  147. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  148. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  149. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  150. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  151. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  152. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  153. public ThemeScope this [string key]
  154. {
  155. [RequiresUnreferencedCode ("AOT")]
  156. [RequiresDynamicCode ("AOT")]
  157. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  158. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  159. get => ((IDictionary<string, ThemeScope>)Themes!) [key];
  160. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  161. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  162. [RequiresUnreferencedCode ("AOT")]
  163. [RequiresDynamicCode ("AOT")]
  164. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  165. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  166. set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value;
  167. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  168. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  169. }
  170. [RequiresUnreferencedCode ("AOT")]
  171. [RequiresDynamicCode ("AOT")]
  172. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  173. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  174. public void Add (string key, ThemeScope value) { ((IDictionary<string, ThemeScope>)Themes!).Add (key, value); }
  175. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  176. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  177. [RequiresUnreferencedCode ("AOT")]
  178. [RequiresDynamicCode ("AOT")]
  179. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  180. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  181. public bool ContainsKey (string key) { return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key); }
  182. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  183. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  184. [RequiresUnreferencedCode ("AOT")]
  185. [RequiresDynamicCode ("AOT")]
  186. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  187. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  188. public bool Remove (string key) { return ((IDictionary<string, ThemeScope>)Themes!).Remove (key); }
  189. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  190. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  191. [RequiresUnreferencedCode ("AOT")]
  192. [RequiresDynamicCode ("AOT")]
  193. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  194. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  195. public bool TryGetValue (string key, out ThemeScope value) { return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!); }
  196. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  197. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  198. [RequiresUnreferencedCode ("AOT")]
  199. [RequiresDynamicCode ("AOT")]
  200. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  201. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  202. public void Add (KeyValuePair<string, ThemeScope> item) { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item); }
  203. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  204. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  205. [RequiresUnreferencedCode ("AOT")]
  206. [RequiresDynamicCode ("AOT")]
  207. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  208. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  209. public void Clear () { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear (); }
  210. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  211. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  212. [RequiresUnreferencedCode ("AOT")]
  213. [RequiresDynamicCode ("AOT")]
  214. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  215. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  216. public bool Contains (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item); }
  217. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  218. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  219. [RequiresUnreferencedCode ("AOT")]
  220. [RequiresDynamicCode ("AOT")]
  221. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  222. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  223. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  224. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  225. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  226. {
  227. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  228. }
  229. [RequiresUnreferencedCode ("AOT")]
  230. [RequiresDynamicCode ("AOT")]
  231. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  232. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  233. public bool Remove (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item); }
  234. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  235. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  236. [RequiresUnreferencedCode ("AOT")]
  237. [RequiresDynamicCode ("AOT")]
  238. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  239. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  240. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator () { return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator (); }
  241. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  242. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  243. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  244. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  245. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  246. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  247. IEnumerator IEnumerable.GetEnumerator () { return ((IEnumerable)Themes!).GetEnumerator (); }
  248. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  249. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  250. #pragma warning restore 1591
  251. #endregion
  252. }