Browse Source

Android - Input Connection fixes (#16666)

* send enter key event on soft keyboard action

* reset composition region when text buffer is updated

* update input connection state when batch edit ends
pull/16693/head
Emmanuel Hansen 2 years ago
committed by GitHub
parent
commit
7cced30e77
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 38
      src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs
  2. 50
      src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs
  3. 11
      src/Android/Avalonia.Android/Platform/Input/EditCommand.cs
  4. 40
      src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs

38
src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs

@ -82,21 +82,7 @@ namespace Avalonia.Android.Platform.Input
_imm.ShowSoftInput(_host, ShowFlags.Implicit); _imm.ShowSoftInput(_host, ShowFlags.Implicit);
var selection = Client.Selection; _inputConnection?.UpdateState();
_imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End);
var surroundingText = _client.SurroundingText ?? "";
var extractedText = new ExtractedText
{
Text = new Java.Lang.String(surroundingText),
SelectionStart = selection.Start,
SelectionEnd = selection.End,
PartialEndOffset = surroundingText.Length
};
_imm.UpdateExtractedText(_host, _inputConnection?.ExtractedTextToken ?? 0, extractedText);
_client.SurroundingTextChanged += _client_SurroundingTextChanged; _client.SurroundingTextChanged += _client_SurroundingTextChanged;
_client.SelectionChanged += _client_SelectionChanged; _client.SelectionChanged += _client_SelectionChanged;
@ -150,27 +136,7 @@ namespace Avalonia.Android.Platform.Input
private void OnSurroundingTextChanged() private void OnSurroundingTextChanged()
{ {
if (_client is null || _inputConnection is null || _inputConnection.IsInUpdate) _inputConnection?.UpdateState();
{
return;
}
if (_inputConnection.IsInMonitorMode)
{
var surroundingText = _client.SurroundingText ?? "";
var selection = _client.Selection;
var extractedText = new ExtractedText
{
Text = new Java.Lang.String(surroundingText),
SelectionStart = selection.Start,
SelectionEnd = selection.End,
PartialEndOffset = surroundingText.Length
};
_imm.UpdateExtractedText(_host, _inputConnection.ExtractedTextToken, extractedText);
}
} }
public void SetCursorRect(Rect rect) public void SetCursorRect(Rect rect)

50
src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs

@ -44,6 +44,20 @@ namespace Avalonia.Android.Platform.Input
public bool IsInUpdate { get; set; } public bool IsInUpdate { get; set; }
internal void UpdateState()
{
var selection = _editBuffer.Selection;
if (IsInMonitorMode && InputMethod.Client is { } client)
{
InputMethod.IMM.UpdateExtractedText(InputMethod.View, ExtractedTextToken,
_editBuffer.ExtractedText);
}
var composition = _editBuffer.HasComposition ? _editBuffer.Composition!.Value : new TextSelection(-1, -1);
InputMethod.IMM.UpdateSelection(InputMethod.View, selection.Start, selection.End, composition.Start, composition.End);
}
public bool SetComposingRegion(int start, int end) public bool SetComposingRegion(int start, int end)
{ {
if (InputMethod.IsActive) if (InputMethod.IsActive)
@ -101,6 +115,8 @@ namespace Avalonia.Android.Platform.Input
} }
IsInUpdate = false; IsInUpdate = false;
} }
UpdateState();
return IsInBatchEdit; return IsInBatchEdit;
} }
@ -147,6 +163,26 @@ namespace Avalonia.Android.Platform.Input
} }
} }
var eventTime = SystemClock.UptimeMillis();
SendKeyEvent(new KeyEvent(eventTime,
eventTime,
KeyEventActions.Down,
Keycode.Enter,
0,
0,
0,
0,
KeyEventFlags.SoftKeyboard | KeyEventFlags.KeepTouchMode | KeyEventFlags.EditorAction));
SendKeyEvent(new KeyEvent(eventTime,
eventTime,
KeyEventActions.Up,
Keycode.Enter,
0,
0,
0,
0,
KeyEventFlags.SoftKeyboard | KeyEventFlags.KeepTouchMode | KeyEventFlags.EditorAction));
return InputMethod.IsActive; return InputMethod.IsActive;
} }
@ -161,19 +197,7 @@ namespace Avalonia.Android.Platform.Input
return null; return null;
} }
var extract = new ExtractedText return _editBuffer.ExtractedText;
{
Flags = 0,
PartialStartOffset = -1,
PartialEndOffset = -1,
SelectionStart = _editBuffer.Selection.Start,
SelectionEnd = _editBuffer.Selection.End,
StartOffset = 0
};
extract.Text = new SpannableString(_editBuffer.Text);
return extract;
} }
public bool PerformContextMenuAction(int id) public bool PerformContextMenuAction(int id)

11
src/Android/Avalonia.Android/Platform/Input/EditCommand.cs

@ -21,7 +21,9 @@ namespace Avalonia.Android.Platform.Input
public override void Apply(TextEditBuffer buffer) public override void Apply(TextEditBuffer buffer)
{ {
buffer.Selection = new TextSelection(Math.Max(_start, 0), Math.Min(_end, buffer.Text.Length)); var start = Math.Clamp(_start, 0, buffer.Text.Length);
var end = Math.Clamp(_end, 0, buffer.Text.Length);
buffer.Selection = new TextSelection(start, end);
} }
} }
@ -157,14 +159,11 @@ namespace Avalonia.Android.Platform.Input
{ {
if (buffer.HasComposition) if (buffer.HasComposition)
{ {
buffer.Remove(buffer.Composition!.Value.Start, buffer.Composition!.Value.End - buffer.Composition!.Value.Start); buffer.Replace(buffer.Composition!.Value.Start, buffer.Composition!.Value.End, _text);
buffer.Insert(buffer.Composition!.Value.Start, _text);
} }
else else
{ {
buffer.Remove(buffer.Selection.Start, buffer.Selection.End - buffer.Selection.Start); buffer.Replace(buffer.Selection.Start, buffer.Selection.End, _text);
buffer.Insert(buffer.Selection.Start, _text);
} }
var newCursor = _newCursorPosition > 0 ? buffer.Selection.Start + _newCursorPosition - 1 : buffer.Selection.Start + _newCursorPosition - _text.Length; var newCursor = _newCursorPosition > 0 ? buffer.Selection.Start + _newCursorPosition - 1 : buffer.Selection.Start + _newCursorPosition - _text.Length;
buffer.Selection = new TextSelection(newCursor, newCursor); buffer.Selection = new TextSelection(newCursor, newCursor);

40
src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs

@ -1,5 +1,7 @@
using System; using System;
using Android.Text;
using Android.Views; using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
@ -64,15 +66,13 @@ namespace Avalonia.Android.Platform.Input
if (HasComposition) if (HasComposition)
{ {
var start = Composition!.Value.Start; var start = Composition!.Value.Start;
Remove(start, Composition!.Value.End - start); Replace(Composition!.Value.Start, Composition!.Value.End, value ?? "");
Insert(start, value ?? "");
Composition = new TextSelection(start, start + (value?.Length ?? 0)); Composition = new TextSelection(start, start + (value?.Length ?? 0));
} }
else else
{ {
var start = Selection.Start; var start = Selection.Start;
Remove(start, Selection.End - start); Replace(start, Selection.End, value ?? "");
Insert(start, value ?? "");
Composition = new TextSelection(start, start + (value?.Length ?? 0)); Composition = new TextSelection(start, start + (value?.Length ?? 0));
} }
} }
@ -80,22 +80,42 @@ namespace Avalonia.Android.Platform.Input
public string Text => _textInputMethod.Client?.SurroundingText ?? ""; public string Text => _textInputMethod.Client?.SurroundingText ?? "";
internal void Insert(int index, string text) public ExtractedText? ExtractedText => new ExtractedText
{
Flags = Text.Contains('\n') ? 0 : ExtractedTextFlags.SingleLine,
PartialStartOffset = -1,
PartialEndOffset = Text.Length,
SelectionStart = Selection.Start,
SelectionEnd = Selection.End,
StartOffset = 0,
Text = new SpannableString(Text)
};
internal void Remove(int index, int length)
{ {
if (_textInputMethod.Client is { } client) if (_textInputMethod.Client is { } client)
{ {
client.Selection = new TextSelection(index, index); client.Selection = new TextSelection(index, index + length);
_topLevel.TextInput(text); if (length > 0)
_textInputMethod?.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
} }
} }
internal void Remove(int index, int length) internal void Replace(int start, int end, string text)
{ {
if (_textInputMethod.Client is { } client) if (_textInputMethod.Client is { } client)
{ {
client.Selection = new TextSelection(index, index + length); var realStart = Math.Min(start, end);
if (length > 0) var realEnd = Math.Max(start, end);
if (realEnd > realStart)
{
client.Selection = new TextSelection(realStart, realEnd);
_textInputMethod?.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); _textInputMethod?.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
}
_topLevel.TextInput(text);
var index = realStart + text.Length;
client.Selection = new TextSelection(index, index);
Composition = null;
} }
} }
} }

Loading…
Cancel
Save