diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-04-22 10:53:04 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-05-13 15:07:55 +0000 |
commit | 92df790f46b3a8b17aec2f385d6472fd3f8647f6 (patch) | |
tree | 93b09b2ea2a3fb426368590a3967efbe8f364791 /src/widgets/kernel/qapplication.cpp | |
parent | 01b2ea83aa0e8e1d624f6c25d78bb5184cd35483 (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.cpp | 114 |
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 |