DefaultMenuInteractionHandlerTests.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. using System;
  2. using Avalonia.Controls.Platform;
  3. using Avalonia.Input;
  4. using Avalonia.Interactivity;
  5. using Avalonia.VisualTree;
  6. using Moq;
  7. using Xunit;
  8. namespace Avalonia.Controls.UnitTests.Platform
  9. {
  10. public class DefaultMenuInteractionHandlerTests
  11. {
  12. static PointerEventArgs CreateArgs(RoutedEvent ev, IInteractive source)
  13. => new PointerEventArgs(ev, source, new FakePointer(), (IVisual)source, default, 0, PointerPointProperties.None, default);
  14. static PointerPressedEventArgs CreatePressed(IInteractive source) => new PointerPressedEventArgs(source,
  15. new FakePointer(), (IVisual)source, default,0, new PointerPointProperties (RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
  16. default);
  17. static PointerReleasedEventArgs CreateReleased(IInteractive source) => new PointerReleasedEventArgs(source,
  18. new FakePointer(), (IVisual)source, default,0,
  19. new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased),
  20. default, MouseButton.Left);
  21. public class TopLevel
  22. {
  23. [Fact]
  24. public void Up_Opens_MenuItem_With_SubMenu()
  25. {
  26. var target = new DefaultMenuInteractionHandler(false);
  27. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  28. var e = new KeyEventArgs { Key = Key.Up, Source = item };
  29. target.KeyDown(item, e);
  30. Mock.Get(item).Verify(x => x.Open());
  31. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  32. Assert.True(e.Handled);
  33. }
  34. [Fact]
  35. public void Down_Opens_MenuItem_With_SubMenu()
  36. {
  37. var target = new DefaultMenuInteractionHandler(false);
  38. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  39. var e = new KeyEventArgs { Key = Key.Down, Source = item };
  40. target.KeyDown(item, e);
  41. Mock.Get(item).Verify(x => x.Open());
  42. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  43. Assert.True(e.Handled);
  44. }
  45. [Fact]
  46. public void Right_Selects_Next_MenuItem()
  47. {
  48. var target = new DefaultMenuInteractionHandler(false);
  49. var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Right, true) == true);
  50. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
  51. var e = new KeyEventArgs { Key = Key.Right, Source = item };
  52. target.KeyDown(item, e);
  53. Mock.Get(menu).Verify(x => x.MoveSelection(NavigationDirection.Right, true));
  54. Assert.True(e.Handled);
  55. }
  56. [Fact]
  57. public void Left_Selects_Previous_MenuItem()
  58. {
  59. var target = new DefaultMenuInteractionHandler(false);
  60. var menu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Left, true) == true);
  61. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
  62. var e = new KeyEventArgs { Key = Key.Left, Source = item };
  63. target.KeyDown(item, e);
  64. Mock.Get(menu).Verify(x => x.MoveSelection(NavigationDirection.Left, true));
  65. Assert.True(e.Handled);
  66. }
  67. [Fact]
  68. public void Enter_On_Item_With_No_SubMenu_Causes_Click()
  69. {
  70. var target = new DefaultMenuInteractionHandler(false);
  71. var menu = Mock.Of<IMenu>();
  72. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
  73. var e = new KeyEventArgs { Key = Key.Enter, Source = item };
  74. target.KeyDown(item, e);
  75. Mock.Get(item).Verify(x => x.RaiseClick());
  76. Mock.Get(menu).Verify(x => x.Close());
  77. Assert.True(e.Handled);
  78. }
  79. [Fact]
  80. public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
  81. {
  82. var target = new DefaultMenuInteractionHandler(false);
  83. var menu = Mock.Of<IMenu>();
  84. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  85. var e = new KeyEventArgs { Key = Key.Enter, Source = item };
  86. target.KeyDown(item, e);
  87. Mock.Get(item).Verify(x => x.Open());
  88. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  89. Assert.True(e.Handled);
  90. }
  91. [Fact]
  92. public void Escape_Closes_Parent_Menu()
  93. {
  94. var target = new DefaultMenuInteractionHandler(false);
  95. var menu = Mock.Of<IMenu>();
  96. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
  97. var e = new KeyEventArgs { Key = Key.Escape, Source = item };
  98. target.KeyDown(item, e);
  99. Mock.Get(menu).Verify(x => x.Close());
  100. Assert.True(e.Handled);
  101. }
  102. [Fact]
  103. public void Click_On_TopLevel_Calls_MainMenu_Open()
  104. {
  105. var target = new DefaultMenuInteractionHandler(false);
  106. var menu = new Mock<IMainMenu>();
  107. menu.As<IMenuElement>();
  108. var item = Mock.Of<IMenuItem>(x =>
  109. x.IsTopLevel == true &&
  110. x.HasSubMenu == true &&
  111. x.Parent == menu.Object);
  112. var e = CreatePressed(item);
  113. target.PointerPressed(item, e);
  114. menu.Verify(x => x.Open());
  115. }
  116. [Fact]
  117. public void Click_On_Open_TopLevel_Menu_Closes_Menu()
  118. {
  119. var target = new DefaultMenuInteractionHandler(false);
  120. var menu = Mock.Of<IMenu>();
  121. var item = Mock.Of<IMenuItem>(x =>
  122. x.IsSubMenuOpen == true &&
  123. x.IsTopLevel == true &&
  124. x.HasSubMenu == true &&
  125. x.Parent == menu);
  126. var e = CreatePressed(item);
  127. target.PointerPressed(item, e);
  128. Mock.Get(menu).Verify(x => x.Close());
  129. }
  130. [Fact]
  131. public void PointerEnter_Opens_Item_When_Old_Item_Is_Open()
  132. {
  133. var target = new DefaultMenuInteractionHandler(false);
  134. var menu = new Mock<IMenu>();
  135. var item = Mock.Of<IMenuItem>(x =>
  136. x.IsSubMenuOpen == true &&
  137. x.IsTopLevel == true &&
  138. x.HasSubMenu == true &&
  139. x.Parent == menu.Object);
  140. var nextItem = Mock.Of<IMenuItem>(x =>
  141. x.IsTopLevel == true &&
  142. x.HasSubMenu == true &&
  143. x.Parent == menu.Object);
  144. var e = CreateArgs(MenuItem.PointerEnterItemEvent, nextItem);
  145. menu.SetupGet(x => x.SelectedItem).Returns(item);
  146. target.PointerEnter(nextItem, e);
  147. Mock.Get(item).Verify(x => x.Close());
  148. menu.VerifySet(x => x.SelectedItem = nextItem);
  149. Mock.Get(nextItem).Verify(x => x.Open());
  150. Mock.Get(nextItem).Verify(x => x.MoveSelection(NavigationDirection.First, true), Times.Never);
  151. Assert.False(e.Handled);
  152. }
  153. [Fact]
  154. public void PointerLeave_Deselects_Item_When_Menu_Not_Open()
  155. {
  156. var target = new DefaultMenuInteractionHandler(false);
  157. var menu = new Mock<IMenu>();
  158. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
  159. var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  160. menu.SetupGet(x => x.SelectedItem).Returns(item);
  161. target.PointerLeave(item, e);
  162. menu.VerifySet(x => x.SelectedItem = null);
  163. Assert.False(e.Handled);
  164. }
  165. [Fact]
  166. public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open()
  167. {
  168. var target = new DefaultMenuInteractionHandler(false);
  169. var menu = new Mock<IMenu>();
  170. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
  171. var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  172. menu.SetupGet(x => x.IsOpen).Returns(true);
  173. menu.SetupGet(x => x.SelectedItem).Returns(item);
  174. target.PointerLeave(item, e);
  175. menu.VerifySet(x => x.SelectedItem = null, Times.Never);
  176. Assert.False(e.Handled);
  177. }
  178. [Fact]
  179. public void Doesnt_Throw_On_Menu_Keypress()
  180. {
  181. // Issue #3459
  182. var target = new DefaultMenuInteractionHandler(false);
  183. var menu = Mock.Of<IMenu>();
  184. var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu);
  185. var e = new KeyEventArgs { Key = Key.Tab, Source = menu };
  186. target.KeyDown(menu, e);
  187. }
  188. }
  189. public class NonTopLevel
  190. {
  191. [Fact]
  192. public void Up_Selects_Previous_MenuItem()
  193. {
  194. var target = new DefaultMenuInteractionHandler(false);
  195. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  196. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  197. var e = new KeyEventArgs { Key = Key.Up, Source = item };
  198. target.KeyDown(item, e);
  199. Mock.Get(parentItem).Verify(x => x.MoveSelection(NavigationDirection.Up, true));
  200. Assert.True(e.Handled);
  201. }
  202. [Fact]
  203. public void Down_Selects_Next_MenuItem()
  204. {
  205. var target = new DefaultMenuInteractionHandler(false);
  206. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  207. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  208. var e = new KeyEventArgs { Key = Key.Down, Source = item };
  209. target.KeyDown(item, e);
  210. Mock.Get(parentItem).Verify(x => x.MoveSelection(NavigationDirection.Down, true));
  211. Assert.True(e.Handled);
  212. }
  213. [Fact]
  214. public void Left_Closes_Parent_SubMenu()
  215. {
  216. var target = new DefaultMenuInteractionHandler(false);
  217. var parentItem = Mock.Of<IMenuItem>(x => x.HasSubMenu == true && x.IsSubMenuOpen == true);
  218. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  219. var e = new KeyEventArgs { Key = Key.Left, Source = item };
  220. target.KeyDown(item, e);
  221. Mock.Get(parentItem).Verify(x => x.Close());
  222. Mock.Get(parentItem).Verify(x => x.Focus());
  223. Assert.True(e.Handled);
  224. }
  225. [Fact]
  226. public void Right_With_SubMenu_Items_Opens_SubMenu()
  227. {
  228. var target = new DefaultMenuInteractionHandler(false);
  229. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  230. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
  231. var e = new KeyEventArgs { Key = Key.Right, Source = item };
  232. target.KeyDown(item, e);
  233. Mock.Get(item).Verify(x => x.Open());
  234. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  235. Assert.True(e.Handled);
  236. }
  237. [Fact]
  238. public void Right_On_TopLevel_Child_Navigates_TopLevel_Selection()
  239. {
  240. var target = new DefaultMenuInteractionHandler(false);
  241. var menu = new Mock<IMenu>();
  242. var parentItem = Mock.Of<IMenuItem>(x =>
  243. x.IsSubMenuOpen == true &&
  244. x.IsTopLevel == true &&
  245. x.HasSubMenu == true &&
  246. x.Parent == menu.Object);
  247. var nextItem = Mock.Of<IMenuItem>(x =>
  248. x.IsTopLevel == true &&
  249. x.HasSubMenu == true &&
  250. x.Parent == menu.Object);
  251. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  252. var e = new KeyEventArgs { Key = Key.Right, Source = item };
  253. menu.Setup(x => x.MoveSelection(NavigationDirection.Right, true))
  254. .Callback(() => menu.SetupGet(x => x.SelectedItem).Returns(nextItem))
  255. .Returns(true);
  256. target.KeyDown(item, e);
  257. menu.Verify(x => x.MoveSelection(NavigationDirection.Right, true));
  258. Mock.Get(parentItem).Verify(x => x.Close());
  259. Mock.Get(nextItem).Verify(x => x.Open());
  260. Mock.Get(nextItem).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  261. Assert.True(e.Handled);
  262. }
  263. [Fact]
  264. public void Enter_On_Item_With_No_SubMenu_Causes_Click()
  265. {
  266. var target = new DefaultMenuInteractionHandler(false);
  267. var menu = Mock.Of<IMenu>();
  268. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  269. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  270. var e = new KeyEventArgs { Key = Key.Enter, Source = item };
  271. target.KeyDown(item, e);
  272. Mock.Get(item).Verify(x => x.RaiseClick());
  273. Mock.Get(menu).Verify(x => x.Close());
  274. Assert.True(e.Handled);
  275. }
  276. [Fact]
  277. public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
  278. {
  279. var target = new DefaultMenuInteractionHandler(false);
  280. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  281. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
  282. var e = new KeyEventArgs { Key = Key.Enter, Source = item };
  283. target.KeyDown(item, e);
  284. Mock.Get(item).Verify(x => x.Open());
  285. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true));
  286. Assert.True(e.Handled);
  287. }
  288. [Fact]
  289. public void Escape_Closes_Parent_MenuItem()
  290. {
  291. var target = new DefaultMenuInteractionHandler(false);
  292. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true);
  293. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  294. var e = new KeyEventArgs { Key = Key.Escape, Source = item };
  295. target.KeyDown(item, e);
  296. Mock.Get(parentItem).Verify(x => x.Close());
  297. Mock.Get(parentItem).Verify(x => x.Focus());
  298. Assert.True(e.Handled);
  299. }
  300. [Fact]
  301. public void PointerEnter_Selects_Item()
  302. {
  303. var target = new DefaultMenuInteractionHandler(false);
  304. var menu = Mock.Of<IMenu>();
  305. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  306. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  307. var e = CreateArgs(MenuItem.PointerEnterItemEvent, item);
  308. target.PointerEnter(item, e);
  309. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
  310. Assert.False(e.Handled);
  311. }
  312. [Fact]
  313. public void PointerEnter_Opens_Submenu_After_Delay()
  314. {
  315. var timer = new TestTimer();
  316. var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
  317. var menu = Mock.Of<IMenu>();
  318. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  319. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
  320. var e = CreateArgs(MenuItem.PointerEnterItemEvent, item);
  321. target.PointerEnter(item, e);
  322. Mock.Get(item).Verify(x => x.Open(), Times.Never);
  323. timer.Pulse();
  324. Mock.Get(item).Verify(x => x.Open());
  325. Assert.False(e.Handled);
  326. }
  327. [Fact]
  328. public void PointerEnter_Closes_Sibling_Submenu_After_Delay()
  329. {
  330. var timer = new TestTimer();
  331. var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
  332. var menu = Mock.Of<IMenu>();
  333. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  334. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  335. var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsSubMenuOpen == true);
  336. var e = CreateArgs(MenuItem.PointerEnterItemEvent, item);
  337. Mock.Get(parentItem).SetupGet(x => x.SubItems).Returns(new[] { item, sibling });
  338. target.PointerEnter(item, e);
  339. Mock.Get(sibling).Verify(x => x.Close(), Times.Never);
  340. timer.Pulse();
  341. Mock.Get(sibling).Verify(x => x.Close());
  342. Assert.False(e.Handled);
  343. }
  344. [Fact]
  345. public void PointerLeave_Deselects_Item()
  346. {
  347. var target = new DefaultMenuInteractionHandler(false);
  348. var menu = Mock.Of<IMenu>();
  349. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  350. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  351. var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  352. Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(item);
  353. target.PointerLeave(item, e);
  354. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null);
  355. Assert.False(e.Handled);
  356. }
  357. [Fact]
  358. public void PointerLeave_Doesnt_Deselect_Sibling()
  359. {
  360. var target = new DefaultMenuInteractionHandler(false);
  361. var menu = Mock.Of<IMenu>();
  362. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  363. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  364. var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  365. var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  366. Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(sibling);
  367. target.PointerLeave(item, e);
  368. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never);
  369. Assert.False(e.Handled);
  370. }
  371. [Fact]
  372. public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu()
  373. {
  374. var target = new DefaultMenuInteractionHandler(false);
  375. var menu = Mock.Of<IMenu>();
  376. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  377. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true);
  378. var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  379. target.PointerLeave(item, e);
  380. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never);
  381. Assert.False(e.Handled);
  382. }
  383. [Fact]
  384. public void PointerReleased_On_Item_With_No_SubMenu_Causes_Click()
  385. {
  386. var target = new DefaultMenuInteractionHandler(false);
  387. var menu = Mock.Of<IMenu>();
  388. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  389. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
  390. var e = CreateReleased(item);
  391. target.PointerReleased(item, e);
  392. Mock.Get(item).Verify(x => x.RaiseClick());
  393. Mock.Get(menu).Verify(x => x.Close());
  394. Assert.True(e.Handled);
  395. }
  396. [Fact]
  397. public void Selection_Is_Correct_When_Pointer_Temporarily_Exits_Item_To_Select_SubItem()
  398. {
  399. var timer = new TestTimer();
  400. var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
  401. var menu = Mock.Of<IMenu>();
  402. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  403. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
  404. var childItem = Mock.Of<IMenuItem>(x => x.Parent == item);
  405. var enter = CreateArgs(MenuItem.PointerEnterItemEvent, item);
  406. var leave = CreateArgs(MenuItem.PointerLeaveItemEvent, item);
  407. // Pointer enters item; item is selected.
  408. target.PointerEnter(item, enter);
  409. Assert.True(timer.ActionIsQueued);
  410. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
  411. Mock.Get(parentItem).Invocations.Clear();
  412. // SubMenu shown after a delay.
  413. timer.Pulse();
  414. Mock.Get(item).Verify(x => x.Open());
  415. Mock.Get(item).SetupGet(x => x.IsSubMenuOpen).Returns(true);
  416. Mock.Get(item).Invocations.Clear();
  417. // Pointer briefly exits item, but submenu remains open.
  418. target.PointerLeave(item, leave);
  419. Mock.Get(item).Verify(x => x.Close(), Times.Never);
  420. Mock.Get(item).Invocations.Clear();
  421. // Pointer enters child item; is selected.
  422. enter.Source = childItem;
  423. target.PointerEnter(childItem, enter);
  424. Mock.Get(item).VerifySet(x => x.SelectedItem = childItem);
  425. Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
  426. Mock.Get(item).Invocations.Clear();
  427. Mock.Get(parentItem).Invocations.Clear();
  428. }
  429. [Fact]
  430. public void PointerPressed_On_Item_With_SubMenu_Causes_Opens_Submenu()
  431. {
  432. var target = new DefaultMenuInteractionHandler(false);
  433. var menu = Mock.Of<IMenu>();
  434. var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
  435. var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
  436. var e = CreatePressed(item);
  437. target.PointerPressed(item, e);
  438. Mock.Get(item).Verify(x => x.Open());
  439. Mock.Get(item).Verify(x => x.MoveSelection(NavigationDirection.First, true), Times.Never);
  440. Assert.True(e.Handled);
  441. }
  442. }
  443. public class ContextMenu
  444. {
  445. [Fact]
  446. public void Down_Selects_Selects_First_MenuItem_When_No_Selection()
  447. {
  448. var target = new DefaultMenuInteractionHandler(true);
  449. var contextMenu = Mock.Of<IMenu>(x => x.MoveSelection(NavigationDirection.Down, true) == true);
  450. var e = new KeyEventArgs { Key = Key.Down, Source = contextMenu };
  451. target.Attach(contextMenu);
  452. target.KeyDown(contextMenu, e);
  453. Mock.Get(contextMenu).Verify(x => x.MoveSelection(NavigationDirection.Down, true));
  454. Assert.True(e.Handled);
  455. }
  456. }
  457. private class TestTimer
  458. {
  459. private Action _action;
  460. public bool ActionIsQueued => _action != null;
  461. public void Pulse()
  462. {
  463. _action();
  464. _action = null;
  465. }
  466. public void RunOnce(Action action, TimeSpan timeSpan)
  467. {
  468. if (_action != null)
  469. {
  470. throw new NotSupportedException("Action already set.");
  471. }
  472. _action = action;
  473. }
  474. }
  475. class FakePointer : IPointer
  476. {
  477. public int Id { get; } = Pointer.GetNextFreeId();
  478. public void Capture(IInputElement control)
  479. {
  480. Captured = control;
  481. }
  482. public IInputElement Captured { get; set; }
  483. public PointerType Type { get; }
  484. public bool IsPrimary { get; } = true;
  485. }
  486. }
  487. }