DefaultMenuInteractionHandlerTests.cs 26 KB

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