summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@digia.com>2013-10-07 12:45:36 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-09 15:38:24 +0200
commit85fc94db1bb2189ea379033aec23b1779b809cc0 (patch)
treea9470ce7421fa508fc19e314526e8f6b0388d8c4 /src
parentc376c272e6a643c934b546aa3971e76a4e275b07 (diff)
Android: Fix backspace bug with 4.3 stock keyboard
The Android 4.3 keyboard will cause setComposingRegion() to be called when backspacing over an existing word. If we don't implement that, the editor will be out of sync with the input method. Task-number: QTBUG-32955 Change-Id: I6c4ff786269a4e74c70a093c5f03c4c5a5727dd5 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java7
-rw-r--r--src/plugins/platforms/android/src/qandroidinputcontext.cpp112
-rw-r--r--src/plugins/platforms/android/src/qandroidinputcontext.h2
3 files changed, 118 insertions, 3 deletions
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
index 3bcec030b5..f251369737 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
@@ -73,6 +73,7 @@ class QtNativeInputConnection
static native String getTextAfterCursor(int length, int flags);
static native String getTextBeforeCursor(int length, int flags);
static native boolean setComposingText(String text, int newCursorPosition);
+ static native boolean setComposingRegion(int start, int end);
static native boolean setSelection(int start, int end);
static native boolean selectAll();
static native boolean cut();
@@ -237,6 +238,12 @@ public class QtInputConnection extends BaseInputConnection
}
@Override
+ public boolean setComposingRegion(int start, int end)
+ {
+ return QtNativeInputConnection.setComposingRegion(start, end);
+ }
+
+ @Override
public boolean setSelection(int start, int end)
{
return QtNativeInputConnection.setSelection(start, end);
diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.cpp b/src/plugins/platforms/android/src/qandroidinputcontext.cpp
index 1981ac0b75..386c8e006a 100644
--- a/src/plugins/platforms/android/src/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/src/qandroidinputcontext.cpp
@@ -54,6 +54,8 @@
#include <QTextCharFormat>
+#include <QDebug>
+
QT_BEGIN_NAMESPACE
static QAndroidInputContext *m_androidInputContext = 0;
@@ -78,6 +80,9 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new
QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text));
env->ReleaseStringChars(text, jstr);
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ COMMIT" << str;
+#endif
return m_androidInputContext->commitText(str, newCursorPosition);
}
@@ -86,6 +91,9 @@ static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint le
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ DELETE" << leftLength << rightLength;
+#endif
return m_androidInputContext->deleteSurroundingText(leftLength, rightLength);
}
@@ -94,6 +102,9 @@ static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ FINISH";
+#endif
return m_androidInputContext->finishComposingText();
}
@@ -110,6 +121,9 @@ static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars,
if (!m_androidInputContext)
return 0;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ GETEX";
+#endif
const QAndroidInputContext::ExtractedText &extractedText =
m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags);
@@ -133,6 +147,9 @@ static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, jint flags)
return 0;
const QString &text = m_androidInputContext->getSelectedText(flags);
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ GETSEL" << text;
+#endif
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -142,6 +159,9 @@ static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, ji
return 0;
const QString &text = m_androidInputContext->getTextAfterCursor(length, flags);
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ GET" << length << text;
+#endif
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -151,6 +171,9 @@ static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, j
return 0;
const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags);
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ GET" << length << text;
+#endif
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -164,14 +187,32 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji
QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text));
env->ReleaseStringChars(text, jstr);
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ SET" << str;
+#endif
return m_androidInputContext->setComposingText(str, newCursorPosition);
}
+static jboolean setComposingRegion(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint end)
+{
+ if (!m_androidInputContext)
+ return JNI_FALSE;
+
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ SETR" << start << end;
+#endif
+ return m_androidInputContext->setComposingRegion(start, end);
+}
+
+
static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint end)
{
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ SETSEL" << start << end;
+#endif
return m_androidInputContext->setSelection(start, end);
}
@@ -180,6 +221,9 @@ static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ SELALL";
+#endif
return m_androidInputContext->selectAll();
}
@@ -188,6 +232,9 @@ static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@";
+#endif
return m_androidInputContext->cut();
}
@@ -196,6 +243,9 @@ static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@";
+#endif
return m_androidInputContext->copy();
}
@@ -204,6 +254,9 @@ static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@";
+#endif
return m_androidInputContext->copyURL();
}
@@ -212,6 +265,9 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@";
+#endif
return m_androidInputContext->paste();
}
@@ -226,6 +282,7 @@ static JNINativeMethod methods[] = {
{"getTextAfterCursor", "(II)Ljava/lang/String;", (void *)getTextAfterCursor},
{"getTextBeforeCursor", "(II)Ljava/lang/String;", (void *)getTextBeforeCursor},
{"setComposingText", "(Ljava/lang/String;I)Z", (void *)setComposingText},
+ {"setComposingRegion", "(II)Z", (void *)setComposingRegion},
{"setSelection", "(II)Z", (void *)setSelection},
{"selectAll", "()Z", (void *)selectAll},
{"cut", "()Z", (void *)cut},
@@ -235,7 +292,8 @@ static JNINativeMethod methods[] = {
};
-QAndroidInputContext::QAndroidInputContext():QPlatformInputContext()
+QAndroidInputContext::QAndroidInputContext()
+ : QPlatformInputContext(), m_blockUpdateSelection(false)
{
QtAndroid::AttachedJNIEnv env;
if (!env.jniEnv)
@@ -340,7 +398,7 @@ void QAndroidInputContext::commit()
void QAndroidInputContext::updateCursorPosition()
{
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (!query.isNull()) {
+ if (!query.isNull() && !m_blockUpdateSelection) {
const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text
}
@@ -557,7 +615,7 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur
sendInputMethodEvent(&event);
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (!query.isNull()) {
+ if (!query.isNull() && !m_blockUpdateSelection) {
int cursorPos = query->value(Qt::ImCursorPosition).toInt();
int preeditLength = text.length();
QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength);
@@ -566,6 +624,54 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur
return JNI_TRUE;
}
+// Android docs say:
+// * start may be after end, same meaning as if swapped
+// * this function must not trigger updateSelection
+// * if start == end then we should stop composing
+jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
+{
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (query.isNull())
+ return JNI_FALSE;
+
+ if (start > end)
+ qSwap(start, end);
+
+ /*
+ start and end are cursor positions, not character positions,
+ i.e. selecting the first character is done by start == 0 and end == 1,
+ and start == end means no character selected
+
+ Therefore, the length of the region is end - start
+ */
+ int length = end - start;
+
+ bool updateSelectionWasBlocked = m_blockUpdateSelection;
+ m_blockUpdateSelection = true;
+
+ QString text = query->value(Qt::ImSurroundingText).toString();
+ m_composingText = text.mid(start, length);
+
+ //in the Qt text controls, cursor pos is the start of the preedit
+ int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ int relativeStart = start - cursorPos;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+
+ // Show compose text underlined
+ QTextCharFormat underlined;
+ underlined.setFontUnderline(true);
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, length,
+ QVariant(underlined)));
+
+ QInputMethodEvent event(m_composingText, attributes);
+ event.setCommitString(QString(), relativeStart, length);
+ sendInputMethodEvent(&event);
+
+ m_blockUpdateSelection = updateSelectionWasBlocked;
+ return JNI_TRUE;
+}
+
jboolean QAndroidInputContext::setSelection(jint start, jint end)
{
QList<QInputMethodEvent::Attribute> attributes;
diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.h b/src/plugins/platforms/android/src/qandroidinputcontext.h
index 482aeffa50..d19dcc384b 100644
--- a/src/plugins/platforms/android/src/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/src/qandroidinputcontext.h
@@ -105,6 +105,7 @@ public:
QString getTextAfterCursor(jint length, jint flags);
QString getTextBeforeCursor(jint length, jint flags);
jboolean setComposingText(const QString &text, jint newCursorPosition);
+ jboolean setComposingRegion(jint start, jint end);
jboolean setSelection(jint start, jint end);
jboolean selectAll();
jboolean cut();
@@ -125,6 +126,7 @@ private:
ExtractedText m_extractedText;
QString m_composingText;
QMetaObject::Connection m_updateCursorPosConnection;
+ bool m_blockUpdateSelection;
};
QT_END_NAMESPACE