1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qdeclarativepincharea_p.h"
43 #include "qdeclarativepincharea_p_p.h"
45 #include <QApplication>
46 #include <QGraphicsScene>
55 \qmlclass PinchEvent QDeclarativePinchEvent
56 \ingroup qml-event-elements
57 \brief The PinchEvent object provides information about a pinch event.
59 \bold {The PinchEvent element was added in QtQuick 1.1}
61 The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points.
63 The \c scale and \c previousScale properties provide the scale factor.
65 The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation.
67 The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points.
69 The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not
76 \qmlproperty QPointF PinchEvent::center
77 \qmlproperty QPointF PinchEvent::startCenter
78 \qmlproperty QPointF PinchEvent::previousCenter
80 These properties hold the position of the center point between the two touch points.
83 \o \c center is the current center point
84 \o \c previousCenter is the center point of the previous event.
85 \o \c startCenter is the center point when the gesture began
90 \qmlproperty real PinchEvent::scale
91 \qmlproperty real PinchEvent::previousScale
93 These properties hold the scale factor determined by the change in distance between the two touch points.
96 \o \c scale is the current scale factor.
97 \o \c previousScale is the scale factor of the previous event.
100 When a pinch gesture is started, the scale is 1.0.
104 \qmlproperty real PinchEvent::angle
105 \qmlproperty real PinchEvent::previousAngle
106 \qmlproperty real PinchEvent::rotation
108 These properties hold the angle between the two touch points.
111 \o \c angle is the current angle between the two points in the range -180 to 180.
112 \o \c previousAngle is the angle of the previous event.
113 \o \c rotation is the total rotation since the pinch gesture started.
116 When a pinch gesture is started, the rotation is 0.0.
120 \qmlproperty QPointF PinchEvent::point1
121 \qmlproperty QPointF PinchEvent::startPoint1
122 \qmlproperty QPointF PinchEvent::point2
123 \qmlproperty QPointF PinchEvent::startPoint2
125 These properties provide the actual touch points generating the pinch.
128 \o \c point1 and \c point2 hold the current positions of the points.
129 \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched.
134 \qmlproperty bool PinchEvent::accepted
136 Setting this property to false in the \c PinchArea::onPinchStarted handler
137 will result in no further pinch events being generated, and the gesture
142 \qmlproperty int PinchEvent::pointCount
144 Holds the number of points currently touched. The PinchArea will not react
145 until two touch points have initited a gesture, but will remain active until
146 all touch points have been released.
149 QDeclarativePinch::QDeclarativePinch()
150 : m_target(0), m_minScale(1.0), m_maxScale(1.0)
151 , m_minRotation(0.0), m_maxRotation(0.0)
152 , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
153 , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false)
157 QDeclarativePinchAreaPrivate::~QDeclarativePinchAreaPrivate()
163 \qmlclass PinchArea QDeclarativePinchArea
164 \brief The PinchArea item enables simple pinch gesture handling.
167 \bold {The PinchArea element was added in QtQuick 1.1}
169 A PinchArea is an invisible item that is typically used in conjunction with
170 a visible item in order to provide pinch gesture handling for that item.
172 The \l enabled property is used to enable and disable pinch handling for
173 the proxied item. When disabled, the pinch area becomes transparent to
176 PinchArea can be used in two ways:
179 \o setting a \c pinch.target to provide automatic interaction with an element
180 \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers
187 \qmlsignal PinchArea::onPinchStarted()
189 This handler is called when the pinch area detects that a pinch gesture has started.
191 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
192 including the scale, center and angle of the pinch.
194 To ignore this gesture set the \c pinch.accepted property to false. The gesture
195 will be cancelled and no further events will be sent.
199 \qmlsignal PinchArea::onPinchUpdated()
201 This handler is called when the pinch area detects that a pinch gesture has changed.
203 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
204 including the scale, center and angle of the pinch.
208 \qmlsignal PinchArea::onPinchFinished()
210 This handler is called when the pinch area detects that a pinch gesture has finished.
212 The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
213 including the scale, center and angle of the pinch.
218 \qmlproperty Item PinchArea::pinch.target
219 \qmlproperty bool PinchArea::pinch.active
220 \qmlproperty real PinchArea::pinch.minimumScale
221 \qmlproperty real PinchArea::pinch.maximumScale
222 \qmlproperty real PinchArea::pinch.minimumRotation
223 \qmlproperty real PinchArea::pinch.maximumRotation
224 \qmlproperty enumeration PinchArea::pinch.dragAxis
225 \qmlproperty real PinchArea::pinch.minimumX
226 \qmlproperty real PinchArea::pinch.maximumX
227 \qmlproperty real PinchArea::pinch.minimumY
228 \qmlproperty real PinchArea::pinch.maximumY
230 \c pinch provides a convenient way to make an item react to pinch gestures.
233 \i \c pinch.target specifies the id of the item to drag.
234 \i \c pinch.active specifies if the target item is currently being dragged.
235 \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property.
236 \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property.
237 \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)
238 \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes.
242 QDeclarativePinchArea::QDeclarativePinchArea(QDeclarativeItem *parent)
243 : QDeclarativeItem(*(new QDeclarativePinchAreaPrivate), parent)
245 Q_D(QDeclarativePinchArea);
249 QDeclarativePinchArea::~QDeclarativePinchArea()
254 \qmlproperty bool PinchArea::enabled
255 This property holds whether the item accepts pinch gestures.
257 This property defaults to true.
259 bool QDeclarativePinchArea::isEnabled() const
261 Q_D(const QDeclarativePinchArea);
265 void QDeclarativePinchArea::setEnabled(bool a)
267 Q_D(QDeclarativePinchArea);
268 if (a != d->absorb) {
270 emit enabledChanged();
274 bool QDeclarativePinchArea::event(QEvent *event)
276 Q_D(QDeclarativePinchArea);
277 if (!d->absorb || !isVisible())
278 return QDeclarativeItem::event(event);
279 switch (event->type()) {
280 case QEvent::TouchBegin:
281 case QEvent::TouchUpdate: {
282 QTouchEvent *touch = static_cast<QTouchEvent*>(event);
283 d->touchPoints.clear();
284 for (int i = 0; i < touch->touchPoints().count(); ++i) {
285 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) {
286 d->touchPoints << touch->touchPoints().at(i);
292 case QEvent::TouchEnd:
293 d->touchPoints.clear();
297 return QDeclarativeItem::event(event);
300 return QDeclarativeItem::event(event);
303 void QDeclarativePinchArea::updatePinch()
305 Q_D(QDeclarativePinchArea);
306 if (d->touchPoints.count() == 0) {
308 d->stealMouse = false;
309 setKeepMouseGrab(false);
311 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
312 QDeclarativePinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
313 pe.setStartCenter(d->pinchStartCenter);
314 pe.setPreviousCenter(pinchCenter);
315 pe.setPreviousAngle(d->pinchLastAngle);
316 pe.setPreviousScale(d->pinchLastScale);
317 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
318 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
319 pe.setPoint1(mapFromScene(d->lastPoint1));
320 pe.setPoint2(mapFromScene(d->lastPoint2));
321 emit pinchFinished(&pe);
322 d->pinchStartDist = 0;
323 d->pinchActivated = false;
324 if (d->pinch && d->pinch->target())
325 d->pinch->setActive(false);
329 QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0);
330 QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0);
331 if (d->touchPoints.count() == 2
332 && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) {
333 d->id1 = touchPoint1.id();
334 d->sceneStartPoint1 = touchPoint1.scenePos();
335 d->sceneStartPoint2 = touchPoint2.scenePos();
337 d->pinchRejected = false;
338 d->pinchActivated = true;
339 } else if (d->pinchActivated && !d->pinchRejected) {
340 const int dragThreshold = QApplication::startDragDistance();
341 QPointF p1 = touchPoint1.scenePos();
342 QPointF p2 = touchPoint2.scenePos();
343 qreal dx = p1.x() - p2.x();
344 qreal dy = p1.y() - p2.y();
345 qreal dist = sqrt(dx*dx + dy*dy);
346 QPointF sceneCenter = (p1 + p2)/2;
347 qreal angle = QLineF(p1, p2).angle();
348 if (d->touchPoints.count() == 1) {
349 // If we only have one point then just move the center
350 if (d->id1 == touchPoint1.id())
351 sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1;
353 sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2;
354 angle = d->pinchLastAngle;
356 d->id1 = touchPoint1.id();
360 if (d->touchPoints.count() >= 2
361 && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
362 || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
363 || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
364 || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) {
365 d->sceneStartCenter = sceneCenter;
366 d->sceneLastCenter = sceneCenter;
367 d->pinchStartCenter = mapFromScene(sceneCenter);
368 d->pinchStartDist = dist;
369 d->pinchStartAngle = angle;
370 d->pinchLastScale = 1.0;
371 d->pinchLastAngle = angle;
372 d->pinchRotation = 0.0;
375 QDeclarativePinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
376 pe.setStartCenter(d->pinchStartCenter);
377 pe.setPreviousCenter(d->pinchStartCenter);
378 pe.setPreviousAngle(d->pinchLastAngle);
379 pe.setPreviousScale(d->pinchLastScale);
380 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
381 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
382 pe.setPoint1(mapFromScene(d->lastPoint1));
383 pe.setPoint2(mapFromScene(d->lastPoint2));
384 pe.setPointCount(d->touchPoints.count());
385 emit pinchStarted(&pe);
388 d->stealMouse = true;
389 QGraphicsScene *s = scene();
390 if (s && s->mouseGrabberItem() != this)
392 setKeepMouseGrab(true);
393 if (d->pinch && d->pinch->target()) {
394 d->pinchStartPos = pinch()->target()->pos();
395 d->pinchStartScale = d->pinch->target()->scale();
396 d->pinchStartRotation = d->pinch->target()->rotation();
397 d->pinch->setActive(true);
400 d->pinchRejected = true;
403 } else if (d->pinchStartDist > 0) {
404 qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale;
405 qreal da = d->pinchLastAngle - angle;
410 d->pinchRotation += da;
411 QPointF pinchCenter = mapFromScene(sceneCenter);
412 QDeclarativePinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
413 pe.setStartCenter(d->pinchStartCenter);
414 pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
415 pe.setPreviousAngle(d->pinchLastAngle);
416 pe.setPreviousScale(d->pinchLastScale);
417 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
418 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
419 pe.setPoint1(touchPoint1.pos());
420 pe.setPoint2(touchPoint2.pos());
421 pe.setPointCount(d->touchPoints.count());
422 d->pinchLastScale = scale;
423 d->sceneLastCenter = sceneCenter;
424 d->pinchLastAngle = angle;
425 d->lastPoint1 = touchPoint1.scenePos();
426 d->lastPoint2 = touchPoint2.scenePos();
427 emit pinchUpdated(&pe);
428 if (d->pinch && d->pinch->target()) {
429 qreal s = d->pinchStartScale * scale;
430 s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
431 pinch()->target()->setScale(s);
432 QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos;
433 if (pinch()->axis() & QDeclarativePinch::XAxis) {
435 if (x < pinch()->xmin())
437 else if (x > pinch()->xmax())
439 pinch()->target()->setX(x);
441 if (pinch()->axis() & QDeclarativePinch::YAxis) {
443 if (y < pinch()->ymin())
445 else if (y > pinch()->ymax())
447 pinch()->target()->setY(y);
449 if (d->pinchStartRotation >= pinch()->minimumRotation()
450 && d->pinchStartRotation <= pinch()->maximumRotation()) {
451 qreal r = d->pinchRotation + d->pinchStartRotation;
452 r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
453 pinch()->target()->setRotation(r);
460 void QDeclarativePinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
462 Q_D(QDeclarativePinchArea);
463 d->stealMouse = false;
465 QDeclarativeItem::mousePressEvent(event);
467 setKeepMouseGrab(false);
468 event->setAccepted(true);
472 void QDeclarativePinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
474 Q_D(QDeclarativePinchArea);
476 QDeclarativeItem::mouseMoveEvent(event);
481 void QDeclarativePinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
483 Q_D(QDeclarativePinchArea);
484 d->stealMouse = false;
486 QDeclarativeItem::mouseReleaseEvent(event);
488 QGraphicsScene *s = scene();
489 if (s && s->mouseGrabberItem() == this)
491 setKeepMouseGrab(false);
495 bool QDeclarativePinchArea::sceneEvent(QEvent *event)
497 bool rv = QDeclarativeItem::sceneEvent(event);
498 if (event->type() == QEvent::UngrabMouse) {
499 setKeepMouseGrab(false);
504 bool QDeclarativePinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
506 Q_D(QDeclarativePinchArea);
507 QGraphicsSceneMouseEvent mouseEvent(event->type());
508 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
510 QGraphicsScene *s = scene();
511 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
512 bool stealThisEvent = d->stealMouse;
513 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
514 mouseEvent.setAccepted(false);
515 for (int i = 0x1; i <= 0x10; i <<= 1) {
516 if (event->buttons() & i) {
517 Qt::MouseButton button = Qt::MouseButton(i);
518 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
521 mouseEvent.setScenePos(event->scenePos());
522 mouseEvent.setLastScenePos(event->lastScenePos());
523 mouseEvent.setPos(mapFromScene(event->scenePos()));
524 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
526 switch(mouseEvent.type()) {
527 case QEvent::GraphicsSceneMouseMove:
528 mouseMoveEvent(&mouseEvent);
530 case QEvent::GraphicsSceneMousePress:
531 mousePressEvent(&mouseEvent);
533 case QEvent::GraphicsSceneMouseRelease:
534 mouseReleaseEvent(&mouseEvent);
539 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
540 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
543 return stealThisEvent;
545 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
546 d->stealMouse = false;
547 if (s && s->mouseGrabberItem() == this)
549 setKeepMouseGrab(false);
554 bool QDeclarativePinchArea::sceneEventFilter(QGraphicsItem *i, QEvent *e)
556 Q_D(QDeclarativePinchArea);
557 if (!d->absorb || !isVisible())
558 return QDeclarativeItem::sceneEventFilter(i, e);
560 case QEvent::GraphicsSceneMousePress:
561 case QEvent::GraphicsSceneMouseMove:
562 case QEvent::GraphicsSceneMouseRelease:
563 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
565 case QEvent::TouchBegin:
566 case QEvent::TouchUpdate: {
567 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
568 d->touchPoints.clear();
569 for (int i = 0; i < touch->touchPoints().count(); ++i)
570 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
571 d->touchPoints << touch->touchPoints().at(i);
575 case QEvent::TouchEnd:
576 d->touchPoints.clear();
583 return QDeclarativeItem::sceneEventFilter(i, e);
586 void QDeclarativePinchArea::geometryChanged(const QRectF &newGeometry,
587 const QRectF &oldGeometry)
589 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
592 QVariant QDeclarativePinchArea::itemChange(GraphicsItemChange change,
593 const QVariant &value)
595 return QDeclarativeItem::itemChange(change, value);
598 QDeclarativePinch *QDeclarativePinchArea::pinch()
600 Q_D(QDeclarativePinchArea);
602 d->pinch = new QDeclarativePinch;