diff options
Diffstat (limited to 'src/quick')
-rw-r--r-- | src/quick/doc/snippets/pointerHandlers/handlerFlick.qml | 88 | ||||
-rw-r--r-- | src/quick/doc/snippets/pointerHandlers/wheelHandler.qml | 63 | ||||
-rw-r--r-- | src/quick/handlers/handlers.pri | 6 | ||||
-rw-r--r-- | src/quick/handlers/qquickwheelhandler.cpp | 520 | ||||
-rw-r--r-- | src/quick/handlers/qquickwheelhandler_p.h | 128 | ||||
-rw-r--r-- | src/quick/handlers/qquickwheelhandler_p_p.h | 87 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 6 |
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() |