ListViewTests.cs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584
  1. #nullable enable
  2. using System.Collections;
  3. using System.Collections.ObjectModel;
  4. using System.Collections.Specialized;
  5. using Moq;
  6. using Terminal.Gui;
  7. using UnitTests;
  8. using Xunit;
  9. using Xunit.Abstractions;
  10. // ReSharper disable AccessToModifiedClosure
  11. namespace UnitTests_Parallelizable.ViewsTests;
  12. public class ListViewTests (ITestOutputHelper output)
  13. {
  14. private readonly ITestOutputHelper _output = output;
  15. [Fact]
  16. public void CollectionNavigatorMatcher_KeybindingsOverrideNavigator ()
  17. {
  18. ObservableCollection<string> source = ["apricot", "arm", "bat", "batman", "bates hotel", "candle"];
  19. var lv = new ListView { Source = new ListWrapper<string> (source) };
  20. lv.SetFocus ();
  21. lv.KeyBindings.Add (Key.B, Command.Down);
  22. Assert.Null (lv.SelectedItem);
  23. // Keys should be consumed to move down the navigation i.e. to apricot
  24. Assert.True (lv.NewKeyDownEvent (Key.B));
  25. Assert.Equal (0, lv.SelectedItem);
  26. Assert.True (lv.NewKeyDownEvent (Key.B));
  27. Assert.Equal (1, lv.SelectedItem);
  28. // There is no keybinding for Key.C so it hits collection navigator i.e. we jump to candle
  29. Assert.True (lv.NewKeyDownEvent (Key.C));
  30. Assert.Equal (5, lv.SelectedItem);
  31. }
  32. [Fact]
  33. public void ListView_CollectionNavigatorMatcher_KeybindingsOverrideNavigator ()
  34. {
  35. ObservableCollection<string> source = ["apricot", "arm", "bat", "batman", "bates hotel", "candle"];
  36. var lv = new ListView { Source = new ListWrapper<string> (source) };
  37. lv.SetFocus ();
  38. lv.KeyBindings.Add (Key.B, Command.Down);
  39. Assert.Null (lv.SelectedItem);
  40. // Keys should be consumed to move down the navigation i.e. to apricot
  41. Assert.True (lv.NewKeyDownEvent (Key.B));
  42. Assert.Equal (0, lv.SelectedItem);
  43. Assert.True (lv.NewKeyDownEvent (Key.B));
  44. Assert.Equal (1, lv.SelectedItem);
  45. // There is no keybinding for Key.C so it hits collection navigator i.e. we jump to candle
  46. Assert.True (lv.NewKeyDownEvent (Key.C));
  47. Assert.Equal (5, lv.SelectedItem);
  48. }
  49. [Fact]
  50. public void ListViewCollectionNavigatorMatcher_DefaultBehaviour ()
  51. {
  52. ObservableCollection<string> source = ["apricot", "arm", "bat", "batman", "bates hotel", "candle"];
  53. var lv = new ListView { Source = new ListWrapper<string> (source) };
  54. // Keys are consumed during navigation
  55. Assert.True (lv.NewKeyDownEvent (Key.B));
  56. Assert.True (lv.NewKeyDownEvent (Key.A));
  57. Assert.True (lv.NewKeyDownEvent (Key.T));
  58. Assert.Equal ("bat", (string)lv.Source.ToList () [lv.SelectedItem!.Value]!);
  59. }
  60. [Fact]
  61. public void ListViewCollectionNavigatorMatcher_IgnoreKeys ()
  62. {
  63. ObservableCollection<string> source = ["apricot", "arm", "bat", "batman", "bates hotel", "candle"];
  64. var lv = new ListView { Source = new ListWrapper<string> (source) };
  65. Mock<ICollectionNavigatorMatcher> matchNone = new ();
  66. matchNone.Setup (m => m.IsCompatibleKey (It.IsAny<Key> ()))
  67. .Returns (false);
  68. lv.KeystrokeNavigator.Matcher = matchNone.Object;
  69. // Keys are ignored because IsCompatibleKey returned false i.e. don't use these keys for navigation
  70. Assert.False (lv.NewKeyDownEvent (Key.B));
  71. Assert.False (lv.NewKeyDownEvent (Key.A));
  72. Assert.False (lv.NewKeyDownEvent (Key.T));
  73. // assert IsMatch never called
  74. matchNone.Verify (m => m.IsMatch (It.IsAny<string> (), It.IsAny<object> ()), Times.Never ());
  75. }
  76. [Fact]
  77. public void ListViewCollectionNavigatorMatcher_OverrideMatching ()
  78. {
  79. ObservableCollection<string> source = ["apricot", "arm", "bat", "batman", "bates hotel", "candle"];
  80. var lv = new ListView { Source = new ListWrapper<string> (source) };
  81. Mock<ICollectionNavigatorMatcher> matchNone = new ();
  82. matchNone.Setup (m => m.IsCompatibleKey (It.IsAny<Key> ()))
  83. .Returns (true);
  84. // Match any string starting with b to "candle" (psych!)
  85. matchNone.Setup (m => m.IsMatch (It.IsAny<string> (), It.IsAny<object> ()))
  86. .Returns ((string s, object key) => s.StartsWith ('B') && key?.ToString () == "candle");
  87. lv.KeystrokeNavigator.Matcher = matchNone.Object;
  88. // Keys are consumed during navigation
  89. Assert.True (lv.NewKeyDownEvent (Key.B));
  90. Assert.Equal (5, lv.SelectedItem);
  91. Assert.True (lv.NewKeyDownEvent (Key.A));
  92. Assert.Equal (5, lv.SelectedItem);
  93. Assert.True (lv.NewKeyDownEvent (Key.T));
  94. Assert.Equal (5, lv.SelectedItem);
  95. Assert.Equal ("candle", (string)lv.Source.ToList () [lv.SelectedItem!.Value]!);
  96. }
  97. #region ListView Tests (from ListViewTests.cs - parallelizable)
  98. [Fact]
  99. public void Constructors_Defaults ()
  100. {
  101. var lv = new ListView ();
  102. Assert.Null (lv.Source);
  103. Assert.True (lv.CanFocus);
  104. Assert.Null (lv.SelectedItem);
  105. Assert.False (lv.AllowsMultipleSelection);
  106. lv = new () { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
  107. Assert.NotNull (lv.Source);
  108. Assert.Null (lv.SelectedItem);
  109. lv = new () { Source = new NewListDataSource () };
  110. Assert.NotNull (lv.Source);
  111. Assert.Null (lv.SelectedItem);
  112. lv = new ()
  113. {
  114. Y = 1, Width = 10, Height = 20, Source = new ListWrapper<string> (["One", "Two", "Three"])
  115. };
  116. Assert.NotNull (lv.Source);
  117. Assert.Null (lv.SelectedItem);
  118. Assert.Equal (new (0, 1, 10, 20), lv.Frame);
  119. lv = new () { Y = 1, Width = 10, Height = 20, Source = new NewListDataSource () };
  120. Assert.NotNull (lv.Source);
  121. Assert.Null (lv.SelectedItem);
  122. Assert.Equal (new (0, 1, 10, 20), lv.Frame);
  123. }
  124. private class NewListDataSource : IListDataSource
  125. {
  126. #pragma warning disable CS0067
  127. public event NotifyCollectionChangedEventHandler? CollectionChanged;
  128. #pragma warning restore CS0067
  129. public int Count => 0;
  130. public int Length => 0;
  131. public bool SuspendCollectionChangedEvent
  132. {
  133. get => throw new NotImplementedException ();
  134. set => throw new NotImplementedException ();
  135. }
  136. public bool IsMarked (int item) { throw new NotImplementedException (); }
  137. public void Render (
  138. ListView container,
  139. bool selected,
  140. int item,
  141. int col,
  142. int line,
  143. int width,
  144. int viewportX = 0
  145. )
  146. {
  147. throw new NotImplementedException ();
  148. }
  149. public void SetMark (int item, bool value) { throw new NotImplementedException (); }
  150. public IList ToList () { return new List<string> { "One", "Two", "Three" }; }
  151. public void Dispose () { throw new NotImplementedException (); }
  152. }
  153. [Fact]
  154. public void KeyBindings_Command ()
  155. {
  156. ObservableCollection<string> source = ["One", "Two", "Three"];
  157. var lv = new ListView { Height = 2, AllowsMarking = true, Source = new ListWrapper<string> (source) };
  158. lv.BeginInit ();
  159. lv.EndInit ();
  160. Assert.Null (lv.SelectedItem);
  161. Assert.True (lv.NewKeyDownEvent (Key.CursorDown));
  162. Assert.Equal (0, lv.SelectedItem);
  163. Assert.True (lv.NewKeyDownEvent (Key.CursorUp));
  164. Assert.Equal (0, lv.SelectedItem);
  165. Assert.True (lv.NewKeyDownEvent (Key.PageDown));
  166. Assert.Equal (2, lv.SelectedItem);
  167. Assert.Equal (2, lv.TopItem);
  168. Assert.True (lv.NewKeyDownEvent (Key.PageUp));
  169. Assert.Equal (0, lv.SelectedItem);
  170. Assert.Equal (0, lv.TopItem);
  171. Assert.False (lv.Source.IsMarked (lv.SelectedItem!.Value));
  172. Assert.True (lv.NewKeyDownEvent (Key.Space));
  173. Assert.True (lv.Source.IsMarked (lv.SelectedItem!.Value));
  174. var opened = false;
  175. lv.OpenSelectedItem += (s, _) => opened = true;
  176. Assert.True (lv.NewKeyDownEvent (Key.Enter));
  177. Assert.True (opened);
  178. Assert.True (lv.NewKeyDownEvent (Key.End));
  179. Assert.Equal (2, lv.SelectedItem);
  180. Assert.True (lv.NewKeyDownEvent (Key.Home));
  181. Assert.Equal (0, lv.SelectedItem);
  182. }
  183. [Fact]
  184. public void HotKey_Command_SetsFocus ()
  185. {
  186. var view = new ListView ();
  187. view.CanFocus = true;
  188. Assert.False (view.HasFocus);
  189. view.InvokeCommand (Command.HotKey);
  190. Assert.True (view.HasFocus);
  191. }
  192. [Fact]
  193. public void HotKey_Command_Does_Not_Accept ()
  194. {
  195. var listView = new ListView ();
  196. var accepted = false;
  197. listView.Accepting += OnAccepted;
  198. listView.InvokeCommand (Command.HotKey);
  199. Assert.False (accepted);
  200. return;
  201. void OnAccepted (object? sender, CommandEventArgs e) { accepted = true; }
  202. }
  203. [Fact]
  204. public void Accept_Command_Accepts_and_Opens_Selected_Item ()
  205. {
  206. ObservableCollection<string> source = ["One", "Two", "Three"];
  207. var listView = new ListView { Source = new ListWrapper<string> (source) };
  208. listView.SelectedItem = 0;
  209. var accepted = false;
  210. var opened = false;
  211. var selectedValue = string.Empty;
  212. listView.Accepting += Accepted;
  213. listView.OpenSelectedItem += OpenSelectedItem;
  214. listView.InvokeCommand (Command.Accept);
  215. Assert.True (accepted);
  216. Assert.True (opened);
  217. Assert.Equal (source [0], selectedValue);
  218. return;
  219. void OpenSelectedItem (object? sender, ListViewItemEventArgs e)
  220. {
  221. opened = true;
  222. selectedValue = e.Value!.ToString ();
  223. }
  224. void Accepted (object? sender, CommandEventArgs e) { accepted = true; }
  225. }
  226. [Fact]
  227. public void Accept_Cancel_Event_Prevents_OpenSelectedItem ()
  228. {
  229. ObservableCollection<string> source = ["One", "Two", "Three"];
  230. var listView = new ListView { Source = new ListWrapper<string> (source) };
  231. listView.SelectedItem = 0;
  232. var accepted = false;
  233. var opened = false;
  234. var selectedValue = string.Empty;
  235. listView.Accepting += Accepted;
  236. listView.OpenSelectedItem += OpenSelectedItem;
  237. listView.InvokeCommand (Command.Accept);
  238. Assert.True (accepted);
  239. Assert.False (opened);
  240. Assert.Equal (string.Empty, selectedValue);
  241. return;
  242. void OpenSelectedItem (object? sender, ListViewItemEventArgs e)
  243. {
  244. opened = true;
  245. selectedValue = e.Value!.ToString ();
  246. }
  247. void Accepted (object? sender, CommandEventArgs e)
  248. {
  249. accepted = true;
  250. e.Handled = true;
  251. }
  252. }
  253. [Fact]
  254. public void ListViewProcessKeyReturnValue_WithMultipleCommands ()
  255. {
  256. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three", "Four"]) };
  257. Assert.NotNull (lv.Source);
  258. // first item should be deselected by default
  259. Assert.Null (lv.SelectedItem);
  260. // bind shift down to move down twice in control
  261. lv.KeyBindings.Add (Key.CursorDown.WithShift, Command.Down, Command.Down);
  262. Key ev = Key.CursorDown.WithShift;
  263. Assert.True (lv.NewKeyDownEvent (ev), "The first time we move down 2 it should be possible");
  264. // After moving down twice from null we should be at 'Two'
  265. Assert.Equal (1, lv.SelectedItem);
  266. // clear the items
  267. lv.SetSource<string> (null);
  268. // Press key combo again - return should be false this time as none of the Commands are allowable
  269. Assert.False (lv.NewKeyDownEvent (ev), "We cannot move down so will not respond to this");
  270. }
  271. [Fact]
  272. public void AllowsMarking_True_SpaceWithShift_SelectsThenDown_SingleSelection ()
  273. {
  274. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
  275. lv.AllowsMarking = true;
  276. lv.AllowsMultipleSelection = false;
  277. Assert.NotNull (lv.Source);
  278. // first item should be deselected by default
  279. Assert.Null (lv.SelectedItem);
  280. // nothing is ticked
  281. Assert.False (lv.Source.IsMarked (0));
  282. Assert.False (lv.Source.IsMarked (1));
  283. Assert.False (lv.Source.IsMarked (2));
  284. // view should indicate that it has accepted and consumed the event
  285. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  286. // first item should now be selected
  287. Assert.Equal (0, lv.SelectedItem);
  288. // none of the items should be ticked
  289. Assert.False (lv.Source.IsMarked (0));
  290. Assert.False (lv.Source.IsMarked (1));
  291. Assert.False (lv.Source.IsMarked (2));
  292. // Press key combo again
  293. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  294. // second item should now be selected
  295. Assert.Equal (1, lv.SelectedItem);
  296. // first item only should be ticked
  297. Assert.True (lv.Source.IsMarked (0));
  298. Assert.False (lv.Source.IsMarked (1));
  299. Assert.False (lv.Source.IsMarked (2));
  300. // Press key combo again
  301. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  302. Assert.Equal (2, lv.SelectedItem);
  303. Assert.False (lv.Source.IsMarked (0));
  304. Assert.True (lv.Source.IsMarked (1));
  305. Assert.False (lv.Source.IsMarked (2));
  306. // Press key combo again
  307. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  308. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  309. Assert.False (lv.Source.IsMarked (0));
  310. Assert.False (lv.Source.IsMarked (1));
  311. Assert.True (lv.Source.IsMarked (2)); // but can toggle marked
  312. // Press key combo again
  313. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  314. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  315. Assert.False (lv.Source.IsMarked (0));
  316. Assert.False (lv.Source.IsMarked (1));
  317. Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked
  318. }
  319. [Fact]
  320. public void AllowsMarking_True_SpaceWithShift_SelectsThenDown_MultipleSelection ()
  321. {
  322. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
  323. lv.AllowsMarking = true;
  324. lv.AllowsMultipleSelection = true;
  325. Assert.NotNull (lv.Source);
  326. // first item should be deselected by default
  327. Assert.Null (lv.SelectedItem);
  328. // nothing is ticked
  329. Assert.False (lv.Source.IsMarked (0));
  330. Assert.False (lv.Source.IsMarked (1));
  331. Assert.False (lv.Source.IsMarked (2));
  332. // view should indicate that it has accepted and consumed the event
  333. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  334. // first item should now be selected
  335. Assert.Equal (0, lv.SelectedItem);
  336. // none of the items should be ticked
  337. Assert.False (lv.Source.IsMarked (0));
  338. Assert.False (lv.Source.IsMarked (1));
  339. Assert.False (lv.Source.IsMarked (2));
  340. // Press key combo again
  341. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  342. // second item should now be selected
  343. Assert.Equal (1, lv.SelectedItem);
  344. // first item only should be ticked
  345. Assert.True (lv.Source.IsMarked (0));
  346. Assert.False (lv.Source.IsMarked (1));
  347. Assert.False (lv.Source.IsMarked (2));
  348. // Press key combo again
  349. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  350. Assert.Equal (2, lv.SelectedItem);
  351. Assert.True (lv.Source.IsMarked (0));
  352. Assert.True (lv.Source.IsMarked (1));
  353. Assert.False (lv.Source.IsMarked (2));
  354. // Press key combo again
  355. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  356. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  357. Assert.True (lv.Source.IsMarked (0));
  358. Assert.True (lv.Source.IsMarked (1));
  359. Assert.True (lv.Source.IsMarked (2)); // but can toggle marked
  360. // Press key combo again
  361. Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
  362. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  363. Assert.True (lv.Source.IsMarked (0));
  364. Assert.True (lv.Source.IsMarked (1));
  365. Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked
  366. }
  367. [Fact]
  368. public void ListWrapper_StartsWith ()
  369. {
  370. ListWrapper<string> lw = new (["One", "Two", "Three"]);
  371. Assert.Equal (1, lw.StartsWith ("t"));
  372. Assert.Equal (1, lw.StartsWith ("tw"));
  373. Assert.Equal (2, lw.StartsWith ("th"));
  374. Assert.Equal (1, lw.StartsWith ("T"));
  375. Assert.Equal (1, lw.StartsWith ("TW"));
  376. Assert.Equal (2, lw.StartsWith ("TH"));
  377. lw = new (["One", "Two", "Three"]);
  378. Assert.Equal (1, lw.StartsWith ("t"));
  379. Assert.Equal (1, lw.StartsWith ("tw"));
  380. Assert.Equal (2, lw.StartsWith ("th"));
  381. Assert.Equal (1, lw.StartsWith ("T"));
  382. Assert.Equal (1, lw.StartsWith ("TW"));
  383. Assert.Equal (2, lw.StartsWith ("TH"));
  384. }
  385. [Fact]
  386. public void OnEnter_Does_Not_Throw_Exception ()
  387. {
  388. var lv = new ListView ();
  389. var top = new View ();
  390. top.Add (lv);
  391. Exception exception = Record.Exception (() => lv.SetFocus ());
  392. Assert.Null (exception);
  393. }
  394. [Fact]
  395. public void SelectedItem_Get_Set ()
  396. {
  397. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
  398. Assert.Null (lv.SelectedItem);
  399. Assert.Throws<ArgumentException> (() => lv.SelectedItem = 3);
  400. Exception exception = Record.Exception (() => lv.SelectedItem = null);
  401. Assert.Null (exception);
  402. }
  403. [Fact]
  404. public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null ()
  405. {
  406. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two"]) };
  407. Assert.NotNull (lv.Source);
  408. lv.SetSource<string> (null);
  409. Assert.NotNull (lv.Source);
  410. lv.Source = null;
  411. Assert.Null (lv.Source);
  412. lv = new () { Source = new ListWrapper<string> (["One", "Two"]) };
  413. Assert.NotNull (lv.Source);
  414. lv.SetSourceAsync<string> (null);
  415. Assert.NotNull (lv.Source);
  416. }
  417. [Fact]
  418. public void SettingEmptyKeybindingThrows ()
  419. {
  420. var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
  421. Assert.Throws<ArgumentException> (() => lv.KeyBindings.Add (Key.Space));
  422. }
  423. [Fact]
  424. public void CollectionChanged_Event ()
  425. {
  426. var added = 0;
  427. var removed = 0;
  428. ObservableCollection<string> source = [];
  429. var lv = new ListView { Source = new ListWrapper<string> (source) };
  430. lv.CollectionChanged += (sender, args) =>
  431. {
  432. if (args.Action == NotifyCollectionChangedAction.Add)
  433. {
  434. added++;
  435. }
  436. else if (args.Action == NotifyCollectionChangedAction.Remove)
  437. {
  438. removed++;
  439. }
  440. };
  441. for (var i = 0; i < 3; i++)
  442. {
  443. source.Add ($"Item{i}");
  444. }
  445. Assert.Equal (3, added);
  446. Assert.Equal (0, removed);
  447. added = 0;
  448. for (var i = 0; i < 3; i++)
  449. {
  450. source.Remove (source [0]);
  451. }
  452. Assert.Equal (0, added);
  453. Assert.Equal (3, removed);
  454. Assert.Empty (source);
  455. }
  456. [Fact]
  457. public void CollectionChanged_Event_Is_Only_Subscribed_Once ()
  458. {
  459. var added = 0;
  460. var removed = 0;
  461. var otherActions = 0;
  462. IList<string> source1 = [];
  463. var lv = new ListView { Source = new ListWrapper<string> (new (source1)) };
  464. lv.CollectionChanged += (sender, args) =>
  465. {
  466. if (args.Action == NotifyCollectionChangedAction.Add)
  467. {
  468. added++;
  469. }
  470. else if (args.Action == NotifyCollectionChangedAction.Remove)
  471. {
  472. removed++;
  473. }
  474. else
  475. {
  476. otherActions++;
  477. }
  478. };
  479. ObservableCollection<string> source2 = [];
  480. lv.Source = new ListWrapper<string> (source2);
  481. ObservableCollection<string> source3 = [];
  482. lv.Source = new ListWrapper<string> (source3);
  483. Assert.Equal (0, added);
  484. Assert.Equal (0, removed);
  485. Assert.Equal (0, otherActions);
  486. for (var i = 0; i < 3; i++)
  487. {
  488. source1.Add ($"Item{i}");
  489. source2.Add ($"Item{i}");
  490. source3.Add ($"Item{i}");
  491. }
  492. Assert.Equal (3, added);
  493. Assert.Equal (0, removed);
  494. Assert.Equal (0, otherActions);
  495. added = 0;
  496. for (var i = 0; i < 3; i++)
  497. {
  498. source1.Remove (source1 [0]);
  499. source2.Remove (source2 [0]);
  500. source3.Remove (source3 [0]);
  501. }
  502. Assert.Equal (0, added);
  503. Assert.Equal (3, removed);
  504. Assert.Equal (0, otherActions);
  505. Assert.Empty (source1);
  506. Assert.Empty (source2);
  507. Assert.Empty (source3);
  508. }
  509. [Fact]
  510. public void CollectionChanged_Event_UnSubscribe_Previous_If_New_Is_Null ()
  511. {
  512. var added = 0;
  513. var removed = 0;
  514. var otherActions = 0;
  515. ObservableCollection<string> source1 = [];
  516. var lv = new ListView { Source = new ListWrapper<string> (source1) };
  517. lv.CollectionChanged += (sender, args) =>
  518. {
  519. if (args.Action == NotifyCollectionChangedAction.Add)
  520. {
  521. added++;
  522. }
  523. else if (args.Action == NotifyCollectionChangedAction.Remove)
  524. {
  525. removed++;
  526. }
  527. else
  528. {
  529. otherActions++;
  530. }
  531. };
  532. lv.Source = new ListWrapper<string> (null);
  533. Assert.Equal (0, added);
  534. Assert.Equal (0, removed);
  535. Assert.Equal (0, otherActions);
  536. for (var i = 0; i < 3; i++)
  537. {
  538. source1.Add ($"Item{i}");
  539. }
  540. Assert.Equal (0, added);
  541. Assert.Equal (0, removed);
  542. Assert.Equal (0, otherActions);
  543. for (var i = 0; i < 3; i++)
  544. {
  545. source1.Remove (source1 [0]);
  546. }
  547. Assert.Equal (0, added);
  548. Assert.Equal (0, removed);
  549. Assert.Equal (0, otherActions);
  550. Assert.Empty (source1);
  551. }
  552. [Fact]
  553. public void ListWrapper_CollectionChanged_Event_Is_Only_Subscribed_Once ()
  554. {
  555. var added = 0;
  556. var removed = 0;
  557. var otherActions = 0;
  558. ObservableCollection<string> source1 = [];
  559. ListWrapper<string> lw = new (source1);
  560. lw.CollectionChanged += (sender, args) =>
  561. {
  562. if (args.Action == NotifyCollectionChangedAction.Add)
  563. {
  564. added++;
  565. }
  566. else if (args.Action == NotifyCollectionChangedAction.Remove)
  567. {
  568. removed++;
  569. }
  570. else
  571. {
  572. otherActions++;
  573. }
  574. };
  575. ObservableCollection<string> source2 = [];
  576. lw = new (source2);
  577. ObservableCollection<string> source3 = [];
  578. lw = new (source3);
  579. Assert.Equal (0, added);
  580. Assert.Equal (0, removed);
  581. Assert.Equal (0, otherActions);
  582. for (var i = 0; i < 3; i++)
  583. {
  584. source1.Add ($"Item{i}");
  585. source2.Add ($"Item{i}");
  586. source3.Add ($"Item{i}");
  587. }
  588. Assert.Equal (3, added);
  589. Assert.Equal (0, removed);
  590. Assert.Equal (0, otherActions);
  591. added = 0;
  592. for (var i = 0; i < 3; i++)
  593. {
  594. source1.Remove (source1 [0]);
  595. source2.Remove (source2 [0]);
  596. source3.Remove (source3 [0]);
  597. }
  598. Assert.Equal (0, added);
  599. Assert.Equal (3, removed);
  600. Assert.Equal (0, otherActions);
  601. Assert.Empty (source1);
  602. Assert.Empty (source2);
  603. Assert.Empty (source3);
  604. }
  605. [Fact]
  606. public void ListWrapper_CollectionChanged_Event_UnSubscribe_Previous_Is_Disposed ()
  607. {
  608. var added = 0;
  609. var removed = 0;
  610. var otherActions = 0;
  611. ObservableCollection<string> source1 = [];
  612. ListWrapper<string> lw = new (source1);
  613. lw.CollectionChanged += Lw_CollectionChanged;
  614. lw.Dispose ();
  615. lw = new (null);
  616. Assert.Equal (0, lw.Count);
  617. Assert.Equal (0, added);
  618. Assert.Equal (0, removed);
  619. Assert.Equal (0, otherActions);
  620. for (var i = 0; i < 3; i++)
  621. {
  622. source1.Add ($"Item{i}");
  623. }
  624. Assert.Equal (0, added);
  625. Assert.Equal (0, removed);
  626. Assert.Equal (0, otherActions);
  627. for (var i = 0; i < 3; i++)
  628. {
  629. source1.Remove (source1 [0]);
  630. }
  631. Assert.Equal (0, added);
  632. Assert.Equal (0, removed);
  633. Assert.Equal (0, otherActions);
  634. Assert.Empty (source1);
  635. void Lw_CollectionChanged (object? sender, NotifyCollectionChangedEventArgs e)
  636. {
  637. if (e.Action == NotifyCollectionChangedAction.Add)
  638. {
  639. added++;
  640. }
  641. }
  642. }
  643. [Fact]
  644. public void ListWrapper_SuspendCollectionChangedEvent_ResumeSuspendCollectionChangedEvent_Tests ()
  645. {
  646. var added = 0;
  647. ObservableCollection<string> source = [];
  648. ListWrapper<string> lw = new (source);
  649. lw.CollectionChanged += Lw_CollectionChanged;
  650. lw.SuspendCollectionChangedEvent = true;
  651. for (var i = 0; i < 3; i++)
  652. {
  653. source.Add ($"Item{i}");
  654. }
  655. Assert.Equal (0, added);
  656. Assert.Equal (3, lw.Count);
  657. Assert.Equal (3, source.Count);
  658. lw.SuspendCollectionChangedEvent = false;
  659. for (var i = 3; i < 6; i++)
  660. {
  661. source.Add ($"Item{i}");
  662. }
  663. Assert.Equal (3, added);
  664. Assert.Equal (6, lw.Count);
  665. Assert.Equal (6, source.Count);
  666. void Lw_CollectionChanged (object? sender, NotifyCollectionChangedEventArgs e)
  667. {
  668. if (e.Action == NotifyCollectionChangedAction.Add)
  669. {
  670. added++;
  671. }
  672. }
  673. }
  674. [Fact]
  675. public void ListView_SuspendCollectionChangedEvent_ResumeSuspendCollectionChangedEvent_Tests ()
  676. {
  677. var added = 0;
  678. ObservableCollection<string> source = [];
  679. var lv = new ListView { Source = new ListWrapper<string> (source) };
  680. lv.CollectionChanged += Lw_CollectionChanged;
  681. lv.SuspendCollectionChangedEvent ();
  682. for (var i = 0; i < 3; i++)
  683. {
  684. source.Add ($"Item{i}");
  685. }
  686. Assert.Equal (0, added);
  687. Assert.Equal (3, lv.Source.Count);
  688. Assert.Equal (3, source.Count);
  689. lv.ResumeSuspendCollectionChangedEvent ();
  690. for (var i = 3; i < 6; i++)
  691. {
  692. source.Add ($"Item{i}");
  693. }
  694. Assert.Equal (3, added);
  695. Assert.Equal (6, lv.Source.Count);
  696. Assert.Equal (6, source.Count);
  697. void Lw_CollectionChanged (object? sender, NotifyCollectionChangedEventArgs e)
  698. {
  699. if (e.Action == NotifyCollectionChangedAction.Add)
  700. {
  701. added++;
  702. }
  703. }
  704. }
  705. #endregion
  706. [Fact]
  707. public void Clicking_On_Border_Is_Ignored ()
  708. {
  709. IApplication? app = Application.Create ();
  710. app.Init ("fake");
  711. var selected = "";
  712. var lv = new ListView
  713. {
  714. Height = 5,
  715. Width = 7,
  716. BorderStyle = LineStyle.Single
  717. };
  718. lv.SetSource (["One", "Two", "Three", "Four"]);
  719. lv.SelectedItemChanged += (s, e) => selected = e.Value!.ToString ();
  720. var top = new Toplevel ();
  721. top.Add (lv);
  722. app.Begin (top);
  723. //AutoInitShutdownAttribute.RunIteration ();
  724. Assert.Equal (new (1), lv.Border!.Thickness);
  725. Assert.Null (lv.SelectedItem);
  726. Assert.Equal ("", lv.Text);
  727. DriverAssert.AssertDriverContentsWithFrameAre (
  728. @"
  729. ┌─────┐
  730. │One │
  731. │Two │
  732. │Three│
  733. └─────┘",
  734. _output, app?.Driver);
  735. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Clicked });
  736. Assert.Equal ("", selected);
  737. Assert.Null (lv.SelectedItem);
  738. app?.Mouse.RaiseMouseEvent (
  739. new ()
  740. {
  741. ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Clicked
  742. });
  743. Assert.Equal ("One", selected);
  744. Assert.Equal (0, lv.SelectedItem);
  745. app?.Mouse.RaiseMouseEvent (
  746. new ()
  747. {
  748. ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Clicked
  749. });
  750. Assert.Equal ("Two", selected);
  751. Assert.Equal (1, lv.SelectedItem);
  752. app?.Mouse.RaiseMouseEvent (
  753. new ()
  754. {
  755. ScreenPosition = new (1, 3), Flags = MouseFlags.Button1Clicked
  756. });
  757. Assert.Equal ("Three", selected);
  758. Assert.Equal (2, lv.SelectedItem);
  759. app?.Mouse.RaiseMouseEvent (
  760. new ()
  761. {
  762. ScreenPosition = new (1, 4), Flags = MouseFlags.Button1Clicked
  763. });
  764. Assert.Equal ("Three", selected);
  765. Assert.Equal (2, lv.SelectedItem);
  766. top.Dispose ();
  767. app?.Shutdown ();
  768. }
  769. [Fact]
  770. public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp ()
  771. {
  772. IApplication? app = Application.Create ();
  773. app.Init ("fake");
  774. app.Driver?.SetScreenSize (12, 12);
  775. ObservableCollection<string> source = [];
  776. for (var i = 0; i < 20; i++)
  777. {
  778. source.Add ($"Line{i}");
  779. }
  780. var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill (), Source = new ListWrapper<string> (source) };
  781. var win = new Window ();
  782. win.Add (lv);
  783. var top = new Toplevel ();
  784. top.Add (win);
  785. app.Begin (top);
  786. Assert.Null (lv.SelectedItem);
  787. DriverAssert.AssertDriverContentsWithFrameAre (
  788. @"
  789. ┌──────────┐
  790. │Line0 │
  791. │Line1 │
  792. │Line2 │
  793. │Line3 │
  794. │Line4 │
  795. │Line5 │
  796. │Line6 │
  797. │Line7 │
  798. │Line8 │
  799. │Line9 │
  800. └──────────┘",
  801. _output, app.Driver
  802. );
  803. Assert.True (lv.ScrollVertical (10));
  804. app.LayoutAndDraw ();
  805. Assert.Null (lv.SelectedItem);
  806. DriverAssert.AssertDriverContentsWithFrameAre (
  807. @"
  808. ┌──────────┐
  809. │Line10 │
  810. │Line11 │
  811. │Line12 │
  812. │Line13 │
  813. │Line14 │
  814. │Line15 │
  815. │Line16 │
  816. │Line17 │
  817. │Line18 │
  818. │Line19 │
  819. └──────────┘",
  820. _output, app.Driver
  821. );
  822. Assert.True (lv.MoveDown ());
  823. app.LayoutAndDraw ();
  824. Assert.Equal (0, lv.SelectedItem);
  825. DriverAssert.AssertDriverContentsWithFrameAre (
  826. @"
  827. ┌──────────┐
  828. │Line0 │
  829. │Line1 │
  830. │Line2 │
  831. │Line3 │
  832. │Line4 │
  833. │Line5 │
  834. │Line6 │
  835. │Line7 │
  836. │Line8 │
  837. │Line9 │
  838. └──────────┘",
  839. _output, app.Driver
  840. );
  841. Assert.True (lv.MoveEnd ());
  842. app.LayoutAndDraw ();
  843. Assert.Equal (19, lv.SelectedItem);
  844. DriverAssert.AssertDriverContentsWithFrameAre (
  845. @"
  846. ┌──────────┐
  847. │Line10 │
  848. │Line11 │
  849. │Line12 │
  850. │Line13 │
  851. │Line14 │
  852. │Line15 │
  853. │Line16 │
  854. │Line17 │
  855. │Line18 │
  856. │Line19 │
  857. └──────────┘",
  858. _output, app.Driver
  859. );
  860. Assert.True (lv.ScrollVertical (-20));
  861. app.LayoutAndDraw ();
  862. Assert.Equal (19, lv.SelectedItem);
  863. DriverAssert.AssertDriverContentsWithFrameAre (
  864. @"
  865. ┌──────────┐
  866. │Line0 │
  867. │Line1 │
  868. │Line2 │
  869. │Line3 │
  870. │Line4 │
  871. │Line5 │
  872. │Line6 │
  873. │Line7 │
  874. │Line8 │
  875. │Line9 │
  876. └──────────┘",
  877. _output, app.Driver
  878. );
  879. Assert.True (lv.MoveDown ());
  880. app.LayoutAndDraw ();
  881. Assert.Equal (19, lv.SelectedItem);
  882. DriverAssert.AssertDriverContentsWithFrameAre (
  883. @"
  884. ┌──────────┐
  885. │Line10 │
  886. │Line11 │
  887. │Line12 │
  888. │Line13 │
  889. │Line14 │
  890. │Line15 │
  891. │Line16 │
  892. │Line17 │
  893. │Line18 │
  894. │Line19 │
  895. └──────────┘",
  896. _output, app.Driver
  897. );
  898. Assert.True (lv.ScrollVertical (-20));
  899. app.LayoutAndDraw ();
  900. Assert.Equal (19, lv.SelectedItem);
  901. DriverAssert.AssertDriverContentsWithFrameAre (
  902. @"
  903. ┌──────────┐
  904. │Line0 │
  905. │Line1 │
  906. │Line2 │
  907. │Line3 │
  908. │Line4 │
  909. │Line5 │
  910. │Line6 │
  911. │Line7 │
  912. │Line8 │
  913. │Line9 │
  914. └──────────┘",
  915. _output, app.Driver
  916. );
  917. Assert.True (lv.MoveDown ());
  918. app.LayoutAndDraw ();
  919. Assert.Equal (19, lv.SelectedItem);
  920. DriverAssert.AssertDriverContentsWithFrameAre (
  921. @"
  922. ┌──────────┐
  923. │Line10 │
  924. │Line11 │
  925. │Line12 │
  926. │Line13 │
  927. │Line14 │
  928. │Line15 │
  929. │Line16 │
  930. │Line17 │
  931. │Line18 │
  932. │Line19 │
  933. └──────────┘",
  934. _output, app.Driver
  935. );
  936. Assert.True (lv.MoveHome ());
  937. app.LayoutAndDraw ();
  938. Assert.Equal (0, lv.SelectedItem);
  939. DriverAssert.AssertDriverContentsWithFrameAre (
  940. @"
  941. ┌──────────┐
  942. │Line0 │
  943. │Line1 │
  944. │Line2 │
  945. │Line3 │
  946. │Line4 │
  947. │Line5 │
  948. │Line6 │
  949. │Line7 │
  950. │Line8 │
  951. │Line9 │
  952. └──────────┘",
  953. _output, app.Driver
  954. );
  955. Assert.True (lv.ScrollVertical (20));
  956. app.LayoutAndDraw ();
  957. Assert.Equal (0, lv.SelectedItem);
  958. DriverAssert.AssertDriverContentsWithFrameAre (
  959. @"
  960. ┌──────────┐
  961. │Line19 │
  962. │ │
  963. │ │
  964. │ │
  965. │ │
  966. │ │
  967. │ │
  968. │ │
  969. │ │
  970. │ │
  971. └──────────┘",
  972. _output, app.Driver
  973. );
  974. Assert.True (lv.MoveUp ());
  975. app.LayoutAndDraw ();
  976. Assert.Equal (0, lv.SelectedItem);
  977. DriverAssert.AssertDriverContentsWithFrameAre (
  978. @"
  979. ┌──────────┐
  980. │Line0 │
  981. │Line1 │
  982. │Line2 │
  983. │Line3 │
  984. │Line4 │
  985. │Line5 │
  986. │Line6 │
  987. │Line7 │
  988. │Line8 │
  989. │Line9 │
  990. └──────────┘",
  991. _output, app.Driver
  992. );
  993. top.Dispose ();
  994. app.Shutdown ();
  995. }
  996. [Fact]
  997. public void EnsureSelectedItemVisible_SelectedItem ()
  998. {
  999. IApplication? app = Application.Create ();
  1000. app.Init ("fake");
  1001. app.Driver?.SetScreenSize (12, 12);
  1002. ObservableCollection<string> source = [];
  1003. for (var i = 0; i < 10; i++)
  1004. {
  1005. source.Add ($"Item {i}");
  1006. }
  1007. var lv = new ListView { Width = 10, Height = 5, Source = new ListWrapper<string> (source) };
  1008. var top = new Toplevel ();
  1009. top.Add (lv);
  1010. app.Begin (top);
  1011. DriverAssert.AssertDriverContentsWithFrameAre (
  1012. @"
  1013. Item 0
  1014. Item 1
  1015. Item 2
  1016. Item 3
  1017. Item 4",
  1018. _output, app.Driver
  1019. );
  1020. // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged
  1021. lv.SelectedItem = 6;
  1022. app.LayoutAndDraw ();
  1023. DriverAssert.AssertDriverContentsWithFrameAre (
  1024. @"
  1025. Item 2
  1026. Item 3
  1027. Item 4
  1028. Item 5
  1029. Item 6",
  1030. _output, app.Driver
  1031. );
  1032. top.Dispose ();
  1033. app.Shutdown ();
  1034. }
  1035. [Fact]
  1036. public void EnsureSelectedItemVisible_Top ()
  1037. {
  1038. IApplication? app = Application.Create ();
  1039. app.Init ("fake");
  1040. IDriver? driver = app.Driver;
  1041. driver?.SetScreenSize (8, 2);
  1042. ObservableCollection<string> source = ["First", "Second"];
  1043. var lv = new ListView { Width = Dim.Fill (), Height = 1, Source = new ListWrapper<string> (source) };
  1044. lv.SelectedItem = 1;
  1045. var top = new Toplevel ();
  1046. top.Add (lv);
  1047. app.Begin (top);
  1048. Assert.Equal ("Second ", GetContents (0));
  1049. Assert.Equal (new (' ', 7), GetContents (1));
  1050. lv.MoveUp ();
  1051. lv.Draw ();
  1052. Assert.Equal ("First ", GetContents (0));
  1053. Assert.Equal (new (' ', 7), GetContents (1));
  1054. string GetContents (int line)
  1055. {
  1056. var item = "";
  1057. for (var i = 0; i < 7; i++)
  1058. {
  1059. item += (app.Driver?.Contents!) [line, i].Rune;
  1060. }
  1061. return item;
  1062. }
  1063. top.Dispose ();
  1064. app.Shutdown ();
  1065. }
  1066. [Fact]
  1067. public void LeftItem_TopItem_Tests ()
  1068. {
  1069. IApplication? app = Application.Create ();
  1070. app.Init ("fake");
  1071. app.Driver?.SetScreenSize (12, 12);
  1072. ObservableCollection<string> source = [];
  1073. for (var i = 0; i < 5; i++)
  1074. {
  1075. source.Add ($"Item {i}");
  1076. }
  1077. var lv = new ListView
  1078. {
  1079. X = 1,
  1080. Source = new ListWrapper<string> (source)
  1081. };
  1082. lv.Height = lv.Source.Count;
  1083. lv.Width = lv.MaxLength;
  1084. var top = new Toplevel ();
  1085. top.Add (lv);
  1086. app.Begin (top);
  1087. DriverAssert.AssertDriverContentsWithFrameAre (
  1088. @"
  1089. Item 0
  1090. Item 1
  1091. Item 2
  1092. Item 3
  1093. Item 4",
  1094. _output, app.Driver);
  1095. lv.LeftItem = 1;
  1096. lv.TopItem = 1;
  1097. app.LayoutAndDraw ();
  1098. DriverAssert.AssertDriverContentsWithFrameAre (
  1099. @"
  1100. tem 1
  1101. tem 2
  1102. tem 3
  1103. tem 4",
  1104. _output, app.Driver);
  1105. top.Dispose ();
  1106. app.Shutdown ();
  1107. }
  1108. [Fact]
  1109. public void RowRender_Event ()
  1110. {
  1111. IApplication? app = Application.Create ();
  1112. app.Init ("fake");
  1113. var rendered = false;
  1114. ObservableCollection<string> source = ["one", "two", "three"];
  1115. var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill () };
  1116. lv.RowRender += (s, _) => rendered = true;
  1117. var top = new Toplevel ();
  1118. top.Add (lv);
  1119. app.Begin (top);
  1120. Assert.False (rendered);
  1121. lv.SetSource (source);
  1122. lv.Draw ();
  1123. Assert.True (rendered);
  1124. top.Dispose ();
  1125. app.Shutdown ();
  1126. }
  1127. [Fact]
  1128. public void Vertical_ScrollBar_Hides_And_Shows_As_Needed ()
  1129. {
  1130. IApplication? app = Application.Create ();
  1131. app.Init ("fake");
  1132. var lv = new ListView
  1133. {
  1134. Width = 10,
  1135. Height = 3
  1136. };
  1137. lv.VerticalScrollBar.AutoShow = true;
  1138. lv.SetSource (["One", "Two", "Three", "Four", "Five"]);
  1139. var top = new Toplevel ();
  1140. top.Add (lv);
  1141. app.Begin (top);
  1142. Assert.True (lv.VerticalScrollBar.Visible);
  1143. DriverAssert.AssertDriverContentsWithFrameAre (
  1144. @"
  1145. One ▲
  1146. Two █
  1147. Three ▼",
  1148. _output, app?.Driver);
  1149. lv.Height = 5;
  1150. app?.LayoutAndDraw ();
  1151. Assert.False (lv.VerticalScrollBar.Visible);
  1152. DriverAssert.AssertDriverContentsWithFrameAre (
  1153. @"
  1154. One
  1155. Two
  1156. Three
  1157. Four
  1158. Five ",
  1159. _output, app?.Driver);
  1160. top.Dispose ();
  1161. app?.Shutdown ();
  1162. }
  1163. [Fact]
  1164. public void Mouse_Wheel_Scrolls ()
  1165. {
  1166. IApplication? app = Application.Create ();
  1167. app.Init ("fake");
  1168. var lv = new ListView
  1169. {
  1170. Width = 10,
  1171. Height = 3,
  1172. };
  1173. lv.SetSource (["One", "Two", "Three", "Four", "Five"]);
  1174. var top = new Toplevel ();
  1175. top.Add (lv);
  1176. app.Begin (top);
  1177. // Initially, we are at the top.
  1178. Assert.Equal (0, lv.TopItem);
  1179. DriverAssert.AssertDriverContentsWithFrameAre (
  1180. @"
  1181. One
  1182. Two
  1183. Three",
  1184. _output, app?.Driver);
  1185. // Scroll down
  1186. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledDown });
  1187. app?.LayoutAndDraw ();
  1188. Assert.Equal (1, lv.TopItem);
  1189. DriverAssert.AssertDriverContentsWithFrameAre (
  1190. @"
  1191. Two
  1192. Three
  1193. Four ",
  1194. _output, app?.Driver);
  1195. // Scroll up
  1196. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledUp });
  1197. app?.LayoutAndDraw ();
  1198. Assert.Equal (0, lv.TopItem);
  1199. DriverAssert.AssertDriverContentsWithFrameAre (
  1200. @"
  1201. One
  1202. Two
  1203. Three",
  1204. _output, app?.Driver);
  1205. top.Dispose ();
  1206. app?.Shutdown ();
  1207. }
  1208. [Fact]
  1209. public void SelectedItem_With_Source_Null_Does_Nothing ()
  1210. {
  1211. var lv = new ListView ();
  1212. Assert.Null (lv.Source);
  1213. // should not throw
  1214. lv.SelectedItem = 0;
  1215. Assert.Null (lv.SelectedItem);
  1216. }
  1217. [Fact]
  1218. public void Horizontal_Scroll ()
  1219. {
  1220. IApplication? app = Application.Create ();
  1221. app.Init ("fake");
  1222. var lv = new ListView
  1223. {
  1224. Width = 10,
  1225. Height = 3,
  1226. };
  1227. lv.SetSource (["One", "Two", "Three - long", "Four", "Five"]);
  1228. var top = new Toplevel ();
  1229. top.Add (lv);
  1230. app.Begin (top);
  1231. Assert.Equal (0, lv.LeftItem);
  1232. DriverAssert.AssertDriverContentsWithFrameAre (
  1233. @"
  1234. One
  1235. Two
  1236. Three - lo",
  1237. _output, app?.Driver);
  1238. lv.ScrollHorizontal (1);
  1239. app?.LayoutAndDraw ();
  1240. Assert.Equal (1, lv.LeftItem);
  1241. DriverAssert.AssertDriverContentsWithFrameAre (
  1242. @"
  1243. ne
  1244. wo
  1245. hree - lon",
  1246. _output, app?.Driver);
  1247. // Scroll right with mouse
  1248. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledRight });
  1249. app?.LayoutAndDraw ();
  1250. Assert.Equal (2, lv.LeftItem);
  1251. DriverAssert.AssertDriverContentsWithFrameAre (
  1252. @"
  1253. e
  1254. o
  1255. ree - long",
  1256. _output, app?.Driver);
  1257. // Scroll left with mouse
  1258. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledLeft });
  1259. app?.LayoutAndDraw ();
  1260. Assert.Equal (1, lv.LeftItem);
  1261. DriverAssert.AssertDriverContentsWithFrameAre (
  1262. @"
  1263. ne
  1264. wo
  1265. hree - lon",
  1266. _output, app?.Driver);
  1267. top.Dispose ();
  1268. app?.Shutdown ();
  1269. }
  1270. [Fact]
  1271. public async Task SetSourceAsync_SetsSource ()
  1272. {
  1273. var lv = new ListView ();
  1274. var source = new ObservableCollection<string> { "One", "Two", "Three" };
  1275. await lv.SetSourceAsync (source);
  1276. Assert.NotNull (lv.Source);
  1277. Assert.Equal (3, lv.Source.Count);
  1278. }
  1279. [Fact]
  1280. public void AllowsMultipleSelection_Set_To_False_Unmarks_All_But_Selected ()
  1281. {
  1282. var lv = new ListView { AllowsMarking = true, AllowsMultipleSelection = true };
  1283. var source = new ListWrapper<string> (["One", "Two", "Three"]);
  1284. lv.Source = source;
  1285. lv.SelectedItem = 0;
  1286. source.SetMark (0, true);
  1287. source.SetMark (1, true);
  1288. source.SetMark (2, true);
  1289. Assert.True (source.IsMarked (0));
  1290. Assert.True (source.IsMarked (1));
  1291. Assert.True (source.IsMarked (2));
  1292. lv.AllowsMultipleSelection = false;
  1293. Assert.True (source.IsMarked (0));
  1294. Assert.False (source.IsMarked (1));
  1295. Assert.False (source.IsMarked (2));
  1296. }
  1297. [Fact]
  1298. public void Source_CollectionChanged_Remove ()
  1299. {
  1300. var source = new ObservableCollection<string> { "One", "Two", "Three" };
  1301. var lv = new ListView { Source = new ListWrapper<string> (source) };
  1302. lv.SelectedItem = 2;
  1303. Assert.Equal (2, lv.SelectedItem);
  1304. Assert.Equal (3, lv.Source.Count);
  1305. source.RemoveAt (0);
  1306. Assert.Equal (2, lv.Source.Count);
  1307. Assert.Equal (1, lv.SelectedItem);
  1308. source.RemoveAt (1);
  1309. Assert.Equal (1, lv.Source.Count);
  1310. Assert.Equal (0, lv.SelectedItem);
  1311. }
  1312. }