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 f548a1fa96..ea3e9c1441 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -63,6 +63,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"; @@ -75,6 +98,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) @@ -83,10 +111,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*/) @@ -98,9 +125,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; } @@ -117,7 +144,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) @@ -128,7 +157,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*/) @@ -139,7 +170,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) @@ -147,7 +180,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) @@ -155,8 +190,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; @@ -181,7 +216,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 @@ -195,7 +231,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 @@ -207,7 +244,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 @@ -227,7 +265,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) @@ -238,7 +278,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; } @@ -250,7 +292,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*/) @@ -261,7 +306,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*/) @@ -272,7 +319,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*/) @@ -283,7 +332,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*/) @@ -294,7 +345,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*/) @@ -305,7 +358,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*/) @@ -316,7 +371,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; } @@ -364,7 +420,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)) { @@ -444,10 +500,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() @@ -485,9 +548,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; @@ -503,7 +566,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(); @@ -541,9 +604,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(); @@ -551,25 +614,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) @@ -577,13 +649,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(); } /* @@ -593,8 +662,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(); @@ -668,34 +739,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 @@ -727,7 +850,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; @@ -751,6 +874,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(); @@ -809,17 +940,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(); @@ -831,15 +960,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; @@ -853,7 +981,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right QInputMethodEvent event; event.setCommitString(QString(), -leftLength, leftLength+rightLength); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); clear(); return JNI_TRUE; @@ -865,7 +993,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; @@ -878,7 +1006,7 @@ jboolean QAndroidInputContext::finishComposingText() QInputMethodEvent event(QString(), attributes); event.setCommitString(m_composingText); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); clear(); return JNI_TRUE; @@ -887,7 +1015,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; @@ -910,7 +1038,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; @@ -955,7 +1083,7 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex QString QAndroidInputContext::getSelectedText(jint /*flags*/) { - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(); + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); @@ -965,13 +1093,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(); @@ -985,12 +1113,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(); @@ -1016,7 +1144,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; @@ -1038,7 +1166,7 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur QVariant(underlined))); QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEventThreadSafe(&event); + sendInputMethodEvent(&event); QMetaObject::invokeMethod(this, "keyDown"); @@ -1062,7 +1190,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; @@ -1083,8 +1211,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(); @@ -1108,12 +1235,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); @@ -1125,7 +1250,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; @@ -1156,26 +1281,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; } @@ -1189,7 +1319,7 @@ jboolean QAndroidInputContext::copyURL() jboolean QAndroidInputContext::paste() { finishComposingText(); - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Paste); return JNI_TRUE; } @@ -1205,65 +1335,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; @@ -1271,19 +1360,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 |