summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java2
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java1
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp17
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp86
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h7
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp9
-rw-r--r--src/widgets/widgets/qplaintextedit.h1
7 files changed, 88 insertions, 35 deletions
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
index aa18dfc2d1..ea8e5cd44c 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
@@ -301,6 +301,8 @@ public class QtActivityDelegate
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
case InputMethodManager.RESULT_SHOWN:
+ QtNativeInputConnection.updateCursorPosition();
+ //FALLTHROUGH
case InputMethodManager.RESULT_UNCHANGED_SHOWN:
setKeyboardVisibility(true);
break;
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 4b2d50ca1f..5fdeb12c15 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java
@@ -78,6 +78,7 @@ class QtNativeInputConnection
static native boolean copy();
static native boolean copyURL();
static native boolean paste();
+ static native boolean updateCursorPosition();
}
class HideKeyboardRunnable implements Runnable {
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index 9d605c7a17..9efdcad158 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -67,8 +67,6 @@ namespace QtAndroidInput
static QPointer<QWindow> m_mouseGrabber;
- static int m_lastCursorPos = -1;
-
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
{
AttachedJNIEnv env;
@@ -78,21 +76,6 @@ namespace QtAndroidInput
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
qDebug() << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd;
#endif
- if (candidatesStart == -1 && candidatesEnd == -1 && selStart == selEnd) {
- // Qt only gives us position inside the block, so if we move to the
- // same position in another block, the Android keyboard will believe
- // we have not changed position, and be terribly confused.
- if (selStart == m_lastCursorPos) {
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << ">>> FAKEUPDATESELECTION" << selStart+1;
-#endif
- env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID,
- selStart+1, selEnd+1, candidatesStart, candidatesEnd);
- }
- m_lastCursorPos = selStart;
- } else {
- m_lastCursorPos = -1;
- }
env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID,
selStart, selEnd, candidatesStart, candidatesEnd);
}
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index 326972e71e..bfb13811e3 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -81,7 +81,7 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new
env->ReleaseStringChars(text, jstr);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ COMMIT" << str;
+ qDebug() << "@@@ COMMIT" << str << newCursorPosition;
#endif
return m_androidInputContext->commitText(str, newCursorPosition);
}
@@ -160,7 +160,7 @@ static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, ji
const QString &text = m_androidInputContext->getTextAfterCursor(length, flags);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GET" << length << text;
+ qDebug() << "@@@ GETA" << length << text;
#endif
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -172,7 +172,7 @@ static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, j
const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GET" << length << text;
+ qDebug() << "@@@ GETB" << length << text;
#endif
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -188,7 +188,7 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji
env->ReleaseStringChars(text, jstr);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ SET" << str;
+ qDebug() << "@@@ SET" << str << newCursorPosition;
#endif
return m_androidInputContext->setComposingText(str, newCursorPosition);
}
@@ -271,6 +271,18 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/)
return m_androidInputContext->paste();
}
+static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/)
+{
+ if (!m_androidInputContext)
+ return JNI_FALSE;
+
+#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
+ qDebug() << "@@@ UPDATECURSORPOS";
+#endif
+ m_androidInputContext->updateCursorPosition();
+ return true;
+}
+
static JNINativeMethod methods[] = {
{"commitText", "(Ljava/lang/String;I)Z", (void *)commitText},
@@ -288,7 +300,8 @@ static JNINativeMethod methods[] = {
{"cut", "()Z", (void *)cut},
{"copy", "()Z", (void *)copy},
{"copyURL", "()Z", (void *)copyURL},
- {"paste", "()Z", (void *)paste}
+ {"paste", "()Z", (void *)paste},
+ {"updateCursorPosition", "()Z", (void *)updateCursorPosition}
};
@@ -404,7 +417,9 @@ void QAndroidInputContext::updateCursorPosition()
{
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (!query.isNull() && !m_blockUpdateSelection) {
- const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ // make sure it also works with editors that have not been updated to the new API
+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
+ const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt();
QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text
}
}
@@ -422,9 +437,9 @@ void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorP
#warning TODO Handle at least QInputMethod::ContextMenu action
Q_UNUSED(action)
Q_UNUSED(cursorPosition)
-
- if (action == QInputMethod::Click)
- commit();
+ //### click should be passed to the IM, but in the meantime it's better to ignore it than to do something wrong
+ // if (action == QInputMethod::Click)
+ // commit();
}
QRectF QAndroidInputContext::keyboardRect() const
@@ -573,6 +588,12 @@ QString QAndroidInputContext::getSelectedText(jint /*flags*/)
QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/)
{
+ QVariant textAfter = queryFocusObjectThreadSafe(Qt::ImTextAfterCursor, QVariant(length));
+ if (textAfter.isValid()) {
+ return textAfter.toString().left(length);
+ }
+
+ //compatibility code for old controls that do not implement the new API
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (query.isNull())
return QString();
@@ -587,15 +608,21 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/)
QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/)
{
+ QVariant textBefore = queryFocusObjectThreadSafe(Qt::ImTextBeforeCursor, QVariant(length));
+ if (textBefore.isValid()) {
+ return textBefore.toString().left(length);
+ }
+
+ //compatibility code for old controls that do not implement the new API
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (query.isNull())
return QString();
+ int cursorPos = query->value(Qt::ImCursorPosition).toInt();
QString text = query->value(Qt::ImSurroundingText).toString();
if (!text.length())
return text;
- int cursorPos = query->value(Qt::ImCursorPosition).toInt();
const int wordLeftPos = cursorPos - length;
return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos);
}
@@ -621,8 +648,9 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (!query.isNull() && !m_blockUpdateSelection) {
- int cursorPos = query->value(Qt::ImCursorPosition).toInt();
- int preeditLength = text.length();
+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
+ const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt();
+ const int preeditLength = text.length();
QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength);
}
@@ -650,16 +678,19 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
Therefore, the length of the region is end - start
*/
int length = end - start;
+ int localPos = query->value(Qt::ImCursorPosition).toInt();
+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
+ int blockPosition = absolutePos.isValid() ? absolutePos.toInt() - localPos : 0;
+ int localStart = start - blockPosition; // Qt uses position inside block
bool updateSelectionWasBlocked = m_blockUpdateSelection;
m_blockUpdateSelection = true;
QString text = query->value(Qt::ImSurroundingText).toString();
- m_composingText = text.mid(start, length);
+ m_composingText = text.mid(localStart, 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;
+ //in the Qt text controls, the cursor position is the start of the preedit
+ int relativeStart = localStart - localPos;
QList<QInputMethodEvent::Attribute> attributes;
@@ -669,6 +700,9 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, length,
QVariant(underlined)));
+ // Keep the cursor position unchanged (don't move to end of preedit)
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, localPos - localStart, length, QVariant()));
+
QInputMethodEvent event(m_composingText, attributes);
event.setCommitString(QString(), relativeStart, length);
sendInputMethodEvent(&event);
@@ -720,6 +754,26 @@ jboolean QAndroidInputContext::paste()
return JNI_FALSE;
}
+
+Q_INVOKABLE QVariant QAndroidInputContext::queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument)
+{
+ return QInputMethod::queryFocusObject(query, argument);
+}
+
+QVariant QAndroidInputContext::queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument)
+{
+ bool inMainThread = qGuiApp->thread() == QThread::currentThread();
+ QVariant retval;
+
+ QMetaObject::invokeMethod(this, "queryFocusObjectUnsafe",
+ inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(QVariant, retval),
+ Q_ARG(Qt::InputMethodQuery, query),
+ Q_ARG(QVariant, argument));
+
+ return retval;
+}
+
QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries)
{
#warning TODO make qGuiApp->focusObject() thread safe !!!
diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h
index 041bd0dc49..2fb54a97c4 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -114,14 +114,19 @@ public:
jboolean copyURL();
jboolean paste();
+public slots:
+ void updateCursorPosition();
+
private:
QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
void sendInputMethodEvent(QInputMethodEvent *event);
+ Q_INVOKABLE QVariant queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument);
+ QVariant queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument);
+
private slots:
virtual void sendEvent(QObject *receiver, QInputMethodEvent *event);
virtual void sendEvent(QObject *receiver, QInputMethodQueryEvent *event);
- void updateCursorPosition();
private:
ExtractedText m_extractedText;
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
index d51dce4765..beb44e602e 100644
--- a/src/widgets/widgets/qplaintextedit.cpp
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -2185,6 +2185,13 @@ void QPlainTextEdit::scrollContentsBy(int dx, int /*dy*/)
*/
QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
{
+ return inputMethodQuery(property, QVariant());
+}
+
+/*!\internal
+ */
+QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+{
Q_D(const QPlainTextEdit);
QVariant v;
switch (property) {
@@ -2192,7 +2199,7 @@ QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
v = QWidget::inputMethodQuery(property);
break;
default:
- v = d->control->inputMethodQuery(property, QVariant());
+ v = d->control->inputMethodQuery(property, argument);
const QPoint offset(-d->horizontalOffset(), -0);
if (v.type() == QVariant::RectF)
v = v.toRectF().toRect().translated(offset);
diff --git a/src/widgets/widgets/qplaintextedit.h b/src/widgets/widgets/qplaintextedit.h
index 1fb4625fb1..54cd3e14ed 100644
--- a/src/widgets/widgets/qplaintextedit.h
+++ b/src/widgets/widgets/qplaintextedit.h
@@ -185,6 +185,7 @@ public:
int blockCount() const;
QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+ Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const;
public Q_SLOTS: