summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2021-08-11 14:21:53 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2021-08-16 16:37:26 +0000
commit06235d36ae9d00366215e748d80ff0faed3c2124 (patch)
treec36b5bb1509596cbbae656f1c95511a64f2994d5
parent4982c872efef7ce8673ed257dce24b971e456a08 (diff)
QGraphicsProxyWidget: fix propagation of high-precision events
In order to fix QTBUG-95552 properly we have to add APIs to QGraphicsSceneWheelEvent that informs QGraphicsProxyWidget about whether the event is a high-precision event where Qt grabs the wheel. If it is, then the wheel grabber will be the QGraphicsView's viewport, and any wheel event sent to any widget will be grabbed by it. This results in infinite recoursion, partly fixed in change I78400ceae8da7a4e22a988c06ed58f99f1a979f4. The proper fix is to re-grab the wheel by the embedded widget if it (or any of its children) accepts the ScrollBegin event (and if not, return the grab to the QGraphicsView). This fixes the scenarios that failed in the test case, so now scrolling through nested widgets and scrolling in nested widgets works as the user would expect. Fixes: QTBUG-95552 Pick-to: 6.2 Change-Id: I3e1f31cbff999c70f8c63c034f77cd2ae567d7e3 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
-rw-r--r--src/widgets/graphicsview/qgraphicsproxywidget.cpp25
-rw-r--r--src/widgets/graphicsview/qgraphicssceneevent.cpp70
-rw-r--r--src/widgets/graphicsview/qgraphicssceneevent.h9
-rw-r--r--src/widgets/graphicsview/qgraphicsview.cpp3
-rw-r--r--src/widgets/kernel/qapplication.cpp12
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp5
6 files changed, 107 insertions, 17 deletions
diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.cpp b/src/widgets/graphicsview/qgraphicsproxywidget.cpp
index aaddaa894f..4d997b3443 100644
--- a/src/widgets/graphicsview/qgraphicsproxywidget.cpp
+++ b/src/widgets/graphicsview/qgraphicsproxywidget.cpp
@@ -1289,6 +1289,19 @@ void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
if (!receiver)
receiver = d->widget;
+ // high precision event streams go to the grabber, which will be the
+ // QGraphicsView's viewport. We need to change that temporarily, otherwise
+ // the event we send to the receiver get grabbed by the viewport, resulting
+ // in infinite recursion
+ QPointer<QWidget> prev_grabber = QApplicationPrivate::wheel_widget;
+ if (event->phase() == Qt::ScrollBegin) {
+ QApplicationPrivate::wheel_widget = receiver;
+ } else if (event->phase() != Qt::NoScrollPhase && QApplicationPrivate::wheel_widget != receiver) {
+ // this event is part of a stream that didn't start here, so ignore
+ event->ignore();
+ return;
+ }
+
// Map event position from us to the receiver
pos = d->mapToReceiver(pos, receiver);
@@ -1300,15 +1313,21 @@ void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
angleDelta.setY(event->delta());
// pixelDelta, inverted, scrollPhase and source from the original QWheelEvent
// were not preserved in the QGraphicsSceneWheelEvent unfortunately
- QWheelEvent wheelEvent(pos, event->screenPos(), QPoint(), angleDelta,
- event->buttons(), event->modifiers(), Qt::NoScrollPhase,
- false, Qt::MouseEventSynthesizedByQt,
+ QWheelEvent wheelEvent(pos, event->screenPos(), event->pixelDelta(), angleDelta,
+ event->buttons(), event->modifiers(), event->phase(),
+ event->isInverted(), Qt::MouseEventSynthesizedByQt,
QPointingDevice::primaryPointingDevice());
QPointer<QWidget> focusWidget = d->widget->focusWidget();
extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
qt_sendSpontaneousEvent(receiver, &wheelEvent);
event->setAccepted(wheelEvent.isAccepted());
+ if (event->phase() == Qt::ScrollBegin) {
+ // reset the wheel grabber if the event wasn't accepted
+ if (!wheelEvent.isAccepted())
+ QApplicationPrivate::wheel_widget = prev_grabber;
+ }
+
// ### Remove, this should be done by proper focusIn/focusOut events.
if (focusWidget && !focusWidget->hasFocus()) {
focusWidget->update();
diff --git a/src/widgets/graphicsview/qgraphicssceneevent.cpp b/src/widgets/graphicsview/qgraphicssceneevent.cpp
index 048ea6dc7d..e33f38c7c9 100644
--- a/src/widgets/graphicsview/qgraphicssceneevent.cpp
+++ b/src/widgets/graphicsview/qgraphicssceneevent.cpp
@@ -695,10 +695,13 @@ public:
QPointF pos;
QPointF scenePos;
QPoint screenPos;
+ QPoint pixelDelta;
Qt::MouseButtons buttons;
Qt::KeyboardModifiers modifiers;
int delta = 0;
Qt::Orientation orientation = Qt::Horizontal;
+ Qt::ScrollPhase scrollPhase = Qt::NoScrollPhase;
+ bool inverted = false;
};
/*!
@@ -865,6 +868,73 @@ void QGraphicsSceneWheelEvent::setOrientation(Qt::Orientation orientation)
d->orientation = orientation;
}
+/*!
+ \since 6.2
+
+ Returns the scrolling phase of this wheel event.
+
+ \sa QWheelEvent::phase
+*/
+Qt::ScrollPhase QGraphicsSceneWheelEvent::phase() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->scrollPhase;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setPhase(Qt::ScrollPhase scrollPhase)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->scrollPhase = scrollPhase;
+}
+
+/*!
+ \since 6.2
+
+ Returns the scrolling distance in pixels on screen. This value is
+ provided on platforms that support high-resolution pixel-based
+ delta values, such as \macos. The value should be used directly
+ to scroll content on screen.
+
+ \sa QWheelEvent::pixelDelta
+*/
+QPoint QGraphicsSceneWheelEvent::pixelDelta() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->pixelDelta;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setPixelDelta(QPoint pixelDelta)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->pixelDelta = pixelDelta;
+}
+
+/*!
+ Returns whether the delta values delivered with the event are inverted.
+
+ \since 6.2
+*/
+bool QGraphicsSceneWheelEvent::isInverted() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->inverted;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setInverted(bool inverted)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->inverted = inverted;
+}
+
class QGraphicsSceneContextMenuEventPrivate : public QGraphicsSceneEventPrivate
{
Q_DECLARE_PUBLIC(QGraphicsSceneContextMenuEvent)
diff --git a/src/widgets/graphicsview/qgraphicssceneevent.h b/src/widgets/graphicsview/qgraphicssceneevent.h
index 4a47b642fe..639b9c750e 100644
--- a/src/widgets/graphicsview/qgraphicssceneevent.h
+++ b/src/widgets/graphicsview/qgraphicssceneevent.h
@@ -157,6 +157,15 @@ public:
Qt::Orientation orientation() const;
void setOrientation(Qt::Orientation orientation);
+ Qt::ScrollPhase phase() const;
+ void setPhase(Qt::ScrollPhase scrollPhase);
+
+ QPoint pixelDelta() const;
+ void setPixelDelta(QPoint delta);
+
+ bool isInverted() const;
+ void setInverted(bool inverted);
+
private:
Q_DECLARE_PRIVATE(QGraphicsSceneWheelEvent)
Q_DISABLE_COPY(QGraphicsSceneWheelEvent)
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp
index dc62e59f12..6d1fb8aab7 100644
--- a/src/widgets/graphicsview/qgraphicsview.cpp
+++ b/src/widgets/graphicsview/qgraphicsview.cpp
@@ -3400,6 +3400,9 @@ void QGraphicsView::wheelEvent(QWheelEvent *event)
wheelEvent.setModifiers(event->modifiers());
const bool horizontal = qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y());
wheelEvent.setDelta(horizontal ? event->angleDelta().x() : event->angleDelta().y());
+ wheelEvent.setPixelDelta(event->pixelDelta());
+ wheelEvent.setPhase(event->phase());
+ wheelEvent.setInverted(event->isInverted());
wheelEvent.setOrientation(horizontal ? Qt::Horizontal : Qt::Vertical);
wheelEvent.setAccepted(false);
QCoreApplication::sendEvent(d->scene, &wheelEvent);
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 25ca295422..3d2554dd32 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -2946,15 +2946,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
// a widget has already grabbed the wheel for a sequence
if (QApplicationPrivate::wheel_widget) {
- // Qt explicitly synthesizes a spontaneous event for the receiver, done
- // by QGraphicsProxyWidget - so trust it
- if (wheel->source() == Qt::MouseEventSynthesizedByQt) {
- QApplicationPrivate::wheel_widget = w;
- } else {
- Q_ASSERT(phase != Qt::NoScrollPhase);
- w = QApplicationPrivate::wheel_widget;
- relpos = w->mapFromGlobal(wheel->globalPosition().toPoint());
- }
+ Q_ASSERT(phase != Qt::NoScrollPhase);
+ w = QApplicationPrivate::wheel_widget;
+ relpos = w->mapFromGlobal(wheel->globalPosition().toPoint());
}
/*
Start or finish a scrolling sequence by grabbing/releasing the wheel via
diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
index bbcef8849e..500b8e3c4b 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
@@ -2351,12 +2351,9 @@ void tst_QGraphicsView::wheelEventPropagation()
QCOMPARE(scrollSpy.count(), ++scrollCount);
for (int i = 0; i < 5; ++i) {
wheelUp(Qt::ScrollUpdate);
- if (i >= 1)
- QEXPECT_FAIL("", "Fixed for Qt 6.2 - QTBUG-65552", Continue);
QCOMPARE(scrollSpy.count(), ++scrollCount);
}
wheelUp(Qt::ScrollEnd);
- QEXPECT_FAIL("", "Fixed for Qt 6.2 - QTBUG-65552", Continue);
QCOMPARE(scrollSpy.count(), ++scrollCount);
// reset
@@ -2373,11 +2370,9 @@ void tst_QGraphicsView::wheelEventPropagation()
QCOMPARE(scrollSpy.count(), ++scrollCount);
for (int i = 0; i < 5; ++i) {
wheelUp(Qt::ScrollUpdate);
- QEXPECT_FAIL("", "Fixed for Qt 6.2 - QTBUG-65552", Continue);
QCOMPARE(scrollSpy.count(), ++scrollCount);
}
wheelUp(Qt::ScrollEnd);
- QEXPECT_FAIL("", "Fixed for Qt 6.2 - QTBUG-65552", Continue);
QCOMPARE(scrollSpy.count(), ++scrollCount);
// starting a scroll on a widget that does accept wheel events