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 "qquickmultipointtoucharea_p.h"
43 #include <QtQuick/qquickcanvas.h>
45 #include <QMouseEvent>
52 \qmlclass TouchPoint QQuickTouchPoint
53 \inqmlmodule QtQuick 2
54 \ingroup qml-event-elements
55 \brief The TouchPoint element describes a touch point in a MultiPointTouchArea.
57 The TouchPoint element contains information about a touch point, such as the current
58 position, pressure, and area.
62 \qmlproperty int QtQuick2::TouchPoint::pointId
64 This property holds the point id of the touch point.
66 Each touch point within a MultiPointTouchArea will have a unique id.
68 void QQuickTouchPoint::setPointId(int id)
73 emit pointIdChanged();
77 \qmlproperty real QtQuick2::TouchPoint::x
78 \qmlproperty real QtQuick2::TouchPoint::y
80 These properties hold the current position of the touch point.
83 void QQuickTouchPoint::setX(qreal x)
91 void QQuickTouchPoint::setY(qreal y)
100 \qmlproperty real QtQuick2::TouchPoint::pressure
101 \qmlproperty rectangle QtQuick2::TouchPoint::area
103 These properties hold additional information about the current state of the touch point.
106 \i \c pressure is a value in the range of 0.0 to 1.0.
107 \i \c area is a rectangle covering the area of the touch point, centered on the current position of the touch point.
110 void QQuickTouchPoint::setPressure(qreal pressure)
112 if (_pressure == pressure)
114 _pressure = pressure;
115 emit pressureChanged();
118 void QQuickTouchPoint::setArea(const QRectF &area)
127 \qmlproperty bool QtQuick2::TouchPoint::pressed
129 This property holds whether the touch point is currently pressed.
131 void QQuickTouchPoint::setPressed(bool pressed)
133 if (_pressed == pressed)
136 emit pressedChanged();
140 \qmlproperty real QtQuick2::TouchPoint::startX
141 \qmlproperty real QtQuick2::TouchPoint::startY
143 These properties hold the starting position of the touch point.
146 void QQuickTouchPoint::setStartX(qreal startX)
148 if (_startX == startX)
151 emit startXChanged();
154 void QQuickTouchPoint::setStartY(qreal startY)
156 if (_startY == startY)
159 emit startYChanged();
163 \qmlproperty real QtQuick2::TouchPoint::previousX
164 \qmlproperty real QtQuick2::TouchPoint::previousY
166 These properties hold the previous position of the touch point.
168 void QQuickTouchPoint::setPreviousX(qreal previousX)
170 if (_previousX == previousX)
172 _previousX = previousX;
173 emit previousXChanged();
176 void QQuickTouchPoint::setPreviousY(qreal previousY)
178 if (_previousY == previousY)
180 _previousY = previousY;
181 emit previousYChanged();
185 \qmlproperty real QtQuick2::TouchPoint::sceneX
186 \qmlproperty real QtQuick2::TouchPoint::sceneY
188 These properties hold the current position of the touch point in scene coordinates.
191 void QQuickTouchPoint::setSceneX(qreal sceneX)
193 if (_sceneX == sceneX)
196 emit sceneXChanged();
199 void QQuickTouchPoint::setSceneY(qreal sceneY)
201 if (_sceneY == sceneY)
204 emit sceneYChanged();
208 \qmlclass MultiPointTouchArea QQuickMultiPointTouchArea
209 \inqmlmodule QtQuick 2
210 \brief The MultiPointTouchArea item enables handling of multiple touch points.
213 A MultiPointTouchArea is an invisible item that is used to track multiple touch points.
215 The \l enabled property is used to enable and disable touch handling. When disabled,
216 the touch area becomes transparent to mouse/touch events.
218 MultiPointTouchArea can be used in two ways:
221 \o setting \c touchPoints to provide touch point objects with properties that can be bound to
222 \o using the onTouchUpdated or onTouchPointsPressed, onTouchPointsUpdated and onTouchPointsReleased handlers
225 While a MultiPointTouchArea \i can take exclusive ownership of certain touch points, it is also possible to have
226 multiple MultiPointTouchAreas active at the same time, each operating on a different set of touch points.
232 \qmlsignal QtQuick2::MultiPointTouchArea::onPressed(list<TouchPoint> touchPoints)
234 This handler is called when new touch points are added. \a touchPoints is a list of these new points.
236 If minimumTouchPoints is set to a value greater than one, this handler will not be called until the minimum number
237 of required touch points has been reached. At that point, onPressed will be called with all the current touch points.
241 \qmlsignal QtQuick2::MultiPointTouchArea::onUpdated(list<TouchPoint> touchPoints)
243 This handler is called when existing touch points are updated. \a touchPoints is a list of these updated points.
247 \qmlsignal QtQuick2::MultiPointTouchArea::onReleased(list<TouchPoint> touchPoints)
249 This handler is called when existing touch points are removed. \a touchPoints is a list of these removed points.
253 \qmlsignal QtQuick2::MultiPointTouchArea::onCanceled(list<TouchPoint> touchPoints)
255 This handler is called when new touch events have been canceled because another element stole the touch event handling.
257 This signal is for advanced use: it is useful when there is more than one MultiPointTouchArea
258 that is handling input, or when there is a MultiPointTouchArea inside a \l Flickable. In the latter
259 case, if you execute some logic on the onPressed signal and then start dragging, the
260 \l Flickable may steal the touch handling from the MultiPointTouchArea. In these cases, to reset
261 the logic when the MultiPointTouchArea has lost the touch handling to the \l Flickable,
262 \c onCanceled should be used in addition to onReleased.
264 \a touchPoints is the list of canceled points.
268 \qmlsignal QtQuick2::MultiPointTouchArea::onGestureStarted(GestureEvent gesture)
270 This handler is called when the global drag threshold has been reached.
272 This function is typically used when a MultiPointTouchAreas has been nested in a Flickable or another MultiPointTouchArea.
273 Wnen the threshold has been reached, and the handler called, you can determine whether or not the touch
274 area should grab the current touch points. By default they will not be grabbed; to grab them call \c gesture.grab(). If the
275 gesture is not grabbed, the nesting Flickable, for example, would also have an opportunity to grab.
277 The gesture object also includes information on the current set of \c touchPoints and the \c dragThreshold.
281 \qmlsignal QtQuick2::MultiPointTouchArea::onTouchUpdated(list<TouchPoint> touchPoints)
283 This handler is called when the touch points handled by the MultiPointTouchArea change. This includes adding new touch points,
284 removing or canceling previous touch points, as well as updating current touch point data. \a touchPoints is the list of all current touch
289 \qmlproperty list<TouchPoint> QtQuick2::MultiPointTouchArea::touchPoints
291 This property holds a set of user-defined touch point objects that can be bound to.
293 In the following example, we have two small rectangles that follow our touch points.
295 \snippet doc/src/snippets/declarative/multipointtoucharea/multipointtoucharea.qml 0
297 By default this property holds an empty list.
302 QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
303 : QQuickItem(parent),
304 _minimumTouchPoints(0),
305 _maximumTouchPoints(INT_MAX),
308 setAcceptedMouseButtons(Qt::LeftButton);
309 setFiltersChildMouseEvents(true);
312 QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea()
315 foreach (QObject *obj, _touchPoints) {
316 QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
317 if (!dtp->isQmlDefined())
323 \qmlproperty int QtQuick2::MultiPointTouchArea::minimumTouchPoints
324 \qmlproperty int QtQuick2::MultiPointTouchArea::maximumTouchPoints
326 These properties hold the range of touch points to be handled by the touch area.
328 These are convenience that allow you to, for example, have nested MultiPointTouchAreas,
329 one handling two finger touches, and another handling three finger touches.
331 By default, all touch points within the touch area are handled.
334 int QQuickMultiPointTouchArea::minimumTouchPoints() const
336 return _minimumTouchPoints;
339 void QQuickMultiPointTouchArea::setMinimumTouchPoints(int num)
341 if (_minimumTouchPoints == num)
343 _minimumTouchPoints = num;
344 emit minimumTouchPointsChanged();
347 int QQuickMultiPointTouchArea::maximumTouchPoints() const
349 return _maximumTouchPoints;
352 void QQuickMultiPointTouchArea::setMaximumTouchPoints(int num)
354 if (_maximumTouchPoints == num)
356 _maximumTouchPoints = num;
357 emit maximumTouchPointsChanged();
360 void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event)
362 switch (event->type()) {
363 case QEvent::TouchBegin:
364 case QEvent::TouchUpdate:
365 case QEvent::TouchEnd: {
366 //if e.g. a parent Flickable has the mouse grab, don't process the touch events
367 QQuickCanvas *c = canvas();
368 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
369 if (grabber && grabber != this && grabber->keepMouseGrab() && grabber->isEnabled()) {
370 QQuickItem *item = this;
371 while ((item = item->parentItem())) {
376 updateTouchData(event);
377 if (event->type() == QEvent::TouchEnd) {
378 //TODO: move to canvas
380 setKeepMouseGrab(false);
381 QQuickCanvas *c = canvas();
382 if (c && c->mouseGrabberItem() == this)
384 setKeepTouchGrab(false);
390 QQuickItem::touchEvent(event);
395 void QQuickMultiPointTouchArea::grabGesture()
400 setKeepMouseGrab(true);
402 grabTouchPoints(_touchPoints.keys());
403 setKeepTouchGrab(true);
406 void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
410 bool started = false;
413 QTouchEvent *e = static_cast<QTouchEvent*>(event);
414 QList<QTouchEvent::TouchPoint> touchPoints = e->touchPoints();
415 int numTouchPoints = touchPoints.count();
416 //always remove released touches, and make sure we handle all releases before adds.
417 foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
418 Qt::TouchPointState touchPointState = p.state();
420 if (touchPointState & Qt::TouchPointReleased) {
421 QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
424 updateTouchPoint(dtp, &p);
425 dtp->setPressed(false);
426 _releasedTouchPoints.append(dtp);
427 _touchPoints.remove(id);
431 if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
432 foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
433 Qt::TouchPointState touchPointState = p.state();
435 if (touchPointState & Qt::TouchPointReleased) {
437 } else if (!_touchPoints.contains(id)) { //could be pressed, moved, or stationary
438 // (we may have just obtained enough points to start tracking them -- in that case moved or stationary count as newly pressed)
441 } else if (touchPointState & Qt::TouchPointMoved) {
442 QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
444 _movedTouchPoints.append(dtp);
445 updateTouchPoint(dtp,&p);
448 QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
450 updateTouchPoint(dtp,&p);
454 //see if we should be grabbing the gesture
455 if (!_stealMouse /* !ignoring gesture*/) {
456 bool offerGrab = false;
457 const int dragThreshold = qApp->styleHints()->startDragDistance();
458 foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
459 if (p.state() == Qt::TouchPointReleased)
461 const QPointF ¤tPos = p.scenePos();
462 const QPointF &startPos = p.startScenePos();
463 if (qAbs(currentPos.x() - startPos.x()) > dragThreshold)
465 else if (qAbs(currentPos.y() - startPos.y()) > dragThreshold)
472 QQuickGrabGestureEvent event;
473 event._touchPoints = _touchPoints.values();
474 emit gestureStarted(&event);
475 if (event.wantsGrab())
481 emit released(_releasedTouchPoints);
482 emit touchPointsReleased(_releasedTouchPoints);
485 emit updated(_movedTouchPoints);
486 emit touchPointsUpdated(_movedTouchPoints);
489 emit pressed(_pressedTouchPoints);
490 emit touchPointsPressed(_pressedTouchPoints);
492 if (ended || moved || started) emit touchUpdated(_touchPoints.values());
496 void QQuickMultiPointTouchArea::clearTouchLists()
498 foreach (QObject *obj, _releasedTouchPoints) {
499 QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
500 if (!dtp->isQmlDefined())
503 dtp->setInUse(false);
505 _releasedTouchPoints.clear();
506 _pressedTouchPoints.clear();
507 _movedTouchPoints.clear();
510 void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
512 QQuickTouchPoint *dtp = 0;
513 foreach (QQuickTouchPoint* tp, _touchPrototypes) {
522 dtp = new QQuickTouchPoint(false);
523 dtp->setPointId(p->id());
524 updateTouchPoint(dtp,p);
525 dtp->setPressed(true);
526 _touchPoints.insert(p->id(),dtp);
527 _pressedTouchPoints.append(dtp);
530 void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype)
532 int id = _touchPrototypes.count();
533 prototype->setPointId(id);
534 _touchPrototypes.insert(id, prototype);
537 void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p)
539 //TODO: if !qmlDefined, could bypass setters.
540 // also, should only emit signals after all values have been set
541 dtp->setX(p->pos().x());
542 dtp->setY(p->pos().y());
543 dtp->setPressure(p->pressure());
544 dtp->setArea(p->rect());
545 dtp->setStartX(p->startPos().x());
546 dtp->setStartY(p->startPos().y());
547 dtp->setPreviousX(p->lastPos().x());
548 dtp->setPreviousY(p->lastPos().y());
549 dtp->setSceneX(p->scenePos().x());
550 dtp->setSceneY(p->scenePos().y());
553 void QQuickMultiPointTouchArea::mousePressEvent(QMouseEvent *event)
556 QQuickItem::mousePressEvent(event);
561 setKeepMouseGrab(false);
562 event->setAccepted(true);
565 void QQuickMultiPointTouchArea::mouseMoveEvent(QMouseEvent *event)
568 QQuickItem::mouseMoveEvent(event);
575 void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event)
579 QQuickItem::mouseReleaseEvent(event);
582 QQuickCanvas *c = canvas();
583 if (c && c->mouseGrabberItem() == this)
585 setKeepMouseGrab(false);
588 void QQuickMultiPointTouchArea::ungrab()
590 if (_touchPoints.count()) {
591 QQuickCanvas *c = canvas();
592 if (c && c->mouseGrabberItem() == this) {
594 setKeepMouseGrab(false);
596 setKeepTouchGrab(false);
597 foreach (QObject *obj, _touchPoints)
598 static_cast<QQuickTouchPoint*>(obj)->setPressed(false);
599 emit canceled(_touchPoints.values());
600 emit touchPointsCanceled(_touchPoints.values());
602 foreach (QObject *obj, _touchPoints) {
603 QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
604 if (!dtp->isQmlDefined())
607 dtp->setInUse(false);
609 _touchPoints.clear();
610 emit touchUpdated(QList<QObject*>());
614 void QQuickMultiPointTouchArea::mouseUngrabEvent()
619 void QQuickMultiPointTouchArea::touchUngrabEvent()
624 bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event)
626 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
628 QQuickCanvas *c = canvas();
629 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
630 bool stealThisEvent = _stealMouse;
631 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
632 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
633 event->button(), event->buttons(), event->modifiers());
634 mouseEvent.setAccepted(false);
636 switch (mouseEvent.type()) {
637 case QEvent::MouseMove:
638 mouseMoveEvent(&mouseEvent);
640 case QEvent::MouseButtonPress:
641 mousePressEvent(&mouseEvent);
643 case QEvent::MouseButtonRelease:
644 mouseReleaseEvent(&mouseEvent);
649 grabber = c->mouseGrabberItem();
650 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
653 return stealThisEvent;
655 if (event->type() == QEvent::MouseButtonRelease) {
657 if (c && c->mouseGrabberItem() == this)
659 setKeepMouseGrab(false);
664 bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *event)
666 if (!isEnabled() || !isVisible())
667 return QQuickItem::childMouseEventFilter(i, event);
668 switch (event->type()) {
669 case QEvent::MouseButtonPress:
670 case QEvent::MouseMove:
671 case QEvent::MouseButtonRelease:
672 return sendMouseEvent(static_cast<QMouseEvent *>(event));
674 case QEvent::TouchBegin:
675 case QEvent::TouchUpdate:
676 if (!shouldFilter(event))
678 updateTouchData(event);
680 case QEvent::TouchEnd: {
681 if (!shouldFilter(event))
683 updateTouchData(event);
684 //TODO: verify this behavior
686 setKeepMouseGrab(false);
687 QQuickCanvas *c = canvas();
688 if (c && c->mouseGrabberItem() == this)
690 setKeepTouchGrab(false);
697 return QQuickItem::childMouseEventFilter(i, event);
700 bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
702 QQuickCanvas *c = canvas();
703 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
704 bool disabledItem = grabber && !grabber->isEnabled();
705 bool stealThisEvent = _stealMouse;
706 bool contains = false;
707 if (!stealThisEvent) {
708 switch (event->type()) {
709 case QEvent::MouseButtonPress:
710 case QEvent::MouseMove:
711 case QEvent::MouseButtonRelease: {
712 QMouseEvent *me = static_cast<QMouseEvent*>(event);
713 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
714 contains = myRect.contains(me->windowPos());
717 case QEvent::TouchBegin:
718 case QEvent::TouchUpdate:
719 case QEvent::TouchEnd: {
720 QTouchEvent *te = static_cast<QTouchEvent*>(event);
721 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
722 foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) {
723 if (myRect.contains(point.scenePos())) {
734 if ((stealThisEvent || contains) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {