LineCanvasTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Xunit;
  5. using Xunit.Abstractions;
  6. namespace Terminal.Gui.DrawingTests {
  7. public class LineCanvasTests {
  8. readonly ITestOutputHelper output;
  9. public LineCanvasTests (ITestOutputHelper output)
  10. {
  11. this.output = output;
  12. }
  13. [InlineData (LineStyle.Single)]
  14. [InlineData (LineStyle.Rounded)]
  15. [Theory, AutoInitShutdown]
  16. public void TestLineCanvas_Horizontal (LineStyle style)
  17. {
  18. var v = GetCanvas (out var canvas);
  19. canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, style);
  20. v.Redraw (v.Bounds);
  21. string looksLike =
  22. @"
  23. ──";
  24. TestHelpers.AssertDriverContentsAre (looksLike, output);
  25. }
  26. [Fact, AutoInitShutdown]
  27. public void TestLineCanvas_Horizontal_Double ()
  28. {
  29. var v = GetCanvas (out var canvas);
  30. canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Double);
  31. v.Redraw (v.Bounds);
  32. string looksLike =
  33. @"
  34. ══";
  35. TestHelpers.AssertDriverContentsAre (looksLike, output);
  36. }
  37. [InlineData (LineStyle.Single)]
  38. [InlineData (LineStyle.Rounded)]
  39. [Theory, AutoInitShutdown]
  40. public void TestLineCanvas_Vertical (LineStyle style)
  41. {
  42. var v = GetCanvas (out var canvas);
  43. canvas.AddLine (new Point (0, 0), 1, Orientation.Vertical, style);
  44. v.Redraw (v.Bounds);
  45. string looksLike =
  46. @"
  47. │";
  48. TestHelpers.AssertDriverContentsAre (looksLike, output);
  49. }
  50. [Fact, AutoInitShutdown]
  51. public void TestLineCanvas_Vertical_Double ()
  52. {
  53. var v = GetCanvas (out var canvas);
  54. canvas.AddLine (new Point (0, 0), 1, Orientation.Vertical, LineStyle.Double);
  55. v.Redraw (v.Bounds);
  56. string looksLike =
  57. @"
  58. ║";
  59. TestHelpers.AssertDriverContentsAre (looksLike, output);
  60. }
  61. /// <summary>
  62. /// This test demonstrates that corners are only drawn when lines overlap.
  63. /// Not when they terminate adjacent to one another.
  64. /// </summary>
  65. [Fact, AutoInitShutdown]
  66. public void TestLineCanvas_Corner_NoOverlap ()
  67. {
  68. var v = GetCanvas (out var canvas);
  69. canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Single);
  70. canvas.AddLine (new Point (0, 1), 1, Orientation.Vertical, LineStyle.Single);
  71. v.Redraw (v.Bounds);
  72. string looksLike =
  73. @"
  74. ──
  75. │";
  76. TestHelpers.AssertDriverContentsAre (looksLike, output);
  77. }
  78. /// <summary>
  79. /// This test demonstrates how to correctly trigger a corner. By
  80. /// overlapping the lines in the same cell
  81. /// </summary>
  82. [Fact, AutoInitShutdown]
  83. public void TestLineCanvas_Corner_Correct ()
  84. {
  85. var v = GetCanvas (out var canvas);
  86. canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Single);
  87. canvas.AddLine (new Point (0, 0), 2, Orientation.Vertical, LineStyle.Single);
  88. v.Redraw (v.Bounds);
  89. string looksLike =
  90. @"
  91. ┌─
  92. │";
  93. TestHelpers.AssertDriverContentsAre (looksLike, output);
  94. }
  95. [Fact, AutoInitShutdown]
  96. public void TestLineCanvas_Window ()
  97. {
  98. var v = GetCanvas (out var canvas);
  99. // outer box
  100. canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, LineStyle.Single);
  101. canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, LineStyle.Single);
  102. canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, LineStyle.Single);
  103. canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, LineStyle.Single);
  104. canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, LineStyle.Single);
  105. canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single);
  106. v.Redraw (v.Bounds);
  107. string looksLike =
  108. @"
  109. ┌────┬───┐
  110. │ │ │
  111. ├────┼───┤
  112. │ │ │
  113. └────┴───┘";
  114. TestHelpers.AssertDriverContentsAre (looksLike, output);
  115. }
  116. /// <summary>
  117. /// Demonstrates when <see cref="LineStyle.Rounded"/> corners are used. Notice how
  118. /// not all lines declare rounded. If there are 1+ lines intersecting and a corner is
  119. /// to be used then if any of them are rounded a rounded corner is used.
  120. /// </summary>
  121. [Fact, AutoInitShutdown]
  122. public void TestLineCanvas_Window_Rounded ()
  123. {
  124. var v = GetCanvas (out var canvas);
  125. // outer box
  126. canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, LineStyle.Rounded);
  127. // BorderStyle.Single is ignored because corner overlaps with the above line which is Rounded
  128. // this results in a rounded corner being used.
  129. canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, LineStyle.Single);
  130. canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, LineStyle.Rounded);
  131. canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, LineStyle.Single);
  132. // These lines say rounded but they will result in the T sections which are never rounded.
  133. canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, LineStyle.Rounded);
  134. canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Rounded);
  135. v.Redraw (v.Bounds);
  136. string looksLike =
  137. @"
  138. ╭────┬───╮
  139. │ │ │
  140. ├────┼───┤
  141. │ │ │
  142. ╰────┴───╯";
  143. TestHelpers.AssertDriverContentsAre (looksLike, output);
  144. }
  145. [Fact, AutoInitShutdown]
  146. public void TestLineCanvas_Window_Double ()
  147. {
  148. var v = GetCanvas (out var canvas);
  149. // outer box
  150. canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, LineStyle.Double);
  151. canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, LineStyle.Double);
  152. canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, LineStyle.Double);
  153. canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, LineStyle.Double);
  154. canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, LineStyle.Double);
  155. canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Double);
  156. v.Redraw (v.Bounds);
  157. string looksLike =
  158. @"
  159. ╔════╦═══╗
  160. ║ ║ ║
  161. ╠════╬═══╣
  162. ║ ║ ║
  163. ╚════╩═══╝";
  164. TestHelpers.AssertDriverContentsAre (looksLike, output);
  165. }
  166. [Theory, AutoInitShutdown]
  167. [InlineData (LineStyle.Single)]
  168. [InlineData (LineStyle.Rounded)]
  169. public void TestLineCanvas_Window_DoubleTop_SingleSides (LineStyle thinStyle)
  170. {
  171. var v = GetCanvas (out var canvas);
  172. // outer box
  173. canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, LineStyle.Double);
  174. canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, thinStyle);
  175. canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, LineStyle.Double);
  176. canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, thinStyle);
  177. canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, thinStyle);
  178. canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Double);
  179. v.Redraw (v.Bounds);
  180. string looksLike =
  181. @"
  182. ╒════╤═══╕
  183. │ │ │
  184. ╞════╪═══╡
  185. │ │ │
  186. ╘════╧═══╛
  187. ";
  188. TestHelpers.AssertDriverContentsAre (looksLike, output);
  189. }
  190. [Theory, AutoInitShutdown]
  191. [InlineData (LineStyle.Single)]
  192. [InlineData (LineStyle.Rounded)]
  193. public void TestLineCanvas_Window_SingleTop_DoubleSides (LineStyle thinStyle)
  194. {
  195. var v = GetCanvas (out var canvas);
  196. // outer box
  197. canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, thinStyle);
  198. canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, LineStyle.Double);
  199. canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, thinStyle);
  200. canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, LineStyle.Double);
  201. canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, LineStyle.Double);
  202. canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, thinStyle);
  203. v.Redraw (v.Bounds);
  204. string looksLike =
  205. @"
  206. ╓────╥───╖
  207. ║ ║ ║
  208. ╟────╫───╢
  209. ║ ║ ║
  210. ╙────╨───╜
  211. ";
  212. TestHelpers.AssertDriverContentsAre (looksLike, output);
  213. }
  214. [Fact, AutoInitShutdown]
  215. public void TestLineCanvas_LeaveMargin_Top1_Left1 ()
  216. {
  217. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  218. var v = GetCanvas (out var canvas, 1, 1);
  219. // outer box
  220. canvas.AddLine (new Point (0, 0), 8, Orientation.Horizontal, LineStyle.Single);
  221. canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single);
  222. canvas.AddLine (new Point (8, 3), -8, Orientation.Horizontal, LineStyle.Single);
  223. canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single);
  224. canvas.AddLine (new Point (5, 0), 3, Orientation.Vertical, LineStyle.Single);
  225. canvas.AddLine (new Point (0, 2), 8, Orientation.Horizontal, LineStyle.Single);
  226. v.Redraw (v.Bounds);
  227. string looksLike =
  228. @"
  229. ┌────┬──┐
  230. │ │ │
  231. ├────┼──┤
  232. └────┴──┘
  233. ";
  234. TestHelpers.AssertDriverContentsAre (looksLike, output);
  235. }
  236. [Fact, AutoInitShutdown]
  237. public void TestLineCanvas_ClipArea_Intersections ()
  238. {
  239. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  240. var v = GetCanvas (out var lc);
  241. v.Width = 10;
  242. v.Height = 1;
  243. v.Bounds = new Rect (0, 0, 10, 1);
  244. // ╔╡ Title ╞═════╗
  245. // Add a short horiz line for ╔╡
  246. lc.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Double);
  247. //LHS line down
  248. lc.AddLine (new Point (0, 0), 5, Orientation.Vertical, LineStyle.Double);
  249. //Vertical line before Title, results in a ╡
  250. lc.AddLine (new Point (1, 0), 0, Orientation.Vertical, LineStyle.Single);
  251. //Vertical line after Title, results in a ╞
  252. lc.AddLine (new Point (6, 0), 0, Orientation.Vertical, LineStyle.Single);
  253. // remainder of title
  254. lc.AddLine (new Point (6, 0), 3, Orientation.Horizontal, LineStyle.Double);
  255. //RHS line down
  256. lc.AddLine (new Point (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  257. v.Redraw (v.Bounds);
  258. string looksLike =
  259. @"
  260. ╔╡ ╞══╗
  261. ";
  262. TestHelpers.AssertDriverContentsAre (looksLike, output);
  263. }
  264. [InlineData(0,0,0, Orientation.Horizontal,LineStyle.Double,"═")]
  265. [InlineData(0,0,0, Orientation.Vertical,LineStyle.Double,"║")]
  266. [InlineData(0,0,0, Orientation.Horizontal,LineStyle.Single,"─")]
  267. [InlineData(0,0,0, Orientation.Vertical,LineStyle.Single,"│")]
  268. [AutoInitShutdown, Theory]
  269. public void TestLineCanvas_1LineTests(
  270. int x1, int y1,int l1, Orientation o1, LineStyle s1,
  271. string expected
  272. )
  273. {
  274. var v = GetCanvas (out var lc);
  275. v.Width = 10;
  276. v.Height = 10;
  277. v.Bounds = new Rect (0, 0, 10, 10);
  278. lc.AddLine (new Point (x1, y1), l1, o1, s1);
  279. v.Redraw (v.Bounds);
  280. TestHelpers.AssertDriverContentsAre (expected, output);
  281. }
  282. [Theory, AutoInitShutdown]
  283. [InlineData(
  284. 0,0,1,Orientation.Horizontal,LineStyle.Double,
  285. 1,0,0, Orientation.Vertical,LineStyle.Single, "═╡"
  286. )]
  287. [InlineData(
  288. 0,0,0, Orientation.Vertical,LineStyle.Single,
  289. 0,0,1,Orientation.Horizontal,LineStyle.Double,
  290. "╞═"
  291. )]
  292. [InlineData(
  293. 0,0,1, Orientation.Vertical,LineStyle.Single,
  294. 0,0,0,Orientation.Horizontal,LineStyle.Double,
  295. @"
  296. │"
  297. )]
  298. [InlineData(
  299. 0,0,1, Orientation.Vertical,LineStyle.Single,
  300. 0,1,0,Orientation.Horizontal,LineStyle.Double,
  301. @"
  302. "
  303. )]
  304. [InlineData(
  305. 0,0,0, Orientation.Vertical,LineStyle.Single,
  306. 0,0,0,Orientation.Horizontal,LineStyle.Single,
  307. @"┼
  308. "
  309. )]
  310. [InlineData(
  311. 0,0,0, Orientation.Vertical,LineStyle.Double,
  312. 0,0,0,Orientation.Horizontal,LineStyle.Double,
  313. @"╬
  314. "
  315. )]
  316. public void TestLineCanvas_2LineTests(
  317. int x1, int y1,int l1, Orientation o1, LineStyle s1,
  318. int x2, int y2, int l2, Orientation o2, LineStyle s2,
  319. string expected
  320. )
  321. {
  322. var v = GetCanvas (out var lc);
  323. v.Width = 10;
  324. v.Height = 10;
  325. v.Bounds = new Rect (0, 0, 10, 10);
  326. lc.AddLine (new Point (x1, y1), l1, o1, s1);
  327. lc.AddLine (new Point (x2, y2), l2, o2, s2);
  328. v.Redraw (v.Bounds);
  329. TestHelpers.AssertDriverContentsAre (expected, output);
  330. }
  331. /// <summary>
  332. /// Creates a new <see cref="View"/> into which a <see cref="LineCanvas"/> is rendered
  333. /// at <see cref="View.DrawContentComplete"/> time.
  334. /// </summary>
  335. /// <param name="canvas">The <see cref="LineCanvas"/> you can draw into.</param>
  336. /// <param name="offsetX">How far to offset drawing in X</param>
  337. /// <param name="offsetY">How far to offset drawing in Y</param>
  338. /// <returns></returns>
  339. private View GetCanvas (out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
  340. {
  341. var v = new View {
  342. Width = 10,
  343. Height = 5,
  344. Bounds = new Rect (0, 0, 10, 5)
  345. };
  346. v.LayoutSubviews ();
  347. var canvasCopy = canvas = new LineCanvas ();
  348. v.DrawContentComplete += (s, e) => {
  349. foreach(var p in canvasCopy.GenerateImage(v.Bounds))
  350. {
  351. v.AddRune(
  352. offsetX + p.Key.X,
  353. offsetY + p.Key.Y,
  354. p.Value);
  355. }
  356. };
  357. return v;
  358. }
  359. }
  360. }