HotKeyManagerTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using System;
  2. using Avalonia.Controls.Presenters;
  3. using Avalonia.Controls.Templates;
  4. using Avalonia.Input;
  5. using Avalonia.Input.Raw;
  6. using Avalonia.Platform;
  7. using Avalonia.UnitTests;
  8. using Xunit;
  9. using Factory = System.Func<int, System.Action<object>, Avalonia.Controls.Window, Avalonia.AvaloniaObject>;
  10. namespace Avalonia.Controls.UnitTests.Utils
  11. {
  12. public class HotKeyManagerTests
  13. {
  14. [Fact]
  15. public void HotKeyManager_Should_Register_And_Unregister_Key_Binding()
  16. {
  17. using (AvaloniaLocator.EnterScope())
  18. {
  19. AvaloniaLocator.CurrentMutable
  20. .Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
  21. var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control);
  22. var gesture2 = new KeyGesture(Key.B, KeyModifiers.Control);
  23. var tl = new Window();
  24. var button = new Button();
  25. tl.Content = button;
  26. tl.Template = CreateWindowTemplate();
  27. tl.ApplyTemplate();
  28. tl.Presenter.ApplyTemplate();
  29. HotKeyManager.SetHotKey(button, gesture1);
  30. Assert.Equal(gesture1, tl.KeyBindings[0].Gesture);
  31. HotKeyManager.SetHotKey(button, gesture2);
  32. Assert.Equal(gesture2, tl.KeyBindings[0].Gesture);
  33. tl.Content = null;
  34. tl.Presenter.ApplyTemplate();
  35. Assert.Empty(tl.KeyBindings);
  36. tl.Content = button;
  37. tl.Presenter.ApplyTemplate();
  38. Assert.Equal(gesture2, tl.KeyBindings[0].Gesture);
  39. HotKeyManager.SetHotKey(button, null);
  40. Assert.Empty(tl.KeyBindings);
  41. }
  42. }
  43. [Theory]
  44. [MemberData(nameof(ElementsFactory), parameters: true)]
  45. public void HotKeyManager_Should_Use_CommandParameter(string factoryName, Factory factory)
  46. {
  47. using (AvaloniaLocator.EnterScope())
  48. {
  49. var target = new KeyboardDevice();
  50. var commandResult = 0;
  51. var expectedParameter = 1;
  52. AvaloniaLocator.CurrentMutable
  53. .Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
  54. var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
  55. var action = new Action<object>(parameter =>
  56. {
  57. if (parameter is int value)
  58. {
  59. commandResult = value;
  60. }
  61. });
  62. var root = new Window();
  63. var element = factory(expectedParameter, action, root);
  64. root.Template = CreateWindowTemplate();
  65. root.ApplyTemplate();
  66. root.Presenter.ApplyTemplate();
  67. HotKeyManager.SetHotKey(element, gesture);
  68. target.ProcessRawEvent(new RawKeyEventArgs(target,
  69. 0,
  70. root,
  71. RawKeyEventType.KeyDown,
  72. Key.A,
  73. RawInputModifiers.Control));
  74. Assert.True(expectedParameter == commandResult, $"{factoryName} HotKey did not carry the CommandParameter.");
  75. }
  76. }
  77. [Theory]
  78. [MemberData(nameof(ElementsFactory), parameters: true)]
  79. public void HotKeyManager_Should_Do_Not_Executed_When_IsEnabled_False(string factoryName, Factory factory)
  80. {
  81. using (AvaloniaLocator.EnterScope())
  82. {
  83. var target = new KeyboardDevice();
  84. var isExecuted = false;
  85. AvaloniaLocator.CurrentMutable
  86. .Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
  87. var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
  88. var action = new Action<object>(parameter =>
  89. {
  90. isExecuted = true;
  91. });
  92. var root = new Window();
  93. var element = factory(0, action, root) as InputElement;
  94. element.IsEnabled = false;
  95. root.Template = CreateWindowTemplate();
  96. root.ApplyTemplate();
  97. root.Presenter.ApplyTemplate();
  98. HotKeyManager.SetHotKey(element, gesture);
  99. target.ProcessRawEvent(new RawKeyEventArgs(target,
  100. 0,
  101. root,
  102. RawKeyEventType.KeyDown,
  103. Key.A,
  104. RawInputModifiers.Control));
  105. Assert.True(isExecuted == false, $"{factoryName} Execution raised when IsEnabled is false.");
  106. }
  107. }
  108. [Theory]
  109. [MemberData(nameof(ElementsFactory), parameters:false)]
  110. public void HotKeyManager_Should_Invoke_Event_Click_When_Command_Is_Null(string factoryName, Factory factory)
  111. {
  112. using (AvaloniaLocator.EnterScope())
  113. {
  114. var target = new KeyboardDevice();
  115. var clickExecutedCount = 0;
  116. AvaloniaLocator.CurrentMutable
  117. .Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
  118. var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
  119. void Clickable_Click(object sender, Interactivity.RoutedEventArgs e)
  120. {
  121. clickExecutedCount++;
  122. }
  123. var root = new Window();
  124. var element = factory(0, default, root) as InputElement;
  125. if (element is IClickableControl clickable)
  126. {
  127. clickable.Click += Clickable_Click;
  128. }
  129. root.Template = CreateWindowTemplate();
  130. root.ApplyTemplate();
  131. root.Presenter.ApplyTemplate();
  132. HotKeyManager.SetHotKey(element, gesture);
  133. target.ProcessRawEvent(new RawKeyEventArgs(target,
  134. 0,
  135. root,
  136. RawKeyEventType.KeyDown,
  137. Key.A,
  138. RawInputModifiers.Control));
  139. element.IsEnabled = false;
  140. target.ProcessRawEvent(new RawKeyEventArgs(target,
  141. 0,
  142. root,
  143. RawKeyEventType.KeyDown,
  144. Key.A,
  145. RawInputModifiers.Control));
  146. Assert.True(clickExecutedCount == 1, $"{factoryName} Execution raised when IsEnabled is false.");
  147. }
  148. }
  149. [Theory]
  150. [MemberData(nameof(ElementsFactory), parameters: true)]
  151. public void HotKeyManager_Should_Not_Invoke_Event_Click_When_Command_Is_Not_Null(string factoryName, Factory factory)
  152. {
  153. using (AvaloniaLocator.EnterScope())
  154. {
  155. var target = new KeyboardDevice();
  156. var clickExecutedCount = 0;
  157. var commandExecutedCount = 0;
  158. AvaloniaLocator.CurrentMutable
  159. .Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
  160. var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
  161. void DoExecute(object parameter)
  162. {
  163. commandExecutedCount++;
  164. }
  165. void Clickable_Click(object sender, Interactivity.RoutedEventArgs e)
  166. {
  167. clickExecutedCount++;
  168. }
  169. var root = new Window();
  170. var element = factory(0, DoExecute, root) as InputElement;
  171. if (element is IClickableControl clickable)
  172. {
  173. clickable.Click += Clickable_Click;
  174. }
  175. root.Template = CreateWindowTemplate();
  176. root.ApplyTemplate();
  177. root.Presenter.ApplyTemplate();
  178. HotKeyManager.SetHotKey(element, gesture);
  179. target.ProcessRawEvent(new RawKeyEventArgs(target,
  180. 0,
  181. root,
  182. RawKeyEventType.KeyDown,
  183. Key.A,
  184. RawInputModifiers.Control));
  185. element.IsEnabled = false;
  186. target.ProcessRawEvent(new RawKeyEventArgs(target,
  187. 0,
  188. root,
  189. RawKeyEventType.KeyDown,
  190. Key.A,
  191. RawInputModifiers.Control));
  192. Assert.True(commandExecutedCount == 1, $"{factoryName} Execution raised when IsEnabled is false.");
  193. Assert.True(clickExecutedCount == 0, $"{factoryName} Execution raised event Click.");
  194. }
  195. }
  196. public static TheoryData<string, Factory> ElementsFactory(bool withCommand) =>
  197. new TheoryData<string, Factory>()
  198. {
  199. {nameof(Button), withCommand ? MakeButton : MakeButtonWithoutCommand},
  200. {nameof(MenuItem),withCommand ? MakeMenu : MakeMenuWithoutCommand},
  201. };
  202. private static AvaloniaObject MakeMenu(int expectedParameter, Action<object> action, Window root)
  203. {
  204. var menuitem = new MenuItem()
  205. {
  206. Command = new Command(action),
  207. CommandParameter = expectedParameter,
  208. };
  209. var rootMenu = new Menu();
  210. rootMenu.Items.Add(menuitem);
  211. root.Content = rootMenu;
  212. return menuitem;
  213. }
  214. private static AvaloniaObject MakeButton(int expectedParameter, Action<object> action, Window root)
  215. {
  216. var button = new Button()
  217. {
  218. Command = new Command(action),
  219. CommandParameter = expectedParameter,
  220. };
  221. root.Content = button;
  222. return button;
  223. }
  224. private static AvaloniaObject MakeMenuWithoutCommand(int expectedParameter, Action<object> action, Window root)
  225. {
  226. var menuitem = new MenuItem()
  227. {
  228. };
  229. var rootMenu = new Menu();
  230. rootMenu.Items.Add(menuitem);
  231. root.Content = rootMenu;
  232. return menuitem;
  233. }
  234. private static AvaloniaObject MakeButtonWithoutCommand(int expectedParameter, Action<object> action, Window root)
  235. {
  236. var button = new Button()
  237. {
  238. };
  239. root.Content = button;
  240. return button;
  241. }
  242. private static FuncControlTemplate CreateWindowTemplate()
  243. {
  244. return new FuncControlTemplate<Window>((parent, scope) =>
  245. {
  246. return new ContentPresenter
  247. {
  248. Name = "PART_ContentPresenter",
  249. [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty],
  250. }.RegisterInNameScope(scope);
  251. });
  252. }
  253. class Command : System.Windows.Input.ICommand
  254. {
  255. private readonly Action<object> _execeute;
  256. #pragma warning disable 67 // Event not used
  257. public event EventHandler CanExecuteChanged;
  258. #pragma warning restore 67 // Event not used
  259. public Command(Action<object> execeute)
  260. {
  261. _execeute = execeute;
  262. }
  263. public bool CanExecute(object parameter) => true;
  264. public void Execute(object parameter) => _execeute?.Invoke(parameter);
  265. }
  266. }
  267. }