From 257cacab183b312bbe60bd7967eee44a3ad7be85 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 30 Sep 2022 17:25:58 -0700
Subject: [PATCH] Android text input now works like iOS, where you get text in
progress and then backspaces and new text if autocomplete changes it or the
IME commits it.
---
.../main/java/org/libsdl/app/SDLActivity.java | 163 +++++-------------
1 file changed, 44 insertions(+), 119 deletions(-)
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index c9019dad6c2..61e4dfe8b47 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -1876,7 +1876,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
class SDLInputConnection extends BaseInputConnection {
protected EditText mEditText;
- protected int m_nLastContentLength = 0;
+ protected String mCommittedText = "";
public SDLInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
@@ -1913,149 +1913,74 @@ public boolean sendKeyEvent(KeyEvent event) {
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
- replaceText(text, newCursorPosition, false);
-
- return super.commitText(text, newCursorPosition);
+ if (!super.commitText(text, newCursorPosition)) {
+ return false;
+ }
+ updateText();
+ return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
- replaceText(text, newCursorPosition, true);
-
- return super.setComposingText(text, newCursorPosition);
- }
-
- @Override
- public boolean setComposingRegion(int start, int end) {
- final Editable content = getEditable();
- if (content != null) {
- int a = start;
- int b = end;
- if (a > b) {
- int tmp = a;
- a = b;
- b = tmp;
- }
-
- // Clip the end points to be within the content bounds.
- final int length = content.length();
- if (a < 0) {
- a = 0;
- }
- if (b < 0) {
- b = 0;
- }
- if (a > length) {
- a = length;
- }
- if (b > length) {
- b = length;
- }
-
- deleteText(a, b);
+ if (!super.setComposingText(text, newCursorPosition)) {
+ return false;
}
-
- return super.setComposingRegion(start, end);
+ updateText();
+ return true;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- final Editable content = getEditable();
- if (content != null) {
- int a = Selection.getSelectionStart(content);
- int b = Selection.getSelectionEnd(content);
-
- if (a > b) {
- int tmp = a;
- a = b;
- b = tmp;
- }
-
- // ignore the composing text.
- int ca = getComposingSpanStart(content);
- int cb = getComposingSpanEnd(content);
- if (cb < ca) {
- int tmp = ca;
- ca = cb;
- cb = tmp;
- }
-
- if (ca != -1 && cb != -1) {
- if (ca < a) {
- a = ca;
- }
- if (cb > b) {
- b = cb;
- }
- }
-
- if (beforeLength > 0) {
- int start = a - beforeLength;
- if (start < 0) {
- start = 0;
- }
- deleteText(start, a);
- }
+ if (!super.deleteSurroundingText(beforeLength, afterLength)) {
+ return false;
}
-
- return super.deleteSurroundingText(beforeLength, afterLength);
+ updateText();
+ return true;
}
- protected void replaceText(CharSequence text, int newCursorPosition, boolean composing) {
+ protected void updateText() {
final Editable content = getEditable();
if (content == null) {
return;
}
-
- // delete composing text set previously.
- int a = getComposingSpanStart(content);
- int b = getComposingSpanEnd(content);
-
- if (b < a) {
- int tmp = a;
- a = b;
- b = tmp;
- }
- if (a == -1 || b == -1) {
- a = Selection.getSelectionStart(content);
- b = Selection.getSelectionEnd(content);
- if (a < 0) {
- a = 0;
- }
- if (b < 0) {
- b = 0;
- }
- if (b < a) {
- int tmp = a;
- a = b;
- b = tmp;
+
+ String text = content.toString();
+ int compareLength = Math.min(text.length(), mCommittedText.length());
+ int matchLength, offset;
+
+ /* Backspace over characters that are no longer in the string */
+ for (matchLength = 0; matchLength < compareLength; ) {
+ int codePoint = mCommittedText.codePointAt(matchLength);
+ if (codePoint != text.codePointAt(matchLength)) {
+ break;
}
+ matchLength += Character.charCount(codePoint);
+ }
+ /* FIXME: This doesn't handle graphemes, like '🌬️' */
+ for (offset = matchLength; offset < mCommittedText.length(); ) {
+ int codePoint = mCommittedText.codePointAt(offset);
+ nativeGenerateScancodeForUnichar('\b');
+ offset += Character.charCount(codePoint);
}
- deleteText(a, b);
-
- if (composing) {
- nativeSetComposingText(text.toString(), newCursorPosition);
- } else {
- for (int i = 0; i < text.length(); i++) {
- char c = text.charAt(i);
- if (c == '\n') {
+ if (matchLength < text.length()) {
+ String pendingText = text.subSequence(matchLength, text.length()).toString();
+ for (offset = 0; offset < pendingText.length(); ) {
+ int codePoint = pendingText.codePointAt(offset);
+ if (codePoint == '\n') {
if (SDLActivity.onNativeSoftReturnKey()) {
return;
}
}
- ++m_nLastContentLength;
- nativeGenerateScancodeForUnichar(c);
+ /* Higher code points don't generate simulated scancodes */
+ if (codePoint < 128) {
+ nativeGenerateScancodeForUnichar((char)codePoint);
+ }
+ offset += Character.charCount(codePoint);
}
- SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
- }
- }
-
- protected void deleteText(int start, int end) {
- while (m_nLastContentLength > start) {
- --m_nLastContentLength;
- nativeGenerateScancodeForUnichar('\b');
+ SDLInputConnection.nativeCommitText(pendingText, 0);
}
+ mCommittedText = text;
}
public static native void nativeCommitText(String text, int newCursorPosition);