aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers/qquickdraghandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/handlers/qquickdraghandler.cpp')
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp258
1 files changed, 169 insertions, 89 deletions
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index e5531369cf..dad07189db 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickdraghandler_p.h"
#include <private/qquickwindow_p.h>
+#include <private/qquickmultipointhandler_p_p.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -87,7 +52,7 @@ Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
At this time, drag-and-drop is not yet supported.
- \sa Drag, MouseArea
+ \sa Drag, MouseArea, {Qt Quick Examples - Pointer Handlers}
*/
QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
@@ -95,27 +60,29 @@ QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
{
}
-bool QQuickDragHandler::targetContainsCentroid()
-{
- Q_ASSERT(parentItem() && target());
- return target()->contains(targetCentroidPosition());
-}
-
QPointF QQuickDragHandler::targetCentroidPosition()
{
- QPointF pos = m_centroid.position();
- if (target() != parentItem())
- pos = parentItem()->mapToItem(target(), pos);
+ QPointF pos = centroid().position();
+ if (auto par = parentItem()) {
+ if (target() != par)
+ pos = par->mapToItem(target(), pos);
+ }
return pos;
}
-void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
+void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point)
{
- QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
- if (grabber == this && transition == QQuickEventPoint::GrabExclusive && target()) {
+ QQuickMultiPointHandler::onGrabChanged(grabber, transition, event, point);
+ if (grabber == this && transition == QPointingDevice::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 parent && (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,12 +90,42 @@ 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 \l eventPoint.
+
+ Possible values:
+ \value DragHandler.SnapNever Never snap
+ \value DragHandler.SnapAuto The \l target snaps if the \l eventPoint was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l {PointerHandler::}{parent} item (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the \l eventPoint 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();
- if (active()) {
+ const bool curActive = active();
+ m_xAxis.onActiveChanged(curActive, 0);
+ m_yAxis.onActiveChanged(curActive, 0);
+ if (curActive) {
if (auto parent = parentItem()) {
- if (currentEvent()->asPointerTouchEvent())
+ if (QQuickDeliveryAgentPrivate::isTouchEvent(currentEvent()))
parent->setKeepTouchGrab(true);
// tablet and mouse are treated the same by Item's legacy event handling, and
// touch becomes synth-mouse for Flickable, so we need to prevent stealing
@@ -145,38 +142,63 @@ void QQuickDragHandler::onActiveChanged()
}
}
-void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+bool QQuickDragHandler::wantsPointerEvent(QPointerEvent *event)
+{
+ if (!QQuickMultiPointHandler::wantsPointerEvent(event))
+ /* Do handle other events than we would normally care about
+ while we are still doing a drag; otherwise we would suddenly
+ become inactive when a wheel event arrives during dragging.
+ This extra condition needs to be kept in sync with
+ handlePointerEventImpl */
+ if (!active())
+ return false;
+
+#if QT_CONFIG(gestures)
+ if (event->type() == QEvent::NativeGesture)
+ return false;
+#endif
+
+ return true;
+}
+
+void QQuickDragHandler::handlePointerEventImpl(QPointerEvent *event)
{
+ if (active() && !QQuickMultiPointHandler::wantsPointerEvent(event))
+ return; // see QQuickDragHandler::wantsPointerEvent; we don't want to handle those events
+
QQuickMultiPointHandler::handlePointerEventImpl(event);
event->setAccepted(true);
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())
accumulatedDragDelta.setY(0);
- setTranslation(accumulatedDragDelta);
+ setActiveTranslation(accumulatedDragDelta);
} else {
// Check that all points have been dragged past the drag threshold,
// to the extent that the constraints allow,
// and in approximately the same direction
qreal minAngle = 361;
qreal maxAngle = -361;
- bool allOverThreshold = !event->isReleaseEvent();
- QVector <QQuickEventPoint *> chosenPoints;
+ bool allOverThreshold = QQuickDeliveryAgentPrivate::isTouchEvent(event) ?
+ static_cast<QTouchEvent *>(event)->touchPointStates() != QEventPoint::Released :
+ !event->isEndEvent();
+ QVector<QEventPoint> chosenPoints;
- if (event->isPressEvent())
- m_pressedInsideTarget = target() && m_currentPoints.count() > 0;
+ if (event->isBeginEvent())
+ m_pressedInsideTarget = target() && currentPoints().size() > 0;
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ for (const QQuickHandlerPoint &p : currentPoints()) {
if (!allOverThreshold)
break;
- QQuickEventPoint *point = event->pointById(p.id());
- chosenPoints << point;
- setPassiveGrab(point);
+ auto point = event->pointById(p.id());
+ Q_ASSERT(point);
+ chosenPoints << *point;
+ setPassiveGrab(event, *point);
// 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(point->scenePosition() - point->scenePressPosition());
@@ -195,7 +217,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
accumulatedDragDelta.setY(0);
}
qreal angle = std::atan2(accumulatedDragDelta.y(), accumulatedDragDelta.x()) * 180 / M_PI;
- bool overThreshold = QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta);
+ bool overThreshold = d_func()->dragOverThreshold(accumulatedDragDelta);
qCDebug(lcDragHandler) << "movement" << accumulatedDragDelta << "angle" << angle << "of point" << point
<< "pressed @" << point->scenePressPosition() << "over threshold?" << overThreshold;
minAngle = qMin(angle, minAngle);
@@ -203,7 +225,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
if (allOverThreshold && !overThreshold)
allOverThreshold = false;
- if (event->isPressEvent()) {
+ if (event->isBeginEvent()) {
// m_pressedInsideTarget should stay true iff ALL points in which DragHandler is interested
// have been pressed inside the target() Item. (E.g. in a Slider the parent might be the
// whole control while the target is just the knob.)
@@ -212,11 +234,11 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
m_pressedInsideTarget &= target()->contains(localPressPos);
m_pressTargetPos = targetCentroidPosition();
}
- // QQuickWindowPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
+ // QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
// (That affects behavior for mouse but not for touch, because Flickable only handles mouse.)
// So we have to compensate by accepting the event here to avoid any parent Flickable from
// getting the event via direct delivery and grabbing too soon.
- point->setAccepted(event->asPointerMouseEvent()); // stop propagation iff it's a mouse event
+ point->setAccepted(QQuickDeliveryAgentPrivate::isMouseEvent(event)); // stop propagation iff it's a mouse event
}
}
if (allOverThreshold) {
@@ -224,7 +246,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
if (angleDiff > 180)
angleDiff = 360 - angleDiff;
qCDebug(lcDragHandler) << "angle min" << minAngle << "max" << maxAngle << "range" << angleDiff;
- if (angleDiff < DragAngleToleranceDegrees && grabPoints(chosenPoints))
+ if (angleDiff < DragAngleToleranceDegrees && grabPoints(event, chosenPoints))
setActive(true);
}
}
@@ -244,17 +266,6 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
}
}
-void QQuickDragHandler::enforceConstraints()
-{
- if (!target() || !target()->parentItem())
- return;
- QPointF pos = target()->position();
- QPointF copy(pos);
- enforceAxisConstraints(&pos);
- if (pos != copy)
- target()->setPosition(pos);
-}
-
void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos)
{
if (m_xAxis.enabled())
@@ -263,12 +274,28 @@ void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos)
localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum()));
}
-void QQuickDragHandler::setTranslation(const QVector2D &trans)
+void QQuickDragHandler::setPersistentTranslation(const QVector2D &trans)
+{
+ if (trans == persistentTranslation())
+ return;
+
+ m_xAxis.updateValue(m_xAxis.activeValue(), trans.x());
+ m_yAxis.updateValue(m_yAxis.activeValue(), trans.y());
+ emit translationChanged({});
+}
+
+void QQuickDragHandler::setActiveTranslation(const QVector2D &trans)
{
- if (trans == m_translation) // fuzzy compare?
+ if (trans == activeTranslation())
return;
- m_translation = trans;
- emit translationChanged();
+
+ const QVector2D delta = trans - activeTranslation();
+ m_xAxis.updateValue(trans.x(), m_xAxis.persistentValue() + delta.x(), delta.x());
+ m_yAxis.updateValue(trans.y(), m_yAxis.persistentValue() + delta.y(), delta.y());
+
+ qCDebug(lcDragHandler) << "translation: delta" << delta
+ << "active" << trans << "accumulated" << persistentTranslation();
+ emit translationChanged(delta);
}
/*!
@@ -276,6 +303,7 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
\qmlproperty real QtQuick::DragHandler::xAxis.minimum
\qmlproperty real QtQuick::DragHandler::xAxis.maximum
\qmlproperty bool QtQuick::DragHandler::xAxis.enabled
+ \qmlproperty real QtQuick::DragHandler::xAxis.activeValue
\c xAxis controls the constraints for horizontal dragging.
@@ -284,13 +312,19 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
\c maximum is the maximum acceptable value of \l {Item::x}{x} to be
applied to the \l {PointerHandler::target} {target}.
If \c enabled is true, horizontal dragging is allowed.
- */
+ \c activeValue is the same as \l {QtQuick::DragHandler::activeTranslation}{activeTranslation.x}.
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the increment by which it changed.
+ This is intended for incrementally adjusting one property via multiple handlers.
+*/
/*!
\qmlpropertygroup QtQuick::DragHandler::yAxis
\qmlproperty real QtQuick::DragHandler::yAxis.minimum
\qmlproperty real QtQuick::DragHandler::yAxis.maximum
\qmlproperty bool QtQuick::DragHandler::yAxis.enabled
+ \qmlproperty real QtQuick::DragHandler::yAxis.activeValue
\c yAxis controls the constraints for vertical dragging.
@@ -299,13 +333,59 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
\c maximum is the maximum acceptable value of \l {Item::y}{y} to be
applied to the \l {PointerHandler::target} {target}.
If \c enabled is true, vertical dragging is allowed.
- */
+ \c activeValue is the same as \l {QtQuick::DragHandler::activeTranslation}{activeTranslation.y}.
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the increment by which it changed.
+ This is intended for incrementally adjusting one property via multiple handlers:
+
+ \snippet pointerHandlers/rotateViaWheelOrDrag.qml 0
+*/
/*!
\readonly
\qmlproperty QVector2D QtQuick::DragHandler::translation
+ \deprecated [6.2] Use activeTranslation
+*/
+
+/*!
+ \qmlproperty QVector2D QtQuick::DragHandler::persistentTranslation
- The translation since the gesture began.
+ The translation to be applied to the \l target if it is not \c null.
+ Otherwise, bindings can be used to do arbitrary things with this value.
+ While the drag gesture is being performed, \l activeTranslation is
+ continuously added to it; after the gesture ends, it stays the same.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::DragHandler::activeTranslation
+
+ The translation while the drag gesture is being performed.
+ It is \c {0, 0} when the gesture begins, and increases as the event
+ point(s) are dragged downward and to the right. After the gesture ends, it
+ stays the same; and when the next drag gesture begins, it is reset to
+ \c {0, 0} again.
+*/
+
+/*!
+ \qmlproperty flags QtQuick::DragHandler::acceptedButtons
+
+ The mouse buttons that can activate this DragHandler.
+
+ By default, this property is set to
+ \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ from other buttons.
+
+ For example, if a component (such as TextEdit) already handles
+ left-button drags in its own way, it can be augmented with a
+ DragHandler that does something different when dragged via the
+ right button:
+
+ \snippet pointerHandlers/dragHandlerAcceptedButtons.qml 0
*/
QT_END_NAMESPACE
+
+#include "moc_qquickdraghandler_p.cpp"