ColorScheme.cs 9.7 KB

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