on 18deb76 help sign in
unity8/plugins/Ubuntu/Gestures/TouchGestureArea.cpp
876 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#include <UbuntuGestures/TouchOwnershipEvent>
20#include <UbuntuGestures/TouchRegistry>
21#include <UbuntuGestures/UnownedTouchEvent>
22// #include "TouchRegistry.h"
23// #include "UnownedTouchEvent.h"
25#include <QGuiApplication>
26#include <QStyleHints>
27#include <private/qquickwindow_p.h>
29#define TOUCHGESTUREAREA_DEBUG 0
31// TODO - understand more about why we lose touch event releases.
32// Workaround for now is to monitor all the touch points from first touch till
33// all have been released; no matter if we've rejected the gesture.
35namespace {
37struct InternalStatus {
38 enum Status {
39 WaitingForTouch,
40 WaitingForMoreTouches,
41 WaitingForOwnership, //Recognizing,
42 Recognized,
43 WaitingForRejection,
44 Rejected
45 };
46};
48TouchGestureArea::Status internalStatusToGestureStatus(int internalStatus) {
49 switch (internalStatus) {
50 case InternalStatus::WaitingForTouch: return TouchGestureArea::WaitingForTouch;
51 case InternalStatus::WaitingForMoreTouches: return TouchGestureArea::Undecided;
52 case InternalStatus::WaitingForOwnership: return TouchGestureArea::Undecided;
53 case InternalStatus::Recognized: return TouchGestureArea::Recognized;
54 case InternalStatus::WaitingForRejection: return TouchGestureArea::Recognized;
55 case InternalStatus::Rejected: return TouchGestureArea::Rejected;
56 }
57 return TouchGestureArea::WaitingForTouch;
62#if TOUCHGESTUREAREA_DEBUG
63#define tgaDebug(params) qDebug().nospace() << "[TGA(" << qPrintable(objectName()) << ")] " << params
64#include "DebugHelpers.h"
66namespace {
68const char *statusToString(int status)
70 if (status == InternalStatus::WaitingForTouch) {
71 return "WaitingForTouch";
72 } else if (status == InternalStatus::WaitingForMoreTouches) {
73 return "WaitingForMoreTouches";
74 } else if (status == InternalStatus::WaitingForOwnership) {
75 return "WaitingForOwnership";
76 } else if (status == InternalStatus::Rejected) {
77 return "Rejected";
78 } else if (status == InternalStatus::WaitingForRejection) {
79 return "WaitingForRejection";
80 } else {
81 return "Recognized";
82 }
83 return "Unknown";
86QString touchState(Qt::TouchPointState state) {
87 switch (state) {
88 case Qt::TouchPointPressed: return "pressed";
89 case Qt::TouchPointMoved: return "moved";
90 case Qt::TouchPointStationary: return "stationary";
91 case Qt::TouchPointReleased: return "released";
92 break;
93 }
94 return "unknown";
97QString touchesString(const QList<QObject*> touches) {
98 QString str;
99 Q_FOREACH(QObject* object, touches) {
100 GestureTouchPoint* touchPoint = qobject_cast<GestureTouchPoint*>(object);
101 if (touchPoint) {
102 str += QStringLiteral("[%1 @ (%2, %3)], ").arg(touchPoint->id())
103 .arg(touchPoint->x())
104 .arg(touchPoint->y());
105 }
106 }
107 return str;
110QString touchEventString(QTouchEvent* event) {
111 if (!event) return QString();
112 QString str;
113 Q_FOREACH(const auto& touchPoint, event->touchPoints()) {
114 str += QStringLiteral("[%1:%2 @ (%3, %4)], ").arg(touchPoint.id())
115 .arg(touchState(touchPoint.state()))
116 .arg(touchPoint.pos().x())
117 .arg(touchPoint.pos().y());
118 }
119 return str;
123} // namespace {
124#else // TOUCHGESTUREAREA_DEBUG
125#define tgaDebug(params) ((void)0)
126#endif // TOUCHGESTUREAREA_DEBUG
128TouchGestureArea::TouchGestureArea(QQuickItem* parent)
129 : QQuickItem(parent)
130 , m_status(WaitingForTouch)
131 , m_recognitionTimer(nullptr)
132 , m_dragging(false)
133 , m_minimumTouchPoints(1)
134 , m_maximumTouchPoints(INT_MAX)
135 , m_recognitionPeriod(50)
136 , m_releaseRejectPeriod(100)
138 setRecognitionTimer(new UbuntuGestures::Timer(this));
139 m_recognitionTimer->setInterval(m_recognitionPeriod);
140 m_recognitionTimer->setSingleShot(true);
143TouchGestureArea::~TouchGestureArea()
145 clearTouchLists();
146 qDeleteAll(m_liveTouchPoints);
147 m_liveTouchPoints.clear();
148 qDeleteAll(m_cachedTouchPoints);
149 m_cachedTouchPoints.clear();
152bool TouchGestureArea::event(QEvent *event)
154 // Process unowned touch events (handles update/release for incomplete gestures)
155 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
156 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
157 return true;
158 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
159 unownedTouchEvent(static_cast<UnownedTouchEvent *>(event)->touchEvent());
160 return true;
161 }
163 return QQuickItem::event(event);
166void TouchGestureArea::touchOwnershipEvent(TouchOwnershipEvent *event)
168 int touchId = event->touchId();
169 tgaDebug("touchOwnershipEvent - id:" << touchId << ", gained:" << event->gained());
171 if (event->gained()) {
172 grabTouchPoints(QVector<int>() << touchId);
173 m_candidateTouches.remove(touchId);
174 TouchRegistry::instance()->addTouchWatcher(touchId, this);
175 m_watchedTouches.insert(touchId);
177 if (m_watchedTouches.count() >= m_minimumTouchPoints) {
178 setInternalStatus(InternalStatus::Recognized);
179 }
180 } else {
181 rejectGesture();
182 }
185void TouchGestureArea::touchEvent(QTouchEvent *event)
187 if (!isEnabled() || !isVisible()) {
188 tgaDebug(QString("NOT ENABLED touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
189 QQuickItem::touchEvent(event);
190 return;
191 }
193 tgaDebug(QString("touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
195 switch (m_status) {
196 case InternalStatus::WaitingForTouch:
197 touchEvent_waitingForTouch(event);
198 break;
199 case InternalStatus::WaitingForMoreTouches:
200 touchEvent_waitingForMoreTouches(event);
201 break;
202 case InternalStatus::WaitingForOwnership:
203 touchEvent_waitingForOwnership(event);
204 break;
205 case InternalStatus::Recognized:
206 case InternalStatus::WaitingForRejection:
207 touchEvent_recognized(event);
208 break;
209 case InternalStatus::Rejected:
210 touchEvent_rejected(event);
211 break;
212 default: // Recognized:
213 break;
214 }
216 updateTouchPoints(event);
219void TouchGestureArea::touchEvent_waitingForTouch(QTouchEvent *event)
221 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
222 Qt::TouchPointState touchPointState = touchPoint.state();
223 int touchId = touchPoint.id();
225 if (touchPointState == Qt::TouchPointPressed) {
226 if (!m_candidateTouches.contains(touchId)) {
227 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
228 m_candidateTouches.insert(touchId);
229 }
230 }
231 }
232 event->ignore();
234 if (m_candidateTouches.count() > m_maximumTouchPoints) {
235 rejectGesture();
236 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
237 setInternalStatus(InternalStatus::WaitingForOwnership);
239 QSet<int> tmpCandidates(m_candidateTouches);
240 Q_FOREACH(int candidateTouchId, tmpCandidates) {
241 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
242 }
243 // We accept the gesture; so don't pass to lower items
244 event->accept();
245 } else if (m_candidateTouches.count() > 0) {
246 setInternalStatus(InternalStatus::WaitingForMoreTouches);
247 }
250void TouchGestureArea::touchEvent_waitingForMoreTouches(QTouchEvent *event)
252 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
253 Qt::TouchPointState touchPointState = touchPoint.state();
254 int touchId = touchPoint.id();
256 if (touchPointState == Qt::TouchPointPressed) {
257 if (!m_candidateTouches.contains(touchId)) {
258 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
259 m_candidateTouches.insert(touchId);
260 }
261 }
262 }
263 event->ignore();
265 if (m_candidateTouches.count() > m_maximumTouchPoints) {
266 rejectGesture();
267 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
268 setInternalStatus(InternalStatus::WaitingForOwnership);
270 QSet<int> tmpCandidates(m_candidateTouches);
271 Q_FOREACH(int candidateTouchId, tmpCandidates) {
272 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
273 }
274 // We accept the gesture; so don't pass to lower items
275 event->accept();
276 }
279void TouchGestureArea::touchEvent_waitingForOwnership(QTouchEvent *event)
281 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
282 Qt::TouchPointState touchPointState = touchPoint.state();
283 int touchId = touchPoint.id();
285 if (touchPointState == Qt::TouchPointPressed) {
286 if (!m_watchedTouches.contains(touchId)) {
287 TouchRegistry::instance()->addTouchWatcher(touchId, this);
288 m_watchedTouches.insert(touchId);
289 }
290 }
291 }
294void TouchGestureArea::touchEvent_recognized(QTouchEvent *event)
296 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
297 Qt::TouchPointState touchPointState = touchPoint.state();
298 int touchId = touchPoint.id();
300 if (touchPointState == Qt::TouchPointPressed) {
301 if (!m_watchedTouches.contains(touchId)) {
302 TouchRegistry::instance()->addTouchWatcher(touchId, this);
303 m_watchedTouches.insert(touchId);
304 }
305 }
306 }
308 if (m_watchedTouches.count() > m_maximumTouchPoints) {
309 rejectGesture();
310 } else if (m_watchedTouches.count() >= m_minimumTouchPoints &&
311 m_status==InternalStatus::WaitingForRejection) {
312 setInternalStatus(InternalStatus::Recognized);
313 }
316void TouchGestureArea::touchEvent_rejected(QTouchEvent *event)
318 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
319 Qt::TouchPointState touchPointState = touchPoint.state();
320 int touchId = touchPoint.id();
322 if (touchPointState == Qt::TouchPointPressed) {
323 if (!m_watchedTouches.contains(touchId)) {
324 TouchRegistry::instance()->addTouchWatcher(touchId, this);
325 m_watchedTouches.insert(touchId);
326 }
327 }
328 }
331void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent)
333 tgaDebug(QString("unownedTouchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(unownedTouchEvent)));
335 // Only monitor unowned touch events for presses/releases
336 if ((unownedTouchEvent->touchPointStates() & (Qt::TouchPointPressed|Qt::TouchPointReleased)) == 0) {
337 return;
338 }
340 switch (m_status) {
341 case InternalStatus::WaitingForTouch:
342 break;
343 case InternalStatus::WaitingForMoreTouches:
344 unownedTouchEvent_waitingForMoreTouches(unownedTouchEvent);
345 // do nothing
346 break;
347 case InternalStatus::WaitingForOwnership:
348 unownedTouchEvent_waitingForOwnership(unownedTouchEvent);
349 break;
350 case InternalStatus::Recognized:
351 case InternalStatus::WaitingForRejection:
352 unownedTouchEvent_recognised(unownedTouchEvent);
353 break;
354 case InternalStatus::Rejected:
355 unownedTouchEvent_rejected(unownedTouchEvent);
356 break;
357 default:
358 break;
359 }
361 updateTouchPoints(unownedTouchEvent);
364void TouchGestureArea::unownedTouchEvent_waitingForMoreTouches(QTouchEvent *event)
366 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
367 Qt::TouchPointState touchPointState = touchPoint.state();
368 int touchId = touchPoint.id();
370 if (touchPointState == Qt::TouchPointReleased) {
371 if (m_candidateTouches.contains(touchId)) {
372 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
373 m_candidateTouches.remove(touchId);
374 }
375 }
376 }
378 if (m_candidateTouches.isEmpty()) {
379 setInternalStatus(InternalStatus::WaitingForTouch);
380 }
383void TouchGestureArea::unownedTouchEvent_waitingForOwnership(QTouchEvent *event)
385 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
386 Qt::TouchPointState touchPointState = touchPoint.state();
387 int touchId = touchPoint.id();
389 if (touchPointState == Qt::TouchPointReleased) {
390 if (m_candidateTouches.contains(touchId)) {
391 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
392 m_candidateTouches.remove(touchId);
393 }
394 if (m_watchedTouches.contains(touchId)) {
395 m_watchedTouches.remove(touchId);
396 }
397 }
398 }
400 if (m_candidateTouches.count() + m_watchedTouches.count() == 0) {
401 setInternalStatus(InternalStatus::WaitingForTouch);
402 }
405void TouchGestureArea::unownedTouchEvent_recognised(QTouchEvent *event)
407 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
408 Qt::TouchPointState touchPointState = touchPoint.state();
409 int touchId = touchPoint.id();
411 if (touchPointState == Qt::TouchPointReleased) {
412 if (m_watchedTouches.contains(touchId)) {
413 m_watchedTouches.remove(touchId);
414 }
415 }
416 }
418 if (m_watchedTouches.count() < m_minimumTouchPoints && m_status==InternalStatus::Recognized) {
419 setInternalStatus(InternalStatus::WaitingForRejection);
420 }
423void TouchGestureArea::unownedTouchEvent_rejected(QTouchEvent *event)
425 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
426 Qt::TouchPointState touchPointState = touchPoint.state();
427 int touchId = touchPoint.id();
429 if (touchPointState == Qt::TouchPointPressed) {
430 if (!m_watchedTouches.contains(touchId)) {
431 TouchRegistry::instance()->addTouchWatcher(touchId, this);
432 m_watchedTouches.insert(touchId);
433 }
434 }
435 if (touchPointState == Qt::TouchPointReleased) {
436 if (m_watchedTouches.contains(touchId)) {
437 m_watchedTouches.remove(touchId);
438 }
439 }
440 }
442 if (m_watchedTouches.isEmpty()) {
443 setInternalStatus(InternalStatus::WaitingForTouch);
444 }
447void TouchGestureArea::updateTouchPoints(QTouchEvent *touchEvent)
449 bool added = false;
450 bool ended = false;
451 bool moved = false;
453 const int dragThreshold = qApp->styleHints()->startDragDistance();
454 const int dragVelocity = qApp->styleHints()->startDragVelocity();
456 clearTouchLists();
457 bool updateable = m_status != InternalStatus::WaitingForRejection;
459 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, touchEvent->touchPoints()) {
460 Qt::TouchPointState touchPointState = touchPoint.state();
461 int touchId = touchPoint.id();
463 if (touchPointState & Qt::TouchPointReleased) {
464 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchId);
465 if (!gtp) continue;
467 gtp->setPos(touchPoint.pos());
468 gtp->setPressed(false);
469 m_releasedTouchPoints.append(gtp);
470 m_liveTouchPoints.remove(touchId);
472 if (updateable) {
473 if (m_cachedTouchPoints.contains(touchId)) {
474 GestureTouchPoint* cachedPoint = m_cachedTouchPoints.take(touchId);
475 cachedPoint->deleteLater();
476 }
477 }
478 ended = true;
479 } else {
480 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchPoint.id(), nullptr);
481 if (!gtp) {
482 gtp = addTouchPoint(&touchPoint);
483 m_pressedTouchPoints.append(gtp);
485 if (updateable) {
486 if (m_cachedTouchPoints.contains(touchId)) {
487 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
488 } else {
489 m_cachedTouchPoints[touchId] = new GestureTouchPoint(*gtp);
490 }
491 }
492 added = true;
493 } else if (touchPointState & Qt::TouchPointMoved) {
494 gtp->setPos(touchPoint.pos());
495 m_movedTouchPoints.append(gtp);
496 moved = true;
498 const QPointF &currentPos = touchPoint.scenePos();
499 const QPointF &startPos = touchPoint.startScenePos();
501 bool overDragThreshold = false;
502 bool supportsVelocity = (touchEvent->device()->capabilities() & QTouchDevice::Velocity) && dragVelocity;
503 overDragThreshold |= qAbs(currentPos.x() - startPos.x()) > dragThreshold ||
504 qAbs(currentPos.y() - startPos.y()) > dragThreshold;
505 if (supportsVelocity) {
506 QVector2D velocityVec = touchPoint.velocity();
507 overDragThreshold |= qAbs(velocityVec.x()) > dragVelocity;
508 overDragThreshold |= qAbs(velocityVec.y()) > dragVelocity;
509 }
511 if (overDragThreshold) {
512 gtp->setDragging(true);
513 }
515 if (updateable) {
516 if (m_cachedTouchPoints.contains(touchId)) {
517 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
518 if (overDragThreshold) {
519 m_cachedTouchPoints[touchId]->setDragging(true);
520 }
521 }
522 }
523 }
524 }
525 }
527 if (updateable) {
528 if (!dragging() && m_status == InternalStatus::Recognized) {
529 bool allWantDrag = !m_liveTouchPoints.isEmpty();
530 Q_FOREACH(const auto &point, m_liveTouchPoints) {
531 allWantDrag &= point->dragging();
532 }
533 // only dragging if all points are dragging.
534 if (allWantDrag) {
535 setDragging(true);
536 }
537 }
539 if (ended) {
540 if (m_liveTouchPoints.isEmpty()) {
541 if (!dragging()) Q_EMIT clicked();
542 setDragging(false);
543 }
544 tgaDebug("Released " << touchesString(m_releasedTouchPoints));
545 Q_EMIT released(m_releasedTouchPoints);
546 }
547 if (added) {
548 tgaDebug("Pressed " << touchesString(m_pressedTouchPoints));
549 Q_EMIT pressed(m_pressedTouchPoints);
550 }
551 if (moved) {
552 tgaDebug("Updated " << touchesString(m_movedTouchPoints));
553 Q_EMIT updated(m_movedTouchPoints);
554 }
555 if (added || ended || moved) {
556 Q_EMIT touchPointsUpdated();
557 }
558 }
561void TouchGestureArea::clearTouchLists()
563 Q_FOREACH (QObject *gtp, m_releasedTouchPoints) {
564 delete gtp;
565 }
566 m_releasedTouchPoints.clear();
567 m_pressedTouchPoints.clear();
568 m_movedTouchPoints.clear();
571void TouchGestureArea::setInternalStatus(uint newStatus)
573 if (newStatus == m_status)
574 return;
576 uint oldStatus = m_status;
578 m_status = newStatus;
579 Q_EMIT statusChanged(status());
581 if (oldStatus == InternalStatus::WaitingForMoreTouches || oldStatus == InternalStatus::WaitingForRejection) {
582 m_recognitionTimer->stop();
583 }
585 tgaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
587 switch (newStatus) {
588 case InternalStatus::WaitingForTouch:
589 resyncCachedTouchPoints();
590 break;
591 case InternalStatus::WaitingForMoreTouches:
592 m_recognitionTimer->setInterval(m_recognitionPeriod);
593 m_recognitionTimer->start();
594 break;
595 case InternalStatus::Recognized:
596 resyncCachedTouchPoints();
597 break;
598 case InternalStatus::WaitingForRejection:
599 m_recognitionTimer->setInterval(m_releaseRejectPeriod);
600 m_recognitionTimer->start();
601 break;
602 case InternalStatus::Rejected:
603 resyncCachedTouchPoints();
604 break;
605 default:
606 // no-op
607 break;
608 }
611void TouchGestureArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
613 int interval = 0;
614 bool timerWasRunning = false;
615 bool wasSingleShot = false;
617 // can be null when called from the constructor
618 if (m_recognitionTimer) {
619 interval = m_recognitionTimer->interval();
620 timerWasRunning = m_recognitionTimer->isRunning();
621 if (m_recognitionTimer->parent() == this) {
622 delete m_recognitionTimer;
623 }
624 }
626 m_recognitionTimer = timer;
627 timer->setInterval(interval);
628 timer->setSingleShot(wasSingleShot);
629 connect(timer, SIGNAL(timeout()),
630 this, SLOT(rejectGesture()));
631 if (timerWasRunning) {
632 m_recognitionTimer->start();
633 }
636int TouchGestureArea::status() const
638 return internalStatusToGestureStatus(m_status);
641bool TouchGestureArea::dragging() const
643 return m_dragging;
646QQmlListProperty<GestureTouchPoint> TouchGestureArea::touchPoints()
648 return QQmlListProperty<GestureTouchPoint>(this,
649 nullptr,
650 TouchGestureArea::touchPoint_count,
651 TouchGestureArea::touchPoint_at);
654int TouchGestureArea::minimumTouchPoints() const
656 return m_minimumTouchPoints;
659void TouchGestureArea::setMinimumTouchPoints(int value)
661 if (m_minimumTouchPoints != value) {
662 m_minimumTouchPoints = value;
663 Q_EMIT minimumTouchPointsChanged(value);
664 }
667int TouchGestureArea::maximumTouchPoints() const
669 return m_maximumTouchPoints;
672void TouchGestureArea::setMaximumTouchPoints(int value)
674 if (m_maximumTouchPoints != value) {
675 m_maximumTouchPoints = value;
676 Q_EMIT maximumTouchPointsChanged(value);
677 }
680int TouchGestureArea::recognitionPeriod() const
682 return m_recognitionPeriod;
685void TouchGestureArea::setRecognitionPeriod(int value)
687 if (value != m_recognitionPeriod) {
688 m_recognitionPeriod = value;
689 Q_EMIT recognitionPeriodChanged(value);
690 }
693int TouchGestureArea::releaseRejectPeriod() const
695 return m_releaseRejectPeriod;
698void TouchGestureArea::setReleaseRejectPeriod(int value)
700 if (value != m_releaseRejectPeriod) {
701 m_releaseRejectPeriod = value;
702 Q_EMIT releaseRejectPeriodChanged(value);
703 }
706void TouchGestureArea::rejectGesture()
708 tgaDebug("rejectGesture");
709 ungrabTouchPoints();
711 Q_FOREACH(int touchId, m_candidateTouches) {
712 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
713 }
715 // Monitor the candidates
716 Q_FOREACH(int touchId, m_candidateTouches) {
717 TouchRegistry::instance()->addTouchWatcher(touchId, this);
718 m_watchedTouches.insert(touchId);
719 }
720 m_candidateTouches.clear();
722 if (m_watchedTouches.isEmpty()) {
723 setInternalStatus(InternalStatus::WaitingForTouch);
724 } else {
725 setInternalStatus(InternalStatus::Rejected);
726 }
729void TouchGestureArea::resyncCachedTouchPoints()
731 clearTouchLists();
733 bool added = false;
734 bool ended = false;
735 bool moved = false;
736 bool wantsDrag = false;
738 // list of deletes
739 QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints);
740 while(removeIter.hasNext()) {
741 removeIter.next();
742 if (!m_liveTouchPoints.contains(removeIter.key())) {
743 m_releasedTouchPoints.append(removeIter.value());
744 removeIter.remove();
745 ended = true;
746 }
747 }
749 // list of adds/moves
750 Q_FOREACH(GestureTouchPoint* touchPoint, m_liveTouchPoints) {
751 if (m_cachedTouchPoints.contains(touchPoint->id())) {
752 GestureTouchPoint* cachedPoint = m_cachedTouchPoints[touchPoint->id()];
754 if (*cachedPoint != *touchPoint) {
755 *cachedPoint = *touchPoint;
756 m_movedTouchPoints.append(touchPoint);
757 moved = true;
758 }
759 } else {
760 m_cachedTouchPoints.insert(touchPoint->id(), new GestureTouchPoint(*touchPoint));
761 m_pressedTouchPoints.append(touchPoint);
762 added = true;
763 }
764 }
766 if (wantsDrag && !dragging()) {
767 setDragging(true);
768 }
770 if (ended) {
771 if (m_cachedTouchPoints.isEmpty()) {
772 if (!dragging()) Q_EMIT clicked();
773 setDragging(false);
774 }
775 tgaDebug("Cached Release " << touchesString(m_releasedTouchPoints));
776 Q_EMIT released(m_releasedTouchPoints);
777 }
778 if (added) {
779 tgaDebug("Cached Press " << touchesString(m_pressedTouchPoints));
780 Q_EMIT pressed(m_pressedTouchPoints);
781 }
782 if (moved) {
783 tgaDebug("Cached Update " << touchesString(m_movedTouchPoints));
784 Q_EMIT updated(m_movedTouchPoints);
785 }
786 if (added || ended || moved) Q_EMIT touchPointsUpdated();
789int TouchGestureArea::touchPoint_count(QQmlListProperty<GestureTouchPoint> *list)
791 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
792 return q->m_cachedTouchPoints.count();
795GestureTouchPoint *TouchGestureArea::touchPoint_at(QQmlListProperty<GestureTouchPoint> *list, int index)
797 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
798 return (q->m_cachedTouchPoints.begin()+index).value();
801GestureTouchPoint* TouchGestureArea::addTouchPoint(QTouchEvent::TouchPoint const* tp)
803 GestureTouchPoint* gtp = new GestureTouchPoint();
804 gtp->setId(tp->id());
805 gtp->setPressed(true);
806 gtp->setPos(tp->pos());
807 m_liveTouchPoints.insert(tp->id(), gtp);
808 return gtp;
811void TouchGestureArea::itemChange(ItemChange change, const ItemChangeData &value)
813 if (change == QQuickItem::ItemSceneChange) {
814 if (value.window != nullptr) {
815 value.window->installEventFilter(TouchRegistry::instance());
816 }
817 }
820void TouchGestureArea::setDragging(bool dragging)
822 if (m_dragging == dragging)
823 return;
825 tgaDebug("setDragging " << dragging);
827 m_dragging = dragging;
828 Q_EMIT draggingChanged(m_dragging);
831void GestureTouchPoint::setId(int id)
833 if (m_id == id)
834 return;
835 m_id = id;
836 Q_EMIT idChanged();
839void GestureTouchPoint::setPressed(bool pressed)
841 if (m_pressed == pressed)
842 return;
843 m_pressed = pressed;
844 Q_EMIT pressedChanged();
847void GestureTouchPoint::setX(qreal x)
849 if (m_x == x)
850 return;
851 m_x = x;
852 Q_EMIT xChanged();
855void GestureTouchPoint::setY(qreal y)
857 if (m_y == y)
858 return;
859 m_y = y;
860 Q_EMIT yChanged();
863void GestureTouchPoint::setDragging(bool dragging)
865 if (m_dragging == dragging)
866 return;
868 m_dragging = dragging;
869 Q_EMIT draggingChanged();
872void GestureTouchPoint::setPos(const QPointF &pos)
874 setX(pos.x());
875 setY(pos.y());