Color.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Immutable;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Linq;
  6. using System.Runtime.CompilerServices;
  7. using System.Text.Json.Serialization;
  8. using System.Text.RegularExpressions;
  9. namespace Terminal.Gui {
  10. /// <summary>
  11. /// Colors that can be used to set the foreground and background colors in console applications.
  12. /// </summary>
  13. /// <remarks>
  14. /// The <see cref="Attribute.HasValidColors"/> value indicates either no-color has been set or the color is invalid.
  15. /// </remarks>
  16. [JsonConverter (typeof (ColorJsonConverter))]
  17. public enum Color {
  18. /// <summary>
  19. /// The black color.
  20. /// </summary>
  21. Black,
  22. /// <summary>
  23. /// The blue color.
  24. /// </summary>
  25. Blue,
  26. /// <summary>
  27. /// The green color.
  28. /// </summary>
  29. Green,
  30. /// <summary>
  31. /// The cyan color.
  32. /// </summary>
  33. Cyan,
  34. /// <summary>
  35. /// The red color.
  36. /// </summary>
  37. Red,
  38. /// <summary>
  39. /// The magenta color.
  40. /// </summary>
  41. Magenta,
  42. /// <summary>
  43. /// The brown color.
  44. /// </summary>
  45. Brown,
  46. /// <summary>
  47. /// The gray color.
  48. /// </summary>
  49. Gray,
  50. /// <summary>
  51. /// The dark gray color.
  52. /// </summary>
  53. DarkGray,
  54. /// <summary>
  55. /// The bright bBlue color.
  56. /// </summary>
  57. BrightBlue,
  58. /// <summary>
  59. /// The bright green color.
  60. /// </summary>
  61. BrightGreen,
  62. /// <summary>
  63. /// The bright cyan color.
  64. /// </summary>
  65. BrightCyan,
  66. /// <summary>
  67. /// The bright red color.
  68. /// </summary>
  69. BrightRed,
  70. /// <summary>
  71. /// The bright magenta color.
  72. /// </summary>
  73. BrightMagenta,
  74. /// <summary>
  75. /// The bright yellow color.
  76. /// </summary>
  77. BrightYellow,
  78. /// <summary>
  79. /// The White color.
  80. /// </summary>
  81. White
  82. }
  83. /// <summary>
  84. /// Indicates the RGB for true colors.
  85. /// </summary>
  86. [JsonConverter (typeof (TrueColorJsonConverter))]
  87. public readonly struct TrueColor : IEquatable<TrueColor> {
  88. private static readonly ImmutableDictionary<TrueColor, Color> TrueColorToConsoleColorMap = new Dictionary<TrueColor, Color> () {
  89. { new TrueColor (0,0,0),Color.Black },
  90. { new TrueColor (0, 0, 0x80),Color.Blue },
  91. { new TrueColor (0, 0x80, 0),Color.Green},
  92. { new TrueColor (0, 0x80, 0x80),Color.Cyan},
  93. { new TrueColor (0x80, 0, 0),Color.Red},
  94. { new TrueColor (0x80, 0, 0x80),Color.Magenta},
  95. { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this
  96. { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray},
  97. { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray},
  98. { new TrueColor (0, 0, 0xFF),Color.BrightBlue},
  99. { new TrueColor (0, 0xFF, 0),Color.BrightGreen},
  100. { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan},
  101. { new TrueColor (0xFF, 0, 0),Color.BrightRed},
  102. { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta },
  103. { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow},
  104. { new TrueColor (0xFF, 0xFF, 0xFF),Color.White},
  105. }.ToImmutableDictionary ();
  106. /// <summary>
  107. /// Red color component.
  108. /// </summary>
  109. public int Red { get; }
  110. /// <summary>
  111. /// Green color component.
  112. /// </summary>
  113. public int Green { get; }
  114. /// <summary>
  115. /// Blue color component.
  116. /// </summary>
  117. public int Blue { get; }
  118. /// <summary>
  119. /// Initializes a new instance of the <see cref="TrueColor"/> struct.
  120. /// </summary>
  121. /// <param name="red"></param>
  122. /// <param name="green"></param>
  123. /// <param name="blue"></param>
  124. public TrueColor (int red, int green, int blue)
  125. {
  126. Red = red;
  127. Green = green;
  128. Blue = blue;
  129. }
  130. /// <summary>
  131. /// Converts the provided text to a <see cref="TrueColor"/>.
  132. /// </summary>
  133. /// <param name="text">The text to analyze.</param>
  134. /// <param name="trueColor">The parsed value.</param>
  135. /// <returns>A boolean value indcating whether it was successful.</returns>
  136. public static bool TryParse (string text, [NotNullWhen (true)] out TrueColor? trueColor)
  137. {
  138. // empty color
  139. if ((text == null) || (text.Length == 0)) {
  140. trueColor = null;
  141. return false;
  142. }
  143. // #RRGGBB or #RGB
  144. if ((text [0] == '#') &&
  145. ((text.Length == 7) || (text.Length == 4))) {
  146. if (text.Length == 7) {
  147. var r = Convert.ToInt32 (text.Substring (1, 2), 16);
  148. var g = Convert.ToInt32 (text.Substring (3, 2), 16);
  149. var b = Convert.ToInt32 (text.Substring (5, 2), 16);
  150. trueColor = new TrueColor (r, g, b);
  151. } else {
  152. var rText = char.ToString (text [1]);
  153. var gText = char.ToString (text [2]);
  154. var bText = char.ToString (text [3]);
  155. var r = Convert.ToInt32 (rText + rText, 16);
  156. var g = Convert.ToInt32 (gText + gText, 16);
  157. var b = Convert.ToInt32 (bText + bText, 16);
  158. trueColor = new TrueColor (r, g, b);
  159. }
  160. return true;
  161. }
  162. // rgb(XX,YY,ZZ)
  163. var match = Regex.Match (text, @"rgb\((\d+),(\d+),(\d+)\)");
  164. if (match.Success) {
  165. var r = int.Parse (match.Groups [1].Value);
  166. var g = int.Parse (match.Groups [2].Value);
  167. var b = int.Parse (match.Groups [3].Value);
  168. trueColor = new TrueColor (r, g, b);
  169. return true;
  170. }
  171. trueColor = null;
  172. return false;
  173. }
  174. /// <summary>
  175. /// Converts a <see cref="Color"/> to a <see cref="TrueColor"/> using a default mapping.
  176. /// </summary>
  177. /// <param name="consoleColor">The <see cref="Color"/> to convert.</param>
  178. /// <returns></returns>
  179. public static TrueColor? FromConsoleColor (Color consoleColor)
  180. {
  181. return consoleColor switch {
  182. Color.Black => new TrueColor (0, 0, 0),
  183. Color.Blue => new TrueColor (0, 0, 0x80),
  184. Color.Green => new TrueColor (0, 0x80, 0),
  185. Color.Cyan => new TrueColor (0, 0x80, 0x80),
  186. Color.Red => new TrueColor (0x80, 0, 0),
  187. Color.Magenta => new TrueColor (0x80, 0, 0x80),
  188. Color.Brown => new TrueColor (0xC1, 0x9C, 0x00) // TODO confirm this
  189. ,
  190. Color.Gray => new TrueColor (0xC0, 0xC0, 0xC0),
  191. Color.DarkGray => new TrueColor (0x80, 0x80, 0x80),
  192. Color.BrightBlue => new TrueColor (0, 0, 0xFF),
  193. Color.BrightGreen => new TrueColor (0, 0xFF, 0),
  194. Color.BrightCyan => new TrueColor (0, 0xFF, 0xFF),
  195. Color.BrightRed => new TrueColor (0xFF, 0, 0),
  196. Color.BrightMagenta => new TrueColor (0xFF, 0, 0xFF),
  197. Color.BrightYellow => new TrueColor (0xFF, 0xFF, 0),
  198. Color.White => new TrueColor (0xFF, 0xFF, 0xFF),
  199. var _ => null
  200. };
  201. ;
  202. }
  203. /// <summary>
  204. /// Converts the provided <see cref="TrueColor"/> to <see cref="Color"/> using a default mapping.
  205. /// </summary>
  206. /// <param name="trueColor"></param>
  207. /// <returns></returns>
  208. public static Color ToConsoleColor (TrueColor? trueColor)
  209. {
  210. if (trueColor.HasValue) {
  211. return TrueColorToConsoleColorMap.MinBy (kv => CalculateDistance (kv.Key, trueColor.Value)).Value;
  212. } else {
  213. return (Color)(-1);
  214. }
  215. }
  216. private static float CalculateDistance (TrueColor color1, TrueColor color2)
  217. {
  218. // use RGB distance
  219. return
  220. Math.Abs (color1.Red - color2.Red) +
  221. Math.Abs (color1.Green - color2.Green) +
  222. Math.Abs (color1.Blue - color2.Blue);
  223. }
  224. /// <inheritdoc/>
  225. public static bool operator == (TrueColor left, TrueColor right)
  226. {
  227. return left.Equals (right);
  228. }
  229. /// <inheritdoc/>
  230. public static bool operator != (TrueColor left, TrueColor right)
  231. {
  232. return !left.Equals (right);
  233. }
  234. /// <inheritdoc/>
  235. public override bool Equals (object obj)
  236. {
  237. return obj is TrueColor other && Equals (other);
  238. }
  239. /// <inheritdoc/>
  240. public bool Equals (TrueColor other)
  241. {
  242. return
  243. Red == other.Red &&
  244. Green == other.Green &&
  245. Blue == other.Blue;
  246. }
  247. /// <inheritdoc/>
  248. public override int GetHashCode ()
  249. {
  250. return HashCode.Combine (Red, Green, Blue);
  251. }
  252. /// <inheritdoc/>
  253. public override string ToString ()
  254. {
  255. return $"#{Red:X2}{Green:X2}{Blue:X2}";
  256. }
  257. }
  258. /// <summary>
  259. /// Attributes represent how text is styled when displayed in the terminal.
  260. /// </summary>
  261. /// <remarks>
  262. /// <see cref="Attribute"/> provides a platform independent representation of colors (and someday other forms of text styling).
  263. /// They encode both the foreground and the background color and are used in the <see cref="ColorScheme"/>
  264. /// class to define color schemes that can be used in an application.
  265. /// </remarks>
  266. [JsonConverter (typeof (AttributeJsonConverter))]
  267. public struct Attribute : IEquatable<Attribute> {
  268. /// <summary>
  269. /// Default empty attribute.
  270. /// </summary>
  271. public static readonly Attribute Default = new Attribute (Color.White, Color.Black);
  272. /// <summary>
  273. /// The <see cref="ConsoleDriver"/>-specific color value. If <see cref="Initialized"/> is <see langword="false"/>
  274. /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded)
  275. /// and the attribute should be re-made (see <see cref="Make(Color, Color)"/>) before it is used.
  276. /// </summary>
  277. [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
  278. internal int Value { get; }
  279. /// <summary>
  280. /// The foreground color.
  281. /// </summary>
  282. [JsonConverter (typeof (ColorJsonConverter))]
  283. public Color Foreground { get; private init; }
  284. /// <summary>
  285. /// The background color.
  286. /// </summary>
  287. [JsonConverter (typeof (ColorJsonConverter))]
  288. public Color Background { get; private init; }
  289. /// <summary>
  290. /// Gets the TrueColor foreground color.
  291. /// </summary>
  292. [JsonConverter (typeof (TrueColorJsonConverter))]
  293. public TrueColor? TrueColorForeground { get; private init; }
  294. /// <summary>
  295. /// Gets the TrueColor background color.
  296. /// </summary>
  297. [JsonConverter (typeof (TrueColorJsonConverter))]
  298. public TrueColor? TrueColorBackground { get; private init; }
  299. /// <summary>
  300. /// Initializes a new instance with a platform-specific color value.
  301. /// </summary>
  302. /// <param name="value">Value.</param>
  303. internal Attribute (int value)
  304. {
  305. Color foreground = default;
  306. Color background = default;
  307. Initialized = false;
  308. if (Application.Driver != null) {
  309. Application.Driver.GetColors (value, out foreground, out background);
  310. Initialized = true;
  311. }
  312. Value = value;
  313. Foreground = foreground;
  314. Background = background;
  315. TrueColorForeground = TrueColor.FromConsoleColor (foreground);
  316. TrueColorBackground = TrueColor.FromConsoleColor (background);
  317. }
  318. /// <summary>
  319. /// Initializes a new instance of the <see cref="Attribute"/> struct.
  320. /// </summary>
  321. /// <param name="value">platform-dependent color value.</param>
  322. /// <param name="foreground">Foreground</param>
  323. /// <param name="background">Background</param>
  324. public Attribute (int value, Color foreground, Color background)
  325. {
  326. Foreground = foreground;
  327. Background = background;
  328. TrueColorForeground = TrueColor.FromConsoleColor (foreground);
  329. TrueColorBackground = TrueColor.FromConsoleColor (background);
  330. Value = value;
  331. Initialized = true;
  332. }
  333. /// <summary>
  334. /// Initializes a new instance of the <see cref="Attribute"/> struct.
  335. /// </summary>
  336. /// <param name="foreground">Foreground</param>
  337. /// <param name="background">Background</param>
  338. public Attribute (Color foreground = new Color (), Color background = new Color ())
  339. {
  340. Foreground = foreground;
  341. Background = background;
  342. TrueColorForeground = TrueColor.FromConsoleColor (foreground);
  343. TrueColorBackground = TrueColor.FromConsoleColor (background);
  344. var make = Make (foreground, background);
  345. Initialized = make.Initialized;
  346. Value = make.Value;
  347. }
  348. /// <summary>
  349. /// Initializes a new instance of the <see cref="Attribute"/> class. Populates
  350. /// <see cref="TrueColorBackground"/> and <see cref="TrueColorForeground"/>. Also computes
  351. /// <see cref="Foreground"/> and <see cref="Background"/> (basic console colors) in case
  352. /// driver does not support true color rendering.
  353. /// </summary>
  354. /// <param name="trueColorForeground"></param>
  355. /// <param name="trueColorBackground"></param>
  356. public Attribute (TrueColor? trueColorForeground, TrueColor? trueColorBackground)
  357. {
  358. Foreground = TrueColor.ToConsoleColor (trueColorForeground);
  359. Background = TrueColor.ToConsoleColor (trueColorBackground);
  360. TrueColorForeground = trueColorForeground;
  361. TrueColorBackground = trueColorBackground;
  362. var make = Make (Foreground, Background);
  363. Value = make.Value;
  364. Initialized = make.Initialized;
  365. }
  366. /// <summary>
  367. /// <para>
  368. /// Initializes a new instance of the <see cref="Attribute"/> class. Populates
  369. /// <see cref="TrueColorBackground"/> and <see cref="TrueColorForeground"/> with explicit
  370. /// fallback values for <see cref="Foreground"/> and <see cref="Background"/> (in case
  371. /// driver does not support true color rendering).
  372. /// </para>
  373. /// <remarks>If you do not want to manually specify the fallback colors use <see cref="Attribute(TrueColor?,TrueColor?)"/>
  374. /// instead which auto calculates these.</remarks>
  375. /// </summary>
  376. /// <param name="trueColorForeground">True color RGB values you would like to use.</param>
  377. /// <param name="trueColorBackground">True color RGB values you would like to use.</param>
  378. /// <param name="foreground">Simple console color replacement if driver does not support true color.</param>
  379. /// <param name="background">Simple console color replacement if driver does not support true color.</param>
  380. public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground, Color foreground, Color background)
  381. {
  382. Foreground = foreground;
  383. Background = background;
  384. TrueColorForeground = trueColorForeground;
  385. TrueColorBackground = trueColorBackground;
  386. var make = Make (Foreground, Background);
  387. Value = make.Value;
  388. Initialized = make.Initialized;
  389. }
  390. /// <summary>
  391. /// Initializes a new instance of the <see cref="Attribute"/> struct
  392. /// with the same colors for the foreground and background.
  393. /// </summary>
  394. /// <param name="color">The color.</param>
  395. public Attribute (Color color) : this (color, color) { }
  396. /// <summary>
  397. /// Compares two attributes for equality.
  398. /// </summary>
  399. /// <param name="left"></param>
  400. /// <param name="right"></param>
  401. /// <returns></returns>
  402. public static bool operator == (Attribute left, Attribute right) => left.Equals (right);
  403. /// <summary>
  404. /// Compares two attributes for inequality.
  405. /// </summary>
  406. /// <param name="left"></param>
  407. /// <param name="right"></param>
  408. /// <returns></returns>
  409. public static bool operator != (Attribute left, Attribute right) => !(left == right);
  410. /// <inheritdoc />
  411. public override bool Equals (object obj)
  412. {
  413. return obj is Attribute other && Equals (other);
  414. }
  415. /// <inheritdoc />
  416. public bool Equals (Attribute other)
  417. {
  418. if (TrueColorForeground.HasValue || TrueColorBackground.HasValue) {
  419. return
  420. TrueColorForeground == other.TrueColorForeground &&
  421. TrueColorBackground == other.TrueColorBackground;
  422. }
  423. return Value == other.Value &&
  424. Foreground == other.Foreground &&
  425. Background == other.Background;
  426. }
  427. /// <inheritdoc />
  428. public override int GetHashCode () => HashCode.Combine (Value, Foreground, Background, TrueColorForeground, TrueColorBackground);
  429. /// <summary>
  430. /// Creates an <see cref="Attribute"/> from the specified foreground and background colors.
  431. /// </summary>
  432. /// <remarks>
  433. /// If a <see cref="ConsoleDriver"/> has not been loaded (<c>Application.Driver == null</c>) this
  434. /// method will return an attribute with <see cref="Initialized"/> set to <see langword="false"/>.
  435. /// </remarks>
  436. /// <returns>The new attribute.</returns>
  437. /// <param name="foreground">Foreground color to use.</param>
  438. /// <param name="background">Background color to use.</param>
  439. public static Attribute Make (Color foreground, Color background)
  440. {
  441. if (Application.Driver == null) {
  442. // Create the attribute, but show it's not been initialized
  443. return new Attribute () {
  444. Initialized = false,
  445. Foreground = foreground,
  446. Background = background
  447. };
  448. }
  449. return Application.Driver.MakeAttribute (foreground, background);
  450. }
  451. /// <summary>
  452. /// Gets the current <see cref="Attribute"/> from the driver.
  453. /// </summary>
  454. /// <returns>The current attribute.</returns>
  455. public static Attribute Get ()
  456. {
  457. if (Application.Driver == null) {
  458. throw new InvalidOperationException ("The Application has not been initialized");
  459. }
  460. return Application.Driver.GetAttribute ();
  461. }
  462. /// <summary>
  463. /// If <see langword="true"/> the attribute has been initialized by a <see cref="ConsoleDriver"/> and
  464. /// thus has <see cref="Value"/> that is valid for that driver. If <see langword="false"/> the <see cref="Foreground"/>
  465. /// and <see cref="Background"/> colors may have been set '-1' but
  466. /// the attribute has not been mapped to a <see cref="ConsoleDriver"/> specific color value.
  467. /// </summary>
  468. /// <remarks>
  469. /// Attributes that have not been initialized must eventually be initialized before being passed to a driver.
  470. /// </remarks>
  471. [JsonIgnore]
  472. public bool Initialized { get; internal set; }
  473. /// <summary>
  474. /// Returns <see langword="true"/> if the Attribute is valid (both foreground and background have valid color values).
  475. /// </summary>
  476. /// <returns></returns>
  477. [JsonIgnore]
  478. public bool HasValidColors => (int)Foreground > -1 && (int)Background > -1;
  479. /// <inheritdoc />
  480. public override string ToString ()
  481. {
  482. // Note, Unit tests are dependent on this format
  483. return $"{Foreground},{Background}";
  484. }
  485. }
  486. /// <summary>
  487. /// Defines the color <see cref="Attribute"/>s for common visible elements in a <see cref="View"/>.
  488. /// Containers such as <see cref="Window"/> and <see cref="FrameView"/> use <see cref="ColorScheme"/> to determine
  489. /// the colors used by sub-views.
  490. /// </summary>
  491. /// <remarks>
  492. /// See also: <see cref="Colors.ColorSchemes"/>.
  493. /// </remarks>
  494. [JsonConverter (typeof (ColorSchemeJsonConverter))]
  495. public class ColorScheme : IEquatable<ColorScheme> {
  496. Attribute _normal = Attribute.Default;
  497. Attribute _focus = Attribute.Default;
  498. Attribute _hotNormal = Attribute.Default;
  499. Attribute _hotFocus = Attribute.Default;
  500. Attribute _disabled = Attribute.Default;
  501. /// <summary>
  502. /// Used by <see cref="Colors.SetColorScheme(ColorScheme, string)"/> and <see cref="Colors.GetColorScheme(string)"/> to track which ColorScheme
  503. /// is being accessed.
  504. /// </summary>
  505. internal string schemeBeingSet = "";
  506. /// <summary>
  507. /// Creates a new instance.
  508. /// </summary>
  509. public ColorScheme () { }
  510. /// <summary>
  511. /// Creates a new instance, initialized with the values from <paramref name="scheme"/>.
  512. /// </summary>
  513. /// <param name="scheme">The scheme to initialize the new instance with.</param>
  514. public ColorScheme (ColorScheme scheme) : base ()
  515. {
  516. if (scheme != null) {
  517. _normal = scheme.Normal;
  518. _focus = scheme.Focus;
  519. _hotNormal = scheme.HotNormal;
  520. _disabled = scheme.Disabled;
  521. _hotFocus = scheme.HotFocus;
  522. }
  523. }
  524. /// <summary>
  525. /// Creates a new instance, initialized with the values from <paramref name="attribute"/>.
  526. /// </summary>
  527. /// <param name="attribute">The attribute to initialize the new instance with.</param>
  528. public ColorScheme (Attribute attribute)
  529. {
  530. _normal = attribute;
  531. _focus = attribute;
  532. _hotNormal = attribute;
  533. _disabled = attribute;
  534. _hotFocus = attribute;
  535. }
  536. /// <summary>
  537. /// The foreground and background color for text when the view is not focused, hot, or disabled.
  538. /// </summary>
  539. public Attribute Normal {
  540. get { return _normal; }
  541. set {
  542. if (!value.HasValidColors) {
  543. return;
  544. }
  545. _normal = value;
  546. }
  547. }
  548. /// <summary>
  549. /// The foreground and background color for text when the view has the focus.
  550. /// </summary>
  551. public Attribute Focus {
  552. get { return _focus; }
  553. set {
  554. if (!value.HasValidColors) {
  555. return;
  556. }
  557. _focus = value;
  558. }
  559. }
  560. /// <summary>
  561. /// The foreground and background color for text when the view is highlighted (hot).
  562. /// </summary>
  563. public Attribute HotNormal {
  564. get { return _hotNormal; }
  565. set {
  566. if (!value.HasValidColors) {
  567. return;
  568. }
  569. _hotNormal = value;
  570. }
  571. }
  572. /// <summary>
  573. /// The foreground and background color for text when the view is highlighted (hot) and has focus.
  574. /// </summary>
  575. public Attribute HotFocus {
  576. get { return _hotFocus; }
  577. set {
  578. if (!value.HasValidColors) {
  579. return;
  580. }
  581. _hotFocus = value;
  582. }
  583. }
  584. /// <summary>
  585. /// The default foreground and background color for text, when the view is disabled.
  586. /// </summary>
  587. public Attribute Disabled {
  588. get { return _disabled; }
  589. set {
  590. if (!value.HasValidColors) {
  591. return;
  592. }
  593. _disabled = value;
  594. }
  595. }
  596. /// <summary>
  597. /// Compares two <see cref="ColorScheme"/> objects for equality.
  598. /// </summary>
  599. /// <param name="obj"></param>
  600. /// <returns>true if the two objects are equal</returns>
  601. public override bool Equals (object obj)
  602. {
  603. return Equals (obj as ColorScheme);
  604. }
  605. /// <summary>
  606. /// Compares two <see cref="ColorScheme"/> objects for equality.
  607. /// </summary>
  608. /// <param name="other"></param>
  609. /// <returns>true if the two objects are equal</returns>
  610. public bool Equals (ColorScheme other)
  611. {
  612. return other != null &&
  613. EqualityComparer<Attribute>.Default.Equals (_normal, other._normal) &&
  614. EqualityComparer<Attribute>.Default.Equals (_focus, other._focus) &&
  615. EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal) &&
  616. EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus) &&
  617. EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
  618. }
  619. /// <summary>
  620. /// Returns a hashcode for this instance.
  621. /// </summary>
  622. /// <returns>hashcode for this instance</returns>
  623. public override int GetHashCode ()
  624. {
  625. int hashCode = -1242460230;
  626. hashCode = hashCode * -1521134295 + _normal.GetHashCode ();
  627. hashCode = hashCode * -1521134295 + _focus.GetHashCode ();
  628. hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode ();
  629. hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode ();
  630. hashCode = hashCode * -1521134295 + _disabled.GetHashCode ();
  631. return hashCode;
  632. }
  633. /// <summary>
  634. /// Compares two <see cref="ColorScheme"/> objects for equality.
  635. /// </summary>
  636. /// <param name="left"></param>
  637. /// <param name="right"></param>
  638. /// <returns><c>true</c> if the two objects are equivalent</returns>
  639. public static bool operator == (ColorScheme left, ColorScheme right)
  640. {
  641. return EqualityComparer<ColorScheme>.Default.Equals (left, right);
  642. }
  643. /// <summary>
  644. /// Compares two <see cref="ColorScheme"/> objects for inequality.
  645. /// </summary>
  646. /// <param name="left"></param>
  647. /// <param name="right"></param>
  648. /// <returns><c>true</c> if the two objects are not equivalent</returns>
  649. public static bool operator != (ColorScheme left, ColorScheme right)
  650. {
  651. return !(left == right);
  652. }
  653. internal void Initialize ()
  654. {
  655. // If the new scheme was created before a driver was loaded, we need to re-make
  656. // the attributes
  657. if (!_normal.Initialized) {
  658. _normal = new Attribute (_normal.Foreground, _normal.Background);
  659. }
  660. if (!_focus.Initialized) {
  661. _focus = new Attribute (_focus.Foreground, _focus.Background);
  662. }
  663. if (!_hotNormal.Initialized) {
  664. _hotNormal = new Attribute (_hotNormal.Foreground, _hotNormal.Background);
  665. }
  666. if (!_hotFocus.Initialized) {
  667. _hotFocus = new Attribute (_hotFocus.Foreground, _hotFocus.Background);
  668. }
  669. if (!_disabled.Initialized) {
  670. _disabled = new Attribute (_disabled.Foreground, _disabled.Background);
  671. }
  672. }
  673. }
  674. /// <summary>
  675. /// The default <see cref="ColorScheme"/>s for the application.
  676. /// </summary>
  677. /// <remarks>
  678. /// This property can be set in a Theme to change the default <see cref="Colors"/> for the application.
  679. /// </remarks>
  680. public static class Colors {
  681. private class SchemeNameComparerIgnoreCase : IEqualityComparer<string> {
  682. public bool Equals (string x, string y)
  683. {
  684. if (x != null && y != null) {
  685. return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
  686. }
  687. return false;
  688. }
  689. public int GetHashCode (string obj)
  690. {
  691. return obj.ToLowerInvariant ().GetHashCode ();
  692. }
  693. }
  694. static Colors ()
  695. {
  696. ColorSchemes = Create ();
  697. }
  698. /// <summary>
  699. /// Creates a new dictionary of new <see cref="ColorScheme"/> objects.
  700. /// </summary>
  701. public static Dictionary<string, ColorScheme> Create ()
  702. {
  703. // Use reflection to dynamically create the default set of ColorSchemes from the list defined
  704. // by the class.
  705. return typeof (Colors).GetProperties ()
  706. .Where (p => p.PropertyType == typeof (ColorScheme))
  707. .Select (p => new KeyValuePair<string, ColorScheme> (p.Name, new ColorScheme ()))
  708. .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ());
  709. }
  710. /// <summary>
  711. /// The application Toplevel color scheme, for the default Toplevel views.
  712. /// </summary>
  713. /// <remarks>
  714. /// <para>
  715. /// This API will be deprecated in the future. Use <see cref="Colors.ColorSchemes"/> instead (e.g. <c>edit.ColorScheme = Colors.ColorSchemes["TopLevel"];</c>
  716. /// </para>
  717. /// </remarks>
  718. public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); }
  719. /// <summary>
  720. /// The base color scheme, for the default Toplevel views.
  721. /// </summary>
  722. /// <remarks>
  723. /// <para>
  724. /// This API will be deprecated in the future. Use <see cref="Colors.ColorSchemes"/> instead (e.g. <c>edit.ColorScheme = Colors.ColorSchemes["Base"];</c>
  725. /// </para>
  726. /// </remarks>
  727. public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); }
  728. /// <summary>
  729. /// The dialog color scheme, for standard popup dialog boxes
  730. /// </summary>
  731. /// <remarks>
  732. /// <para>
  733. /// This API will be deprecated in the future. Use <see cref="Colors.ColorSchemes"/> instead (e.g. <c>edit.ColorScheme = Colors.ColorSchemes["Dialog"];</c>
  734. /// </para>
  735. /// </remarks>
  736. public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); }
  737. /// <summary>
  738. /// The menu bar color
  739. /// </summary>
  740. /// <remarks>
  741. /// <para>
  742. /// This API will be deprecated in the future. Use <see cref="Colors.ColorSchemes"/> instead (e.g. <c>edit.ColorScheme = Colors.ColorSchemes["Menu"];</c>
  743. /// </para>
  744. /// </remarks>
  745. public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); }
  746. /// <summary>
  747. /// The color scheme for showing errors.
  748. /// </summary>
  749. /// <remarks>
  750. /// <para>
  751. /// This API will be deprecated in the future. Use <see cref="Colors.ColorSchemes"/> instead (e.g. <c>edit.ColorScheme = Colors.ColorSchemes["Error"];</c>
  752. /// </para>
  753. /// </remarks>
  754. public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); }
  755. static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null)
  756. {
  757. return ColorSchemes [schemeBeingSet];
  758. }
  759. static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null)
  760. {
  761. ColorSchemes [schemeBeingSet] = colorScheme;
  762. colorScheme.schemeBeingSet = schemeBeingSet;
  763. }
  764. /// <summary>
  765. /// Provides the defined <see cref="ColorScheme"/>s.
  766. /// </summary>
  767. [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
  768. [JsonConverter (typeof (DictionaryJsonConverter<ColorScheme>))]
  769. public static Dictionary<string, ColorScheme> ColorSchemes { get; private set; }
  770. }
  771. }