Browse Source

android - use batch edits to defer updates to input manager

pull/12270/head
Emmanuel Hansen 3 years ago
parent
commit
24e2321bf1
  1. 29
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  2. 85
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

29
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -18,6 +18,8 @@ namespace Avalonia.Android
public bool IsActive { get; }
public InputMethodManager IMM { get; }
void OnBatchEditedEnded();
}
enum CustomImeFlags
@ -103,6 +105,13 @@ namespace Avalonia.Android
}
private void _client_SelectionChanged(object sender, EventArgs e)
{
if (_inputConnection.IsInBatchEdit)
return;
OnSelectionChanged();
}
private void OnSelectionChanged()
{
var selection = Client.Selection;
@ -112,6 +121,22 @@ namespace Avalonia.Android
}
private void _client_SurroundingTextChanged(object sender, EventArgs e)
{
if (_inputConnection.IsInBatchEdit)
return;
OnSurroundingTextChanged();
}
public void OnBatchEditedEnded()
{
if (_inputConnection.IsInBatchEdit)
return;
OnSurroundingTextChanged();
OnSelectionChanged();
}
private void OnSurroundingTextChanged()
{
var surroundingText = _client.SurroundingText ?? "";
var editableText = _inputConnection.EditableWrapper.ToString();
@ -133,7 +158,7 @@ namespace Avalonia.Android
var longerLength = Math.Max(surroundingText.Length, editableText.Length);
for(int i = 0; i < longerLength; i++)
for (int i = 0; i < longerLength; i++)
{
if (surroundingText.Length == i || editableText.Length == i || surroundingText[i] != editableText[i])
{
@ -142,7 +167,7 @@ namespace Avalonia.Android
}
}
var diffString = surroundingText.Substring(index, surroundingText.Length - index);
var diffString = surroundingText.Substring(index, surroundingText.Length - index);
return (index, diffString);
}

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

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
using Android.App;
using Android.Content;
using Android.Graphics;
@ -454,6 +455,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_inputConnection = inputConnection;
}
public TextSelection CurrentSelection => new TextSelection(Selection.GetSelectionStart(this), Selection.GetSelectionEnd(this));
public TextSelection CurrentComposition => new TextSelection(BaseInputConnection.GetComposingSpanStart(this), BaseInputConnection.GetComposingSpanEnd(this));
public bool IgnoreChange { get; set; }
public override IEditable Replace(int start, int end, ICharSequence tb)
@ -480,11 +484,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
_inputConnection.InputMethod.Client.Selection = new TextSelection(start, end);
}
public override void SetSpan(Java.Lang.Object what, int start, int end, [GeneratedEnum] SpanTypes flags)
{
base.SetSpan(what, start, end, flags);
}
}
internal class AvaloniaInputConnection : BaseInputConnection
@ -493,8 +492,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
private readonly IAndroidInputMethod _inputMethod;
private readonly EditableWrapper _editable;
private bool _commitInProgress;
private (int Start, int End)? _composingRegion;
private TextSelection _selection;
private int _batchLevel = 0;
public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true)
{
@ -513,36 +511,64 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public TopLevelImpl Toplevel => _toplevel;
public bool IsInBatchEdit => _batchLevel > 0;
public override bool SetComposingRegion(int start, int end)
{
_composingRegion = new(start, end);
return base.SetComposingRegion(start, end);
}
public override bool SetComposingText(ICharSequence text, int newCursorPosition)
{
if(_composingRegion != null)
BeginBatchEdit();
_editable.IgnoreChange = true;
try
{
// Select the composing region.
InputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End);
}
var compositionText = text.SubSequence(0, text.Length());
if (_editable.CurrentComposition.Start > -1)
{
// Select the composing region.
InputMethod.Client.Selection = new TextSelection(_editable.CurrentComposition.Start, _editable.CurrentComposition.End);
}
var compositionText = text.SubSequence(0, text.Length());
if (_inputMethod.IsActive && !_commitInProgress)
{
if (string.IsNullOrEmpty(compositionText))
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
if (_inputMethod.IsActive && !_commitInProgress)
else
_toplevel.TextInput(compositionText);
}
base.SetComposingText(text, newCursorPosition);
}
finally
{
if (string.IsNullOrEmpty(compositionText))
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
_editable.IgnoreChange = false;
else
_toplevel.TextInput(compositionText);
EndBatchEdit();
}
return true;
}
public override bool BeginBatchEdit()
{
_batchLevel = Interlocked.Increment(ref _batchLevel);
return base.BeginBatchEdit();
}
public override bool EndBatchEdit()
{
_batchLevel = Interlocked.Decrement(ref _batchLevel);
_inputMethod.OnBatchEditedEnded();
return base.EndBatchEdit();
}
public override bool CommitText(ICharSequence text, int newCursorPosition)
{
BeginBatchEdit();
_commitInProgress = true;
var ret = base.CommitText(text, newCursorPosition);
@ -551,22 +577,15 @@ namespace Avalonia.Android.Platform.SkiaPlatform
if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText))
{
_toplevel.TextInput(committedText);
_composingRegion = null;
_toplevel.TextInput(committedText);
}
_commitInProgress = false;
EndBatchEdit();
return ret;
}
public override bool FinishComposingText()
{
_composingRegion = null;
return base.FinishComposingText();
}
public override bool DeleteSurroundingText(int beforeLength, int afterLength)
{
if (InputMethod.IsActive)
@ -577,7 +596,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
if (InputMethod.IsActive)
{
var selection = _selection;
var selection = _editable.CurrentSelection;
InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.End + afterLength);
@ -589,12 +608,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
return result;
}
public override bool SetSelection(int start, int end)
{
_selection = new TextSelection(start, end);
return base.SetSelection(start, end);
}
public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode)
{
switch (actionCode)
@ -628,7 +641,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
return null;
}
var selection = _selection;
var selection = _editable.CurrentSelection;
ExtractedText extract = new ExtractedText
{

Loading…
Cancel
Save