Browse Source

Merge pull request #346 from donandren/iostext

Text handling in iOS
pull/347/head
Nikita Tsukanov 10 years ago
parent
commit
a74d24acaf
  1. 1
      src/iOS/Perspex.iOS/Perspex.iOS.csproj
  2. 26
      src/iOS/Perspex.iOS/PerspexView.cs
  3. 147
      src/iOS/Perspex.iOS/Specific/KeyboardEventsHelper.cs

1
src/iOS/Perspex.iOS/Perspex.iOS.csproj

@ -42,6 +42,7 @@
<Compile Include="PlatformSettings.cs" />
<Compile Include="PlatformThreadingInterface.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Specific\KeyboardEventsHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj">

26
src/iOS/Perspex.iOS/PerspexView.cs

@ -14,26 +14,41 @@ using Perspex.Media;
using Perspex.Platform;
using Perspex.Skia.iOS;
using UIKit;
using Perspex.iOS.Specific;
using ObjCRuntime;
namespace Perspex.iOS
{
[Adopts("UIKeyInput")]
class PerspexView : SkiaView, IWindowImpl
{
private readonly UIWindow _window;
private readonly UIViewController _controller;
private IInputRoot _inputRoot;
private readonly KeyboardEventsHelper<PerspexView> _keyboardHelper;
public PerspexView(UIWindow window, UIViewController controller) : base(onFrame => PlatformThreadingInterface.Instance.Render = onFrame)
{
if (controller == null) throw new ArgumentNullException(nameof(controller));
_window = window;
_controller = controller;
_keyboardHelper = new KeyboardEventsHelper<PerspexView>(this);
AutoresizingMask = UIViewAutoresizing.All;
AutoFit();
UIApplication.Notifications.ObserveDidChangeStatusBarOrientation(delegate { AutoFit(); });
UIApplication.Notifications.ObserveDidChangeStatusBarFrame(delegate { AutoFit(); });
}
[Export("hasText")]
bool HasText => _keyboardHelper.HasText();
[Export("insertText:")]
void InsertText(string text) => _keyboardHelper.InsertText(text);
[Export("deleteBackward")]
void DeleteBackward() => _keyboardHelper.DeleteBackward();
public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
void AutoFit()
{
@ -89,6 +104,7 @@ namespace Perspex.iOS
public void Show()
{
_keyboardHelper.ActivateAutoShowKeybord();
}
public Size MaxClientSize => Bounds.Size.ToPerspex();
@ -152,8 +168,14 @@ namespace Perspex.iOS
RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
else
{
Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint) touch.Timestamp,
_inputRoot, location, location - _touchLastPoint, InputModifiers.LeftMouseButton));
double x = location.X - _touchLastPoint.X;
double y = location.Y - _touchLastPoint.Y;
double correction = 0.02;
var scale = PerspexLocator.Current.GetService<IPlatformSettings>().RenderScalingFactor;
scale = 1;
Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint)touch.Timestamp,
_inputRoot, location, new Vector(x * correction / scale, y * correction / scale), InputModifiers.LeftMouseButton));
}
_touchLastPoint = location;
}

147
src/iOS/Perspex.iOS/Specific/KeyboardEventsHelper.cs

@ -0,0 +1,147 @@
using ObjCRuntime;
using Perspex.Controls;
using Perspex.Input;
using Perspex.Input.Raw;
using Perspex.Platform;
using System;
using System.ComponentModel;
using System.Linq;
using UIKit;
namespace Perspex.iOS.Specific
{
/// <summary>
/// In order to have properly handle of keyboard event in iOS View should already made some things in the View:
/// 1. Adopt the UIKeyInput protocol - add [Adopts("UIKeyInput")] to your view class
/// 2. Implement all the methods required by UIKeyInput:
/// 2.1 Implement HasText
/// example:
/// [Export("hasText")]
/// bool HasText => _keyboardHelper.HasText()
/// 2.2 Implement InsertText
/// example:
/// [Export("insertText:")]
/// void InsertText(string text) => _keyboardHelper.InsertText(text);
/// 2.3 Implement InsertText
/// example:
/// [Export("deleteBackward")]
/// void DeleteBackward() => _keyboardHelper.DeleteBackward();
/// 3.Let iOS know that this can become a first responder:
/// public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
/// or
/// public override bool CanBecomeFirstResponder { get { return true; } }
///
/// 4. To show keyboard:
/// view.BecomeFirstResponder();
/// 5. To hide keyboard
/// view.ResignFirstResponder();
/// </summary>
/// <typeparam name="TView">View that needs keyboard events and show/hide keyboard</typeparam>
internal class KeyboardEventsHelper<TView> where TView : UIView, IWindowImpl
{
private TView _view;
private IInputElement _lastFocusedElement;
public KeyboardEventsHelper(TView view)
{
_view = view;
var uiKeyInputAttribute = view.GetType().GetCustomAttributes(typeof(AdoptsAttribute), true).OfType<AdoptsAttribute>().Where(a => a.ProtocolType == "UIKeyInput").FirstOrDefault();
if (uiKeyInputAttribute == null) throw new NotSupportedException($"View class {typeof(TView).Name} should have class attribute - [Adopts(\"UIKeyInput\")] in order to access keyboard events!");
HandleEvents = true;
}
/// <summary>
/// HandleEvents in order to suspend keyboard notifications or resume it
/// </summary>
public bool HandleEvents { get; set; }
public bool HasText() => false;
public bool CanBecomeFirstResponder() => true;
public void DeleteBackward()
{
HandleKey(Key.Back, RawKeyEventType.KeyDown);
HandleKey(Key.Back, RawKeyEventType.KeyUp);
}
public void InsertText(string text)
{
var rawTextEvent = new RawTextInputEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, text);
_view.Input(rawTextEvent);
}
private void HandleKey(Key key, RawKeyEventType type)
{
var rawKeyEvent = new RawKeyEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, type, key, InputModifiers.None);
_view.Input(rawKeyEvent);
}
//currently not found a way to get InputModifiers state
//private static InputModifiers GetModifierKeys(object e)
//{
// var im = InputModifiers.None;
// //if (IsCtrlPressed) rv |= InputModifiers.Control;
// //if (IsShiftPressed) rv |= InputModifiers.Shift;
// return im;
//}
private bool NeedsKeyboard(IInputElement element)
{
//may be some other elements
return element is TextBox;
}
private void TryShowHideKeyboard(IInputElement element, bool value)
{
if (value)
{
_view.BecomeFirstResponder();
}
else
{
_view.ResignFirstResponder();
}
}
public void UpdateKeyboardState(IInputElement element)
{
var focusedElement = element;
bool oldValue = NeedsKeyboard(_lastFocusedElement);
bool newValue = NeedsKeyboard(focusedElement);
if (newValue != oldValue || newValue)
{
TryShowHideKeyboard(focusedElement, newValue);
}
_lastFocusedElement = element;
}
public void ActivateAutoShowKeybord()
{
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged);
//just in case we've called more than once the method
kbDevice.PropertyChanged -= KeyboardDevice_PropertyChanged;
kbDevice.PropertyChanged += KeyboardDevice_PropertyChanged;
}
private void KeyboardDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(KeyboardDevice.FocusedElement))
{
UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement);
}
}
public void Dispose()
{
HandleEvents = false;
}
}
}
Loading…
Cancel
Save