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 QtSG 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 "qsgpincharea_p_p.h"
43 #include "qsgcanvas.h"
45 #include <QtGui/qgraphicssceneevent.h>
46 #include <QtGui/qapplication.h>
54 : m_target(0), m_minScale(1.0), m_maxScale(1.0)
55 , m_minRotation(0.0), m_maxRotation(0.0)
56 , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
57 , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false)
61 QSGPinchAreaPrivate::~QSGPinchAreaPrivate()
66 QSGPinchArea::QSGPinchArea(QSGItem *parent)
67 : QSGItem(*(new QSGPinchAreaPrivate), parent)
73 QSGPinchArea::~QSGPinchArea()
77 bool QSGPinchArea::isEnabled() const
79 Q_D(const QSGPinchArea);
83 void QSGPinchArea::setEnabled(bool a)
88 emit enabledChanged();
92 void QSGPinchArea::touchEvent(QTouchEvent *event)
95 if (!d->absorb || !isVisible()) {
96 QSGItem::event(event);
100 switch (event->type()) {
101 case QEvent::TouchBegin:
102 case QEvent::TouchUpdate:
103 d->touchPoints.clear();
104 for (int i = 0; i < event->touchPoints().count(); ++i) {
105 if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) {
106 d->touchPoints << event->touchPoints().at(i);
111 case QEvent::TouchEnd:
112 d->touchPoints.clear();
116 QSGItem::event(event);
120 void QSGPinchArea::updatePinch()
123 if (d->touchPoints.count() == 0) {
125 d->stealMouse = false;
126 setKeepMouseGrab(false);
128 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
129 QSGPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
130 pe.setStartCenter(d->pinchStartCenter);
131 pe.setPreviousCenter(pinchCenter);
132 pe.setPreviousAngle(d->pinchLastAngle);
133 pe.setPreviousScale(d->pinchLastScale);
134 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
135 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
136 pe.setPoint1(mapFromScene(d->lastPoint1));
137 pe.setPoint2(mapFromScene(d->lastPoint2));
138 emit pinchFinished(&pe);
139 d->pinchStartDist = 0;
140 d->pinchActivated = false;
141 if (d->pinch && d->pinch->target())
142 d->pinch->setActive(false);
146 QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0);
147 QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0);
148 if (d->touchPoints.count() == 2
149 && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) {
150 d->id1 = touchPoint1.id();
151 d->sceneStartPoint1 = touchPoint1.scenePos();
152 d->sceneStartPoint2 = touchPoint2.scenePos();
154 d->pinchRejected = false;
155 d->pinchActivated = true;
156 } else if (d->pinchActivated && !d->pinchRejected){
157 const int dragThreshold = QApplication::startDragDistance();
158 QPointF p1 = touchPoint1.scenePos();
159 QPointF p2 = touchPoint2.scenePos();
160 qreal dx = p1.x() - p2.x();
161 qreal dy = p1.y() - p2.y();
162 qreal dist = sqrt(dx*dx + dy*dy);
163 QPointF sceneCenter = (p1 + p2)/2;
164 qreal angle = QLineF(p1, p2).angle();
165 if (d->touchPoints.count() == 1) {
166 // If we only have one point then just move the center
167 if (d->id1 == touchPoint1.id())
168 sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1;
170 sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2;
171 angle = d->pinchLastAngle;
173 d->id1 = touchPoint1.id();
177 if (d->touchPoints.count() >= 2
178 && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
179 || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
180 || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
181 || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) {
182 d->sceneStartCenter = sceneCenter;
183 d->sceneLastCenter = sceneCenter;
184 d->pinchStartCenter = mapFromScene(sceneCenter);
185 d->pinchStartDist = dist;
186 d->pinchStartAngle = angle;
187 d->pinchLastScale = 1.0;
188 d->pinchLastAngle = angle;
189 d->pinchRotation = 0.0;
192 QSGPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
193 pe.setStartCenter(d->pinchStartCenter);
194 pe.setPreviousCenter(d->pinchStartCenter);
195 pe.setPreviousAngle(d->pinchLastAngle);
196 pe.setPreviousScale(d->pinchLastScale);
197 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
198 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
199 pe.setPoint1(mapFromScene(d->lastPoint1));
200 pe.setPoint2(mapFromScene(d->lastPoint2));
201 pe.setPointCount(d->touchPoints.count());
202 emit pinchStarted(&pe);
205 d->stealMouse = true;
206 QSGCanvas *c = canvas();
207 if (c && c->mouseGrabberItem() != this)
209 setKeepMouseGrab(true);
210 if (d->pinch && d->pinch->target()) {
211 d->pinchStartPos = pinch()->target()->pos();
212 d->pinchStartScale = d->pinch->target()->scale();
213 d->pinchStartRotation = d->pinch->target()->rotation();
214 d->pinch->setActive(true);
217 d->pinchRejected = true;
220 } else if (d->pinchStartDist > 0) {
221 qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale;
222 qreal da = d->pinchLastAngle - angle;
227 d->pinchRotation += da;
228 QPointF pinchCenter = mapFromScene(sceneCenter);
229 QSGPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
230 pe.setStartCenter(d->pinchStartCenter);
231 pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
232 pe.setPreviousAngle(d->pinchLastAngle);
233 pe.setPreviousScale(d->pinchLastScale);
234 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
235 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
236 pe.setPoint1(touchPoint1.pos());
237 pe.setPoint2(touchPoint2.pos());
238 pe.setPointCount(d->touchPoints.count());
239 d->pinchLastScale = scale;
240 d->sceneLastCenter = sceneCenter;
241 d->pinchLastAngle = angle;
242 d->lastPoint1 = touchPoint1.scenePos();
243 d->lastPoint2 = touchPoint2.scenePos();
244 emit pinchUpdated(&pe);
245 if (d->pinch && d->pinch->target()) {
246 qreal s = d->pinchStartScale * scale;
247 s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
248 pinch()->target()->setScale(s);
249 QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos;
250 if (pinch()->axis() & QSGPinch::XAxis) {
252 if (x < pinch()->xmin())
254 else if (x > pinch()->xmax())
256 pinch()->target()->setX(x);
258 if (pinch()->axis() & QSGPinch::YAxis) {
260 if (y < pinch()->ymin())
262 else if (y > pinch()->ymax())
264 pinch()->target()->setY(y);
266 if (d->pinchStartRotation >= pinch()->minimumRotation()
267 && d->pinchStartRotation <= pinch()->maximumRotation()) {
268 qreal r = d->pinchRotation + d->pinchStartRotation;
269 r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
270 pinch()->target()->setRotation(r);
277 void QSGPinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
280 d->stealMouse = false;
282 QSGItem::mousePressEvent(event);
284 setKeepMouseGrab(false);
285 event->setAccepted(true);
289 void QSGPinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
293 QSGItem::mouseMoveEvent(event);
298 void QSGPinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
301 d->stealMouse = false;
303 QSGItem::mouseReleaseEvent(event);
305 QSGCanvas *c = canvas();
306 if (c && c->mouseGrabberItem() == this)
308 setKeepMouseGrab(false);
312 void QSGPinchArea::mouseUngrabEvent()
314 setKeepMouseGrab(false);
317 bool QSGPinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
320 QGraphicsSceneMouseEvent mouseEvent(event->type());
321 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
323 QSGCanvas *c = canvas();
324 QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
325 bool stealThisEvent = d->stealMouse;
326 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
327 mouseEvent.setAccepted(false);
328 for (int i = 0x1; i <= 0x10; i <<= 1) {
329 if (event->buttons() & i) {
330 Qt::MouseButton button = Qt::MouseButton(i);
331 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
334 mouseEvent.setScenePos(event->scenePos());
335 mouseEvent.setLastScenePos(event->lastScenePos());
336 mouseEvent.setPos(mapFromScene(event->scenePos()));
337 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
339 switch(mouseEvent.type()) {
340 case QEvent::GraphicsSceneMouseMove:
341 mouseMoveEvent(&mouseEvent);
343 case QEvent::GraphicsSceneMousePress:
344 mousePressEvent(&mouseEvent);
346 case QEvent::GraphicsSceneMouseRelease:
347 mouseReleaseEvent(&mouseEvent);
352 grabber = c->mouseGrabberItem();
353 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
356 return stealThisEvent;
358 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
359 d->stealMouse = false;
360 if (c && c->mouseGrabberItem() == this)
362 setKeepMouseGrab(false);
367 bool QSGPinchArea::childMouseEventFilter(QSGItem *i, QEvent *e)
370 if (!d->absorb || !isVisible())
371 return QSGItem::childMouseEventFilter(i, e);
373 case QEvent::GraphicsSceneMousePress:
374 case QEvent::GraphicsSceneMouseMove:
375 case QEvent::GraphicsSceneMouseRelease:
376 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
378 case QEvent::TouchBegin:
379 case QEvent::TouchUpdate: {
380 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
381 d->touchPoints.clear();
382 for (int i = 0; i < touch->touchPoints().count(); ++i)
383 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
384 d->touchPoints << touch->touchPoints().at(i);
388 case QEvent::TouchEnd:
389 d->touchPoints.clear();
396 return QSGItem::childMouseEventFilter(i, e);
399 void QSGPinchArea::geometryChanged(const QRectF &newGeometry,
400 const QRectF &oldGeometry)
402 QSGItem::geometryChanged(newGeometry, oldGeometry);
405 void QSGPinchArea::itemChange(ItemChange change, const ItemChangeData &value)
407 QSGItem::itemChange(change, value);
410 QSGPinch *QSGPinchArea::pinch()
414 d->pinch = new QSGPinch;