diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-02-24 09:00:48 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-07-16 14:05:15 +0000 |
commit | baa395864555e113c4d7d51a11a4934c792150db (patch) | |
tree | 808fe11db1764108727d981398c20d4d0b138f6d /tests | |
parent | e97bd5dcc695136ec9020fab3983bf4811323031 (diff) |
TextInput/Field: selectByMouse default=true, but not on touchscreens
When you drag a finger across a TextInput or TextField, it should
not select text.
- your finger probably covers several characters, so you can't see where
you're selecting until afterwards
- if the item is in a Flickable, flicking by touch should be prioritized
- if flicking happens, the text cursor should not move; but to avoid
losing too much functionality, we allow it to move on release, if
the TextInput or TextField gets the release (i.e. if it still has
the exclusive grab)
- TextField's pressed, pressAndHold and released signals continue to
behave the same with touch as with mouse
So now we distinguish mouse events that are synthesized from non-mouse
devices and avoid mouse-like behaviors as described above, but there is
no behavior change if the event comes from an actual mouse or touchpad.
Since most users want selecting text by mouse to "just work", and an
actual mouse is precise enough to do so, and dragging a Flickable with
the mouse is unintuitive (since most UIs don't allow it and most mice
have wheels), selectByMouse now defaults to true, and has the stricter
meaning that its name implies. To select text on a touchscreen, the
end-user needs to rely on text-selection handles, which are provided on
touch-centric mobile platforms, and could also be implemented from
scratch if someone builds a custom text field using TextInput.
[ChangeLog][QtQuick][TextInput] The selectByMouse property is now
enabled by default, but no longer enables selecting by dragging your
finger across text on a touchscreen. Platforms that are optimized for
touchscreens normally use special text-selection handles, which interact
with Qt via QInputMethod. You can opt out of the behavior change by
using an import version < 6.4.
[ChangeLog][Controls][TextField] The selectByMouse property is now
enabled by default, but no longer enables selecting by dragging your
finger across text on a touchscreen. Platforms that are optimized for
touchscreens normally use special text-selection handles, which interact
with Qt via QInputMethod. You can opt out of the behavior change by
using an import version < 6.4.
Task-number: QTBUG-10684
Task-number: QTBUG-38934
Task-number: QTBUG-101205
Change-Id: I6d3158dd48896a0bed37cbc0b2da01d313a499f8
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
(cherry picked from commit 650342de792e0ab37ce8bac8ccde21ab9b96b2c9)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/quick/qquicktextinput/data/mouseselection_old_default.qml (renamed from tests/auto/quick/qquicktextinput/data/mouseselection_true.qml) | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextinput/data/mouseselectionmode_default.qml | 3 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextinput/data/twoInAColumn.qml | 14 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 103 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/controls/data/tst_textfield.qml | 45 |
5 files changed, 159 insertions, 7 deletions
diff --git a/tests/auto/quick/qquicktextinput/data/mouseselection_true.qml b/tests/auto/quick/qquicktextinput/data/mouseselection_old_default.qml index 974041b04a..42ab4931ea 100644 --- a/tests/auto/quick/qquicktextinput/data/mouseselection_true.qml +++ b/tests/auto/quick/qquicktextinput/data/mouseselection_old_default.qml @@ -3,5 +3,4 @@ import QtQuick 2.0 TextInput { focus: true text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - selectByMouse: true } diff --git a/tests/auto/quick/qquicktextinput/data/mouseselectionmode_default.qml b/tests/auto/quick/qquicktextinput/data/mouseselectionmode_default.qml index 974041b04a..ad19fa8c2a 100644 --- a/tests/auto/quick/qquicktextinput/data/mouseselectionmode_default.qml +++ b/tests/auto/quick/qquicktextinput/data/mouseselectionmode_default.qml @@ -1,7 +1,6 @@ -import QtQuick 2.0 +import QtQuick TextInput { focus: true text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - selectByMouse: true } diff --git a/tests/auto/quick/qquicktextinput/data/twoInAColumn.qml b/tests/auto/quick/qquicktextinput/data/twoInAColumn.qml new file mode 100644 index 0000000000..dbc70afe4b --- /dev/null +++ b/tests/auto/quick/qquicktextinput/data/twoInAColumn.qml @@ -0,0 +1,14 @@ +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + height: 100 + TextInput { + objectName: "top" + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + } + TextInput { + objectName: "bottom" + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + } +} diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index ad9bd7a501..54f650f86e 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -4,6 +4,7 @@ #include <QtTest/QSignalSpy> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/testhttpserver_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> #include <private/qinputmethod_p.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -13,6 +14,8 @@ #include <QtGui/qguiapplication.h> #include <QtGui/qstylehints.h> #include <QtGui/qvalidator.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qpointingdevice_p.h> #include <QInputMethod> #include <private/qquicktextinput_p.h> #include <private/qquicktextinput_p_p.h> @@ -197,15 +200,22 @@ private slots: void checkCursorDelegateWhenPaddingChanged(); void focusReason(); + + void touchscreenDoesNotSelect_data(); + void touchscreenDoesNotSelect(); + void touchscreenSetsFocusAndMovesCursor(); + private: void simulateKeys(QWindow *window, const QList<Key> &keys); #if QT_CONFIG(shortcut) void simulateKeys(QWindow *window, const QKeySequence &sequence); #endif + static bool hasWindowActivation(); QQmlEngine engine; QStringList standard; QStringList colorStrings; + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; typedef QList<int> IntList; @@ -226,6 +236,11 @@ void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys) } } +bool tst_qquicktextinput::hasWindowActivation() +{ + return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)); +} + #if QT_CONFIG(shortcut) void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence) @@ -1253,7 +1268,7 @@ void tst_qquicktextinput::moveCursorSelectionSequence() void tst_qquicktextinput::dragMouseSelection() { - QString qmlfile = testFile("mouseselection_true.qml"); + QString qmlfile = testFile("mouseselectionmode_default.qml"); QQuickView window(QUrl::fromLocalFile(qmlfile)); @@ -1264,6 +1279,7 @@ void tst_qquicktextinput::dragMouseSelection() QVERIFY(window.rootObject() != nullptr); QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject()); QVERIFY(textInputObject != nullptr); + textInputObject->setSelectByMouse(true); // press-and-drag-and-release from x1 to x2 int x1 = 10; @@ -2744,7 +2760,7 @@ void tst_qquicktextinput::middleClickPaste() if (!PlatformQuirks::isClipboardAvailable()) QSKIP("This machine doesn't support the clipboard"); - QQuickView window(testFileUrl("mouseselection_true.qml")); + QQuickView window(testFileUrl("mouseselectionmode_default.qml")); window.show(); window.requestActivate(); @@ -2753,6 +2769,7 @@ void tst_qquicktextinput::middleClickPaste() QVERIFY(window.rootObject() != nullptr); QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject()); QVERIFY(textInputObject != nullptr); + textInputObject->setSelectByMouse(true); textInputObject->setFocus(true); @@ -7091,6 +7108,88 @@ void tst_qquicktextinput::focusReason() QCOMPARE(eventFilter.lastFocusReason[first], Qt::TabFocusReason); } +void tst_qquicktextinput::touchscreenDoesNotSelect_data() +{ + QTest::addColumn<QUrl>("src"); + QTest::addColumn<bool>("expectDefaultSelectByMouse"); + QTest::addColumn<bool>("overrideSelectByMouseFalse"); + QTest::newRow("new default") << testFileUrl("mouseselectionmode_default.qml") << true << false; + QTest::newRow("new override") << testFileUrl("mouseselectionmode_default.qml") << true << true; +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + QTest::newRow("old default") << testFileUrl("mouseselection_old_default.qml") << false << false; +#endif +} + +void tst_qquicktextinput::touchscreenDoesNotSelect() +{ + QFETCH(QUrl, src); + QFETCH(bool, expectDefaultSelectByMouse); + QFETCH(bool, overrideSelectByMouseFalse); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, src)); + + QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject()); + QVERIFY(textInputObject); + QCOMPARE(textInputObject->selectByMouse(), expectDefaultSelectByMouse); + if (overrideSelectByMouseFalse) + textInputObject->setSelectByMouse(overrideSelectByMouseFalse); + + // press-drag-and-release from x1 to x2 + int x1 = 10; + int x2 = 70; + int y = textInputObject->height() / 2; + QTest::touchEvent(&window, touchscreen.data()).press(0, QPoint(x1,y), &window); + QTest::touchEvent(&window, touchscreen.data()).move(0, QPoint(x2,y), &window); + QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x2,y), &window); + QQuickTouchUtils::flush(&window); + QVERIFY(textInputObject->selectedText().isEmpty()); +} + +void tst_qquicktextinput::touchscreenSetsFocusAndMovesCursor() +{ + if (!hasWindowActivation()) + QSKIP("Window activation is not supported"); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("twoInAColumn.qml"))); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + QQuickTextInput *top = window.rootObject()->findChild<QQuickTextInput*>("top"); + QVERIFY(top); + QQuickTextInput *bottom = window.rootObject()->findChild<QQuickTextInput*>("bottom"); + QVERIFY(bottom); + + // tap the bottom field + int x1 = 10; + int y = bottom->position().y() + bottom->height() / 2; + QTest::touchEvent(&window, touchscreen.data()).press(0, QPoint(x1,y), &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(qApp->focusObject(), bottom); + // text cursor is at the end by default, on press + const auto len = bottom->text().length(); + QCOMPARE(bottom->cursorPosition(), len); + // so typing a character appends it + QVERIFY(!bottom->text().endsWith('q')); + QTest::keyClick(&window, Qt::Key_Q); + QVERIFY(bottom->text().endsWith('q')); + QCOMPARE(bottom->text().length(), len + 1); + QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x1,y), &window); + QQuickTouchUtils::flush(&window); + // the cursor gets moved on release, as long as TextInput's grab wasn't stolen (e.g. by Flickable) + QVERIFY(bottom->cursorPosition() < 5); + + // press-drag-and-release from x1 to x2 on the top field + int x2 = 70; + y = top->position().y() + top->height() / 2; + QTest::touchEvent(&window, touchscreen.data()).press(0, QPoint(x1,y), &window); + QTest::touchEvent(&window, touchscreen.data()).move(0, QPoint(x2,y), &window); + QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x2,y), &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(qApp->focusObject(), top); + QVERIFY(top->selectedText().isEmpty()); +} + QTEST_MAIN(tst_qquicktextinput) #include "tst_qquicktextinput.moc" diff --git a/tests/auto/quickcontrols2/controls/data/tst_textfield.qml b/tests/auto/quickcontrols2/controls/data/tst_textfield.qml index 09669a8df2..ecda6c25b5 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_textfield.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_textfield.qml @@ -394,7 +394,7 @@ TestCase { } function test_multiClick() { - var control = createTemporaryObject(textField, testCase, {text: "Qt Quick Controls 2 TextArea", selectByMouse: true}) + var control = createTemporaryObject(textField, testCase, {text: "Qt Quick Controls 2 TextArea"}) verify(control) waitForRendering(control) @@ -412,7 +412,7 @@ TestCase { // QTBUG-64048 function test_rightClick() { - var control = createTemporaryObject(textField, testCase, {text: "TextField", selectByMouse: true}) + var control = createTemporaryObject(textField, testCase, {text: "TextField"}) verify(control) control.selectAll() @@ -425,6 +425,47 @@ TestCase { compare(control.selectedText, "") } + function test_mouseSelect() { + var control = createTemporaryObject(textField, testCase, {text: "Text", width: parent.width}) + verify(control) + verify(control.selectByMouse) // true by default since 6.4 + var pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"}) + + const y = control.height / 2 + mousePress(control, 0, y, Qt.LeftButton) + tryCompare(pressSpy, "count", 1) + mouseMove(control, control.implicitWidth, y, 0, Qt.LeftButton) + mouseRelease(control, control.implicitWidth, y, Qt.LeftButton) + tryVerify(function() { return control.selectedText.length > 1 }) // ideally the whole 4-letter word + } + + function test_noTouchSelect() { + var control = createTemporaryObject(textField, testCase, {text: "Text"}) + verify(control) + verify(control.selectByMouse) // true by default since 6.4 + + var touch = touchEvent(control) + const y = control.height / 2 + touch.press(0, control, 0, y).commit() + touch.move(0, control, control.implicitWidth, 0).commit() + touch.release(0, control) + compare(control.selectedText, "") + } + + function test_aaTouchPressAndHold() { + var control = createTemporaryObject(textField, testCase, {text: "Text"}) + verify(control) + verify(control.selectByMouse) // true by default since 6.4 + var pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"}) + var pressAndHoldSpy = signalSpy.createObject(control, {target: control, signalName: "pressAndHold"}) + + var touch = touchEvent(control) + touch.press(0, control).commit() + tryCompare(pressSpy, "count", 1) + tryCompare(pressAndHoldSpy, "count", 1) + touch.release(0, control).commit() + } + // QTBUG-66260 function test_placeholderTextColor() { var control = createTemporaryObject(textField, testCase) |