Skip to content

Commit

Permalink
Add QWheelEvent::inverted()
Browse files Browse the repository at this point in the history
Some consumers of wheel events need to know if the
scrolling direction is inverted in order to provide
consistent behavior.

For example, the scrolling direction of a horizontal
slider should not change when the user toggles the
"natural scrolling" setting on OS X. In this case
the inverted bit will change state and the slider
can compensate.

This change adds a bit to QWheelEvent and sets it
on OS X.

Task-number: QTBUG-35972
Change-Id: I221435dea45d436d570b113bd0e24ee6f6832211
Reviewed-by: Shawn Rutledge <[email protected]>
  • Loading branch information
Morten Johan Sørvig authored and Jan Arve Sæther committed Mar 6, 2016
1 parent d4b6ed3 commit bcd88d8
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 22 deletions.
82 changes: 74 additions & 8 deletions src/gui/kernel/qevent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,31 @@ QHoverEvent::~QHoverEvent()
\sa Qt::MouseEventSource
*/

/*!
\fn bool QWheelEvent::inverted() const
\since 5.7
Returns whether the delta values delivered with the event are inverted.
Normally, a vertical wheel will produce a QWheelEvent with positive delta
values if the top of the wheel is rotating away from the hand operating it.
Similarly, a horizontal wheel movement will produce a QWheelEvent with
positive delta values if the top of the wheel is moved to the left.
However, on some platforms this is configurable, so that the same
operations described above will produce negative delta values (but with the
same magnitude). With the inverted property a wheel event consumer can
choose to always follow the direction of the wheel, regardless of the
system settings, but only for specific widgets. (One such use case could be
that the user is rotating the wheel in the same direction as a visual
Tumbler rotates. Another usecase is to make a slider handle follow the
direction of movement of fingers on a touchpad regardless of system
configuration.)
\note Many platforms provide no such information. On such platforms
\l inverted always returns false.
*/

/*!
\fn Qt::Orientation QWheelEvent::orientation() const
\obsolete
Expand Down Expand Up @@ -734,7 +759,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient)
: QInputEvent(Wheel, modifiers), p(pos), qt4D(delta), qt4O(orient), mouseState(buttons),
ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized)
ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
{
g = QCursor::pos();
if (orient == Qt::Vertical)
Expand Down Expand Up @@ -769,7 +794,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), qt4D(delta), qt4O(orient), mouseState(buttons),
ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized)
ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
{
if (orient == Qt::Vertical)
angleD = QPoint(0, delta);
Expand Down Expand Up @@ -806,7 +831,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta),
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(Qt::NoScrollPhase),
src(Qt::MouseEventNotSynthesized)
src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
{}

/*!
Expand Down Expand Up @@ -837,15 +862,14 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta),
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(phase),
src(Qt::MouseEventNotSynthesized)
src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
{}

/*!
Constructs a wheel event object.
The \a pos provides the location of the mouse cursor
within the window. The position in global coordinates is specified
by \a globalPos.
The \a pos provides the location of the mouse cursor within the window. The
position in global coordinates is specified by \a globalPos.
\a pixelDelta contains the scrolling distance in pixels on screen, while
\a angleDelta contains the wheel rotation distance. \a pixelDelta is
Expand Down Expand Up @@ -873,7 +897,49 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta),
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(phase), src(source)
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(phase), src(source),
invertedScrolling(false)
{}

/*!
Constructs a wheel event object.
The \a pos provides the location of the mouse cursor
within the window. The position in global coordinates is specified
by \a globalPos.
\a pixelDelta contains the scrolling distance in pixels on screen, while
\a angleDelta contains the wheel rotation distance. \a pixelDelta is
optional and can be null.
The mouse and keyboard states at the time of the event are specified by
\a buttons and \a modifiers.
For backwards compatibility, the event can also hold monodirectional wheel
event data: \a qt4Delta specifies the rotation, and \a qt4Orientation the
direction.
The scrolling phase of the event is specified by \a phase.
If the wheel event comes from a physical mouse wheel, \a source is set to
Qt::MouseEventNotSynthesized. If it comes from a gesture detected by the
operating system, or from a non-mouse hardware device, such that \a
pixelDelta is directly related to finger movement, \a source is set to
Qt::MouseEventSynthesizedBySystem. If it comes from Qt, source would be set
to Qt::MouseEventSynthesizedByQt.
If the system is configured to invert the delta values delivered with the
event (such as natural scrolling of the touchpad on OS X), \a inverted
should be \c true. Otherwise, \a inverted is \c false
\sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool inverted)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta),
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(phase), src(source),
invertedScrolling(inverted)
{}

#endif // QT_NO_WHEELEVENT
Expand Down
7 changes: 6 additions & 1 deletion src/gui/kernel/qevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ class Q_GUI_EXPORT QWheelEvent : public QInputEvent
QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pixelDelta, QPoint angleDelta,
int qt4Delta, Qt::Orientation qt4Orientation, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source);
QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pixelDelta, QPoint angleDelta,
int qt4Delta, Qt::Orientation qt4Orientation, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool inverted);
~QWheelEvent();


Expand All @@ -210,6 +213,7 @@ class Q_GUI_EXPORT QWheelEvent : public QInputEvent
inline Qt::MouseButtons buttons() const { return mouseState; }

inline Qt::ScrollPhase phase() const { return Qt::ScrollPhase(ph); }
inline bool inverted() const { return invertedScrolling; }

Qt::MouseEventSource source() const { return Qt::MouseEventSource(src); }

Expand All @@ -223,7 +227,8 @@ class Q_GUI_EXPORT QWheelEvent : public QInputEvent
Qt::MouseButtons mouseState;
uint ph : 2;
uint src: 2;
int reserved : 28;
bool invertedScrolling : 1;
int reserved : 27;

friend class QApplication;
};
Expand Down
3 changes: 2 additions & 1 deletion src/gui/kernel/qguiapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,8 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh
return;
}

QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, buttons, e->modifiers, e->phase, e->source);
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation,
buttons, e->modifiers, e->phase, e->source, e->inverted);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
#endif /* ifndef QT_NO_WHEELEVENT */
Expand Down
12 changes: 7 additions & 5 deletions src/gui/kernel/qwindowsysteminterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ void QWindowSystemInterface::handleWheelEvent(QWindow *w, const QPointF & local,
handleWheelEvent(w, time, local, global, pixelDelta, angleDelta, mods, phase, source);
}

void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source)
void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase,
Qt::MouseEventSource source, bool invertedScrolling)
{
// Qt 4 sends two separate wheel events for horizontal and vertical
// deltas. For Qt 5 we want to send the deltas in one event, but at the
Expand All @@ -333,27 +334,28 @@ void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, con

// Simple case: vertical deltas only:
if (angleDelta.y() != 0 && angleDelta.x() == 0) {
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source);
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical,
mods, phase, source, invertedScrolling);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
return;
}

// Simple case: horizontal deltas only:
if (angleDelta.y() == 0 && angleDelta.x() != 0) {
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source);
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
return;
}

// Both horizontal and vertical deltas: Send two wheel events.
// The first event contains the Qt 5 pixel and angle delta as points,
// and in addition the Qt 4 compatibility vertical angle delta.
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source);
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source, invertedScrolling);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);

// The second event contains null pixel and angle points and the
// Qt 4 compatibility horizontal angle delta.
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source);
e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}

Expand Down
3 changes: 2 additions & 1 deletion src/gui/kernel/qwindowsysteminterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ class Q_GUI_EXPORT QWindowSystemInterface
QPoint pixelDelta, QPoint angleDelta,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::ScrollPhase phase = Qt::NoScrollPhase,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized,
bool inverted = false);

// Wheel event compatibility functions. Will be removed: do not use.
static void handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods = Qt::NoModifier);
Expand Down
5 changes: 3 additions & 2 deletions src/gui/kernel/qwindowsysteminterface_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ class Q_GUI_EXPORT QWindowSystemInterfacePrivate {
class WheelEvent : public InputEvent {
public:
WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O,
Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized)
: InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src) { }
Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized, bool inverted = false)
: InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src), inverted(inverted) { }
QPoint pixelDelta;
QPoint angleDelta;
int qt4Delta;
Expand All @@ -252,6 +252,7 @@ class Q_GUI_EXPORT QWindowSystemInterfacePrivate {
QPointF globalPos;
Qt::ScrollPhase phase;
Qt::MouseEventSource source;
bool inverted;
};

class KeyEvent : public InputEvent {
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/platforms/cocoa/qnsview.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1413,8 +1413,10 @@ - (void)scrollWheel:(NSEvent *)theEvent
} else if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
ph = Qt::NoScrollPhase;
}
// "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective.
bool isInverted = [theEvent isDirectionInvertedFromDevice];

QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source);
QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source, isInverted);
}
#endif //QT_NO_WHEELEVENT

Expand Down
2 changes: 1 addition & 1 deletion src/widgets/itemviews/qlistview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ void QListView::wheelEvent(QWheelEvent *e)
QPoint pixelDelta(e->pixelDelta().y(), e->pixelDelta().x());
QPoint angleDelta(e->angleDelta().y(), e->angleDelta().x());
QWheelEvent hwe(e->pos(), e->globalPos(), pixelDelta, angleDelta, e->delta(),
Qt::Horizontal, e->buttons(), e->modifiers(), e->phase());
Qt::Horizontal, e->buttons(), e->modifiers(), e->phase(), e->source(), e->inverted());
if (e->spontaneous())
qt_sendSpontaneousEvent(d->hbar, &hwe);
else
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/kernel/qapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3331,7 +3331,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos);

QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(),
wheel->modifiers(), phase, wheel->source());
wheel->modifiers(), phase, wheel->source(), wheel->inverted());
bool eventAccepted;
while (w) {
we.spont = spontaneous && w == receiver;
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/kernel/qwidgetwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event)

QPoint mapped = widget->mapFrom(rootWidget, pos);

QWheelEvent translated(mapped, event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source());
QWheelEvent translated(mapped, event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
QGuiApplication::sendSpontaneousEvent(widget, &translated);
}

Expand Down
2 changes: 2 additions & 0 deletions src/widgets/widgets/qabstractslider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ void QAbstractSlider::wheelEvent(QWheelEvent * e)
Q_D(QAbstractSlider);
e->ignore();
int delta = e->delta();
if (e->inverted())
delta = -delta;
if (d->scrollByDelta(e->orientation(), e->modifiers(), delta))
e->accept();
}
Expand Down

0 comments on commit bcd88d8

Please sign in to comment.