ThemeManager.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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
  103. || oldTheme != Settings! ["Theme"].PropertyValue as string)
  104. && Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes && themes.ContainsKey (_theme))
  105. {
  106. Settings! ["Theme"].PropertyValue = _theme;
  107. Instance.OnThemeChanged (oldTheme);
  108. }
  109. }
  110. }
  111. /// <summary>Event fired he selected theme has changed. application.</summary>
  112. public event EventHandler<ThemeManagerEventArgs>? ThemeChanged;
  113. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  114. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  115. internal static void GetHardCodedDefaults ()
  116. {
  117. //Debug.WriteLine ("Themes.GetHardCodedDefaults()");
  118. var theme = new ThemeScope ();
  119. theme.RetrieveValues ();
  120. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase)
  121. {
  122. { "Default", theme }
  123. };
  124. SelectedTheme = "Default";
  125. }
  126. /// <summary>Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.</summary>
  127. internal void OnThemeChanged (string theme)
  128. {
  129. //Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
  130. ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme));
  131. }
  132. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  133. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  134. internal static void Reset ()
  135. {
  136. Debug.WriteLine ("Themes.Reset()");
  137. Colors.Reset ();
  138. Themes?.Clear ();
  139. SelectedTheme = string.Empty;
  140. }
  141. #region IDictionary
  142. #pragma warning disable 1591
  143. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  144. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  145. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  146. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  147. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  148. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  149. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  150. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  151. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  152. [UnconditionalSuppressMessage ("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
  153. [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
  154. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  155. public ThemeScope this [string key]
  156. {
  157. [RequiresUnreferencedCode ("AOT")]
  158. [RequiresDynamicCode ("AOT")]
  159. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  160. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  161. get => ((IDictionary<string, ThemeScope>)Themes!) [key];
  162. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  163. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  164. [RequiresUnreferencedCode ("AOT")]
  165. [RequiresDynamicCode ("AOT")]
  166. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  167. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  168. set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value;
  169. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  170. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  171. }
  172. [RequiresUnreferencedCode ("AOT")]
  173. [RequiresDynamicCode ("AOT")]
  174. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  175. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  176. public void Add (string key, ThemeScope value) { ((IDictionary<string, ThemeScope>)Themes!).Add (key, value); }
  177. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  178. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  179. [RequiresUnreferencedCode ("AOT")]
  180. [RequiresDynamicCode ("AOT")]
  181. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  182. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  183. public bool ContainsKey (string key) { return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key); }
  184. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  185. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  186. [RequiresUnreferencedCode ("AOT")]
  187. [RequiresDynamicCode ("AOT")]
  188. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  189. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  190. public bool Remove (string key) { return ((IDictionary<string, ThemeScope>)Themes!).Remove (key); }
  191. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  192. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  193. [RequiresUnreferencedCode ("AOT")]
  194. [RequiresDynamicCode ("AOT")]
  195. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  196. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  197. public bool TryGetValue (string key, out ThemeScope value) { return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!); }
  198. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  199. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  200. [RequiresUnreferencedCode ("AOT")]
  201. [RequiresDynamicCode ("AOT")]
  202. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  203. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  204. public void Add (KeyValuePair<string, ThemeScope> item) { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item); }
  205. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  206. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  207. [RequiresUnreferencedCode ("AOT")]
  208. [RequiresDynamicCode ("AOT")]
  209. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  210. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  211. public void Clear () { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear (); }
  212. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  213. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  214. [RequiresUnreferencedCode ("AOT")]
  215. [RequiresDynamicCode ("AOT")]
  216. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  217. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  218. public bool Contains (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item); }
  219. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  220. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  221. [RequiresUnreferencedCode ("AOT")]
  222. [RequiresDynamicCode ("AOT")]
  223. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  224. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  225. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  226. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  227. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  228. {
  229. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  230. }
  231. [RequiresUnreferencedCode ("AOT")]
  232. [RequiresDynamicCode ("AOT")]
  233. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  234. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  235. public bool Remove (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item); }
  236. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  237. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  238. [RequiresUnreferencedCode ("AOT")]
  239. [RequiresDynamicCode ("AOT")]
  240. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  241. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  242. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator () { return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator (); }
  243. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  244. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  245. [RequiresUnreferencedCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  246. [RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
  247. #pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  248. #pragma warning disable IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  249. IEnumerator IEnumerable.GetEnumerator () { return ((IEnumerable)Themes!).GetEnumerator (); }
  250. #pragma warning restore IL3051 // 'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.
  251. #pragma warning restore IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
  252. #pragma warning restore 1591
  253. #endregion
  254. }