Bug 1593683 - Part 2. Handle arrow left/right key when having composition. r=geckoview-reviewers,esawin

Most IMEs handle arrow key, then set caret position by IME. But GBoard doesn't
handle it. GBoard will dispatch key event to application for arrow left/right
even if having IME composition.

Since Gecko doesn't dispatch key press during IME composition due to DOM UI
events spec, we have to emulate arrow key's behaviour.

And, `GeckoEditable` has a hack that composition text is committed when
dispatching key event. This hack is unnecessary after landing
bug 1613804 that `InputConnection.finishComposingText` is implemented.

Differential Revision: https://phabricator.services.mozilla.com/D76658
This commit is contained in:
Makoto Kato 2020-06-01 08:36:29 +00:00
Родитель 83fbd04e63
Коммит 0917864e45
3 изменённых файлов: 86 добавлений и 1 удалений

Просмотреть файл

@ -791,4 +791,24 @@ class TextInputDelegateTest : BaseSessionTest() {
assertText("commit abc", ic, "abc")
}
// Bug 1593683 - Cursor is jumping when using the arrow keys in input field on GBoard
@WithDisplay(width = 512, height = 512) // Child process updates require having a display.
@Test fun inputConnection_bug1593683() {
setupContent("")
val ic = mainSession.textInput.onCreateInputConnection(EditorInfo())!!
setComposingText(ic, "foo", 1)
assertTextAndSelectionAt("Can set the composing text", ic, "foo", 3)
// Arrow key should keep composition then move caret
pressKey(ic, KeyEvent.KEYCODE_DPAD_LEFT)
pressKey(ic, KeyEvent.KEYCODE_DPAD_LEFT)
pressKey(ic, KeyEvent.KEYCODE_DPAD_LEFT)
assertSelection("IME caret is moved to top", ic, 0, 0, /* checkGecko */ false)
setComposingText(ic, "bar", 1)
finishComposingText(ic)
assertText("commit abc", ic, "bar")
}
}

Просмотреть файл

@ -1014,10 +1014,52 @@ import android.view.inputmethod.EditorInfo;
return;
}
// Most IMEs handle arrow key, then set caret position. But GBoard
// doesn't handle it. GBoard will dispatch KeyEvent for arrow left/right
// even if having IME composition.
// Since Gecko doesn't dispatch keypress during IME composition due to
// DOM UI events spec, we have to emulate arrow key's behaviour.
boolean commitCompositionBeforeKeyEvent = action == KeyEvent.ACTION_DOWN;
if (isComposing(mText.getShadowText()) &&
action == KeyEvent.ACTION_DOWN && event.hasNoModifiers()) {
final int selStart = Selection.getSelectionStart(mText.getShadowText());
final int selEnd = Selection.getSelectionEnd(mText.getShadowText());
if (selStart == selEnd) {
// If dispatching arrow left/right key into composition,
// we update IME caret.
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (getComposingStart(mText.getShadowText()) < selStart) {
Selection.setSelection(getEditable(), selStart - 1, selStart - 1);
mNeedUpdateComposition = true;
commitCompositionBeforeKeyEvent = false;
} else if (selStart == 0) {
// Keep current composition
commitCompositionBeforeKeyEvent = false;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (getComposingEnd(mText.getShadowText()) > selEnd) {
Selection.setSelection(getEditable(), selStart + 1, selStart + 1);
mNeedUpdateComposition = true;
commitCompositionBeforeKeyEvent = false;
} else if (selEnd == mText.getShadowText().length()) {
// Keep current composition
commitCompositionBeforeKeyEvent = false;
}
break;
}
}
}
// Focused; key event may go to chrome window or to content window.
if (mNeedUpdateComposition) {
icMaybeSendComposition(mText.getShadowText(), SEND_COMPOSITION_NOTIFY_GECKO);
}
if (commitCompositionBeforeKeyEvent) {
mFocusedChild.onImeRequestCommit();
}
onKeyEvent(mFocusedChild, event, action, metaState,
/* isSynthesizedImeKey */ false);
icOfferAction(new Action(Action.TYPE_EVENT));
@ -2251,5 +2293,29 @@ import android.view.inputmethod.EditorInfo;
return false;
}
private static int getComposingStart(final Spanned text) {
int composingStart = Integer.MAX_VALUE;
final Object[] spans = text.getSpans(0, text.length(), Object.class);
for (final Object span : spans) {
if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
composingStart = Math.min(composingStart, text.getSpanStart(span));
}
}
return composingStart;
}
private static int getComposingEnd(final Spanned text) {
int composingEnd = -1;
final Object[] spans = text.getSpans(0, text.length(), Object.class);
for (final Object span : spans) {
if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
composingEnd = Math.max(composingEnd, text.getSpanEnd(span));
}
}
return composingEnd;
}
}

Просмотреть файл

@ -502,7 +502,6 @@ void GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
// these keys are dispatched in sequence.
mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(event.Duplicate()));
} else {
RemoveComposition();
NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(dispatcher));
dispatcher->DispatchKeyboardEvent(msg, event, status);
if (widget->Destroyed() || status == nsEventStatus_eConsumeNoDefault) {