diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs index bbcf8276f1..cb105197cb 100644 --- a/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs @@ -82,21 +82,7 @@ namespace Avalonia.Android.Platform.Input _imm.ShowSoftInput(_host, ShowFlags.Implicit); - var selection = Client.Selection; - - _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); + _inputConnection?.UpdateState(); _client.SurroundingTextChanged += _client_SurroundingTextChanged; _client.SelectionChanged += _client_SelectionChanged; @@ -150,27 +136,7 @@ namespace Avalonia.Android.Platform.Input private void OnSurroundingTextChanged() { - if (_client is null || _inputConnection is null || _inputConnection.IsInUpdate) - { - 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); - } + _inputConnection?.UpdateState(); } public void SetCursorRect(Rect rect) diff --git a/src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs b/src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs index 7519ab15d1..2169936b56 100644 --- a/src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs +++ b/src/Android/Avalonia.Android/Platform/Input/AvaloniaInputConnection.cs @@ -44,6 +44,20 @@ namespace Avalonia.Android.Platform.Input 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) { if (InputMethod.IsActive) @@ -101,6 +115,8 @@ namespace Avalonia.Android.Platform.Input } IsInUpdate = false; } + + UpdateState(); 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; } @@ -161,19 +197,7 @@ namespace Avalonia.Android.Platform.Input return null; } - var extract = new 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; + return _editBuffer.ExtractedText; } public bool PerformContextMenuAction(int id) diff --git a/src/Android/Avalonia.Android/Platform/Input/EditCommand.cs b/src/Android/Avalonia.Android/Platform/Input/EditCommand.cs index 1e9ce5f19f..c110807dd9 100644 --- a/src/Android/Avalonia.Android/Platform/Input/EditCommand.cs +++ b/src/Android/Avalonia.Android/Platform/Input/EditCommand.cs @@ -21,7 +21,9 @@ namespace Avalonia.Android.Platform.Input 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) { - buffer.Remove(buffer.Composition!.Value.Start, buffer.Composition!.Value.End - buffer.Composition!.Value.Start); - buffer.Insert(buffer.Composition!.Value.Start, _text); + buffer.Replace(buffer.Composition!.Value.Start, buffer.Composition!.Value.End, _text); } else { - buffer.Remove(buffer.Selection.Start, buffer.Selection.End - buffer.Selection.Start); - buffer.Insert(buffer.Selection.Start, _text); - + buffer.Replace(buffer.Selection.Start, buffer.Selection.End, _text); } var newCursor = _newCursorPosition > 0 ? buffer.Selection.Start + _newCursorPosition - 1 : buffer.Selection.Start + _newCursorPosition - _text.Length; buffer.Selection = new TextSelection(newCursor, newCursor); diff --git a/src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs b/src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs index 21dea56fb4..95c631c10d 100644 --- a/src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs +++ b/src/Android/Avalonia.Android/Platform/Input/TextEditBuffer.cs @@ -1,5 +1,7 @@ using System; +using Android.Text; using Android.Views; +using Android.Views.InputMethods; using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Input.TextInput; @@ -64,15 +66,13 @@ namespace Avalonia.Android.Platform.Input if (HasComposition) { var start = Composition!.Value.Start; - Remove(start, Composition!.Value.End - start); - Insert(start, value ?? ""); + Replace(Composition!.Value.Start, Composition!.Value.End, value ?? ""); Composition = new TextSelection(start, start + (value?.Length ?? 0)); } else { var start = Selection.Start; - Remove(start, Selection.End - start); - Insert(start, value ?? ""); + Replace(start, Selection.End, value ?? ""); Composition = new TextSelection(start, start + (value?.Length ?? 0)); } } @@ -80,22 +80,42 @@ namespace Avalonia.Android.Platform.Input 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) { - client.Selection = new TextSelection(index, index); - _topLevel.TextInput(text); + client.Selection = new TextSelection(index, index + length); + 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) { - client.Selection = new TextSelection(index, index + length); - if (length > 0) + var realStart = Math.Min(start, end); + var realEnd = Math.Max(start, end); + if (realEnd > realStart) + { + client.Selection = new TextSelection(realStart, realEnd); _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; } } }