diff options
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 10 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquicklistview.cpp | 56 | ||||
-rw-r--r-- | src/quick/items/qquicklistview_p.h | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/data/clickHeaderAndFooterWhenClip.qml | 60 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 27 |
6 files changed, 142 insertions, 16 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index f222a4a32e..9a68be4c49 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1424,7 +1424,7 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event) void QQuickFlickable::mousePressEvent(QMouseEvent *event) { Q_D(QQuickFlickable); - if (d->interactive) { + if (d->interactive && d->wantsPointerEvent(event)) { if (!d->pressed) d->handleMousePressEvent(event); event->accept(); @@ -1436,7 +1436,7 @@ void QQuickFlickable::mousePressEvent(QMouseEvent *event) void QQuickFlickable::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickFlickable); - if (d->interactive) { + if (d->interactive && d->wantsPointerEvent(event)) { d->handleMouseMoveEvent(event); event->accept(); } else { @@ -1447,7 +1447,7 @@ void QQuickFlickable::mouseMoveEvent(QMouseEvent *event) void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickFlickable); - if (d->interactive) { + if (d->interactive && d->wantsPointerEvent(event)) { if (d->delayedPressEvent) { d->replayDelayedPress(); @@ -1475,7 +1475,7 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) void QQuickFlickable::wheelEvent(QWheelEvent *event) { Q_D(QQuickFlickable); - if (!d->interactive) { + if (!d->interactive || !d->wantsPointerEvent(event)) { QQuickItem::wheelEvent(event); return; } @@ -2445,7 +2445,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) { Q_D(QQuickFlickable); - if (!isVisible() || !isEnabled() || !isInteractive()) { + if (!isVisible() || !isEnabled() || !isInteractive() || !d->wantsPointerEvent(e)) { d->cancelInteraction(); return QQuickItem::childMouseEventFilter(i, e); } diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 3f5f11effd..414c9c33d6 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -208,6 +208,9 @@ public: void addPointerHandler(QQuickPointerHandler *h) override; + // TODO Qt 6: QPointerEvent + virtual bool wantsPointerEvent(const QEvent *) { return true; } + public: QQuickItem *contentItem; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 9dd85ded19..0f9394f695 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE #define QML_FLICK_SNAPONETHRESHOLD 30 #endif +Q_LOGGING_CATEGORY(lcEvents, "qt.quick.listview.events") + class FxListItemSG; class QQuickListViewPrivate : public QQuickItemViewPrivate @@ -143,6 +145,9 @@ public: void fixupHeader(); void fixupHeaderCompleted(); + + bool wantsPointerEvent(const QEvent *event) override; + QQuickListView::Orientation orient; qreal visiblePos; qreal averageSize; @@ -179,6 +184,7 @@ public: bool correctFlick : 1; bool inFlickCorrection : 1; + bool wantedMousePress : 1; QQuickListViewPrivate() : orient(QQuickListView::Vertical) @@ -192,7 +198,7 @@ public: , sectionCriteria(nullptr), currentSectionItem(nullptr), nextSectionItem(nullptr) , overshootDist(0.0), desiredViewportPosition(0.0), fixupHeaderPosition(0.0) , headerNeedsSeparateFixup(false), desiredHeaderVisible(false) - , correctFlick(false), inFlickCorrection(false) + , correctFlick(false), inFlickCorrection(false), wantedMousePress(false) { highlightMoveDuration = -1; //override default value set in base class } @@ -3819,20 +3825,52 @@ QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj) return new QQuickListViewAttached(obj); } -bool QQuickListView::contains(const QPointF &point) const +/*! \internal + Prevents clicking or dragging through floating headers (QTBUG-74046). +*/ +bool QQuickListViewPrivate::wantsPointerEvent(const QEvent *event) { - bool ret = QQuickItemView::contains(point); - // QTBUG-74046: if a mouse press "falls through" a floating header or footer, don't allow dragging the list from there - if (ret) { - if (auto header = headerItem()) { - if (headerPositioning() != QQuickListView::InlineHeader && header->contains(mapToItem(header, point))) + Q_Q(const QQuickListView); + bool ret = true; + + QPointF pos; + // TODO switch not needed in Qt 6: use points().first().position() + switch (event->type()) { + case QEvent::Wheel: + pos = static_cast<const QWheelEvent *>(event)->position(); + break; + case QEvent::MouseButtonPress: + pos = static_cast<const QMouseEvent *>(event)->localPos(); + break; + default: + break; + } + + if (!pos.isNull()) { + if (auto header = q->headerItem()) { + if (q->headerPositioning() != QQuickListView::InlineHeader && + header->contains(q->mapToItem(header, pos))) ret = false; } - if (auto footer = footerItem()) { - if (footerPositioning() != QQuickListView::InlineFooter && footer->contains(mapToItem(footer, point))) + if (auto footer = q->footerItem()) { + if (q->footerPositioning() != QQuickListView::InlineFooter && + footer->contains(q->mapToItem(footer, pos))) ret = false; } } + + switch (event->type()) { + case QEvent::MouseButtonPress: + wantedMousePress = ret; + break; + case QEvent::MouseMove: + ret = wantedMousePress; + break; + default: + break; + } + + qCDebug(lcEvents) << q << (ret ? "WANTS" : "DOESN'T want") << event; return ret; } diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index be21b93155..1c72a10190 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -179,8 +179,6 @@ public: static QQuickListViewAttached *qmlAttachedProperties(QObject *); - bool contains(const QPointF &point) const override; - public Q_SLOTS: void incrementCurrentIndex(); void decrementCurrentIndex(); diff --git a/tests/auto/quick/qquicklistview/data/clickHeaderAndFooterWhenClip.qml b/tests/auto/quick/qquicklistview/data/clickHeaderAndFooterWhenClip.qml new file mode 100644 index 0000000000..c48e2d47d1 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/clickHeaderAndFooterWhenClip.qml @@ -0,0 +1,60 @@ +import QtQuick 2.9 +import QtQuick.Window 2.15 + +ListView { + id: list + anchors.fill: parent + property bool headerPressed: false + property bool footerPressed: false + model: ListModel { + ListElement { + name: "Element 1" + } + ListElement { + name: "Element 2" + } + ListElement { + name: "Element 3" + } + ListElement { + name: "Element 4" + } + ListElement { + name: "Element 5" + } + ListElement { + name: "Element 6" + } + } + clip: true + headerPositioning: ListView.OverlayHeader + footerPositioning: ListView.OverlayHeader + + delegate: Text { + height: 100 + text: name + } + + header: Rectangle { + width: parent.width + height: 50 + z: 2 + color: "blue" + MouseArea { + objectName: "header" + anchors.fill: parent + onClicked: list.headerPressed = true + } + } + footer: Rectangle { + width: parent.width + height: 50 + color: "red" + z: 2 + MouseArea { + objectName: "footer" + anchors.fill: parent + onClicked: list.footerPressed = true + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index f63793ca80..2af6b084fb 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -297,6 +297,7 @@ private slots: void objectModelCulling(); void requiredObjectListModel(); + void clickHeaderAndFooterWhenClip(); private: template <class T> void items(const QUrl &source); @@ -10067,6 +10068,32 @@ void tst_QQuickListView::requiredObjectListModel() } } +void tst_QQuickListView::clickHeaderAndFooterWhenClip() // QTBUG-85302 +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("clickHeaderAndFooterWhenClip.qml")); + window->resize(640, 480); + window->show(); + + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + auto *root = window->rootObject(); + QVERIFY(root); + + auto *header = root->findChild<QQuickItem *>("header"); + QVERIFY(header); + + auto *footer = root->findChild<QQuickItem *>("footer"); + QVERIFY(footer); + + QVERIFY(root->property("headerPressed").isValid() && root->property("headerPressed").canConvert<bool>() && !root->property("headerPressed").toBool()); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, header->mapToItem(root, QPoint(header->width() / 2, header->height() / 2)).toPoint()); + QVERIFY(root->property("headerPressed").toBool()); + + QVERIFY(root->property("footerPressed").isValid() && root->property("footerPressed").canConvert<bool>() && !root->property("footerPressed").toBool()); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, footer->mapToItem(root, QPoint(footer->width() / 2, footer->height() / 2)).toPoint()); + QVERIFY(root->property("footerPressed").toBool()); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" |