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 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 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