Lomiri
Loading...
Searching...
No Matches
TouchGestureArea.cpp
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 */
16
17#include "TouchGestureArea.h"
18
19#include <LomiriGestures/private/touchownershipevent_p.h>
20#include <LomiriGestures/private/touchregistry_p.h>
21#include <LomiriGestures/private/unownedtouchevent_p.h>
22// #include "TouchRegistry.h"
23// #include "UnownedTouchEvent.h"
24
25#include <QGuiApplication>
26#include <QStyleHints>
27#include <private/qquickwindow_p.h>
28
29UG_USE_NAMESPACE
30
31#define TOUCHGESTUREAREA_DEBUG 0
32
33// TODO - understand more about why we lose touch event releases.
34// Workaround for now is to monitor all the touch points from first touch till
35// all have been released; no matter if we've rejected the gesture.
36
37namespace {
38
39struct InternalStatus {
40 enum Status {
41 WaitingForTouch,
42 WaitingForMoreTouches,
43 WaitingForOwnership, //Recognizing,
44 Recognized,
45 WaitingForRejection,
46 Rejected
47 };
48};
49
50TouchGestureArea::Status internalStatusToGestureStatus(int internalStatus) {
51 switch (internalStatus) {
52 case InternalStatus::WaitingForTouch: return TouchGestureArea::WaitingForTouch;
53 case InternalStatus::WaitingForMoreTouches: return TouchGestureArea::Undecided;
54 case InternalStatus::WaitingForOwnership: return TouchGestureArea::Undecided;
55 case InternalStatus::Recognized: return TouchGestureArea::Recognized;
56 case InternalStatus::WaitingForRejection: return TouchGestureArea::Recognized;
57 case InternalStatus::Rejected: return TouchGestureArea::Rejected;
58 }
59 return TouchGestureArea::WaitingForTouch;
60}
61
62}
63
64#if TOUCHGESTUREAREA_DEBUG
65#define tgaDebug(params) qDebug().nospace() << "[TGA(" << qPrintable(objectName()) << ")] " << params
66#include "DebugHelpers.h"
67
68namespace {
69
70const char *statusToString(int status)
71{
72 if (status == InternalStatus::WaitingForTouch) {
73 return "WaitingForTouch";
74 } else if (status == InternalStatus::WaitingForMoreTouches) {
75 return "WaitingForMoreTouches";
76 } else if (status == InternalStatus::WaitingForOwnership) {
77 return "WaitingForOwnership";
78 } else if (status == InternalStatus::Rejected) {
79 return "Rejected";
80 } else if (status == InternalStatus::WaitingForRejection) {
81 return "WaitingForRejection";
82 } else {
83 return "Recognized";
84 }
85 return "Unknown";
86}
87
88QString touchState(Qt::TouchPointState state) {
89 switch (state) {
90 case Qt::TouchPointPressed: return "pressed";
91 case Qt::TouchPointMoved: return "moved";
92 case Qt::TouchPointStationary: return "stationary";
93 case Qt::TouchPointReleased: return "released";
94 break;
95 }
96 return "unknown";
97}
98
99QString touchesString(const QList<QObject*> touches) {
100 QString str;
101 Q_FOREACH(QObject* object, touches) {
102 GestureTouchPoint* touchPoint = qobject_cast<GestureTouchPoint*>(object);
103 if (touchPoint) {
104 str += QStringLiteral("[%1 @ (%2, %3)], ").arg(touchPoint->id())
105 .arg(touchPoint->x())
106 .arg(touchPoint->y());
107 }
108 }
109 return str;
110}
111
112QString touchEventString(QTouchEvent* event) {
113 if (!event) return QString();
114 QString str;
115 Q_FOREACH(const auto& touchPoint, event->touchPoints()) {
116 str += QStringLiteral("[%1:%2 @ (%3, %4)], ").arg(touchPoint.id())
117 .arg(touchState(touchPoint.state()))
118 .arg(touchPoint.pos().x())
119 .arg(touchPoint.pos().y());
120 }
121 return str;
122}
123
124
125} // namespace {
126#else // TOUCHGESTUREAREA_DEBUG
127#define tgaDebug(params) ((void)0)
128#endif // TOUCHGESTUREAREA_DEBUG
129
130TouchGestureArea::TouchGestureArea(QQuickItem* parent)
131 : QQuickItem(parent)
132 , m_status(WaitingForTouch)
133 , m_recognitionTimer(nullptr)
134 , m_dragging(false)
135 , m_minimumTouchPoints(1)
136 , m_maximumTouchPoints(INT_MAX)
137 , m_recognitionPeriod(50)
138 , m_releaseRejectPeriod(100)
139{
140 setRecognitionTimer(new Timer(this));
141 m_recognitionTimer->setInterval(m_recognitionPeriod);
142 m_recognitionTimer->setSingleShot(true);
143}
144
145TouchGestureArea::~TouchGestureArea()
146{
147 clearTouchLists();
148 qDeleteAll(m_liveTouchPoints);
149 m_liveTouchPoints.clear();
150 qDeleteAll(m_cachedTouchPoints);
151 m_cachedTouchPoints.clear();
152}
153
154bool TouchGestureArea::event(QEvent *event)
155{
156 // Process unowned touch events (handles update/release for incomplete gestures)
157 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
158 touchOwnershipEvent(static_cast<TouchOwnershipEvent*>(event));
159 return true;
160 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
161 unownedTouchEvent(static_cast<UnownedTouchEvent*>(event)->touchEvent());
162 return true;
163 }
164
165 return QQuickItem::event(event);
166}
167
168void TouchGestureArea::touchOwnershipEvent(TouchOwnershipEvent *event)
169{
170 int touchId = event->touchId();
171 tgaDebug("touchOwnershipEvent - id:" << touchId << ", gained:" << event->gained());
172
173 if (event->gained()) {
174 grabTouchPoints(QVector<int>() << touchId);
175 m_candidateTouches.remove(touchId);
176 TouchRegistry::instance()->addTouchWatcher(touchId, this);
177 m_watchedTouches.insert(touchId);
178
179 if (m_watchedTouches.count() >= m_minimumTouchPoints) {
180 setInternalStatus(InternalStatus::Recognized);
181 }
182 } else {
183 rejectGesture();
184 }
185}
186
187void TouchGestureArea::touchEvent(QTouchEvent *event)
188{
189 if (!isEnabled() || !isVisible()) {
190 tgaDebug(QString("NOT ENABLED touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
191 QQuickItem::touchEvent(event);
192 return;
193 }
194
195 tgaDebug(QString("touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event)));
196
197 switch (m_status) {
198 case InternalStatus::WaitingForTouch:
199 touchEvent_waitingForTouch(event);
200 break;
201 case InternalStatus::WaitingForMoreTouches:
202 touchEvent_waitingForMoreTouches(event);
203 break;
204 case InternalStatus::WaitingForOwnership:
205 touchEvent_waitingForOwnership(event);
206 break;
207 case InternalStatus::Recognized:
208 case InternalStatus::WaitingForRejection:
209 touchEvent_recognized(event);
210 break;
211 case InternalStatus::Rejected:
212 touchEvent_rejected(event);
213 break;
214 default: // Recognized:
215 break;
216 }
217
218 updateTouchPoints(event);
219}
220
221void TouchGestureArea::touchEvent_waitingForTouch(QTouchEvent *event)
222{
223 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
224 Qt::TouchPointState touchPointState = touchPoint.state();
225 int touchId = touchPoint.id();
226
227 if (touchPointState == Qt::TouchPointPressed) {
228 if (!m_candidateTouches.contains(touchId)) {
229 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
230 m_candidateTouches.insert(touchId);
231 }
232 }
233 }
234 event->ignore();
235
236 if (m_candidateTouches.count() > m_maximumTouchPoints) {
237 rejectGesture();
238 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
239 setInternalStatus(InternalStatus::WaitingForOwnership);
240
241 QSet<int> tmpCandidates(m_candidateTouches);
242 Q_FOREACH(int candidateTouchId, tmpCandidates) {
243 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
244 }
245 // We accept the gesture; so don't pass to lower items
246 event->accept();
247 } else if (m_candidateTouches.count() > 0) {
248 setInternalStatus(InternalStatus::WaitingForMoreTouches);
249 }
250}
251
252void TouchGestureArea::touchEvent_waitingForMoreTouches(QTouchEvent *event)
253{
254 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
255 Qt::TouchPointState touchPointState = touchPoint.state();
256 int touchId = touchPoint.id();
257
258 if (touchPointState == Qt::TouchPointPressed) {
259 if (!m_candidateTouches.contains(touchId)) {
260 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
261 m_candidateTouches.insert(touchId);
262 }
263 }
264 }
265 event->ignore();
266
267 if (m_candidateTouches.count() > m_maximumTouchPoints) {
268 rejectGesture();
269 } else if (m_candidateTouches.count() >= m_minimumTouchPoints) {
270 setInternalStatus(InternalStatus::WaitingForOwnership);
271
272 QSet<int> tmpCandidates(m_candidateTouches);
273 Q_FOREACH(int candidateTouchId, tmpCandidates) {
274 TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this);
275 }
276 // We accept the gesture; so don't pass to lower items
277 event->accept();
278 }
279}
280
281void TouchGestureArea::touchEvent_waitingForOwnership(QTouchEvent *event)
282{
283 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
284 Qt::TouchPointState touchPointState = touchPoint.state();
285 int touchId = touchPoint.id();
286
287 if (touchPointState == Qt::TouchPointPressed) {
288 if (!m_watchedTouches.contains(touchId)) {
289 TouchRegistry::instance()->addTouchWatcher(touchId, this);
290 m_watchedTouches.insert(touchId);
291 }
292 }
293 }
294}
295
296void TouchGestureArea::touchEvent_recognized(QTouchEvent *event)
297{
298 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
299 Qt::TouchPointState touchPointState = touchPoint.state();
300 int touchId = touchPoint.id();
301
302 if (touchPointState == Qt::TouchPointPressed) {
303 if (!m_watchedTouches.contains(touchId)) {
304 TouchRegistry::instance()->addTouchWatcher(touchId, this);
305 m_watchedTouches.insert(touchId);
306 }
307 }
308 }
309
310 if (m_watchedTouches.count() > m_maximumTouchPoints) {
311 rejectGesture();
312 } else if (m_watchedTouches.count() >= m_minimumTouchPoints &&
313 m_status==InternalStatus::WaitingForRejection) {
314 setInternalStatus(InternalStatus::Recognized);
315 }
316}
317
318void TouchGestureArea::touchEvent_rejected(QTouchEvent *event)
319{
320 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
321 Qt::TouchPointState touchPointState = touchPoint.state();
322 int touchId = touchPoint.id();
323
324 if (touchPointState == Qt::TouchPointPressed) {
325 if (!m_watchedTouches.contains(touchId)) {
326 TouchRegistry::instance()->addTouchWatcher(touchId, this);
327 m_watchedTouches.insert(touchId);
328 }
329 }
330 }
331
332 // If this event is a new touch, ignore it and let the item below have it.
333 // We won't recognize any new touch until the whole current gesture is done
334 // i.e. we're in WaitingForTouch state.
335 // And even if it isn't, just ignore it anyway; we don't care about it.
336 event->ignore();
337}
338
339void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent)
340{
341 tgaDebug(QString("unownedTouchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(unownedTouchEvent)));
342
343 // Only monitor unowned touch events for presses/releases
344 if ((unownedTouchEvent->touchPointStates() & (Qt::TouchPointPressed|Qt::TouchPointReleased)) == 0) {
345 return;
346 }
347
348 switch (m_status) {
349 case InternalStatus::WaitingForTouch:
350 break;
351 case InternalStatus::WaitingForMoreTouches:
352 unownedTouchEvent_waitingForMoreTouches(unownedTouchEvent);
353 // do nothing
354 break;
355 case InternalStatus::WaitingForOwnership:
356 unownedTouchEvent_waitingForOwnership(unownedTouchEvent);
357 break;
358 case InternalStatus::Recognized:
359 case InternalStatus::WaitingForRejection:
360 unownedTouchEvent_recognised(unownedTouchEvent);
361 break;
362 case InternalStatus::Rejected:
363 unownedTouchEvent_rejected(unownedTouchEvent);
364 break;
365 default:
366 break;
367 }
368
369 updateTouchPoints(unownedTouchEvent);
370}
371
372void TouchGestureArea::unownedTouchEvent_waitingForMoreTouches(QTouchEvent *event)
373{
374 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
375 Qt::TouchPointState touchPointState = touchPoint.state();
376 int touchId = touchPoint.id();
377
378 if (touchPointState == Qt::TouchPointReleased) {
379 if (m_candidateTouches.contains(touchId)) {
380 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
381 m_candidateTouches.remove(touchId);
382 }
383 }
384 }
385
386 if (m_candidateTouches.isEmpty()) {
387 setInternalStatus(InternalStatus::WaitingForTouch);
388 }
389}
390
391void TouchGestureArea::unownedTouchEvent_waitingForOwnership(QTouchEvent *event)
392{
393 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
394 Qt::TouchPointState touchPointState = touchPoint.state();
395 int touchId = touchPoint.id();
396
397 if (touchPointState == Qt::TouchPointReleased) {
398 if (m_candidateTouches.contains(touchId)) {
399 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
400 m_candidateTouches.remove(touchId);
401 }
402 if (m_watchedTouches.contains(touchId)) {
403 m_watchedTouches.remove(touchId);
404 }
405 }
406 }
407
408 if (m_candidateTouches.count() + m_watchedTouches.count() == 0) {
409 setInternalStatus(InternalStatus::WaitingForTouch);
410 }
411}
412
413void TouchGestureArea::unownedTouchEvent_recognised(QTouchEvent *event)
414{
415 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
416 Qt::TouchPointState touchPointState = touchPoint.state();
417 int touchId = touchPoint.id();
418
419 if (touchPointState == Qt::TouchPointReleased) {
420 if (m_watchedTouches.contains(touchId)) {
421 m_watchedTouches.remove(touchId);
422 }
423 }
424 }
425
426 if (m_watchedTouches.count() < m_minimumTouchPoints && m_status==InternalStatus::Recognized) {
427 setInternalStatus(InternalStatus::WaitingForRejection);
428 }
429}
430
431void TouchGestureArea::unownedTouchEvent_rejected(QTouchEvent *event)
432{
433 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) {
434 Qt::TouchPointState touchPointState = touchPoint.state();
435 int touchId = touchPoint.id();
436
437 if (touchPointState == Qt::TouchPointPressed) {
438 if (!m_watchedTouches.contains(touchId)) {
439 TouchRegistry::instance()->addTouchWatcher(touchId, this);
440 m_watchedTouches.insert(touchId);
441 }
442 }
443 if (touchPointState == Qt::TouchPointReleased) {
444 if (m_watchedTouches.contains(touchId)) {
445 m_watchedTouches.remove(touchId);
446 }
447 }
448 }
449
450 if (m_watchedTouches.isEmpty()) {
451 setInternalStatus(InternalStatus::WaitingForTouch);
452 }
453}
454
455void TouchGestureArea::updateTouchPoints(QTouchEvent *touchEvent)
456{
457 bool added = false;
458 bool ended = false;
459 bool moved = false;
460
461 const int dragThreshold = qApp->styleHints()->startDragDistance();
462 const int dragVelocity = qApp->styleHints()->startDragVelocity();
463
464 clearTouchLists();
465 bool updateable = m_status != InternalStatus::WaitingForRejection;
466
467 Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, touchEvent->touchPoints()) {
468 Qt::TouchPointState touchPointState = touchPoint.state();
469 int touchId = touchPoint.id();
470
471 if (touchPointState & Qt::TouchPointReleased) {
472 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchId);
473 if (!gtp) continue;
474
475 gtp->setPos(touchPoint.pos());
476 gtp->setPressed(false);
477 m_releasedTouchPoints.append(gtp);
478 m_liveTouchPoints.remove(touchId);
479
480 if (updateable) {
481 if (m_cachedTouchPoints.contains(touchId)) {
482 GestureTouchPoint* cachedPoint = m_cachedTouchPoints.take(touchId);
483 cachedPoint->deleteLater();
484 }
485 }
486 ended = true;
487 } else {
488 GestureTouchPoint* gtp = m_liveTouchPoints.value(touchPoint.id(), nullptr);
489 if (!gtp) {
490 gtp = addTouchPoint(&touchPoint);
491 m_pressedTouchPoints.append(gtp);
492
493 if (updateable) {
494 if (m_cachedTouchPoints.contains(touchId)) {
495 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
496 } else {
497 m_cachedTouchPoints[touchId] = new GestureTouchPoint(*gtp);
498 }
499 }
500 added = true;
501 } else if (touchPointState & Qt::TouchPointMoved) {
502 gtp->setPos(touchPoint.pos());
503 m_movedTouchPoints.append(gtp);
504 moved = true;
505
506 const QPointF &currentPos = touchPoint.scenePos();
507 const QPointF &startPos = touchPoint.startScenePos();
508
509 bool overDragThreshold = false;
510 bool supportsVelocity = (touchEvent->device()->capabilities() & QTouchDevice::Velocity) && dragVelocity;
511 overDragThreshold |= qAbs(currentPos.x() - startPos.x()) > dragThreshold ||
512 qAbs(currentPos.y() - startPos.y()) > dragThreshold;
513 if (supportsVelocity) {
514 QVector2D velocityVec = touchPoint.velocity();
515 overDragThreshold |= qAbs(velocityVec.x()) > dragVelocity;
516 overDragThreshold |= qAbs(velocityVec.y()) > dragVelocity;
517 }
518
519 if (overDragThreshold) {
520 gtp->setDragging(true);
521 }
522
523 if (updateable) {
524 if (m_cachedTouchPoints.contains(touchId)) {
525 m_cachedTouchPoints[touchId]->setPos(touchPoint.pos());
526 if (overDragThreshold) {
527 m_cachedTouchPoints[touchId]->setDragging(true);
528 }
529 }
530 }
531 }
532 }
533 }
534
535 if (updateable) {
536 if (!dragging() && m_status == InternalStatus::Recognized) {
537 bool allWantDrag = !m_liveTouchPoints.isEmpty();
538 Q_FOREACH(const auto &point, m_liveTouchPoints) {
539 allWantDrag &= point->dragging();
540 }
541 // only dragging if all points are dragging.
542 if (allWantDrag) {
543 setDragging(true);
544 }
545 }
546
547 if (ended) {
548 if (m_liveTouchPoints.isEmpty()) {
549 if (!dragging()) Q_EMIT clicked();
550 setDragging(false);
551 }
552 tgaDebug("Released " << touchesString(m_releasedTouchPoints));
553 Q_EMIT released(m_releasedTouchPoints);
554 }
555 if (added) {
556 tgaDebug("Pressed " << touchesString(m_pressedTouchPoints));
557 Q_EMIT pressed(m_pressedTouchPoints);
558 }
559 if (moved) {
560 tgaDebug("Updated " << touchesString(m_movedTouchPoints));
561 Q_EMIT updated(m_movedTouchPoints);
562 }
563 if (added || ended || moved) {
564 Q_EMIT touchPointsUpdated();
565 }
566 }
567}
568
569void TouchGestureArea::clearTouchLists()
570{
571 Q_FOREACH (QObject *gtp, m_releasedTouchPoints) {
572 delete gtp;
573 }
574 m_releasedTouchPoints.clear();
575 m_pressedTouchPoints.clear();
576 m_movedTouchPoints.clear();
577}
578
579void TouchGestureArea::setInternalStatus(uint newStatus)
580{
581 if (newStatus == m_status)
582 return;
583
584 uint oldStatus = m_status;
585
586 m_status = newStatus;
587 Q_EMIT statusChanged(status());
588
589 if (oldStatus == InternalStatus::WaitingForMoreTouches || oldStatus == InternalStatus::WaitingForRejection) {
590 m_recognitionTimer->stop();
591 }
592
593 tgaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
594
595 switch (newStatus) {
596 case InternalStatus::WaitingForTouch:
597 resyncCachedTouchPoints();
598 break;
599 case InternalStatus::WaitingForMoreTouches:
600 m_recognitionTimer->setInterval(m_recognitionPeriod);
601 m_recognitionTimer->start();
602 break;
603 case InternalStatus::Recognized:
604 resyncCachedTouchPoints();
605 break;
606 case InternalStatus::WaitingForRejection:
607 m_recognitionTimer->setInterval(m_releaseRejectPeriod);
608 m_recognitionTimer->start();
609 break;
610 case InternalStatus::Rejected:
611 resyncCachedTouchPoints();
612 break;
613 default:
614 // no-op
615 break;
616 }
617}
618
619void TouchGestureArea::setRecognitionTimer(AbstractTimer *timer)
620{
621 int interval = 0;
622 bool timerWasRunning = false;
623 bool wasSingleShot = false;
624
625 // can be null when called from the constructor
626 if (m_recognitionTimer) {
627 interval = m_recognitionTimer->interval();
628 timerWasRunning = m_recognitionTimer->isRunning();
629 if (m_recognitionTimer->parent() == this) {
630 delete m_recognitionTimer;
631 }
632 }
633
634 m_recognitionTimer = timer;
635 timer->setInterval(interval);
636 timer->setSingleShot(wasSingleShot);
637 connect(timer, SIGNAL(timeout()),
638 this, SLOT(rejectGesture()));
639 if (timerWasRunning) {
640 m_recognitionTimer->start();
641 }
642}
643
644int TouchGestureArea::status() const
645{
646 return internalStatusToGestureStatus(m_status);
647}
648
649bool TouchGestureArea::dragging() const
650{
651 return m_dragging;
652}
653
654QQmlListProperty<GestureTouchPoint> TouchGestureArea::touchPoints()
655{
656 return QQmlListProperty<GestureTouchPoint>(this,
657 nullptr,
658 TouchGestureArea::touchPoint_count,
659 TouchGestureArea::touchPoint_at);
660}
661
662int TouchGestureArea::minimumTouchPoints() const
663{
664 return m_minimumTouchPoints;
665}
666
667void TouchGestureArea::setMinimumTouchPoints(int value)
668{
669 if (m_minimumTouchPoints != value) {
670 m_minimumTouchPoints = value;
671 Q_EMIT minimumTouchPointsChanged(value);
672 }
673}
674
675int TouchGestureArea::maximumTouchPoints() const
676{
677 return m_maximumTouchPoints;
678}
679
680void TouchGestureArea::setMaximumTouchPoints(int value)
681{
682 if (m_maximumTouchPoints != value) {
683 m_maximumTouchPoints = value;
684 Q_EMIT maximumTouchPointsChanged(value);
685 }
686}
687
688int TouchGestureArea::recognitionPeriod() const
689{
690 return m_recognitionPeriod;
691}
692
693void TouchGestureArea::setRecognitionPeriod(int value)
694{
695 if (value != m_recognitionPeriod) {
696 m_recognitionPeriod = value;
697 Q_EMIT recognitionPeriodChanged(value);
698 }
699}
700
701int TouchGestureArea::releaseRejectPeriod() const
702{
703 return m_releaseRejectPeriod;
704}
705
706void TouchGestureArea::setReleaseRejectPeriod(int value)
707{
708 if (value != m_releaseRejectPeriod) {
709 m_releaseRejectPeriod = value;
710 Q_EMIT releaseRejectPeriodChanged(value);
711 }
712}
713
714void TouchGestureArea::rejectGesture()
715{
716 tgaDebug("rejectGesture");
717 ungrabTouchPoints();
718
719 Q_FOREACH(int touchId, m_candidateTouches) {
720 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
721 }
722
723 // Monitor the candidates
724 Q_FOREACH(int touchId, m_candidateTouches) {
725 TouchRegistry::instance()->addTouchWatcher(touchId, this);
726 m_watchedTouches.insert(touchId);
727 }
728 m_candidateTouches.clear();
729
730 if (m_watchedTouches.isEmpty()) {
731 setInternalStatus(InternalStatus::WaitingForTouch);
732 } else {
733 setInternalStatus(InternalStatus::Rejected);
734 }
735}
736
737void TouchGestureArea::resyncCachedTouchPoints()
738{
739 clearTouchLists();
740
741 bool added = false;
742 bool ended = false;
743 bool moved = false;
744
745 // list of deletes
746 QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints);
747 while(removeIter.hasNext()) {
748 removeIter.next();
749 if (!m_liveTouchPoints.contains(removeIter.key())) {
750 m_releasedTouchPoints.append(removeIter.value());
751 removeIter.remove();
752 ended = true;
753 }
754 }
755
756 // list of adds/moves
757 Q_FOREACH(GestureTouchPoint* touchPoint, m_liveTouchPoints) {
758 if (m_cachedTouchPoints.contains(touchPoint->id())) {
759 GestureTouchPoint* cachedPoint = m_cachedTouchPoints[touchPoint->id()];
760
761 if (*cachedPoint != *touchPoint) {
762 *cachedPoint = *touchPoint;
763 m_movedTouchPoints.append(touchPoint);
764 moved = true;
765 }
766 } else {
767 m_cachedTouchPoints.insert(touchPoint->id(), new GestureTouchPoint(*touchPoint));
768 m_pressedTouchPoints.append(touchPoint);
769 added = true;
770 }
771 }
772
773 if (ended) {
774 if (m_cachedTouchPoints.isEmpty()) {
775 if (!dragging()) Q_EMIT clicked();
776 setDragging(false);
777 }
778 tgaDebug("Cached Release " << touchesString(m_releasedTouchPoints));
779 Q_EMIT released(m_releasedTouchPoints);
780 }
781 if (added) {
782 tgaDebug("Cached Press " << touchesString(m_pressedTouchPoints));
783 Q_EMIT pressed(m_pressedTouchPoints);
784 }
785 if (moved) {
786 tgaDebug("Cached Update " << touchesString(m_movedTouchPoints));
787 Q_EMIT updated(m_movedTouchPoints);
788 }
789 if (added || ended || moved) Q_EMIT touchPointsUpdated();
790}
791
792int TouchGestureArea::touchPoint_count(QQmlListProperty<GestureTouchPoint> *list)
793{
794 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
795 return q->m_cachedTouchPoints.count();
796}
797
798GestureTouchPoint *TouchGestureArea::touchPoint_at(QQmlListProperty<GestureTouchPoint> *list, int index)
799{
800 TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object);
801 return (q->m_cachedTouchPoints.begin()+index).value();
802}
803
804GestureTouchPoint* TouchGestureArea::addTouchPoint(QTouchEvent::TouchPoint const* tp)
805{
806 GestureTouchPoint* gtp = new GestureTouchPoint();
807 gtp->setId(tp->id());
808 gtp->setPressed(true);
809 gtp->setPos(tp->pos());
810 m_liveTouchPoints.insert(tp->id(), gtp);
811 return gtp;
812}
813
814void TouchGestureArea::itemChange(ItemChange change, const ItemChangeData &value)
815{
816 if (change == QQuickItem::ItemSceneChange) {
817 if (value.window != nullptr) {
818 value.window->installEventFilter(TouchRegistry::instance());
819 }
820 }
821}
822
823void TouchGestureArea::setDragging(bool dragging)
824{
825 if (m_dragging == dragging)
826 return;
827
828 tgaDebug("setDragging " << dragging);
829
830 m_dragging = dragging;
831 Q_EMIT draggingChanged(m_dragging);
832}
833
834void GestureTouchPoint::setId(int id)
835{
836 if (m_id == id)
837 return;
838 m_id = id;
839 Q_EMIT idChanged();
840}
841
842void GestureTouchPoint::setPressed(bool pressed)
843{
844 if (m_pressed == pressed)
845 return;
846 m_pressed = pressed;
847 Q_EMIT pressedChanged();
848}
849
850void GestureTouchPoint::setX(qreal x)
851{
852 if (m_x == x)
853 return;
854 m_x = x;
855 Q_EMIT xChanged();
856}
857
858void GestureTouchPoint::setY(qreal y)
859{
860 if (m_y == y)
861 return;
862 m_y = y;
863 Q_EMIT yChanged();
864}
865
866void GestureTouchPoint::setDragging(bool dragging)
867{
868 if (m_dragging == dragging)
869 return;
870
871 m_dragging = dragging;
872 Q_EMIT draggingChanged();
873}
874
875void GestureTouchPoint::setPos(const QPointF &pos)
876{
877 setX(pos.x());
878 setY(pos.y());
879}