From 59df7bd0a92acf6cb9ef2eac551b7a0913e2cd1f Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Tue, 9 May 2017 18:17:54 +0200 Subject: Fix selectionChanged signal out of input field Task-number: QTBUG-60688 Change-Id: I6d0b78e6b8df54c40ae30d5f0909c631c440a9cd Reviewed-by: Alexandru Croitor --- src/core/render_widget_host_view_qt.cpp | 33 ++++++-- src/core/render_widget_host_view_qt.h | 4 +- .../widgets/qwebengineview/tst_qwebengineview.cpp | 95 +++++++++++++++++++++- 3 files changed, 122 insertions(+), 10 deletions(-) diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 72abe04af..cf22273e4 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -97,6 +97,7 @@ enum ImStateFlags { TextInputStateUpdated = 1 << 0, TextSelectionUpdated = 1 << 1, TextSelectionBoundsUpdated = 1 << 2, + TextSelectionFlags = TextSelectionUpdated | TextSelectionBoundsUpdated, AllFlags = TextInputStateUpdated | TextSelectionUpdated | TextSelectionBoundsUpdated }; @@ -266,8 +267,8 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget , m_needsBeginFrames(false) , m_addedFrameObserver(false) , m_imState(0) - , m_anchorPositionWithinSelection(0) - , m_cursorPositionWithinSelection(0) + , m_anchorPositionWithinSelection(-1) + , m_cursorPositionWithinSelection(-1) , m_cursorPosition(0) , m_emptyPreviousSelection(true) { @@ -776,8 +777,10 @@ void RenderWidgetHostViewQt::OnSelectionBoundsChanged(content::TextInputManager Q_UNUSED(updated_view); m_imState |= ImStateFlags::TextSelectionBoundsUpdated; - if (m_imState == ImStateFlags::AllFlags) + if (m_imState == ImStateFlags::AllFlags + || (m_imState == ImStateFlags::TextSelectionFlags && getTextInputType() == ui::TEXT_INPUT_TYPE_NONE)) { selectionChanged(); + } } void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view) @@ -794,8 +797,10 @@ void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *t #endif // defined(USE_X11) m_imState |= ImStateFlags::TextSelectionUpdated; - if (m_imState == ImStateFlags::AllFlags) + if (m_imState == ImStateFlags::AllFlags + || (m_imState == ImStateFlags::TextSelectionFlags && getTextInputType() == ui::TEXT_INPUT_TYPE_NONE)) { selectionChanged(); + } } void RenderWidgetHostViewQt::selectionChanged() @@ -803,6 +808,22 @@ void RenderWidgetHostViewQt::selectionChanged() // Reset input manager state m_imState = 0; + // Handle text selection out of an input field + if (getTextInputType() == ui::TEXT_INPUT_TYPE_NONE) { + if (GetSelectedText().empty() && m_emptyPreviousSelection) + return; + + // Reset position values to emit selectionChanged signal when clearing text selection + // by clicking into an input field. These values are intended to be used by inputMethodQuery + // so they are not expected to be valid when selection is out of an input field. + m_anchorPositionWithinSelection = -1; + m_cursorPositionWithinSelection = -1; + + m_emptyPreviousSelection = GetSelectedText().empty(); + m_adapterClient->selectionChanged(); + return; + } + const content::TextInputManager::TextSelection *selection = text_input_manager_->GetTextSelection(); if (!selection) return; @@ -817,8 +838,8 @@ void RenderWidgetHostViewQt::selectionChanged() return; } - uint newAnchorPositionWithinSelection = 0; - uint newCursorPositionWithinSelection = 0; + int newAnchorPositionWithinSelection = 0; + int newCursorPositionWithinSelection = 0; if (text_input_manager_->GetSelectionRegion()->anchor.type() == gfx::SelectionBound::RIGHT) { newAnchorPositionWithinSelection = selection->range.GetMax() - selection->offset; diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 799930830..3b679923e 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -263,8 +263,8 @@ private: gfx::SizeF m_lastContentsSize; uint m_imState; - uint m_anchorPositionWithinSelection; - uint m_cursorPositionWithinSelection; + int m_anchorPositionWithinSelection; + int m_cursorPositionWithinSelection; uint m_cursorPosition; bool m_emptyPreviousSelection; QString m_surroundingText; diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 32a518ad8..37c7ae881 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -100,7 +100,8 @@ private Q_SLOTS: void softwareInputPanel(); void inputMethods(); - void textSelection(); + void textSelectionInInputField(); + void textSelectionOutOfInputField(); void hiddenText(); void emptyInputMethodEvent(); void imeComposition(); @@ -1584,7 +1585,7 @@ void tst_QWebEngineView::inputMethods() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine")); } -void tst_QWebEngineView::textSelection() +void tst_QWebEngineView::textSelectionInInputField() { QWebEngineView view; view.show(); @@ -1662,6 +1663,96 @@ void tst_QWebEngineView::textSelection() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("QtWebEngi")); } +void tst_QWebEngineView::textSelectionOutOfInputField() +{ + QWebEngineView view; + view.show(); + + QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged())); + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setHtml("" + " This is a text" + ""); + QVERIFY(loadFinishedSpy.wait()); + + QCOMPARE(selectionChangedSpy.count(), 0); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + + // Simple click should not update text selection, however it updates selection bounds in Chromium + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center()); + QCOMPARE(selectionChangedSpy.count(), 0); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + + // Workaround for macOS: press ctrl+a without key text + QKeyEvent keyPressCtrlA(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier); + QKeyEvent keyReleaseCtrlA(QEvent::KeyRelease, Qt::Key_A, Qt::ControlModifier); + + // Select text by ctrl+a + QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); + QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 1); + QVERIFY(view.hasSelection()); + QCOMPARE(view.page()->selectedText(), QString("This is a text")); + + // Deselect text by mouse click + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center()); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 2); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + + selectionChangedSpy.clear(); + view.setHtml("" + " This is a text" + "
" + " " + ""); + QVERIFY(loadFinishedSpy.wait()); + + QCOMPARE(selectionChangedSpy.count(), 0); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + + // Make sure the input field does not have the focus + evaluateJavaScriptSync(view.page(), "document.getElementById('input1').blur()"); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + + // Select the whole page by ctrl+a + QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); + QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 1); + QVERIFY(view.hasSelection()); + QVERIFY(view.page()->selectedText().startsWith(QString("This is a text"))); + + // Remove selection by clicking into an input field + QPoint textInputCenter = elementCenter(view.page(), "input1"); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); + QCOMPARE(selectionChangedSpy.count(), 2); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + + // Select the content of the input field by ctrl+a + QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); + QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 3); + QVERIFY(view.hasSelection()); + QCOMPARE(view.page()->selectedText(), QString("QtWebEngine")); + + // Deselect input field's text by mouse click + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center()); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 4); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); +} + void tst_QWebEngineView::hiddenText() { QWebEngineView view; -- cgit v1.2.3