Browse Source

Android soft input using Avalonia.Input

pull/5735/head
ili 5 years ago
parent
commit
12c2c16d13
  1. 2
      global.json
  2. 2
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  3. 6
      samples/ControlCatalog/Pages/TextBoxPage.xaml.cs
  4. 47
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  5. 1
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  6. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  7. 78
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  8. 2
      src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs
  9. 9
      src/Avalonia.Controls/TextBox.cs
  10. 11
      src/Avalonia.Input/ISoftInputElement.cs
  11. 32
      src/Avalonia.Input/InputType.cs

2
global.json

@ -1,6 +1,6 @@
{
"sdk": {
"version": "3.1.407"
"version": "3.1.401"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",

2
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -13,7 +13,7 @@
<StackPanel Orientation="Vertical" Spacing="8">
<TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" />
<TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Watermark" InputType="Numeric" />
<TextBox Width="200" Watermark="Numeric Watermark" x:Name="numericWatermark"/>
<TextBox Width="200"
Watermark="Floating Watermark"
UseFloatingWatermark="True"

6
samples/ControlCatalog/Pages/TextBoxPage.xaml.cs

@ -13,6 +13,12 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.Get<TextBox>("numericWatermark")
.TextInputOptionsQuery += (s, a) =>
{
a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number;
};
}
}
}

47
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -5,6 +5,8 @@ using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.TextInput;
namespace Avalonia.Android
@ -14,6 +16,7 @@ namespace Avalonia.Android
{
private readonly TView _host;
private readonly InputMethodManager _imm;
private IInputElement _inputElement;
public AndroidInputMethod(TView host)
{
@ -44,7 +47,49 @@ namespace Avalonia.Android
public void SetOptions(TextInputOptionsQueryEventArgs options)
{
//throw new NotImplementedException();
if (_inputElement != null)
{
_inputElement.PointerReleased -= RestoreSoftKeyboard;
}
_inputElement = options.Source as InputElement;
if (_inputElement == null)
{
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
_host.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = options.ContentType switch
{
TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
_ => global::Android.Text.InputTypes.Null
};
if (options.AutoCapitalization)
{
outAttrs.InitialCapsMode = global::Android.Text.CapitalizationMode.Sentences;
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagCapSentences;
}
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
});
Reset();
_inputElement.PointerReleased += RestoreSoftKeyboard;
RestoreSoftKeyboard(null, null);
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)
{
//_imm.ToggleSoftInput(ShowFlags.Implicit, HideSoftInputFlags.NotAlways);
_imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
}
}

1
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@ -16,7 +16,6 @@ namespace Avalonia.Android
public abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHolderCallback, IPlatformHandle
{
bool _invalidateQueued;
private ISoftInputElement _softInputElement;
readonly object _lock = new object();
private readonly Handler _handler;

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -35,7 +35,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
_view = new ViewImpl(context, this, placeOnTop);
_textInputMethod = new AndroidInputMethod<ViewImpl>(_view);
_keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this);
_keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this, _textInputMethod);
_touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot,
GetAvaloniaPointFromEvent);
@ -237,9 +237,5 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
throw new NotImplementedException();
}
public void InitEditorInfo(Action<EditorInfo> init)
{
_view.InitEditorInfo(init);
}
}
}

78
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -12,19 +12,22 @@ using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
namespace Avalonia.Android.Platform.Specific.Helpers
{
internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl, IAndroidView, ITopLevelImplWithTextInputMethod
internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl, IAndroidView
{
private TView _view;
private readonly TView _view;
private readonly ITextInputMethodImpl _textInpuMethod;
private IInputElement _lastFocusedElement;
public bool HandleEvents { get; set; }
public AndroidKeyboardEventsHelper(TView view)
public AndroidKeyboardEventsHelper(TView view, ITextInputMethodImpl androidTextInput)
{
this._view = view;
_view = view;
_textInpuMethod = androidTextInput;
HandleEvents = true;
}
@ -100,71 +103,6 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return rv;
}
private bool NeedsKeyboard(IInputElement element)
{
//may be some other elements
return element is ISoftInputElement;
}
private void TryShowHideKeyboard(ISoftInputElement element, bool value)
{
_view.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = element.InputType switch
{
InputType.Numeric => global::Android.Text.InputTypes.ClassNumber,
InputType.Phone => global::Android.Text.InputTypes.ClassPhone,
_ => global::Android.Text.InputTypes.Null
};
});
var input = _view.View.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
if (value && element != null && element.InputType != InputType.None)
{
_view.View.RequestFocus();
if (!ReferenceEquals(_lastFocusedElement, element))
{
input.RestartInput(_view.View);
}
input.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.NotAlways);
}
else
{
input.HideSoftInputFromWindow(_view.View.WindowToken, HideSoftInputFlags.None);
}
}
public void UpdateKeyboardState(IInputElement element)
{
var focusedElement = element as ISoftInputElement;
var lastElement = _lastFocusedElement as ISoftInputElement;
bool oldValue = lastElement?.InputType > InputType.None;
bool newValue = focusedElement?.InputType > InputType.None;
if (newValue != oldValue || newValue)
{
if (_lastFocusedElement != null)
_lastFocusedElement.PointerReleased -= RestoreSoftKeyboard;
TryShowHideKeyboard(focusedElement, newValue);
if (newValue && focusedElement != null)
element.PointerReleased += RestoreSoftKeyboard;
}
_lastFocusedElement = element;
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)
{
if (_lastFocusedElement is ISoftInputElement softInputElement && softInputElement.InputType != InputType.None)
TryShowHideKeyboard(softInputElement, true);
}
public void ActivateAutoShowKeyboard()
{
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged);
@ -178,7 +116,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
if (e.PropertyName == nameof(KeyboardDevice.FocusedElement))
{
UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement);
//UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement);
}
}

2
src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs

@ -6,7 +6,5 @@ namespace Avalonia.Android.Platform.Specific
public interface IAndroidView
{
View View { get; }
}
}

9
src/Avalonia.Controls/TextBox.cs

@ -18,7 +18,7 @@ using Avalonia.Controls.Metadata;
namespace Avalonia.Controls
{
[PseudoClasses(":empty")]
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost, ISoftInputElement
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
{
public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current
.GetService<PlatformHotkeyConfiguration>()?.Cut.FirstOrDefault();
@ -130,11 +130,6 @@ namespace Avalonia.Controls
nameof(CanPaste),
o => o.CanPaste);
public static readonly DirectProperty<TextBox, InputType> InputTypeProperty =
AvaloniaProperty.RegisterDirect<TextBox, InputType>(
nameof(InputType),
o => o.InputType);
struct UndoRedoState : IEquatable<UndoRedoState>
{
public string Text { get; }
@ -1248,7 +1243,5 @@ namespace Avalonia.Controls
ClearSelection();
}
}
public InputType InputType { get; set; } = InputType.Text;
}
}

11
src/Avalonia.Input/ISoftInputElement.cs

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Input
{
public interface ISoftInputElement
{
InputType InputType { get; }
}
}

32
src/Avalonia.Input/InputType.cs

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Input
{
/// <summary>
/// Input type enumeration
/// </summary>
public enum InputType
{
/// <summary>
/// Do not use input
/// </summary>
None,
/// <summary>
/// User full text input
/// </summary>
Text,
/// <summary>
/// Use numeric text input
/// </summary>
Numeric,
/// <summary>
/// Use phone input
/// </summary>
Phone
}
}
Loading…
Cancel
Save