on b5b99cd help sign in
unity8/plugins/Ubuntu/Gestures/TouchGestureArea.cpp
873 lines 28 kB history raw
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include "TouchGestureArea.h"
19// local
20#include "TouchOwnershipEvent.h"
21#include "TouchRegistry.h"
22#include "UnownedTouchEvent.h"
24#include <QGuiApplication>
25#include <QStyleHints>
26#include <private/qquickwindow_p.h>
28#define TOUCHGESTUREAREA_DEBUG 0
30// TODO - understand more about why we lose touch event releases.
31// Workaround for now is to monitor all the touch points from first touch till
32// all have been released; no matter if we've rejected the gesture.
34namespace {
36struct InternalStatus {
37 enum Status {
38 WaitingForTouch,
39 WaitingForMoreTouches,
40 WaitingForOwnership, //Recognizing,
41 Recognized,
42 WaitingForRejection,
43 Rejected
44 };
45};
47TouchGestureArea::Status internalStatusToGestureStatus(int internalStatus) {
48 switch (internalStatus) {
49 case InternalStatus::WaitingForTouch: return TouchGestureArea::WaitingForTouch;
50 case InternalStatus::WaitingForMoreTouches: return TouchGestureArea::Undecided;
51 case InternalStatus::WaitingForOwnership: return TouchGestureArea::Undecided;
52 case InternalStatus::Recognized: return TouchGestureArea::Recognized;
53 case InternalStatus::WaitingForRejection: return TouchGestureArea::Recognized;
54 case InternalStatus::Rejected: return TouchGestureArea::Rejected;
55 }
56 return TouchGestureArea::WaitingForTouch;
61#if TOUCHGESTUREAREA_DEBUG
62#define tgaDebug(params) qDebug().nospace() << "[TGA(" << qPrintable(objectName()) << ")] " << params
63#include "DebugHelpers.h"
65namespace {
67const char *statusToString(int status)
69 if (status == InternalStatus::WaitingForTouch) {
70 return "WaitingForTouch";
71 } else if (status == InternalStatus::WaitingForMoreTouches) {
72 return "WaitingForMoreTouches";
73 } else if (status == InternalStatus::WaitingForOwnership) {
74 return "WaitingForOwnership";
75 } else if (status == InternalStatus::Rejected) {
76 return "Rejected";
77 } else if (status == InternalStatus::WaitingForRejection) {
78 return "WaitingForRejection";
79 } else {
80 return "Recognized";
81 }
82 return "Unknown";
85QString touchState(Qt::TouchPointState state) {
86 switch (state) {
87 case Qt::TouchPointPressed: return "pressed";
88 case Qt::TouchPointMoved: return "moved";
89 case Qt::TouchPointStationary: return "stationary";
90 case Qt::TouchPointReleased: return "released";
91 break;
92 }
93 return "unknown";
96QString touchesString(const QList<QObject*> touches) {
97 QString str;
98 Q_FOREACH(QObject* object, touches) {
99 GestureTouchPoint* touchPoint = qobject_cast<GestureTouchPoint*>(object);
100 if (touchPoint) {
101 str += QStringLiteral("[%1 @ (%2, %3)], ").arg(touchPoint->id())
102 .arg(touchPoint->x())
103 .arg(touchPoint->y());
104 }
105 }
106 return str;
109QString touchEventString(QTouchEvent* event) {
110 if (!event) return QString();
111 QString str;
112 Q_FOREACH(const auto& touchPoint, event->touchPoints()) {
113 str += QStringLiteral("[%1:%2 @ (%3, %4)], ").arg(touchPoint.id())
114 .arg(touchState(touchPoint.state()))
115 .arg(touchPoint.pos().x())
116 .arg(touchPoint.pos().y());
117 }
118 return str;
122} // namespace {
123#else // TOUCHGESTUREAREA_DEBUG
124#define tgaDebug(params) ((void)0)
125#endif // TOUCHGESTUREAREA_DEBUG
127TouchGestureArea::TouchGestureArea(QQuickItem* parent)
128 : QQuickItem(parent)
129 , m_status(WaitingForTouch)
130 , m_recognitionTimer(nullptr)
131 , m_dragging(false)
132 , m_minimumTouchPoints(1)
133 , m_maximumTouchPoints(INT_MAX)
134 , m_recognitionPeriod(50)
135 , m_releaseRejectPeriod(100)
137 setRecognitionTimer(new UbuntuGestures::Timer(this));
138 m_recognitionTimer->setInterval(m_recognitionPeriod);
139 m_recognitionTimer->setSingleShot(true);
142TouchGestureArea::~TouchGestureArea()
144 clearTouchLists();
145 qDeleteAll(m_liveTouchPoints);
146 m_liveTouchPoints.clear();
147 qDeleteAll(m_cachedTouchPoints);
148 m_cachedTouchPoints.clear();
151bool TouchGestureArea::event(QEvent *event)
153 // Process unowned touch events (handles update/release for incomplete gestures)
154 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
155 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
156 return true;
157 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
158 unownedTouchEvent(static_cast<UnownedTouchEvent *>(event)->touchEvent());
159 return true;
160 }
162 return QQuickItem::event(event);
165void TouchGestureArea::touchOwnershipEvent(TouchOwnershipEvent *event)
167 int touchId = event->touchId();
168 tgaDebug("touchOwnershipEvent - id:" << touchId << ", gained:" << event->gained());
170 if (event->gained()) {
171 grabTouchPoints(QVector<int>() << touchId);
172 m_candidateTouches.remove(touchId);
173 TouchRegistry::instance()->addTouchWatcher(touchId, this);
174 m_watchedTouches.insert(touchId);
176 if (m_watchedTouches.count() >= m_minimumTouchPoints) {
177 setInternalStatus(InternalStatus::Recognized);
178 }
179 } else {
180 rejectGesture();
181 }
184void TouchGestureArea::touchEvent(QTouchEvent *event)
186 if (!isEnabled() || !isVisible()) {
187 tgaDebug(QString("NOT ENABLED touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
188 QQuickItem::touchEvent(event);
189 return;
190 }
192 tgaDebug(QString("touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
194 switch (m_status) {
195 case InternalStatus::WaitingForTouch:
196 touchEvent_waitingForTouch(event);
197 break;
198 case InternalStatus::WaitingForMoreTouches:
199 touchEvent_waitingForMoreTouches(event);
200 break;
201 case InternalStatus::WaitingForOwnership:
202 touchEvent_waitingForOwnership(event);
203 break;
204 case InternalStatus::Recognized:
205 case InternalStatus::WaitingForRejection:
206 touchEvent_recognized(event);
207 break;
208 case InternalStatus::Rejected:
209 touchEvent_rejected(event);
210 break;
211 default: // Recognized:
212 break;
213 }
215 updateTouchPoints(event);
218void TouchGestureArea::touchEvent_waitingForTouch(QTouchEvent *event)
220 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
221 Qt::TouchPointState touchPointState = touchPoint.state();
222 int touchId = touchPoint.id();
224 if (touchPointState == Qt::TouchPointPressed) {
225 if (!m_candidateTouches.contains(touchId)) {
226 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
227 m_candidateTouches.insert(touchId);
228 }
229 }
230 }
231 event->ignore();
233 if (m_candidateTouches.count() > m_maximumTouchPoints) {
234 rejectGesture();
235 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
236 setInternalStatus(InternalStatus::WaitingForOwnership);
238 QSet<int> tmpCandidates(m_candidateTouches);
239 Q_FOREACH(int candidateTouchId, tmpCandidates) {
240 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
241 }
242 // We accept the gesture; so don't pass to lower items
243 event->accept();
244 } else if (m_candidateTouches.count() > 0) {
245 setInternalStatus(InternalStatus::WaitingForMoreTouches);
246 }
249void TouchGestureArea::touchEvent_waitingForMoreTouches(QTouchEvent *event)
251 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
252 Qt::TouchPointState touchPointState = touchPoint.state();
253 int touchId = touchPoint.id();
255 if (touchPointState == Qt::TouchPointPressed) {
256 if (!m_candidateTouches.contains(touchId)) {
257 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
258 m_candidateTouches.insert(touchId);
259 }
260 }
261 }
262 event->ignore();
264 if (m_candidateTouches.count() > m_maximumTouchPoints) {
265 rejectGesture();
266 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
267 setInternalStatus(InternalStatus::WaitingForOwnership);
269 QSet<int> tmpCandidates(m_candidateTouches);
270 Q_FOREACH(int candidateTouchId, tmpCandidates) {
271 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
272 }
273 // We accept the gesture; so don't pass to lower items
274 event->accept();
275 }
278void TouchGestureArea::touchEvent_waitingForOwnership(QTouchEvent *event)
280 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
281 Qt::TouchPointState touchPointState = touchPoint.state();
282 int touchId = touchPoint.id();
284 if (touchPointState == Qt::TouchPointPressed) {
285 if (!m_watchedTouches.contains(touchId)) {
286 TouchRegistry::instance()->addTouchWatcher(touchId, this);
287 m_watchedTouches.insert(touchId);
288 }
289 }
290 }
293void TouchGestureArea::touchEvent_recognized(QTouchEvent *event)
295 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
296 Qt::TouchPointState touchPointState = touchPoint.state();
297 int touchId = touchPoint.id();
299 if (touchPointState == Qt::TouchPointPressed) {
300 if (!m_watchedTouches.contains(touchId)) {
301 TouchRegistry::instance()->addTouchWatcher(touchId, this);
302 m_watchedTouches.insert(touchId);
303 }
304 }
305 }
307 if (m_watchedTouches.count() > m_maximumTouchPoints) {
308 rejectGesture();
309 } else if (m_watchedTouches.count() >= m_minimumTouchPoints &&
310 m_status==InternalStatus::WaitingForRejection) {
311 setInternalStatus(InternalStatus::Recognized);
312 }
315void TouchGestureArea::touchEvent_rejected(QTouchEvent *event)
317 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
318 Qt::TouchPointState touchPointState = touchPoint.state();
319 int touchId = touchPoint.id();
321 if (touchPointState == Qt::TouchPointPressed) {
322 if (!m_watchedTouches.contains(touchId)) {
323 TouchRegistry::instance()->addTouchWatcher(touchId, this);
324 m_watchedTouches.insert(touchId);
325 }
326 }
327 }
330void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent)
332 tgaDebug(QString("unownedTouchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(unownedTouchEvent)));
334 // Only monitor unowned touch events for presses/releases
335 if ((unownedTouchEvent->touchPointStates() & (Qt::TouchPointPressed|Qt::TouchPointReleased)) == 0) {
336 return;
337 }
339 switch (m_status) {
340 case InternalStatus::WaitingForTouch:
341 break;
342 case InternalStatus::WaitingForMoreTouches:
343 unownedTouchEvent_waitingForMoreTouches(unownedTouchEvent);
344 // do nothing
345 break;
346 case InternalStatus::WaitingForOwnership:
347 unownedTouchEvent_waitingForOwnership(unownedTouchEvent);
348 break;
349 case InternalStatus::Recognized:
350 case InternalStatus::WaitingForRejection:
351 unownedTouchEvent_recognised(unownedTouchEvent);
352 break;
353 case InternalStatus::Rejected:
354 unownedTouchEvent_rejected(unownedTouchEvent);
355 break;
356 default:
357 break;
358 }
360 updateTouchPoints(unownedTouchEvent);
363void TouchGestureArea::unownedTouchEvent_waitingForMoreTouches(QTouchEvent *event)
365 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
366 Qt::TouchPointState touchPointState = touchPoint.state();
367 int touchId = touchPoint.id();
369 if (touchPointState == Qt::TouchPointReleased) {
370 if (m_candidateTouches.contains(touchId)) {
371 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
372 m_candidateTouches.remove(touchId);
373 }
374 }
375 }
377 if (m_candidateTouches.isEmpty()) {
378 setInternalStatus(InternalStatus::WaitingForTouch);
379 }
382void TouchGestureArea::unownedTouchEvent_waitingForOwnership(QTouchEvent *event)
384 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
385 Qt::TouchPointState touchPointState = touchPoint.state();
386 int touchId = touchPoint.id();
388 if (touchPointState == Qt::TouchPointReleased) {
389 if (m_candidateTouches.contains(touchId)) {
390 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
391 m_candidateTouches.remove(touchId);
392 }
393 if (m_watchedTouches.contains(touchId)) {
394 m_watchedTouches.remove(touchId);
395 }
396 }
397 }
399 if (m_candidateTouches.count() + m_watchedTouches.count() == 0) {
400 setInternalStatus(InternalStatus::WaitingForTouch);
401 }
404void TouchGestureArea::unownedTouchEvent_recognised(QTouchEvent *event)
406 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
407 Qt::TouchPointState touchPointState = touchPoint.state();
408 int touchId = touchPoint.id();
410 if (touchPointState == Qt::TouchPointReleased) {
411 if (m_watchedTouches.contains(touchId)) {
412 m_watchedTouches.remove(touchId);
413 }
414 }
415 }
417 if (m_watchedTouches.count() < m_minimumTouchPoints && m_status==InternalStatus::Recognized) {
418 setInternalStatus(InternalStatus::WaitingForRejection);
419 }
422void TouchGestureArea::unownedTouchEvent_rejected(QTouchEvent *event)
424 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
425 Qt::TouchPointState touchPointState = touchPoint.state();
426 int touchId = touchPoint.id();
428 if (touchPointState == Qt::TouchPointPressed) {
429 if (!m_watchedTouches.contains(touchId)) {
430 TouchRegistry::instance()->addTouchWatcher(touchId, this);
431 m_watchedTouches.insert(touchId);
432 }
433 }
434 if (touchPointState == Qt::TouchPointReleased) {
435 if (m_watchedTouches.contains(touchId)) {
436 m_watchedTouches.remove(touchId);
437 }
438 }
439 }
441 if (m_watchedTouches.isEmpty()) {
442 setInternalStatus(InternalStatus::WaitingForTouch);
443 }
446void TouchGestureArea::updateTouchPoints(QTouchEvent *touchEvent)
448 bool added = false;
449 bool ended = false;
450 bool moved = false;
452 const int dragThreshold = qApp->styleHints()->startDragDistance();
453 const int dragVelocity = qApp->styleHints()->startDragVelocity();
455 clearTouchLists();
456 bool updateable = m_status != InternalStatus::WaitingForRejection;
458 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, touchEvent->touchPoints()) {
459 Qt::TouchPointState touchPointState = touchPoint.state();
460 int touchId = touchPoint.id();
462 if (touchPointState & Qt::TouchPointReleased) {
463 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchId);
464 if (!gtp) continue;
466 gtp->setPos(touchPoint.pos());
467 gtp->setPressed(false);
468 m_releasedTouchPoints.append(gtp);
469 m_liveTouchPoints.remove(touchId);
471 if (updateable) {
472 if (m_cachedTouchPoints.contains(touchId)) {
473 GestureTouchPoint* cachedPoint = m_cachedTouchPoints.take(touchId);
474 cachedPoint->deleteLater();
475 }
476 }
477 ended = true;
478 } else {
479 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchPoint.id(), nullptr);
480 if (!gtp) {
481 gtp = addTouchPoint(&touchPoint);
482 m_pressedTouchPoints.append(gtp);
484 if (updateable) {
485 if (m_cachedTouchPoints.contains(touchId)) {
486 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
487 } else {
488 m_cachedTouchPoints[touchId] = new GestureTouchPoint(*gtp);
489 }
490 }
491 added = true;
492 } else if (touchPointState & Qt::TouchPointMoved) {
493 gtp->setPos(touchPoint.pos());
494 m_movedTouchPoints.append(gtp);
495 moved = true;
497 const QPointF &currentPos = touchPoint.scenePos();
498 const QPointF &startPos = touchPoint.startScenePos();
500 bool overDragThreshold = false;
501 bool supportsVelocity = (touchEvent->device()->capabilities() & QTouchDevice::Velocity) && dragVelocity;
502 overDragThreshold |= qAbs(currentPos.x() - startPos.x()) > dragThreshold ||
503 qAbs(currentPos.y() - startPos.y()) > dragThreshold;
504 if (supportsVelocity) {
505 QVector2D velocityVec = touchPoint.velocity();
506 overDragThreshold |= qAbs(velocityVec.x()) > dragVelocity;
507 overDragThreshold |= qAbs(velocityVec.y()) > dragVelocity;
508 }
510 if (overDragThreshold) {
511 gtp->setDragging(true);
512 }
514 if (updateable) {
515 if (m_cachedTouchPoints.contains(touchId)) {
516 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
517 if (overDragThreshold) {
518 m_cachedTouchPoints[touchId]->setDragging(true);
519 }
520 }
521 }
522 }
523 }
524 }
526 if (updateable) {
527 if (!dragging() && m_status == InternalStatus::Recognized) {
528 bool allWantDrag = !m_liveTouchPoints.isEmpty();
529 Q_FOREACH(const auto &point, m_liveTouchPoints) {
530 allWantDrag &= point->dragging();
531 }
532 // only dragging if all points are dragging.
533 if (allWantDrag) {
534 setDragging(true);
535 }
536 }
538 if (ended) {
539 if (m_liveTouchPoints.isEmpty()) {
540 if (!dragging()) Q_EMIT clicked();
541 setDragging(false);
542 }
543 tgaDebug("Released " << touchesString(m_releasedTouchPoints));
544 Q_EMIT released(m_releasedTouchPoints);
545 }
546 if (added) {
547 tgaDebug("Pressed " << touchesString(m_pressedTouchPoints));
548 Q_EMIT pressed(m_pressedTouchPoints);
549 }
550 if (moved) {
551 tgaDebug("Updated " << touchesString(m_movedTouchPoints));
552 Q_EMIT updated(m_movedTouchPoints);
553 }
554 if (added || ended || moved) {
555 Q_EMIT touchPointsUpdated();
556 }
557 }
560void TouchGestureArea::clearTouchLists()
562 Q_FOREACH (QObject *gtp, m_releasedTouchPoints) {
563 delete gtp;
564 }
565 m_releasedTouchPoints.clear();
566 m_pressedTouchPoints.clear();
567 m_movedTouchPoints.clear();
570void TouchGestureArea::setInternalStatus(uint newStatus)
572 if (newStatus == m_status)
573 return;
575 uint oldStatus = m_status;
577 m_status = newStatus;
578 Q_EMIT statusChanged(status());
580 if (oldStatus == InternalStatus::WaitingForMoreTouches || oldStatus == InternalStatus::WaitingForRejection) {
581 m_recognitionTimer->stop();
582 }
584 tgaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
586 switch (newStatus) {
587 case InternalStatus::WaitingForTouch:
588 resyncCachedTouchPoints();
589 break;
590 case InternalStatus::WaitingForMoreTouches:
591 m_recognitionTimer->start(m_recognitionPeriod);
592 break;
593 case InternalStatus::Recognized:
594 resyncCachedTouchPoints();
595 break;
596 case InternalStatus::WaitingForRejection:
597 m_recognitionTimer->start(m_releaseRejectPeriod);
598 break;
599 case InternalStatus::Rejected:
600 resyncCachedTouchPoints();
601 break;
602 default:
603 // no-op
604 break;
605 }
608void TouchGestureArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
610 int interval = 0;
611 bool timerWasRunning = false;
612 bool wasSingleShot = false;
614 // can be null when called from the constructor
615 if (m_recognitionTimer) {
616 interval = m_recognitionTimer->interval();
617 timerWasRunning = m_recognitionTimer->isRunning();
618 if (m_recognitionTimer->parent() == this) {
619 delete m_recognitionTimer;
620 }
621 }
623 m_recognitionTimer = timer;
624 timer->setInterval(interval);
625 timer->setSingleShot(wasSingleShot);
626 connect(timer, SIGNAL(timeout()),
627 this, SLOT(rejectGesture()));
628 if (timerWasRunning) {
629 m_recognitionTimer->start();
630 }
633int TouchGestureArea::status() const
635 return internalStatusToGestureStatus(m_status);
638bool TouchGestureArea::dragging() const
640 return m_dragging;
643QQmlListProperty<GestureTouchPoint> TouchGestureArea::touchPoints()
645 return QQmlListProperty<GestureTouchPoint>(this,
646 nullptr,
647 TouchGestureArea::touchPoint_count,
648 TouchGestureArea::touchPoint_at);
651int TouchGestureArea::minimumTouchPoints() const
653 return m_minimumTouchPoints;
656void TouchGestureArea::setMinimumTouchPoints(int value)
658 if (m_minimumTouchPoints != value) {
659 m_minimumTouchPoints = value;
660 Q_EMIT minimumTouchPointsChanged(value);
661 }
664int TouchGestureArea::maximumTouchPoints() const
666 return m_maximumTouchPoints;
669void TouchGestureArea::setMaximumTouchPoints(int value)
671 if (m_maximumTouchPoints != value) {
672 m_maximumTouchPoints = value;
673 Q_EMIT maximumTouchPointsChanged(value);
674 }
677int TouchGestureArea::recognitionPeriod() const
679 return m_recognitionPeriod;
682void TouchGestureArea::setRecognitionPeriod(int value)
684 if (value != m_recognitionPeriod) {
685 m_recognitionPeriod = value;
686 Q_EMIT recognitionPeriodChanged(value);
687 }
690int TouchGestureArea::releaseRejectPeriod() const
692 return m_releaseRejectPeriod;
695void TouchGestureArea::setReleaseRejectPeriod(int value)
697 if (value != m_releaseRejectPeriod) {
698 m_releaseRejectPeriod = value;
699 Q_EMIT releaseRejectPeriodChanged(value);
700 }
703void TouchGestureArea::rejectGesture()
705 tgaDebug("rejectGesture");
706 ungrabTouchPoints();
708 Q_FOREACH(int touchId, m_candidateTouches) {
709 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
710 }
712 // Monitor the candidates
713 Q_FOREACH(int touchId, m_candidateTouches) {
714 TouchRegistry::instance()->addTouchWatcher(touchId, this);
715 m_watchedTouches.insert(touchId);
716 }
717 m_candidateTouches.clear();
719 if (m_watchedTouches.isEmpty()) {
720 setInternalStatus(InternalStatus::WaitingForTouch);
721 } else {
722 setInternalStatus(InternalStatus::Rejected);
723 }
726void TouchGestureArea::resyncCachedTouchPoints()
728 clearTouchLists();
730 bool added = false;
731 bool ended = false;
732 bool moved = false;
733 bool wantsDrag = false;
735 // list of deletes
736 QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints);
737 while(removeIter.hasNext()) {
738 removeIter.next();
739 if (!m_liveTouchPoints.contains(removeIter.key())) {
740 m_releasedTouchPoints.append(removeIter.value());
741 removeIter.remove();
742 ended = true;
743 }
744 }
746 // list of adds/moves
747 Q_FOREACH(GestureTouchPoint* touchPoint, m_liveTouchPoints) {
748 if (m_cachedTouchPoints.contains(touchPoint->id())) {
749 GestureTouchPoint* cachedPoint = m_cachedTouchPoints[touchPoint->id()];
751 if (*cachedPoint != *touchPoint) {
752 *cachedPoint = *touchPoint;
753 m_movedTouchPoints.append(touchPoint);
754 moved = true;
755 }
756 } else {
757 m_cachedTouchPoints.insert(touchPoint->id(), new GestureTouchPoint(*touchPoint));
758 m_pressedTouchPoints.append(touchPoint);
759 added = true;
760 }
761 }
763 if (wantsDrag && !dragging()) {
764 setDragging(true);
765 }
767 if (ended) {
768 if (m_cachedTouchPoints.isEmpty()) {
769 if (!dragging()) Q_EMIT clicked();
770 setDragging(false);
771 }
772 tgaDebug("Cached Release " << touchesString(m_releasedTouchPoints));
773 Q_EMIT released(m_releasedTouchPoints);
774 }
775 if (added) {
776 tgaDebug("Cached Press " << touchesString(m_pressedTouchPoints));
777 Q_EMIT pressed(m_pressedTouchPoints);
778 }
779 if (moved) {
780 tgaDebug("Cached Update " << touchesString(m_movedTouchPoints));
781 Q_EMIT updated(m_movedTouchPoints);
782 }
783 if (added || ended || moved) Q_EMIT touchPointsUpdated();
786int TouchGestureArea::touchPoint_count(QQmlListProperty<GestureTouchPoint> *list)
788 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
789 return q->m_cachedTouchPoints.count();
792GestureTouchPoint *TouchGestureArea::touchPoint_at(QQmlListProperty<GestureTouchPoint> *list, int index)
794 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
795 return (q->m_cachedTouchPoints.begin()+index).value();
798GestureTouchPoint* TouchGestureArea::addTouchPoint(QTouchEvent::TouchPoint const* tp)
800 GestureTouchPoint* gtp = new GestureTouchPoint();
801 gtp->setId(tp->id());
802 gtp->setPressed(true);
803 gtp->setPos(tp->pos());
804 m_liveTouchPoints.insert(tp->id(), gtp);
805 return gtp;
808void TouchGestureArea::itemChange(ItemChange change, const ItemChangeData &value)
810 if (change == QQuickItem::ItemSceneChange) {
811 if (value.window != nullptr) {
812 value.window->installEventFilter(TouchRegistry::instance());
813 }
814 }
817void TouchGestureArea::setDragging(bool dragging)
819 if (m_dragging == dragging)
820 return;
822 tgaDebug("setDragging " << dragging);
824 m_dragging = dragging;
825 Q_EMIT draggingChanged(m_dragging);
828void GestureTouchPoint::setId(int id)
830 if (m_id == id)
831 return;
832 m_id = id;
833 Q_EMIT idChanged();
836void GestureTouchPoint::setPressed(bool pressed)
838 if (m_pressed == pressed)
839 return;
840 m_pressed = pressed;
841 Q_EMIT pressedChanged();
844void GestureTouchPoint::setX(qreal x)
846 if (m_x == x)
847 return;
848 m_x = x;
849 Q_EMIT xChanged();
852void GestureTouchPoint::setY(qreal y)
854 if (m_y == y)
855 return;
856 m_y = y;
857 Q_EMIT yChanged();
860void GestureTouchPoint::setDragging(bool dragging)
862 if (m_dragging == dragging)
863 return;
865 m_dragging = dragging;
866 Q_EMIT draggingChanged();
869void GestureTouchPoint::setPos(const QPointF &pos)
871 setX(pos.x());
872 setY(pos.y());