DefaultMenuInteractionHandlerTests.cs 26 KB

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