InputElement.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Perspex.Interactivity;
  7. using Perspex.Rendering;
  8. using Perspex.VisualTree;
  9. namespace Perspex.Input
  10. {
  11. /// <summary>
  12. /// Implements input-related functionality for a control.
  13. /// </summary>
  14. public class InputElement : Interactive, IInputElement
  15. {
  16. /// <summary>
  17. /// Defines the <see cref="Focusable"/> property.
  18. /// </summary>
  19. public static readonly StyledProperty<bool> FocusableProperty =
  20. PerspexProperty.Register<InputElement, bool>(nameof(Focusable));
  21. /// <summary>
  22. /// Defines the <see cref="IsEnabled"/> property.
  23. /// </summary>
  24. public static readonly StyledProperty<bool> IsEnabledProperty =
  25. PerspexProperty.Register<InputElement, bool>(nameof(IsEnabled), true);
  26. /// <summary>
  27. /// Defines the <see cref="IsEnabledCore"/> property.
  28. /// </summary>
  29. public static readonly StyledProperty<bool> IsEnabledCoreProperty =
  30. PerspexProperty.Register<InputElement, bool>("IsEnabledCore", true);
  31. /// <summary>
  32. /// Gets or sets associated mouse cursor.
  33. /// </summary>
  34. public static readonly StyledProperty<Cursor> CursorProperty =
  35. PerspexProperty.Register<InputElement, Cursor>("Cursor", null, true);
  36. /// <summary>
  37. /// Defines the <see cref="IsFocused"/> property.
  38. /// </summary>
  39. public static readonly DirectProperty<InputElement, bool> IsFocusedProperty =
  40. PerspexProperty.RegisterDirect<InputElement, bool>("IsFocused", o => o.IsFocused);
  41. /// <summary>
  42. /// Defines the <see cref="IsHitTestVisible"/> property.
  43. /// </summary>
  44. public static readonly StyledProperty<bool> IsHitTestVisibleProperty =
  45. PerspexProperty.Register<InputElement, bool>("IsHitTestVisible", true);
  46. /// <summary>
  47. /// Defines the <see cref="IsPointerOver"/> property.
  48. /// </summary>
  49. public static readonly DirectProperty<InputElement, bool> IsPointerOverProperty =
  50. PerspexProperty.RegisterDirect<InputElement, bool>("IsPointerOver", o => o.IsPointerOver);
  51. /// <summary>
  52. /// Defines the <see cref="GotFocus"/> event.
  53. /// </summary>
  54. public static readonly RoutedEvent<GotFocusEventArgs> GotFocusEvent =
  55. RoutedEvent.Register<InputElement, GotFocusEventArgs>("GotFocus", RoutingStrategies.Bubble);
  56. /// <summary>
  57. /// Defines the <see cref="LostFocus"/> event.
  58. /// </summary>
  59. public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent =
  60. RoutedEvent.Register<InputElement, RoutedEventArgs>("LostFocus", RoutingStrategies.Bubble);
  61. /// <summary>
  62. /// Defines the <see cref="KeyDown"/> event.
  63. /// </summary>
  64. public static readonly RoutedEvent<KeyEventArgs> KeyDownEvent =
  65. RoutedEvent.Register<InputElement, KeyEventArgs>(
  66. "KeyDown",
  67. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  68. /// <summary>
  69. /// Defines the <see cref="KeyUp"/> event.
  70. /// </summary>
  71. public static readonly RoutedEvent<KeyEventArgs> KeyUpEvent =
  72. RoutedEvent.Register<InputElement, KeyEventArgs>(
  73. "KeyUp",
  74. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  75. /// <summary>
  76. /// Defines the <see cref="TextInput"/> event.
  77. /// </summary>
  78. public static readonly RoutedEvent<TextInputEventArgs> TextInputEvent =
  79. RoutedEvent.Register<InputElement, TextInputEventArgs>(
  80. "TextInput",
  81. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  82. /// <summary>
  83. /// Defines the <see cref="PointerEnter"/> event.
  84. /// </summary>
  85. public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
  86. RoutedEvent.Register<InputElement, PointerEventArgs>("PointerEnter", RoutingStrategies.Direct);
  87. /// <summary>
  88. /// Defines the <see cref="PointerLeave"/> event.
  89. /// </summary>
  90. public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
  91. RoutedEvent.Register<InputElement, PointerEventArgs>("PointerLeave", RoutingStrategies.Direct);
  92. /// <summary>
  93. /// Defines the <see cref="PointerMoved"/> event.
  94. /// </summary>
  95. public static readonly RoutedEvent<PointerEventArgs> PointerMovedEvent =
  96. RoutedEvent.Register<InputElement, PointerEventArgs>(
  97. "PointerMove",
  98. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  99. /// <summary>
  100. /// Defines the <see cref="PointerPressed"/> event.
  101. /// </summary>
  102. public static readonly RoutedEvent<PointerPressEventArgs> PointerPressedEvent =
  103. RoutedEvent.Register<InputElement, PointerPressEventArgs>(
  104. "PointerPressed",
  105. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  106. /// <summary>
  107. /// Defines the <see cref="PointerReleased"/> event.
  108. /// </summary>
  109. public static readonly RoutedEvent<PointerReleasedEventArgs> PointerReleasedEvent =
  110. RoutedEvent.Register<InputElement, PointerReleasedEventArgs>(
  111. "PointerReleased",
  112. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  113. /// <summary>
  114. /// Defines the <see cref="PointerWheelChanged"/> event.
  115. /// </summary>
  116. public static readonly RoutedEvent<PointerWheelEventArgs> PointerWheelChangedEvent =
  117. RoutedEvent.Register<InputElement, PointerWheelEventArgs>(
  118. "PointerWheelChanged",
  119. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  120. private bool _isFocused;
  121. private bool _isPointerOver;
  122. /// <summary>
  123. /// Initializes static members of the <see cref="InputElement"/> class.
  124. /// </summary>
  125. static InputElement()
  126. {
  127. IsEnabledProperty.Changed.Subscribe(IsEnabledChanged);
  128. GotFocusEvent.AddClassHandler<InputElement>(x => x.OnGotFocus);
  129. LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus);
  130. KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown);
  131. KeyUpEvent.AddClassHandler<InputElement>(x => x.OnKeyUp);
  132. TextInputEvent.AddClassHandler<InputElement>(x => x.OnTextInput);
  133. PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter);
  134. PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave);
  135. PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved);
  136. PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed);
  137. PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
  138. PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged);
  139. }
  140. /// <summary>
  141. /// Occurs when the control receives focus.
  142. /// </summary>
  143. public event EventHandler<RoutedEventArgs> GotFocus
  144. {
  145. add { AddHandler(GotFocusEvent, value); }
  146. remove { RemoveHandler(GotFocusEvent, value); }
  147. }
  148. /// <summary>
  149. /// Occurs when the control loses focus.
  150. /// </summary>
  151. public event EventHandler<RoutedEventArgs> LostFocus
  152. {
  153. add { AddHandler(LostFocusEvent, value); }
  154. remove { RemoveHandler(LostFocusEvent, value); }
  155. }
  156. /// <summary>
  157. /// Occurs when a key is pressed while the control has focus.
  158. /// </summary>
  159. public event EventHandler<KeyEventArgs> KeyDown
  160. {
  161. add { AddHandler(KeyDownEvent, value); }
  162. remove { RemoveHandler(KeyDownEvent, value); }
  163. }
  164. /// <summary>
  165. /// Occurs when a key is released while the control has focus.
  166. /// </summary>
  167. public event EventHandler<KeyEventArgs> KeyUp
  168. {
  169. add { AddHandler(KeyUpEvent, value); }
  170. remove { RemoveHandler(KeyUpEvent, value); }
  171. }
  172. /// <summary>
  173. /// Occurs when a user typed some text while the control has focus.
  174. /// </summary>
  175. public event EventHandler<TextInputEventArgs> TextInput
  176. {
  177. add { AddHandler(TextInputEvent, value); }
  178. remove { RemoveHandler(TextInputEvent, value); }
  179. }
  180. /// <summary>
  181. /// Occurs when the pointer enters the control.
  182. /// </summary>
  183. public event EventHandler<PointerEventArgs> PointerEnter
  184. {
  185. add { AddHandler(PointerEnterEvent, value); }
  186. remove { RemoveHandler(PointerEnterEvent, value); }
  187. }
  188. /// <summary>
  189. /// Occurs when the pointer leaves the control.
  190. /// </summary>
  191. public event EventHandler<PointerEventArgs> PointerLeave
  192. {
  193. add { AddHandler(PointerLeaveEvent, value); }
  194. remove { RemoveHandler(PointerLeaveEvent, value); }
  195. }
  196. /// <summary>
  197. /// Occurs when the pointer moves over the control.
  198. /// </summary>
  199. public event EventHandler<PointerEventArgs> PointerMoved
  200. {
  201. add { AddHandler(PointerMovedEvent, value); }
  202. remove { RemoveHandler(PointerMovedEvent, value); }
  203. }
  204. /// <summary>
  205. /// Occurs when the pointer is pressed over the control.
  206. /// </summary>
  207. public event EventHandler<PointerPressEventArgs> PointerPressed
  208. {
  209. add { AddHandler(PointerPressedEvent, value); }
  210. remove { RemoveHandler(PointerPressedEvent, value); }
  211. }
  212. /// <summary>
  213. /// Occurs when the pointer is released over the control.
  214. /// </summary>
  215. public event EventHandler<PointerReleasedEventArgs> PointerReleased
  216. {
  217. add { AddHandler(PointerReleasedEvent, value); }
  218. remove { RemoveHandler(PointerReleasedEvent, value); }
  219. }
  220. /// <summary>
  221. /// Occurs when the mouse wheen is scrolled over the control.
  222. /// </summary>
  223. public event EventHandler<PointerWheelEventArgs> PointerWheelChanged
  224. {
  225. add { AddHandler(PointerWheelChangedEvent, value); }
  226. remove { RemoveHandler(PointerWheelChangedEvent, value); }
  227. }
  228. /// <summary>
  229. /// Gets or sets a value indicating whether the control can receive focus.
  230. /// </summary>
  231. public bool Focusable
  232. {
  233. get { return GetValue(FocusableProperty); }
  234. set { SetValue(FocusableProperty, value); }
  235. }
  236. /// <summary>
  237. /// Gets or sets a value indicating whether the control is enabled for user interaction.
  238. /// </summary>
  239. public bool IsEnabled
  240. {
  241. get { return GetValue(IsEnabledProperty); }
  242. set { SetValue(IsEnabledProperty, value); }
  243. }
  244. /// <summary>
  245. /// Gets or sets associated mouse cursor.
  246. /// </summary>
  247. public Cursor Cursor
  248. {
  249. get { return GetValue(CursorProperty); }
  250. set { SetValue(CursorProperty, value); }
  251. }
  252. /// <summary>
  253. /// Gets or sets a value indicating whether the control is focused.
  254. /// </summary>
  255. public bool IsFocused
  256. {
  257. get { return _isFocused; }
  258. private set { SetAndRaise(IsFocusedProperty, ref _isFocused, value); }
  259. }
  260. /// <summary>
  261. /// Gets or sets a value indicating whether the control is considered for hit testing.
  262. /// </summary>
  263. public bool IsHitTestVisible
  264. {
  265. get { return GetValue(IsHitTestVisibleProperty); }
  266. set { SetValue(IsHitTestVisibleProperty, value); }
  267. }
  268. /// <summary>
  269. /// Gets or sets a value indicating whether the pointer is currently over the control.
  270. /// </summary>
  271. public bool IsPointerOver
  272. {
  273. get { return _isPointerOver; }
  274. internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); }
  275. }
  276. /// <summary>
  277. /// Gets a value indicating whether the control is effectively enabled for user interaction.
  278. /// </summary>
  279. /// <remarks>
  280. /// The <see cref="IsEnabled"/> property is used to toggle the enabled state for individual
  281. /// controls. The <see cref="IsEnabledCore"/> property takes into account the
  282. /// <see cref="IsEnabled"/> value of this control and its parent controls.
  283. /// </remarks>
  284. bool IInputElement.IsEnabledCore => IsEnabledCore;
  285. /// <summary>
  286. /// Gets a value indicating whether the control is effectively enabled for user interaction.
  287. /// </summary>
  288. /// <remarks>
  289. /// The <see cref="IsEnabled"/> property is used to toggle the enabled state for individual
  290. /// controls. The <see cref="IsEnabledCore"/> property takes into account the
  291. /// <see cref="IsEnabled"/> value of this control and its parent controls.
  292. /// </remarks>
  293. protected bool IsEnabledCore
  294. {
  295. get { return GetValue(IsEnabledCoreProperty); }
  296. set { SetValue(IsEnabledCoreProperty, value); }
  297. }
  298. public List<KeyBinding> KeyBindings { get; } = new List<KeyBinding>();
  299. /// <summary>
  300. /// Focuses the control.
  301. /// </summary>
  302. public void Focus()
  303. {
  304. FocusManager.Instance.Focus(this);
  305. }
  306. /// <inheritdoc/>
  307. protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
  308. {
  309. base.OnDetachedFromVisualTree(e);
  310. if (IsFocused)
  311. {
  312. FocusManager.Instance.Focus(null);
  313. }
  314. }
  315. /// <inheritdoc/>
  316. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  317. {
  318. base.OnAttachedToVisualTree(e);
  319. UpdateIsEnabledCore();
  320. }
  321. /// <summary>
  322. /// Called before the <see cref="GotFocus"/> event occurs.
  323. /// </summary>
  324. /// <param name="e">The event args.</param>
  325. protected virtual void OnGotFocus(GotFocusEventArgs e)
  326. {
  327. IsFocused = e.Source == this;
  328. }
  329. /// <summary>
  330. /// Called before the <see cref="LostFocus"/> event occurs.
  331. /// </summary>
  332. /// <param name="e">The event args.</param>
  333. protected virtual void OnLostFocus(RoutedEventArgs e)
  334. {
  335. IsFocused = false;
  336. }
  337. /// <summary>
  338. /// Called before the <see cref="KeyDown"/> event occurs.
  339. /// </summary>
  340. /// <param name="e">The event args.</param>
  341. protected virtual void OnKeyDown(KeyEventArgs e)
  342. {
  343. }
  344. /// <summary>
  345. /// Called before the <see cref="KeyUp"/> event occurs.
  346. /// </summary>
  347. /// <param name="e">The event args.</param>
  348. protected virtual void OnKeyUp(KeyEventArgs e)
  349. {
  350. }
  351. /// <summary>
  352. /// Called before the <see cref="TextInput"/> event occurs.
  353. /// </summary>
  354. /// <param name="e">The event args.</param>
  355. protected virtual void OnTextInput(TextInputEventArgs e)
  356. {
  357. }
  358. /// <summary>
  359. /// Called before the <see cref="PointerEnter"/> event occurs.
  360. /// </summary>
  361. /// <param name="e">The event args.</param>
  362. protected virtual void OnPointerEnter(PointerEventArgs e)
  363. {
  364. IsPointerOver = true;
  365. }
  366. /// <summary>
  367. /// Called before the <see cref="PointerLeave"/> event occurs.
  368. /// </summary>
  369. /// <param name="e">The event args.</param>
  370. protected virtual void OnPointerLeave(PointerEventArgs e)
  371. {
  372. IsPointerOver = false;
  373. }
  374. /// <summary>
  375. /// Called before the <see cref="PointerMoved"/> event occurs.
  376. /// </summary>
  377. /// <param name="e">The event args.</param>
  378. protected virtual void OnPointerMoved(PointerEventArgs e)
  379. {
  380. }
  381. /// <summary>
  382. /// Called before the <see cref="PointerPressed"/> event occurs.
  383. /// </summary>
  384. /// <param name="e">The event args.</param>
  385. protected virtual void OnPointerPressed(PointerPressEventArgs e)
  386. {
  387. }
  388. /// <summary>
  389. /// Called before the <see cref="PointerReleased"/> event occurs.
  390. /// </summary>
  391. /// <param name="e">The event args.</param>
  392. protected virtual void OnPointerReleased(PointerEventArgs e)
  393. {
  394. }
  395. /// <summary>
  396. /// Called before the <see cref="PointerWheelChanged"/> event occurs.
  397. /// </summary>
  398. /// <param name="e">The event args.</param>
  399. protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e)
  400. {
  401. }
  402. private static void IsEnabledChanged(PerspexPropertyChangedEventArgs e)
  403. {
  404. ((InputElement)e.Sender).UpdateIsEnabledCore();
  405. }
  406. /// <summary>
  407. /// Updates the <see cref="IsEnabledCore"/> property value.
  408. /// </summary>
  409. private void UpdateIsEnabledCore()
  410. {
  411. UpdateIsEnabledCore(this.GetVisualParent<InputElement>());
  412. }
  413. /// <summary>
  414. /// Updates the <see cref="IsEnabledCore"/> property based on the parent's
  415. /// <see cref="IsEnabledCore"/>.
  416. /// </summary>
  417. /// <param name="parent">The parent control.</param>
  418. private void UpdateIsEnabledCore(InputElement parent)
  419. {
  420. if (parent != null)
  421. {
  422. IsEnabledCore = IsEnabled && parent.IsEnabledCore;
  423. }
  424. else
  425. {
  426. IsEnabledCore = IsEnabled;
  427. }
  428. foreach (var child in this.GetVisualChildren().OfType<InputElement>())
  429. {
  430. child.UpdateIsEnabledCore(this);
  431. }
  432. }
  433. }
  434. }