diff options
-rw-r--r-- | src/core/render_widget_host_view_qt.cpp | 13 | ||||
-rw-r--r-- | tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp | 93 | ||||
-rw-r--r-- | tests/auto/quick/shared/util.h | 77 |
3 files changed, 183 insertions, 0 deletions
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 9968d3e49..980550620 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -1414,6 +1414,19 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) break; } + if (m_imeInProgress && ev->type() == QEvent::TouchBegin) { + m_imeInProgress = false; + // Tell input method to commit the pre-edit string entered so far, and finish the + // composition operation. +#ifdef Q_OS_WIN + // Yes the function name is counter-intuitive, but commit isn't actually implemented + // by the Windows QPA, and reset does exactly what is necessary in this case. + qApp->inputMethod()->reset(); +#else + qApp->inputMethod()->commit(); +#endif + } + // Make sure that ACTION_POINTER_DOWN is delivered before ACTION_MOVE, // and ACTION_MOVE before ACTION_POINTER_UP. std::sort(touchPoints.begin(), touchPoints.end(), compareTouchPoints); diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 2f9063ea5..4bc136539 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -35,7 +35,9 @@ #include <QtQml/QQmlEngine> #include <QtTest/QtTest> #include <QtWebEngine/QQuickWebEngineProfile> +#include <private/qinputmethod_p.h> #include <private/qquickwebengineview_p.h> +#include <qpa/qplatforminputcontext.h> #include <functional> @@ -70,6 +72,8 @@ private Q_SLOTS: void inputMethod(); void inputMethodHints(); + void interruptImeTextComposition_data(); + void interruptImeTextComposition(); void basicRenderingSanity(); void setZoomFactor(); void printToPdf(); @@ -450,6 +454,95 @@ void tst_QQuickWebEngineView::inputMethod() #endif } +class TestInputContext : public QPlatformInputContext +{ +public: + TestInputContext() + : commitCallCount(0) + , resetCallCount(0) + { + QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); + inputMethodPrivate->testContext = this; + } + + ~TestInputContext() + { + QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); + inputMethodPrivate->testContext = 0; + } + + virtual void commit() { + commitCallCount++; + } + + virtual void reset() { + resetCallCount++; + } + + int commitCallCount; + int resetCallCount; +}; + +void tst_QQuickWebEngineView::interruptImeTextComposition_data() +{ + QTest::addColumn<QString>("eventType"); + + QTest::newRow("MouseButton") << QString("MouseButton"); +#ifndef Q_OS_MACOS + QTest::newRow("Touch") << QString("Touch"); +#endif +} + +void tst_QQuickWebEngineView::interruptImeTextComposition() +{ + m_window->show(); + QTRY_VERIFY(qApp->focusObject()); + QQuickItem *input; + + QQuickWebEngineView *view = webEngineView(); + view->loadHtml("<html><body>" + " <input type='text' id='input1' /><br>" + " <input type='text' id='input2' />" + "</body></html>"); + QVERIFY(waitForLoadSucceeded(view)); + + runJavaScript("document.getElementById('input1').focus();"); + QTRY_COMPARE(activeElementId(view), QStringLiteral("input1")); + + TestInputContext testContext; + + // Send temporary text, which makes the editor has composition 'x' + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("x", attributes); + input = qobject_cast<QQuickItem *>(qApp->focusObject()); + QGuiApplication::sendEvent(input, &event); + QTRY_COMPARE(elementValue(view, "input1"), QStringLiteral("x")); + + // Focus 'input2' input field by an input event + QFETCH(QString, eventType); + if (eventType == "MouseButton") { + QPoint textInputCenter = elementCenter(view, QStringLiteral("input2")); + QTest::mouseClick(view->window(), Qt::LeftButton, 0, textInputCenter); + } else if (eventType == "Touch") { + QPoint textInputCenter = elementCenter(view, QStringLiteral("input2")); + QTouchDevice *touchDevice = QTest::createTouchDevice(); + QTest::touchEvent(view->window(), touchDevice).press(0, textInputCenter, view->window()); + QTest::touchEvent(view->window(), touchDevice).release(0, textInputCenter, view->window()); + } + QTRY_COMPARE(activeElementId(view), QStringLiteral("input2")); +#ifndef Q_OS_WIN + QTRY_COMPARE(testContext.commitCallCount, 1); +#else + QTRY_COMPARE(testContext.resetCallCount, 2); +#endif + + // Check the composition text has been committed + runJavaScript("document.getElementById('input1').focus();"); + QTRY_COMPARE(activeElementId(view), QStringLiteral("input1")); + input = qobject_cast<QQuickItem *>(qApp->focusObject()); + QTRY_COMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QStringLiteral("x")); +} + void tst_QQuickWebEngineView::inputMethodHints() { #if !defined(QQUICKWEBENGINEVIEW_ITEMACCEPTSINPUTMETHOD) diff --git a/tests/auto/quick/shared/util.h b/tests/auto/quick/shared/util.h index dce0afb8e..8e7169be7 100644 --- a/tests/auto/quick/shared/util.h +++ b/tests/auto/quick/shared/util.h @@ -142,4 +142,81 @@ inline QString bodyInnerText(QQuickWebEngineView *webEngineView) return arguments.at(1).toString(); } +inline QString activeElementId(QQuickWebEngineView *webEngineView) +{ + qRegisterMetaType<QQuickWebEngineView::JavaScriptConsoleMessageLevel>("JavaScriptConsoleMessageLevel"); + QSignalSpy consoleMessageSpy(webEngineView, &QQuickWebEngineView::javaScriptConsoleMessage); + + webEngineView->runJavaScript( + "if (document.activeElement == null)" + " console.log('');" + "else" + " console.log(document.activeElement.id);" + ); + + if (!consoleMessageSpy.wait()) + return QString(); + + QList<QVariant> arguments = consoleMessageSpy.takeFirst(); + if (static_cast<QQuickWebEngineView::JavaScriptConsoleMessageLevel>(arguments.at(0).toInt()) != QQuickWebEngineView::InfoMessageLevel) + return QString(); + + return arguments.at(1).toString(); +} + +inline QString elementValue(QQuickWebEngineView *webEngineView, const QString &id) +{ + qRegisterMetaType<QQuickWebEngineView::JavaScriptConsoleMessageLevel>("JavaScriptConsoleMessageLevel"); + QSignalSpy consoleMessageSpy(webEngineView, &QQuickWebEngineView::javaScriptConsoleMessage); + + webEngineView->runJavaScript(QString( + "var element = document.getElementById('" + id + "');" + "if (element == null)" + " console.log('');" + "else" + " console.log(element.value);") + ); + + if (!consoleMessageSpy.wait()) + return QString(); + + QList<QVariant> arguments = consoleMessageSpy.takeFirst(); + if (static_cast<QQuickWebEngineView::JavaScriptConsoleMessageLevel>(arguments.at(0).toInt()) != QQuickWebEngineView::InfoMessageLevel) + return QString(); + + return arguments.at(1).toString(); +} + +inline QPoint elementCenter(QQuickWebEngineView *webEngineView, const QString &id) +{ + qRegisterMetaType<QQuickWebEngineView::JavaScriptConsoleMessageLevel>("JavaScriptConsoleMessageLevel"); + QSignalSpy consoleMessageSpy(webEngineView, &QQuickWebEngineView::javaScriptConsoleMessage); + + webEngineView->runJavaScript(QString( + "var element = document.getElementById('" + id + "');" + "var rect = element.getBoundingClientRect();" + "console.log((rect.left + rect.right) / 2);" + "console.log((rect.top + rect.bottom) / 2);") + ); + + QTRY_LOOP_IMPL(consoleMessageSpy.count() == 2, 5000, 50); + if (consoleMessageSpy.count() != 2) + return QPoint(); + + QList<QVariant> arguments; + double x, y; + + arguments = consoleMessageSpy.takeFirst(); + if (static_cast<QQuickWebEngineView::JavaScriptConsoleMessageLevel>(arguments.at(0).toInt()) != QQuickWebEngineView::InfoMessageLevel) + return QPoint(); + x = arguments.at(1).toDouble(); + + arguments = consoleMessageSpy.takeLast(); + if (static_cast<QQuickWebEngineView::JavaScriptConsoleMessageLevel>(arguments.at(0).toInt()) != QQuickWebEngineView::InfoMessageLevel) + return QPoint(); + y = arguments.at(1).toDouble(); + + return QPoint(x, y); +} + #endif /* UTIL_H */ |