ColorPickerTests.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. using System.Reflection.Emit;
  2. using Xunit.Abstractions;
  3. using Color = Terminal.Gui.Color;
  4. namespace Terminal.Gui.ViewsTests;
  5. public class ColorPickerTests
  6. {
  7. [Fact]
  8. [SetupFakeDriver]
  9. public void ColorPicker_Construct_DefaultValue ()
  10. {
  11. var cp = GetColorPicker (ColorModel.HSV, false);
  12. // Should be only a single text field (Hex) because ShowTextFields is false
  13. Assert.Single (cp.Subviews.OfType<TextField> ());
  14. cp.Draw ();
  15. // All bars should be at 0 with the triangle at 0 (+2 because of "H:", "S:" etc)
  16. var h = GetColorBar (cp, ColorPickerPart.Bar1);
  17. Assert.Equal ("H:", h.Text);
  18. Assert.Equal (2, h.TrianglePosition);
  19. Assert.IsType<HueBar> (h);
  20. var s = GetColorBar (cp, ColorPickerPart.Bar2);
  21. Assert.Equal ("S:", s.Text);
  22. Assert.Equal (2, s.TrianglePosition);
  23. Assert.IsType<SaturationBar> (s);
  24. var v = GetColorBar (cp, ColorPickerPart.Bar3);
  25. Assert.Equal ("V:", v.Text);
  26. Assert.Equal (2, v.TrianglePosition);
  27. Assert.IsType<ValueBar> (v);
  28. var hex = GetTextField (cp, ColorPickerPart.Hex);
  29. Assert.Equal ("#000000", hex.Text);
  30. }
  31. [Fact]
  32. [SetupFakeDriver]
  33. public void ColorPicker_RGB_KeyboardNavigation ()
  34. {
  35. var cp = GetColorPicker (ColorModel.RGB, false);
  36. cp.Draw ();
  37. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  38. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  39. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  40. var hex = GetTextField (cp, ColorPickerPart.Hex);
  41. Assert.Equal ("R:", r.Text);
  42. Assert.Equal (2, r.TrianglePosition);
  43. Assert.IsType<RBar> (r);
  44. Assert.Equal ("G:", g.Text);
  45. Assert.Equal (2, g.TrianglePosition);
  46. Assert.IsType<GBar> (g);
  47. Assert.Equal ("B:", b.Text);
  48. Assert.Equal (2, b.TrianglePosition);
  49. Assert.IsType<BBar> (b);
  50. Assert.Equal ("#000000", hex.Text);
  51. Assert.IsAssignableFrom<IColorBar> (cp.Focused);
  52. cp.Draw ();
  53. Application.RaiseKeyDownEvent (Key.CursorRight);
  54. cp.Draw ();
  55. Assert.Equal (3, r.TrianglePosition);
  56. Assert.Equal ("#0F0000", hex.Text);
  57. Application.RaiseKeyDownEvent (Key.CursorRight);
  58. cp.Draw ();
  59. Assert.Equal (4, r.TrianglePosition);
  60. Assert.Equal ("#1E0000", hex.Text);
  61. // Use cursor to move the triangle all the way to the right
  62. for (int i = 0; i < 1000; i++)
  63. {
  64. Application.RaiseKeyDownEvent (Key.CursorRight);
  65. }
  66. cp.Draw ();
  67. // 20 width and TrianglePosition is 0 indexed
  68. // Meaning we are asserting that triangle is at end
  69. Assert.Equal (19, r.TrianglePosition);
  70. Assert.Equal ("#FF0000", hex.Text);
  71. Application.Top.Dispose ();
  72. }
  73. [Fact]
  74. [SetupFakeDriver]
  75. public void ColorPicker_RGB_MouseNavigation ()
  76. {
  77. var cp = GetColorPicker (ColorModel.RGB, false);
  78. cp.Draw ();
  79. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  80. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  81. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  82. var hex = GetTextField (cp, ColorPickerPart.Hex);
  83. Assert.Equal ("R:", r.Text);
  84. Assert.Equal (2, r.TrianglePosition);
  85. Assert.IsType<RBar> (r);
  86. Assert.Equal ("G:", g.Text);
  87. Assert.Equal (2, g.TrianglePosition);
  88. Assert.IsType<GBar> (g);
  89. Assert.Equal ("B:", b.Text);
  90. Assert.Equal (2, b.TrianglePosition);
  91. Assert.IsType<BBar> (b);
  92. Assert.Equal ("#000000", hex.Text);
  93. Assert.IsAssignableFrom<IColorBar> (cp.Focused);
  94. cp.Focused.OnMouseEvent (
  95. new ()
  96. {
  97. Flags = MouseFlags.Button1Pressed,
  98. Position = new (3, 0)
  99. });
  100. cp.Draw ();
  101. Assert.Equal (3, r.TrianglePosition);
  102. Assert.Equal ("#0F0000", hex.Text);
  103. cp.Focused.OnMouseEvent (
  104. new ()
  105. {
  106. Flags = MouseFlags.Button1Pressed,
  107. Position = new (4, 0)
  108. });
  109. cp.Draw ();
  110. Assert.Equal (4, r.TrianglePosition);
  111. Assert.Equal ("#1E0000", hex.Text);
  112. Application.Top?.Dispose ();
  113. }
  114. public static IEnumerable<object []> ColorPickerTestData ()
  115. {
  116. yield return new object []
  117. {
  118. new Color(255, 0),
  119. "R:", 19, "G:", 2, "B:", 2, "#FF0000"
  120. };
  121. yield return new object []
  122. {
  123. new Color(0, 255),
  124. "R:", 2, "G:", 19, "B:", 2, "#00FF00"
  125. };
  126. yield return new object []
  127. {
  128. new Color(0, 0, 255),
  129. "R:", 2, "G:", 2, "B:", 19, "#0000FF"
  130. };
  131. yield return new object []
  132. {
  133. new Color(125, 125, 125),
  134. "R:", 11, "G:", 11, "B:", 11, "#7D7D7D"
  135. };
  136. }
  137. [Theory]
  138. [SetupFakeDriver]
  139. [MemberData (nameof (ColorPickerTestData))]
  140. public void ColorPicker_RGB_NoText (Color c, string expectedR, int expectedRTriangle, string expectedG, int expectedGTriangle, string expectedB, int expectedBTriangle, string expectedHex)
  141. {
  142. var cp = GetColorPicker (ColorModel.RGB, false);
  143. cp.SelectedColor = c;
  144. cp.Draw ();
  145. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  146. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  147. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  148. var hex = GetTextField (cp, ColorPickerPart.Hex);
  149. Assert.Equal (expectedR, r.Text);
  150. Assert.Equal (expectedRTriangle, r.TrianglePosition);
  151. Assert.Equal (expectedG, g.Text);
  152. Assert.Equal (expectedGTriangle, g.TrianglePosition);
  153. Assert.Equal (expectedB, b.Text);
  154. Assert.Equal (expectedBTriangle, b.TrianglePosition);
  155. Assert.Equal (expectedHex, hex.Text);
  156. Application.Top.Dispose ();
  157. }
  158. public static IEnumerable<object []> ColorPickerTestData_WithTextFields ()
  159. {
  160. yield return new object []
  161. {
  162. new Color(255, 0),
  163. "R:", 15, 255, "G:", 2, 0, "B:", 2, 0, "#FF0000"
  164. };
  165. yield return new object []
  166. {
  167. new Color(0, 255),
  168. "R:", 2, 0, "G:", 15, 255, "B:", 2, 0, "#00FF00"
  169. };
  170. yield return new object []
  171. {
  172. new Color(0, 0, 255),
  173. "R:", 2, 0, "G:", 2, 0, "B:", 15, 255, "#0000FF"
  174. };
  175. yield return new object []
  176. {
  177. new Color(125, 125, 125),
  178. "R:", 9, 125, "G:", 9, 125, "B:", 9, 125, "#7D7D7D"
  179. };
  180. }
  181. [Theory]
  182. [SetupFakeDriver]
  183. [MemberData (nameof (ColorPickerTestData_WithTextFields))]
  184. public void ColorPicker_RGB_NoText_WithTextFields (Color c, string expectedR, int expectedRTriangle, int expectedRValue, string expectedG, int expectedGTriangle, int expectedGValue, string expectedB, int expectedBTriangle, int expectedBValue, string expectedHex)
  185. {
  186. var cp = GetColorPicker (ColorModel.RGB, true);
  187. cp.SelectedColor = c;
  188. cp.Draw ();
  189. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  190. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  191. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  192. var hex = GetTextField (cp, ColorPickerPart.Hex);
  193. var rTextField = GetTextField (cp, ColorPickerPart.Bar1);
  194. var gTextField = GetTextField (cp, ColorPickerPart.Bar2);
  195. var bTextField = GetTextField (cp, ColorPickerPart.Bar3);
  196. Assert.Equal (expectedR, r.Text);
  197. Assert.Equal (expectedRTriangle, r.TrianglePosition);
  198. Assert.Equal (expectedRValue.ToString (), rTextField.Text);
  199. Assert.Equal (expectedG, g.Text);
  200. Assert.Equal (expectedGTriangle, g.TrianglePosition);
  201. Assert.Equal (expectedGValue.ToString (), gTextField.Text);
  202. Assert.Equal (expectedB, b.Text);
  203. Assert.Equal (expectedBTriangle, b.TrianglePosition);
  204. Assert.Equal (expectedBValue.ToString (), bTextField.Text);
  205. Assert.Equal (expectedHex, hex.Text);
  206. Application.Top?.Dispose ();
  207. }
  208. [Fact]
  209. [SetupFakeDriver]
  210. public void ColorPicker_ClickingAtEndOfBar_SetsMaxValue ()
  211. {
  212. var cp = GetColorPicker (ColorModel.RGB, false);
  213. cp.Draw ();
  214. // Click at the end of the Red bar
  215. cp.Focused.OnMouseEvent (
  216. new ()
  217. {
  218. Flags = MouseFlags.Button1Pressed,
  219. Position = new (19, 0) // Assuming 0-based indexing
  220. });
  221. cp.Draw ();
  222. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  223. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  224. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  225. var hex = GetTextField (cp, ColorPickerPart.Hex);
  226. Assert.Equal ("R:", r.Text);
  227. Assert.Equal (19, r.TrianglePosition);
  228. Assert.Equal ("G:", g.Text);
  229. Assert.Equal (2, g.TrianglePosition);
  230. Assert.Equal ("B:", b.Text);
  231. Assert.Equal (2, b.TrianglePosition);
  232. Assert.Equal ("#FF0000", hex.Text);
  233. Application.Top?.Dispose ();
  234. }
  235. [Fact]
  236. [SetupFakeDriver]
  237. public void ColorPicker_ClickingBeyondBar_ChangesToMaxValue ()
  238. {
  239. var cp = GetColorPicker (ColorModel.RGB, false);
  240. cp.Draw ();
  241. // Click beyond the bar
  242. cp.Focused.OnMouseEvent (
  243. new ()
  244. {
  245. Flags = MouseFlags.Button1Pressed,
  246. Position = new (21, 0) // Beyond the bar
  247. });
  248. cp.Draw ();
  249. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  250. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  251. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  252. var hex = GetTextField (cp, ColorPickerPart.Hex);
  253. Assert.Equal ("R:", r.Text);
  254. Assert.Equal (19, r.TrianglePosition);
  255. Assert.Equal ("G:", g.Text);
  256. Assert.Equal (2, g.TrianglePosition);
  257. Assert.Equal ("B:", b.Text);
  258. Assert.Equal (2, b.TrianglePosition);
  259. Assert.Equal ("#FF0000", hex.Text);
  260. Application.Top?.Dispose ();
  261. }
  262. [Fact]
  263. [SetupFakeDriver]
  264. public void ColorPicker_ChangeValueOnUI_UpdatesAllUIElements ()
  265. {
  266. var cp = GetColorPicker (ColorModel.RGB, true);
  267. View otherView = new View () { CanFocus = true };
  268. Application.Top?.Add (otherView); // thi sets focus to otherView
  269. Assert.True (otherView.HasFocus);
  270. cp.SetFocus ();
  271. Assert.False (otherView.HasFocus);
  272. cp.Draw ();
  273. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  274. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  275. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  276. var hex = GetTextField (cp, ColorPickerPart.Hex);
  277. var rTextField = GetTextField (cp, ColorPickerPart.Bar1);
  278. var gTextField = GetTextField (cp, ColorPickerPart.Bar2);
  279. var bTextField = GetTextField (cp, ColorPickerPart.Bar3);
  280. Assert.Equal ("R:", r.Text);
  281. Assert.Equal (2, r.TrianglePosition);
  282. Assert.Equal ("0", rTextField.Text);
  283. Assert.Equal ("G:", g.Text);
  284. Assert.Equal (2, g.TrianglePosition);
  285. Assert.Equal ("0", gTextField.Text);
  286. Assert.Equal ("B:", b.Text);
  287. Assert.Equal (2, b.TrianglePosition);
  288. Assert.Equal ("0", bTextField.Text);
  289. Assert.Equal ("#000000", hex.Text);
  290. // Change value using text field
  291. TextField rBarTextField = cp.Subviews.OfType<TextField> ().First (tf => tf.Text == "0");
  292. rBarTextField.SetFocus ();
  293. rBarTextField.Text = "128";
  294. otherView.SetFocus ();
  295. Assert.True (otherView.HasFocus);
  296. cp.Draw ();
  297. Assert.Equal ("R:", r.Text);
  298. Assert.Equal (9, r.TrianglePosition);
  299. Assert.Equal ("128", rTextField.Text);
  300. Assert.Equal ("G:", g.Text);
  301. Assert.Equal (2, g.TrianglePosition);
  302. Assert.Equal ("0", gTextField.Text);
  303. Assert.Equal ("B:", b.Text);
  304. Assert.Equal (2, b.TrianglePosition);
  305. Assert.Equal ("0", bTextField.Text);
  306. Assert.Equal ("#800000", hex.Text);
  307. Application.Top?.Dispose ();
  308. }
  309. [Fact]
  310. [SetupFakeDriver]
  311. public void ColorPicker_InvalidHexInput_DoesNotChangeColor ()
  312. {
  313. var cp = GetColorPicker (ColorModel.RGB, true);
  314. cp.Draw ();
  315. // Enter invalid hex value
  316. TextField hexField = cp.Subviews.OfType<TextField> ().First (tf => tf.Text == "#000000");
  317. hexField.SetFocus ();
  318. hexField.Text = "#ZZZZZZ";
  319. Assert.True (hexField.HasFocus);
  320. Assert.Equal ("#ZZZZZZ", hexField.Text);
  321. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  322. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  323. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  324. var hex = GetTextField (cp, ColorPickerPart.Hex);
  325. Assert.Equal ("#ZZZZZZ", hex.Text);
  326. // Advance away from hexField to cause validation
  327. cp.AdvanceFocus (NavigationDirection.Forward, null);
  328. cp.Draw ();
  329. Assert.Equal ("R:", r.Text);
  330. Assert.Equal (2, r.TrianglePosition);
  331. Assert.Equal ("G:", g.Text);
  332. Assert.Equal (2, g.TrianglePosition);
  333. Assert.Equal ("B:", b.Text);
  334. Assert.Equal (2, b.TrianglePosition);
  335. Assert.Equal ("#000000", hex.Text);
  336. Application.Top?.Dispose ();
  337. }
  338. [Fact]
  339. [SetupFakeDriver]
  340. public void ColorPicker_ClickingDifferentBars_ChangesFocus ()
  341. {
  342. var cp = GetColorPicker (ColorModel.RGB, false);
  343. cp.Draw ();
  344. // Click on Green bar
  345. Application.RaiseMouseEvent (new ()
  346. {
  347. Flags = MouseFlags.Button1Pressed,
  348. ScreenPosition = new (0, 1)
  349. });
  350. //cp.Subviews.OfType<GBar> ()
  351. // .Single ()
  352. // .OnMouseEvent (
  353. // new ()
  354. // {
  355. // Flags = MouseFlags.Button1Pressed,
  356. // Position = new (0, 1)
  357. // });
  358. cp.Draw ();
  359. Assert.IsAssignableFrom<GBar> (cp.Focused);
  360. // Click on Blue bar
  361. Application.RaiseMouseEvent (new ()
  362. {
  363. Flags = MouseFlags.Button1Pressed,
  364. ScreenPosition = new (0, 2)
  365. });
  366. //cp.Subviews.OfType<BBar> ()
  367. // .Single ()
  368. // .OnMouseEvent (
  369. // new ()
  370. // {
  371. // Flags = MouseFlags.Button1Pressed,
  372. // Position = new (0, 2)
  373. // });
  374. cp.Draw ();
  375. Assert.IsAssignableFrom<BBar> (cp.Focused);
  376. Application.Top?.Dispose ();
  377. }
  378. [Fact]
  379. [SetupFakeDriver]
  380. public void ColorPicker_SwitchingColorModels_ResetsBars ()
  381. {
  382. var cp = GetColorPicker (ColorModel.RGB, false);
  383. cp.BeginInit ();
  384. cp.EndInit ();
  385. cp.SelectedColor = new (255, 0);
  386. cp.Draw ();
  387. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  388. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  389. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  390. var hex = GetTextField (cp, ColorPickerPart.Hex);
  391. Assert.Equal ("R:", r.Text);
  392. Assert.Equal (19, r.TrianglePosition);
  393. Assert.Equal ("G:", g.Text);
  394. Assert.Equal (2, g.TrianglePosition);
  395. Assert.Equal ("B:", b.Text);
  396. Assert.Equal (2, b.TrianglePosition);
  397. Assert.Equal ("#FF0000", hex.Text);
  398. // Switch to HSV
  399. cp.Style.ColorModel = ColorModel.HSV;
  400. cp.ApplyStyleChanges ();
  401. cp.Draw ();
  402. var h = GetColorBar (cp, ColorPickerPart.Bar1);
  403. var s = GetColorBar (cp, ColorPickerPart.Bar2);
  404. var v = GetColorBar (cp, ColorPickerPart.Bar3);
  405. Assert.Equal ("H:", h.Text);
  406. Assert.Equal (2, h.TrianglePosition);
  407. Assert.Equal ("S:", s.Text);
  408. Assert.Equal (19, s.TrianglePosition);
  409. Assert.Equal ("V:", v.Text);
  410. Assert.Equal (19, v.TrianglePosition);
  411. Assert.Equal ("#FF0000", hex.Text);
  412. Application.Top!.Dispose ();
  413. }
  414. [Fact]
  415. [SetupFakeDriver]
  416. public void ColorPicker_SyncBetweenTextFieldAndBars ()
  417. {
  418. var cp = GetColorPicker (ColorModel.RGB, true);
  419. cp.Draw ();
  420. // Change value using the bar
  421. RBar rBar = cp.Subviews.OfType<RBar> ().First ();
  422. rBar.Value = 128;
  423. cp.Draw ();
  424. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  425. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  426. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  427. var hex = GetTextField (cp, ColorPickerPart.Hex);
  428. var rTextField = GetTextField (cp, ColorPickerPart.Bar1);
  429. var gTextField = GetTextField (cp, ColorPickerPart.Bar2);
  430. var bTextField = GetTextField (cp, ColorPickerPart.Bar3);
  431. Assert.Equal ("R:", r.Text);
  432. Assert.Equal (9, r.TrianglePosition);
  433. Assert.Equal ("128", rTextField.Text);
  434. Assert.Equal ("G:", g.Text);
  435. Assert.Equal (2, g.TrianglePosition);
  436. Assert.Equal ("0", gTextField.Text);
  437. Assert.Equal ("B:", b.Text);
  438. Assert.Equal (2, b.TrianglePosition);
  439. Assert.Equal ("0", bTextField.Text);
  440. Assert.Equal ("#800000", hex.Text);
  441. Application.Top?.Dispose ();
  442. }
  443. enum ColorPickerPart
  444. {
  445. Bar1 = 0,
  446. Bar2 = 1,
  447. Bar3 = 2,
  448. ColorName = 3,
  449. Hex = 4,
  450. }
  451. private TextField GetTextField (ColorPicker cp, ColorPickerPart toGet)
  452. {
  453. var hasBarValueTextFields = cp.Style.ShowTextFields;
  454. var hasColorNameTextField = cp.Style.ShowColorName;
  455. switch (toGet)
  456. {
  457. case ColorPickerPart.Bar1:
  458. case ColorPickerPart.Bar2:
  459. case ColorPickerPart.Bar3:
  460. if (!hasBarValueTextFields)
  461. {
  462. throw new NotSupportedException ("Corresponding Style option is not enabled");
  463. }
  464. return cp.Subviews.OfType<TextField> ().ElementAt ((int)toGet);
  465. case ColorPickerPart.ColorName:
  466. if (!hasColorNameTextField)
  467. {
  468. throw new NotSupportedException ("Corresponding Style option is not enabled");
  469. }
  470. return cp.Subviews.OfType<TextField> ().ElementAt (hasBarValueTextFields ? (int)toGet : (int)toGet - 3);
  471. case ColorPickerPart.Hex:
  472. int offset = hasBarValueTextFields ? 0 : 3;
  473. offset += hasColorNameTextField ? 0 : 1;
  474. return cp.Subviews.OfType<TextField> ().ElementAt ((int)toGet - offset);
  475. default:
  476. throw new ArgumentOutOfRangeException (nameof (toGet), toGet, null);
  477. }
  478. }
  479. private ColorBar GetColorBar (ColorPicker cp, ColorPickerPart toGet)
  480. {
  481. if (toGet <= ColorPickerPart.Bar3)
  482. {
  483. return cp.Subviews.OfType<ColorBar> ().ElementAt ((int)toGet);
  484. }
  485. throw new NotSupportedException ("ColorPickerPart must be a bar");
  486. }
  487. [Fact]
  488. [SetupFakeDriver]
  489. public void ColorPicker_ChangedEvent_Fires ()
  490. {
  491. Color newColor = default;
  492. var count = 0;
  493. var cp = new ColorPicker ();
  494. cp.ColorChanged += (s, e) =>
  495. {
  496. count++;
  497. newColor = e.CurrentValue;
  498. Assert.Equal (cp.SelectedColor, e.CurrentValue);
  499. };
  500. cp.SelectedColor = new (1, 2, 3);
  501. Assert.Equal (1, count);
  502. Assert.Equal (new (1, 2, 3), newColor);
  503. cp.SelectedColor = new (2, 3, 4);
  504. Assert.Equal (2, count);
  505. Assert.Equal (new (2, 3, 4), newColor);
  506. // Set to same value
  507. cp.SelectedColor = new (2, 3, 4);
  508. // Should have no effect
  509. Assert.Equal (2, count);
  510. }
  511. [Fact]
  512. [SetupFakeDriver]
  513. public void ColorPicker_DisposesOldViews_OnModelChange ()
  514. {
  515. var cp = GetColorPicker (ColorModel.HSL, true);
  516. var b1 = GetColorBar (cp, ColorPickerPart.Bar1);
  517. var b2 = GetColorBar (cp, ColorPickerPart.Bar2);
  518. var b3 = GetColorBar (cp, ColorPickerPart.Bar3);
  519. var tf1 = GetTextField (cp, ColorPickerPart.Bar1);
  520. var tf2 = GetTextField (cp, ColorPickerPart.Bar2);
  521. var tf3 = GetTextField (cp, ColorPickerPart.Bar3);
  522. var hex = GetTextField (cp, ColorPickerPart.Hex);
  523. #if DEBUG_IDISPOSABLE
  524. Assert.All (new View [] { b1, b2, b3, tf1, tf2, tf3, hex }, b => Assert.False (b.WasDisposed));
  525. #endif
  526. cp.Style.ColorModel = ColorModel.RGB;
  527. cp.ApplyStyleChanges ();
  528. var b1After = GetColorBar (cp, ColorPickerPart.Bar1);
  529. var b2After = GetColorBar (cp, ColorPickerPart.Bar2);
  530. var b3After = GetColorBar (cp, ColorPickerPart.Bar3);
  531. var tf1After = GetTextField (cp, ColorPickerPart.Bar1);
  532. var tf2After = GetTextField (cp, ColorPickerPart.Bar2);
  533. var tf3After = GetTextField (cp, ColorPickerPart.Bar3);
  534. var hexAfter = GetTextField (cp, ColorPickerPart.Hex);
  535. // Old bars should be disposed
  536. #if DEBUG_IDISPOSABLE
  537. Assert.All (new View [] { b1, b2, b3, tf1, tf2, tf3, hex }, b => Assert.True (b.WasDisposed));
  538. #endif
  539. Assert.NotSame (hex, hexAfter);
  540. Assert.NotSame (b1, b1After);
  541. Assert.NotSame (b2, b2After);
  542. Assert.NotSame (b3, b3After);
  543. Assert.NotSame (tf1, tf1After);
  544. Assert.NotSame (tf2, tf2After);
  545. Assert.NotSame (tf3, tf3After);
  546. }
  547. [Fact]
  548. [SetupFakeDriver]
  549. public void ColorPicker_TabCompleteColorName ()
  550. {
  551. var cp = GetColorPicker (ColorModel.RGB, true, true);
  552. Application.Navigation = new ();
  553. Application.Top = new ();
  554. Application.Top.Add (cp);
  555. cp.Draw ();
  556. var r = GetColorBar (cp, ColorPickerPart.Bar1);
  557. var g = GetColorBar (cp, ColorPickerPart.Bar2);
  558. var b = GetColorBar (cp, ColorPickerPart.Bar3);
  559. var name = GetTextField (cp, ColorPickerPart.ColorName);
  560. var hex = GetTextField (cp, ColorPickerPart.Hex);
  561. name.SetFocus ();
  562. Assert.True (name.HasFocus);
  563. Assert.Same (name, cp.Focused);
  564. name.Text = "";
  565. Assert.Empty (name.Text);
  566. Application.RaiseKeyDownEvent (Key.A);
  567. Application.RaiseKeyDownEvent (Key.Q);
  568. Assert.Equal ("aq", name.Text);
  569. // Auto complete the color name
  570. Application.RaiseKeyDownEvent (Key.Tab);
  571. Assert.Equal ("Aquamarine", name.Text);
  572. // Tab out of the text field
  573. Application.RaiseKeyDownEvent (Key.Tab);
  574. Assert.False (name.HasFocus);
  575. Assert.NotSame (name, cp.Focused);
  576. Assert.Equal ("#7FFFD4", hex.Text);
  577. Application.Top?.Dispose ();
  578. Application.ResetState (ignoreDisposed: true);
  579. }
  580. [Fact]
  581. [SetupFakeDriver]
  582. public void ColorPicker_EnterHexFor_ColorName ()
  583. {
  584. var cp = GetColorPicker (ColorModel.RGB, true, true);
  585. Application.Navigation = new ();
  586. Application.Top = new ();
  587. Application.Top.Add (cp);
  588. cp.Draw ();
  589. var name = GetTextField (cp, ColorPickerPart.ColorName);
  590. var hex = GetTextField (cp, ColorPickerPart.Hex);
  591. hex.SetFocus ();
  592. Assert.True (hex.HasFocus);
  593. Assert.Same (hex, cp.Focused);
  594. hex.Text = "";
  595. name.Text = "";
  596. Assert.Empty (hex.Text);
  597. Assert.Empty (name.Text);
  598. Application.RaiseKeyDownEvent ('#');
  599. Assert.Empty (name.Text);
  600. //7FFFD4
  601. Assert.Equal ("#", hex.Text);
  602. Application.RaiseKeyDownEvent ('7');
  603. Application.RaiseKeyDownEvent ('F');
  604. Application.RaiseKeyDownEvent ('F');
  605. Application.RaiseKeyDownEvent ('F');
  606. Application.RaiseKeyDownEvent ('D');
  607. Assert.Empty (name.Text);
  608. Application.RaiseKeyDownEvent ('4');
  609. Assert.True (hex.HasFocus);
  610. // Tab out of the hex field - should wrap to first focusable subview
  611. Application.RaiseKeyDownEvent (Key.Tab);
  612. Assert.False (hex.HasFocus);
  613. Assert.NotSame (hex, cp.Focused);
  614. // Color name should be recognised as a known string and populated
  615. Assert.Equal ("#7FFFD4", hex.Text);
  616. Assert.Equal ("Aquamarine", name.Text);
  617. Application.Top?.Dispose ();
  618. Application.ResetState (ignoreDisposed: true);
  619. }
  620. /// <summary>
  621. /// In this version we use the Enter button to accept the typed text instead
  622. /// of tabbing to the next view.
  623. /// </summary>
  624. [Fact]
  625. [SetupFakeDriver]
  626. public void ColorPicker_EnterHexFor_ColorName_AcceptVariation ()
  627. {
  628. var cp = GetColorPicker (ColorModel.RGB, true, true);
  629. Application.Navigation = new ();
  630. Application.Top = new ();
  631. Application.Top.Add (cp);
  632. cp.Draw ();
  633. var name = GetTextField (cp, ColorPickerPart.ColorName);
  634. var hex = GetTextField (cp, ColorPickerPart.Hex);
  635. hex.SetFocus ();
  636. Assert.True (hex.HasFocus);
  637. Assert.Same (hex, cp.Focused);
  638. hex.Text = "";
  639. name.Text = "";
  640. Assert.Empty (hex.Text);
  641. Assert.Empty (name.Text);
  642. Application.RaiseKeyDownEvent ('#');
  643. Assert.Empty (name.Text);
  644. //7FFFD4
  645. Assert.Equal ("#", hex.Text);
  646. Application.RaiseKeyDownEvent ('7');
  647. Application.RaiseKeyDownEvent ('F');
  648. Application.RaiseKeyDownEvent ('F');
  649. Application.RaiseKeyDownEvent ('F');
  650. Application.RaiseKeyDownEvent ('D');
  651. Assert.Empty (name.Text);
  652. Application.RaiseKeyDownEvent ('4');
  653. Assert.True (hex.HasFocus);
  654. // Should stay in the hex field (because accept not tab)
  655. Application.RaiseKeyDownEvent (Key.Enter);
  656. Assert.True (hex.HasFocus);
  657. Assert.Same (hex, cp.Focused);
  658. // But still, Color name should be recognised as a known string and populated
  659. Assert.Equal ("#7FFFD4", hex.Text);
  660. Assert.Equal ("Aquamarine", name.Text);
  661. Application.Top?.Dispose ();
  662. Application.ResetState (ignoreDisposed: true);
  663. }
  664. [Fact]
  665. public void TestColorNames ()
  666. {
  667. var colors = new W3CColors ();
  668. Assert.Contains ("Aquamarine", colors.GetColorNames ());
  669. Assert.DoesNotContain ("Save as", colors.GetColorNames ());
  670. }
  671. private ColorPicker GetColorPicker (ColorModel colorModel, bool showTextFields, bool showName = false)
  672. {
  673. var cp = new ColorPicker { Width = 20, SelectedColor = new (0, 0) };
  674. cp.Style.ColorModel = colorModel;
  675. cp.Style.ShowTextFields = showTextFields;
  676. cp.Style.ShowColorName = showName;
  677. cp.ApplyStyleChanges ();
  678. Application.Top = new Toplevel () { Width = 20, Height = 5 };
  679. Application.Top.Add (cp);
  680. Application.Top.LayoutSubviews ();
  681. Application.Top.SetFocus ();
  682. return cp;
  683. }
  684. }