Add mouse wheel events handler to MouseArea

This patch was based on the attached patch in QTBUG-7369. It basically
exposes the wheel events to MouseArea via the onWheel signal.

The current API is based on the new QWheelEvent API introduced by this
patch: http://codereview.qt-project.org/#change,12532

Task-number: QTBUG-7369
Change-Id: Id58513715c2d0ae81e3a69e9e1ed400bbae07507
Reviewed-by: Michael Brasser <michael.brasser@nokia.com>
This commit is contained in:
Luis Gabriel Lima 2012-01-05 15:19:26 -03:00 committed by Qt by Nokia
parent 78356f6038
commit f2e1be963f
10 changed files with 314 additions and 2 deletions

View File

@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.0
Rectangle {
height: 700
width: 485
color: "#333333"
Column {
anchors.centerIn: parent
spacing: 2
Repeater {
model: ["#9ACD32", "#EEEEEE", "#FFD700", "#87CEEB"]
Rectangle {
property real scaleFactor: 1
height: 40 * scaleFactor
width: 60 * scaleFactor
color: modelData
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
if (wheel.angleDelta.y > 0)
parent.scaleFactor += 0.2;
else if (parent.scaleFactor - 0.2 >= 0.2)
parent.scaleFactor -= 0.2;
}
}
}
}
}
}
Text {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
color: "#FFD700"
text: "Rotate the mouse wheel pressing <Control> to resize the squares."
}
}

View File

@ -1185,7 +1185,8 @@ bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event
QPointF p = item->mapFromScene(event->posF()); QPointF p = item->mapFromScene(event->posF());
if (QRectF(0, 0, item->width(), item->height()).contains(p)) { if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation()); QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
event->orientation(), event->buttons(), event->modifiers());
wheel.accept(); wheel.accept();
q->sendEvent(item, &wheel); q->sendEvent(item, &wheel);
if (wheel.isAccepted()) { if (wheel.isAccepted()) {

View File

@ -236,4 +236,108 @@ Item {
*/ */
/*!
\qmlclass WheelEvent QQuickWheelEvent
\inqmlmodule QtQuick 2
\ingroup qml-event-elements
\brief The WheelEvent object provides information about a mouse wheel event.
The position of the mouse can be found via the \l x and \l y properties.
\sa MouseArea
*/
/*!
\internal
\class QQuickWheelEvent
*/
/*!
\qmlproperty int QtQuick2::WheelEvent::x
\qmlproperty int QtQuick2::WheelEvent::y
These properties hold the coordinates of the position supplied by the wheel event.
*/
/*!
\qmlproperty bool QtQuick2::WheelEvent::accepted
Setting \a accepted to true prevents the wheel event from being
propagated to items below this item.
Generally, if the item acts on the wheel event then it should be accepted
so that items lower in the stacking order do not also respond to the same event.
*/
/*!
\qmlproperty int QtQuick2::WheelEvent::buttons
This property holds the mouse buttons pressed when the wheel event was generated.
It contains a bitwise combination of:
\list
\o Qt.LeftButton
\o Qt.RightButton
\o Qt.MiddleButton
\endlist
*/
/*!
\qmlproperty point QtQuick2::WheelEvent::angleDelta
This property holds the distance that the wheel is rotated in wheel degrees.
The x and y cordinate of this property holds the delta in horizontal and
vertical orientation.
A positive value indicates that the wheel was rotated up/right;
a negative value indicates that the wheel was rotated down/left.
Most mouse types work in steps of 15 degrees, in which case the delta value is a
multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
*/
/*!
\qmlproperty point QtQuick2::WheelEvent::pixelDelta
This property holds the delta in screen pixels and is available in plataforms that
have high-resolution trackpads, such as Mac OS X.
The x and y cordinate of this property holds the delta in horizontal and
vertical orientation. The value should be used directly to scroll content on screen.
For platforms without high-resolution trackpad support, pixelDelta will always be (0,0),
and angleDelta should be used instead.
*/
/*!
\qmlproperty int QtQuick2::WheelEvent::modifiers
This property holds the keyboard modifier flags that existed immediately
before the event occurred.
It contains a bitwise combination of:
\list
\o Qt.NoModifier - No modifier key is pressed.
\o Qt.ShiftModifier - A Shift key on the keyboard is pressed.
\o Qt.ControlModifier - A Ctrl key on the keyboard is pressed.
\o Qt.AltModifier - An Alt key on the keyboard is pressed.
\o Qt.MetaModifier - A Meta key on the keyboard is pressed.
\o Qt.KeypadModifier - A keypad button is pressed.
\endlist
For example, to react to a Control key pressed during the wheel event:
\qml
MouseArea {
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
if (wheel.angleDelta.y > 0)
zoomIn();
else
zoomOut();
}
}
}
\endqml
*/
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -201,9 +201,47 @@ private:
}; };
class QQuickWheelEvent : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal x READ x)
Q_PROPERTY(qreal y READ y)
Q_PROPERTY(QPoint angleDelta READ angleDelta)
Q_PROPERTY(QPoint pixelDelta READ pixelDelta)
Q_PROPERTY(int buttons READ buttons)
Q_PROPERTY(int modifiers READ modifiers)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
QQuickWheelEvent(qreal x, qreal y, const QPoint& angleDelta, const QPoint& pixelDelta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: _x(x), _y(y), _angleDelta(angleDelta), _pixelDelta(pixelDelta), _buttons(buttons),
_modifiers(modifiers), _accepted(true) {}
qreal x() const { return _x; }
qreal y() const { return _y; }
QPoint angleDelta() const { return _angleDelta; }
QPoint pixelDelta() const { return _pixelDelta; }
int buttons() const { return _buttons; }
int modifiers() const { return _modifiers; }
bool isAccepted() { return _accepted; }
void setAccepted(bool accepted) { _accepted = accepted; }
private:
qreal _x;
qreal _y;
QPoint _angleDelta;
QPoint _pixelDelta;
Qt::MouseButtons _buttons;
Qt::KeyboardModifiers _modifiers;
bool _accepted;
};
QT_END_NAMESPACE QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickKeyEvent) QML_DECLARE_TYPE(QQuickKeyEvent)
QML_DECLARE_TYPE(QQuickMouseEvent) QML_DECLARE_TYPE(QQuickMouseEvent)
QML_DECLARE_TYPE(QQuickWheelEvent)
#endif // QQUICKEVENTS_P_P_H #endif // QQUICKEVENTS_P_P_H

View File

@ -169,6 +169,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickAnchors>(); qmlRegisterType<QQuickAnchors>();
qmlRegisterType<QQuickKeyEvent>(); qmlRegisterType<QQuickKeyEvent>();
qmlRegisterType<QQuickMouseEvent>(); qmlRegisterType<QQuickMouseEvent>();
qmlRegisterType<QQuickWheelEvent>();
qmlRegisterType<QQuickTransform>(); qmlRegisterType<QQuickTransform>();
qmlRegisterType<QQuickPathElement>(); qmlRegisterType<QQuickPathElement>();
qmlRegisterType<QQuickCurve>(); qmlRegisterType<QQuickCurve>();

View File

@ -236,6 +236,13 @@ bool QQuickMouseAreaPrivate::isClickConnected()
return QObjectPrivate::get(q)->isSignalConnected(idx); return QObjectPrivate::get(q)->isSignalConnected(idx);
} }
bool QQuickMouseAreaPrivate::isWheelConnected()
{
Q_Q(QQuickMouseArea);
static int idx = QObjectPrivate::get(q)->signalIndex("wheel(QQuickWheelEvent*)");
return QObjectPrivate::get(q)->isSignalConnected(idx);
}
void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t) void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t)
{ {
Q_Q(QQuickMouseArea); Q_Q(QQuickMouseArea);
@ -331,7 +338,8 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
Information about the mouse position and button clicks are provided via Information about the mouse position and button clicks are provided via
signals for which event handler properties are defined. The most commonly signals for which event handler properties are defined. The most commonly
used involved handling mouse presses and clicks: onClicked, onDoubleClicked, used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
onPressed, onReleased and onPressAndHold. onPressed, onReleased and onPressAndHold. It's also possible to handle mouse
wheel events via the onWheel signal.
By default, MouseArea items only report mouse clicks and not changes to the By default, MouseArea items only report mouse clicks and not changes to the
position of the mouse cursor. Setting the hoverEnabled property ensures that position of the mouse cursor. Setting the hoverEnabled property ensures that
@ -513,6 +521,17 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
the logic when the MouseArea has lost the mouse handling to the \l Flickable, the logic when the MouseArea has lost the mouse handling to the \l Flickable,
\c onCanceled should be used in addition to onReleased. \c onCanceled should be used in addition to onReleased.
*/ */
/*!
\qmlsignal QtQuick2::MouseArea::onWheel(WheelEvent mouse)
This handler is called in response to both mouse wheel and trackpad scroll gestures.
The \l {WheelEvent}{wheel} parameter provides information about the event, including the x and y
position, any buttons currently pressed, and information about the wheel movement, including
angleDelta and pixelDelta.
*/
QQuickMouseArea::QQuickMouseArea(QQuickItem *parent) QQuickMouseArea::QQuickMouseArea(QQuickItem *parent)
: QQuickItem(*(new QQuickMouseAreaPrivate), parent) : QQuickItem(*(new QQuickMouseAreaPrivate), parent)
{ {
@ -860,6 +879,22 @@ void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
setHovered(false); setHovered(false);
} }
void QQuickMouseArea::wheelEvent(QWheelEvent *event)
{
Q_D(QQuickMouseArea);
if (!d->absorb) {
QQuickItem::wheelEvent(event);
return;
}
QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
event->pixelDelta(), event->buttons(), event->modifiers());
we.setAccepted(d->isWheelConnected());
emit wheel(&we);
if (!we.isAccepted())
QQuickItem::wheelEvent(event);
}
void QQuickMouseArea::ungrabMouse() void QQuickMouseArea::ungrabMouse()
{ {
Q_D(QQuickMouseArea); Q_D(QQuickMouseArea);

View File

@ -119,6 +119,7 @@ private:
}; };
class QQuickMouseAreaPrivate; class QQuickMouseAreaPrivate;
class QQuickWheelEvent;
// used in QtLocation // used in QtLocation
class Q_QUICK_EXPORT QQuickMouseArea : public QQuickItem class Q_QUICK_EXPORT QQuickMouseArea : public QQuickItem
{ {
@ -182,6 +183,7 @@ Q_SIGNALS:
void released(QQuickMouseEvent *mouse); void released(QQuickMouseEvent *mouse);
void clicked(QQuickMouseEvent *mouse); void clicked(QQuickMouseEvent *mouse);
void doubleClicked(QQuickMouseEvent *mouse); void doubleClicked(QQuickMouseEvent *mouse);
void wheel(QQuickWheelEvent *wheel);
void entered(); void entered();
void exited(); void exited();
void canceled(); void canceled();
@ -199,6 +201,7 @@ protected:
virtual void hoverEnterEvent(QHoverEvent *event); virtual void hoverEnterEvent(QHoverEvent *event);
virtual void hoverMoveEvent(QHoverEvent *event); virtual void hoverMoveEvent(QHoverEvent *event);
virtual void hoverLeaveEvent(QHoverEvent *event); virtual void hoverLeaveEvent(QHoverEvent *event);
virtual void wheelEvent(QWheelEvent *event);
virtual bool childMouseEventFilter(QQuickItem *i, QEvent *e); virtual bool childMouseEventFilter(QQuickItem *i, QEvent *e);
virtual void timerEvent(QTimerEvent *event); virtual void timerEvent(QTimerEvent *event);
virtual void windowDeactivateEvent(); virtual void windowDeactivateEvent();

View File

@ -83,6 +83,7 @@ public:
bool isPressAndHoldConnected(); bool isPressAndHoldConnected();
bool isDoubleClickConnected(); bool isDoubleClickConnected();
bool isClickConnected(); bool isClickConnected();
bool isWheelConnected();
bool absorb : 1; bool absorb : 1;
bool hovered : 1; bool hovered : 1;

View File

@ -0,0 +1,24 @@
import QtQuick 2.0
Rectangle {
id: root
property var angleDeltaY
property real mouseX
property real mouseY
property bool controlPressed
width: 400
height: 400
MouseArea {
anchors.fill: parent
onWheel: {
root.angleDeltaY = wheel.angleDelta.y;
root.mouseX = wheel.x;
root.mouseY = wheel.y;
root.controlPressed = wheel.modifiers & Qt.ControlModifier;
}
}
}

View File

@ -73,6 +73,7 @@ private slots:
void hoverPropagation(); void hoverPropagation();
void hoverVisible(); void hoverVisible();
void disableAfterPress(); void disableAfterPress();
void onWheel();
private: private:
QQuickView *createView(); QQuickView *createView();
@ -906,6 +907,26 @@ void tst_QQuickMouseArea::disableAfterPress()
delete canvas; delete canvas;
} }
void tst_QQuickMouseArea::onWheel()
{
QQuickView *canvas = createView();
canvas->setSource(testFileUrl("wheel.qml"));
QQuickItem *root = canvas->rootObject();
QVERIFY(root != 0);
QWheelEvent wheelEvent(QPoint(10, 32), QPoint(10, 32), QPoint(60, 20), QPoint(0, 120),
0, Qt::Vertical,Qt::NoButton, Qt::ControlModifier);
QGuiApplication::sendEvent(canvas, &wheelEvent);
QCOMPARE(root->property("angleDeltaY").toInt(), 120);
QCOMPARE(root->property("mouseX").toReal(), qreal(10));
QCOMPARE(root->property("mouseY").toReal(), qreal(32));
QCOMPARE(root->property("controlPressed").toBool(), true);
delete canvas;
}
QTEST_MAIN(tst_QQuickMouseArea) QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc" #include "tst_qquickmousearea.moc"