diff options
author | J-P Nurmi <jpnurmi@qt.io> | 2017-01-02 17:39:44 +0100 |
---|---|---|
committer | J-P Nurmi <jpnurmi@qt.io> | 2017-01-03 13:36:48 +0000 |
commit | a11ae74d23890fc3ba1782a286bc226e2db21439 (patch) | |
tree | e8ba21a0fb7dc58e7b57d113dbddb7a121249cf7 | |
parent | 7c5920885ccefd999ed55245c1a500e110a5acb7 (diff) |
QQuickSlider: handle touch events
In comparison to handling synthesized mouse events, handling touch
events has the advantage that it gives multi-touch support. That is,
it is possible to move multiple sliders at the same time, each
handling its own touch point.
Change-Id: I713307b0e6b5ee777496fc9ba68a5180d13a6aca
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickslider.cpp | 60 | ||||
-rw-r--r-- | src/quicktemplates2/qquickslider_p.h | 2 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_slider.qml | 211 | ||||
-rw-r--r-- | tests/auto/controls/default/tst_default.cpp | 1 | ||||
-rw-r--r-- | tests/auto/controls/material/tst_material.cpp | 1 | ||||
-rw-r--r-- | tests/auto/controls/universal/tst_universal.cpp | 1 |
6 files changed, 274 insertions, 2 deletions
diff --git a/src/quicktemplates2/qquickslider.cpp b/src/quicktemplates2/qquickslider.cpp index bb3c0158..2398b1e1 100644 --- a/src/quicktemplates2/qquickslider.cpp +++ b/src/quicktemplates2/qquickslider.cpp @@ -89,7 +89,7 @@ class QQuickSliderPrivate : public QQuickControlPrivate public: QQuickSliderPrivate() : from(0), to(1), value(0), position(0), stepSize(0), live(false), pressed(false), - orientation(Qt::Horizontal), snapMode(QQuickSlider::NoSnap), + touchId(-1), orientation(Qt::Horizontal), snapMode(QQuickSlider::NoSnap), handle(nullptr) { } @@ -111,6 +111,7 @@ public: qreal stepSize; bool live; bool pressed; + int touchId; QPointF pressPoint; Qt::Orientation orientation; QQuickSlider::SnapMode snapMode; @@ -201,6 +202,7 @@ void QQuickSliderPrivate::handleMove(const QPointF &point) void QQuickSliderPrivate::handleRelease(const QPointF &point) { Q_Q(QQuickSlider); + touchId = -1; pressPoint = QPointF(); const qreal oldPos = position; qreal pos = positionAt(point); @@ -220,6 +222,7 @@ void QQuickSliderPrivate::handleRelease(const QPointF &point) void QQuickSliderPrivate::handleUngrab() { Q_Q(QQuickSlider); + touchId = -1; pressPoint = QPointF(); q->setPressed(false); } @@ -645,6 +648,61 @@ void QQuickSlider::mouseUngrabEvent() d->handleUngrab(); } +void QQuickSlider::touchEvent(QTouchEvent *event) +{ + Q_D(QQuickSlider); + switch (event->type()) { + case QEvent::TouchBegin: + if (d->touchId == -1) { + const QTouchEvent::TouchPoint point = event->touchPoints().first(); + d->touchId = point.id(); + d->handlePress(point.pos()); + } else { + event->ignore(); + } + break; + + case QEvent::TouchUpdate: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (point.id() != d->touchId) + continue; + + if (!keepMouseGrab()) { + if (d->orientation == Qt::Horizontal) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(point.pos().x() - d->pressPoint.x(), Qt::XAxis, &point)); + else + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(point.pos().y() - d->pressPoint.y(), Qt::YAxis, &point)); + } + d->handleMove(point.pos()); + } + break; + + case QEvent::TouchEnd: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (point.id() != d->touchId) + continue; + + d->handleRelease(point.pos()); + } + break; + + case QEvent::TouchCancel: + d->handleUngrab(); + break; + + default: + QQuickControl::touchEvent(event); + break; + } +} + +void QQuickSlider::touchUngrabEvent() +{ + Q_D(QQuickSlider); + QQuickControl::touchUngrabEvent(); + d->handleUngrab(); +} + void QQuickSlider::wheelEvent(QWheelEvent *event) { Q_D(QQuickSlider); diff --git a/src/quicktemplates2/qquickslider_p.h b/src/quicktemplates2/qquickslider_p.h index 7b96168a..972dfd65 100644 --- a/src/quicktemplates2/qquickslider_p.h +++ b/src/quicktemplates2/qquickslider_p.h @@ -136,6 +136,8 @@ protected: void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseUngrabEvent() override; + void touchEvent(QTouchEvent *event) override; + void touchUngrabEvent() override; void wheelEvent(QWheelEvent *event) override; void mirrorChange() override; diff --git a/tests/auto/controls/data/tst_slider.qml b/tests/auto/controls/data/tst_slider.qml index d5c734a1..ef57d8d2 100644 --- a/tests/auto/controls/data/tst_slider.qml +++ b/tests/auto/controls/data/tst_slider.qml @@ -286,6 +286,184 @@ TestCase { compare(control.position, 0.5) } + function test_touch_data() { + return [ + { tag: "horizontal", orientation: Qt.Horizontal, live: false }, + { tag: "vertical", orientation: Qt.Vertical, live: false }, + { tag: "horizontal:live", orientation: Qt.Horizontal, live: true }, + { tag: "vertical:live", orientation: Qt.Vertical, live: true } + ] + } + + function test_touch(data) { + var control = createTemporaryObject(slider, testCase, {orientation: data.orientation, live: data.live}) + verify(control) + + var pressedCount = 0 + var movedCount = 0 + + var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"}) + verify(pressedSpy.valid) + + var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"}) + verify(movedSpy.valid) + + var touch = touchEvent(control) + touch.press(0, control, 0, 0).commit() + compare(pressedSpy.count, ++pressedCount) + compare(movedSpy.count, movedCount) + compare(control.pressed, true) + compare(control.value, 0.0) + compare(control.position, 0.0) + + // mininum on the left in horizontal vs. at the bottom in vertical + touch.move(0, control, -control.width, 2 * control.height, 0).commit() + compare(pressedSpy.count, pressedCount) + compare(movedSpy.count, movedCount) + compare(control.pressed, true) + compare(control.value, 0.0) + compare(control.position, 0.0) + + touch.move(0, control, control.width * 0.5, control.height * 0.5, 0).commit() + compare(pressedSpy.count, pressedCount) + compare(movedSpy.count, ++movedCount) + compare(control.pressed, true) + compare(control.value, data.live ? 0.5 : 0.0) + compare(control.position, 0.5) + + touch.release(0, control, control.width * 0.5, control.height * 0.5).commit() + compare(pressedSpy.count, ++pressedCount) + compare(movedSpy.count, movedCount) + compare(control.pressed, false) + compare(control.value, 0.5) + compare(control.position, 0.5) + + touch.press(0, control, control.width, control.height).commit() + compare(pressedSpy.count, ++pressedCount) + compare(movedSpy.count, movedCount) + compare(control.pressed, true) + compare(control.value, 0.5) + compare(control.position, 0.5) + + // maximum on the right in horizontal vs. at the top in vertical + touch.move(0, control, control.width * 2, -control.height, 0).commit() + compare(pressedSpy.count, pressedCount) + compare(movedSpy.count, ++movedCount) + compare(control.pressed, true) + compare(control.value, data.live ? 1.0 : 0.5) + compare(control.position, 1.0) + + touch.move(0, control, control.width * 0.75, control.height * 0.25, 0).commit() + compare(pressedSpy.count, pressedCount) + compare(movedSpy.count, ++movedCount) + compare(control.pressed, true) + compare(control.value, data.live ? control.position : 0.5) + verify(control.position >= 0.75) + + touch.release(0, control, control.width * 0.25, control.height * 0.75).commit() + compare(pressedSpy.count, ++pressedCount) + compare(movedSpy.count, ++movedCount) + compare(control.pressed, false) + compare(control.value, control.position) + verify(control.value <= 0.25 && control.value >= 0.0) + verify(control.position <= 0.25 && control.position >= 0.0) + + // QTBUG-53846 + touch.press(0, control).commit().release(0, control).commit() + compare(movedSpy.count, ++movedCount) + compare(pressedSpy.count, pressedCount += 2) + compare(control.value, 0.5) + compare(control.position, 0.5) + } + + function test_multiTouch() { + var control1 = createTemporaryObject(slider, testCase) + verify(control1) + + var pressedCount1 = 0 + var movedCount1 = 0 + + var pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"}) + verify(pressedSpy1.valid) + + var movedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "moved"}) + verify(movedSpy1.valid) + + var touch = touchEvent(control1) + touch.press(0, control1, 0, 0).commit().move(0, control1, control1.width, control1.height).commit() + + compare(pressedSpy1.count, ++pressedCount1) + compare(movedSpy1.count, ++movedCount1) + compare(control1.pressed, true) + compare(control1.position, 1.0) + + // second touch point on the same control is ignored + touch.stationary(0).press(1, control1, 0, 0).commit() + touch.stationary(0).move(1, control1).commit() + touch.stationary(0).release(1).commit() + + compare(pressedSpy1.count, pressedCount1) + compare(movedSpy1.count, movedCount1) + compare(control1.pressed, true) + compare(control1.position, 1.0) + + var control2 = createTemporaryObject(slider, testCase, {y: control1.height}) + verify(control2) + waitForRendering(control2) + + var pressedCount2 = 0 + var movedCount2 = 0 + + var pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"}) + verify(pressedSpy2.valid) + + var movedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "moved"}) + verify(movedSpy2.valid) + + // press the second slider + touch.stationary(0).press(2, control2, 0, 0).commit() + + compare(pressedSpy2.count, ++pressedCount2) + compare(movedSpy2.count, movedCount2) + compare(control2.pressed, true) + compare(control2.position, 0.0) + + compare(pressedSpy1.count, pressedCount1) + compare(movedSpy1.count, movedCount1) + compare(control1.pressed, true) + compare(control1.position, 1.0) + + // move both sliders + touch.move(0, control1).move(2, control2).commit() + + compare(pressedSpy2.count, pressedCount2) + compare(movedSpy2.count, ++movedCount2) + compare(control2.pressed, true) + compare(control2.position, 0.5) + compare(control2.value, 0.0) + + compare(pressedSpy1.count, pressedCount1) + compare(movedSpy1.count, ++movedCount1) + compare(control1.pressed, true) + compare(control1.position, 0.5) + compare(control1.value, 0.0) + + // release both sliders + touch.release(0, control1).release(2, control2).commit() + + compare(pressedSpy2.count, ++pressedCount2) + compare(movedSpy2.count, movedCount2) + compare(control2.pressed, false) + compare(control2.position, 0.5) + compare(control2.value, 0.5) + + compare(pressedSpy1.count, ++pressedCount1) + compare(movedSpy1.count, movedCount1) + compare(control1.pressed, false) + compare(control1.position, 0.5) + compare(control1.value, 0.5) + } + function test_keys_data() { return [ { tag: "horizontal", orientation: Qt.Horizontal, decrease: Qt.Key_Left, increase: Qt.Key_Right }, @@ -468,7 +646,11 @@ TestCase { ] } - function test_snapMode(data) { + function test_snapMode_mouse_data() { + return test_snapMode_data() + } + + function test_snapMode_mouse(data) { var control = createTemporaryObject(slider, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2}) verify(control) @@ -490,6 +672,33 @@ TestCase { verify(sliderCompare(control.position, data.positions[2])) } + function test_snapMode_touch_data() { + return test_snapMode_data() + } + + function test_snapMode_touch(data) { + var control = createTemporaryObject(slider, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2}) + verify(control) + + function sliderCompare(left, right) { + return Math.abs(left - right) < 0.05 + } + + var touch = touchEvent(control) + touch.press(0, control, control.leftPadding).commit() + compare(control.value, data.values[0]) + compare(control.position, data.positions[0]) + + touch.move(0, control, control.leftPadding + 0.15 * (control.availableWidth + control.handle.width / 2)).commit() + + verify(sliderCompare(control.value, data.values[1])) + verify(sliderCompare(control.position, data.positions[1])) + + touch.release(0, control, control.leftPadding + 0.15 * (control.availableWidth + control.handle.width / 2)).commit() + verify(sliderCompare(control.value, data.values[2])) + verify(sliderCompare(control.position, data.positions[2])) + } + function test_wheel_data() { return [ { tag: "horizontal", orientation: Qt.Horizontal, dx: 120, dy: 0 }, diff --git a/tests/auto/controls/default/tst_default.cpp b/tests/auto/controls/default/tst_default.cpp index 68c3ab8f..7bf3078f 100644 --- a/tests/auto/controls/default/tst_default.cpp +++ b/tests/auto/controls/default/tst_default.cpp @@ -40,5 +40,6 @@ int main(int argc, char *argv[]) { QTEST_ADD_GPU_BLACKLIST_SUPPORT QTEST_SET_MAIN_SOURCE_PATH + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); return quick_test_main(argc, argv, "tst_controls::Default", TST_CONTROLS_DATA); } diff --git a/tests/auto/controls/material/tst_material.cpp b/tests/auto/controls/material/tst_material.cpp index 2825b127..5d82c030 100644 --- a/tests/auto/controls/material/tst_material.cpp +++ b/tests/auto/controls/material/tst_material.cpp @@ -41,6 +41,7 @@ int main(int argc, char *argv[]) { QTEST_ADD_GPU_BLACKLIST_SUPPORT QTEST_SET_MAIN_SOURCE_PATH + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); QQuickStyle::setStyle("Material"); return quick_test_main(argc, argv, "tst_controls::Material", TST_CONTROLS_DATA); } diff --git a/tests/auto/controls/universal/tst_universal.cpp b/tests/auto/controls/universal/tst_universal.cpp index de4ca85c..c06e1fc3 100644 --- a/tests/auto/controls/universal/tst_universal.cpp +++ b/tests/auto/controls/universal/tst_universal.cpp @@ -41,6 +41,7 @@ int main(int argc, char *argv[]) { QTEST_ADD_GPU_BLACKLIST_SUPPORT QTEST_SET_MAIN_SOURCE_PATH + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); QQuickStyle::setStyle("Universal"); return quick_test_main(argc, argv, "tst_controls::Universal", TST_CONTROLS_DATA); } |