2015-08-11 16:14:02 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2016 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 "qquickpinchhandler_p.h"
|
2019-01-18 13:39:00 +00:00
|
|
|
#include <QtQml/qqmlinfo.h>
|
2015-08-11 16:14:02 +00:00
|
|
|
#include <QtQuick/qquickwindow.h>
|
|
|
|
#include <private/qsgadaptationlayer_p.h>
|
|
|
|
#include <private/qquickitem_p.h>
|
|
|
|
#include <private/qguiapplication_p.h>
|
|
|
|
#include <private/qquickwindow_p.h>
|
|
|
|
#include <QEvent>
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <qpa/qplatformnativeinterface.h>
|
2018-05-22 12:59:08 +00:00
|
|
|
#include <math.h>
|
2015-08-11 16:14:02 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmltype PinchHandler
|
|
|
|
\instantiates QQuickPinchHandler
|
2017-09-22 12:09:45 +00:00
|
|
|
\inherits MultiPointHandler
|
2018-07-23 13:15:59 +00:00
|
|
|
\inqmlmodule QtQuick
|
|
|
|
\ingroup qtquick-input-handlers
|
2018-06-18 13:09:56 +00:00
|
|
|
\brief Handler for pinch gestures.
|
2015-08-11 16:14:02 +00:00
|
|
|
|
2017-08-16 08:35:43 +00:00
|
|
|
PinchHandler is a handler that interprets a multi-finger gesture to
|
2018-06-19 16:05:14 +00:00
|
|
|
interactively rotate, zoom, and drag an Item. Like other Input Handlers,
|
2017-08-16 08:35:43 +00:00
|
|
|
by default it is fully functional, and manipulates its \l target,
|
|
|
|
which is the Item within which it is declared.
|
|
|
|
|
|
|
|
\snippet pointerHandlers/pinchHandler.qml 0
|
|
|
|
|
|
|
|
It has properties to restrict the range of dragging, rotation, and zoom.
|
|
|
|
|
|
|
|
If it is declared within one Item but is assigned a different \l target, it
|
|
|
|
handles events within the bounds of the outer Item but manipulates the
|
|
|
|
\c target Item instead:
|
|
|
|
|
|
|
|
\snippet pointerHandlers/pinchHandlerDifferentTarget.qml 0
|
|
|
|
|
|
|
|
A third way to use it is to set \l target to \c null and react to property
|
|
|
|
changes in some other way:
|
|
|
|
|
|
|
|
\snippet pointerHandlers/pinchHandlerNullTarget.qml 0
|
|
|
|
|
|
|
|
\image touchpoints-pinchhandler.png
|
|
|
|
|
|
|
|
\sa PinchArea
|
2015-08-11 16:14:02 +00:00
|
|
|
*/
|
|
|
|
|
2018-07-26 14:33:13 +00:00
|
|
|
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
|
2017-08-30 13:18:04 +00:00
|
|
|
: QQuickMultiPointHandler(parent, 2)
|
2015-08-11 16:14:02 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-08-16 08:35:43 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::minimumScale
|
|
|
|
|
|
|
|
The minimum acceptable \l {Item::scale}{scale} to be applied
|
|
|
|
to the \l target.
|
|
|
|
*/
|
2015-08-11 16:14:02 +00:00
|
|
|
void QQuickPinchHandler::setMinimumScale(qreal minimumScale)
|
|
|
|
{
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_minimumScale, minimumScale))
|
2015-08-11 16:14:02 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
m_minimumScale = minimumScale;
|
|
|
|
emit minimumScaleChanged();
|
|
|
|
}
|
|
|
|
|
2017-08-16 08:35:43 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::maximumScale
|
|
|
|
|
|
|
|
The maximum acceptable \l {Item::scale}{scale} to be applied
|
|
|
|
to the \l target.
|
|
|
|
*/
|
2015-08-11 16:14:02 +00:00
|
|
|
void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
|
|
|
|
{
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_maximumScale, maximumScale))
|
2015-08-11 16:14:02 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
m_maximumScale = maximumScale;
|
|
|
|
emit maximumScaleChanged();
|
|
|
|
}
|
|
|
|
|
2017-08-16 08:35:43 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::minimumRotation
|
|
|
|
|
|
|
|
The minimum acceptable \l {Item::rotation}{rotation} to be applied
|
|
|
|
to the \l target.
|
|
|
|
*/
|
2015-08-11 16:14:02 +00:00
|
|
|
void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation)
|
|
|
|
{
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_minimumRotation, minimumRotation))
|
2015-08-11 16:14:02 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
m_minimumRotation = minimumRotation;
|
|
|
|
emit minimumRotationChanged();
|
|
|
|
}
|
|
|
|
|
2017-08-16 08:35:43 +00:00
|
|
|
/*!
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::maximumRotation
|
|
|
|
|
|
|
|
The maximum acceptable \l {Item::rotation}{rotation} to be applied
|
|
|
|
to the \l target.
|
|
|
|
*/
|
2015-08-11 16:14:02 +00:00
|
|
|
void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
|
|
|
|
{
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_maximumRotation, maximumRotation))
|
2015-08-11 16:14:02 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
m_maximumRotation = maximumRotation;
|
|
|
|
emit maximumRotationChanged();
|
|
|
|
}
|
|
|
|
|
2019-01-18 13:39:00 +00:00
|
|
|
#if QT_DEPRECATED_SINCE(5, 12)
|
|
|
|
void QQuickPinchHandler::warnAboutMinMaxDeprecated() const
|
|
|
|
{
|
|
|
|
qmlWarning(this) << "min and max constraints are now part of the xAxis and yAxis properties";
|
|
|
|
}
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
|
|
|
|
void QQuickPinchHandler::setMinimumX(qreal minX)
|
|
|
|
{
|
2019-01-18 13:39:00 +00:00
|
|
|
warnAboutMinMaxDeprecated();
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_minimumX, minX))
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
return;
|
|
|
|
m_minimumX = minX;
|
|
|
|
emit minimumXChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPinchHandler::setMaximumX(qreal maxX)
|
|
|
|
{
|
2019-01-18 13:39:00 +00:00
|
|
|
warnAboutMinMaxDeprecated();
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_maximumX, maxX))
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
return;
|
|
|
|
m_maximumX = maxX;
|
|
|
|
emit maximumXChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPinchHandler::setMinimumY(qreal minY)
|
|
|
|
{
|
2019-01-18 13:39:00 +00:00
|
|
|
warnAboutMinMaxDeprecated();
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_minimumY, minY))
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
return;
|
|
|
|
m_minimumY = minY;
|
|
|
|
emit minimumYChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPinchHandler::setMaximumY(qreal maxY)
|
|
|
|
{
|
2019-01-18 13:39:00 +00:00
|
|
|
warnAboutMinMaxDeprecated();
|
2019-01-25 09:42:45 +00:00
|
|
|
if (qFuzzyCompare(m_maximumY, maxY))
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
return;
|
|
|
|
m_maximumY = maxY;
|
|
|
|
emit maximumYChanged();
|
|
|
|
}
|
2019-01-18 13:39:00 +00:00
|
|
|
#endif
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
|
2017-08-10 10:05:41 +00:00
|
|
|
bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
|
|
|
|
{
|
|
|
|
if (!QQuickMultiPointHandler::wantsPointerEvent(event))
|
|
|
|
return false;
|
|
|
|
|
2018-05-09 05:00:07 +00:00
|
|
|
#if QT_CONFIG(gestures)
|
2017-11-13 13:43:54 +00:00
|
|
|
if (const auto gesture = event->asPointerNativeGestureEvent()) {
|
|
|
|
if (minimumPointCount() == 2) {
|
2017-08-10 10:05:41 +00:00
|
|
|
switch (gesture->type()) {
|
|
|
|
case Qt::BeginNativeGesture:
|
|
|
|
case Qt::EndNativeGesture:
|
|
|
|
case Qt::ZoomNativeGesture:
|
|
|
|
case Qt::RotateNativeGesture:
|
|
|
|
return parentContains(event->point(0));
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-13 13:43:54 +00:00
|
|
|
} else {
|
|
|
|
return false;
|
2017-08-10 10:05:41 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-09 05:00:07 +00:00
|
|
|
#endif
|
2017-08-10 10:05:41 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-18 13:39:00 +00:00
|
|
|
/*!
|
|
|
|
\qmlpropertygroup QtQuick::PinchHandler::xAxis
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::xAxis.minimum
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::xAxis.maximum
|
|
|
|
\qmlproperty bool QtQuick::PinchHandler::xAxis.enabled
|
|
|
|
|
|
|
|
\c xAxis controls the constraints for horizontal translation of the \l target item.
|
|
|
|
|
|
|
|
\c minimum is the minimum acceptable x coordinate of the translation.
|
|
|
|
\c maximum is the maximum acceptable x coordinate of the translation.
|
|
|
|
If \c enabled is true, horizontal dragging is allowed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlpropertygroup QtQuick::PinchHandler::yAxis
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::yAxis.minimum
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::yAxis.maximum
|
|
|
|
\qmlproperty bool QtQuick::PinchHandler::yAxis.enabled
|
|
|
|
|
|
|
|
\c yAxis controls the constraints for vertical translation of the \l target item.
|
|
|
|
|
|
|
|
\c minimum is the minimum acceptable y coordinate of the translation.
|
|
|
|
\c maximum is the maximum acceptable y coordinate of the translation.
|
|
|
|
If \c enabled is true, vertical dragging is allowed.
|
|
|
|
*/
|
|
|
|
|
2015-08-11 16:14:02 +00:00
|
|
|
/*!
|
2017-08-16 08:35:43 +00:00
|
|
|
\qmlproperty int QtQuick::PinchHandler::minimumTouchPoints
|
2015-08-11 16:14:02 +00:00
|
|
|
|
|
|
|
The pinch begins when this number of fingers are pressed.
|
|
|
|
Until then, PinchHandler tracks the positions of any pressed fingers,
|
|
|
|
but if it's an insufficient number, it does not scale or rotate
|
|
|
|
its \l target, and the \l active property will remain false.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2017-08-16 08:35:43 +00:00
|
|
|
\qmlproperty bool QtQuick::PinchHandler::active
|
|
|
|
|
|
|
|
This property is true when all the constraints (epecially \l minimumTouchPoints)
|
|
|
|
are satisfied and the \l target, if any, is being manipulated.
|
2015-08-11 16:14:02 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
void QQuickPinchHandler::onActiveChanged()
|
|
|
|
{
|
2018-05-09 18:32:16 +00:00
|
|
|
QQuickMultiPointHandler::onActiveChanged();
|
2015-08-11 16:14:02 +00:00
|
|
|
if (active()) {
|
2017-09-12 09:00:33 +00:00
|
|
|
m_startMatrix = QMatrix4x4();
|
2018-05-09 18:32:16 +00:00
|
|
|
m_startAngles = angles(m_centroid.sceneGrabPosition());
|
|
|
|
m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
|
2017-09-12 09:00:33 +00:00
|
|
|
m_activeRotation = 0;
|
|
|
|
m_activeTranslation = QVector2D();
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
if (const QQuickItem *t = target()) {
|
|
|
|
m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
|
|
|
|
m_startRotation = t->rotation();
|
|
|
|
QVector3D xformOrigin(t->transformOriginPoint());
|
2019-01-25 09:42:45 +00:00
|
|
|
m_startMatrix.translate(float(t->x()), float(t->y()));
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
m_startMatrix.translate(xformOrigin);
|
2019-01-25 09:42:45 +00:00
|
|
|
m_startMatrix.scale(float(m_startScale));
|
|
|
|
m_startMatrix.rotate(float(m_startRotation), 0, 0, -1);
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
m_startMatrix.translate(-xformOrigin);
|
2017-09-12 09:00:33 +00:00
|
|
|
} else {
|
2018-06-22 14:37:32 +00:00
|
|
|
m_startScale = m_accumulatedScale;
|
2017-09-12 09:00:33 +00:00
|
|
|
m_startRotation = 0;
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
}
|
2017-09-12 09:00:33 +00:00
|
|
|
qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
|
2016-10-18 09:49:13 +00:00
|
|
|
} else {
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation;
|
2015-08-11 16:14:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
|
|
|
|
{
|
|
|
|
if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
|
QQuickMultiPointHandler: store QQuickHandlerPoints, not QQEventPoint*s
QQuickPointerTouchEvent::reset(QEvent *event) reuses instances of
QQuickEventPoint from one touch event to the next, but it makes no
attempt to match up each instance with the same pointId from the event.
So from the perspective of Handlers, each event can have its
touchpoints in a different order, and therefore it's always wrong to
hold onto any QQuickEventPoint pointer between events. Instead we
use QQuickHandlerPoint for storage: both for exposing to QML, and for
internal storage in handlers that need to remember touchpoint state.
Without this change, any MultiPointHandler could "forget" which
point IDs it had chosen to react to, in any case where the event
contains additional points. It was using a QVector<QQuickEventPoint *>
to remember the chosen points, but each of those instances might be
assigned a different touchpoint during the handling of the next
touch event (and thus its ID would change "underneath").
Perhaps this went unnoticed until now because 1) the only subclass
of MultiPointHandler was PinchHandler, and we didn't often test
use cases with extra touchpoints beyond the ones involved in the pinch
and 2) on Linux/X11 they stayed in the same order in each event.
But as soon as we try to make DragHandler inherit MultiPointHandler,
it becomes clear that it does not succeed in holding on to a particular
touchpoint while dragging multiple DragHandlers with multiple fingers,
without this patch.
Change-Id: If7e0daa9ed77b263efc09f5ea73dfba6a3c8205c
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2018-06-29 13:16:52 +00:00
|
|
|
for (const QQuickHandlerPoint &p : m_currentPoints)
|
|
|
|
qCDebug(lcPinchHandler) << hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
|
2015-08-11 16:14:02 +00:00
|
|
|
}
|
2018-05-09 18:32:16 +00:00
|
|
|
QQuickMultiPointHandler::handlePointerEventImpl(event);
|
2015-08-11 16:14:02 +00:00
|
|
|
|
2017-08-10 10:05:41 +00:00
|
|
|
qreal dist = 0;
|
2018-05-09 05:00:07 +00:00
|
|
|
#if QT_CONFIG(gestures)
|
2017-08-10 10:05:41 +00:00
|
|
|
if (const auto gesture = event->asPointerNativeGestureEvent()) {
|
2018-09-12 12:05:06 +00:00
|
|
|
m_centroid.reset(event->point(0));
|
2017-08-10 10:05:41 +00:00
|
|
|
switch (gesture->type()) {
|
|
|
|
case Qt::EndNativeGesture:
|
|
|
|
m_activeScale = 1;
|
|
|
|
m_activeRotation = 0;
|
|
|
|
m_activeTranslation = QVector2D();
|
2018-05-09 18:32:16 +00:00
|
|
|
m_centroid.reset();
|
2017-08-10 10:05:41 +00:00
|
|
|
setActive(false);
|
|
|
|
emit updated();
|
|
|
|
return;
|
|
|
|
case Qt::ZoomNativeGesture:
|
|
|
|
m_activeScale *= 1 + gesture->value();
|
|
|
|
break;
|
|
|
|
case Qt::RotateNativeGesture:
|
|
|
|
m_activeRotation += gesture->value();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Nothing of interest (which is unexpected, because wantsPointerEvent() should have returned false)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!active()) {
|
|
|
|
setActive(true);
|
|
|
|
// Native gestures for 2-finger pinch do not allow dragging, so
|
|
|
|
// the centroid won't move during the gesture, and translation stays at zero
|
|
|
|
m_activeTranslation = QVector2D();
|
|
|
|
}
|
2018-05-09 05:00:07 +00:00
|
|
|
} else
|
|
|
|
#endif // QT_CONFIG(gestures)
|
|
|
|
{
|
2018-05-22 12:59:08 +00:00
|
|
|
const bool containsReleasedPoints = event->isReleaseEvent();
|
QQuickMultiPointHandler: store QQuickHandlerPoints, not QQEventPoint*s
QQuickPointerTouchEvent::reset(QEvent *event) reuses instances of
QQuickEventPoint from one touch event to the next, but it makes no
attempt to match up each instance with the same pointId from the event.
So from the perspective of Handlers, each event can have its
touchpoints in a different order, and therefore it's always wrong to
hold onto any QQuickEventPoint pointer between events. Instead we
use QQuickHandlerPoint for storage: both for exposing to QML, and for
internal storage in handlers that need to remember touchpoint state.
Without this change, any MultiPointHandler could "forget" which
point IDs it had chosen to react to, in any case where the event
contains additional points. It was using a QVector<QQuickEventPoint *>
to remember the chosen points, but each of those instances might be
assigned a different touchpoint during the handling of the next
touch event (and thus its ID would change "underneath").
Perhaps this went unnoticed until now because 1) the only subclass
of MultiPointHandler was PinchHandler, and we didn't often test
use cases with extra touchpoints beyond the ones involved in the pinch
and 2) on Linux/X11 they stayed in the same order in each event.
But as soon as we try to make DragHandler inherit MultiPointHandler,
it becomes clear that it does not succeed in holding on to a particular
touchpoint while dragging multiple DragHandlers with multiple fingers,
without this patch.
Change-Id: If7e0daa9ed77b263efc09f5ea73dfba6a3c8205c
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2018-06-29 13:16:52 +00:00
|
|
|
QVector<QQuickEventPoint *> chosenPoints;
|
|
|
|
for (const QQuickHandlerPoint &p : m_currentPoints) {
|
|
|
|
QQuickEventPoint *ep = event->pointById(p.id());
|
|
|
|
chosenPoints << ep;
|
|
|
|
}
|
2017-05-18 09:23:25 +00:00
|
|
|
if (!active()) {
|
2017-08-10 10:05:41 +00:00
|
|
|
// Verify that at least one of the points has moved beyond threshold needed to activate the handler
|
2018-05-22 12:59:08 +00:00
|
|
|
int numberOfPointsDraggedOverThreshold = 0;
|
|
|
|
QVector2D accumulatedDrag;
|
|
|
|
const QVector2D currentCentroid(m_centroid.scenePosition());
|
|
|
|
const QVector2D pressCentroid(m_centroid.scenePressPosition());
|
|
|
|
|
|
|
|
QStyleHints *styleHints = QGuiApplication::styleHints();
|
|
|
|
const int dragThreshold = styleHints->startDragDistance();
|
|
|
|
const int dragThresholdSquared = dragThreshold * dragThreshold;
|
|
|
|
|
|
|
|
double accumulatedCentroidDistance = 0; // Used to detect scale
|
|
|
|
if (event->isPressEvent())
|
|
|
|
m_accumulatedStartCentroidDistance = 0; // Used to detect scale
|
|
|
|
|
|
|
|
float accumulatedMovementMagnitude = 0;
|
|
|
|
|
QQuickMultiPointHandler: store QQuickHandlerPoints, not QQEventPoint*s
QQuickPointerTouchEvent::reset(QEvent *event) reuses instances of
QQuickEventPoint from one touch event to the next, but it makes no
attempt to match up each instance with the same pointId from the event.
So from the perspective of Handlers, each event can have its
touchpoints in a different order, and therefore it's always wrong to
hold onto any QQuickEventPoint pointer between events. Instead we
use QQuickHandlerPoint for storage: both for exposing to QML, and for
internal storage in handlers that need to remember touchpoint state.
Without this change, any MultiPointHandler could "forget" which
point IDs it had chosen to react to, in any case where the event
contains additional points. It was using a QVector<QQuickEventPoint *>
to remember the chosen points, but each of those instances might be
assigned a different touchpoint during the handling of the next
touch event (and thus its ID would change "underneath").
Perhaps this went unnoticed until now because 1) the only subclass
of MultiPointHandler was PinchHandler, and we didn't often test
use cases with extra touchpoints beyond the ones involved in the pinch
and 2) on Linux/X11 they stayed in the same order in each event.
But as soon as we try to make DragHandler inherit MultiPointHandler,
it becomes clear that it does not succeed in holding on to a particular
touchpoint while dragging multiple DragHandlers with multiple fingers,
without this patch.
Change-Id: If7e0daa9ed77b263efc09f5ea73dfba6a3c8205c
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2018-06-29 13:16:52 +00:00
|
|
|
for (QQuickEventPoint *point : qAsConst(chosenPoints)) {
|
2018-05-22 12:59:08 +00:00
|
|
|
if (!containsReleasedPoints) {
|
|
|
|
accumulatedDrag += QVector2D(point->scenePressPosition() - point->scenePosition());
|
|
|
|
/*
|
|
|
|
In order to detect a drag, we want to check if all points have moved more or
|
|
|
|
less in the same direction.
|
|
|
|
|
|
|
|
We then take each point, and convert the point to a local coordinate system where
|
|
|
|
the centroid is the origin. This is done both for the press positions and the
|
|
|
|
current positions. We will then have two positions:
|
|
|
|
|
|
|
|
- pressCentroidRelativePosition
|
|
|
|
is the start point relative to the press centroid
|
|
|
|
- currentCentroidRelativePosition
|
|
|
|
is the current point relative to the current centroid
|
|
|
|
|
|
|
|
If those two points are far enough apart, it might not be considered as a drag
|
|
|
|
anymore. (Note that the threshold will matched to the average of the relative
|
|
|
|
movement of all the points). Therefore, a big relative movement will make a big
|
|
|
|
contribution to the average relative movement.
|
|
|
|
|
|
|
|
The algorithm then can be described as:
|
|
|
|
For each point:
|
|
|
|
- Calculate vector pressCentroidRelativePosition (from the press centroid to the press position)
|
|
|
|
- Calculate vector currentCentroidRelativePosition (from the current centroid to the current position)
|
|
|
|
- Calculate the relative movement vector:
|
|
|
|
|
|
|
|
centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition
|
|
|
|
|
|
|
|
and measure its magnitude. Add the magnitude to the accumulatedMovementMagnitude.
|
|
|
|
|
|
|
|
Finally, if the accumulatedMovementMagnitude is below some threshold, it means
|
|
|
|
that the points were stationary or they were moved in parallel (e.g. the hand
|
|
|
|
was moved, but the relative position between each finger remained very much
|
|
|
|
the same). This is then used to rule out if there is a rotation or scale.
|
|
|
|
*/
|
|
|
|
QVector2D pressCentroidRelativePosition = QVector2D(point->scenePosition()) - currentCentroid;
|
|
|
|
QVector2D currentCentroidRelativePosition = QVector2D(point->scenePressPosition()) - pressCentroid;
|
|
|
|
QVector2D centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition;
|
|
|
|
accumulatedMovementMagnitude += centroidRelativeMovement.length();
|
|
|
|
|
2019-01-25 09:42:45 +00:00
|
|
|
accumulatedCentroidDistance += qreal(pressCentroidRelativePosition.length());
|
2018-05-22 12:59:08 +00:00
|
|
|
if (event->isPressEvent())
|
2019-01-25 09:42:45 +00:00
|
|
|
m_accumulatedStartCentroidDistance += qreal((QVector2D(point->scenePressPosition()) - pressCentroid).length());
|
2017-05-18 09:23:25 +00:00
|
|
|
} else {
|
|
|
|
setPassiveGrab(point);
|
|
|
|
}
|
|
|
|
if (point->state() == QQuickEventPoint::Pressed) {
|
|
|
|
point->setAccepted(false); // don't stop propagation
|
|
|
|
setPassiveGrab(point);
|
2017-08-10 10:05:41 +00:00
|
|
|
}
|
2018-05-22 12:59:08 +00:00
|
|
|
if (QQuickWindowPrivate::dragOverThreshold(point))
|
|
|
|
++numberOfPointsDraggedOverThreshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
|
|
|
|
accumulatedMovementMagnitude /= m_currentPoints.count();
|
|
|
|
|
|
|
|
QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
|
|
|
|
if (!xAxis()->enabled())
|
|
|
|
avgDrag.setX(0);
|
|
|
|
if (!yAxis()->enabled())
|
|
|
|
avgDrag.setY(0);
|
|
|
|
|
2019-01-25 09:42:45 +00:00
|
|
|
const qreal centroidMovementDelta = qreal((currentCentroid - pressCentroid).length());
|
2018-05-22 12:59:08 +00:00
|
|
|
|
|
|
|
qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale
|
|
|
|
if (numberOfPointsDraggedOverThreshold >= 1) {
|
|
|
|
if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
|
|
|
|
// Drag
|
|
|
|
if (grabPoints(chosenPoints))
|
|
|
|
setActive(true);
|
|
|
|
} else if (distanceToCentroidDelta > dragThreshold) { // all points should in accumulation have been moved beyond threshold (?)
|
|
|
|
// Scale
|
|
|
|
if (grabPoints(chosenPoints))
|
|
|
|
setActive(true);
|
|
|
|
} else if (distanceToCentroidDelta < dragThreshold && (centroidMovementDelta < dragThreshold)) {
|
|
|
|
// Rotate
|
|
|
|
// Since it wasn't a scale and if we exceeded the dragthreshold, and the
|
|
|
|
// centroid didn't moved much, the points must have been moved around the centroid.
|
|
|
|
if (grabPoints(chosenPoints))
|
|
|
|
setActive(true);
|
|
|
|
}
|
2017-03-08 15:16:36 +00:00
|
|
|
}
|
2017-08-10 10:05:41 +00:00
|
|
|
if (!active())
|
|
|
|
return;
|
2017-03-08 15:16:36 +00:00
|
|
|
}
|
2018-05-22 12:59:08 +00:00
|
|
|
|
2017-08-10 10:05:41 +00:00
|
|
|
// avoid mapping the minima and maxima, as they might have unmappable values
|
|
|
|
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
|
|
|
|
// 1. scale
|
2018-05-09 18:32:16 +00:00
|
|
|
dist = averageTouchPointDistance(m_centroid.scenePosition());
|
2017-08-10 10:05:41 +00:00
|
|
|
m_activeScale = dist / m_startDistance;
|
|
|
|
m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
|
|
|
|
|
|
|
|
// 2. rotate
|
2018-05-09 18:32:16 +00:00
|
|
|
QVector<PointData> newAngles = angles(m_centroid.scenePosition());
|
2017-08-10 10:05:41 +00:00
|
|
|
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
|
|
|
|
m_activeRotation += angleDelta;
|
|
|
|
m_startAngles = std::move(newAngles);
|
|
|
|
|
|
|
|
if (!containsReleasedPoints)
|
QQuickMultiPointHandler: store QQuickHandlerPoints, not QQEventPoint*s
QQuickPointerTouchEvent::reset(QEvent *event) reuses instances of
QQuickEventPoint from one touch event to the next, but it makes no
attempt to match up each instance with the same pointId from the event.
So from the perspective of Handlers, each event can have its
touchpoints in a different order, and therefore it's always wrong to
hold onto any QQuickEventPoint pointer between events. Instead we
use QQuickHandlerPoint for storage: both for exposing to QML, and for
internal storage in handlers that need to remember touchpoint state.
Without this change, any MultiPointHandler could "forget" which
point IDs it had chosen to react to, in any case where the event
contains additional points. It was using a QVector<QQuickEventPoint *>
to remember the chosen points, but each of those instances might be
assigned a different touchpoint during the handling of the next
touch event (and thus its ID would change "underneath").
Perhaps this went unnoticed until now because 1) the only subclass
of MultiPointHandler was PinchHandler, and we didn't often test
use cases with extra touchpoints beyond the ones involved in the pinch
and 2) on Linux/X11 they stayed in the same order in each event.
But as soon as we try to make DragHandler inherit MultiPointHandler,
it becomes clear that it does not succeed in holding on to a particular
touchpoint while dragging multiple DragHandlers with multiple fingers,
without this patch.
Change-Id: If7e0daa9ed77b263efc09f5ea73dfba6a3c8205c
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
2018-06-29 13:16:52 +00:00
|
|
|
acceptPoints(chosenPoints);
|
2017-03-08 15:16:36 +00:00
|
|
|
}
|
2017-08-10 10:05:41 +00:00
|
|
|
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
const qreal totalRotation = m_startRotation + m_activeRotation;
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation);
|
|
|
|
m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above
|
2018-06-22 14:37:32 +00:00
|
|
|
m_accumulatedScale = m_startScale * m_activeScale;
|
Fix rotation in PinchHandler
If we want to allow rotation with more than 2 fingers, its not
straightforward to calculate the rotation angle, because we cannot anymore
calculate the angle between the two touchpoints.
The approach chosen was to calculate the angle between the centroid and
the touchpoints, and take the average between the angles.
Then, for the next event we calculated the new average of the angles.
However, this is not really reliable in some scenarios, suppose we have
these three angles:
0 120 240, avg = 360/3 = 120
next, touch points rotate clockwise with 2 degrees, and we get these
angles:
358 118 238, avg = 714/3 = 238
So, just by rotating all fingers by 2 degrees, we got a jump by 118
degrees "in average".
Instead we need to track the angles of *all* touch points, and when the
next touch event is received we calculate how much the angle has changed
per touch point. We then take the average of those angles as the effective
"rotation" of the PinchHandler
Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-09-02 15:07:57 +00:00
|
|
|
|
2016-10-18 09:49:13 +00:00
|
|
|
if (target() && target()->parentItem()) {
|
2018-07-31 15:45:27 +00:00
|
|
|
const QPointF centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
|
2016-10-18 09:49:13 +00:00
|
|
|
// 3. Drag/translate
|
2018-05-09 18:32:16 +00:00
|
|
|
const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
|
2017-09-12 09:00:33 +00:00
|
|
|
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
|
2016-10-18 09:49:13 +00:00
|
|
|
// apply rotation + scaling around the centroid - then apply translation.
|
|
|
|
QMatrix4x4 mat;
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
|
|
|
|
const QVector3D centroidParentVector(centroidParentPos);
|
|
|
|
mat.translate(centroidParentVector);
|
2019-01-25 09:42:45 +00:00
|
|
|
mat.rotate(float(m_activeRotation), 0, 0, 1);
|
|
|
|
mat.scale(float(m_activeScale));
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
mat.translate(-centroidParentVector);
|
|
|
|
mat.translate(QVector3D(m_activeTranslation));
|
|
|
|
|
|
|
|
mat = mat * m_startMatrix;
|
|
|
|
|
|
|
|
QPointF xformOriginPoint = target()->transformOriginPoint();
|
|
|
|
QPointF pos = mat * xformOriginPoint;
|
|
|
|
pos -= xformOriginPoint;
|
|
|
|
|
2018-07-31 15:45:27 +00:00
|
|
|
if (xAxis()->enabled())
|
|
|
|
pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
|
2019-01-02 20:00:33 +00:00
|
|
|
else
|
2019-01-25 09:42:45 +00:00
|
|
|
pos.rx() -= qreal(m_activeTranslation.x());
|
2018-07-31 15:45:27 +00:00
|
|
|
if (yAxis()->enabled())
|
|
|
|
pos.setY(qBound(yAxis()->minimum(), pos.y(), yAxis()->maximum()));
|
2019-01-02 20:00:33 +00:00
|
|
|
else
|
2019-01-25 09:42:45 +00:00
|
|
|
pos.ry() -= qreal(m_activeTranslation.y());
|
2018-07-31 15:45:27 +00:00
|
|
|
|
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2016-12-13 10:38:23 +00:00
|
|
|
target()->setPosition(pos);
|
|
|
|
target()->setRotation(rotation);
|
2018-06-22 14:37:32 +00:00
|
|
|
target()->setScale(m_accumulatedScale);
|
2017-09-12 09:00:33 +00:00
|
|
|
} else {
|
2018-05-09 18:32:16 +00:00
|
|
|
m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
|
2016-10-18 09:49:13 +00:00
|
|
|
}
|
2017-08-10 10:05:41 +00:00
|
|
|
|
2018-05-09 18:32:16 +00:00
|
|
|
qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
|
2017-09-12 09:00:33 +00:00
|
|
|
<< ", distance" << m_startDistance << "->" << dist
|
2018-06-22 14:37:32 +00:00
|
|
|
<< ", startScale" << m_startScale << "->" << m_accumulatedScale
|
2017-09-12 09:00:33 +00:00
|
|
|
<< ", activeRotation" << m_activeRotation
|
2017-08-10 10:05:41 +00:00
|
|
|
<< ", rotation" << rotation
|
|
|
|
<< " from " << event->device()->type();
|
2015-08-11 16:14:02 +00:00
|
|
|
|
|
|
|
emit updated();
|
|
|
|
}
|
|
|
|
|
2017-09-22 12:09:45 +00:00
|
|
|
/*!
|
|
|
|
\readonly
|
2018-09-10 16:33:56 +00:00
|
|
|
\qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
|
2017-09-22 12:09:45 +00:00
|
|
|
|
|
|
|
A point exactly in the middle of the currently-pressed touch points.
|
2018-08-31 12:10:27 +00:00
|
|
|
The \l target will be rotated around this point.
|
2017-09-22 12:09:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\readonly
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::scale
|
|
|
|
|
2018-06-22 14:37:32 +00:00
|
|
|
The scale factor that will automatically be set on the \l target if it is not null.
|
|
|
|
Otherwise, bindings can be used to do arbitrary things with this value.
|
|
|
|
While the pinch gesture is being performed, it is continuously multiplied by
|
|
|
|
\l activeScale; after the gesture ends, it stays the same; and when the next
|
|
|
|
pinch gesture begins, it begins to be multiplied by activeScale again.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\readonly
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::activeScale
|
|
|
|
|
|
|
|
The scale factor while the pinch gesture is being performed.
|
|
|
|
It is 1.0 when the gesture begins, increases as the touchpoints are spread
|
|
|
|
apart, and decreases as the touchpoints are brought together.
|
|
|
|
If \l target is not null, its \l {Item::scale}{scale} will be automatically
|
|
|
|
multiplied by this value.
|
|
|
|
Otherwise, bindings can be used to do arbitrary things with this value.
|
2017-09-22 12:09:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\readonly
|
|
|
|
\qmlproperty real QtQuick::PinchHandler::rotation
|
|
|
|
|
|
|
|
The rotation of the pinch gesture in degrees, with positive values clockwise.
|
|
|
|
It is 0 when the gesture begins. If \l target is not null, this will be
|
|
|
|
automatically applied to its \l {Item::rotation}{rotation}. Otherwise,
|
|
|
|
bindings can be used to do arbitrary things with this value.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\readonly
|
|
|
|
\qmlproperty QVector2D QtQuick::PinchHandler::translation
|
|
|
|
|
|
|
|
The translation of the gesture \l centroid. It is \c (0, 0) when the
|
|
|
|
gesture begins.
|
|
|
|
*/
|
|
|
|
|
2015-08-11 16:14:02 +00:00
|
|
|
QT_END_NAMESPACE
|