diff options
Diffstat (limited to 'src/plugins/platforms/android/qandroidinputcontext.cpp')
-rw-r--r-- | src/plugins/platforms/android/qandroidinputcontext.cpp | 368 |
1 files changed, 221 insertions, 147 deletions
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index c46a435db1..c5cd0b92d9 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -64,6 +64,29 @@ QT_BEGIN_NAMESPACE +template <typename T> +class ScopedValueChangeBack +{ +public: + ScopedValueChangeBack(T &variable, T newValue) + : m_oldValue(variable), + m_variable(variable) + { + m_variable = newValue; + } + inline void setOldValue() + { + m_variable = m_oldValue; + } + ~ScopedValueChangeBack() + { + setOldValue(); + } +private: + T m_oldValue; + T &m_variable; +}; + static QAndroidInputContext *m_androidInputContext = 0; static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection"; static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText"; @@ -76,6 +99,11 @@ static jfieldID m_selectionStartFieldID = 0; static jfieldID m_startOffsetFieldID = 0; static jfieldID m_textFieldID = 0; +static void runOnQtThread(const std::function<void()> &func) +{ + QMetaObject::invokeMethod(m_androidInputContext, "safeCall", Qt::BlockingQueuedConnection, Q_ARG(std::function<void()>, func)); +} + static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) { if (!m_androidInputContext) @@ -84,10 +112,9 @@ static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ BEGINBATCH"); #endif - - return m_androidInputContext->beginBatchEdit(); - - return JNI_TRUE; + jboolean res; + runOnQtThread([&res]{res = m_androidInputContext->beginBatchEdit();}); + return res; } static jboolean endBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) @@ -99,9 +126,9 @@ static jboolean endBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) qDebug("@@@ ENDBATCH"); #endif - return m_androidInputContext->endBatchEdit(); - - return JNI_TRUE; + jboolean res; + runOnQtThread([&res]{res = m_androidInputContext->endBatchEdit();}); + return res; } @@ -118,7 +145,9 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ COMMIT" << str << newCursorPosition; #endif - return m_androidInputContext->commitText(str, newCursorPosition); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->commitText(str, newCursorPosition);}); + return res; } static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint leftLength, jint rightLength) @@ -129,7 +158,9 @@ static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint le #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ DELETE" << leftLength << rightLength; #endif - return m_androidInputContext->deleteSurroundingText(leftLength, rightLength); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->deleteSurroundingText(leftLength, rightLength);}); + return res; } static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/) @@ -140,7 +171,9 @@ static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ FINISH"); #endif - return m_androidInputContext->finishComposingText(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->finishComposingText();}); + return res; } static jint getCursorCapsMode(JNIEnv */*env*/, jobject /*thiz*/, jint reqModes) @@ -148,7 +181,9 @@ static jint getCursorCapsMode(JNIEnv */*env*/, jobject /*thiz*/, jint reqModes) if (!m_androidInputContext) return 0; - return m_androidInputContext->getCursorCapsMode(reqModes); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->getCursorCapsMode(reqModes);}); + return res; } static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars, int hintMaxLines, jint flags) @@ -156,8 +191,8 @@ static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars, if (!m_androidInputContext) return 0; - const QAndroidInputContext::ExtractedText &extractedText = - m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags); + QAndroidInputContext::ExtractedText extractedText; + runOnQtThread([&]{extractedText = m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags);}); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ GETEX" << hintMaxChars << hintMaxLines << QString::fromLatin1("0x") + QString::number(flags,16) << extractedText.text << "partOff:" << extractedText.partialStartOffset << extractedText.partialEndOffset << "sel:" << extractedText.selectionStart << extractedText.selectionEnd << "offset:" << extractedText.startOffset; @@ -182,7 +217,8 @@ static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, jint flags) if (!m_androidInputContext) return 0; - const QString &text = m_androidInputContext->getSelectedText(flags); + QString text; + runOnQtThread([&]{text = m_androidInputContext->getSelectedText(flags);}); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ GETSEL" << text; #endif @@ -196,7 +232,8 @@ static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, ji if (!m_androidInputContext) return 0; - const QString &text = m_androidInputContext->getTextAfterCursor(length, flags); + QString text; + runOnQtThread([&]{text = m_androidInputContext->getTextAfterCursor(length, flags);}); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ GETA" << length << text; #endif @@ -208,7 +245,8 @@ static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, j if (!m_androidInputContext) return 0; - const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags); + QString text; + runOnQtThread([&]{text = m_androidInputContext->getTextBeforeCursor(length, flags);}); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ GETB" << length << text; #endif @@ -228,7 +266,9 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ SET" << str << newCursorPosition; #endif - return m_androidInputContext->setComposingText(str, newCursorPosition); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->setComposingText(str, newCursorPosition);}); + return res; } static jboolean setComposingRegion(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint end) @@ -239,7 +279,9 @@ static jboolean setComposingRegion(JNIEnv */*env*/, jobject /*thiz*/, jint start #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ SETR" << start << end; #endif - return m_androidInputContext->setComposingRegion(start, end); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->setComposingRegion(start, end);}); + return res; } @@ -251,7 +293,10 @@ static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ SETSEL" << start << end; #endif - return m_androidInputContext->setSelection(start, end); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->setSelection(start, end);}); + return res; + } static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/) @@ -262,7 +307,9 @@ static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ SELALL"); #endif - return m_androidInputContext->selectAll(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->selectAll();}); + return res; } static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/) @@ -273,7 +320,9 @@ static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@"); #endif - return m_androidInputContext->cut(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->cut();}); + return res; } static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/) @@ -284,7 +333,9 @@ static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@"); #endif - return m_androidInputContext->copy(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->copy();}); + return res; } static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/) @@ -295,7 +346,9 @@ static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@"); #endif - return m_androidInputContext->copyURL(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->copyURL();}); + return res; } static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/) @@ -306,7 +359,9 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ PASTE"); #endif - return m_androidInputContext->paste(); + jboolean res; + runOnQtThread([&]{res = m_androidInputContext->paste();}); + return res; } static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/) @@ -317,7 +372,8 @@ static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/) #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ UPDATECURSORPOS"); #endif - m_androidInputContext->updateCursorPosition(); + + runOnQtThread([&]{m_androidInputContext->updateCursorPosition();}); return true; } @@ -365,7 +421,7 @@ static QRect inputItemRectangle() QAndroidInputContext::QAndroidInputContext() : QPlatformInputContext(), m_composingTextStart(-1), m_blockUpdateSelection(false), - m_cursorHandleShown(CursorHandleNotShown), m_batchEditNestingLevel(0), m_focusObject(0) + m_handleMode(Hidden), m_batchEditNestingLevel(0), m_focusObject(0) { jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName); if (Q_UNLIKELY(!clazz)) { @@ -445,10 +501,17 @@ QAndroidInputContext::QAndroidInputContext() auto im = qGuiApp->inputMethod(); if (!im->inputItemClipRectangle().contains(im->anchorRectangle()) || !im->inputItemClipRectangle().contains(im->cursorRectangle())) { - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode = Hidden; updateSelectionHandles(); } }); + m_hideCursorHandleTimer.setInterval(4000); + m_hideCursorHandleTimer.setSingleShot(true); + m_hideCursorHandleTimer.setTimerType(Qt::VeryCoarseTimer); + connect(&m_hideCursorHandleTimer, &QTimer::timeout, this, [this]{ + m_handleMode = Hidden; + updateSelectionHandles(); + }); } QAndroidInputContext::~QAndroidInputContext() @@ -486,9 +549,9 @@ void QAndroidInputContext::reset() { clear(); m_batchEditNestingLevel = 0; - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; + m_handleMode = Hidden; if (qGuiApp->focusObject()) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(Qt::ImEnabled); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(Qt::ImEnabled); if (!query.isNull() && query->value(Qt::ImEnabled).toBool()) { QtAndroidInput::resetSoftwareKeyboard(); return; @@ -504,7 +567,7 @@ void QAndroidInputContext::commit() void QAndroidInputContext::updateCursorPosition() { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) { const int cursorPos = getAbsoluteCursorPosition(query); const int composeLength = m_composingText.length(); @@ -542,9 +605,9 @@ void QAndroidInputContext::updateSelectionHandles() return; auto im = qGuiApp->inputMethod(); - if (!m_focusObject || (m_cursorHandleShown == CursorHandleNotShown)) { + if (!m_focusObject || ((m_handleMode & 0xff) == Hidden)) { // Hide the handles - QtAndroidInput::updateHandles(0); + QtAndroidInput::updateHandles(Hidden); return; } QWindow *window = qGuiApp->focusWindow(); @@ -552,25 +615,34 @@ void QAndroidInputContext::updateSelectionHandles() ? QHighDpiScaling::factor(window) : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen()); - QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled | Qt::ImCurrentSelection); + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled | Qt::ImCurrentSelection | Qt::ImHints | Qt::ImSurroundingText); QCoreApplication::sendEvent(m_focusObject, &query); + int cpos = query.value(Qt::ImCursorPosition).toInt(); int anchor = query.value(Qt::ImAnchorPosition).toInt(); if (cpos == anchor || im->anchorRectangle().isNull()) { if (!query.value(Qt::ImEnabled).toBool()) { - QtAndroidInput::updateHandles(0); + QtAndroidInput::updateHandles(Hidden); return; } auto curRect = im->cursorRectangle(); QPoint cursorPoint(curRect.center().x(), curRect.bottom()); - QPoint editMenuPoint(curRect.center().x(), curRect.top()); - QtAndroidInput::updateHandles(m_cursorHandleShown, cursorPoint * pixelDensity, - editMenuPoint * pixelDensity); + QPoint editMenuPoint(curRect.x(), curRect.y()); + m_handleMode &= ShowEditPopup; + m_handleMode |= ShowCursor; + uint32_t buttons = EditContext::PasteButton; + if (!query.value(Qt::ImSurroundingText).toString().isEmpty()) + buttons |= EditContext::SelectAllButton; + QtAndroidInput::updateHandles(m_handleMode, editMenuPoint * pixelDensity, buttons, cursorPoint * pixelDensity); + // The VK is hidden, reset the timer + if (m_hideCursorHandleTimer.isActive()) + m_hideCursorHandleTimer.start(); return; } + m_handleMode = ShowSelection | ShowEditPopup ; auto leftRect = im->cursorRectangle(); auto rightRect = im->anchorRectangle(); if (cpos > anchor) @@ -578,13 +650,10 @@ void QAndroidInputContext::updateSelectionHandles() QPoint leftPoint(leftRect.bottomLeft().toPoint() * pixelDensity); QPoint righPoint(rightRect.bottomRight().toPoint() * pixelDensity); - QtAndroidInput::updateHandles(CursorHandleShowSelection, leftPoint, righPoint, + QPoint editPoint(leftRect.united(rightRect).topLeft().toPoint() * pixelDensity); + QtAndroidInput::updateHandles(m_handleMode, editPoint, EditContext::AllButtons, leftPoint, righPoint, query.value(Qt::ImCurrentSelection).toString().isRightToLeft()); - - if (m_cursorHandleShown == CursorHandleShowPopup) { - // make sure the popup does not reappear when the selection menu closes - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; - } + m_hideCursorHandleTimer.stop(); } /* @@ -594,8 +663,10 @@ void QAndroidInputContext::updateSelectionHandles() */ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) { - if (m_batchEditNestingLevel.load() || m_blockUpdateSelection) + if (m_batchEditNestingLevel.load() || m_blockUpdateSelection) { + qWarning() << "QAndroidInputContext::handleLocationChanged returned"; return; + } finishComposingText(); @@ -669,34 +740,86 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) void QAndroidInputContext::touchDown(int x, int y) { - if (m_focusObject && inputItemRectangle().contains(x, y) && !m_cursorHandleShown) { + if (m_focusObject && inputItemRectangle().contains(x, y)) { // If the user touch the input rectangle, we can show the cursor handle - m_cursorHandleShown = QAndroidInputContext::CursorHandleShowNormal; + m_handleMode = ShowCursor; + // The VK will appear in a moment, stop the timer + m_hideCursorHandleTimer.stop(); + finishComposingText(); updateSelectionHandles(); } } void QAndroidInputContext::longPress(int x, int y) { + static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES"); + if (noHandles) + return; + if (m_focusObject && inputItemRectangle().contains(x, y)) { - // Show the paste menu if there is something to paste. - m_cursorHandleShown = QAndroidInputContext::CursorHandleShowPopup; + finishComposingText(); + + // Release left button, otherwise the following events will cancel the menu popup + QtAndroidInput::releaseMouse(x, y); + + handleLocationChanged(1, x, y); + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); + + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor); + QCoreApplication::sendEvent(m_focusObject, &query); + int cursor = query.value(Qt::ImCursorPosition).toInt(); + int anchor = cursor; + QString before = query.value(Qt::ImTextBeforeCursor).toString(); + QString after = query.value(Qt::ImTextAfterCursor).toString(); + for (const auto &ch : after) { + if (!ch.isLetterOrNumber()) + break; + ++anchor; + } + + for (auto itch = before.rbegin(); itch != after.rend(); ++itch) { + if (!itch->isLetterOrNumber()) + break; + --cursor; + } + if (cursor == anchor || cursor < 0 || cursor - anchor > 500) { + m_handleMode = ShowCursor | ShowEditPopup; + updateSelectionHandles(); + return; + } + QList<QInputMethodEvent::Attribute> imAttributes; + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, 0, QVariant())); + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); + QInputMethodEvent event(QString(), imAttributes); + QGuiApplication::sendEvent(m_focusObject, &event); + + m_handleMode = ShowSelection | ShowEditPopup; updateSelectionHandles(); } } void QAndroidInputContext::keyDown() { - if (m_cursorHandleShown) { + if (m_handleMode) { // When the user enter text on the keyboard, we hide the cursor handle - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; + m_handleMode = Hidden; + updateSelectionHandles(); + } +} + +void QAndroidInputContext::hideSelectionHandles() +{ + if (m_handleMode & ShowSelection) { + m_handleMode = Hidden; updateSelectionHandles(); + } else { + m_hideCursorHandleTimer.start(); } } void QAndroidInputContext::update(Qt::InputMethodQueries queries) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(queries); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries); if (query.isNull()) return; #warning TODO extract the needed data from query @@ -728,7 +851,7 @@ void QAndroidInputContext::showInputPanel() connect(qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(showInputPanelLater(Qt::ApplicationState))); return; } - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return; @@ -752,6 +875,14 @@ void QAndroidInputContext::showInputPanelLater(Qt::ApplicationState state) showInputPanel(); } +void QAndroidInputContext::safeCall(const std::function<void()> &func, Qt::ConnectionType conType) +{ + if (qGuiApp->thread() == QThread::currentThread()) + func(); + else + QMetaObject::invokeMethod(this, "safeCall", conType, Q_ARG(std::function<void()>, func)); +} + void QAndroidInputContext::hideInputPanel() { QtAndroidInput::hideSoftwareKeyboard(); @@ -810,17 +941,15 @@ jboolean QAndroidInputContext::endBatchEdit() */ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPosition) { - bool updateSelectionWasBlocked = m_blockUpdateSelection; - m_blockUpdateSelection = true; - + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); QInputMethodEvent event; event.setCommitString(text); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); clear(); // Qt has now put the cursor at the end of the text, corresponding to newCursorPosition == 1 if (newCursorPosition != 1) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (!query.isNull()) { QList<QInputMethodEvent::Attribute> attributes; const int localPos = query->value(Qt::ImCursorPosition).toInt(); @@ -832,15 +961,14 @@ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPos newLocalPos, 0)); } } - m_blockUpdateSelection = updateSelectionWasBlocked; - + svcb.setOldValue(); updateCursorPosition(); return JNI_TRUE; } jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_TRUE; @@ -854,7 +982,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right QInputMethodEvent event; event.setCommitString(QString(), -leftLength, leftLength+rightLength); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); clear(); return JNI_TRUE; @@ -866,7 +994,7 @@ jboolean QAndroidInputContext::finishComposingText() if (m_composingText.isEmpty()) return JNI_TRUE; // not composing - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_FALSE; @@ -879,7 +1007,7 @@ jboolean QAndroidInputContext::finishComposingText() QInputMethodEvent event(QString(), attributes); event.setCommitString(m_composingText); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); clear(); return JNI_TRUE; @@ -888,7 +1016,7 @@ jboolean QAndroidInputContext::finishComposingText() jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) { jint res = 0; - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return res; @@ -922,7 +1050,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex // updateExtractedText(View, int, ExtractedText) whenever you call // updateSelection(View, int, int, int, int)." QTBUG-37980 - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return m_extractedText; @@ -967,7 +1095,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex QString QAndroidInputContext::getSelectedText(jint /*flags*/) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); @@ -977,13 +1105,13 @@ QString QAndroidInputContext::getSelectedText(jint /*flags*/) QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) { //### the preedit text could theoretically be after the cursor - QVariant textAfter = queryFocusObjectThreadSafe(Qt::ImTextAfterCursor, QVariant(length)); + QVariant textAfter = QInputMethod::queryFocusObject(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 = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); @@ -997,12 +1125,12 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) { - QVariant textBefore = queryFocusObjectThreadSafe(Qt::ImTextBeforeCursor, QVariant(length)); + QVariant textBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, QVariant(length)); if (textBefore.isValid()) return textBefore.toString().rightRef(length) + m_composingText; //compatibility code for old controls that do not implement the new API - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); @@ -1028,7 +1156,7 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_FALSE; @@ -1050,7 +1178,7 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur QVariant(underlined))); QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); QMetaObject::invokeMethod(this, "keyDown"); @@ -1074,7 +1202,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) if (wasComposing) finishComposingText(); - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_FALSE; @@ -1095,8 +1223,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) int localStart = start - blockPosition; // Qt uses position inside block int currentCursor = wasComposing ? m_composingCursor : blockPosition + localPos; - bool updateSelectionWasBlocked = m_blockUpdateSelection; - m_blockUpdateSelection = true; + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); QString text = query->value(Qt::ImSurroundingText).toString(); @@ -1120,12 +1247,10 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) QInputMethodEvent event(m_composingText, attributes); event.setCommitString(QString(), relativeStart, length); - sendInputMethodEventThreadSafe(&event); - - m_blockUpdateSelection = updateSelectionWasBlocked; + sendInputMethodEvent(&event); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - QSharedPointer<QInputMethodQueryEvent> query2 = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query2 = focusObjectInputMethodQuery(); if (!query2.isNull()) { qDebug() << "Setting. Prev local cpos:" << localPos << "block pos:" <<blockPosition << "comp.start:" << m_composingTextStart << "rel.start:" << relativeStart << "len:" << length << "cpos attr:" << localPos - localStart; qDebug() << "New cursor pos" << getAbsoluteCursorPosition(query2); @@ -1137,7 +1262,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) jboolean QAndroidInputContext::setSelection(jint start, jint end) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_FALSE; @@ -1168,26 +1293,31 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end) end - start)); } QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); updateCursorPosition(); return JNI_TRUE; } jboolean QAndroidInputContext::selectAll() { + finishComposingText(); + m_handleMode = ShowCursor; sendShortcut(QKeySequence::SelectAll); return JNI_TRUE; } jboolean QAndroidInputContext::cut() { - m_cursorHandleShown = CursorHandleNotShown; + finishComposingText(); + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Cut); return JNI_TRUE; } jboolean QAndroidInputContext::copy() { + finishComposingText(); + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Copy); return JNI_TRUE; } @@ -1201,7 +1331,7 @@ jboolean QAndroidInputContext::copyURL() jboolean QAndroidInputContext::paste() { finishComposingText(); - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Paste); return JNI_TRUE; } @@ -1217,65 +1347,24 @@ void QAndroidInputContext::sendShortcut(const QKeySequence &sequence) } } -Q_INVOKABLE QVariant QAndroidInputContext::queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument) -{ - return QInputMethod::queryFocusObject(query, argument); -} - -QVariant QAndroidInputContext::queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument) -{ - QVariant retval; +QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) { if (!qGuiApp) - return retval; - const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); - if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) - return retval; - AndroidDeadlockProtector protector; - if (!inMainThread && !protector.acquire()) - return 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 {}; - return retval; -} - -QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQueryThreadSafe(Qt::InputMethodQueries queries) { - QSharedPointer<QInputMethodQueryEvent> retval; - if (!qGuiApp) - return QSharedPointer<QInputMethodQueryEvent>(); - const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); - if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) - return QSharedPointer<QInputMethodQueryEvent>(); - AndroidDeadlockProtector protector; - if (!inMainThread && !protector.acquire()) - return QSharedPointer<QInputMethodQueryEvent>(); - - QInputMethodQueryEvent *queryEvent = 0; - QMetaObject::invokeMethod(this, "focusObjectInputMethodQueryUnsafe", - inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QInputMethodQueryEvent*, queryEvent), - Q_ARG(Qt::InputMethodQueries, queries)); - - return QSharedPointer<QInputMethodQueryEvent>(queryEvent); -} - -QInputMethodQueryEvent *QAndroidInputContext::focusObjectInputMethodQueryUnsafe(Qt::InputMethodQueries queries) -{ QObject *focusObject = qGuiApp->focusObject(); if (!focusObject) - return 0; + return {}; QInputMethodQueryEvent *ret = new QInputMethodQueryEvent(queries); QCoreApplication::sendEvent(focusObject, ret); - return ret; + return QSharedPointer<QInputMethodQueryEvent>(ret); } -void QAndroidInputContext::sendInputMethodEventUnsafe(QInputMethodEvent *event) +void QAndroidInputContext::sendInputMethodEvent(QInputMethodEvent *event) { + if (!qGuiApp) + return; + QObject *focusObject = qGuiApp->focusObject(); if (!focusObject) return; @@ -1283,19 +1372,4 @@ void QAndroidInputContext::sendInputMethodEventUnsafe(QInputMethodEvent *event) QCoreApplication::sendEvent(focusObject, event); } -void QAndroidInputContext::sendInputMethodEventThreadSafe(QInputMethodEvent *event) -{ - if (!qGuiApp) - return; - const bool inMainThread = qGuiApp->thread() == QThread::currentThread(); - if (QAndroidEventDispatcherStopper::stopped() && !inMainThread) - return; - AndroidDeadlockProtector protector; - if (!inMainThread && !protector.acquire()) - return; - QMetaObject::invokeMethod(this, "sendInputMethodEventUnsafe", - inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection, - Q_ARG(QInputMethodEvent*, event)); -} - QT_END_NAMESPACE |