ScrollBarTests.cs 23 KB

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