GesturesTests.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Base.UnitTests.Utilities;
  5. using Avalonia.Controls;
  6. using Avalonia.Input;
  7. using Avalonia.Input.GestureRecognizers;
  8. using Avalonia.Media;
  9. using Avalonia.Platform;
  10. using Avalonia.Threading;
  11. using Avalonia.UnitTests;
  12. using Avalonia.Utilities;
  13. using Moq;
  14. using Xunit;
  15. // ReSharper disable RedundantArgumentDefaultValue
  16. namespace Avalonia.Base.UnitTests.Input
  17. {
  18. public class GesturesTests
  19. {
  20. private readonly MouseTestHelper _mouse = new MouseTestHelper();
  21. [Fact]
  22. public void Tapped_Should_Follow_Pointer_Pressed_Released()
  23. {
  24. Border border = new Border();
  25. var root = new TestRoot
  26. {
  27. Child = border
  28. };
  29. var result = new List<string>();
  30. AddHandlers(root, border, result, false);
  31. _mouse.Click(border);
  32. Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt" }, result);
  33. }
  34. [Fact]
  35. public void Tapped_Should_Be_Raised_Even_When_Pressed_Released_Handled()
  36. {
  37. Border border = new Border();
  38. var root = new TestRoot
  39. {
  40. Child = border
  41. };
  42. var result = new List<string>();
  43. AddHandlers(root, border, result, true);
  44. _mouse.Click(border);
  45. Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt" }, result);
  46. }
  47. [Fact]
  48. public void Tapped_Should_Not_Be_Raised_For_Middle_Button()
  49. {
  50. Border border = new Border();
  51. var root = new TestRoot
  52. {
  53. Child = border
  54. };
  55. var raised = false;
  56. root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true);
  57. _mouse.Click(border, MouseButton.Middle);
  58. Assert.False(raised);
  59. }
  60. [Fact]
  61. public void Tapped_Should_Not_Be_Raised_For_Right_Button()
  62. {
  63. Border border = new Border();
  64. var root = new TestRoot
  65. {
  66. Child = border
  67. };
  68. var raised = false;
  69. root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true);
  70. _mouse.Click(border, MouseButton.Right);
  71. Assert.False(raised);
  72. }
  73. [Fact]
  74. public void Tapped_Should_Be_Raised_From_Captured_Control()
  75. {
  76. Border inner = new Border()
  77. {
  78. Focusable = true,
  79. Name = "Inner"
  80. };
  81. Border border = new Border()
  82. {
  83. Focusable = true,
  84. Child = inner,
  85. Name = "Parent"
  86. };
  87. var root = new TestRoot
  88. {
  89. Child = border
  90. };
  91. var raised = false;
  92. border.PointerPressed += (s, e) =>
  93. {
  94. e.Pointer.Capture(inner);
  95. };
  96. _mouse.Click(border, MouseButton.Left);
  97. root.AddHandler(Gestures.TappedEvent, (_, _) => raised = true);
  98. _mouse.Click(border, MouseButton.Left);
  99. Assert.True(raised);
  100. }
  101. [Fact]
  102. public void RightTapped_Should_Be_Raised_For_Right_Button()
  103. {
  104. Border border = new Border();
  105. var root = new TestRoot
  106. {
  107. Child = border
  108. };
  109. var raised = false;
  110. root.AddHandler(Gestures.RightTappedEvent, (_, _) => raised = true);
  111. _mouse.Click(border, MouseButton.Right);
  112. Assert.True(raised);
  113. }
  114. [Fact]
  115. public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed()
  116. {
  117. Border border = new Border();
  118. var root = new TestRoot
  119. {
  120. Child = border
  121. };
  122. var result = new List<string>();
  123. AddHandlers(root, border, result, false);
  124. _mouse.Click(border);
  125. _mouse.Down(border, clickCount: 2);
  126. Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp", "bdt", "ddt" }, result);
  127. }
  128. [Fact]
  129. public void DoubleTapped_Should_Be_Raised_Even_When_Pressed_Released_Handled()
  130. {
  131. Border border = new Border();
  132. var root = new TestRoot
  133. {
  134. Child = border
  135. };
  136. var result = new List<string>();
  137. AddHandlers(root, border, result, true);
  138. _mouse.Click(border);
  139. _mouse.Down(border, clickCount: 2);
  140. Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp", "bdt", "ddt" }, result);
  141. }
  142. [Fact]
  143. public void DoubleTapped_Should_Not_Be_Raised_For_Middle_Button()
  144. {
  145. Border border = new Border();
  146. var root = new TestRoot
  147. {
  148. Child = border
  149. };
  150. var raised = false;
  151. root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true);
  152. _mouse.Click(border, MouseButton.Middle);
  153. _mouse.Down(border, MouseButton.Middle, clickCount: 2);
  154. Assert.False(raised);
  155. }
  156. [Fact]
  157. public void DoubleTapped_Should_Not_Be_Raised_For_Right_Button()
  158. {
  159. Border border = new Border();
  160. var root = new TestRoot
  161. {
  162. Child = border
  163. };
  164. var raised = false;
  165. root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => raised = true);
  166. _mouse.Click(border, MouseButton.Right);
  167. _mouse.Down(border, MouseButton.Right, clickCount: 2);
  168. Assert.False(raised);
  169. }
  170. [Fact]
  171. public void Hold_Should_Be_Raised_After_Hold_Duration()
  172. {
  173. using var scope = AvaloniaLocator.EnterScope();
  174. var iSettingsMock = new Mock<IPlatformSettings>();
  175. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  176. iSettingsMock.Setup(x => x.GetTapSize(It.IsAny<PointerType>())).Returns(new Size(16, 16));
  177. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  178. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  179. using var app = UnitTestApplication.Start();
  180. Border border = new Border();
  181. Gestures.SetIsHoldWithMouseEnabled(border, true);
  182. var root = new TestRoot
  183. {
  184. Child = border
  185. };
  186. HoldingState holding = HoldingState.Cancelled;
  187. root.AddHandler(Gestures.HoldingEvent, (_, e) => holding = e.HoldingState);
  188. _mouse.Down(border);
  189. Assert.False(holding != HoldingState.Cancelled);
  190. // Verify timer duration, but execute it immediately.
  191. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  192. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  193. timer.ForceFire();
  194. Assert.True(holding == HoldingState.Started);
  195. _mouse.Up(border);
  196. Assert.True(holding == HoldingState.Completed);
  197. }
  198. [Fact]
  199. public void Hold_Should_Not_Raised_When_Pointer_Released_Before_Timer()
  200. {
  201. using var scope = AvaloniaLocator.EnterScope();
  202. var iSettingsMock = new Mock<IPlatformSettings>();
  203. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  204. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  205. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  206. using var app = UnitTestApplication.Start();
  207. Border border = new Border();
  208. Gestures.SetIsHoldWithMouseEnabled(border, true);
  209. var root = new TestRoot
  210. {
  211. Child = border
  212. };
  213. var raised = false;
  214. root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Started);
  215. _mouse.Down(border);
  216. Assert.False(raised);
  217. _mouse.Up(border);
  218. Assert.False(raised);
  219. // Verify timer duration, but execute it immediately.
  220. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  221. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  222. timer.ForceFire();
  223. Assert.False(raised);
  224. }
  225. [Fact]
  226. public void Hold_Should_Not_Raised_When_Pointer_Is_Moved_Before_Timer()
  227. {
  228. using var scope = AvaloniaLocator.EnterScope();
  229. var iSettingsMock = new Mock<IPlatformSettings>();
  230. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  231. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  232. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  233. using var app = UnitTestApplication.Start();
  234. Border border = new Border();
  235. Gestures.SetIsHoldWithMouseEnabled(border, true);
  236. var root = new TestRoot
  237. {
  238. Child = border
  239. };
  240. var raised = false;
  241. root.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed);
  242. _mouse.Down(border);
  243. Assert.False(raised);
  244. _mouse.Move(border, position: new Point(20, 20));
  245. Assert.False(raised);
  246. // Verify timer duration, but execute it immediately.
  247. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  248. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  249. timer.ForceFire();
  250. Assert.False(raised);
  251. }
  252. [Fact]
  253. public void Hold_Should_Be_Cancelled_When_Second_Contact_Is_Detected()
  254. {
  255. using var scope = AvaloniaLocator.EnterScope();
  256. var iSettingsMock = new Mock<IPlatformSettings>();
  257. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  258. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  259. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  260. using var app = UnitTestApplication.Start();
  261. Border border = new Border();
  262. Gestures.SetIsHoldWithMouseEnabled(border, true);
  263. var root = new TestRoot
  264. {
  265. Child = border
  266. };
  267. var cancelled = false;
  268. root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled);
  269. _mouse.Down(border);
  270. Assert.False(cancelled);
  271. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  272. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  273. timer.ForceFire();
  274. var secondMouse = new MouseTestHelper();
  275. secondMouse.Down(border);
  276. Assert.True(cancelled);
  277. }
  278. [Fact]
  279. public void Hold_Should_Be_Cancelled_When_Pointer_Moves_Too_Far()
  280. {
  281. using var scope = AvaloniaLocator.EnterScope();
  282. var iSettingsMock = new Mock<IPlatformSettings>();
  283. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  284. iSettingsMock.Setup(x => x.GetTapSize(It.IsAny<PointerType>())).Returns(new Size(16, 16));
  285. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  286. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  287. using var app = UnitTestApplication.Start();
  288. Border border = new Border();
  289. Gestures.SetIsHoldWithMouseEnabled(border, true);
  290. var root = new TestRoot()
  291. {
  292. Child = border
  293. };
  294. var cancelled = false;
  295. root.AddHandler(Gestures.HoldingEvent, (_, e) => cancelled = e.HoldingState == HoldingState.Cancelled);
  296. _mouse.Down(border);
  297. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  298. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  299. timer.ForceFire();
  300. _mouse.Move(border, position: new Point(3, 3));
  301. Assert.False(cancelled);
  302. _mouse.Move(border, position: new Point(20, 20));
  303. Assert.True(cancelled);
  304. }
  305. [Fact]
  306. public void Hold_Should_Not_Be_Raised_For_Multiple_Contacts()
  307. {
  308. using var scope = AvaloniaLocator.EnterScope();
  309. var iSettingsMock = new Mock<IPlatformSettings>();
  310. iSettingsMock.Setup(x => x.HoldWaitDuration).Returns(TimeSpan.FromMilliseconds(300));
  311. AvaloniaLocator.CurrentMutable.BindToSelf(this)
  312. .Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
  313. using var app = UnitTestApplication.Start();
  314. Border border = new Border();
  315. Gestures.SetIsHoldWithMouseEnabled(border, true);
  316. var testRoot = new TestRoot
  317. {
  318. Child = border
  319. };
  320. var raised = false;
  321. testRoot.AddHandler(Gestures.HoldingEvent, (_, e) => raised = e.HoldingState == HoldingState.Completed);
  322. var secondMouse = new MouseTestHelper();
  323. _mouse.Down(border, MouseButton.Left);
  324. // Verify timer duration, but execute it immediately.
  325. var timer = Assert.Single(Dispatcher.SnapshotTimersForUnitTests());
  326. Assert.Equal(iSettingsMock.Object.HoldWaitDuration, timer.Interval);
  327. timer.ForceFire();
  328. secondMouse.Down(border, MouseButton.Left);
  329. Assert.False(raised);
  330. }
  331. private static void AddHandlers(
  332. TestRoot root,
  333. Border border,
  334. IList<string> result,
  335. bool markHandled)
  336. {
  337. root.AddHandler(InputElement.PointerPressedEvent, (_, e) =>
  338. {
  339. result.Add("dp");
  340. if (markHandled)
  341. {
  342. e.Handled = true;
  343. }
  344. });
  345. root.AddHandler(InputElement.PointerReleasedEvent, (_, e) =>
  346. {
  347. result.Add("dr");
  348. if (markHandled)
  349. {
  350. e.Handled = true;
  351. }
  352. });
  353. border.AddHandler(InputElement.PointerPressedEvent, (_, _) => result.Add("bp"));
  354. border.AddHandler(InputElement.PointerReleasedEvent, (_, _) => result.Add("br"));
  355. root.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("dt"));
  356. root.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("ddt"));
  357. border.AddHandler(Gestures.TappedEvent, (_, _) => result.Add("bt"));
  358. border.AddHandler(Gestures.DoubleTappedEvent, (_, _) => result.Add("bdt"));
  359. }
  360. [Fact]
  361. public void Pinched_Should_Not_Be_Raised_For_Same_Pointer()
  362. {
  363. var touch = new TouchTestHelper();
  364. Border border = new Border()
  365. {
  366. Width = 100,
  367. Height = 100,
  368. Background = new SolidColorBrush(Colors.Red)
  369. };
  370. border.GestureRecognizers.Add(new PinchGestureRecognizer());
  371. var root = new TestRoot
  372. {
  373. Child = border
  374. };
  375. var raised = false;
  376. root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true);
  377. var firstPoint = new Point(5, 5);
  378. var secondPoint = new Point(10, 10);
  379. touch.Down(border, position: firstPoint);
  380. touch.Down(border, position: secondPoint);
  381. touch.Down(border, position: new Point(20, 20));
  382. Assert.False(raised);
  383. }
  384. [Fact]
  385. public void Pinched_Should_Be_Raised_For_Two_Pointers_Moving()
  386. {
  387. Border border = new Border()
  388. {
  389. Width = 100,
  390. Height = 100,
  391. Background = new SolidColorBrush(Colors.Red)
  392. };
  393. border.GestureRecognizers.Add(new PinchGestureRecognizer());
  394. var root = new TestRoot
  395. {
  396. Child = border
  397. };
  398. var raised = false;
  399. root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true);
  400. var firstPoint = new Point(5, 5);
  401. var secondPoint = new Point(10, 10);
  402. var firstTouch = new TouchTestHelper();
  403. var secondTouch = new TouchTestHelper();
  404. firstTouch.Down(border, position: firstPoint);
  405. secondTouch.Down(border, position: secondPoint);
  406. secondTouch.Move(border, position: new Point(20, 20));
  407. Assert.True(raised);
  408. }
  409. [Fact]
  410. public void Gestures_Should_Be_Cancelled_When_Pointer_Capture_Is_Lost()
  411. {
  412. Border border = new Border()
  413. {
  414. Width = 100,
  415. Height = 100,
  416. Background = new SolidColorBrush(Colors.Red)
  417. };
  418. border.GestureRecognizers.Add(new PinchGestureRecognizer());
  419. var root = new TestRoot
  420. {
  421. Child = border
  422. };
  423. var raised = false;
  424. root.AddHandler(Gestures.PinchEvent, (_, _) => raised = true);
  425. var firstPoint = new Point(5, 5);
  426. var secondPoint = new Point(10, 10);
  427. var firstTouch = new TouchTestHelper();
  428. var secondTouch = new TouchTestHelper();
  429. firstTouch.Down(border, position: firstPoint);
  430. firstTouch.Cancel();
  431. secondTouch.Down(border, position: secondPoint);
  432. secondTouch.Move(border, position: new Point(20, 20));
  433. Assert.False(raised);
  434. }
  435. [Fact]
  436. public void Scrolling_Should_Start_After_Start_Distance_Is_Exceeded()
  437. {
  438. Border border = new Border()
  439. {
  440. Width = 100,
  441. Height = 100,
  442. Background = new SolidColorBrush(Colors.Red)
  443. };
  444. border.GestureRecognizers.Add(new ScrollGestureRecognizer()
  445. {
  446. CanHorizontallyScroll = true,
  447. CanVerticallyScroll = true,
  448. ScrollStartDistance = 50
  449. });
  450. var root = new TestRoot
  451. {
  452. Child = border
  453. };
  454. var raised = false;
  455. root.AddHandler(Gestures.ScrollGestureEvent, (_, _) => raised = true);
  456. var firstTouch = new TouchTestHelper();
  457. firstTouch.Down(border, position: new Point(5, 5));
  458. firstTouch.Move(border, position: new Point(20, 20));
  459. Assert.False(raised);
  460. firstTouch.Move(border, position: new Point(70, 20));
  461. Assert.True(raised);
  462. }
  463. }
  464. }