From 49423c18f5fa7f26b1a02a79055a05b67f322a6a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 07:51:12 +0200 Subject: QQuickRangeSlider: don't crash on press with null handles The handle visuals should be optional. There were a few missing checks for null pointers. Change-Id: I13e38f373428dbe0c8e338442370fbe7bb02c15a Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickrangeslider.cpp | 20 +++++++++++--------- tests/auto/controls/data/tst_rangeslider.qml | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/quicktemplates2/qquickrangeslider.cpp b/src/quicktemplates2/qquickrangeslider.cpp index 99815d36..5b03de88 100644 --- a/src/quicktemplates2/qquickrangeslider.cpp +++ b/src/quicktemplates2/qquickrangeslider.cpp @@ -435,16 +435,15 @@ void QQuickRangeSliderPrivate::handlePress(const QPointF &point) otherNode = first; } else { // find the nearest - const qreal firstDistance = QLineF(firstHandle->boundingRect().center(), - q->mapToItem(firstHandle, point)).length(); - const qreal secondDistance = QLineF(secondHandle->boundingRect().center(), - q->mapToItem(secondHandle, point)).length(); + const qreal firstPos = positionAt(q, firstHandle, point); + const qreal secondPos = positionAt(q, secondHandle, point); + const qreal firstDistance = qAbs(firstPos - first->position()); + const qreal secondDistance = qAbs(secondPos - second->position()); if (qFuzzyCompare(firstDistance, secondDistance)) { // same distance => choose the one that can be moved towards the press position const bool inverted = from > to; - const qreal pos = positionAt(q, firstHandle, point); - if ((!inverted && pos < first->position()) || (inverted && pos > first->position())) { + if ((!inverted && firstPos < first->position()) || (inverted && firstPos > first->position())) { hitNode = first; otherNode = second; } else { @@ -462,11 +461,14 @@ void QQuickRangeSliderPrivate::handlePress(const QPointF &point) if (hitNode) { hitNode->setPressed(true); - hitNode->handle()->setZ(1); + if (QQuickItem *handle = hitNode->handle()) + handle->setZ(1); QQuickRangeSliderNodePrivate::get(hitNode)->touchId = touchId; } - if (otherNode) - otherNode->handle()->setZ(0); + if (otherNode) { + if (QQuickItem *handle = otherNode->handle()) + handle->setZ(0); + } } void QQuickRangeSliderPrivate::handleMove(const QPointF &point) diff --git a/tests/auto/controls/data/tst_rangeslider.qml b/tests/auto/controls/data/tst_rangeslider.qml index 69ec6218..da6e24a1 100644 --- a/tests/auto/controls/data/tst_rangeslider.qml +++ b/tests/auto/controls/data/tst_rangeslider.qml @@ -896,4 +896,31 @@ TestCase { mouseMove(control, node.handle.x - 1, node.handle.y - 1) compare(node.hovered, false) } + + function test_nullHandles() { + var control = createTemporaryObject(sliderComponent, testCase, {"second.value": 1}) + verify(control) + + verify(control.first.handle) + verify(control.second.handle) + + control.first.handle = null + control.second.handle = null + + mousePress(control, control.leftPadding, control.height / 2) + verify(control.first.pressed, true) + compare(control.second.pressed, false) + + mouseRelease(control, control.leftPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, false) + + mousePress(control, control.width - control.rightPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, true) + + mouseRelease(control, control.width - control.rightPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, false) + } } -- cgit v1.2.3 From 1691662e66e5651c2995a07f9159a4395fe67373 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 07:55:38 +0200 Subject: tst_slider & tst_dial: add simple tests for null handles Just to ensure that these corner cases are also tested. We should not assume that the handle visuals exist, so these tests must not crash. Change-Id: I3dbf6ec78667bd9e99b3de79ffa8109858f9edd5 Reviewed-by: Mitch Curtis --- tests/auto/controls/data/tst_dial.qml | 13 +++++++++++++ tests/auto/controls/data/tst_slider.qml | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/auto/controls/data/tst_dial.qml b/tests/auto/controls/data/tst_dial.qml index 3955e60d..33b0dbea 100644 --- a/tests/auto/controls/data/tst_dial.qml +++ b/tests/auto/controls/data/tst_dial.qml @@ -579,4 +579,17 @@ TestCase { compare(control.value, 2.5) compare(control.position, 0.25) } + + function test_nullHandle() { + var control = createTemporaryObject(dialComponent, testCase) + verify(control) + + control.handle = null + + mousePress(control) + verify(control.pressed, true) + + mouseRelease(control) + compare(control.pressed, false) + } } diff --git a/tests/auto/controls/data/tst_slider.qml b/tests/auto/controls/data/tst_slider.qml index 127bdea6..8d696297 100644 --- a/tests/auto/controls/data/tst_slider.qml +++ b/tests/auto/controls/data/tst_slider.qml @@ -782,4 +782,17 @@ TestCase { compare(control.valueAt(0.5), data.values[2]) compare(control.valueAt(1.0), data.values[3]) } + + function test_nullHandle() { + var control = createTemporaryObject(slider, testCase) + verify(control) + + control.handle = null + + mousePress(control) + verify(control.pressed, true) + + mouseRelease(control) + compare(control.pressed, false) + } } -- cgit v1.2.3 From ce87c5be886d4c24045f7bdbaaf93fda3eed366f Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 10:09:00 +0200 Subject: tst_Drawer: disable compression for touch events Touch event compression makes auto tests unpredictable and is causing headache for the upcoming "smooth dragging" changes. Disable touch event compression and remove the manual flushing that is no longer needed. Change-Id: I1264203255d1c796829479026c84ba368f4758b3 Reviewed-by: Mitch Curtis --- tests/auto/drawer/tst_drawer.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index 251f5567..02e15257 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -58,6 +58,8 @@ class tst_Drawer : public QQmlDataTest Q_OBJECT private slots: + void initTestCase(); + void visible_data(); void visible(); @@ -89,6 +91,13 @@ private slots: void interactive(); }; + +void tst_Drawer::initTestCase() +{ + QQmlDataTest::initTestCase(); + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); +} + void tst_Drawer::visible_data() { QTest::addColumn("source"); @@ -755,10 +764,8 @@ void tst_Drawer::touch() // drag to close QTest::touchEvent(window, device.data()).press(0, QPoint(300, 100)); QTest::touchEvent(window, device.data()).move(0, QPoint(300 - drawer->dragMargin(), 100)); - for (int x = 300; x > 100; x -= 10) { + for (int x = 300; x > 100; x -= 10) QTest::touchEvent(window, device.data()).move(0, QPoint(x, 100)); - QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); - } QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); QTRY_COMPARE(drawer->position(), 0.5); QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); -- cgit v1.2.3 From 743d0132fe006699dd33e5b4254a36f4e2ec7f9f Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 08:50:22 +0200 Subject: QQuickDrawer: don't jump when dragging open Typically drawer's drag margin is so small that one can barely notice that it jumps when dragging begins. On systems with larger start drag distance, or when drawer's drag margin is manually increased, the jump was noticeable and disturbing. Unfortunately several tests had to be adjusted accordingly, because they were assuming that pressing at the edge and then dragging in the middle would immediately snap the drawer. Now the tests are sending a press at the edge, first moving past the start drag distance, and then moving to the actual tested position. Change-Id: Id8badf256fd4dd51f34db76ebe03bf6b15203cd9 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 10 ++++++++- tests/auto/drawer/tst_drawer.cpp | 40 ++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 0c53093b..b5b4100d 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -425,10 +425,18 @@ bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *ev if (!grabber || !grabber->keepMouseGrab()) { popupItem->grabMouse(); popupItem->setKeepMouseGrab(true); - offset = qMin(0.0, positionAt(movePoint) - position); + offset = positionAt(movePoint) - position; + + // don't jump when dragged open + if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; } } + // limit/reset the offset to the edge of the drawer when pushed from the outside + if (qFuzzyCompare(position, 1.0) && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; + if (popupItem->keepMouseGrab()) q->setPosition(positionAt(movePoint) - offset); event->accept(); diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index 02e15257..7fd4f0d9 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -249,19 +249,21 @@ void tst_Drawer::state() void tst_Drawer::position_data() { QTest::addColumn("edge"); + QTest::addColumn("press"); QTest::addColumn("from"); QTest::addColumn("to"); QTest::addColumn("position"); - QTest::newRow("top") << Qt::TopEdge << QPoint(100, 0) << QPoint(100, 100) << qreal(0.5); - QTest::newRow("left") << Qt::LeftEdge << QPoint(0, 100) << QPoint(100, 100) << qreal(0.5); - QTest::newRow("right") << Qt::RightEdge << QPoint(399, 100) << QPoint(300, 100) << qreal(0.5); - QTest::newRow("bottom") << Qt::BottomEdge << QPoint(100, 399) << QPoint(100, 300) << qreal(0.5); + QTest::newRow("top") << Qt::TopEdge << QPoint(100, 0) << QPoint(100, 50) << QPoint(100, 150) << qreal(0.5); + QTest::newRow("left") << Qt::LeftEdge << QPoint(0, 100) << QPoint(50, 100) << QPoint(150, 100) << qreal(0.5); + QTest::newRow("right") << Qt::RightEdge << QPoint(399, 100) << QPoint(350, 100) << QPoint(250, 100) << qreal(0.5); + QTest::newRow("bottom") << Qt::BottomEdge << QPoint(100, 399) << QPoint(100, 350) << QPoint(150, 250) << qreal(0.5); } void tst_Drawer::position() { QFETCH(Qt::Edge, edge); + QFETCH(QPoint, press); QFETCH(QPoint, from); QFETCH(QPoint, to); QFETCH(qreal, position); @@ -277,7 +279,8 @@ void tst_Drawer::position() QVERIFY(drawer); drawer->setEdge(edge); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, from); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, press); + QTest::mouseMove(window, from); QTest::mouseMove(window, to); QCOMPARE(drawer->position(), position); @@ -323,26 +326,30 @@ void tst_Drawer::dragMargin() drawer->setEdge(edge); drawer->setDragMargin(dragMargin); + const int startDragDistance = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5) + 1; + // drag from the left int leftX = qMax(0, dragMargin); - int leftDistance = drawer->width() * 0.45; + int leftDistance = startDragDistance + drawer->width() * 0.45; QVERIFY(leftDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(leftDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(leftX + startDragDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(leftX + leftDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromLeft); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDistance, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftX + leftDistance, drawer->height() / 2)); drawer->close(); QTRY_COMPARE(drawer->position(), qreal(0.0)); // drag from the right int rightX = qMin(window->width() - 1, window->width() - dragMargin); - int rightDistance = drawer->width() * 0.75; + int rightDistance = startDragDistance + drawer->width() * 0.75; QVERIFY(rightDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(rightX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(window->width() - rightDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(rightX - startDragDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(rightX - rightDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromRight); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDistance, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(rightX - rightDistance, drawer->height() / 2)); } static QRectF geometry(const QQuickItem *item) @@ -629,7 +636,8 @@ void tst_Drawer::multiple() // drag the left drawer open QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(0, window->height() / 2)); - QTest::mouseMove(window, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 4, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 4 * 3, window->height() / 2)); QCOMPARE(leftDrawer->position(), 0.5); QCOMPARE(rightDrawer->position(), 0.0); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDrawer->width() / 2, window->height() / 2)); @@ -708,7 +716,8 @@ void tst_Drawer::multiple() // drag the right drawer open QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() / 2)); - QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 4, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 4 * 3, window->height() / 2)); QCOMPARE(rightDrawer->position(), 0.5); QCOMPARE(leftDrawer->position(), 0.0); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); @@ -755,9 +764,10 @@ void tst_Drawer::touch() // drag to open QTest::touchEvent(window, device.data()).press(0, QPoint(0, 100)); - QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(50, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(150, 100)); QTRY_COMPARE(drawer->position(), 0.5); - QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); + QTest::touchEvent(window, device.data()).release(0, QPoint(150, 100)); QVERIFY(drawerOpenedSpy.wait()); QCOMPARE(drawer->position(), 1.0); -- cgit v1.2.3 From 039c25daac5ec6b96c0aa074eae2a983a1b5aabd Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 24 Apr 2017 12:07:46 +0200 Subject: Forward focus to the contentItem of editable spinboxes This fixes the scenario where a SpinBox is shown for the first time with focus, but its editor (TextInput) doesn't have active focus. tst_focus' keyNavigation.qml had to be adjusted, as an editable spinbox will now consume key events. Task-number: QTBUG-60356 Change-Id: I3af0260a22e9633ab6110d6adab7b39a22b849de Reviewed-by: J-P Nurmi --- src/quicktemplates2/qquickspinbox.cpp | 10 ++++++++++ src/quicktemplates2/qquickspinbox_p.h | 1 + tests/auto/controls/data/tst_spinbox.qml | 11 +++++++++++ tests/auto/focus/data/keyNavigation.qml | 1 - 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp index b6e99609..a41383aa 100644 --- a/src/quicktemplates2/qquickspinbox.cpp +++ b/src/quicktemplates2/qquickspinbox.cpp @@ -719,6 +719,16 @@ void QQuickSpinBox::decrease() setValue(d->value - d->effectiveStepSize()); } +void QQuickSpinBox::focusInEvent(QFocusEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::focusInEvent(event); + + // When an editable SpinBox gets focus, it must pass on the focus to its editor. + if (d->editable && d->contentItem && !d->contentItem->hasActiveFocus()) + d->contentItem->forceActiveFocus(event->reason()); +} + void QQuickSpinBox::hoverEnterEvent(QHoverEvent *event) { Q_D(QQuickSpinBox); diff --git a/src/quicktemplates2/qquickspinbox_p.h b/src/quicktemplates2/qquickspinbox_p.h index d51a2ccb..73cb3389 100644 --- a/src/quicktemplates2/qquickspinbox_p.h +++ b/src/quicktemplates2/qquickspinbox_p.h @@ -127,6 +127,7 @@ Q_SIGNALS: Q_REVISION(2) void inputMethodComposingChanged(); protected: + void focusInEvent(QFocusEvent *event) override; void hoverEnterEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent *event) override; void hoverLeaveEvent(QHoverEvent *event) override; diff --git a/tests/auto/controls/data/tst_spinbox.qml b/tests/auto/controls/data/tst_spinbox.qml index 6d681d60..4b2e38b3 100644 --- a/tests/auto/controls/data/tst_spinbox.qml +++ b/tests/auto/controls/data/tst_spinbox.qml @@ -51,6 +51,7 @@ import QtQuick 2.2 import QtTest 1.0 import QtQuick.Controls 2.2 +import QtQuick.Window 2.3 TestCase { id: testCase @@ -422,6 +423,16 @@ TestCase { compare(control.value, 100) } + function test_initialFocus() { + var window = testCase.Window.window + verify(window) + compare(window.activeFocusItem, window.contentItem) + + var control = createTemporaryObject(spinBox, testCase, { editable: true, focus: true }) + verify(control) + tryCompare(control.contentItem, "activeFocus", true) + } + function test_editable() { var control = createTemporaryObject(spinBox, testCase) verify(control) diff --git a/tests/auto/focus/data/keyNavigation.qml b/tests/auto/focus/data/keyNavigation.qml index 98a58ab5..ba7d5807 100644 --- a/tests/auto/focus/data/keyNavigation.qml +++ b/tests/auto/focus/data/keyNavigation.qml @@ -191,7 +191,6 @@ Item { SpinBox { id: spinbox objectName: "spinbox" - editable: true value: 50 KeyNavigation.left: radiobutton2 KeyNavigation.right: swtich -- cgit v1.2.3 From 381a2cf4a5a65f446b41240e8a6e6a6356988cb2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 20:41:44 +0200 Subject: QQuickPopup: tryClose() via handlePress() & handleRelease() This is a preparation step to make Drawer compatible with touch events. It will eventually override handlePress/Move/Release() to deal with both touch and mouse input. Change-Id: I8ae21f6909ca51f86f19dbe68a3e820e9af676ab Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 13 +-- src/quicktemplates2/qquickpopup.cpp | 171 +++++++++++++++++----------------- src/quicktemplates2/qquickpopup_p_p.h | 5 +- 3 files changed, 96 insertions(+), 93 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index b5b4100d..d8397e92 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -395,6 +395,8 @@ bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) { + handlePress(item->mapToScene(event->localPos())); + offset = 0; pressPoint = event->windowPos(); velocityCalculator.startMeasuring(pressPoint, event->timestamp()); @@ -410,7 +412,7 @@ bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *e bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) { Q_Q(QQuickDrawer); - Q_UNUSED(item); + handleMove(item->mapToScene(event->localPos())); // Don't react to synthesized mouse move events at INF,INF coordinates. // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover @@ -446,9 +448,9 @@ bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *ev bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) { - Q_UNUSED(item); - const bool wasGrabbed = ungrabMouse(event); + if (!wasGrabbed) + handleRelease(item->mapToScene(event->localPos())); popupItem->setKeepMouseGrab(false); pressPoint = QPoint(); @@ -651,21 +653,18 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) void QQuickDrawer::mousePressEvent(QMouseEvent *event) { Q_D(QQuickDrawer); - QQuickPopup::mousePressEvent(event); d->handleMousePressEvent(d->popupItem, event); } void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickDrawer); - QQuickPopup::mouseMoveEvent(event); d->handleMouseMoveEvent(d->popupItem, event); } void QQuickDrawer::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickDrawer); - QQuickPopup::mouseReleaseEvent(event); d->handleMouseReleaseEvent(d->popupItem, event); } @@ -690,12 +689,10 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) return false; case QEvent::MouseButtonPress: - d->tryClose(item, event); return d->handleMousePressEvent(item, static_cast(event)); case QEvent::MouseMove: return d->handleMouseMoveEvent(item, static_cast(event)); case QEvent::MouseButtonRelease: - d->tryClose(item, event); return d->handleMouseReleaseEvent(item, static_cast(event)); default: return QQuickPopup::overlayEvent(item, event); diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index f03e8182..a3ff4f20 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -44,7 +44,6 @@ #include "qquickdialog_p.h" #include -#include #include #include #include @@ -274,46 +273,19 @@ void QQuickPopupPrivate::closeOrReject() q->close(); } -bool QQuickPopupPrivate::tryClose(QQuickItem *item, QEvent *event) +bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags) { if (!interactive) return false; - bool isPress = false; - QQmlNullableValue pos; + static const QQuickPopup::ClosePolicy outsideFlags = QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnReleaseOutside; + static const QQuickPopup::ClosePolicy outsideParentFlags = QQuickPopup::CloseOnPressOutsideParent | QQuickPopup::CloseOnReleaseOutsideParent; - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - pos = static_cast(event)->pos(); - isPress = event->type() == QEvent::MouseButtonPress; - break; - - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - for (const QTouchEvent::TouchPoint &point : static_cast(event)->touchPoints()) { - if (point.state() == Qt::TouchPointMoved) - continue; - - pos = point.pos(); - isPress = point.state() == Qt::TouchPointPressed; - break; // TODO: multiple touch points - } - break; - - default: - break; - } - - if (pos.isNull) // ignore touch moves - return false; - - const bool onOutside = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutside : QQuickPopup::CloseOnReleaseOutside); - const bool onOutsideParent = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutsideParent : QQuickPopup::CloseOnReleaseOutsideParent); + const bool onOutside = closePolicy & (flags & outsideFlags); + const bool onOutsideParent = closePolicy & (flags & outsideParentFlags); if (onOutside || onOutsideParent) { - if (!popupItem->contains(item->mapToItem(popupItem, pos))) { - if (!onOutsideParent || !parentItem || !parentItem->contains(item->mapToItem(parentItem, pos))) { + if (!popupItem->contains(popupItem->mapFromScene(pos))) { + if (!onOutsideParent || !parentItem || !parentItem->contains(parentItem->mapFromScene(pos))) { closeOrReject(); return true; } @@ -335,16 +307,18 @@ bool QQuickPopupPrivate::acceptTouch(const QTouchEvent::TouchPoint &point) return false; } -void QQuickPopupPrivate::handlePress(const QPointF &) +void QQuickPopupPrivate::handlePress(const QPointF &point) { + tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); } void QQuickPopupPrivate::handleMove(const QPointF &) { } -void QQuickPopupPrivate::handleRelease(const QPointF &) +void QQuickPopupPrivate::handleRelease(const QPointF &point) { + tryClose(point, QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); touchId = -1; } @@ -360,6 +334,72 @@ void QQuickPopupPrivate::handleUngrab() touchId = -1; } +void QQuickPopupPrivate::handleMouseEvent(QQuickItem *item, QMouseEvent *event) +{ + QPointF pos = item->mapToScene(event->localPos()); + switch (event->type()) { + case QEvent::MouseButtonPress: + handlePress(pos); + break; + case QEvent::MouseMove: + handleMove(pos); + break; + case QEvent::MouseButtonRelease: + handleRelease(pos); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (acceptTouch(point)) + handlePress(item->mapToScene(point.pos())); + } + break; + + case QEvent::TouchUpdate: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (!acceptTouch(point)) + continue; + + switch (point.state()) { + case Qt::TouchPointPressed: + handlePress(item->mapToScene(point.pos())); + break; + case Qt::TouchPointMoved: + handleMove(item->mapToScene(point.pos())); + break; + case Qt::TouchPointReleased: + handleRelease(item->mapToScene(point.pos())); + break; + default: + break; + } + } + break; + + case QEvent::TouchEnd: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (acceptTouch(point)) + handleRelease(item->mapToScene(point.pos())); + } + break; + + case QEvent::TouchCancel: + handleUngrab(); + break; + + default: + break; + } +} + bool QQuickPopupPrivate::prepareEnterTransition() { Q_Q(QQuickPopup); @@ -1872,21 +1912,21 @@ void QQuickPopup::keyReleaseEvent(QKeyEvent *event) void QQuickPopup::mousePressEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handlePress(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } void QQuickPopup::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handleMove(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } void QQuickPopup::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handleRelease(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } @@ -1916,11 +1956,16 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: + if (d->modal) + event->accept(); + d->handleTouchEvent(item, static_cast(event)); + return d->modal; + case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: if (d->modal) event->accept(); - d->tryClose(item, event); + d->handleMouseEvent(item, static_cast(event)); return d->modal; default: @@ -1931,49 +1976,7 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) void QQuickPopup::touchEvent(QTouchEvent *event) { Q_D(QQuickPopup); - switch (event->type()) { - case QEvent::TouchBegin: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - if (d->acceptTouch(point)) - d->handlePress(point.pos()); - } - break; - - case QEvent::TouchUpdate: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - if (!d->acceptTouch(point)) - continue; - - switch (point.state()) { - case Qt::TouchPointPressed: - d->handlePress(point.pos()); - break; - case Qt::TouchPointMoved: - d->handleMove(point.pos()); - break; - case Qt::TouchPointReleased: - d->handleRelease(point.pos()); - break; - default: - break; - } - } - break; - - case QEvent::TouchEnd: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - if (d->acceptTouch(point)) - d->handleRelease(point.pos()); - } - break; - - case QEvent::TouchCancel: - d->handleUngrab(); - break; - - default: - break; - } + d->handleTouchEvent(d->popupItem, event); // TODO: QQuickPopup still relies on synthesized mouse events event->ignore(); diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index b68f18f6..2ca11efe 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -95,7 +95,7 @@ public: void init(); void closeOrReject(); - bool tryClose(QQuickItem *item, QEvent *event); + bool tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags); virtual bool acceptTouch(const QTouchEvent::TouchPoint &point); virtual void handlePress(const QPointF &point); @@ -103,6 +103,9 @@ public: virtual void handleRelease(const QPointF &point); virtual void handleUngrab(); + void handleMouseEvent(QQuickItem *item, QMouseEvent *event); + void handleTouchEvent(QQuickItem *item, QTouchEvent *event); + virtual void reposition(); virtual void resizeOverlay(); -- cgit v1.2.3 From a5eee7695ff09338d1481efde268ccdc62cd9143 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 22:04:58 +0200 Subject: Pass timestamp to QQuickPopupPrivate::handlePress/Move/Release() QQuickDrawer needs the timestamp to calculate velocity. Change-Id: I1ab5fc2e492948a424a8c56e4b9788fc3d489333 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 6 +++--- src/quicktemplates2/qquickpopup.cpp | 25 ++++++++++++++----------- src/quicktemplates2/qquickpopup_p_p.h | 6 +++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index d8397e92..862b36dc 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -395,7 +395,7 @@ bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) { - handlePress(item->mapToScene(event->localPos())); + handlePress(item->mapToScene(event->localPos()), event->timestamp()); offset = 0; pressPoint = event->windowPos(); @@ -412,7 +412,7 @@ bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *e bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) { Q_Q(QQuickDrawer); - handleMove(item->mapToScene(event->localPos())); + handleMove(item->mapToScene(event->localPos()), event->timestamp()); // Don't react to synthesized mouse move events at INF,INF coordinates. // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover @@ -450,7 +450,7 @@ bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent { const bool wasGrabbed = ungrabMouse(event); if (!wasGrabbed) - handleRelease(item->mapToScene(event->localPos())); + handleRelease(item->mapToScene(event->localPos()), event->timestamp()); popupItem->setKeepMouseGrab(false); pressPoint = QPoint(); diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index a3ff4f20..b4c3b84e 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -307,17 +307,20 @@ bool QQuickPopupPrivate::acceptTouch(const QTouchEvent::TouchPoint &point) return false; } -void QQuickPopupPrivate::handlePress(const QPointF &point) +void QQuickPopupPrivate::handlePress(const QPointF &point, ulong timestamp) { + Q_UNUSED(timestamp); tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); } -void QQuickPopupPrivate::handleMove(const QPointF &) +void QQuickPopupPrivate::handleMove(const QPointF &, ulong timestamp) { + Q_UNUSED(timestamp); } -void QQuickPopupPrivate::handleRelease(const QPointF &point) +void QQuickPopupPrivate::handleRelease(const QPointF &point, ulong timestamp) { + Q_UNUSED(timestamp); tryClose(point, QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); touchId = -1; } @@ -339,13 +342,13 @@ void QQuickPopupPrivate::handleMouseEvent(QQuickItem *item, QMouseEvent *event) QPointF pos = item->mapToScene(event->localPos()); switch (event->type()) { case QEvent::MouseButtonPress: - handlePress(pos); + handlePress(pos, event->timestamp()); break; case QEvent::MouseMove: - handleMove(pos); + handleMove(pos, event->timestamp()); break; case QEvent::MouseButtonRelease: - handleRelease(pos); + handleRelease(pos, event->timestamp()); break; default: Q_UNREACHABLE(); @@ -359,7 +362,7 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) case QEvent::TouchBegin: for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { if (acceptTouch(point)) - handlePress(item->mapToScene(point.pos())); + handlePress(item->mapToScene(point.pos()), event->timestamp()); } break; @@ -370,13 +373,13 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) switch (point.state()) { case Qt::TouchPointPressed: - handlePress(item->mapToScene(point.pos())); + handlePress(item->mapToScene(point.pos()), event->timestamp()); break; case Qt::TouchPointMoved: - handleMove(item->mapToScene(point.pos())); + handleMove(item->mapToScene(point.pos()), event->timestamp()); break; case Qt::TouchPointReleased: - handleRelease(item->mapToScene(point.pos())); + handleRelease(item->mapToScene(point.pos()), event->timestamp()); break; default: break; @@ -387,7 +390,7 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) case QEvent::TouchEnd: for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { if (acceptTouch(point)) - handleRelease(item->mapToScene(point.pos())); + handleRelease(item->mapToScene(point.pos()), event->timestamp()); } break; diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 2ca11efe..56226bd9 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -98,9 +98,9 @@ public: bool tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags); virtual bool acceptTouch(const QTouchEvent::TouchPoint &point); - virtual void handlePress(const QPointF &point); - virtual void handleMove(const QPointF &point); - virtual void handleRelease(const QPointF &point); + virtual void handlePress(const QPointF &point, ulong timestamp); + virtual void handleMove(const QPointF &point, ulong timestamp); + virtual void handleRelease(const QPointF &point, ulong timestamp); virtual void handleUngrab(); void handleMouseEvent(QQuickItem *item, QMouseEvent *event); -- cgit v1.2.3 From c6151f84a7bd7cadb5f86d58582fb659ce4fae09 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 22:06:47 +0200 Subject: QQuickDrawer: override handlePress/Move/Release/Ungrab() Change-Id: Id947e868aa1c89aaab0cb6988b76f0cfe25d8339 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 39 +++++++++++++++++++++++----------- src/quicktemplates2/qquickdrawer_p.h | 1 - src/quicktemplates2/qquickdrawer_p_p.h | 5 +++++ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 862b36dc..7ecd1558 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -393,13 +393,36 @@ bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) return wasGrabbed; } -bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +void QQuickDrawerPrivate::handlePress(const QPointF &point, ulong timestamp) { - handlePress(item->mapToScene(event->localPos()), event->timestamp()); + QQuickPopupPrivate::handlePress(point, timestamp); offset = 0; - pressPoint = event->windowPos(); - velocityCalculator.startMeasuring(pressPoint, event->timestamp()); + pressPoint = point; + velocityCalculator.startMeasuring(point, timestamp); +} + +void QQuickDrawerPrivate::handleMove(const QPointF &point, ulong timestamp) +{ + QQuickPopupPrivate::handleMove(point, timestamp); +} + +void QQuickDrawerPrivate::handleRelease(const QPointF &point, ulong timestamp) +{ + QQuickPopupPrivate::handleRelease(point, timestamp); +} + +void QQuickDrawerPrivate::handleUngrab() +{ + QQuickPopupPrivate::handleUngrab(); + + pressPoint = QPoint(); + velocityCalculator.reset(); +} + +bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + handlePress(item->mapToScene(event->localPos()), event->timestamp()); // don't block press events // a) outside a non-modal drawer, @@ -668,14 +691,6 @@ void QQuickDrawer::mouseReleaseEvent(QMouseEvent *event) d->handleMouseReleaseEvent(d->popupItem, event); } -void QQuickDrawer::mouseUngrabEvent() -{ - Q_D(QQuickDrawer); - QQuickPopup::mouseUngrabEvent(); - d->pressPoint = QPoint(); - d->velocityCalculator.reset(); -} - bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) { Q_D(QQuickDrawer); diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index 68b58362..f4987e8b 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -89,7 +89,6 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; - void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index dc17647d..bb341d50 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -74,6 +74,11 @@ public: bool grabMouse(QMouseEvent *event); bool ungrabMouse(QMouseEvent *event); + void handlePress(const QPointF &point, ulong timestamp) override; + void handleMove(const QPointF &point, ulong timestamp) override; + void handleRelease(const QPointF &point, ulong timestamp) override; + void handleUngrab() override; + bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); -- cgit v1.2.3 From 279ab48f8bf61a06d5a334924f0b01f45e202cef Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 22:35:59 +0200 Subject: QQuickDrawer: move mouse/touch release handling to handleRelease() Change-Id: I279fe0a0c77e482a414424f90827e85a91df221a Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 150 +++++++++++++++------------------ src/quicktemplates2/qquickdrawer_p.h | 1 - src/quicktemplates2/qquickdrawer_p_p.h | 2 - 3 files changed, 66 insertions(+), 87 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 7ecd1558..02281d5e 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -333,66 +333,6 @@ bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) static const qreal openCloseVelocityThreshold = 300; -bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) -{ - bool wasGrabbed = popupItem->keepMouseGrab(); - if (wasGrabbed) { - const QPointF releasePoint = event->windowPos(); - velocityCalculator.stopMeasuring(releasePoint, event->timestamp()); - - qreal velocity = 0; - if (edge == Qt::LeftEdge || edge == Qt::RightEdge) - velocity = velocityCalculator.velocity().x(); - else - velocity = velocityCalculator.velocity().y(); - - // the velocity is calculated so that swipes from left to right - // and top to bottom have positive velocity, and swipes from right - // to left and bottom to top have negative velocity. - // - // - top/left edge: positive velocity opens, negative velocity closes - // - bottom/right edge: negative velocity opens, positive velocity closes - // - // => invert the velocity for bottom and right edges, for the threshold comparison below - if (edge == Qt::RightEdge || edge == Qt::BottomEdge) - velocity = -velocity; - - if (position > 0.7 || velocity > openCloseVelocityThreshold) { - transitionManager.transitionEnter(); - } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { - transitionManager.transitionExit(); - } else { - switch (edge) { - case Qt::LeftEdge: - if (releasePoint.x() - pressPoint.x() > 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::RightEdge: - if (releasePoint.x() - pressPoint.x() < 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::TopEdge: - if (releasePoint.y() - pressPoint.y() > 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::BottomEdge: - if (releasePoint.y() - pressPoint.y() < 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - } - } - } - return wasGrabbed; -} - void QQuickDrawerPrivate::handlePress(const QPointF &point, ulong timestamp) { QQuickPopupPrivate::handlePress(point, timestamp); @@ -409,7 +349,68 @@ void QQuickDrawerPrivate::handleMove(const QPointF &point, ulong timestamp) void QQuickDrawerPrivate::handleRelease(const QPointF &point, ulong timestamp) { - QQuickPopupPrivate::handleRelease(point, timestamp); + pressPoint = QPointF(); + + if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) { + velocityCalculator.reset(); + QQuickPopupPrivate::handleRelease(point, timestamp); + return; + } + + velocityCalculator.stopMeasuring(point, timestamp); + + qreal velocity = 0; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + velocity = velocityCalculator.velocity().x(); + else + velocity = velocityCalculator.velocity().y(); + + // the velocity is calculated so that swipes from left to right + // and top to bottom have positive velocity, and swipes from right + // to left and bottom to top have negative velocity. + // + // - top/left edge: positive velocity opens, negative velocity closes + // - bottom/right edge: negative velocity opens, positive velocity closes + // + // => invert the velocity for bottom and right edges, for the threshold comparison below + if (edge == Qt::RightEdge || edge == Qt::BottomEdge) + velocity = -velocity; + + if (position > 0.7 || velocity > openCloseVelocityThreshold) { + transitionManager.transitionEnter(); + } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { + transitionManager.transitionExit(); + } else { + switch (edge) { + case Qt::LeftEdge: + if (point.x() - pressPoint.x() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::RightEdge: + if (point.x() - pressPoint.x() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::TopEdge: + if (point.y() - pressPoint.y() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::BottomEdge: + if (point.y() - pressPoint.y() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + } + } + + popupItem->setKeepMouseGrab(false); + popupItem->setKeepTouchGrab(false); } void QQuickDrawerPrivate::handleUngrab() @@ -469,19 +470,6 @@ bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *ev return popupItem->keepMouseGrab(); } -bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) -{ - const bool wasGrabbed = ungrabMouse(event); - if (!wasGrabbed) - handleRelease(item->mapToScene(event->localPos()), event->timestamp()); - - popupItem->setKeepMouseGrab(false); - pressPoint = QPoint(); - event->accept(); - - return wasGrabbed; -} - static QList prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) { QList actions; @@ -667,10 +655,12 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) case QEvent::MouseMove: return d->handleMouseMoveEvent(child, static_cast(event)); case QEvent::MouseButtonRelease: - return d->handleMouseReleaseEvent(child, static_cast(event)); + d->handleMouseEvent(child, static_cast(event)); + break; default: - return false; + break; } + return false; } void QQuickDrawer::mousePressEvent(QMouseEvent *event) @@ -685,12 +675,6 @@ void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) d->handleMouseMoveEvent(d->popupItem, event); } -void QQuickDrawer::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QQuickDrawer); - d->handleMouseReleaseEvent(d->popupItem, event); -} - bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) { Q_D(QQuickDrawer); @@ -707,8 +691,6 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) return d->handleMousePressEvent(item, static_cast(event)); case QEvent::MouseMove: return d->handleMouseMoveEvent(item, static_cast(event)); - case QEvent::MouseButtonRelease: - return d->handleMouseReleaseEvent(item, static_cast(event)); default: return QQuickPopup::overlayEvent(item, event); } diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index f4987e8b..74b92717 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -88,7 +88,6 @@ protected: bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; bool overlayEvent(QQuickItem *item, QEvent *event) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index bb341d50..5117ef77 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -72,7 +72,6 @@ public: bool startDrag(QEvent *event); bool grabMouse(QMouseEvent *event); - bool ungrabMouse(QMouseEvent *event); void handlePress(const QPointF &point, ulong timestamp) override; void handleMove(const QPointF &point, ulong timestamp) override; @@ -81,7 +80,6 @@ public: bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); - bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); bool prepareEnterTransition() override; bool prepareExitTransition() override; -- cgit v1.2.3 From 6a9042ad7d79865fb2077081d4357536d8e460a9 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 23:00:04 +0200 Subject: Simplify QQuickDrawerPrivate::startDrag() The purpose is to calculate whether a press is within the drag margin ie. whether a drawer can start dragging. There seems to be no reason to call the clumsy QQuickWindowPrivate::dragOverThreshold() method, which is different for mouse and touch events. In addition to the start drag distance, it actually calculates start drag velocity too, which is entirely irrelevant for us. Instead, we can do ourselves a very simple mouse vs. touch agnostic calculation whether a press lands within the drag margin. Change-Id: I356a349fdef3df1455715ae7572dbcb9e8100f93 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 38 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 02281d5e..0fb26772 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -234,35 +234,17 @@ void QQuickDrawerPrivate::resizeOverlay() dimmer->setSize(geometry.size()); } -static bool mouseDragOverThreshold(QQuickDrawer *drawer, QMouseEvent *event) +static bool isWithinDragMargin(QQuickDrawer *drawer, const QPointF &pos) { switch (drawer->edge()) { case Qt::LeftEdge: - return !QQuickWindowPrivate::dragOverThreshold(event->windowPos().x(), Qt::XAxis, event, drawer->dragMargin()); + return pos.x() <= drawer->dragMargin(); case Qt::RightEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->width() - event->windowPos().x(), Qt::XAxis, event, drawer->dragMargin()); + return pos.x() >= drawer->window()->width() - drawer->dragMargin(); case Qt::TopEdge: - return !QQuickWindowPrivate::dragOverThreshold(event->windowPos().y(), Qt::YAxis, event, drawer->dragMargin()); + return pos.y() <= drawer->dragMargin(); case Qt::BottomEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->height() - event->windowPos().y(), Qt::YAxis, event, drawer->dragMargin()); - default: - Q_UNREACHABLE(); - break; - } - return false; -} - -static bool touchDragOverThreshold(QQuickDrawer *drawer, const QTouchEvent::TouchPoint &point) -{ - switch (drawer->edge()) { - case Qt::LeftEdge: - return !QQuickWindowPrivate::dragOverThreshold(point.scenePos().x(), Qt::XAxis, &point, drawer->dragMargin()); - case Qt::RightEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->width() - point.scenePos().x(), Qt::XAxis, &point, drawer->dragMargin()); - case Qt::TopEdge: - return !QQuickWindowPrivate::dragOverThreshold(point.scenePos().y(), Qt::YAxis, &point, drawer->dragMargin()); - case Qt::BottomEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->height() - point.scenePos().y(), Qt::YAxis, &point, drawer->dragMargin()); + return pos.y() >= drawer->window()->height() - drawer->dragMargin(); default: Q_UNREACHABLE(); break; @@ -276,19 +258,19 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event) if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin)) return false; - bool overThreshold = false; + bool withinMargin = false; bool mouse = event->type() == QEvent::MouseButtonPress; if (mouse) { - overThreshold = mouseDragOverThreshold(q, static_cast(event)); + withinMargin = isWithinDragMargin(q, static_cast(event)->windowPos()); } else { for (const QTouchEvent::TouchPoint &point : static_cast(event)->touchPoints()) { - if (touchDragOverThreshold(q, point)) { - overThreshold = true; + if (isWithinDragMargin(q, point.scenePos())) { + withinMargin = true; break; } } } - if (!overThreshold) + if (!withinMargin) return false; prepareEnterTransition(); -- cgit v1.2.3 From 35415a2d70d5103d5a25c369fd42fec9a3842262 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 23:23:08 +0200 Subject: QQuickDrawer: move mouse/touch press handling to handlePress() Change-Id: I8744525e1f185fa1e58d9e5d81f233a3aad3b2e1 Reviewed-by: Robin Burchell Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 25 ++----------------------- src/quicktemplates2/qquickdrawer_p.h | 1 - src/quicktemplates2/qquickdrawer_p_p.h | 1 - 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 0fb26772..41b169a4 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -276,7 +276,7 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event) prepareEnterTransition(); reposition(); if (mouse) { - handleMousePressEvent(window->contentItem(), static_cast(event)); + handleMouseEvent(window->contentItem(), static_cast(event)); return true; } return false; @@ -403,18 +403,6 @@ void QQuickDrawerPrivate::handleUngrab() velocityCalculator.reset(); } -bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) -{ - handlePress(item->mapToScene(event->localPos()), event->timestamp()); - - // don't block press events - // a) outside a non-modal drawer, - // b) to drawer children, or - // c) outside a modal drawer's background dimming - event->setAccepted(modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(pressPoint)))); - return event->isAccepted(); -} - bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) { Q_Q(QQuickDrawer); @@ -632,10 +620,9 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) { Q_D(QQuickDrawer); switch (event->type()) { - case QEvent::MouseButtonPress: - return d->handleMousePressEvent(child, static_cast(event)); case QEvent::MouseMove: return d->handleMouseMoveEvent(child, static_cast(event)); + case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: d->handleMouseEvent(child, static_cast(event)); break; @@ -645,12 +632,6 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) return false; } -void QQuickDrawer::mousePressEvent(QMouseEvent *event) -{ - Q_D(QQuickDrawer); - d->handleMousePressEvent(d->popupItem, event); -} - void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickDrawer); @@ -669,8 +650,6 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) event->ignore(); return false; - case QEvent::MouseButtonPress: - return d->handleMousePressEvent(item, static_cast(event)); case QEvent::MouseMove: return d->handleMouseMoveEvent(item, static_cast(event)); default: diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index 74b92717..38b425be 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -86,7 +86,6 @@ Q_SIGNALS: protected: bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; bool overlayEvent(QQuickItem *item, QEvent *event) override; diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index 5117ef77..9fca728f 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -78,7 +78,6 @@ public: void handleRelease(const QPointF &point, ulong timestamp) override; void handleUngrab() override; - bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); bool prepareEnterTransition() override; -- cgit v1.2.3 From cacac5a630d6e91417f077e072455a94e78bf571 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 24 Apr 2017 23:52:44 +0200 Subject: Eradicate QQuickDrawerPrivate::handleMouseMoveEvent() Change-Id: I4759baa3691498fce8c291cbb0d3f77e9e2fd8d6 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 73 +++++++++++++++------------------- src/quicktemplates2/qquickdrawer_p_p.h | 4 +- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 41b169a4..c2597cc4 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -282,9 +282,11 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event) return false; } -bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) +bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event) { Q_Q(QQuickDrawer); + handleMouseEvent(item, event); + if (!window || !interactive || popupItem->keepMouseGrab()) return false; @@ -310,6 +312,19 @@ bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; } + if (overThreshold) { + QQuickItem *grabber = window->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + popupItem->grabMouse(); + popupItem->setKeepMouseGrab(true); + offset = positionAt(movePoint) - position; + + // don't jump when dragged open + if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; + } + } + return overThreshold; } @@ -326,7 +341,15 @@ void QQuickDrawerPrivate::handlePress(const QPointF &point, ulong timestamp) void QQuickDrawerPrivate::handleMove(const QPointF &point, ulong timestamp) { + Q_Q(QQuickDrawer); QQuickPopupPrivate::handleMove(point, timestamp); + + // limit/reset the offset to the edge of the drawer when pushed from the outside + if (qFuzzyCompare(position, 1.0) && !popupItem->contains(popupItem->mapFromScene(point))) + offset = 0; + + if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab()) + q->setPosition(positionAt(point) - offset); } void QQuickDrawerPrivate::handleRelease(const QPointF &point, ulong timestamp) @@ -403,43 +426,6 @@ void QQuickDrawerPrivate::handleUngrab() velocityCalculator.reset(); } -bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) -{ - Q_Q(QQuickDrawer); - handleMove(item->mapToScene(event->localPos()), event->timestamp()); - - // Don't react to synthesized mouse move events at INF,INF coordinates. - // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover - // on touch release (QTBUG-55995). - if (qIsInf(event->screenPos().x()) || qIsInf(event->screenPos().y())) - return true; - - const QPointF movePoint = event->windowPos(); - - if (grabMouse(event)) { - QQuickItem *grabber = window->mouseGrabberItem(); - if (!grabber || !grabber->keepMouseGrab()) { - popupItem->grabMouse(); - popupItem->setKeepMouseGrab(true); - offset = positionAt(movePoint) - position; - - // don't jump when dragged open - if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) - offset = 0; - } - } - - // limit/reset the offset to the edge of the drawer when pushed from the outside - if (qFuzzyCompare(position, 1.0) && !popupItem->contains(popupItem->mapFromScene(movePoint))) - offset = 0; - - if (popupItem->keepMouseGrab()) - q->setPosition(positionAt(movePoint) - offset); - event->accept(); - - return popupItem->keepMouseGrab(); -} - static QList prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) { QList actions; @@ -621,7 +607,7 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) Q_D(QQuickDrawer); switch (event->type()) { case QEvent::MouseMove: - return d->handleMouseMoveEvent(child, static_cast(event)); + return d->grabMouse(child, static_cast(event)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: d->handleMouseEvent(child, static_cast(event)); @@ -635,7 +621,7 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickDrawer); - d->handleMouseMoveEvent(d->popupItem, event); + d->grabMouse(d->popupItem, event); } bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) @@ -651,10 +637,13 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) return false; case QEvent::MouseMove: - return d->handleMouseMoveEvent(item, static_cast(event)); + return d->grabMouse(item, static_cast(event)); + default: - return QQuickPopup::overlayEvent(item, event); + break; } + + return QQuickPopup::overlayEvent(item, event); } void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index 9fca728f..4c577982 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -71,15 +71,13 @@ public: void resizeOverlay() override; bool startDrag(QEvent *event); - bool grabMouse(QMouseEvent *event); + bool grabMouse(QQuickItem *item, QMouseEvent *event); void handlePress(const QPointF &point, ulong timestamp) override; void handleMove(const QPointF &point, ulong timestamp) override; void handleRelease(const QPointF &point, ulong timestamp) override; void handleUngrab() override; - bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); - bool prepareEnterTransition() override; bool prepareExitTransition() override; -- cgit v1.2.3 From 927b1280bb33a240c33a54d8e72531ab606ac590 Mon Sep 17 00:00:00 2001 From: Nikita Krupenko Date: Mon, 24 Apr 2017 22:09:20 +0300 Subject: Material: fix elevation of the RoundButton In accordance with Material Design guidelines, elevation for resting and pressed states of the raised FAB should be 6 and 12 accordingly. Change-Id: I526bb8579aa760c60e25cbeb071b912a30293615 Reviewed-by: J-P Nurmi Reviewed-by: Mitch Curtis --- src/imports/controls/material/RoundButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/controls/material/RoundButton.qml b/src/imports/controls/material/RoundButton.qml index 249acb20..9660a1e4 100644 --- a/src/imports/controls/material/RoundButton.qml +++ b/src/imports/controls/material/RoundButton.qml @@ -52,7 +52,7 @@ T.RoundButton { padding: 12 Material.elevation: flat ? control.down || control.hovered ? 2 : 0 - : control.down ? 8 : 2 + : control.down ? 12 : 6 Material.background: flat ? "transparent" : undefined contentItem: Text { -- cgit v1.2.3 From cf67bb976e082105994ff00a2616129833edb857 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 26 Apr 2017 10:35:00 +0200 Subject: Popups: fix non-dimming modal background leaking events through This piece of code, which was thought unnecessary, went missing when moved from mousePressEvent() to handlePress() in 7faafa4. It is needed after all for non-dimming modal popups, because in that case we simply cannot rely on the background dimming blocking the press... Task-number: QTBUG-60405 Change-Id: I53d89133eeae4ad8531b3c1e07c3b22295d438de Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickoverlay.cpp | 11 ++++++++--- tests/auto/popup/tst_popup.cpp | 32 ++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 58ec5582..10ac3623 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -201,10 +201,15 @@ void QQuickOverlayPrivate::handlePress(QEvent *event) } if (!mouseGrabberPopup) { - // allow non-modal popups to close themselves + // allow non-modal popups to close themselves, + // and non-dimming modal popups to block the event const auto popups = stackingOrderPopups(); - for (QQuickPopup *popup : popups) - popup->overlayEvent(q, event); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(q, event)) { + setMouseGrabberPopup(popup); + return; + } + } } event->ignore(); diff --git a/tests/auto/popup/tst_popup.cpp b/tests/auto/popup/tst_popup.cpp index a6de0c03..0e9b55f7 100644 --- a/tests/auto/popup/tst_popup.cpp +++ b/tests/auto/popup/tst_popup.cpp @@ -160,13 +160,26 @@ void tst_popup::state() void tst_popup::overlay_data() { QTest::addColumn("source"); - QTest::newRow("Window") << "window.qml"; - QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; + QTest::addColumn("modal"); + QTest::addColumn("dim"); + + QTest::newRow("Window") << "window.qml" << false << false; + QTest::newRow("Window,dim") << "window.qml" << false << true; + QTest::newRow("Window,modal") << "window.qml" << true << false; + QTest::newRow("Window,modal,dim") << "window.qml" << true << true; + + QTest::newRow("ApplicationWindow") << "applicationwindow.qml" << false << false; + QTest::newRow("ApplicationWindow,dim") << "applicationwindow.qml" << false << true; + QTest::newRow("ApplicationWindow,modal") << "applicationwindow.qml" << true << false; + QTest::newRow("ApplicationWindow,modal,dim") << "applicationwindow.qml" << true << true; } void tst_popup::overlay() { QFETCH(QString, source); + QFETCH(bool, modal); + QFETCH(bool, dim); + QQuickApplicationHelper helper(this, source); QQuickWindow *window = helper.window; @@ -210,7 +223,8 @@ void tst_popup::overlay() QVERIFY(!popup->isVisible()); QVERIFY(!overlay->isVisible()); - popup->setModal(true); + popup->setDim(dim); + popup->setModal(modal); popup->setClosePolicy(QQuickPopup::CloseOnReleaseOutside); popup->open(); @@ -223,10 +237,20 @@ void tst_popup::overlay() QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); QCOMPARE(overlayPressedSignal.count(), 2); + + #define comment "Non-modal popups do not yet support CloseOnReleaseXxx" + #define QEXPECT_NON_MODAL_POPUP_FAILS() \ + QEXPECT_FAIL("Window", comment, Continue); \ + QEXPECT_FAIL("Window,dim", comment, Continue); \ + QEXPECT_FAIL("ApplicationWindow", comment, Continue); \ + QEXPECT_FAIL("ApplicationWindow,dim", comment, Continue); + + QEXPECT_NON_MODAL_POPUP_FAILS() QCOMPARE(overlayReleasedSignal.count(), 1); + QEXPECT_NON_MODAL_POPUP_FAILS() QVERIFY(!popup->isVisible()); - QVERIFY(!overlay->isVisible()); + QCOMPARE(overlay->isVisible(), popup->isVisible()); } void tst_popup::zOrder_data() -- cgit v1.2.3 From 3326439361d3ec4e60bcdb189e6bfb75189136ca Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 26 Apr 2017 13:50:54 +0200 Subject: Revise the press/move/release handlers Otherwise the handlers are not able to control whether presses, moves and releases should be blocked or not. a) outside a non-modal popup, b) to popup children/content, or b) outside a modal popups's background dimming Change-Id: I637295fc3122cdcddb7727ec3939ec6a68a3cf98 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 28 ++++++++++------ src/quicktemplates2/qquickdrawer_p_p.h | 6 ++-- src/quicktemplates2/qquickpopup.cpp | 58 ++++++++++++++++++---------------- src/quicktemplates2/qquickpopup_p_p.h | 12 ++++--- 4 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index c2597cc4..66ba74a2 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -330,36 +330,42 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event) static const qreal openCloseVelocityThreshold = 300; -void QQuickDrawerPrivate::handlePress(const QPointF &point, ulong timestamp) +bool QQuickDrawerPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) { - QQuickPopupPrivate::handlePress(point, timestamp); + if (!QQuickPopupPrivate::handlePress(item, point, timestamp)) + return false; offset = 0; pressPoint = point; velocityCalculator.startMeasuring(point, timestamp); + + return true; } -void QQuickDrawerPrivate::handleMove(const QPointF &point, ulong timestamp) +bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp) { Q_Q(QQuickDrawer); - QQuickPopupPrivate::handleMove(point, timestamp); + if (!QQuickPopupPrivate::handleMove(item, point, timestamp)) + return false; // limit/reset the offset to the edge of the drawer when pushed from the outside if (qFuzzyCompare(position, 1.0) && !popupItem->contains(popupItem->mapFromScene(point))) offset = 0; - if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab()) + bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); + if (isGrabbed) q->setPosition(positionAt(point) - offset); + + return isGrabbed; } -void QQuickDrawerPrivate::handleRelease(const QPointF &point, ulong timestamp) +bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp) { pressPoint = QPointF(); if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) { velocityCalculator.reset(); - QQuickPopupPrivate::handleRelease(point, timestamp); - return; + return QQuickPopupPrivate::handleRelease(item, point, timestamp); } velocityCalculator.stopMeasuring(point, timestamp); @@ -414,8 +420,11 @@ void QQuickDrawerPrivate::handleRelease(const QPointF &point, ulong timestamp) } } + bool wasGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); popupItem->setKeepMouseGrab(false); popupItem->setKeepTouchGrab(false); + + return wasGrabbed; } void QQuickDrawerPrivate::handleUngrab() @@ -610,8 +619,7 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) return d->grabMouse(child, static_cast(event)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - d->handleMouseEvent(child, static_cast(event)); - break; + return d->handleMouseEvent(child, static_cast(event)); default: break; } diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index 4c577982..bcfd7206 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -73,9 +73,9 @@ public: bool startDrag(QEvent *event); bool grabMouse(QQuickItem *item, QMouseEvent *event); - void handlePress(const QPointF &point, ulong timestamp) override; - void handleMove(const QPointF &point, ulong timestamp) override; - void handleRelease(const QPointF &point, ulong timestamp) override; + bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp) override; + bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp) override; + bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp) override; void handleUngrab() override; bool prepareEnterTransition() override; diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index b4c3b84e..ccbfd3c8 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -307,22 +307,34 @@ bool QQuickPopupPrivate::acceptTouch(const QTouchEvent::TouchPoint &point) return false; } -void QQuickPopupPrivate::handlePress(const QPointF &point, ulong timestamp) +bool QQuickPopupPrivate::blockInput(QQuickItem *item, const QPointF &point) const +{ + // don't block presses and releases + // a) outside a non-modal popup, + // b) to popup children/content, or + // b) outside a modal popups's background dimming + return modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point))); +} + +bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) { Q_UNUSED(timestamp); tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + return blockInput(item, point); } -void QQuickPopupPrivate::handleMove(const QPointF &, ulong timestamp) +bool QQuickPopupPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp) { Q_UNUSED(timestamp); + return blockInput(item, point); } -void QQuickPopupPrivate::handleRelease(const QPointF &point, ulong timestamp) +bool QQuickPopupPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp) { Q_UNUSED(timestamp); tryClose(point, QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); touchId = -1; + return blockInput(item, point); } void QQuickPopupPrivate::handleUngrab() @@ -337,32 +349,29 @@ void QQuickPopupPrivate::handleUngrab() touchId = -1; } -void QQuickPopupPrivate::handleMouseEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickPopupPrivate::handleMouseEvent(QQuickItem *item, QMouseEvent *event) { QPointF pos = item->mapToScene(event->localPos()); switch (event->type()) { case QEvent::MouseButtonPress: - handlePress(pos, event->timestamp()); - break; + return handlePress(item, pos, event->timestamp()); case QEvent::MouseMove: - handleMove(pos, event->timestamp()); - break; + return handleMove(item, pos, event->timestamp()); case QEvent::MouseButtonRelease: - handleRelease(pos, event->timestamp()); - break; + return handleRelease(item, pos, event->timestamp()); default: Q_UNREACHABLE(); - break; + return false; } } -void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) +bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) { switch (event->type()) { case QEvent::TouchBegin: for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { if (acceptTouch(point)) - handlePress(item->mapToScene(point.pos()), event->timestamp()); + return handlePress(item, item->mapToScene(point.pos()), event->timestamp()); } break; @@ -373,14 +382,11 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) switch (point.state()) { case Qt::TouchPointPressed: - handlePress(item->mapToScene(point.pos()), event->timestamp()); - break; + return handlePress(item, item->mapToScene(point.pos()), event->timestamp()); case Qt::TouchPointMoved: - handleMove(item->mapToScene(point.pos()), event->timestamp()); - break; + return handleMove(item, item->mapToScene(point.pos()), event->timestamp()); case Qt::TouchPointReleased: - handleRelease(item->mapToScene(point.pos()), event->timestamp()); - break; + return handleRelease(item, item->mapToScene(point.pos()), event->timestamp()); default: break; } @@ -390,7 +396,7 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) case QEvent::TouchEnd: for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { if (acceptTouch(point)) - handleRelease(item->mapToScene(point.pos()), event->timestamp()); + return handleRelease(item, item->mapToScene(point.pos()), event->timestamp()); } break; @@ -401,6 +407,8 @@ void QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event) default: break; } + + return false; } bool QQuickPopupPrivate::prepareEnterTransition() @@ -1959,17 +1967,11 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: - if (d->modal) - event->accept(); - d->handleTouchEvent(item, static_cast(event)); - return d->modal; + return d->handleTouchEvent(item, static_cast(event)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - if (d->modal) - event->accept(); - d->handleMouseEvent(item, static_cast(event)); - return d->modal; + return d->handleMouseEvent(item, static_cast(event)); default: return false; diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 56226bd9..5c2fe4f7 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -98,13 +98,15 @@ public: bool tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags); virtual bool acceptTouch(const QTouchEvent::TouchPoint &point); - virtual void handlePress(const QPointF &point, ulong timestamp); - virtual void handleMove(const QPointF &point, ulong timestamp); - virtual void handleRelease(const QPointF &point, ulong timestamp); + virtual bool blockInput(QQuickItem *item, const QPointF &point) const; + + virtual bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp); + virtual bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp); + virtual bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp); virtual void handleUngrab(); - void handleMouseEvent(QQuickItem *item, QMouseEvent *event); - void handleTouchEvent(QQuickItem *item, QTouchEvent *event); + bool handleMouseEvent(QQuickItem *item, QMouseEvent *event); + bool handleTouchEvent(QQuickItem *item, QTouchEvent *event); virtual void reposition(); virtual void resizeOverlay(); -- cgit v1.2.3 From 763b247911cb58663c749e44064e2ab6fc3396f8 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 25 Apr 2017 00:06:39 +0200 Subject: Enable touch events for popups and drawers Change-Id: Icc0a496facdc2ada1c87b4b02c49a58cb9a4daec Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 65 +++++++++++++++++++++++++++++----- src/quicktemplates2/qquickdrawer_p.h | 1 + src/quicktemplates2/qquickdrawer_p_p.h | 1 + src/quicktemplates2/qquickpopup.cpp | 3 -- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 66ba74a2..3275a0b2 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -328,6 +328,53 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event) return overThreshold; } +bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event) +{ + Q_Q(QQuickDrawer); + handleTouchEvent(item, event); + + if (!window || !interactive || popupItem->keepTouchGrab() || !event->touchPointStates().testFlag(Qt::TouchPointMoved)) + return false; + + bool overThreshold = false; + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (!acceptTouch(point) || point.state() != Qt::TouchPointMoved) + continue; + + const QPointF movePoint = point.scenePos(); + + // Flickable uses a hard-coded threshold of 15 for flicking, and + // QStyleHints::startDragDistance for dragging. Drawer uses a bit + // larger threshold to avoid being too eager to steal touch (QTBUG-50045) + const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + if (position > 0 || dragMargin > 0) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, &point, threshold); + else + overThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, &point, threshold); + } + + // Don't be too eager to steal presses outside the drawer (QTBUG-53929) + if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !popupItem->contains(popupItem->mapFromScene(movePoint))) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin; + else + overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; + } + + if (overThreshold) { + popupItem->setKeepTouchGrab(true); + offset = positionAt(movePoint) - position; + + // don't jump when dragged open + if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; + } + } + + return overThreshold; +} + static const qreal openCloseVelocityThreshold = 300; bool QQuickDrawerPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) @@ -615,6 +662,8 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) { Q_D(QQuickDrawer); switch (event->type()) { + case QEvent::TouchUpdate: + return d->grabTouch(child, static_cast(event)); case QEvent::MouseMove: return d->grabMouse(child, static_cast(event)); case QEvent::MouseButtonPress: @@ -636,24 +685,22 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) { Q_D(QQuickDrawer); switch (event->type()) { - case QEvent::TouchBegin: case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::TouchCancel: - // TODO: QQuickDrawer still relies on synthesized mouse events - event->ignore(); - return false; - + return d->grabTouch(item, static_cast(event)); case QEvent::MouseMove: return d->grabMouse(item, static_cast(event)); - default: break; } - return QQuickPopup::overlayEvent(item, event); } +void QQuickDrawer::touchEvent(QTouchEvent *event) +{ + Q_D(QQuickDrawer); + d->grabTouch(d->popupItem, event); +} + void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickDrawer); diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index 38b425be..9ab1c4ef 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -88,6 +88,7 @@ protected: bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; bool overlayEvent(QQuickItem *item, QEvent *event) override; + void touchEvent(QTouchEvent *event) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index bcfd7206..8956acef 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -72,6 +72,7 @@ public: bool startDrag(QEvent *event); bool grabMouse(QQuickItem *item, QMouseEvent *event); + bool grabTouch(QQuickItem *item, QTouchEvent *event); bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp) override; bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp) override; diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index ccbfd3c8..68a82204 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -1982,9 +1982,6 @@ void QQuickPopup::touchEvent(QTouchEvent *event) { Q_D(QQuickPopup); d->handleTouchEvent(d->popupItem, event); - - // TODO: QQuickPopup still relies on synthesized mouse events - event->ignore(); } void QQuickPopup::touchUngrabEvent() -- cgit v1.2.3 From 5cdfc0d4dfc4be29c0e8271526058e24e9b6cc27 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 25 Apr 2017 14:54:04 +0200 Subject: tst_dialog: get rid of waitForRendering() The intention is to wait until a dialog and its content is fully open and visible. While waitForRendering() does the job, it can sometimes take a bit of unnecessary extra time. Using SignalSpy to wait for opened() allows the test to continue immediately after the enter transition is finished. Change-Id: I86f2d524c616981148988f67dfed09c79002f7a9 Reviewed-by: Mitch Curtis Reviewed-by: Qt CI Bot --- tests/auto/controls/data/tst_dialog.qml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/auto/controls/data/tst_dialog.qml b/tests/auto/controls/data/tst_dialog.qml index 6b18a323..2f3d2a6b 100644 --- a/tests/auto/controls/data/tst_dialog.qml +++ b/tests/auto/controls/data/tst_dialog.qml @@ -87,8 +87,12 @@ TestCase { function test_accept() { var control = createTemporaryObject(dialog, testCase) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) @@ -102,8 +106,12 @@ TestCase { function test_reject() { var control = createTemporaryObject(dialog, testCase) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) @@ -226,8 +234,12 @@ TestCase { var control = createTemporaryObject(dialog, testCase, {width: 100, height: 100}) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) compare(control.width, 100) @@ -315,8 +327,12 @@ TestCase { var control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100}) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) control.contentItem.visible = data.content -- cgit v1.2.3 From 92cd0879c5e3031bb977e89ba1ef2efe99fd8456 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 26 Apr 2017 17:51:08 +0200 Subject: QQuickOverlay: add return value to handlePress/Move/Release() Inform the caller whether the event was handled. Change-Id: I9433164ec810f55e760960dd70e4539c5722e775 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickoverlay.cpp | 23 ++++++++++++++--------- src/quicktemplates2/qquickoverlay_p_p.h | 6 +++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 10ac3623..a13661e9 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -181,7 +181,7 @@ QQuickOverlayPrivate::QQuickOverlayPrivate() { } -void QQuickOverlayPrivate::handlePress(QEvent *event) +bool QQuickOverlayPrivate::handlePress(QEvent *event) { Q_Q(QQuickOverlay); emit q->pressed(); @@ -195,7 +195,7 @@ void QQuickOverlayPrivate::handlePress(QEvent *event) QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); if (p->startDrag(event)) { setMouseGrabberPopup(drawer); - return; + return true; } } } @@ -207,36 +207,41 @@ void QQuickOverlayPrivate::handlePress(QEvent *event) for (QQuickPopup *popup : popups) { if (popup->overlayEvent(q, event)) { setMouseGrabberPopup(popup); - return; + return true; } } } event->ignore(); + return false; } -void QQuickOverlayPrivate::handleMove(QEvent *event) +bool QQuickOverlayPrivate::handleMove(QEvent *event) { Q_Q(QQuickOverlay); if (mouseGrabberPopup) - mouseGrabberPopup->overlayEvent(q, event); + return mouseGrabberPopup->overlayEvent(q, event); + return false; } -void QQuickOverlayPrivate::handleRelease(QEvent *event) +bool QQuickOverlayPrivate::handleRelease(QEvent *event) { Q_Q(QQuickOverlay); emit q->released(); if (mouseGrabberPopup) { - mouseGrabberPopup->overlayEvent(q, event); - setMouseGrabberPopup(nullptr); + if (mouseGrabberPopup->overlayEvent(q, event)) { + setMouseGrabberPopup(nullptr); + return true; + } } else { const auto popups = stackingOrderPopups(); for (QQuickPopup *popup : popups) { if (popup->overlayEvent(q, event)) - break; + return true; } } + return false; } void QQuickOverlayPrivate::addPopup(QQuickPopup *popup) diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index b0145fc9..82065c79 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -70,9 +70,9 @@ public: return overlay->d_func(); } - void handlePress(QEvent *event); - void handleMove(QEvent *event); - void handleRelease(QEvent *event); + bool handlePress(QEvent *event); + bool handleMove(QEvent *event); + bool handleRelease(QEvent *event); void addPopup(QQuickPopup *popup); void removePopup(QQuickPopup *popup); -- cgit v1.2.3 From 4bfa96a6926c8351e3f6ebda14235fd96fb146d2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 27 Apr 2017 10:52:45 +0200 Subject: QQuickDrawerPrivate::startDrag(): start delivering touch events Change-Id: If483f0787568c18712fe0d2068d26709e6dd012d Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickdrawer.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 3275a0b2..76a09d11 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -258,27 +258,30 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event) if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin)) return false; - bool withinMargin = false; - bool mouse = event->type() == QEvent::MouseButtonPress; - if (mouse) { - withinMargin = isWithinDragMargin(q, static_cast(event)->windowPos()); - } else { + switch (event->type()) { + case QEvent::MouseButtonPress: + if (isWithinDragMargin(q, static_cast(event)->windowPos())) { + prepareEnterTransition(); + reposition(); + return handleMouseEvent(window->contentItem(), static_cast(event)); + } + break; + + case QEvent::TouchBegin: + case QEvent::TouchUpdate: for (const QTouchEvent::TouchPoint &point : static_cast(event)->touchPoints()) { - if (isWithinDragMargin(q, point.scenePos())) { - withinMargin = true; - break; + if (point.state() == Qt::TouchPointPressed && isWithinDragMargin(q, point.scenePos())) { + prepareEnterTransition(); + reposition(); + return handleTouchEvent(window->contentItem(), static_cast(event)); } } - } - if (!withinMargin) - return false; + break; - prepareEnterTransition(); - reposition(); - if (mouse) { - handleMouseEvent(window->contentItem(), static_cast(event)); - return true; + default: + break; } + return false; } -- cgit v1.2.3 From 52ea821c43dfd6afc9ad21a9066c1eb39937c40d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 25 Apr 2017 15:23:16 +0200 Subject: tst_popup: get rid of waitForRendering() Change-Id: I4db519712ad3d2aa53d8fa7cd387c8a893fc6472 Reviewed-by: Mitch Curtis --- tests/auto/controls/data/tst_popup.qml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/auto/controls/data/tst_popup.qml b/tests/auto/controls/data/tst_popup.qml index 24465db1..beae39d1 100644 --- a/tests/auto/controls/data/tst_popup.qml +++ b/tests/auto/controls/data/tst_popup.qml @@ -936,8 +936,13 @@ TestCase { var control = createTemporaryObject(popupControl, testCase) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) + verify(control.visible) // implicit size of the content control.contentItem.implicitWidth = 10 -- cgit v1.2.3 From 6060986368fcd14559141233719c767b97992590 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 27 Apr 2017 11:09:14 +0200 Subject: Add QQuickOverlayPrivate::startDrag() Change-Id: I9558f82cb7330e451c043e25c9a140f49eefa219 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickoverlay.cpp | 32 +++++++++++++++++++------------- src/quicktemplates2/qquickoverlay_p_p.h | 1 + 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index a13661e9..985c8f3d 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -181,24 +181,30 @@ QQuickOverlayPrivate::QQuickOverlayPrivate() { } +bool QQuickOverlayPrivate::startDrag(QEvent *event) +{ + if (allDrawers.isEmpty()) + return false; + + const QVector drawers = stackingOrderDrawers(); + for (QQuickDrawer *drawer : drawers) { + QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); + if (p->startDrag(event)) { + setMouseGrabberPopup(drawer); + return true; + } + } + + return false; +} + bool QQuickOverlayPrivate::handlePress(QEvent *event) { Q_Q(QQuickOverlay); emit q->pressed(); - if (!allDrawers.isEmpty()) { - // the overlay background was pressed, so there are no modal popups open. - // test if the press point lands on any drawer's drag margin - - const QVector drawers = stackingOrderDrawers(); - for (QQuickDrawer *drawer : drawers) { - QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); - if (p->startDrag(event)) { - setMouseGrabberPopup(drawer); - return true; - } - } - } + if (startDrag(event)) + return true; if (!mouseGrabberPopup) { // allow non-modal popups to close themselves, diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index 82065c79..f3ff0a52 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -70,6 +70,7 @@ public: return overlay->d_func(); } + bool startDrag(QEvent *event); bool handlePress(QEvent *event); bool handleMove(QEvent *event); bool handleRelease(QEvent *event); -- cgit v1.2.3 From c96cdc8728361864f24c0f4612a9eee2215927f6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 27 Apr 2017 11:53:54 +0200 Subject: QQuickOverlay: add handleMouseEvent() and handleTouchEvent() To unify overlay event delivery from the child event filter and the event handlers in QQuickOverlay itself. Change-Id: I0e867baeda3a8b829fe1d1992ea8289d466afc85 Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickoverlay.cpp | 146 +++++++++++++++++++------------- src/quicktemplates2/qquickoverlay_p_p.h | 9 +- 2 files changed, 94 insertions(+), 61 deletions(-) diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 985c8f3d..308eda86 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -198,20 +198,23 @@ bool QQuickOverlayPrivate::startDrag(QEvent *event) return false; } -bool QQuickOverlayPrivate::handlePress(QEvent *event) +bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target) { Q_Q(QQuickOverlay); emit q->pressed(); - if (startDrag(event)) - return true; - - if (!mouseGrabberPopup) { + if (target) { + if (target->overlayEvent(source, event)) { + setMouseGrabberPopup(target); + return true; + } + return false; + } else if (!mouseGrabberPopup) { // allow non-modal popups to close themselves, // and non-dimming modal popups to block the event const auto popups = stackingOrderPopups(); for (QQuickPopup *popup : popups) { - if (popup->overlayEvent(q, event)) { + if (popup->overlayEvent(source, event)) { setMouseGrabberPopup(popup); return true; } @@ -222,34 +225,94 @@ bool QQuickOverlayPrivate::handlePress(QEvent *event) return false; } -bool QQuickOverlayPrivate::handleMove(QEvent *event) +bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target) { - Q_Q(QQuickOverlay); - if (mouseGrabberPopup) - return mouseGrabberPopup->overlayEvent(q, event); + if (target) + return target->overlayEvent(source, event); return false; } -bool QQuickOverlayPrivate::handleRelease(QEvent *event) +bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target) { Q_Q(QQuickOverlay); emit q->released(); - if (mouseGrabberPopup) { - if (mouseGrabberPopup->overlayEvent(q, event)) { + if (target) { + setMouseGrabberPopup(nullptr); + if (target->overlayEvent(source, event)) { setMouseGrabberPopup(nullptr); return true; } } else { const auto popups = stackingOrderPopups(); for (QQuickPopup *popup : popups) { - if (popup->overlayEvent(q, event)) + if (popup->overlayEvent(source, event)) return true; } } return false; } +bool QQuickOverlayPrivate::handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target) +{ + switch (event->type()) { + case QEvent::MouseButtonPress: + if (!target && startDrag(event)) + return true; + return handlePress(source, event, target); + case QEvent::MouseMove: + return handleMove(source, event, target ? target : mouseGrabberPopup.data()); + case QEvent::MouseButtonRelease: + return handleRelease(source, event, target ? target : mouseGrabberPopup.data()); + default: + break; + } + return false; +} + +bool QQuickOverlayPrivate::handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target) +{ + bool handled = false; + switch (event->type()) { + case QEvent::TouchBegin: + if (!target && startDrag(event)) + handled = true; + else + handled = handlePress(source, event, target); + break; + + case QEvent::TouchUpdate: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + switch (point.state()) { + case Qt::TouchPointPressed: + if (!target && startDrag(event)) + handled = true; + else + handled |= handlePress(source, event, target); + break; + case Qt::TouchPointMoved: + handled |= handleMove(source, event, target ? target : mouseGrabberPopup.data()); + break; + case Qt::TouchPointReleased: + handled |= handleRelease(source, event, target ? target : mouseGrabberPopup.data()); + break; + default: + break; + } + } + break; + + case QEvent::TouchEnd: + handled = handleRelease(source, event, target ? target : mouseGrabberPopup.data()); + break; + + default: + break; + } + + return handled; +} + void QQuickOverlayPrivate::addPopup(QQuickPopup *popup) { Q_Q(QQuickOverlay); @@ -398,55 +461,25 @@ void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &old void QQuickOverlay::mousePressEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handlePress(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handleMove(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handleRelease(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::touchEvent(QTouchEvent *event) { Q_D(QQuickOverlay); - switch (event->type()) { - case QEvent::TouchBegin: - d->handlePress(event); - break; - - case QEvent::TouchUpdate: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - switch (point.state()) { - case Qt::TouchPointPressed: - d->handlePress(event); - break; - case Qt::TouchPointMoved: - d->handleMove(event); - break; - case Qt::TouchPointReleased: - d->handleRelease(event); - break; - default: - break; - } - } - break; - - case QEvent::TouchEnd: - d->handleRelease(event); - break; - - default: - QQuickItem::touchEvent(event); - break; - } + d->handleTouchEvent(this, event); } #if QT_CONFIG(wheelevent) @@ -484,19 +517,16 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) // does not have background dimming. if (item == p->dimmer || !p->popupItem->isAncestorOf(item)) { switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + return d->handleTouchEvent(item, static_cast(event), popup); + case QEvent::MouseButtonPress: - emit pressed(); - if (popup->overlayEvent(item, event)) { - d->setMouseGrabberPopup(popup); - return true; - } - break; case QEvent::MouseMove: - return popup->overlayEvent(item, event); case QEvent::MouseButtonRelease: - emit released(); - d->setMouseGrabberPopup(nullptr); - return popup->overlayEvent(item, event); + return d->handleMouseEvent(item, static_cast(event), popup); + default: break; } diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index f3ff0a52..fa53d52b 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -71,9 +71,12 @@ public: } bool startDrag(QEvent *event); - bool handlePress(QEvent *event); - bool handleMove(QEvent *event); - bool handleRelease(QEvent *event); + bool handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target); + bool handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target); + bool handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target); + + bool handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target = nullptr); + bool handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target = nullptr); void addPopup(QQuickPopup *popup); void removePopup(QQuickPopup *popup); -- cgit v1.2.3 From f9552a96a7c6f8ea58d8ea90cf3027cc0636bf64 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 27 Apr 2017 14:58:12 +0200 Subject: Container: document and test current index management Change-Id: I368dd50a4ded743826d6dc4d25dde379c98af20d Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickcontainer.cpp | 70 ++++++++++++++++++++++++++++-- tests/auto/controls/data/tst_container.qml | 36 +++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp index 6777fa5d..2bc37e9c 100644 --- a/src/quicktemplates2/qquickcontainer.cpp +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -96,6 +96,54 @@ QT_BEGIN_NAMESPACE } \endcode + \section2 Managing the Current Index + + When using multiple containers, such as \l TabBar and \l SwipeView, together, + their \l currentIndex properties can be bound to each other to keep them in + sync. When the user interacts with either container, its current index changes + automatically propagate to the other container. + + Notice, however, that assigning a \c currentIndex value in JavaScript removes + the respective binding. In order to retain the bindings, use the following + methods to alter the current index: + + \list + \li \l incrementCurrentIndex() + \li \l decrementCurrentIndex() + \li \l setCurrentIndex(int index) + \endlist + + \code + TabBar { + id: tabBar + currentIndex: swipeView.currentIndex + } + + SwipeView { + id: swipeView + currentIndex: tabBar.currentIndex + } + + Button { + text: qsTr("Home") + onClicked: swipeView.setCurrentIndex(0) + enabled: swipeView.currentIndex != 0 + } + + Button { + text: qsTr("Previous") + onClicked: swipeView.decrementCurrentIndex() + enabled: swipeView.currentIndex > 0 + } + + Button { + text: qsTr("Next") + onClicked: swipeView.incrementCurrentIndex() + enabled: swipeView.currentIndex < swipeView.count - 1 + } + \endcode + + \section2 Implementing Containers Container does not provide any default visualization. It is used to implement @@ -551,7 +599,7 @@ QQmlListProperty QQuickContainer::contentChildren() This property holds the index of the current item. - \sa currentItem, incrementCurrentIndex(), decrementCurrentIndex() + \sa currentItem, {Managing the Current Index} */ int QQuickContainer::currentIndex() const { @@ -559,6 +607,16 @@ int QQuickContainer::currentIndex() const return d->currentIndex; } +/*! + \qmlmethod void QtQuick.Controls::Container::setCurrentIndex(int index) + + Sets the current index of the container. + + This method can be called to set a specific current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} +*/ void QQuickContainer::setCurrentIndex(int index) { Q_D(QQuickContainer); @@ -576,7 +634,10 @@ void QQuickContainer::setCurrentIndex(int index) Increments the current index of the container. - \sa currentIndex + This method can be called to alter the current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} */ void QQuickContainer::incrementCurrentIndex() { @@ -591,7 +652,10 @@ void QQuickContainer::incrementCurrentIndex() Decrements the current index of the container. - \sa currentIndex + This method can be called to alter the current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} */ void QQuickContainer::decrementCurrentIndex() { diff --git a/tests/auto/controls/data/tst_container.qml b/tests/auto/controls/data/tst_container.qml index 684f0931..049982fb 100644 --- a/tests/auto/controls/data/tst_container.qml +++ b/tests/auto/controls/data/tst_container.qml @@ -90,4 +90,40 @@ TestCase { compare(control.implicitWidth, 210) compare(control.implicitHeight, 220) } + + function test_currentIndex() { + var control1 = createTemporaryObject(container, testCase) + verify(control1) + + var control2 = createTemporaryObject(container, testCase) + verify(control2) + + compare(control1.currentIndex, -1) + compare(control2.currentIndex, -1) + + for (var i = 0; i < 3; ++i) { + control1.addItem(rectangle.createObject(control1)) + control2.addItem(rectangle.createObject(control2)) + } + + compare(control1.count, 3) + compare(control2.count, 3) + compare(control1.currentIndex, 0) + compare(control2.currentIndex, 0) + + control1.currentIndex = Qt.binding(function() { return control2.currentIndex }) + control2.currentIndex = Qt.binding(function() { return control1.currentIndex }) + + control1.setCurrentIndex(1) + compare(control1.currentIndex, 1) + compare(control2.currentIndex, 1) + + control1.incrementCurrentIndex() + compare(control1.currentIndex, 2) + compare(control2.currentIndex, 2) + + control2.decrementCurrentIndex() + compare(control1.currentIndex, 1) + compare(control2.currentIndex, 1) + } } -- cgit v1.2.3