ListViewTests.cs 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585
  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 UnitTests_Parallelizable.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 Toplevel ();
  722. top.Add (lv);
  723. app.Begin (top);
  724. //AutoInitShutdownAttribute.RunIteration ();
  725. Assert.Equal (new (1), lv.Border!.Thickness);
  726. Assert.Null (lv.SelectedItem);
  727. Assert.Equal ("", lv.Text);
  728. DriverAssert.AssertDriverContentsWithFrameAre (
  729. @"
  730. ┌─────┐
  731. │One │
  732. │Two │
  733. │Three│
  734. └─────┘",
  735. _output, app?.Driver);
  736. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Clicked });
  737. Assert.Equal ("", selected);
  738. Assert.Null (lv.SelectedItem);
  739. app?.Mouse.RaiseMouseEvent (
  740. new ()
  741. {
  742. ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Clicked
  743. });
  744. Assert.Equal ("One", selected);
  745. Assert.Equal (0, lv.SelectedItem);
  746. app?.Mouse.RaiseMouseEvent (
  747. new ()
  748. {
  749. ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Clicked
  750. });
  751. Assert.Equal ("Two", selected);
  752. Assert.Equal (1, lv.SelectedItem);
  753. app?.Mouse.RaiseMouseEvent (
  754. new ()
  755. {
  756. ScreenPosition = new (1, 3), Flags = MouseFlags.Button1Clicked
  757. });
  758. Assert.Equal ("Three", selected);
  759. Assert.Equal (2, lv.SelectedItem);
  760. app?.Mouse.RaiseMouseEvent (
  761. new ()
  762. {
  763. ScreenPosition = new (1, 4), Flags = MouseFlags.Button1Clicked
  764. });
  765. Assert.Equal ("Three", selected);
  766. Assert.Equal (2, lv.SelectedItem);
  767. top.Dispose ();
  768. app?.Shutdown ();
  769. }
  770. [Fact]
  771. public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp ()
  772. {
  773. IApplication? app = Application.Create ();
  774. app.Init ("fake");
  775. app.Driver?.SetScreenSize (12, 12);
  776. ObservableCollection<string> source = [];
  777. for (var i = 0; i < 20; i++)
  778. {
  779. source.Add ($"Line{i}");
  780. }
  781. var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill (), Source = new ListWrapper<string> (source) };
  782. var win = new Window ();
  783. win.Add (lv);
  784. var top = new Toplevel ();
  785. top.Add (win);
  786. app.Begin (top);
  787. Assert.Null (lv.SelectedItem);
  788. DriverAssert.AssertDriverContentsWithFrameAre (
  789. @"
  790. ┌──────────┐
  791. │Line0 │
  792. │Line1 │
  793. │Line2 │
  794. │Line3 │
  795. │Line4 │
  796. │Line5 │
  797. │Line6 │
  798. │Line7 │
  799. │Line8 │
  800. │Line9 │
  801. └──────────┘",
  802. _output, app.Driver
  803. );
  804. Assert.True (lv.ScrollVertical (10));
  805. app.LayoutAndDraw ();
  806. Assert.Null (lv.SelectedItem);
  807. DriverAssert.AssertDriverContentsWithFrameAre (
  808. @"
  809. ┌──────────┐
  810. │Line10 │
  811. │Line11 │
  812. │Line12 │
  813. │Line13 │
  814. │Line14 │
  815. │Line15 │
  816. │Line16 │
  817. │Line17 │
  818. │Line18 │
  819. │Line19 │
  820. └──────────┘",
  821. _output, app.Driver
  822. );
  823. Assert.True (lv.MoveDown ());
  824. app.LayoutAndDraw ();
  825. Assert.Equal (0, lv.SelectedItem);
  826. DriverAssert.AssertDriverContentsWithFrameAre (
  827. @"
  828. ┌──────────┐
  829. │Line0 │
  830. │Line1 │
  831. │Line2 │
  832. │Line3 │
  833. │Line4 │
  834. │Line5 │
  835. │Line6 │
  836. │Line7 │
  837. │Line8 │
  838. │Line9 │
  839. └──────────┘",
  840. _output, app.Driver
  841. );
  842. Assert.True (lv.MoveEnd ());
  843. app.LayoutAndDraw ();
  844. Assert.Equal (19, lv.SelectedItem);
  845. DriverAssert.AssertDriverContentsWithFrameAre (
  846. @"
  847. ┌──────────┐
  848. │Line10 │
  849. │Line11 │
  850. │Line12 │
  851. │Line13 │
  852. │Line14 │
  853. │Line15 │
  854. │Line16 │
  855. │Line17 │
  856. │Line18 │
  857. │Line19 │
  858. └──────────┘",
  859. _output, app.Driver
  860. );
  861. Assert.True (lv.ScrollVertical (-20));
  862. app.LayoutAndDraw ();
  863. Assert.Equal (19, lv.SelectedItem);
  864. DriverAssert.AssertDriverContentsWithFrameAre (
  865. @"
  866. ┌──────────┐
  867. │Line0 │
  868. │Line1 │
  869. │Line2 │
  870. │Line3 │
  871. │Line4 │
  872. │Line5 │
  873. │Line6 │
  874. │Line7 │
  875. │Line8 │
  876. │Line9 │
  877. └──────────┘",
  878. _output, app.Driver
  879. );
  880. Assert.True (lv.MoveDown ());
  881. app.LayoutAndDraw ();
  882. Assert.Equal (19, lv.SelectedItem);
  883. DriverAssert.AssertDriverContentsWithFrameAre (
  884. @"
  885. ┌──────────┐
  886. │Line10 │
  887. │Line11 │
  888. │Line12 │
  889. │Line13 │
  890. │Line14 │
  891. │Line15 │
  892. │Line16 │
  893. │Line17 │
  894. │Line18 │
  895. │Line19 │
  896. └──────────┘",
  897. _output, app.Driver
  898. );
  899. Assert.True (lv.ScrollVertical (-20));
  900. app.LayoutAndDraw ();
  901. Assert.Equal (19, lv.SelectedItem);
  902. DriverAssert.AssertDriverContentsWithFrameAre (
  903. @"
  904. ┌──────────┐
  905. │Line0 │
  906. │Line1 │
  907. │Line2 │
  908. │Line3 │
  909. │Line4 │
  910. │Line5 │
  911. │Line6 │
  912. │Line7 │
  913. │Line8 │
  914. │Line9 │
  915. └──────────┘",
  916. _output, app.Driver
  917. );
  918. Assert.True (lv.MoveDown ());
  919. app.LayoutAndDraw ();
  920. Assert.Equal (19, lv.SelectedItem);
  921. DriverAssert.AssertDriverContentsWithFrameAre (
  922. @"
  923. ┌──────────┐
  924. │Line10 │
  925. │Line11 │
  926. │Line12 │
  927. │Line13 │
  928. │Line14 │
  929. │Line15 │
  930. │Line16 │
  931. │Line17 │
  932. │Line18 │
  933. │Line19 │
  934. └──────────┘",
  935. _output, app.Driver
  936. );
  937. Assert.True (lv.MoveHome ());
  938. app.LayoutAndDraw ();
  939. Assert.Equal (0, lv.SelectedItem);
  940. DriverAssert.AssertDriverContentsWithFrameAre (
  941. @"
  942. ┌──────────┐
  943. │Line0 │
  944. │Line1 │
  945. │Line2 │
  946. │Line3 │
  947. │Line4 │
  948. │Line5 │
  949. │Line6 │
  950. │Line7 │
  951. │Line8 │
  952. │Line9 │
  953. └──────────┘",
  954. _output, app.Driver
  955. );
  956. Assert.True (lv.ScrollVertical (20));
  957. app.LayoutAndDraw ();
  958. Assert.Equal (0, lv.SelectedItem);
  959. DriverAssert.AssertDriverContentsWithFrameAre (
  960. @"
  961. ┌──────────┐
  962. │Line19 │
  963. │ │
  964. │ │
  965. │ │
  966. │ │
  967. │ │
  968. │ │
  969. │ │
  970. │ │
  971. │ │
  972. └──────────┘",
  973. _output, app.Driver
  974. );
  975. Assert.True (lv.MoveUp ());
  976. app.LayoutAndDraw ();
  977. Assert.Equal (0, lv.SelectedItem);
  978. DriverAssert.AssertDriverContentsWithFrameAre (
  979. @"
  980. ┌──────────┐
  981. │Line0 │
  982. │Line1 │
  983. │Line2 │
  984. │Line3 │
  985. │Line4 │
  986. │Line5 │
  987. │Line6 │
  988. │Line7 │
  989. │Line8 │
  990. │Line9 │
  991. └──────────┘",
  992. _output, app.Driver
  993. );
  994. top.Dispose ();
  995. app.Shutdown ();
  996. }
  997. [Fact]
  998. public void EnsureSelectedItemVisible_SelectedItem ()
  999. {
  1000. IApplication? app = Application.Create ();
  1001. app.Init ("fake");
  1002. app.Driver?.SetScreenSize (12, 12);
  1003. ObservableCollection<string> source = [];
  1004. for (var i = 0; i < 10; i++)
  1005. {
  1006. source.Add ($"Item {i}");
  1007. }
  1008. var lv = new ListView { Width = 10, Height = 5, Source = new ListWrapper<string> (source) };
  1009. var top = new Toplevel ();
  1010. top.Add (lv);
  1011. app.Begin (top);
  1012. DriverAssert.AssertDriverContentsWithFrameAre (
  1013. @"
  1014. Item 0
  1015. Item 1
  1016. Item 2
  1017. Item 3
  1018. Item 4",
  1019. _output, app.Driver
  1020. );
  1021. // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged
  1022. lv.SelectedItem = 6;
  1023. app.LayoutAndDraw ();
  1024. DriverAssert.AssertDriverContentsWithFrameAre (
  1025. @"
  1026. Item 2
  1027. Item 3
  1028. Item 4
  1029. Item 5
  1030. Item 6",
  1031. _output, app.Driver
  1032. );
  1033. top.Dispose ();
  1034. app.Shutdown ();
  1035. }
  1036. [Fact]
  1037. public void EnsureSelectedItemVisible_Top ()
  1038. {
  1039. IApplication? app = Application.Create ();
  1040. app.Init ("fake");
  1041. IDriver? driver = app.Driver;
  1042. driver?.SetScreenSize (8, 2);
  1043. ObservableCollection<string> source = ["First", "Second"];
  1044. var lv = new ListView { Width = Dim.Fill (), Height = 1, Source = new ListWrapper<string> (source) };
  1045. lv.SelectedItem = 1;
  1046. var top = new Toplevel ();
  1047. top.Add (lv);
  1048. app.Begin (top);
  1049. Assert.Equal ("Second ", GetContents (0));
  1050. Assert.Equal (new (' ', 7), GetContents (1));
  1051. lv.MoveUp ();
  1052. lv.Draw ();
  1053. Assert.Equal ("First ", GetContents (0));
  1054. Assert.Equal (new (' ', 7), GetContents (1));
  1055. string GetContents (int line)
  1056. {
  1057. var sb = new StringBuilder ();
  1058. for (var i = 0; i < 7; i++)
  1059. {
  1060. sb.Append ((app?.Driver?.Contents!) [line, i].Grapheme);
  1061. }
  1062. return sb.ToString ();
  1063. }
  1064. top.Dispose ();
  1065. app.Shutdown ();
  1066. }
  1067. [Fact]
  1068. public void LeftItem_TopItem_Tests ()
  1069. {
  1070. IApplication? app = Application.Create ();
  1071. app.Init ("fake");
  1072. app.Driver?.SetScreenSize (12, 12);
  1073. ObservableCollection<string> source = [];
  1074. for (var i = 0; i < 5; i++)
  1075. {
  1076. source.Add ($"Item {i}");
  1077. }
  1078. var lv = new ListView
  1079. {
  1080. X = 1,
  1081. Source = new ListWrapper<string> (source)
  1082. };
  1083. lv.Height = lv.Source.Count;
  1084. lv.Width = lv.MaxLength;
  1085. var top = new Toplevel ();
  1086. top.Add (lv);
  1087. app.Begin (top);
  1088. DriverAssert.AssertDriverContentsWithFrameAre (
  1089. @"
  1090. Item 0
  1091. Item 1
  1092. Item 2
  1093. Item 3
  1094. Item 4",
  1095. _output, app.Driver);
  1096. lv.LeftItem = 1;
  1097. lv.TopItem = 1;
  1098. app.LayoutAndDraw ();
  1099. DriverAssert.AssertDriverContentsWithFrameAre (
  1100. @"
  1101. tem 1
  1102. tem 2
  1103. tem 3
  1104. tem 4",
  1105. _output, app.Driver);
  1106. top.Dispose ();
  1107. app.Shutdown ();
  1108. }
  1109. [Fact]
  1110. public void RowRender_Event ()
  1111. {
  1112. IApplication? app = Application.Create ();
  1113. app.Init ("fake");
  1114. var rendered = false;
  1115. ObservableCollection<string> source = ["one", "two", "three"];
  1116. var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill () };
  1117. lv.RowRender += (s, _) => rendered = true;
  1118. var top = new Toplevel ();
  1119. top.Add (lv);
  1120. app.Begin (top);
  1121. Assert.False (rendered);
  1122. lv.SetSource (source);
  1123. lv.Draw ();
  1124. Assert.True (rendered);
  1125. top.Dispose ();
  1126. app.Shutdown ();
  1127. }
  1128. [Fact]
  1129. public void Vertical_ScrollBar_Hides_And_Shows_As_Needed ()
  1130. {
  1131. IApplication? app = Application.Create ();
  1132. app.Init ("fake");
  1133. var lv = new ListView
  1134. {
  1135. Width = 10,
  1136. Height = 3
  1137. };
  1138. lv.VerticalScrollBar.AutoShow = true;
  1139. lv.SetSource (["One", "Two", "Three", "Four", "Five"]);
  1140. var top = new Toplevel ();
  1141. top.Add (lv);
  1142. app.Begin (top);
  1143. Assert.True (lv.VerticalScrollBar.Visible);
  1144. DriverAssert.AssertDriverContentsWithFrameAre (
  1145. @"
  1146. One ▲
  1147. Two █
  1148. Three ▼",
  1149. _output, app?.Driver);
  1150. lv.Height = 5;
  1151. app?.LayoutAndDraw ();
  1152. Assert.False (lv.VerticalScrollBar.Visible);
  1153. DriverAssert.AssertDriverContentsWithFrameAre (
  1154. @"
  1155. One
  1156. Two
  1157. Three
  1158. Four
  1159. Five ",
  1160. _output, app?.Driver);
  1161. top.Dispose ();
  1162. app?.Shutdown ();
  1163. }
  1164. [Fact]
  1165. public void Mouse_Wheel_Scrolls ()
  1166. {
  1167. IApplication? app = Application.Create ();
  1168. app.Init ("fake");
  1169. var lv = new ListView
  1170. {
  1171. Width = 10,
  1172. Height = 3,
  1173. };
  1174. lv.SetSource (["One", "Two", "Three", "Four", "Five"]);
  1175. var top = new Toplevel ();
  1176. top.Add (lv);
  1177. app.Begin (top);
  1178. // Initially, we are at the top.
  1179. Assert.Equal (0, lv.TopItem);
  1180. DriverAssert.AssertDriverContentsWithFrameAre (
  1181. @"
  1182. One
  1183. Two
  1184. Three",
  1185. _output, app?.Driver);
  1186. // Scroll down
  1187. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledDown });
  1188. app?.LayoutAndDraw ();
  1189. Assert.Equal (1, lv.TopItem);
  1190. DriverAssert.AssertDriverContentsWithFrameAre (
  1191. @"
  1192. Two
  1193. Three
  1194. Four ",
  1195. _output, app?.Driver);
  1196. // Scroll up
  1197. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledUp });
  1198. app?.LayoutAndDraw ();
  1199. Assert.Equal (0, lv.TopItem);
  1200. DriverAssert.AssertDriverContentsWithFrameAre (
  1201. @"
  1202. One
  1203. Two
  1204. Three",
  1205. _output, app?.Driver);
  1206. top.Dispose ();
  1207. app?.Shutdown ();
  1208. }
  1209. [Fact]
  1210. public void SelectedItem_With_Source_Null_Does_Nothing ()
  1211. {
  1212. var lv = new ListView ();
  1213. Assert.Null (lv.Source);
  1214. // should not throw
  1215. lv.SelectedItem = 0;
  1216. Assert.Null (lv.SelectedItem);
  1217. }
  1218. [Fact]
  1219. public void Horizontal_Scroll ()
  1220. {
  1221. IApplication? app = Application.Create ();
  1222. app.Init ("fake");
  1223. var lv = new ListView
  1224. {
  1225. Width = 10,
  1226. Height = 3,
  1227. };
  1228. lv.SetSource (["One", "Two", "Three - long", "Four", "Five"]);
  1229. var top = new Toplevel ();
  1230. top.Add (lv);
  1231. app.Begin (top);
  1232. Assert.Equal (0, lv.LeftItem);
  1233. DriverAssert.AssertDriverContentsWithFrameAre (
  1234. @"
  1235. One
  1236. Two
  1237. Three - lo",
  1238. _output, app?.Driver);
  1239. lv.ScrollHorizontal (1);
  1240. app?.LayoutAndDraw ();
  1241. Assert.Equal (1, lv.LeftItem);
  1242. DriverAssert.AssertDriverContentsWithFrameAre (
  1243. @"
  1244. ne
  1245. wo
  1246. hree - lon",
  1247. _output, app?.Driver);
  1248. // Scroll right with mouse
  1249. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledRight });
  1250. app?.LayoutAndDraw ();
  1251. Assert.Equal (2, lv.LeftItem);
  1252. DriverAssert.AssertDriverContentsWithFrameAre (
  1253. @"
  1254. e
  1255. o
  1256. ree - long",
  1257. _output, app?.Driver);
  1258. // Scroll left with mouse
  1259. app?.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.WheeledLeft });
  1260. app?.LayoutAndDraw ();
  1261. Assert.Equal (1, lv.LeftItem);
  1262. DriverAssert.AssertDriverContentsWithFrameAre (
  1263. @"
  1264. ne
  1265. wo
  1266. hree - lon",
  1267. _output, app?.Driver);
  1268. top.Dispose ();
  1269. app?.Shutdown ();
  1270. }
  1271. [Fact]
  1272. public async Task SetSourceAsync_SetsSource ()
  1273. {
  1274. var lv = new ListView ();
  1275. var source = new ObservableCollection<string> { "One", "Two", "Three" };
  1276. await lv.SetSourceAsync (source);
  1277. Assert.NotNull (lv.Source);
  1278. Assert.Equal (3, lv.Source.Count);
  1279. }
  1280. [Fact]
  1281. public void AllowsMultipleSelection_Set_To_False_Unmarks_All_But_Selected ()
  1282. {
  1283. var lv = new ListView { AllowsMarking = true, AllowsMultipleSelection = true };
  1284. var source = new ListWrapper<string> (["One", "Two", "Three"]);
  1285. lv.Source = source;
  1286. lv.SelectedItem = 0;
  1287. source.SetMark (0, true);
  1288. source.SetMark (1, true);
  1289. source.SetMark (2, true);
  1290. Assert.True (source.IsMarked (0));
  1291. Assert.True (source.IsMarked (1));
  1292. Assert.True (source.IsMarked (2));
  1293. lv.AllowsMultipleSelection = false;
  1294. Assert.True (source.IsMarked (0));
  1295. Assert.False (source.IsMarked (1));
  1296. Assert.False (source.IsMarked (2));
  1297. }
  1298. [Fact]
  1299. public void Source_CollectionChanged_Remove ()
  1300. {
  1301. var source = new ObservableCollection<string> { "One", "Two", "Three" };
  1302. var lv = new ListView { Source = new ListWrapper<string> (source) };
  1303. lv.SelectedItem = 2;
  1304. Assert.Equal (2, lv.SelectedItem);
  1305. Assert.Equal (3, lv.Source.Count);
  1306. source.RemoveAt (0);
  1307. Assert.Equal (2, lv.Source.Count);
  1308. Assert.Equal (1, lv.SelectedItem);
  1309. source.RemoveAt (1);
  1310. Assert.Equal (1, lv.Source.Count);
  1311. Assert.Equal (0, lv.SelectedItem);
  1312. }
  1313. }