KeyboardEventsHelper.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using System;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using Avalonia.Controls;
  5. using Avalonia.Input;
  6. using Avalonia.Input.Raw;
  7. using Avalonia.Platform;
  8. using ObjCRuntime;
  9. using UIKit;
  10. namespace Avalonia.iOS.Specific
  11. {
  12. /// <summary>
  13. /// In order to have properly handle of keyboard event in iOS View should already made some things in the View:
  14. /// 1. Adopt the UIKeyInput protocol - add [Adopts("UIKeyInput")] to your view class
  15. /// 2. Implement all the methods required by UIKeyInput:
  16. /// 2.1 Implement HasText
  17. /// example:
  18. /// [Export("hasText")]
  19. /// bool HasText => _keyboardHelper.HasText()
  20. /// 2.2 Implement InsertText
  21. /// example:
  22. /// [Export("insertText:")]
  23. /// void InsertText(string text) => _keyboardHelper.InsertText(text);
  24. /// 2.3 Implement InsertText
  25. /// example:
  26. /// [Export("deleteBackward")]
  27. /// void DeleteBackward() => _keyboardHelper.DeleteBackward();
  28. /// 3.Let iOS know that this can become a first responder:
  29. /// public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
  30. /// or
  31. /// public override bool CanBecomeFirstResponder { get { return true; } }
  32. ///
  33. /// 4. To show keyboard:
  34. /// view.BecomeFirstResponder();
  35. /// 5. To hide keyboard
  36. /// view.ResignFirstResponder();
  37. /// </summary>
  38. /// <typeparam name="TView">View that needs keyboard events and show/hide keyboard</typeparam>
  39. internal class KeyboardEventsHelper<TView> where TView : UIView, ITopLevelImpl
  40. {
  41. private TView _view;
  42. private IInputElement _lastFocusedElement;
  43. public KeyboardEventsHelper(TView view)
  44. {
  45. _view = view;
  46. var uiKeyInputAttribute = view.GetType().GetCustomAttributes(typeof(AdoptsAttribute), true).OfType<AdoptsAttribute>().Where(a => a.ProtocolType == "UIKeyInput").FirstOrDefault();
  47. if (uiKeyInputAttribute == null) throw new NotSupportedException($"View class {typeof(TView).Name} should have class attribute - [Adopts(\"UIKeyInput\")] in order to access keyboard events!");
  48. HandleEvents = true;
  49. }
  50. /// <summary>
  51. /// HandleEvents in order to suspend keyboard notifications or resume it
  52. /// </summary>
  53. public bool HandleEvents { get; set; }
  54. public bool HasText() => false;
  55. public bool CanBecomeFirstResponder() => true;
  56. public void DeleteBackward()
  57. {
  58. HandleKey(Key.Back, RawKeyEventType.KeyDown);
  59. HandleKey(Key.Back, RawKeyEventType.KeyUp);
  60. }
  61. public void InsertText(string text)
  62. {
  63. var rawTextEvent = new RawTextInputEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, text);
  64. _view.Input(rawTextEvent);
  65. }
  66. private void HandleKey(Key key, RawKeyEventType type)
  67. {
  68. var rawKeyEvent = new RawKeyEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, type, key, RawInputModifiers.None);
  69. _view.Input(rawKeyEvent);
  70. }
  71. //currently not found a way to get InputModifiers state
  72. //private static InputModifiers GetModifierKeys(object e)
  73. //{
  74. // var im = InputModifiers.None;
  75. // //if (IsCtrlPressed) rv |= InputModifiers.Control;
  76. // //if (IsShiftPressed) rv |= InputModifiers.Shift;
  77. // return im;
  78. //}
  79. private bool NeedsKeyboard(IInputElement element)
  80. {
  81. //may be some other elements
  82. return element is TextBox;
  83. }
  84. private void TryShowHideKeyboard(IInputElement element, bool value)
  85. {
  86. if (value)
  87. {
  88. _view.BecomeFirstResponder();
  89. }
  90. else
  91. {
  92. _view.ResignFirstResponder();
  93. }
  94. }
  95. public void UpdateKeyboardState(IInputElement element)
  96. {
  97. var focusedElement = element;
  98. bool oldValue = NeedsKeyboard(_lastFocusedElement);
  99. bool newValue = NeedsKeyboard(focusedElement);
  100. if (newValue != oldValue || newValue)
  101. {
  102. TryShowHideKeyboard(focusedElement, newValue);
  103. }
  104. _lastFocusedElement = element;
  105. }
  106. public void ActivateAutoShowKeyboard()
  107. {
  108. var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged);
  109. //just in case we've called more than once the method
  110. kbDevice.PropertyChanged -= KeyboardDevice_PropertyChanged;
  111. kbDevice.PropertyChanged += KeyboardDevice_PropertyChanged;
  112. }
  113. private void KeyboardDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
  114. {
  115. if (e.PropertyName == nameof(KeyboardDevice.FocusedElement))
  116. {
  117. UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement);
  118. }
  119. }
  120. public void Dispose()
  121. {
  122. HandleEvents = false;
  123. }
  124. }
  125. }