aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/handlers')
-rw-r--r--src/quick/handlers/handlers.pri7
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp45
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h13
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp11
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp170
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h18
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p_p.h83
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp58
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h1
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp3
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp16
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp520
-rw-r--r--src/quick/handlers/qquickwheelhandler_p.h128
-rw-r--r--src/quick/handlers/qquickwheelhandler_p_p.h87
14 files changed, 1045 insertions, 115 deletions
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 226cca22cb..49975bd1ca 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -3,6 +3,7 @@ HEADERS += \
$$PWD/qquickhandlerpoint_p.h \
$$PWD/qquickhoverhandler_p.h \
$$PWD/qquickmultipointhandler_p.h \
+ $$PWD/qquickmultipointhandler_p_p.h \
$$PWD/qquickpinchhandler_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
$$PWD/qquickpointerdevicehandler_p_p.h \
@@ -26,3 +27,9 @@ SOURCES += \
$$PWD/qquicksinglepointhandler.cpp \
$$PWD/qquicktaphandler.cpp \
$$PWD/qquickdragaxis.cpp
+
+qtConfig(wheelevent) {
+ HEADERS += $$PWD/qquickwheelhandler_p.h $$PWD/qquickwheelhandler_p_p.h
+ SOURCES += $$PWD/qquickwheelhandler.cpp
+}
+
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index e5531369cf..61b66beff4 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -103,7 +103,7 @@ bool QQuickDragHandler::targetContainsCentroid()
QPointF QQuickDragHandler::targetCentroidPosition()
{
- QPointF pos = m_centroid.position();
+ QPointF pos = centroid().position();
if (target() != parentItem())
pos = parentItem()->mapToItem(target(), pos);
return pos;
@@ -114,8 +114,14 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
if (grabber == this && transition == QQuickEventPoint::GrabExclusive && target()) {
// In case the grab got handed over from another grabber, we might not get the Press.
- if (!m_pressedInsideTarget) {
- if (target() != parentItem())
+
+ auto isDescendant = [](QQuickItem *parent, QQuickItem *target) {
+ return (target != parent) && !target->isAncestorOf(parent);
+ };
+ if (m_snapMode == SnapAlways
+ || (m_snapMode == SnapIfPressedOutsideTarget && !m_pressedInsideTarget)
+ || (m_snapMode == SnapAuto && !m_pressedInsideTarget && isDescendant(parentItem(), target()))
+ ) {
m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
} else if (m_pressTargetPos.isNull()) {
m_pressTargetPos = targetCentroidPosition();
@@ -123,6 +129,33 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
}
}
+/*!
+ \qmlproperty enumeration QtQuick.DragHandler::snapMode
+
+ This property holds the snap mode.
+
+ The snap mode configures snapping of the \l target item's center to the event point.
+
+ Possible values:
+ \value DragHandler.SnapNever Never snap
+ \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l parentItem (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAlways Always snap
+*/
+QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
+{
+ return m_snapMode;
+}
+
+void QQuickDragHandler::setSnapMode(QQuickDragHandler::SnapMode mode)
+{
+ if (mode == m_snapMode)
+ return;
+ m_snapMode = mode;
+ emit snapModeChanged();
+}
+
void QQuickDragHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
@@ -153,7 +186,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
if (active()) {
// Calculate drag delta, taking into account the axis enabled constraint
// i.e. if xAxis is not enabled, then ignore the horizontal component of the actual movement
- QVector2D accumulatedDragDelta = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ QVector2D accumulatedDragDelta = QVector2D(centroid().scenePosition() - centroid().scenePressPosition());
if (!m_xAxis.enabled())
accumulatedDragDelta.setX(0);
if (!m_yAxis.enabled())
@@ -169,9 +202,9 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
QVector <QQuickEventPoint *> chosenPoints;
if (event->isPressEvent())
- m_pressedInsideTarget = target() && m_currentPoints.count() > 0;
+ m_pressedInsideTarget = target() && currentPoints().count() > 0;
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ for (const QQuickHandlerPoint &p : currentPoints()) {
if (!allOverThreshold)
break;
QQuickEventPoint *point = event->pointById(p.id());
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index 748026488a..e8f47163ed 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -62,8 +62,17 @@ class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged REVISION 14)
public:
+ enum SnapMode {
+ NoSnap = 0,
+ SnapAuto,
+ SnapIfPressedOutsideTarget,
+ SnapAlways
+ };
+ Q_ENUM(SnapMode)
+
explicit QQuickDragHandler(QQuickItem *parent = nullptr);
void handlePointerEventImpl(QQuickPointerEvent *event) override;
@@ -73,11 +82,14 @@ public:
QVector2D translation() const { return m_translation; }
void setTranslation(const QVector2D &trans);
+ QQuickDragHandler::SnapMode snapMode() const;
+ void setSnapMode(QQuickDragHandler::SnapMode mode);
void enforceConstraints();
Q_SIGNALS:
void translationChanged();
+ Q_REVISION(14) void snapModeChanged();
protected:
void onActiveChanged() override;
@@ -97,6 +109,7 @@ private:
QQuickDragAxis m_xAxis;
QQuickDragAxis m_yAxis;
+ QQuickDragHandler::SnapMode m_snapMode = SnapAuto;
bool m_pressedInsideTarget = false;
friend class QQuickDragAxis;
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index a6325e084b..79cb288af8 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -72,9 +72,6 @@ QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
{
// Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
d_func()->acceptedButtons = Qt::NoButton;
- // Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
- setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
- static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
}
QQuickHoverHandler::~QQuickHoverHandler()
@@ -103,7 +100,11 @@ bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
{
- setHovered(true);
+ bool hovered = true;
+ if (point->state() == QQuickEventPoint::Released &&
+ point->pointerEvent()->device()->pointerType() == QQuickPointerDevice::Finger)
+ hovered = false;
+ setHovered(hovered);
setPassiveGrab(point);
}
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index 6fbab29077..f404788de4 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickmultipointhandler_p.h"
+#include "qquickmultipointhandler_p_p.h"
#include <private/qquickitem_p.h>
#include <QLineF>
#include <QMouseEvent>
@@ -59,14 +60,13 @@ QT_BEGIN_NAMESPACE
of multiple touchpoints.
*/
QQuickMultiPointHandler::QQuickMultiPointHandler(QQuickItem *parent, int minimumPointCount, int maximumPointCount)
- : QQuickPointerDeviceHandler(parent)
- , m_minimumPointCount(minimumPointCount)
- , m_maximumPointCount(maximumPointCount)
+ : QQuickPointerDeviceHandler(*(new QQuickMultiPointHandlerPrivate(minimumPointCount, maximumPointCount)), parent)
{
}
bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(QQuickMultiPointHandler);
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
@@ -84,14 +84,14 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
// handle a specific number of points, so a differing number of points will
// usually result in different behavior. But otherwise if the currentPoints
// are all still there in the event, we're good to go (do not reset
- // m_currentPoints, because we don't want to lose the pressPosition, and do
+ // currentPoints, because we don't want to lose the pressPosition, and do
// not want to reshuffle the order either).
const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
- if (candidatePoints.count() != m_currentPoints.count()) {
- m_currentPoints.clear();
+ if (candidatePoints.count() != d->currentPoints.count()) {
+ d->currentPoints.clear();
if (active()) {
setActive(false);
- m_centroid.reset();
+ d->centroid.reset();
emit centroidChanged();
}
} else if (hasCurrentPoints(event)) {
@@ -101,57 +101,60 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
ret = ret || (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
if (ret) {
const int c = candidatePoints.count();
- m_currentPoints.resize(c);
+ d->currentPoints.resize(c);
for (int i = 0; i < c; ++i) {
- m_currentPoints[i].reset(candidatePoints[i]);
- m_currentPoints[i].localize(parentItem());
+ d->currentPoints[i].reset(candidatePoints[i]);
+ d->currentPoints[i].localize(parentItem());
}
} else {
- m_currentPoints.clear();
+ d->currentPoints.clear();
}
return ret;
}
void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
+ Q_D(QQuickMultiPointHandler);
QQuickPointerHandler::handlePointerEventImpl(event);
- // event's points can be reordered since the previous event, which is why m_currentPoints
+ // event's points can be reordered since the previous event, which is why currentPoints
// is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector.
- // So we have to update our m_currentPoints instances based on the given event.
- for (QQuickHandlerPoint &p : m_currentPoints) {
+ // So we have to update our currentPoints instances based on the given event.
+ for (QQuickHandlerPoint &p : d->currentPoints) {
const QQuickEventPoint *ep = event->pointById(p.id());
if (ep)
p.reset(ep);
}
- QPointF sceneGrabPos = m_centroid.sceneGrabPosition();
- m_centroid.reset(m_currentPoints);
- m_centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
+ QPointF sceneGrabPos = d->centroid.sceneGrabPosition();
+ d->centroid.reset(d->currentPoints);
+ d->centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
emit centroidChanged();
}
void QQuickMultiPointHandler::onActiveChanged()
{
+ Q_D(QQuickMultiPointHandler);
if (active()) {
- m_centroid.m_sceneGrabPosition = m_centroid.m_scenePosition;
+ d->centroid.m_sceneGrabPosition = d->centroid.m_scenePosition;
} else {
- // Don't call m_centroid.reset() here, because in a QML onActiveChanged
+ // Don't call centroid.reset() here, because in a QML onActiveChanged
// callback, we'd like to see what the position _was_, what the velocity _was_, etc.
// (having them undefined is not useful)
// But pressedButtons and pressedModifiers are meant to be more real-time than those
// (which seems a bit inconsistent, from one side).
- m_centroid.m_pressedButtons = Qt::NoButton;
- m_centroid.m_pressedModifiers = Qt::NoModifier;
+ d->centroid.m_pressedButtons = Qt::NoButton;
+ d->centroid.m_pressedModifiers = Qt::NoModifier;
}
}
void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
{
+ Q_D(QQuickMultiPointHandler);
// If another handler or item takes over this set of points, assume it has
// decided that it's the better fit for them. Don't immediately re-grab
// at the next opportunity. This should help to avoid grab cycles
// (e.g. between DragHandler and PinchHandler).
if (transition == QQuickEventPoint::UngrabExclusive || transition == QQuickEventPoint::CancelGrabExclusive)
- m_currentPoints.clear();
+ d->currentPoints.clear();
}
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
@@ -191,14 +194,21 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
The default value is 2.
*/
+int QQuickMultiPointHandler::minimumPointCount() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->minimumPointCount;
+}
+
void QQuickMultiPointHandler::setMinimumPointCount(int c)
{
- if (m_minimumPointCount == c)
+ Q_D(QQuickMultiPointHandler);
+ if (d->minimumPointCount == c)
return;
- m_minimumPointCount = c;
+ d->minimumPointCount = c;
emit minimumPointCountChanged();
- if (m_maximumPointCount < 0)
+ if (d->maximumPointCount < 0)
emit maximumPointCountChanged();
}
@@ -217,22 +227,61 @@ void QQuickMultiPointHandler::setMinimumPointCount(int c)
The default value is the same as \l minimumPointCount.
*/
+int QQuickMultiPointHandler::maximumPointCount() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->maximumPointCount >= 0 ? d->maximumPointCount : d->minimumPointCount;
+}
+
void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
{
- if (m_maximumPointCount == maximumPointCount)
+ Q_D(QQuickMultiPointHandler);
+ if (d->maximumPointCount == maximumPointCount)
return;
- m_maximumPointCount = maximumPointCount;
+ d->maximumPointCount = maximumPointCount;
emit maximumPointCountChanged();
}
+/*!
+ \readonly
+ \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+
+ A point exactly in the middle of the currently-pressed touch points.
+ If only one point is pressed, it's the same as that point.
+ A handler that has a \l target will normally transform it relative to this point.
+*/
+const QQuickHandlerPoint &QQuickMultiPointHandler::centroid() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->centroid;
+}
+
+/*!
+ Returns a modifiable reference to the point that will be returned by the
+ \l centroid property. If you modify it, you are responsible to emit
+ \l centroidChanged.
+*/
+QQuickHandlerPoint &QQuickMultiPointHandler::mutableCentroid()
+{
+ Q_D(QQuickMultiPointHandler);
+ return d->centroid;
+}
+
+QVector<QQuickHandlerPoint> &QQuickMultiPointHandler::currentPoints()
+{
+ Q_D(QQuickMultiPointHandler);
+ return d->currentPoints;
+}
+
bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
{
- if (event->pointCount() < m_currentPoints.size() || m_currentPoints.size() == 0)
+ Q_D(const QQuickMultiPointHandler);
+ if (event->pointCount() < d->currentPoints.size() || d->currentPoints.size() == 0)
return false;
// TODO optimize: either ensure the points are sorted,
// or use std::equal with a predicate
- for (const QQuickHandlerPoint &p : qAsConst(m_currentPoints)) {
+ for (const QQuickHandlerPoint &p : qAsConst(d->currentPoints)) {
const QQuickEventPoint *ep = event->pointById(p.id());
if (!ep)
return false;
@@ -244,30 +293,33 @@ bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
{
+ Q_D(const QQuickMultiPointHandler);
qreal ret = 0;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ if (Q_UNLIKELY(d->currentPoints.size() == 0))
return ret;
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : d->currentPoints)
ret += QVector2D(p.scenePosition() - ref).length();
- return ret / m_currentPoints.size();
+ return ret / d->currentPoints.size();
}
qreal QQuickMultiPointHandler::averageStartingDistance(const QPointF &ref)
{
+ Q_D(const QQuickMultiPointHandler);
// TODO cache it in setActive()?
qreal ret = 0;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ if (Q_UNLIKELY(d->currentPoints.size() == 0))
return ret;
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : d->currentPoints)
ret += QVector2D(p.sceneGrabPosition() - ref).length();
- return ret / m_currentPoints.size();
+ return ret / d->currentPoints.size();
}
QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(const QPointF &ref) const
{
+ Q_D(const QQuickMultiPointHandler);
QVector<PointData> angles;
- angles.reserve(m_currentPoints.count());
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ angles.reserve(d->currentPoints.count());
+ for (const QQuickHandlerPoint &p : d->currentPoints) {
qreal angle = QLineF(ref, p.scenePosition()).angle();
angles.append(PointData(p.id(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
}
@@ -312,7 +364,7 @@ void QQuickMultiPointHandler::acceptPoints(const QVector<QQuickEventPoint *> &po
point->setAccepted();
}
-bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
+bool QQuickMultiPointHandler::grabPoints(const QVector<QQuickEventPoint *> &points)
{
if (points.isEmpty())
return false;
@@ -332,17 +384,41 @@ bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
void QQuickMultiPointHandler::moveTarget(QPointF pos)
{
- target()->setPosition(pos);
- m_centroid.m_position = target()->mapFromScene(m_centroid.m_scenePosition);
+ Q_D(QQuickMultiPointHandler);
+ if (QQuickItem *t = target()) {
+ d->xMetaProperty().write(t, pos.x());
+ d->yMetaProperty().write(t, pos.y());
+ d->centroid.m_position = t->mapFromScene(d->centroid.m_scenePosition);
+ } else {
+ qWarning() << "moveTarget: target is null";
+ }
}
-/*!
- \readonly
- \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+QQuickMultiPointHandlerPrivate::QQuickMultiPointHandlerPrivate(int minPointCount, int maxPointCount)
+ : QQuickPointerDeviceHandlerPrivate()
+ , minimumPointCount(minPointCount)
+ , maximumPointCount(maxPointCount)
+{
+}
- A point exactly in the middle of the currently-pressed touch points.
- If only one point is pressed, it's the same as that point.
- A handler that has a \l target will normally transform it relative to this point.
-*/
+QMetaProperty &QQuickMultiPointHandlerPrivate::xMetaProperty() const
+{
+ Q_Q(const QQuickMultiPointHandler);
+ if (!xProperty.isValid() && q->target()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ xProperty = targetMeta->property(targetMeta->indexOfProperty("x"));
+ }
+ return xProperty;
+}
+
+QMetaProperty &QQuickMultiPointHandlerPrivate::yMetaProperty() const
+{
+ Q_Q(const QQuickMultiPointHandler);
+ if (!yProperty.isValid() && q->target()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ yProperty = targetMeta->property(targetMeta->indexOfProperty("y"));
+ }
+ return yProperty;
+}
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index 06f170154b..480f69035b 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
+class QQuickMultiPointHandlerPrivate;
+
class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
@@ -68,13 +70,13 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDevic
public:
explicit QQuickMultiPointHandler(QQuickItem *parent = nullptr, int minimumPointCount = 2, int maximumPointCount = -1);
- int minimumPointCount() const { return m_minimumPointCount; }
+ int minimumPointCount() const;
void setMinimumPointCount(int c);
- int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
+ int maximumPointCount() const;
void setMaximumPointCount(int maximumPointCount);
- QQuickHandlerPoint centroid() const { return m_centroid; }
+ const QQuickHandlerPoint &centroid() const;
signals:
void minimumPointCountChanged();
@@ -94,6 +96,8 @@ protected:
void handlePointerEventImpl(QQuickPointerEvent *event) override;
void onActiveChanged() override;
void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+ QVector<QQuickHandlerPoint> &currentPoints();
+ QQuickHandlerPoint &mutableCentroid();
bool hasCurrentPoints(QQuickPointerEvent *event);
QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
qreal averageTouchPointDistance(const QPointF &ref);
@@ -103,14 +107,10 @@ protected:
QVector<PointData> angles(const QPointF &ref) const;
static qreal averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles);
void acceptPoints(const QVector<QQuickEventPoint *> &points);
- bool grabPoints(QVector<QQuickEventPoint *> points);
+ bool grabPoints(const QVector<QQuickEventPoint *> &points);
void moveTarget(QPointF pos);
-protected:
- QVector<QQuickHandlerPoint> m_currentPoints;
- QQuickHandlerPoint m_centroid;
- int m_minimumPointCount;
- int m_maximumPointCount;
+ Q_DECLARE_PRIVATE(QQuickMultiPointHandler)
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p_p.h b/src/quick/handlers/qquickmultipointhandler_p_p.h
new file mode 100644
index 0000000000..406e4a1fdf
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler_p_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPOINTERMULTIHANDLER_P_H
+#define QQUICKPOINTERMULTIHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickhandlerpoint_p.h"
+#include "qquickpointerdevicehandler_p_p.h"
+#include "qquickmultipointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickMultiPointHandler)
+
+public:
+ static QQuickMultiPointHandlerPrivate* get(QQuickMultiPointHandler *q) { return q->d_func(); }
+ static const QQuickMultiPointHandlerPrivate* get(const QQuickMultiPointHandler *q) { return q->d_func(); }
+
+ QQuickMultiPointHandlerPrivate(int minPointCount, int maxPointCount);
+
+ QMetaProperty &xMetaProperty() const;
+ QMetaProperty &yMetaProperty() const;
+
+ QVector<QQuickHandlerPoint> currentPoints;
+ QQuickHandlerPoint centroid;
+ int minimumPointCount;
+ int maximumPointCount;
+ mutable QMetaProperty xProperty;
+ mutable QMetaProperty yProperty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERMULTIHANDLER_P_H
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index dc1a9a92f9..a5a867015c 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -267,20 +267,14 @@ void QQuickPinchHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
if (active()) {
- m_startMatrix = QMatrix4x4();
- m_startAngles = angles(m_centroid.sceneGrabPosition());
- m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
+ m_startAngles = angles(centroid().sceneGrabPosition());
+ m_startDistance = averageTouchPointDistance(centroid().sceneGrabPosition());
m_activeRotation = 0;
m_activeTranslation = QVector2D();
if (const QQuickItem *t = target()) {
m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
m_startRotation = t->rotation();
- QVector3D xformOrigin(t->transformOriginPoint());
- m_startMatrix.translate(float(t->x()), float(t->y()));
- m_startMatrix.translate(xformOrigin);
- m_startMatrix.scale(float(m_startScale));
- m_startMatrix.rotate(float(m_startRotation), 0, 0, -1);
- m_startMatrix.translate(-xformOrigin);
+ m_startPos = t->position();
} else {
m_startScale = m_accumulatedScale;
m_startRotation = 0;
@@ -294,7 +288,7 @@ void QQuickPinchHandler::onActiveChanged()
void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : currentPoints())
qCDebug(lcPinchHandler) << hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
}
QQuickMultiPointHandler::handlePointerEventImpl(event);
@@ -302,13 +296,13 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
qreal dist = 0;
#if QT_CONFIG(gestures)
if (const auto gesture = event->asPointerNativeGestureEvent()) {
- m_centroid.reset(event->point(0));
+ mutableCentroid().reset(event->point(0));
switch (gesture->type()) {
case Qt::EndNativeGesture:
m_activeScale = 1;
m_activeRotation = 0;
m_activeTranslation = QVector2D();
- m_centroid.reset();
+ mutableCentroid().reset();
setActive(false);
emit updated();
return;
@@ -333,7 +327,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
const bool containsReleasedPoints = event->isReleaseEvent();
QVector<QQuickEventPoint *> chosenPoints;
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ for (const QQuickHandlerPoint &p : currentPoints()) {
QQuickEventPoint *ep = event->pointById(p.id());
chosenPoints << ep;
}
@@ -341,8 +335,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
// Verify that at least one of the points has moved beyond threshold needed to activate the handler
int numberOfPointsDraggedOverThreshold = 0;
QVector2D accumulatedDrag;
- const QVector2D currentCentroid(m_centroid.scenePosition());
- const QVector2D pressCentroid(m_centroid.scenePressPosition());
+ const QVector2D currentCentroid(centroid().scenePosition());
+ const QVector2D pressCentroid(centroid().scenePressPosition());
QStyleHints *styleHints = QGuiApplication::styleHints();
const int dragThreshold = styleHints->startDragDistance();
@@ -410,9 +404,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
}
const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
- accumulatedMovementMagnitude /= m_currentPoints.count();
+ accumulatedMovementMagnitude /= currentPoints().count();
- QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
+ QVector2D avgDrag = accumulatedDrag / currentPoints().count();
if (!xAxis()->enabled())
avgDrag.setX(0);
if (!yAxis()->enabled())
@@ -445,12 +439,12 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
// avoid mapping the minima and maxima, as they might have unmappable values
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
// 1. scale
- dist = averageTouchPointDistance(m_centroid.scenePosition());
+ dist = averageTouchPointDistance(centroid().scenePosition());
m_activeScale = dist / m_startDistance;
m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
// 2. rotate
- QVector<PointData> newAngles = angles(m_centroid.scenePosition());
+ QVector<PointData> newAngles = angles(centroid().scenePosition());
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
m_activeRotation += angleDelta;
m_startAngles = std::move(newAngles);
@@ -465,25 +459,15 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
m_accumulatedScale = m_startScale * m_activeScale;
if (target() && target()->parentItem()) {
- const QPointF centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
+ const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition());
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition());
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
// apply rotation + scaling around the centroid - then apply translation.
- QMatrix4x4 mat;
-
- const QVector3D centroidParentVector(centroidParentPos);
- mat.translate(centroidParentVector);
- mat.rotate(float(m_activeRotation), 0, 0, 1);
- mat.scale(float(m_activeScale));
- mat.translate(-centroidParentVector);
- mat.translate(QVector3D(m_activeTranslation));
-
- mat = mat * m_startMatrix;
-
- QPointF xformOriginPoint = target()->transformOriginPoint();
- QPointF pos = mat * xformOriginPoint;
- pos -= xformOriginPoint;
+ QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos,
+ m_startPos, m_activeTranslation,
+ m_startScale, m_activeScale,
+ m_startRotation, m_activeRotation);
if (xAxis()->enabled())
pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
@@ -498,10 +482,10 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
target()->setRotation(rotation);
target()->setScale(m_accumulatedScale);
} else {
- m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ m_activeTranslation = QVector2D(centroid().scenePosition() - centroid().scenePressPosition());
}
- qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
+ qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition()
<< ", distance" << m_startDistance << "->" << dist
<< ", startScale" << m_startScale << "->" << m_accumulatedScale
<< ", activeRotation" << m_activeRotation
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index 766d57f892..cea794f0c8 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -158,7 +158,6 @@ private:
qreal m_accumulatedStartCentroidDistance = 0;
QVector<PointData> m_startAngles;
QQuickMatrix4x4 m_transform;
- QMatrix4x4 m_startMatrix;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 2964042f39..90f31bf9fd 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -307,7 +307,8 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
return false;
// HoverHandler sets acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
if (event->device()->pointerType() != QQuickPointerDevice::Finger && acceptedButtons() != Qt::NoButton &&
- (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0
+ && !event->asPointerScrollEvent())
return false;
return true;
}
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 3b42c5e536..7b26adbe6f 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -78,8 +78,8 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
\l gesturePolicy to \c TapHandler.ReleaseWithinBounds.
For multi-tap gestures (double-tap, triple-tap etc.), the distance moved
- must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and
- QPlatformTheme::TouchDoubleTapDistance with touch, and the time between
+ must not exceed QStyleHints::mouseDoubleClickDistance() with mouse and
+ QStyleHints::touchDoubleTapDistance() with touch, and the time between
taps must not exceed QStyleHints::mouseDoubleClickInterval().
\sa MouseArea
@@ -90,11 +90,9 @@ QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
{
if (m_mouseMultiClickDistanceSquared < 0) {
m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
- m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()->
- themeHint(QPlatformTheme::MouseDoubleClickDistance).toInt();
+ m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
- m_touchMultiTapDistanceSquared = QGuiApplicationPrivate::platformTheme()->
- themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
}
}
@@ -410,9 +408,9 @@ void QQuickTapHandler::updateTimeHeld()
\since 5.11
This signal is emitted when the \c parent Item is tapped twice within a
- short span of time (QStyleHints::mouseDoubleClickInterval) and distance
- (QPlatformTheme::MouseDoubleClickDistance or
- QPlatformTheme::TouchDoubleTapDistance). This signal always occurs after
+ short span of time (QStyleHints::mouseDoubleClickInterval()) and distance
+ (QStyleHints::mouseDoubleClickDistance() or
+ QStyleHints::touchDoubleTapDistance()). This signal always occurs after
\l singleTapped, \l tapped, and \l tapCountChanged. The \a eventPoint
signal parameter contains information from the release event about the
point that was tapped.
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
new file mode 100644
index 0000000000..90e4fef97e
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -0,0 +1,520 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickwheelhandler_p.h"
+#include "qquickwheelhandler_p_p.h"
+#include <QLoggingCategory>
+#include <QtMath>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel")
+
+/*!
+ \qmltype WheelHandler
+ \instantiates QQuickWheelHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for the mouse wheel.
+
+ WheelHandler is a handler that is used to interactively manipulate some
+ numeric property of an Item as the user rotates the mouse wheel. Like other
+ Input Handlers, by default it manipulates its \l {PointerHandler::target}
+ {target}. Declare \l property to control which target property will be
+ manipulated:
+
+ \snippet pointerHandlers/wheelHandler.qml 0
+
+ \l BoundaryRule is quite useful in combination with WheelHandler (as well
+ as with other Input Handlers) to declare the allowed range of values that
+ the target property can have. For example it is possible to implement
+ scrolling using a combination of WheelHandler and \l DragHandler to
+ manipulate the scrollable Item's \l{QQuickItem::y}{y} property when the
+ user rotates the wheel or drags the item on a touchscreen, and
+ \l BoundaryRule to limit the range of motion from the top to the bottom:
+
+ \snippet pointerHandlers/handlerFlick.qml 0
+
+ Alternatively if \l targetProperty is not set or \l target is null,
+ WheelHandler will not automatically manipulate anything; but the
+ \l rotation property can be used in a binding to manipulate another
+ property, or you can implement \c onWheel and handle the wheel event
+ directly.
+
+ WheelHandler handles only a rotating mouse wheel by default.
+ Optionally it can handle smooth-scrolling events from touchpad gestures,
+ by setting \l acceptedDevices to \c{PointerDevice.Mouse | PointerDevice.TouchPad}.
+
+ \note Some non-mouse hardware (such as a touch-sensitive Wacom tablet, or
+ a Linux laptop touchpad) generates real wheel events from gestures.
+ WheelHandler will respond to those events as wheel events regardless of the
+ setting of the \l acceptedDevices property.
+
+ \sa MouseArea
+ \sa Flickable
+*/
+
+QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
+ : QQuickSinglePointHandler(*(new QQuickWheelHandlerPrivate), parent)
+{
+ setAcceptedDevices(QQuickPointerDevice::Mouse);
+}
+
+/*!
+ \qmlproperty enum QtQuick::WheelHandler::orientation
+
+ Which wheel to react to. The default is \c Qt.Vertical.
+
+ Not every mouse has a \c Horizontal wheel; sometimes it is emulated by
+ tilting the wheel sideways. A touchpad can usually generate both vertical
+ and horizontal wheel events.
+*/
+Qt::Orientation QQuickWheelHandler::orientation() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->orientation;
+}
+
+void QQuickWheelHandler::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->orientation == orientation)
+ return;
+
+ d->orientation = orientation;
+ emit orientationChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick::WheelHandler::invertible
+
+ Whether or not to reverse the direction of property change if
+ \l QQuickPointerScrollEvent::inverted is true. The default is \c true.
+
+ If the operating system has a "natural scrolling" setting that causes
+ scrolling to be in the same direction as the finger movement, then if this
+ property is set to \c true, and WheelHandler is directly setting a property
+ on \l target, the direction of movement will correspond to the system setting.
+ If this property is set to \l false, it will invert the \l rotation so that
+ the direction of motion is always the same as the direction of finger movement.
+*/
+bool QQuickWheelHandler::isInvertible() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->invertible;
+}
+
+void QQuickWheelHandler::setInvertible(bool invertible)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->invertible == invertible)
+ return;
+
+ d->invertible = invertible;
+ emit invertibleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::activeTimeout
+
+ The amount of time in seconds after which the \l active property will
+ revert to \c false if no more wheel events are received. The default is
+ \c 0.1 (100 ms).
+
+ When WheelHandler handles events that contain
+ \l {Qt::ScrollPhase}{scroll phase} information, such as events from some
+ touchpads, the \l active property will become \c false as soon as an event
+ with phase \l Qt::ScrollEnd is received; in that case the timeout is not
+ necessary. But a conventional mouse with a wheel does not provide the
+ \l {QQuickPointerScrollEvent::phase}{scroll phase}: the mouse cannot detect
+ when the user has decided to stop scrolling, so the \l active property
+ transitions to \c false after this much time has elapsed.
+*/
+qreal QQuickWheelHandler::activeTimeout() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->activeTimeout;
+}
+
+void QQuickWheelHandler::setActiveTimeout(qreal timeout)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->activeTimeout, timeout))
+ return;
+
+ if (timeout < 0) {
+ qWarning("activeTimeout must be positive");
+ return;
+ }
+
+ d->activeTimeout = timeout;
+ emit activeTimeoutChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::rotation
+
+ The angle through which the mouse wheel has been rotated since the last
+ time this property was set, in wheel degrees.
+
+ A positive value indicates that the wheel was rotated up/right;
+ a negative value indicates that the wheel was rotated down/left.
+
+ A basic mouse click-wheel works in steps of 15 degrees.
+
+ The default is \c 0 at startup. It can be programmatically set to any value
+ at any time. The value will be adjusted from there as the user rotates the
+ mouse wheel.
+
+ \sa orientation
+*/
+qreal QQuickWheelHandler::rotation() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->rotation * d->rotationScale;
+}
+
+void QQuickWheelHandler::setRotation(qreal rotation)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->rotation, rotation / d->rotationScale))
+ return;
+
+ d->rotation = rotation / d->rotationScale;
+ emit rotationChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::rotationScale
+
+ The scaling to be applied to the \l rotation property, and to the
+ \l property on the \l target item, if any. The default is 1, such that
+ \l rotation will be in units of degrees of rotation. It can be set to a
+ negative number to invert the effect of the direction of mouse wheel
+ rotation.
+*/
+qreal QQuickWheelHandler::rotationScale() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->rotationScale;
+}
+
+void QQuickWheelHandler::setRotationScale(qreal rotationScale)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->rotationScale, rotationScale))
+ return;
+ if (qFuzzyIsNull(rotationScale)) {
+ qWarning("rotationScale cannot be set to zero");
+ return;
+ }
+
+ d->rotationScale = rotationScale;
+ emit rotationScaleChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick::WheelHandler::property
+
+ The property to be modified on the \l target when the mouse wheel is rotated.
+
+ The default is no property (empty string). When no target property is being
+ automatically modified, you can use bindings to react to mouse wheel
+ rotation in arbitrary ways.
+
+ You can use the mouse wheel to adjust any numeric property. For example if
+ \c property is set to \c x, the \l target will move horizontally as the
+ wheel is rotated. The following properties have special behavior:
+
+ \value scale
+ \l{QQuickItem::scale}{scale} will be modified in a non-linear fashion
+ as described under \l targetScaleMultiplier. If
+ \l targetTransformAroundCursor is \c true, the \l{QQuickItem::x}{x} and
+ \l{QQuickItem::y}{y} properties will be simultaneously adjusted so that
+ the user will effectively zoom into or out of the point under the mouse
+ cursor.
+ \value rotation
+ \l{QQuickItem::rotation}{rotation} will be set to \l rotation. If
+ \l targetTransformAroundCursor is \c true, the l{QQuickItem::x}{x} and
+ \l{QQuickItem::y}{y} properties will be simultaneously adjusted so
+ that the user will effectively rotate the item around the point under
+ the mouse cursor.
+
+ The adjustment of the given target property is always scaled by \l rotationScale.
+*/
+QString QQuickWheelHandler::property() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->propertyName;
+}
+
+void QQuickWheelHandler::setProperty(const QString &propertyName)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->propertyName == propertyName)
+ return;
+
+ d->propertyName = propertyName;
+ d->metaPropertyDirty = true;
+ emit propertyChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::targetScaleMultiplier
+
+ The amount by which the \l target \l{QQuickItem::scale}{scale} is to be
+ multiplied whenever the \l rotation changes by 15 degrees. This
+ is relevant only when \l property is \c "scale".
+
+ The \c scale will be multiplied by
+ \c targetScaleMultiplier \sup {angleDelta * rotationScale / 15}.
+ The default is \c 2 \sup {1/3}, which means that if \l rotationScale is left
+ at its default value, and the mouse wheel is rotated by one "click"
+ (15 degrees), the \l target will be scaled by approximately 1.25; after
+ three "clicks" its size will be doubled or halved, depending on the
+ direction that the wheel is rotated. If you want to make it double or halve
+ with every 2 clicks of the wheel, set this to \c 2 \sup {1/2} (1.4142).
+ If you want to make it scale the opposite way as the wheel is rotated,
+ set \c rotationScale to a negative value.
+*/
+qreal QQuickWheelHandler::targetScaleMultiplier() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->targetScaleMultiplier;
+}
+
+void QQuickWheelHandler::setTargetScaleMultiplier(qreal targetScaleMultiplier)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->targetScaleMultiplier, targetScaleMultiplier))
+ return;
+
+ d->targetScaleMultiplier = targetScaleMultiplier;
+ emit targetScaleMultiplierChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick::WheelHandler::targetTransformAroundCursor
+
+ Whether the \l target should automatically be repositioned in such a way
+ that it is transformed around the mouse cursor position while the
+ \l property is adjusted. The default is \c true.
+
+ If \l property is set to \c "rotation" and \l targetTransformAroundCursor
+ is \c true, then as the wheel is rotated, the \l target item will rotate in
+ place around the mouse cursor position. If \c targetTransformAroundCursor
+ is \c false, it will rotate around its
+ \l{QQuickItem::transformOrigin}{transformOrigin} instead.
+*/
+bool QQuickWheelHandler::isTargetTransformAroundCursor() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->targetTransformAroundCursor;
+}
+
+void QQuickWheelHandler::setTargetTransformAroundCursor(bool ttac)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->targetTransformAroundCursor == ttac)
+ return;
+
+ d->targetTransformAroundCursor = ttac;
+ emit targetTransformAroundCursorChanged();
+}
+
+bool QQuickWheelHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!event)
+ return false;
+ QQuickPointerScrollEvent *scroll = event->asPointerScrollEvent();
+ if (!scroll)
+ return false;
+ if (!acceptedDevices().testFlag(QQuickPointerDevice::DeviceType::TouchPad)
+ && scroll->synthSource() != Qt::MouseEventNotSynthesized)
+ return false;
+ if (!active()) {
+ switch (orientation()) {
+ case Qt::Horizontal:
+ if (qFuzzyIsNull(scroll->angleDelta().x()) && qFuzzyIsNull(scroll->pixelDelta().x()))
+ return false;
+ break;
+ case Qt::Vertical:
+ if (qFuzzyIsNull(scroll->angleDelta().y()) && qFuzzyIsNull(scroll->pixelDelta().y()))
+ return false;
+ break;
+ }
+ }
+ QQuickEventPoint *point = event->point(0);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) {
+ setPointId(point->pointId());
+ return true;
+ }
+ return false;
+}
+
+void QQuickWheelHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ Q_D(QQuickWheelHandler);
+ QQuickPointerScrollEvent *event = point->pointerEvent()->asPointerScrollEvent();
+ setActive(true); // ScrollEnd will not happen unless it was already active (see setActive(false) below)
+ point->setAccepted();
+ qreal inversion = !d->invertible && event->isInverted() ? -1 : 1;
+ qreal angleDelta = inversion * qreal(orientation() == Qt::Horizontal ? event->angleDelta().x() :
+ event->angleDelta().y()) / 8;
+ d->rotation += angleDelta;
+ emit rotationChanged();
+ emit wheel(event);
+ if (!d->propertyName.isEmpty() && target()) {
+ QQuickItem *t = target();
+ // writing target()'s property is done via QMetaProperty::write() so that any registered interceptors can react.
+ if (d->propertyName == QLatin1String("scale")) {
+ qreal multiplier = qPow(d->targetScaleMultiplier, angleDelta * d->rotationScale / 15); // wheel "clicks"
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(point->scenePosition());
+ const QPointF positionWas = t->position();
+ const qreal scaleWas = t->scale();
+ const qreal activePropertyValue = scaleWas * multiplier;
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in parent" << centroidParentPos
+ << "in scene" << point->scenePosition()
+ << "multiplier" << multiplier << "scale" << scaleWas
+ << "->" << activePropertyValue;
+ d->targetMetaProperty().write(t, activePropertyValue);
+ if (d->targetTransformAroundCursor) {
+ // If an interceptor intervened, scale may now be different than we asked for. Adjust accordingly.
+ multiplier = t->scale() / scaleWas;
+ const QPointF adjPos = QQuickItemPrivate::get(t)->adjustedPosForTransform(
+ centroidParentPos, positionWas, QVector2D(), scaleWas, multiplier, t->rotation(), 0);
+ qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(adjPos);
+ t->setPosition(adjPos);
+ }
+ } else if (d->propertyName == QLatin1String("rotation")) {
+ const QPointF positionWas = t->position();
+ const qreal rotationWas = t->rotation();
+ const qreal activePropertyValue = rotationWas + angleDelta * d->rotationScale;
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(point->scenePosition());
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in parent" << centroidParentPos
+ << "in scene" << point->scenePosition() << "rotation" << t->rotation()
+ << "->" << activePropertyValue;
+ d->targetMetaProperty().write(t, activePropertyValue);
+ if (d->targetTransformAroundCursor) {
+ // If an interceptor intervened, rotation may now be different than we asked for. Adjust accordingly.
+ const QPointF adjPos = QQuickItemPrivate::get(t)->adjustedPosForTransform(
+ centroidParentPos, positionWas, QVector2D(),
+ t->scale(), 1, rotationWas, t->rotation() - rotationWas);
+ qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(adjPos);
+ t->setPosition(adjPos);
+ }
+ } else {
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "scaled" << angleDelta << "total" << d->rotation << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in scene" << point->scenePosition() << "rotation" << t->rotation();
+ qreal delta = 0;
+ if (event->hasPixelDelta()) {
+ delta = inversion * d->rotationScale * qreal(orientation() == Qt::Horizontal ? event->pixelDelta().x() : event->pixelDelta().y());
+ qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by pixel delta" << delta << "from" << event;
+ } else {
+ delta = angleDelta * d->rotationScale;
+ qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by scaled angle delta" << delta << "from" << event;
+ }
+ bool ok = false;
+ qreal value = d->targetMetaProperty().read(t).toReal(&ok);
+ if (ok)
+ d->targetMetaProperty().write(t, value + qreal(delta));
+ else
+ qWarning() << "failed to read property" << d->propertyName << "of" << t;
+ }
+ }
+ switch (event->phase()) {
+ case Qt::ScrollEnd:
+ qCDebug(lcWheelHandler) << objectName() << "deactivating due to ScrollEnd phase";
+ setActive(false);
+ break;
+ case Qt::NoScrollPhase:
+ d->deactivationTimer.start(qRound(d->activeTimeout * 1000), this);
+ break;
+ case Qt::ScrollBegin:
+ case Qt::ScrollUpdate:
+ case Qt::ScrollMomentum:
+ break;
+ }
+}
+
+void QQuickWheelHandler::onTargetChanged(QQuickItem *oldTarget)
+{
+ Q_UNUSED(oldTarget)
+ Q_D(QQuickWheelHandler);
+ d->metaPropertyDirty = true;
+}
+
+void QQuickWheelHandler::onActiveChanged()
+{
+ Q_D(QQuickWheelHandler);
+ if (!active())
+ d->deactivationTimer.stop();
+}
+
+void QQuickWheelHandler::timerEvent(QTimerEvent *event)
+{
+ Q_D(const QQuickWheelHandler);
+ if (event->timerId() == d->deactivationTimer.timerId()) {
+ qCDebug(lcWheelHandler) << objectName() << "deactivating due to timeout";
+ setActive(false);
+ }
+}
+
+QQuickWheelHandlerPrivate::QQuickWheelHandlerPrivate()
+ : QQuickSinglePointHandlerPrivate()
+{
+}
+
+QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
+{
+ Q_Q(const QQuickWheelHandler);
+ if (metaPropertyDirty && q->target()) {
+ if (!propertyName.isEmpty()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ metaProperty = targetMeta->property(
+ targetMeta->indexOfProperty(propertyName.toLocal8Bit().constData()));
+ }
+ metaPropertyDirty = false;
+ }
+ return metaProperty;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickwheelhandler_p.h b/src/quick/handlers/qquickwheelhandler_p.h
new file mode 100644
index 0000000000..f8d1c00726
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWHEELHANDLER_H
+#define QQUICKWHEELHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+#include "qevent.h"
+#include "qquicksinglepointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWheelEvent;
+class QQuickWheelHandlerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
+ Q_PROPERTY(bool invertible READ isInvertible WRITE setInvertible NOTIFY invertibleChanged)
+ Q_PROPERTY(qreal activeTimeout READ activeTimeout WRITE setActiveTimeout NOTIFY activeTimeoutChanged)
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(qreal rotationScale READ rotationScale WRITE setRotationScale NOTIFY rotationScaleChanged)
+ Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
+ Q_PROPERTY(qreal targetScaleMultiplier READ targetScaleMultiplier WRITE setTargetScaleMultiplier NOTIFY targetScaleMultiplierChanged)
+ Q_PROPERTY(bool targetTransformAroundCursor READ isTargetTransformAroundCursor WRITE setTargetTransformAroundCursor NOTIFY targetTransformAroundCursorChanged)
+
+public:
+ explicit QQuickWheelHandler(QQuickItem *parent = nullptr);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+ bool isInvertible() const;
+ void setInvertible(bool invertible);
+
+ qreal activeTimeout() const;
+ void setActiveTimeout(qreal timeout);
+
+ qreal rotation() const;
+ void setRotation(qreal rotation);
+
+ qreal rotationScale() const;
+ void setRotationScale(qreal rotationScale);
+
+ QString property() const;
+ void setProperty(const QString &name);
+
+ qreal targetScaleMultiplier() const;
+ void setTargetScaleMultiplier(qreal targetScaleMultiplier);
+
+ bool isTargetTransformAroundCursor() const;
+ void setTargetTransformAroundCursor(bool ttac);
+
+Q_SIGNALS:
+ void wheel(QQuickPointerScrollEvent *event);
+
+ void orientationChanged();
+ void invertibleChanged();
+ void activeTimeoutChanged();
+ void rotationChanged();
+ void rotationScaleChanged();
+ void propertyChanged();
+ void targetScaleMultiplierChanged();
+ void targetTransformAroundCursorChanged();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+ void onTargetChanged(QQuickItem *oldTarget) override;
+ void onActiveChanged() override;
+ void timerEvent(QTimerEvent *event) override;
+
+ Q_DECLARE_PRIVATE(QQuickWheelHandler)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickWheelHandler)
+
+#endif // QQUICKWHEELHANDLER_H
diff --git a/src/quick/handlers/qquickwheelhandler_p_p.h b/src/quick/handlers/qquickwheelhandler_p_p.h
new file mode 100644
index 0000000000..d35e04c51b
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler_p_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWHEELHANDLER_P_P_H
+#define QQUICKWHEELHANDLER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquicksinglepointhandler_p_p.h"
+#include "qquickwheelhandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandlerPrivate : public QQuickSinglePointHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickWheelHandler)
+
+public:
+ static QQuickWheelHandlerPrivate* get(QQuickWheelHandler *q) { return q->d_func(); }
+ static const QQuickWheelHandlerPrivate* get(const QQuickWheelHandler *q) { return q->d_func(); }
+
+ QQuickWheelHandlerPrivate();
+
+ QMetaProperty &targetMetaProperty() const;
+
+ QBasicTimer deactivationTimer;
+ qreal activeTimeout = 0.1;
+ qreal rotationScale = 1;
+ qreal rotation = 0; // in units of degrees
+ qreal targetScaleMultiplier = 1.25992104989487; // qPow(2, 1/3)
+ QString propertyName;
+ mutable QMetaProperty metaProperty;
+ Qt::Orientation orientation = Qt::Vertical;
+ mutable bool metaPropertyDirty = true;
+ bool invertible = true;
+ bool targetTransformAroundCursor = true;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWHEELHANDLER_P_P_H