ThemeManager.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #nullable enable
  2. using System.Collections;
  3. using System.Diagnostics;
  4. using System.Text.Json.Serialization;
  5. namespace Terminal.Gui;
  6. /// <summary>Contains a dictionary of the <see cref="ThemeManager.Theme"/>s for a Terminal.Gui application.</summary>
  7. /// <remarks>
  8. /// <para>A Theme is a collection of settings that are named. The default theme is named "Default".</para>
  9. /// <para>The <see cref="ThemeManager.Theme"/> property is used to detemrine the currently active theme.</para>
  10. /// </remarks>
  11. /// <para>
  12. /// <see cref="ThemeManager"/> is a singleton class. It is created when the first <see cref="ThemeManager"/> property
  13. /// is accessed. Accessing <see cref="ThemeManager.Instance"/> is the same as accessing
  14. /// <see cref="ConfigurationManager.Themes"/>.
  15. /// </para>
  16. /// <example>
  17. /// <code>
  18. /// "Themes": [
  19. /// {
  20. /// "Default": {
  21. /// "ColorSchemes": [
  22. /// {
  23. /// "TopLevel": {
  24. /// "Normal": {
  25. /// "Foreground": "BrightGreen",
  26. /// "Background": "Black"
  27. /// },
  28. /// "Focus": {
  29. /// "Foreground": "White",
  30. /// "Background": "Cyan"
  31. ///
  32. /// },
  33. /// "HotNormal": {
  34. /// "Foreground": "Yellow",
  35. /// "Background": "Black"
  36. ///
  37. /// },
  38. /// "HotFocus": {
  39. /// "Foreground": "Blue",
  40. /// "Background": "Cyan"
  41. /// },
  42. /// "Disabled": {
  43. /// "Foreground": "DarkGray",
  44. /// "Background": "Black"
  45. ///
  46. /// }
  47. /// }
  48. /// }
  49. /// </code>
  50. /// </example>
  51. public class ThemeManager : IDictionary<string, ThemeScope>
  52. {
  53. private static string _theme = string.Empty;
  54. static ThemeManager () { } // Make sure it's truly lazy
  55. private ThemeManager () { } // Prevent instantiation outside
  56. /// <summary>Class is a singleton...</summary>
  57. public static ThemeManager Instance { get; } = new ();
  58. /// <summary>Gets or sets the currently selected theme. The value is persisted to the "Theme" property.</summary>
  59. [JsonIgnore]
  60. public string Theme
  61. {
  62. get => SelectedTheme;
  63. set => SelectedTheme = value;
  64. }
  65. /// <summary>Holds the <see cref="ThemeScope"/> definitions.</summary>
  66. [JsonInclude]
  67. [JsonConverter (typeof (DictionaryJsonConverter<ThemeScope>))]
  68. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  69. public static Dictionary<string, ThemeScope>? Themes
  70. {
  71. get => Settings? ["Themes"]
  72. ?.PropertyValue as
  73. Dictionary<string, ThemeScope>; // themes ?? new Dictionary<string, ThemeScope> ();
  74. set =>
  75. //if (themes == null || value == null) {
  76. // themes = value;
  77. //} else {
  78. // themes = (Dictionary<string, ThemeScope>)DeepMemberwiseCopy (value!, themes!)!;
  79. //}
  80. Settings! ["Themes"].PropertyValue = value;
  81. }
  82. /// <summary>The currently selected theme. This is the internal version; see <see cref="Theme"/>.</summary>
  83. [JsonInclude]
  84. [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
  85. [JsonPropertyName ("Theme")]
  86. internal static string SelectedTheme
  87. {
  88. get => _theme;
  89. set
  90. {
  91. string oldTheme = _theme;
  92. _theme = value;
  93. if (oldTheme != _theme && Settings! ["Themes"]?.PropertyValue is Dictionary<string, ThemeScope> themes && themes.ContainsKey (_theme))
  94. {
  95. Settings! ["Theme"].PropertyValue = _theme;
  96. Instance.OnThemeChanged (oldTheme);
  97. }
  98. }
  99. }
  100. /// <summary>Event fired he selected theme has changed. application.</summary>
  101. public event EventHandler<ThemeManagerEventArgs>? ThemeChanged;
  102. internal static void GetHardCodedDefaults ()
  103. {
  104. Debug.WriteLine ("Themes.GetHardCodedDefaults()");
  105. var theme = new ThemeScope ();
  106. theme.RetrieveValues ();
  107. Themes = new Dictionary<string, ThemeScope> (StringComparer.InvariantCultureIgnoreCase)
  108. {
  109. { "Default", theme }
  110. };
  111. SelectedTheme = "Default";
  112. }
  113. /// <summary>Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.</summary>
  114. internal void OnThemeChanged (string theme)
  115. {
  116. Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
  117. ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme));
  118. }
  119. internal static void Reset ()
  120. {
  121. Debug.WriteLine ("Themes.Reset()");
  122. Colors.Reset ();
  123. Themes?.Clear ();
  124. SelectedTheme = string.Empty;
  125. }
  126. #region IDictionary
  127. #pragma warning disable 1591
  128. public ICollection<string> Keys => ((IDictionary<string, ThemeScope>)Themes!).Keys;
  129. public ICollection<ThemeScope> Values => ((IDictionary<string, ThemeScope>)Themes!).Values;
  130. public int Count => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Count;
  131. public bool IsReadOnly => ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).IsReadOnly;
  132. public ThemeScope this [string key]
  133. {
  134. get => ((IDictionary<string, ThemeScope>)Themes!) [key];
  135. set => ((IDictionary<string, ThemeScope>)Themes!) [key] = value;
  136. }
  137. public void Add (string key, ThemeScope value) { ((IDictionary<string, ThemeScope>)Themes!).Add (key, value); }
  138. public bool ContainsKey (string key) { return ((IDictionary<string, ThemeScope>)Themes!).ContainsKey (key); }
  139. public bool Remove (string key) { return ((IDictionary<string, ThemeScope>)Themes!).Remove (key); }
  140. public bool TryGetValue (string key, out ThemeScope value) { return ((IDictionary<string, ThemeScope>)Themes!).TryGetValue (key, out value!); }
  141. public void Add (KeyValuePair<string, ThemeScope> item) { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Add (item); }
  142. public void Clear () { ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Clear (); }
  143. public bool Contains (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Contains (item); }
  144. public void CopyTo (KeyValuePair<string, ThemeScope> [] array, int arrayIndex)
  145. {
  146. ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).CopyTo (array, arrayIndex);
  147. }
  148. public bool Remove (KeyValuePair<string, ThemeScope> item) { return ((ICollection<KeyValuePair<string, ThemeScope>>)Themes!).Remove (item); }
  149. public IEnumerator<KeyValuePair<string, ThemeScope>> GetEnumerator () { return ((IEnumerable<KeyValuePair<string, ThemeScope>>)Themes!).GetEnumerator (); }
  150. IEnumerator IEnumerable.GetEnumerator () { return ((IEnumerable)Themes!).GetEnumerator (); }
  151. #pragma warning restore 1591
  152. #endregion
  153. }