ListViewTests.cs 48 KB

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