2
0

ScrollTests.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. using Xunit.Abstractions;
  2. namespace Terminal.Gui.ViewsTests;
  3. public class ScrollTests
  4. {
  5. public ScrollTests (ITestOutputHelper output) { _output = output; }
  6. private readonly ITestOutputHelper _output;
  7. [Theory]
  8. [AutoInitShutdown]
  9. [InlineData (
  10. 20,
  11. @"
  12. ░",
  13. @"
  14. ░",
  15. @"
  16. █",
  17. @"
  18. ░",
  19. @"
  20. █████░░░░░",
  21. @"
  22. ░░█████░░░",
  23. @"
  24. ░░░░░█████",
  25. @"
  26. ░░███░░░░░")]
  27. [InlineData (
  28. 40,
  29. @"
  30. ░",
  31. @"
  32. ░",
  33. @"
  34. ░",
  35. @"
  36. ░",
  37. @"
  38. ███░░░░░░░",
  39. @"
  40. ░███░░░░░░",
  41. @"
  42. ░░███░░░░░",
  43. @"
  44. ░██░░░░░░░")]
  45. public void Changing_Position_Size_Orientation_Draws_Correctly (
  46. int size,
  47. string firstVertExpected,
  48. string middleVertExpected,
  49. string endVertExpected,
  50. string sizeVertExpected,
  51. string firstHoriExpected,
  52. string middleHoriExpected,
  53. string endHoriExpected,
  54. string sizeHoriExpected
  55. )
  56. {
  57. var scroll = new Scroll
  58. {
  59. Orientation = Orientation.Vertical,
  60. Size = size,
  61. Height = 10
  62. };
  63. var top = new Toplevel ();
  64. top.Add (scroll);
  65. Application.Begin (top);
  66. _ = TestHelpers.AssertDriverContentsWithFrameAre (firstVertExpected, _output);
  67. scroll.Position = 4;
  68. Application.Refresh ();
  69. _ = TestHelpers.AssertDriverContentsWithFrameAre (middleVertExpected, _output);
  70. scroll.Position = 10;
  71. Application.Refresh ();
  72. _ = TestHelpers.AssertDriverContentsWithFrameAre (endVertExpected, _output);
  73. scroll.Size = size * 2;
  74. Application.Refresh ();
  75. _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeVertExpected, _output);
  76. scroll.Orientation = Orientation.Horizontal;
  77. scroll.Width = 10;
  78. scroll.Height = 1;
  79. scroll.Position = 0;
  80. scroll.Size = size;
  81. Application.Refresh ();
  82. _ = TestHelpers.AssertDriverContentsWithFrameAre (firstHoriExpected, _output);
  83. scroll.Position = 4;
  84. Application.Refresh ();
  85. _ = TestHelpers.AssertDriverContentsWithFrameAre (middleHoriExpected, _output);
  86. scroll.Position = 10;
  87. Application.Refresh ();
  88. _ = TestHelpers.AssertDriverContentsWithFrameAre (endHoriExpected, _output);
  89. scroll.Size = size * 2;
  90. Application.Refresh ();
  91. _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeHoriExpected, _output);
  92. }
  93. [Fact]
  94. public void Constructor_Defaults ()
  95. {
  96. var scroll = new Scroll ();
  97. Assert.True (scroll.WantContinuousButtonPressed);
  98. Assert.False (scroll.CanFocus);
  99. Assert.Equal (Orientation.Vertical, scroll.Orientation);
  100. Assert.Equal (0, scroll.Size);
  101. Assert.Equal (0, scroll.Position);
  102. }
  103. [Theory]
  104. [AutoInitShutdown]
  105. [InlineData (
  106. Orientation.Vertical,
  107. 20,
  108. 10,
  109. 4,
  110. @"
  111. █",
  112. 0,
  113. @"
  114. ░")]
  115. [InlineData (
  116. Orientation.Vertical,
  117. 40,
  118. 10,
  119. 5,
  120. @"
  121. ░",
  122. 20,
  123. @"
  124. ░")]
  125. [InlineData (
  126. Orientation.Horizontal,
  127. 20,
  128. 10,
  129. 4,
  130. @"
  131. ░░░░░█████",
  132. 0,
  133. @"
  134. █████░░░░░")]
  135. [InlineData (
  136. Orientation.Horizontal,
  137. 40,
  138. 10,
  139. 5,
  140. @"
  141. ░░███░░░░░",
  142. 20,
  143. @"
  144. ░░░░░███░░")]
  145. public void Mouse_On_The_Container (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
  146. {
  147. var scroll = new Scroll
  148. {
  149. Width = orientation == Orientation.Vertical ? 1 : 10,
  150. Height = orientation == Orientation.Vertical ? 10 : 1,
  151. Orientation = orientation, Size = size,
  152. Position = position
  153. };
  154. var top = new Toplevel ();
  155. top.Add (scroll);
  156. Application.Begin (top);
  157. _ = TestHelpers.AssertDriverContentsWithFrameAre (output, _output);
  158. Application.OnMouseEvent (
  159. new ()
  160. {
  161. Position = orientation == Orientation.Vertical ? new (0, location) : new Point (location, 0),
  162. Flags = MouseFlags.Button1Pressed
  163. });
  164. Assert.Equal (expectedPos, scroll.Position);
  165. Application.Refresh ();
  166. _ = TestHelpers.AssertDriverContentsWithFrameAre (expectedOut, _output);
  167. }
  168. [Theory]
  169. [AutoInitShutdown]
  170. [InlineData (
  171. Orientation.Vertical,
  172. 20,
  173. 10,
  174. 5,
  175. 5,
  176. @"
  177. █",
  178. MouseFlags.Button1Pressed,
  179. 10,
  180. @"
  181. █")]
  182. [InlineData (
  183. Orientation.Vertical,
  184. 40,
  185. 10,
  186. 3,
  187. 3,
  188. @"
  189. ░",
  190. MouseFlags.Button1Pressed,
  191. 10,
  192. @"
  193. ░")]
  194. [InlineData (
  195. Orientation.Horizontal,
  196. 20,
  197. 10,
  198. 5,
  199. 5,
  200. @"
  201. ░░░░░█████",
  202. MouseFlags.Button1Pressed,
  203. 10,
  204. @"
  205. ░░░░░█████")]
  206. [InlineData (
  207. Orientation.Horizontal,
  208. 40,
  209. 10,
  210. 3,
  211. 3,
  212. @"
  213. ░░███░░░░░",
  214. MouseFlags.Button1Pressed,
  215. 10,
  216. @"
  217. ░░███░░░░░")]
  218. [InlineData (
  219. Orientation.Vertical,
  220. 20,
  221. 10,
  222. 5,
  223. 4,
  224. @"
  225. █",
  226. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  227. 8,
  228. @"
  229. ░")]
  230. [InlineData (
  231. Orientation.Horizontal,
  232. 20,
  233. 10,
  234. 5,
  235. 4,
  236. @"
  237. ░░░░░█████",
  238. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  239. 8,
  240. @"
  241. ░░░░█████░")]
  242. [InlineData (
  243. Orientation.Vertical,
  244. 20,
  245. 10,
  246. 5,
  247. 6,
  248. @"
  249. █",
  250. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  251. 10,
  252. @"
  253. █")]
  254. [InlineData (
  255. Orientation.Horizontal,
  256. 20,
  257. 10,
  258. 5,
  259. 6,
  260. @"
  261. ░░░░░█████",
  262. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  263. 10,
  264. @"
  265. ░░░░░█████")]
  266. [InlineData (
  267. Orientation.Vertical,
  268. 40,
  269. 10,
  270. 2,
  271. 1,
  272. @"
  273. ░",
  274. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  275. 4,
  276. @"
  277. ░")]
  278. [InlineData (
  279. Orientation.Horizontal,
  280. 40,
  281. 10,
  282. 2,
  283. 1,
  284. @"
  285. ░░███░░░░░",
  286. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  287. 4,
  288. @"
  289. ░███░░░░░░")]
  290. [InlineData (
  291. Orientation.Vertical,
  292. 40,
  293. 10,
  294. 3,
  295. 4,
  296. @"
  297. ░",
  298. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  299. 12,
  300. @"
  301. ░")]
  302. [InlineData (
  303. Orientation.Horizontal,
  304. 40,
  305. 10,
  306. 3,
  307. 4,
  308. @"
  309. ░░███░░░░░",
  310. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  311. 12,
  312. @"
  313. ░░░███░░░░")]
  314. [InlineData (
  315. Orientation.Vertical,
  316. 40,
  317. 10,
  318. 2,
  319. 3,
  320. @"
  321. ░",
  322. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  323. 12,
  324. @"
  325. ░")]
  326. [InlineData (
  327. Orientation.Horizontal,
  328. 40,
  329. 10,
  330. 2,
  331. 3,
  332. @"
  333. ░░███░░░░░",
  334. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  335. 12,
  336. @"
  337. ░░░███░░░░")]
  338. [InlineData (
  339. Orientation.Vertical,
  340. 40,
  341. 10,
  342. 2,
  343. 4,
  344. @"
  345. ░",
  346. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  347. 16,
  348. @"
  349. ░")]
  350. [InlineData (
  351. Orientation.Horizontal,
  352. 40,
  353. 10,
  354. 2,
  355. 4,
  356. @"
  357. ░░███░░░░░",
  358. MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
  359. 16,
  360. @"
  361. ░░░░███░░░")]
  362. public void Mouse_On_The_Slider (
  363. Orientation orientation,
  364. int size,
  365. int position,
  366. int startLocation,
  367. int endLocation,
  368. string output,
  369. MouseFlags mouseFlags,
  370. int expectedPos,
  371. string expectedOut
  372. )
  373. {
  374. var scroll = new Scroll
  375. {
  376. Width = orientation == Orientation.Vertical ? 1 : 10,
  377. Height = orientation == Orientation.Vertical ? 10 : 1,
  378. Orientation = orientation,
  379. Size = size, Position = position
  380. };
  381. var top = new Toplevel ();
  382. top.Add (scroll);
  383. Application.Begin (top);
  384. _ = TestHelpers.AssertDriverContentsWithFrameAre (output, _output);
  385. Assert.Null (Application.MouseGrabView);
  386. if (mouseFlags.HasFlag (MouseFlags.ReportMousePosition))
  387. {
  388. MouseFlags mf = mouseFlags & ~MouseFlags.ReportMousePosition;
  389. Application.OnMouseEvent (
  390. new ()
  391. {
  392. Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
  393. Flags = mf
  394. });
  395. Application.OnMouseEvent (
  396. new ()
  397. {
  398. Position = orientation == Orientation.Vertical ? new (0, endLocation) : new (endLocation, 0),
  399. Flags = mouseFlags
  400. });
  401. }
  402. else
  403. {
  404. Assert.Equal (startLocation, endLocation);
  405. Application.OnMouseEvent (
  406. new ()
  407. {
  408. Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
  409. Flags = mouseFlags
  410. });
  411. }
  412. Assert.Equal ("scrollSlider", Application.MouseGrabView?.Id);
  413. Assert.Equal (expectedPos, scroll.Position);
  414. Application.Refresh ();
  415. _ = TestHelpers.AssertDriverContentsWithFrameAre (expectedOut, _output);
  416. Application.OnMouseEvent (
  417. new ()
  418. {
  419. Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
  420. Flags = MouseFlags.Button1Released
  421. });
  422. Assert.Null (Application.MouseGrabView);
  423. }
  424. [Theory]
  425. [AutoInitShutdown]
  426. [InlineData (Orientation.Vertical)]
  427. [InlineData (Orientation.Horizontal)]
  428. public void Moving_Mouse_Outside_Host_Ensures_Correct_Location (Orientation orientation)
  429. {
  430. var scroll = new Scroll
  431. {
  432. X = 10, Y = 10, Width = orientation == Orientation.Vertical ? 1 : 10, Height = orientation == Orientation.Vertical ? 10 : 1, Size = 20,
  433. Position = 5, Orientation = orientation
  434. };
  435. var top = new Toplevel ();
  436. top.Add (scroll);
  437. Application.Begin (top);
  438. Rectangle scrollSliderFrame = scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame;
  439. Assert.Equal (scrollSliderFrame, orientation == Orientation.Vertical ? new (0, 2, 1, 5) : new (2, 0, 5, 1));
  440. Application.OnMouseEvent (new () { Position = orientation == Orientation.Vertical ? new (10, 12) : new (12, 10), Flags = MouseFlags.Button1Pressed });
  441. Application.OnMouseEvent (
  442. new ()
  443. {
  444. Position = orientation == Orientation.Vertical ? new (10, 0) : new (0, 10),
  445. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  446. });
  447. Assert.Equal (new (0, 0), scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
  448. Application.OnMouseEvent (
  449. new ()
  450. {
  451. Position = orientation == Orientation.Vertical ? new (0, 25) : new (80, 0),
  452. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  453. });
  454. Assert.Equal (
  455. orientation == Orientation.Vertical ? new (0, 5) : new (5, 0),
  456. scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
  457. }
  458. [Theory]
  459. [InlineData (Orientation.Vertical, 20, 10)]
  460. [InlineData (Orientation.Vertical, 40, 30)]
  461. public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length (Orientation orientation, int size, int expectedPos)
  462. {
  463. var scroll = new Scroll { Orientation = orientation, Height = 10, Size = size };
  464. Assert.Equal (0, scroll.Position);
  465. scroll.Position = -1;
  466. Assert.Equal (0, scroll.Position);
  467. scroll.Position = size;
  468. Assert.Equal (0, scroll.Position);
  469. scroll.Position = expectedPos;
  470. Assert.Equal (expectedPos, scroll.Position);
  471. }
  472. [Fact]
  473. public void PositionChanging_Cancelable_And_PositionChanged_Events ()
  474. {
  475. var changingCount = 0;
  476. var changedCount = 0;
  477. var scroll = new Scroll { Size = 10 };
  478. scroll.PositionChanging += (s, e) =>
  479. {
  480. if (changingCount == 0)
  481. {
  482. e.Cancel = true;
  483. }
  484. changingCount++;
  485. };
  486. scroll.PositionChanged += (s, e) => changedCount++;
  487. scroll.Position = 1;
  488. Assert.Equal (0, scroll.Position);
  489. Assert.Equal (1, changingCount);
  490. Assert.Equal (0, changedCount);
  491. scroll.Position = 1;
  492. Assert.Equal (1, scroll.Position);
  493. Assert.Equal (2, changingCount);
  494. Assert.Equal (1, changedCount);
  495. }
  496. [Fact]
  497. public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
  498. {
  499. var changing = 0;
  500. var cancel = false;
  501. var changed = 0;
  502. var scroll = new Scroll { Height = 10, Size = 20 };
  503. scroll.PositionChanging += Scroll_PositionChanging;
  504. scroll.PositionChanged += Scroll_PositionChanged;
  505. Assert.Equal (Orientation.Vertical, scroll.Orientation);
  506. Assert.Equal (new (0, 0, 1, 10), scroll.Viewport);
  507. Assert.Equal (0, scroll.Position);
  508. Assert.Equal (0, changing);
  509. Assert.Equal (0, changed);
  510. scroll.Position = 0;
  511. Assert.Equal (0, scroll.Position);
  512. Assert.Equal (0, changing);
  513. Assert.Equal (0, changed);
  514. scroll.Position = 1;
  515. Assert.Equal (1, scroll.Position);
  516. Assert.Equal (1, changing);
  517. Assert.Equal (1, changed);
  518. Reset ();
  519. cancel = true;
  520. scroll.Position = 2;
  521. Assert.Equal (1, scroll.Position);
  522. Assert.Equal (1, changing);
  523. Assert.Equal (0, changed);
  524. Reset ();
  525. scroll.Position = 10;
  526. Assert.Equal (10, scroll.Position);
  527. Assert.Equal (1, changing);
  528. Assert.Equal (1, changed);
  529. Reset ();
  530. scroll.Position = 11;
  531. Assert.Equal (10, scroll.Position);
  532. Assert.Equal (0, changing);
  533. Assert.Equal (0, changed);
  534. Reset ();
  535. scroll.Position = 0;
  536. Assert.Equal (0, scroll.Position);
  537. Assert.Equal (1, changing);
  538. Assert.Equal (1, changed);
  539. scroll.PositionChanging -= Scroll_PositionChanging;
  540. scroll.PositionChanged -= Scroll_PositionChanged;
  541. void Scroll_PositionChanging (object sender, CancelEventArgs<int> e)
  542. {
  543. changing++;
  544. e.Cancel = cancel;
  545. }
  546. void Scroll_PositionChanged (object sender, EventArgs<int> e) { changed++; }
  547. void Reset ()
  548. {
  549. changing = 0;
  550. cancel = false;
  551. changed = 0;
  552. }
  553. }
  554. [Fact]
  555. public void SizeChanged_Event ()
  556. {
  557. var count = 0;
  558. var scroll = new Scroll ();
  559. scroll.SizeChanged += (s, e) => count++;
  560. scroll.Size = 10;
  561. Assert.Equal (10, scroll.Size);
  562. Assert.Equal (1, count);
  563. }
  564. [Theory]
  565. [AutoInitShutdown]
  566. [InlineData (
  567. 3,
  568. 10,
  569. 1,
  570. Orientation.Vertical,
  571. @"
  572. ┌─┐
  573. │█│
  574. │█│
  575. │█│
  576. │█│
  577. │░│
  578. │░│
  579. │░│
  580. │░│
  581. └─┘")]
  582. [InlineData (
  583. 10,
  584. 3,
  585. 1,
  586. Orientation.Horizontal,
  587. @"
  588. ┌────────┐
  589. │████░░░░│
  590. └────────┘")]
  591. [InlineData (
  592. 3,
  593. 10,
  594. 3,
  595. Orientation.Vertical,
  596. @"
  597. ┌───┐
  598. │███│
  599. │███│
  600. │███│
  601. │███│
  602. │░░░│
  603. │░░░│
  604. │░░░│
  605. │░░░│
  606. └───┘")]
  607. [InlineData (
  608. 10,
  609. 3,
  610. 3,
  611. Orientation.Horizontal,
  612. @"
  613. ┌────────┐
  614. │████░░░░│
  615. │████░░░░│
  616. │████░░░░│
  617. └────────┘")]
  618. public void Vertical_Horizontal_Draws_Correctly (int sizeWidth, int sizeHeight, int widthHeight, Orientation orientation, string expected)
  619. {
  620. var super = new Window { Id = "super", Width = Dim.Fill (), Height = Dim.Fill () };
  621. var top = new Toplevel ();
  622. top.Add (super);
  623. var scroll = new Scroll
  624. {
  625. Orientation = orientation,
  626. Size = orientation == Orientation.Vertical ? sizeHeight * 2 : sizeWidth * 2,
  627. Width = orientation == Orientation.Vertical ? widthHeight : Dim.Fill (),
  628. Height = orientation == Orientation.Vertical ? Dim.Fill () : widthHeight
  629. };
  630. super.Add (scroll);
  631. Application.Begin (top);
  632. ((FakeDriver)Application.Driver)!.SetBufferSize (
  633. sizeWidth + (orientation == Orientation.Vertical ? widthHeight - 1 : 0),
  634. sizeHeight + (orientation == Orientation.Vertical ? 0 : widthHeight - 1));
  635. _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  636. }
  637. }