InputElement.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Controls;
  5. using Avalonia.Controls.Metadata;
  6. using Avalonia.Data;
  7. using Avalonia.Input.GestureRecognizers;
  8. using Avalonia.Interactivity;
  9. using Avalonia.VisualTree;
  10. namespace Avalonia.Input
  11. {
  12. /// <summary>
  13. /// Implements input-related functionality for a control.
  14. /// </summary>
  15. [PseudoClasses(":disabled", ":focus", ":focus-visible", ":pointerover")]
  16. public class InputElement : Interactive, IInputElement
  17. {
  18. /// <summary>
  19. /// Defines the <see cref="Focusable"/> property.
  20. /// </summary>
  21. public static readonly StyledProperty<bool> FocusableProperty =
  22. AvaloniaProperty.Register<InputElement, bool>(nameof(Focusable));
  23. /// <summary>
  24. /// Defines the <see cref="IsEnabled"/> property.
  25. /// </summary>
  26. public static readonly StyledProperty<bool> IsEnabledProperty =
  27. AvaloniaProperty.Register<InputElement, bool>(nameof(IsEnabled), true);
  28. /// <summary>
  29. /// Defines the <see cref="IsEffectivelyEnabled"/> property.
  30. /// </summary>
  31. public static readonly DirectProperty<InputElement, bool> IsEffectivelyEnabledProperty =
  32. AvaloniaProperty.RegisterDirect<InputElement, bool>(
  33. nameof(IsEffectivelyEnabled),
  34. o => o.IsEffectivelyEnabled);
  35. /// <summary>
  36. /// Gets or sets associated mouse cursor.
  37. /// </summary>
  38. public static readonly StyledProperty<Cursor?> CursorProperty =
  39. AvaloniaProperty.Register<InputElement, Cursor?>(nameof(Cursor), null, true);
  40. /// <summary>
  41. /// Defines the <see cref="IsKeyboardFocusWithin"/> property.
  42. /// </summary>
  43. public static readonly DirectProperty<InputElement, bool> IsKeyboardFocusWithinProperty =
  44. AvaloniaProperty.RegisterDirect<InputElement, bool>(
  45. nameof(IsKeyboardFocusWithin),
  46. o => o.IsKeyboardFocusWithin);
  47. /// <summary>
  48. /// Defines the <see cref="IsFocused"/> property.
  49. /// </summary>
  50. public static readonly DirectProperty<InputElement, bool> IsFocusedProperty =
  51. AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsFocused), o => o.IsFocused);
  52. /// <summary>
  53. /// Defines the <see cref="IsHitTestVisible"/> property.
  54. /// </summary>
  55. public static readonly StyledProperty<bool> IsHitTestVisibleProperty =
  56. AvaloniaProperty.Register<InputElement, bool>(nameof(IsHitTestVisible), true);
  57. /// <summary>
  58. /// Defines the <see cref="IsPointerOver"/> property.
  59. /// </summary>
  60. public static readonly DirectProperty<InputElement, bool> IsPointerOverProperty =
  61. AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsPointerOver), o => o.IsPointerOver);
  62. /// <summary>
  63. /// Defines the <see cref="GotFocus"/> event.
  64. /// </summary>
  65. public static readonly RoutedEvent<GotFocusEventArgs> GotFocusEvent =
  66. RoutedEvent.Register<InputElement, GotFocusEventArgs>(nameof(GotFocus), RoutingStrategies.Bubble);
  67. /// <summary>
  68. /// Defines the <see cref="LostFocus"/> event.
  69. /// </summary>
  70. public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent =
  71. RoutedEvent.Register<InputElement, RoutedEventArgs>(nameof(LostFocus), RoutingStrategies.Bubble);
  72. /// <summary>
  73. /// Defines the <see cref="KeyDown"/> event.
  74. /// </summary>
  75. public static readonly RoutedEvent<KeyEventArgs> KeyDownEvent =
  76. RoutedEvent.Register<InputElement, KeyEventArgs>(
  77. "KeyDown",
  78. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  79. /// <summary>
  80. /// Defines the <see cref="KeyUp"/> event.
  81. /// </summary>
  82. public static readonly RoutedEvent<KeyEventArgs> KeyUpEvent =
  83. RoutedEvent.Register<InputElement, KeyEventArgs>(
  84. "KeyUp",
  85. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  86. /// <summary>
  87. /// Defines the <see cref="TextInput"/> event.
  88. /// </summary>
  89. public static readonly RoutedEvent<TextInputEventArgs> TextInputEvent =
  90. RoutedEvent.Register<InputElement, TextInputEventArgs>(
  91. "TextInput",
  92. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  93. /// <summary>
  94. /// Defines the <see cref="PointerEnter"/> event.
  95. /// </summary>
  96. public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
  97. RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct);
  98. /// <summary>
  99. /// Defines the <see cref="PointerLeave"/> event.
  100. /// </summary>
  101. public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
  102. RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct);
  103. /// <summary>
  104. /// Defines the <see cref="PointerMoved"/> event.
  105. /// </summary>
  106. public static readonly RoutedEvent<PointerEventArgs> PointerMovedEvent =
  107. RoutedEvent.Register<InputElement, PointerEventArgs>(
  108. "PointerMove",
  109. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  110. /// <summary>
  111. /// Defines the <see cref="PointerPressed"/> event.
  112. /// </summary>
  113. public static readonly RoutedEvent<PointerPressedEventArgs> PointerPressedEvent =
  114. RoutedEvent.Register<InputElement, PointerPressedEventArgs>(
  115. "PointerPressed",
  116. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  117. /// <summary>
  118. /// Defines the <see cref="PointerReleased"/> event.
  119. /// </summary>
  120. public static readonly RoutedEvent<PointerReleasedEventArgs> PointerReleasedEvent =
  121. RoutedEvent.Register<InputElement, PointerReleasedEventArgs>(
  122. "PointerReleased",
  123. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  124. /// <summary>
  125. /// Defines the <see cref="PointerCaptureLost"/> routed event.
  126. /// </summary>
  127. public static readonly RoutedEvent<PointerCaptureLostEventArgs> PointerCaptureLostEvent =
  128. RoutedEvent.Register<InputElement, PointerCaptureLostEventArgs>(
  129. "PointerCaptureLost",
  130. RoutingStrategies.Direct);
  131. /// <summary>
  132. /// Defines the <see cref="PointerWheelChanged"/> event.
  133. /// </summary>
  134. public static readonly RoutedEvent<PointerWheelEventArgs> PointerWheelChangedEvent =
  135. RoutedEvent.Register<InputElement, PointerWheelEventArgs>(
  136. "PointerWheelChanged",
  137. RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
  138. /// <summary>
  139. /// Defines the <see cref="Tapped"/> event.
  140. /// </summary>
  141. public static readonly RoutedEvent<RoutedEventArgs> TappedEvent = Gestures.TappedEvent;
  142. /// <summary>
  143. /// Defines the <see cref="DoubleTapped"/> event.
  144. /// </summary>
  145. public static readonly RoutedEvent<RoutedEventArgs> DoubleTappedEvent = Gestures.DoubleTappedEvent;
  146. private bool _isEffectivelyEnabled = true;
  147. private bool _isFocused;
  148. private bool _isKeyboardFocusWithin;
  149. private bool _isFocusVisible;
  150. private bool _isPointerOver;
  151. private GestureRecognizerCollection? _gestureRecognizers;
  152. /// <summary>
  153. /// Initializes static members of the <see cref="InputElement"/> class.
  154. /// </summary>
  155. static InputElement()
  156. {
  157. IsEnabledProperty.Changed.Subscribe(IsEnabledChanged);
  158. GotFocusEvent.AddClassHandler<InputElement>((x, e) => x.OnGotFocus(e));
  159. LostFocusEvent.AddClassHandler<InputElement>((x, e) => x.OnLostFocus(e));
  160. KeyDownEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyDown(e));
  161. KeyUpEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyUp(e));
  162. TextInputEvent.AddClassHandler<InputElement>((x, e) => x.OnTextInput(e));
  163. PointerEnterEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnterCore(e));
  164. PointerLeaveEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerLeaveCore(e));
  165. PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e));
  166. PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e));
  167. PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
  168. PointerCaptureLostEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureLost(e));
  169. PointerWheelChangedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerWheelChanged(e));
  170. }
  171. public InputElement()
  172. {
  173. UpdatePseudoClasses(IsFocused, IsPointerOver);
  174. }
  175. /// <summary>
  176. /// Occurs when the control receives focus.
  177. /// </summary>
  178. public event EventHandler<GotFocusEventArgs> GotFocus
  179. {
  180. add { AddHandler(GotFocusEvent, value); }
  181. remove { RemoveHandler(GotFocusEvent, value); }
  182. }
  183. /// <summary>
  184. /// Occurs when the control loses focus.
  185. /// </summary>
  186. public event EventHandler<RoutedEventArgs> LostFocus
  187. {
  188. add { AddHandler(LostFocusEvent, value); }
  189. remove { RemoveHandler(LostFocusEvent, value); }
  190. }
  191. /// <summary>
  192. /// Occurs when a key is pressed while the control has focus.
  193. /// </summary>
  194. public event EventHandler<KeyEventArgs> KeyDown
  195. {
  196. add { AddHandler(KeyDownEvent, value); }
  197. remove { RemoveHandler(KeyDownEvent, value); }
  198. }
  199. /// <summary>
  200. /// Occurs when a key is released while the control has focus.
  201. /// </summary>
  202. public event EventHandler<KeyEventArgs> KeyUp
  203. {
  204. add { AddHandler(KeyUpEvent, value); }
  205. remove { RemoveHandler(KeyUpEvent, value); }
  206. }
  207. /// <summary>
  208. /// Occurs when a user typed some text while the control has focus.
  209. /// </summary>
  210. public event EventHandler<TextInputEventArgs> TextInput
  211. {
  212. add { AddHandler(TextInputEvent, value); }
  213. remove { RemoveHandler(TextInputEvent, value); }
  214. }
  215. /// <summary>
  216. /// Occurs when the pointer enters the control.
  217. /// </summary>
  218. public event EventHandler<PointerEventArgs> PointerEnter
  219. {
  220. add { AddHandler(PointerEnterEvent, value); }
  221. remove { RemoveHandler(PointerEnterEvent, value); }
  222. }
  223. /// <summary>
  224. /// Occurs when the pointer leaves the control.
  225. /// </summary>
  226. public event EventHandler<PointerEventArgs> PointerLeave
  227. {
  228. add { AddHandler(PointerLeaveEvent, value); }
  229. remove { RemoveHandler(PointerLeaveEvent, value); }
  230. }
  231. /// <summary>
  232. /// Occurs when the pointer moves over the control.
  233. /// </summary>
  234. public event EventHandler<PointerEventArgs> PointerMoved
  235. {
  236. add { AddHandler(PointerMovedEvent, value); }
  237. remove { RemoveHandler(PointerMovedEvent, value); }
  238. }
  239. /// <summary>
  240. /// Occurs when the pointer is pressed over the control.
  241. /// </summary>
  242. public event EventHandler<PointerPressedEventArgs> PointerPressed
  243. {
  244. add { AddHandler(PointerPressedEvent, value); }
  245. remove { RemoveHandler(PointerPressedEvent, value); }
  246. }
  247. /// <summary>
  248. /// Occurs when the pointer is released over the control.
  249. /// </summary>
  250. public event EventHandler<PointerReleasedEventArgs> PointerReleased
  251. {
  252. add { AddHandler(PointerReleasedEvent, value); }
  253. remove { RemoveHandler(PointerReleasedEvent, value); }
  254. }
  255. /// <summary>
  256. /// Occurs when the control or its child control loses the pointer capture for any reason,
  257. /// event will not be triggered for a parent control if capture was transferred to another child of that parent control
  258. /// </summary>
  259. public event EventHandler<PointerCaptureLostEventArgs> PointerCaptureLost
  260. {
  261. add => AddHandler(PointerCaptureLostEvent, value);
  262. remove => RemoveHandler(PointerCaptureLostEvent, value);
  263. }
  264. /// <summary>
  265. /// Occurs when the mouse wheen is scrolled over the control.
  266. /// </summary>
  267. public event EventHandler<PointerWheelEventArgs> PointerWheelChanged
  268. {
  269. add { AddHandler(PointerWheelChangedEvent, value); }
  270. remove { RemoveHandler(PointerWheelChangedEvent, value); }
  271. }
  272. /// <summary>
  273. /// Occurs when a tap gesture occurs on the control.
  274. /// </summary>
  275. public event EventHandler<RoutedEventArgs> Tapped
  276. {
  277. add { AddHandler(TappedEvent, value); }
  278. remove { RemoveHandler(TappedEvent, value); }
  279. }
  280. /// <summary>
  281. /// Occurs when a double-tap gesture occurs on the control.
  282. /// </summary>
  283. public event EventHandler<RoutedEventArgs> DoubleTapped
  284. {
  285. add { AddHandler(DoubleTappedEvent, value); }
  286. remove { RemoveHandler(DoubleTappedEvent, value); }
  287. }
  288. /// <summary>
  289. /// Gets or sets a value indicating whether the control can receive focus.
  290. /// </summary>
  291. public bool Focusable
  292. {
  293. get { return GetValue(FocusableProperty); }
  294. set { SetValue(FocusableProperty, value); }
  295. }
  296. /// <summary>
  297. /// Gets or sets a value indicating whether the control is enabled for user interaction.
  298. /// </summary>
  299. public bool IsEnabled
  300. {
  301. get { return GetValue(IsEnabledProperty); }
  302. set { SetValue(IsEnabledProperty, value); }
  303. }
  304. /// <summary>
  305. /// Gets or sets associated mouse cursor.
  306. /// </summary>
  307. public Cursor? Cursor
  308. {
  309. get { return GetValue(CursorProperty); }
  310. set { SetValue(CursorProperty, value); }
  311. }
  312. /// <summary>
  313. /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements.
  314. /// </summary>
  315. public bool IsKeyboardFocusWithin
  316. {
  317. get => _isKeyboardFocusWithin;
  318. internal set => SetAndRaise(IsKeyboardFocusWithinProperty, ref _isKeyboardFocusWithin, value);
  319. }
  320. /// <summary>
  321. /// Gets a value indicating whether the control is focused.
  322. /// </summary>
  323. public bool IsFocused
  324. {
  325. get { return _isFocused; }
  326. private set { SetAndRaise(IsFocusedProperty, ref _isFocused, value); }
  327. }
  328. /// <summary>
  329. /// Gets or sets a value indicating whether the control is considered for hit testing.
  330. /// </summary>
  331. public bool IsHitTestVisible
  332. {
  333. get { return GetValue(IsHitTestVisibleProperty); }
  334. set { SetValue(IsHitTestVisibleProperty, value); }
  335. }
  336. /// <summary>
  337. /// Gets a value indicating whether the pointer is currently over the control.
  338. /// </summary>
  339. public bool IsPointerOver
  340. {
  341. get { return _isPointerOver; }
  342. internal set { SetAndRaise(IsPointerOverProperty, ref _isPointerOver, value); }
  343. }
  344. /// <inheritdoc/>
  345. public bool IsEffectivelyEnabled
  346. {
  347. get => _isEffectivelyEnabled;
  348. private set
  349. {
  350. SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
  351. PseudoClasses.Set(":disabled", !value);
  352. }
  353. }
  354. public List<KeyBinding> KeyBindings { get; } = new List<KeyBinding>();
  355. /// <summary>
  356. /// Allows a derived class to override the enabled state of the control.
  357. /// </summary>
  358. /// <remarks>
  359. /// Derived controls may wish to disable the enabled state of the control without overwriting the
  360. /// user-supplied <see cref="IsEnabled"/> setting. This can be done by overriding this property
  361. /// to return the overridden enabled state. If the value returned from <see cref="IsEnabledCore"/>
  362. /// should change, then the derived control should call <see cref="UpdateIsEffectivelyEnabled()"/>.
  363. /// </remarks>
  364. protected virtual bool IsEnabledCore => IsEnabled;
  365. public GestureRecognizerCollection GestureRecognizers
  366. => _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this));
  367. /// <summary>
  368. /// Focuses the control.
  369. /// </summary>
  370. public void Focus()
  371. {
  372. FocusManager.Instance?.Focus(this);
  373. }
  374. /// <inheritdoc/>
  375. protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
  376. {
  377. base.OnDetachedFromVisualTreeCore(e);
  378. if (IsFocused)
  379. {
  380. FocusManager.Instance.Focus(null);
  381. }
  382. }
  383. /// <inheritdoc/>
  384. protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
  385. {
  386. base.OnAttachedToVisualTreeCore(e);
  387. UpdateIsEffectivelyEnabled();
  388. }
  389. /// <summary>
  390. /// Called before the <see cref="GotFocus"/> event occurs.
  391. /// </summary>
  392. /// <param name="e">The event args.</param>
  393. protected virtual void OnGotFocus(GotFocusEventArgs e)
  394. {
  395. var isFocused = e.Source == this;
  396. _isFocusVisible = isFocused && (e.NavigationMethod == NavigationMethod.Directional || e.NavigationMethod == NavigationMethod.Tab);
  397. IsFocused = isFocused;
  398. }
  399. /// <summary>
  400. /// Called before the <see cref="LostFocus"/> event occurs.
  401. /// </summary>
  402. /// <param name="e">The event args.</param>
  403. protected virtual void OnLostFocus(RoutedEventArgs e)
  404. {
  405. _isFocusVisible = false;
  406. IsFocused = false;
  407. }
  408. /// <summary>
  409. /// Called before the <see cref="KeyDown"/> event occurs.
  410. /// </summary>
  411. /// <param name="e">The event args.</param>
  412. protected virtual void OnKeyDown(KeyEventArgs e)
  413. {
  414. }
  415. /// <summary>
  416. /// Called before the <see cref="KeyUp"/> event occurs.
  417. /// </summary>
  418. /// <param name="e">The event args.</param>
  419. protected virtual void OnKeyUp(KeyEventArgs e)
  420. {
  421. }
  422. /// <summary>
  423. /// Called before the <see cref="TextInput"/> event occurs.
  424. /// </summary>
  425. /// <param name="e">The event args.</param>
  426. protected virtual void OnTextInput(TextInputEventArgs e)
  427. {
  428. }
  429. /// <summary>
  430. /// Called before the <see cref="PointerEnter"/> event occurs.
  431. /// </summary>
  432. /// <param name="e">The event args.</param>
  433. protected virtual void OnPointerEnter(PointerEventArgs e)
  434. {
  435. }
  436. /// <summary>
  437. /// Called before the <see cref="PointerLeave"/> event occurs.
  438. /// </summary>
  439. /// <param name="e">The event args.</param>
  440. protected virtual void OnPointerLeave(PointerEventArgs e)
  441. {
  442. }
  443. /// <summary>
  444. /// Called before the <see cref="PointerMoved"/> event occurs.
  445. /// </summary>
  446. /// <param name="e">The event args.</param>
  447. protected virtual void OnPointerMoved(PointerEventArgs e)
  448. {
  449. if (_gestureRecognizers?.HandlePointerMoved(e) == true)
  450. e.Handled = true;
  451. }
  452. /// <summary>
  453. /// Called before the <see cref="PointerPressed"/> event occurs.
  454. /// </summary>
  455. /// <param name="e">The event args.</param>
  456. protected virtual void OnPointerPressed(PointerPressedEventArgs e)
  457. {
  458. if (_gestureRecognizers?.HandlePointerPressed(e) == true)
  459. e.Handled = true;
  460. }
  461. /// <summary>
  462. /// Called before the <see cref="PointerReleased"/> event occurs.
  463. /// </summary>
  464. /// <param name="e">The event args.</param>
  465. protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
  466. {
  467. if (_gestureRecognizers?.HandlePointerReleased(e) == true)
  468. e.Handled = true;
  469. }
  470. /// <summary>
  471. /// Called before the <see cref="PointerCaptureLost"/> event occurs.
  472. /// </summary>
  473. /// <param name="e">The event args.</param>
  474. protected virtual void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
  475. {
  476. _gestureRecognizers?.HandlePointerCaptureLost(e);
  477. }
  478. /// <summary>
  479. /// Called before the <see cref="PointerWheelChanged"/> event occurs.
  480. /// </summary>
  481. /// <param name="e">The event args.</param>
  482. protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e)
  483. {
  484. }
  485. protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
  486. {
  487. base.OnPropertyChanged(change);
  488. if (change.Property == IsFocusedProperty)
  489. {
  490. UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>(), null);
  491. }
  492. else if (change.Property == IsPointerOverProperty)
  493. {
  494. UpdatePseudoClasses(null, change.NewValue.GetValueOrDefault<bool>());
  495. }
  496. else if (change.Property == IsKeyboardFocusWithinProperty)
  497. {
  498. PseudoClasses.Set(":focus-within", _isKeyboardFocusWithin);
  499. }
  500. }
  501. /// <summary>
  502. /// Updates the <see cref="IsEffectivelyEnabled"/> property value according to the parent
  503. /// control's enabled state and <see cref="IsEnabledCore"/>.
  504. /// </summary>
  505. protected void UpdateIsEffectivelyEnabled()
  506. {
  507. UpdateIsEffectivelyEnabled(this.GetVisualParent<InputElement>());
  508. }
  509. private static void IsEnabledChanged(AvaloniaPropertyChangedEventArgs e)
  510. {
  511. ((InputElement)e.Sender).UpdateIsEffectivelyEnabled();
  512. }
  513. /// <summary>
  514. /// Called before the <see cref="PointerEnter"/> event occurs.
  515. /// </summary>
  516. /// <param name="e">The event args.</param>
  517. private void OnPointerEnterCore(PointerEventArgs e)
  518. {
  519. IsPointerOver = true;
  520. OnPointerEnter(e);
  521. }
  522. /// <summary>
  523. /// Called before the <see cref="PointerLeave"/> event occurs.
  524. /// </summary>
  525. /// <param name="e">The event args.</param>
  526. private void OnPointerLeaveCore(PointerEventArgs e)
  527. {
  528. IsPointerOver = false;
  529. OnPointerLeave(e);
  530. }
  531. /// <summary>
  532. /// Updates the <see cref="IsEffectivelyEnabled"/> property based on the parent's
  533. /// <see cref="IsEffectivelyEnabled"/>.
  534. /// </summary>
  535. /// <param name="parent">The parent control.</param>
  536. private void UpdateIsEffectivelyEnabled(InputElement parent)
  537. {
  538. IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true);
  539. // PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ
  540. // will cause extra allocations and overhead.
  541. var children = VisualChildren;
  542. // ReSharper disable once ForCanBeConvertedToForeach
  543. for (int i = 0; i < children.Count; ++i)
  544. {
  545. var child = children[i] as InputElement;
  546. child?.UpdateIsEffectivelyEnabled(this);
  547. }
  548. }
  549. private void UpdatePseudoClasses(bool? isFocused, bool? isPointerOver)
  550. {
  551. if (isFocused.HasValue)
  552. {
  553. PseudoClasses.Set(":focus", isFocused.Value);
  554. PseudoClasses.Set(":focus-visible", _isFocusVisible);
  555. }
  556. if (isPointerOver.HasValue)
  557. {
  558. PseudoClasses.Set(":pointerover", isPointerOver.Value);
  559. }
  560. }
  561. }
  562. }