summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel/qapplication.cpp
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2020-04-22 10:53:04 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2020-05-13 15:07:55 +0000
commit92df790f46b3a8b17aec2f385d6472fd3f8647f6 (patch)
tree93b09b2ea2a3fb426368590a3967efbe8f364791 /src/widgets/kernel/qapplication.cpp
parent01b2ea83aa0e8e1d624f6c25d78bb5184cd35483 (diff)
QApplication: refactor delivery and propagation of wheel events
Handle wheel grabbing via wheel_widget in a single place, and propagate events in the same way for all (spontaneous) events. Handle ScrollMomentum the same way as ScrollUpdate to allow partial sequences. Fix the incorrect ignoring of wheel events by default; like all other input events, they are now again accepted by default and ignored in the default event handler implementation of QWidget. This way, implementing the handle suffices to accept the event. Note that QWidget::wheelEvent doesn't need to be changed, as the event is ignored there today (an oversight of the change made in f253f4c3, perhaps). This also fixes changing of direction of a wheel event while the event sequence is grabbed by a widget. Change-Id: Ia0f03c14dede80322d690ca50d085898a0497dbe Fixes: QTBUG-67032 Task-number: QTBUG-79102 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/widgets/kernel/qapplication.cpp')
-rw-r--r--src/widgets/kernel/qapplication.cpp114
1 files changed, 55 insertions, 59 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 78eccc91d6..f5d857b623 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -2992,8 +2992,18 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
}
QWheelEvent* wheel = static_cast<QWheelEvent*>(e);
- const bool spontaneous = wheel->spontaneous();
+ if (!wheel->spontaneous()) {
+ /*
+ Synthesized events shouldn't propagate, e.g. QScrollArea passes events from the
+ viewport on to the scrollbars, which might ignore the event if there is no more
+ space to scroll. If we would propagate, the event would come back to the viewport.
+ */
+ res = d->notify_helper(w, wheel);
+ break;
+ }
+
const Qt::ScrollPhase phase = wheel->phase();
+ QPoint relpos = wheel->position().toPoint();
// Ideally, we should lock on a widget when it starts receiving wheel
// events. This avoids other widgets to start receiving those events
@@ -3014,68 +3024,54 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
//
// This means that we can have scrolling sequences (starting with ScrollBegin)
// or partial sequences (after a ScrollEnd and starting with ScrollUpdate).
- // If wheel_widget is null because it was deleted, we also take the same
- // code path as an initial sequence.
- if (phase == Qt::NoScrollPhase || phase == Qt::ScrollBegin || !QApplicationPrivate::wheel_widget) {
- // A system-generated ScrollBegin event starts a new user scrolling
- // sequence, so we reset wheel_widget in case no one accepts the event
- // or if we didn't get (or missed) a ScrollEnd previously.
- if (spontaneous && phase == Qt::ScrollBegin)
- QApplicationPrivate::wheel_widget = nullptr;
-
- const QPoint relpos = wheel->position().toPoint();
+ // a widget has already grabbed the wheel for a sequence
+ if (QApplicationPrivate::wheel_widget) {
+ 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
+ wheel_widget. The sequence might be partial (ie. not start with ScrollBegin),
+ e.g. if the previous wheel_widget was destroyed mid-sequence.
+ */
+ switch (phase) {
+ case Qt::ScrollEnd:
+ QApplicationPrivate::wheel_widget = nullptr;
+ break;
+ case Qt::ScrollBegin:
+ QApplicationPrivate::wheel_widget = w;
+ Q_FALLTHROUGH();
+ case Qt::ScrollUpdate:
+ case Qt::ScrollMomentum:
+ if (!QApplicationPrivate::wheel_widget)
+ QApplicationPrivate::wheel_widget = w;
+ Q_FALLTHROUGH();
+ case Qt::NoScrollPhase:
+ QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos);
+ break;
+ // no default: - we want warnings if we don't handle all phases explicitly
+ }
- if (spontaneous && (phase == Qt::NoScrollPhase || phase == Qt::ScrollUpdate))
- QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos);
+ QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(),
+ wheel->modifiers(), phase, wheel->inverted(), wheel->source());
- QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(),
- wheel->modifiers(), phase, wheel->inverted(), wheel->source());
- we.setTimestamp(wheel->timestamp());
- bool eventAccepted;
- do {
- we.spont = spontaneous && w == receiver;
- we.ignore();
- res = d->notify_helper(w, &we);
- eventAccepted = we.isAccepted();
- if (res && eventAccepted) {
- // A new scrolling sequence or partial sequence starts and w has accepted
- // the event. Therefore, we can set wheel_widget, but only if it's not
- // the end of a sequence.
- if (QApplicationPrivate::wheel_widget == nullptr && (phase == Qt::ScrollBegin || phase == Qt::ScrollUpdate))
- QApplicationPrivate::wheel_widget = w;
- break;
- }
- if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
- break;
+ we.setTimestamp(wheel->timestamp());
+ bool eventAccepted;
+ do {
+ we.spont = wheel->spontaneous() && w == receiver;
+ res = d->notify_helper(w, &we);
+ eventAccepted = we.isAccepted();
+ if (res && eventAccepted)
+ break;
+ if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
- we.p += w->pos();
- w = w->parentWidget();
- } while (w);
- wheel->setAccepted(eventAccepted);
- } else if (!spontaneous) {
- // wheel_widget may forward the wheel event to a delegate widget,
- // either directly or indirectly (e.g. QAbstractScrollArea will
- // forward to its QScrollBars through viewportEvent()). In that
- // case, the event will not be spontaneous but synthesized, so
- // we can send it straight to the receiver.
- d->notify_helper(w, wheel);
- } else {
- // The phase is either ScrollUpdate, ScrollMomentum, or ScrollEnd, and wheel_widget
- // is set. Since it accepted the wheel event previously, we continue
- // sending those events until we get a ScrollEnd, which signifies
- // the end of the natural scrolling sequence.
- const QPoint &relpos = QApplicationPrivate::wheel_widget->mapFromGlobal(wheel->globalPosition().toPoint());
- QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(),
- wheel->modifiers(), wheel->phase(), wheel->inverted(), wheel->source());
- we.setTimestamp(wheel->timestamp());
- we.spont = true;
- we.ignore();
- d->notify_helper(QApplicationPrivate::wheel_widget, &we);
- wheel->setAccepted(we.isAccepted());
- if (phase == Qt::ScrollEnd)
- QApplicationPrivate::wheel_widget = nullptr;
- }
+ we.p += w->pos();
+ w = w->parentWidget();
+ } while (w);
+ wheel->setAccepted(eventAccepted);
}
break;
#endif