1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativepincharea_p.h"
43 #include "QtQuick1/private/qdeclarativepincharea_p_p.h"
45 #include <QApplication>
46 #include <QGraphicsScene>
57 \qmlclass PinchEvent QDeclarative1PinchEvent
58 \inqmlmodule QtQuick 1
59 \ingroup qml-event-elements
60 \brief The PinchEvent object provides information about a pinch event.
62 \bold {The PinchEvent element was added in QtQuick 1.1}
64 The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points.
66 The \c scale and \c previousScale properties provide the scale factor.
68 The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation.
70 The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points.
72 The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not
79 \qmlproperty QPointF QtQuick1::PinchEvent::center
80 \qmlproperty QPointF QtQuick1::PinchEvent::startCenter
81 \qmlproperty QPointF QtQuick1::PinchEvent::previousCenter
83 These properties hold the position of the center point between the two touch points.
86 \o \c center is the current center point
87 \o \c previousCenter is the center point of the previous event.
88 \o \c startCenter is the center point when the gesture began
93 \qmlproperty real QtQuick1::PinchEvent::scale
94 \qmlproperty real QtQuick1::PinchEvent::previousScale
96 These properties hold the scale factor determined by the change in distance between the two touch points.
99 \o \c scale is the current scale factor.
100 \o \c previousScale is the scale factor of the previous event.
103 When a pinch gesture is started, the scale is 1.0.
107 \qmlproperty real QtQuick1::PinchEvent::angle
108 \qmlproperty real QtQuick1::PinchEvent::previousAngle
109 \qmlproperty real QtQuick1::PinchEvent::rotation
111 These properties hold the angle between the two touch points.
114 \o \c angle is the current angle between the two points in the range -180 to 180.
115 \o \c previousAngle is the angle of the previous event.
116 \o \c rotation is the total rotation since the pinch gesture started.
119 When a pinch gesture is started, the rotation is 0.0.
123 \qmlproperty QPointF QtQuick1::PinchEvent::point1
124 \qmlproperty QPointF QtQuick1::PinchEvent::startPoint1
125 \qmlproperty QPointF QtQuick1::PinchEvent::point2
126 \qmlproperty QPointF QtQuick1::PinchEvent::startPoint2
128 These properties provide the actual touch points generating the pinch.
131 \o \c point1 and \c point2 hold the current positions of the points.
132 \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched.
137 \qmlproperty bool QtQuick1::PinchEvent::accepted
139 Setting this property to false in the \c PinchArea::onPinchStarted handler
140 will result in no further pinch events being generated, and the gesture
145 \qmlproperty int QtQuick1::PinchEvent::pointCount
147 Holds the number of points currently touched. The PinchArea will not react
148 until two touch points have initited a gesture, but will remain active until
149 all touch points have been released.
152 QDeclarative1Pinch::QDeclarative1Pinch()
153 : m_target(0), m_minScale(1.0), m_maxScale(1.0)
154 , m_minRotation(0.0), m_maxRotation(0.0)
155 , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
156 , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false)
160 QDeclarative1PinchAreaPrivate::~QDeclarative1PinchAreaPrivate()
166 \qmlclass PinchArea QDeclarative1PinchArea
167 \inqmlmodule QtQuick 1
168 \brief The PinchArea item enables simple pinch gesture handling.
171 \bold {The PinchArea element was added in QtQuick 1.1}
173 A PinchArea is an invisible item that is typically used in conjunction with
174 a visible item in order to provide pinch gesture handling for that item.
176 The \l enabled property is used to enable and disable pinch handling for
177 the proxied item. When disabled, the pinch area becomes transparent to
180 PinchArea can be used in two ways:
183 \o setting a \c pinch.target to provide automatic interaction with an element
184 \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers
191 \qmlsignal QtQuick1::PinchArea::onPinchStarted()
193 This handler is called when the pinch area detects that a pinch gesture has started.
195 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
196 including the scale, center and angle of the pinch.
198 To ignore this gesture set the \c pinch.accepted property to false. The gesture
199 will be cancelled and no further events will be sent.
203 \qmlsignal QtQuick1::PinchArea::onPinchUpdated()
205 This handler is called when the pinch area detects that a pinch gesture has changed.
207 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
208 including the scale, center and angle of the pinch.
212 \qmlsignal QtQuick1::PinchArea::onPinchFinished()
214 This handler is called when the pinch area detects that a pinch gesture has finished.
216 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
217 including the scale, center and angle of the pinch.
222 \qmlproperty Item QtQuick1::PinchArea::pinch.target
223 \qmlproperty bool QtQuick1::PinchArea::pinch.active
224 \qmlproperty real QtQuick1::PinchArea::pinch.minimumScale
225 \qmlproperty real QtQuick1::PinchArea::pinch.maximumScale
226 \qmlproperty real QtQuick1::PinchArea::pinch.minimumRotation
227 \qmlproperty real QtQuick1::PinchArea::pinch.maximumRotation
228 \qmlproperty enumeration QtQuick1::PinchArea::pinch.dragAxis
229 \qmlproperty real QtQuick1::PinchArea::pinch.minimumX
230 \qmlproperty real QtQuick1::PinchArea::pinch.maximumX
231 \qmlproperty real QtQuick1::PinchArea::pinch.minimumY
232 \qmlproperty real QtQuick1::PinchArea::pinch.maximumY
234 \c pinch provides a convenient way to make an item react to pinch gestures.
237 \i \c pinch.target specifies the id of the item to drag.
238 \i \c pinch.active specifies if the target item is currently being dragged.
239 \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property.
240 \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property.
241 \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis)
242 \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes.
246 QDeclarative1PinchArea::QDeclarative1PinchArea(QDeclarativeItem *parent)
247 : QDeclarativeItem(*(new QDeclarative1PinchAreaPrivate), parent)
249 Q_D(QDeclarative1PinchArea);
253 QDeclarative1PinchArea::~QDeclarative1PinchArea()
258 \qmlproperty bool QtQuick1::PinchArea::enabled
259 This property holds whether the item accepts pinch gestures.
261 This property defaults to true.
263 bool QDeclarative1PinchArea::isEnabled() const
265 Q_D(const QDeclarative1PinchArea);
269 void QDeclarative1PinchArea::setEnabled(bool a)
271 Q_D(QDeclarative1PinchArea);
272 if (a != d->absorb) {
274 emit enabledChanged();
278 bool QDeclarative1PinchArea::event(QEvent *event)
280 Q_D(QDeclarative1PinchArea);
281 if (!d->absorb || !isVisible())
282 return QDeclarativeItem::event(event);
283 switch (event->type()) {
284 case QEvent::TouchBegin:
285 case QEvent::TouchUpdate: {
286 QTouchEvent *touch = static_cast<QTouchEvent*>(event);
287 d->touchPoints.clear();
288 for (int i = 0; i < touch->touchPoints().count(); ++i) {
289 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) {
290 d->touchPoints << touch->touchPoints().at(i);
296 case QEvent::TouchEnd:
297 d->touchPoints.clear();
301 return QDeclarativeItem::event(event);
304 return QDeclarativeItem::event(event);
307 void QDeclarative1PinchArea::updatePinch()
309 Q_D(QDeclarative1PinchArea);
310 if (d->touchPoints.count() == 0) {
312 d->stealMouse = false;
313 setKeepMouseGrab(false);
315 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
316 QDeclarative1PinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
317 pe.setStartCenter(d->pinchStartCenter);
318 pe.setPreviousCenter(pinchCenter);
319 pe.setPreviousAngle(d->pinchLastAngle);
320 pe.setPreviousScale(d->pinchLastScale);
321 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
322 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
323 pe.setPoint1(mapFromScene(d->lastPoint1));
324 pe.setPoint2(mapFromScene(d->lastPoint2));
325 emit pinchFinished(&pe);
326 d->pinchStartDist = 0;
327 d->pinchActivated = false;
328 if (d->pinch && d->pinch->target())
329 d->pinch->setActive(false);
333 QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0);
334 QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0);
335 if (d->touchPoints.count() == 2
336 && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) {
337 d->id1 = touchPoint1.id();
338 d->sceneStartPoint1 = touchPoint1.scenePos();
339 d->sceneStartPoint2 = touchPoint2.scenePos();
341 d->pinchRejected = false;
342 d->pinchActivated = true;
343 } else if (d->pinchActivated && !d->pinchRejected) {
344 const int dragThreshold = QApplication::startDragDistance();
345 QPointF p1 = touchPoint1.scenePos();
346 QPointF p2 = touchPoint2.scenePos();
347 qreal dx = p1.x() - p2.x();
348 qreal dy = p1.y() - p2.y();
349 qreal dist = sqrt(dx*dx + dy*dy);
350 QPointF sceneCenter = (p1 + p2)/2;
351 qreal angle = QLineF(p1, p2).angle();
352 if (d->touchPoints.count() == 1) {
353 // If we only have one point then just move the center
354 if (d->id1 == touchPoint1.id())
355 sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1;
357 sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2;
358 angle = d->pinchLastAngle;
360 d->id1 = touchPoint1.id();
364 if (d->touchPoints.count() >= 2
365 && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
366 || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
367 || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
368 || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) {
369 d->sceneStartCenter = sceneCenter;
370 d->sceneLastCenter = sceneCenter;
371 d->pinchStartCenter = mapFromScene(sceneCenter);
372 d->pinchStartDist = dist;
373 d->pinchStartAngle = angle;
374 d->pinchLastScale = 1.0;
375 d->pinchLastAngle = angle;
376 d->pinchRotation = 0.0;
379 QDeclarative1PinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
380 pe.setStartCenter(d->pinchStartCenter);
381 pe.setPreviousCenter(d->pinchStartCenter);
382 pe.setPreviousAngle(d->pinchLastAngle);
383 pe.setPreviousScale(d->pinchLastScale);
384 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
385 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
386 pe.setPoint1(mapFromScene(d->lastPoint1));
387 pe.setPoint2(mapFromScene(d->lastPoint2));
388 pe.setPointCount(d->touchPoints.count());
389 emit pinchStarted(&pe);
392 d->stealMouse = true;
393 QGraphicsScene *s = scene();
394 if (s && s->mouseGrabberItem() != this)
396 setKeepMouseGrab(true);
397 if (d->pinch && d->pinch->target()) {
398 d->pinchStartPos = pinch()->target()->pos();
399 d->pinchStartScale = d->pinch->target()->scale();
400 d->pinchStartRotation = d->pinch->target()->rotation();
401 d->pinch->setActive(true);
404 d->pinchRejected = true;
407 } else if (d->pinchStartDist > 0) {
408 qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale;
409 qreal da = d->pinchLastAngle - angle;
414 d->pinchRotation += da;
415 QPointF pinchCenter = mapFromScene(sceneCenter);
416 QDeclarative1PinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
417 pe.setStartCenter(d->pinchStartCenter);
418 pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
419 pe.setPreviousAngle(d->pinchLastAngle);
420 pe.setPreviousScale(d->pinchLastScale);
421 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
422 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
423 pe.setPoint1(touchPoint1.pos());
424 pe.setPoint2(touchPoint2.pos());
425 pe.setPointCount(d->touchPoints.count());
426 d->pinchLastScale = scale;
427 d->sceneLastCenter = sceneCenter;
428 d->pinchLastAngle = angle;
429 d->lastPoint1 = touchPoint1.scenePos();
430 d->lastPoint2 = touchPoint2.scenePos();
431 emit pinchUpdated(&pe);
432 if (d->pinch && d->pinch->target()) {
433 qreal s = d->pinchStartScale * scale;
434 s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
435 pinch()->target()->setScale(s);
436 QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos;
437 if (pinch()->axis() & QDeclarative1Pinch::XAxis) {
439 if (x < pinch()->xmin())
441 else if (x > pinch()->xmax())
443 pinch()->target()->setX(x);
445 if (pinch()->axis() & QDeclarative1Pinch::YAxis) {
447 if (y < pinch()->ymin())
449 else if (y > pinch()->ymax())
451 pinch()->target()->setY(y);
453 if (d->pinchStartRotation >= pinch()->minimumRotation()
454 && d->pinchStartRotation <= pinch()->maximumRotation()) {
455 qreal r = d->pinchRotation + d->pinchStartRotation;
456 r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
457 pinch()->target()->setRotation(r);
464 void QDeclarative1PinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
466 Q_D(QDeclarative1PinchArea);
467 d->stealMouse = false;
469 QDeclarativeItem::mousePressEvent(event);
471 setKeepMouseGrab(false);
472 event->setAccepted(true);
476 void QDeclarative1PinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
478 Q_D(QDeclarative1PinchArea);
480 QDeclarativeItem::mouseMoveEvent(event);
485 void QDeclarative1PinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
487 Q_D(QDeclarative1PinchArea);
488 d->stealMouse = false;
490 QDeclarativeItem::mouseReleaseEvent(event);
492 QGraphicsScene *s = scene();
493 if (s && s->mouseGrabberItem() == this)
495 setKeepMouseGrab(false);
499 bool QDeclarative1PinchArea::sceneEvent(QEvent *event)
501 bool rv = QDeclarativeItem::sceneEvent(event);
502 if (event->type() == QEvent::UngrabMouse) {
503 setKeepMouseGrab(false);
508 bool QDeclarative1PinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
510 Q_D(QDeclarative1PinchArea);
511 QGraphicsSceneMouseEvent mouseEvent(event->type());
512 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
514 QGraphicsScene *s = scene();
515 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
516 bool stealThisEvent = d->stealMouse;
517 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
518 mouseEvent.setAccepted(false);
519 for (int i = 0x1; i <= 0x10; i <<= 1) {
520 if (event->buttons() & i) {
521 Qt::MouseButton button = Qt::MouseButton(i);
522 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
525 mouseEvent.setScenePos(event->scenePos());
526 mouseEvent.setLastScenePos(event->lastScenePos());
527 mouseEvent.setPos(mapFromScene(event->scenePos()));
528 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
530 switch(mouseEvent.type()) {
531 case QEvent::GraphicsSceneMouseMove:
532 mouseMoveEvent(&mouseEvent);
534 case QEvent::GraphicsSceneMousePress:
535 mousePressEvent(&mouseEvent);
537 case QEvent::GraphicsSceneMouseRelease:
538 mouseReleaseEvent(&mouseEvent);
543 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
544 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
547 return stealThisEvent;
549 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
550 d->stealMouse = false;
551 if (s && s->mouseGrabberItem() == this)
553 setKeepMouseGrab(false);
558 bool QDeclarative1PinchArea::sceneEventFilter(QGraphicsItem *i, QEvent *e)
560 Q_D(QDeclarative1PinchArea);
561 if (!d->absorb || !isVisible())
562 return QDeclarativeItem::sceneEventFilter(i, e);
564 case QEvent::GraphicsSceneMousePress:
565 case QEvent::GraphicsSceneMouseMove:
566 case QEvent::GraphicsSceneMouseRelease:
567 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
569 case QEvent::TouchBegin:
570 case QEvent::TouchUpdate: {
571 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
572 d->touchPoints.clear();
573 for (int i = 0; i < touch->touchPoints().count(); ++i)
574 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
575 d->touchPoints << touch->touchPoints().at(i);
579 case QEvent::TouchEnd:
580 d->touchPoints.clear();
587 return QDeclarativeItem::sceneEventFilter(i, e);
590 void QDeclarative1PinchArea::geometryChanged(const QRectF &newGeometry,
591 const QRectF &oldGeometry)
593 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
596 QVariant QDeclarative1PinchArea::itemChange(GraphicsItemChange change,
597 const QVariant &value)
599 return QDeclarativeItem::itemChange(change, value);
602 QDeclarative1Pinch *QDeclarative1PinchArea::pinch()
604 Q_D(QDeclarative1PinchArea);
606 d->pinch = new QDeclarative1Pinch;