aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2018-12-27 15:08:04 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2019-04-27 05:41:21 +0000
commit5d45aa1a6400b9fdb9bafa0559675996aff5e58a (patch)
treec5e28c73152b6c5e8b931814668910671968bb38 /src/quick
parentf1b4348dae2b992aeaa75e4257c6db4a5182415c (diff)
Add WheelHandler
It can be used to change any qreal property of its target Item in response to wheel rotation, or it can be used in other ways that involve bindings but without a target item. [ChangeLog][QtQuick][Event Handlers] Added WheelHandler, which handles mouse wheel rotation by modifying arbitrary Item properties. Fixes: QTBUG-68119 Change-Id: I247e2325ee993cc1b91a47fbd6c4ba0ffde7ad49 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/doc/snippets/pointerHandlers/handlerFlick.qml88
-rw-r--r--src/quick/doc/snippets/pointerHandlers/wheelHandler.qml63
-rw-r--r--src/quick/handlers/handlers.pri6
-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
-rw-r--r--src/quick/items/qquickevents_p_p.h1
-rw-r--r--src/quick/items/qquickitemsmodule.cpp6
8 files changed, 898 insertions, 1 deletions
diff --git a/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
new file mode 100644
index 0000000000..f74b075357
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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 The Qt Company Ltd 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+
+Item {
+ width: 320; height: 480
+ Flow {
+ id: content
+ width: parent.width
+ spacing: 2; padding: 2
+
+ WheelHandler {
+ orientation: Qt.Vertical
+ property: "y"
+ rotationScale: 15
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged: if (!active) ybr.returnToBounds()
+ }
+
+ DragHandler {
+ xAxis.enabled: false
+ onActiveChanged: if (!active) ybr.returnToBounds()
+ }
+
+ BoundaryRule on y {
+ id: ybr
+ minimum: content.parent.height - content.height
+ maximum: 0
+ minimumOvershoot: 400; maximumOvershoot: 400
+ overshootFilter: BoundaryRule.Peak
+ }
+
+ Repeater {
+ model: 1000
+ Rectangle { color: "gray"; width: 10 + Math.random() * 100; height: 15 }
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
new file mode 100644
index 0000000000..2c9913ac97
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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 The Qt Company Ltd 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.14
+
+Rectangle {
+ width: 170; height: 120
+ color: "green"; antialiasing: true
+
+ WheelHandler {
+ property: "rotation"
+ onWheel: console.log("rotation", event.angleDelta.y,
+ "scaled", rotation, "@", point.position, "=>", parent.rotation)
+ }
+}
+//![0]
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index fa2f25c793..49975bd1ca 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -27,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/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
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index e614b1bd6d..1a3737091f 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -605,6 +605,7 @@ private:
bool m_inverted = false;
friend class QQuickWindowPrivate;
+ friend class QQuickWheelHandler;
Q_DISABLE_COPY(QQuickPointerScrollEvent)
};
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index a6dce33f45..2a1c442653 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 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.
@@ -120,6 +120,7 @@
#include "handlers/qquickpinchhandler_p.h"
#include "handlers/qquickpointhandler_p.h"
#include "handlers/qquicktaphandler_p.h"
+#include "handlers/qquickwheelhandler_p.h"
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
@@ -478,6 +479,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
#if QT_CONFIG(quick_tableview)
qmlRegisterType<QQuickTableView, 14>(uri, 2, 14, "TableView");
#endif
+#if QT_CONFIG(wheelevent)
+ qmlRegisterType<QQuickWheelHandler>(uri, 2, 14, "WheelHandler");
+#endif
}
static void initResources()