ColorScheme.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #nullable enable
  2. using System.Globalization;
  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="ColorSchemesConfiguration.ColorSchemes"/>.
  16. /// </para>
  17. /// </remarks>
  18. [JsonConverter (typeof (ColorSchemeJsonConverter))]
  19. public class ColorScheme : IEquatable<ColorScheme> {
  20. readonly Attribute _disabled;
  21. readonly Attribute _focus;
  22. readonly Attribute _hotFocus;
  23. readonly Attribute _hotNormal;
  24. readonly Attribute _normal;
  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 is 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 is { } &&
  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 as ColorScheme);
  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. /// <inheritdoc />
  137. public override string ToString ( ) => $"Normal: {Normal}; Focus: {Focus}; HotNormal: {HotNormal}; HotFocus: {HotFocus}; Disabled: {Disabled}";
  138. }
  139. /// <summary>
  140. /// Holds the <see cref="ColorScheme"/>s that define the <see cref="Attribute"/>s that are used by views to render themselves.
  141. /// </summary>
  142. public static class Colors {
  143. static Colors () => Reset ();
  144. /// <summary>
  145. /// Gets a dictionary of defined <see cref="ColorScheme"/> objects.
  146. /// </summary>
  147. /// <remarks>
  148. /// <para>
  149. /// The <see cref="ColorSchemes"/> dictionary includes the following keys, by default:
  150. /// <list type="table">
  151. /// <listheader>
  152. /// <term>Built-in Color Scheme</term>
  153. /// <description>Description</description>
  154. /// </listheader>
  155. /// <item>
  156. /// <term>
  157. /// Base
  158. /// </term>
  159. /// <description>
  160. /// The base color scheme used for most Views.
  161. /// </description>
  162. /// </item>
  163. /// <item>
  164. /// <term>
  165. /// TopLevel
  166. /// </term>
  167. /// <description>
  168. /// The application Toplevel color scheme; used for the <see cref="Toplevel"/> View.
  169. /// </description>
  170. /// </item>
  171. /// <item>
  172. /// <term>
  173. /// Dialog
  174. /// </term>
  175. /// <description>
  176. /// The dialog color scheme; used for <see cref="Dialog"/>, <see cref="MessageBox"/>, and other views dialog-like views.
  177. /// </description>
  178. /// </item>
  179. /// <item>
  180. /// <term>
  181. /// Menu
  182. /// </term>
  183. /// <description>
  184. /// The menu color scheme; used for <see cref="MenuBar"/>, <see cref="ContextMenu"/>, and <see cref="StatusBar"/>.
  185. /// </description>
  186. /// </item>
  187. /// <item>
  188. /// <term>
  189. /// Error
  190. /// </term>
  191. /// <description>
  192. /// The color scheme for showing errors, such as in <see cref="MessageBox.ErrorQuery(string, string, string[])"/>.
  193. /// </description>
  194. /// </item>
  195. /// </list>
  196. /// </para>
  197. /// <para>
  198. /// Changing the values of an entry in this dictionary will affect all views that use the scheme.
  199. /// </para>
  200. /// <para>
  201. /// <see cref="ConfigurationManager"/> can be used to override the default values for these schemes and add additional schemes.
  202. /// See <see cref="ConfigurationManager.Themes"/>.
  203. /// </para>
  204. /// </remarks>
  205. [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
  206. [JsonConverter (typeof (DictionaryJsonConverter<ColorScheme>))]
  207. public static Dictionary<string, ColorScheme> ColorSchemes { get; private set; } // Serialization requires this to have a setter (private set;)
  208. /// <summary>
  209. /// Resets the <see cref="ColorSchemes"/> dictionary to the default values.
  210. /// </summary>
  211. public static Dictionary<string, ColorScheme> Reset ()
  212. {
  213. ColorSchemes ??= new Dictionary<string, ColorScheme> (5, CultureInfo.InvariantCulture.CompareInfo.GetStringComparer (CompareOptions.IgnoreCase));
  214. ColorSchemes.Clear ();
  215. ColorSchemes.Add ("TopLevel", new ColorScheme ());
  216. ColorSchemes.Add ("Base", new ColorScheme ());
  217. ColorSchemes.Add ("Dialog", new ColorScheme ());
  218. ColorSchemes.Add ("Menu", new ColorScheme ());
  219. ColorSchemes.Add ("Error", new ColorScheme ());
  220. return ColorSchemes;
  221. }
  222. class SchemeNameComparerIgnoreCase : IEqualityComparer<string> {
  223. public bool Equals (string x, string y)
  224. {
  225. if (x != null && y != null) {
  226. return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
  227. }
  228. return false;
  229. }
  230. public int GetHashCode (string obj) => obj.ToLowerInvariant ().GetHashCode ();
  231. }
  232. }