aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Arve Sæther <jan-arve.saether@qt.io>2021-08-17 10:01:18 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-08-20 13:56:26 +0000
commit87cbf885a9ecb8302c0285882c4724f4c5a20bcf (patch)
treecd32112e3f36d5d65974921f8f64bd2a08ddecd9
parent6c5f03250623036f590ff95e7fb3965867c9627a (diff)
Fix regression in ListView/Flickable event delivery
The regression was introduced with 2acb31641fc9c34d24ac29232cdfec The problem was that when we entered QQuickListViewPrivate::wantsPointerEvent(), the event was not localized to the QQuickFlickable when mouse filtering was done. Therefore, since the code assumed that the localPos was local to the Flickable, it always ended up assuming that the point was within the bounds of the header/footer. Fixes: QTBUG-89409 Change-Id: Ib595e61b7995241b58e3051da09139e1e1f13cba Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> (cherry picked from commit b2e4f09bfa2c42af562f6741c0a0246a53028f0a) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quick/items/qquickflickable.cpp12
-rw-r--r--tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml120
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp92
3 files changed, 223 insertions, 1 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index b02840208d..63cf3d9fe7 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -2629,8 +2629,18 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickFlickable);
+
+ auto wantsPointerEvent_helper = [=]() {
+ QPointerEvent *pe = static_cast<QPointerEvent *>(e);
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pe, this);
+ const bool wants = d->wantsPointerEvent(pe);
+ // re-localize event back to \a i before returning
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pe, i);
+ return wants;
+ };
+
if (!isVisible() || !isEnabled() || !isInteractive() ||
- (e->isPointerEvent() && !d->wantsPointerEvent(static_cast<QPointerEvent *>(e)))) {
+ (e->isPointerEvent() && !wantsPointerEvent_helper())) {
d->cancelInteraction();
return QQuickItem::childMouseEventFilter(i, e);
}
diff --git a/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml b/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml
new file mode 100644
index 0000000000..92a6963962
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+Rectangle {
+
+ width: 240
+ height: 320
+ color: "#ffffff"
+
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ width: list.orientation == ListView.Vertical ? 240 : 20
+ height: list.orientation == ListView.Vertical ? 20 : 240
+ border.width: 1
+ border.color: "black"
+ MouseArea {
+ anchors.fill: parent
+ }
+ Text {
+ text: index + ":" + (list.orientation == ListView.Vertical ? parent.y : parent.x).toFixed(0)
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 200
+ clip: true
+ model: 30
+ headerPositioning: ListView.OverlayHeader
+ delegate: myDelegate
+
+ header: Rectangle {
+ width: list.orientation == Qt.Vertical ? 240 : 30
+ height: list.orientation == Qt.Vertical ? 30 : 240
+ color: "green"
+ z: 11
+ Text {
+ anchors.centerIn: parent
+ text: "header " + (list.orientation == ListView.Vertical ? parent.y : parent.x).toFixed(1)
+ }
+ }
+ }
+
+ // debug
+ Rectangle {
+ color: "#40ff0000"
+ border.width: txt.x
+ border.color: "black"
+ radius: 5
+ width: txt.implicitWidth + 50
+ height: txt.implicitHeight + 2 * txt.x
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.left: parent.left
+
+ Text {
+ id: txt
+ x: 3
+ y: x
+ text: "header position: " + (list.orientation == ListView.Vertical ? list.headerItem.y : list.headerItem.x).toFixed(1)
+ + "\ncontent position: " + (list.orientation == ListView.Vertical ? list.contentY : list.contentX).toFixed(1)
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index c9c7769182..f74a9805b2 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -31,6 +31,7 @@
#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuickTest/QtQuickTest>
+
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
#include "../../shared/util.h"
@@ -46,6 +47,8 @@ public:
private slots:
void urlListModel();
+ void dragDelegateWithMouseArea_data();
+ void dragDelegateWithMouseArea();
};
tst_QQuickListView2::tst_QQuickListView2()
@@ -71,6 +74,95 @@ void tst_QQuickListView2::urlListModel()
QCOMPARE(view->count(), model.size());
}
+static void dragListView(QWindow *window, QPoint *startPos, const QPoint &delta)
+{
+ auto drag_helper = [&](QWindow *window, QPoint *startPos, const QPoint &d) {
+ QPoint pos = *startPos;
+ const int dragDistance = d.manhattanLength();
+ const QPoint unitVector(qBound(-1, d.x(), 1), qBound(-1, d.y(), 1));
+ for (int i = 0; i < dragDistance; ++i) {
+ QTest::mouseMove(window, pos);
+ pos += unitVector;
+ }
+ // Move to the final position
+ pos = *startPos + d;
+ QTest::mouseMove(window, pos);
+ *startPos = pos;
+ };
+
+ if (delta.manhattanLength() == 0)
+ return;
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ const QPoint unitVector(qBound(-1, delta.x(), 1), qBound(-1, delta.y(), 1));
+ // go just beyond the drag theshold
+ drag_helper(window, startPos, unitVector * (dragThreshold + 1));
+ drag_helper(window, startPos, unitVector);
+
+ // next drag will actually scroll the listview
+ drag_helper(window, startPos, delta);
+}
+
+void tst_QQuickListView2::dragDelegateWithMouseArea_data()
+{
+ QTest::addColumn<QQuickItemView::LayoutDirection>("layoutDirection");
+
+ for (int layDir = QQuickItemView::LeftToRight; layDir <= (int)QQuickItemView::VerticalBottomToTop; layDir++) {
+ const char *enumValueName = QMetaEnum::fromType<QQuickItemView::LayoutDirection>().valueToKey(layDir);
+ QTest::newRow(enumValueName) << static_cast<QQuickItemView::LayoutDirection>(layDir);
+ }
+}
+
+void tst_QQuickListView2::dragDelegateWithMouseArea()
+{
+ QFETCH(QQuickItemView::LayoutDirection, layoutDirection);
+
+ QScopedPointer<QQuickView> window(createView());
+ QVERIFY(window);
+ window->setSource(testFileUrl("delegateWithMouseArea.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+ QVERIFY(listview != nullptr);
+
+ const bool horizontal = layoutDirection < QQuickItemView::VerticalTopToBottom;
+ listview->setOrientation(horizontal ? QQuickListView::Horizontal : QQuickListView::Vertical);
+
+ if (horizontal)
+ listview->setLayoutDirection(static_cast<Qt::LayoutDirection>(layoutDirection));
+ else
+ listview->setVerticalLayoutDirection(static_cast<QQuickItemView::VerticalLayoutDirection>(layoutDirection));
+
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+
+ auto contentPosition = [&](QQuickListView *listview) {
+ return (listview->orientation() == QQuickListView::Horizontal ? listview->contentX(): listview->contentY());
+ };
+
+ qreal expectedContentPosition = contentPosition(listview);
+ QPoint startPos = (QPointF(listview->width(), listview->height())/2).toPoint();
+ QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, startPos, 200);
+
+ QPoint dragDelta(0, -10);
+
+ if (layoutDirection == QQuickItemView::RightToLeft || layoutDirection == QQuickItemView::VerticalBottomToTop)
+ dragDelta = -dragDelta;
+ expectedContentPosition -= dragDelta.y();
+ if (horizontal)
+ dragDelta = dragDelta.transposed();
+
+ dragListView(window.data(), &startPos, dragDelta);
+
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, startPos, 200); // Wait 200 ms before we release to avoid trigger a flick
+
+ // wait for the "fixup" animation to finish
+ QVERIFY(QTest::qWaitFor([&]()
+ { return !listview->isMoving();}
+ ));
+
+ QCOMPARE(contentPosition(listview), expectedContentPosition);
+}
+
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"