ColorScheme.cs 9.8 KB

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