ColorScheme.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text.Json.Serialization;
  4. namespace Terminal.Gui;
  5. /// <summary>
  6. /// Defines a standard set of <see cref="Attribute"/>s for common visible elements in a <see cref="View"/>.
  7. /// </summary>
  8. /// <remarks>
  9. /// <para>
  10. /// ColorScheme objects are immutable. Once constructed, the properties cannot be changed.
  11. /// To change a ColorScheme, create a new one with the desired values,
  12. /// using the <see cref="ColorScheme(ColorScheme)"/> constructor.
  13. /// </para>
  14. /// <para>
  15. /// See also: <see cref="Colors.ColorSchemes"/>.
  16. /// </para>
  17. /// </remarks>
  18. [JsonConverter (typeof (ColorSchemeJsonConverter))]
  19. public class ColorScheme : IEquatable<ColorScheme> {
  20. readonly Attribute _disabled = Attribute.Default;
  21. readonly Attribute _focus = Attribute.Default;
  22. readonly Attribute _hotFocus = Attribute.Default;
  23. readonly Attribute _hotNormal = Attribute.Default;
  24. readonly Attribute _normal = Attribute.Default;
  25. /// <summary>
  26. /// Creates a new instance set to the default colors (see <see cref="Attribute.Default"/>).
  27. /// </summary>
  28. public ColorScheme () : this (Attribute.Default) { }
  29. /// <summary>
  30. /// Creates a new instance, initialized with the values from <paramref name="scheme"/>.
  31. /// </summary>
  32. /// <param name="scheme">The scheme to initialize the new instance with.</param>
  33. public ColorScheme (ColorScheme scheme)
  34. {
  35. if (scheme == null) {
  36. throw new ArgumentNullException (nameof (scheme));
  37. }
  38. _normal = scheme.Normal;
  39. _focus = scheme.Focus;
  40. _hotNormal = scheme.HotNormal;
  41. _disabled = scheme.Disabled;
  42. _hotFocus = scheme.HotFocus;
  43. }
  44. /// <summary>
  45. /// Creates a new instance, initialized with the values from <paramref name="attribute"/>.
  46. /// </summary>
  47. /// <param name="attribute">The attribute to initialize the new instance with.</param>
  48. public ColorScheme (Attribute attribute)
  49. {
  50. _normal = attribute;
  51. _focus = attribute;
  52. _hotNormal = attribute;
  53. _disabled = attribute;
  54. _hotFocus = attribute;
  55. }
  56. /// <summary>
  57. /// The foreground and background color for text when the view is not focused, hot, or disabled.
  58. /// </summary>
  59. public Attribute Normal {
  60. get => _normal;
  61. init => _normal = value;
  62. }
  63. /// <summary>
  64. /// The foreground and background color for text when the view has the focus.
  65. /// </summary>
  66. public Attribute Focus {
  67. get => _focus;
  68. init => _focus = value;
  69. }
  70. /// <summary>
  71. /// The foreground and background color for text in a non-focused view that indicates a <see cref="View.HotKey"/>.
  72. /// </summary>
  73. public Attribute HotNormal {
  74. get => _hotNormal;
  75. init => _hotNormal = value;
  76. }
  77. /// <summary>
  78. /// The foreground and background color for for text in a focused view that indicates a <see cref="View.HotKey"/>.
  79. /// </summary>
  80. public Attribute HotFocus {
  81. get => _hotFocus;
  82. init => _hotFocus = value;
  83. }
  84. /// <summary>
  85. /// The default foreground and background color for text when the view is disabled.
  86. /// </summary>
  87. public Attribute Disabled {
  88. get => _disabled;
  89. init => _disabled = value;
  90. }
  91. /// <summary>
  92. /// Compares two <see cref="ColorScheme"/> objects for equality.
  93. /// </summary>
  94. /// <param name="other"></param>
  95. /// <returns>true if the two objects are equal</returns>
  96. public bool Equals (ColorScheme other) => other != null &&
  97. EqualityComparer<Attribute>.Default.Equals (_normal, other._normal) &&
  98. EqualityComparer<Attribute>.Default.Equals (_focus, other._focus) &&
  99. EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal) &&
  100. EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus) &&
  101. EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
  102. /// <summary>
  103. /// Compares two <see cref="ColorScheme"/> objects for equality.
  104. /// </summary>
  105. /// <param name="obj"></param>
  106. /// <returns>true if the two objects are equal</returns>
  107. public override bool Equals (object obj) => Equals (obj is ColorScheme ? (ColorScheme)obj : default);
  108. /// <summary>
  109. /// Returns a hashcode for this instance.
  110. /// </summary>
  111. /// <returns>hashcode for this instance</returns>
  112. public override int GetHashCode ()
  113. {
  114. var hashCode = -1242460230;
  115. hashCode = hashCode * -1521134295 + _normal.GetHashCode ();
  116. hashCode = hashCode * -1521134295 + _focus.GetHashCode ();
  117. hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode ();
  118. hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode ();
  119. hashCode = hashCode * -1521134295 + _disabled.GetHashCode ();
  120. return hashCode;
  121. }
  122. /// <summary>
  123. /// Compares two <see cref="ColorScheme"/> objects for equality.
  124. /// </summary>
  125. /// <param name="left"></param>
  126. /// <param name="right"></param>
  127. /// <returns><c>true</c> if the two objects are equivalent</returns>
  128. public static bool operator == (ColorScheme left, ColorScheme right) => EqualityComparer<ColorScheme>.Default.Equals (left, right);
  129. /// <summary>
  130. /// Compares two <see cref="ColorScheme"/> objects for inequality.
  131. /// </summary>
  132. /// <param name="left"></param>
  133. /// <param name="right"></param>
  134. /// <returns><c>true</c> if the two objects are not equivalent</returns>
  135. public static bool operator != (ColorScheme left, ColorScheme right) => !(left == right);
  136. }
  137. /// <summary>
  138. /// Holds the <see cref="ColorScheme"/>s that define the <see cref="Attribute"/>s that are used by views to render themselves.
  139. /// </summary>
  140. public static class Colors {
  141. static Colors () => Reset ();
  142. /// <summary>
  143. /// Gets a dictionary of defined <see cref="ColorScheme"/> objects.
  144. /// </summary>
  145. /// <remarks>
  146. /// <para>
  147. /// The <see cref="ColorSchemes"/> dictionary includes the following keys, by default:
  148. /// <list type="table">
  149. /// <listheader>
  150. /// <term>Built-in Color Scheme</term>
  151. /// <description>Description</description>
  152. /// </listheader>
  153. /// <item>
  154. /// <term>
  155. /// Base
  156. /// </term>
  157. /// <description>
  158. /// The base color scheme used for most Views.
  159. /// </description>
  160. /// </item>
  161. /// <item>
  162. /// <term>
  163. /// TopLevel
  164. /// </term>
  165. /// <description>
  166. /// The application Toplevel color scheme; used for the <see cref="Toplevel"/> View.
  167. /// </description>
  168. /// </item>
  169. /// <item>
  170. /// <term>
  171. /// Dialog
  172. /// </term>
  173. /// <description>
  174. /// The dialog color scheme; used for <see cref="Dialog"/>, <see cref="MessageBox"/>, and other views dialog-like views.
  175. /// </description>
  176. /// </item>
  177. /// <item>
  178. /// <term>
  179. /// Menu
  180. /// </term>
  181. /// <description>
  182. /// The menu color scheme; used for <see cref="MenuBar"/>, <see cref="ContextMenu"/>, and <see cref="StatusBar"/>.
  183. /// </description>
  184. /// </item>
  185. /// <item>
  186. /// <term>
  187. /// Error
  188. /// </term>
  189. /// <description>
  190. /// The color scheme for showing errors, such as in <see cref="MessageBox.ErrorQuery(string, string, string[])"/>.
  191. /// </description>
  192. /// </item>
  193. /// </list>
  194. /// </para>
  195. /// <para>
  196. /// Changing the values of an entry in this dictionary will affect all views that use the scheme.
  197. /// </para>
  198. /// <para>
  199. /// <see cref="ConfigurationManager"/> can be used to override the default values for these schemes and add additional schemes.
  200. /// See <see cref="ConfigurationManager.Themes"/>.
  201. /// </para>
  202. /// </remarks>
  203. [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
  204. [JsonConverter (typeof (DictionaryJsonConverter<ColorScheme>))]
  205. public static Dictionary<string, ColorScheme> ColorSchemes { get; private set; } // Serialization requires this to have a setter (private set;)
  206. /// <summary>
  207. /// Resets the <see cref="ColorSchemes"/> dictionary to the default values.
  208. /// </summary>
  209. public static Dictionary<string, ColorScheme> Reset () =>
  210. ColorSchemes = new Dictionary<string, ColorScheme> (comparer: new SchemeNameComparerIgnoreCase ()) {
  211. { "TopLevel", new ColorScheme () },
  212. { "Base", new ColorScheme () },
  213. { "Dialog", new ColorScheme () },
  214. { "Menu", new ColorScheme () },
  215. { "Error", new ColorScheme () },
  216. };
  217. class SchemeNameComparerIgnoreCase : IEqualityComparer<string> {
  218. public bool Equals (string x, string y)
  219. {
  220. if (x != null && y != null) {
  221. return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
  222. }
  223. return false;
  224. }
  225. public int GetHashCode (string obj) => obj.ToLowerInvariant ().GetHashCode ();
  226. }
  227. }