@@ -1,5 +1,5 @@
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,64 +14,32 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#define ACTIVETOUCHESINFO_DEBUG 0
18+#define DIRECTIONALDRAGAREA_DEBUG 0
17#include "DirectionalDragArea.h"
20#include "DirectionalDragArea.h"
22+#include <QQuickWindow>
19#include <QtCore/qmath.h>
23#include <QtCore/qmath.h>
20−#include <QtCore/QTimer>
21#include <QDebug>
24#include <QDebug>
23−using namespace UbuntuGestures;
26+#pragma GCC diagnostic push
27+#pragma GCC diagnostic ignored "-pedantic"
28+#include <private/qquickwindow_p.h>
29+#pragma GCC diagnostic pop
25−#define DIRECTIONALDRAGAREA_DEBUG 0
31+// local
32+#include "TouchOwnershipEvent.h"
33+#include "TouchRegistry.h"
34+#include "UnownedTouchEvent.h"
36+using namespace UbuntuGestures;
27#if DIRECTIONALDRAGAREA_DEBUG
38#if DIRECTIONALDRAGAREA_DEBUG
28#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
39#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
29−namespace {
40+#include "DebugHelpers.h"
30−QString touchPointStateToString(Qt::TouchPointState state) {
32− case Qt::TouchPointPressed:
33− return QString("pressed");
34− case Qt::TouchPointMoved:
35− return QString("moved");
36− case Qt::TouchPointStationary:
37− return QString("stationary");
38− default: // Qt::TouchPointReleased:
39− return QString("released");
42−QString touchEventToString(QTouchEvent *ev)
46− switch (ev->type()) {
47− case QEvent::TouchBegin:
48− message.append("TouchBegin ");
50− case QEvent::TouchUpdate:
51− message.append("TouchUpdate ");
53− case QEvent::TouchEnd:
54− message.append("TouchEnd ");
56− default: //QEvent::TouchCancel
57− message.append("TouchCancel ");
60− for (int i=0; i < ev->touchPoints().size(); ++i) {
62− const QTouchEvent::TouchPoint& touchPoint = ev->touchPoints().at(i);
64− QString("(id:%1, state:%2, scenePos:(%3,%4)) ")
65− .arg(touchPoint.id())
66− .arg(touchPointStateToString(touchPoint.state()))
67− .arg(touchPoint.scenePos().x())
68− .arg(touchPoint.scenePos().y())
75const char *statusToString(DirectionalDragArea::Status status)
43const char *statusToString(DirectionalDragArea::Status status)
77 if (status == DirectionalDragArea::WaitingForTouch) {
45 if (status == DirectionalDragArea::WaitingForTouch) {
@@ -88,23 +56,6 @@ const char *statusToString(DirectionalDragArea::Status status)
88#define DDA_DEBUG(msg) do{}while(0)
56#define DDA_DEBUG(msg) do{}while(0)
89#endif // DIRECTIONALDRAGAREA_DEBUG
57#endif // DIRECTIONALDRAGAREA_DEBUG
91−// Essentially a QTimer wrapper
92−class RecognitionTimer : public UbuntuGestures::AbstractTimer
96− RecognitionTimer(QObject *parent) : UbuntuGestures::AbstractTimer(parent) {
97− m_timer.setSingleShot(false);
98− connect(&m_timer, &QTimer::timeout,
99− this, &UbuntuGestures::AbstractTimer::timeout);
101− virtual int interval() const { return m_timer.interval(); }
102− virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
103− virtual void start() { m_timer.start(); UbuntuGestures::AbstractTimer::start(); }
104− virtual void stop() { m_timer.stop(); UbuntuGestures::AbstractTimer::stop(); }
109DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
110 : QQuickItem(parent)
61 : QQuickItem(parent)
@@ -126,12 +77,14 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
126 , m_timeSource(new RealTimeSource)
77 , m_timeSource(new RealTimeSource)
127 , m_activeTouches(m_timeSource)
78 , m_activeTouches(m_timeSource)
129− setRecognitionTimer(new RecognitionTimer(this));
80+ setRecognitionTimer(new Timer(this));
130 m_recognitionTimer->setInterval(60);
81 m_recognitionTimer->setInterval(60);
82+ m_recognitionTimer->setSingleShot(false);
132 m_velocityCalculator = new AxisVelocityCalculator(this);
84 m_velocityCalculator = new AxisVelocityCalculator(this);
134− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::onEnabledChanged);
86+ connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87+ connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
137Direction::Type DirectionalDragArea::direction() const
90Direction::Type DirectionalDragArea::direction() const
@@ -214,6 +167,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
215 int interval = 0;
168 int interval = 0;
216 bool timerWasRunning = false;
169 bool timerWasRunning = false;
170+ bool wasSingleShot = false;
218 // can be null when called from the constructor
172 // can be null when called from the constructor
219 if (m_recognitionTimer) {
173 if (m_recognitionTimer) {
@@ -226,6 +180,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
227 m_recognitionTimer = timer;
181 m_recognitionTimer = timer;
228 timer->setInterval(interval);
182 timer->setInterval(interval);
183+ timer->setSingleShot(wasSingleShot);
229 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
184 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
230 this, &DirectionalDragArea::checkSpeed);
185 this, &DirectionalDragArea::checkSpeed);
231 if (timerWasRunning) {
186 if (timerWasRunning) {
@@ -280,8 +235,144 @@ qreal DirectionalDragArea::touchSceneY() const
280 return m_previousScenePos.y();
235 return m_previousScenePos.y();
238+bool DirectionalDragArea::event(QEvent *event)
240+ if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241+ touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
243+ } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244+ unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
247+ return QQuickItem::event(event);
251+void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
253+ if (event->gained()) {
255+ ids.append(event->touchId());
256+ DDA_DEBUG("grabbing touch");
257+ grabTouchPoints(ids);
259+ // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260+ // emulation it will cause the emulation logic to go nuts.
261+ // Thus we have to also grab the mouse in this case.
262+ // TODO: Report bug to Qt
264+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
265+ if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
266+ DDA_DEBUG("removing mouse grabber");
267+ window()->mouseGrabberItem()->ungrabMouse();
271+ // We still wanna know when it ends for keeping the composition time window up-to-date
272+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
274+ setStatus(WaitingForTouch);
278+void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
280+ QTouchEvent *event = unownedTouchEvent->touchEvent();
282+ Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
284+ #if DIRECTIONALDRAGAREA_DEBUG
285+ // TODO Consider using qCDebug() when available (Qt 5.2)
286+ qDebug() << "[DDA] Unowned" << m_timeSource->msecsSinceReference()
287+ << qPrintable(touchEventToString(event));
291+ case WaitingForTouch:
295+ Q_ASSERT(isEnabled() && isVisible());
296+ unownedTouchEvent_undecided(unownedTouchEvent);
298+ default: // Recognized:
303+ m_activeTouches.update(event);
306+void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
308+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
310+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
311+ << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
312+ "Considering it as released.";
314+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
315+ setStatus(WaitingForTouch);
319+ const QPointF &touchScenePos = touchPoint->scenePos();
321+ if (touchPoint->state() == Qt::TouchPointReleased) {
322+ // touch has ended before recognition concluded
323+ DDA_DEBUG("Touch has ended before recognition concluded");
324+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
325+ emitSignalIfTapped();
326+ setStatus(WaitingForTouch);
330+ m_previousDampedScenePos.setX(m_dampedScenePos.x());
331+ m_previousDampedScenePos.setY(m_dampedScenePos.y());
332+ m_dampedScenePos.update(touchScenePos);
333+ updateVelocityCalculator(touchScenePos);
335+ if (!pointInsideAllowedArea()) {
336+ DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
337+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
338+ // We still wanna know when it ends for keeping the composition time window up-to-date
339+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
340+ setStatus(WaitingForTouch);
344+ if (!movingInRightDirection()) {
345+ DDA_DEBUG("Rejecting gesture because touch point is moving in the wrong direction.");
346+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
347+ // We still wanna know when it ends for keeping the composition time window up-to-date
348+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
349+ setStatus(WaitingForTouch);
353+ setPreviousPos(touchPoint->pos());
354+ setPreviousScenePos(touchScenePos);
356+ if (isWithinTouchCompositionWindow()) {
357+ // There's still time for some new touch to appear and ruin our party as it would be combined
358+ // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
359+ DDA_DEBUG("Sill within composition window. Let's wait more.");
363+ if (movedFarEnough(touchScenePos)) {
364+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
365+ setStatus(Recognized);
367+ DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
283void DirectionalDragArea::touchEvent(QTouchEvent *event)
371void DirectionalDragArea::touchEvent(QTouchEvent *event)
373+ // TODO: Consider when more than one touch starts in the same event (although it's not possible
374+ // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
285 #if DIRECTIONALDRAGAREA_DEBUG
376 #if DIRECTIONALDRAGAREA_DEBUG
286 // TODO Consider using qCDebug() when available (Qt 5.2)
377 // TODO Consider using qCDebug() when available (Qt 5.2)
287 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
378 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
@@ -310,29 +401,34 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
311void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
402void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
404+ // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
313 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
406 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
314 // Nothing to see here. No touch starting in this event.
407 // Nothing to see here. No touch starting in this event.
411+ // to be proven wrong, if that's the case
412+ bool allGood = true;
318 if (isWithinTouchCompositionWindow()) {
414 if (isWithinTouchCompositionWindow()) {
319 // too close to the last touch start. So we consider them as starting roughly at the same time.
415 // too close to the last touch start. So we consider them as starting roughly at the same time.
320 // Can't be a single-touch gesture.
416 // Can't be a single-touch gesture.
321 #if DIRECTIONALDRAGAREA_DEBUG
417 #if DIRECTIONALDRAGAREA_DEBUG
322 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
418 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
324− return;
420+ allGood = false;
327 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
423 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
329 const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
425 const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
330− for (int i = 0; i < touchPoints.count(); ++i) {
426+ for (int i = 0; i < touchPoints.count() && allGood; ++i) {
331 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
427 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
332 if (touchPoint.state() == Qt::TouchPointPressed) {
428 if (touchPoint.state() == Qt::TouchPointPressed) {
333 if (newTouchPoint) {
429 if (newTouchPoint) {
334 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
430 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
335− return;
431+ allGood = false;
337 // that's our candidate
433 // that's our candidate
338 m_touchId = touchPoint.id();
434 m_touchId = touchPoint.id();
@@ -341,85 +437,61 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
344− Q_ASSERT(newTouchPoint);
441+ Q_ASSERT(newTouchPoint);
346− // If we have made this far, we are good to go to the next status.
348− m_startPos = newTouchPoint->pos();
443+ m_startPos = newTouchPoint->pos();
349− m_startScenePos = newTouchPoint->scenePos();
444+ m_startScenePos = newTouchPoint->scenePos();
350− m_touchId = newTouchPoint->id();
445+ m_touchId = newTouchPoint->id();
351− m_dampedScenePos.reset(m_startScenePos);
446+ m_dampedScenePos.reset(m_startScenePos);
352− m_velocityCalculator->setTrackedPosition(0.);
447+ m_velocityCalculator->setTrackedPosition(0.);
353− m_velocityCalculator->reset();
448+ m_velocityCalculator->reset();
354− m_numSamplesOnLastSpeedCheck = 0;
449+ m_numSamplesOnLastSpeedCheck = 0;
355− m_silenceTime = 0;
450+ m_silenceTime = 0;
356− setPreviousPos(m_startPos);
451+ setPreviousPos(m_startPos);
357− setPreviousScenePos(m_startScenePos);
452+ setPreviousScenePos(m_startScenePos);
358− updateSceneDirectionVector();
453+ updateSceneDirectionVector();
455+ if (recognitionIsDisabled()) {
456+ // Behave like a dumb TouchArea
457+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
458+ setStatus(Recognized);
461+ // just monitor the touch points for now.
462+ TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
360− setStatus(Undecided);
464+ setStatus(Undecided);
465+ // Let the item below have it. We will monitor it and grab it later if a gesture
470+ watchPressedTouchPoints(touchPoints);
363void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
475void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
477+ Q_ASSERT(event->type() == QEvent::TouchBegin);
365− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
478+ Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
367− if (!touchPoint) {
480+ // We're not interested in new touch points. We already have our candidate (m_touchId).
368− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
481+ // But we do want to know when those new touches end for keeping the composition time
369− << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
482+ // window up-to-date
370− "Considering it as released.";
483+ event->ignore();
371− setStatus(WaitingForTouch);
484+ watchPressedTouchPoints(event->touchPoints());
375− const QPointF &touchScenePos = touchPoint->scenePos();
377− if (touchPoint->state() == Qt::TouchPointReleased) {
378− // touch has ended before recognition concluded
379− DDA_DEBUG("Touch has ended before recognition concluded");
380− setStatus(WaitingForTouch);
385 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
486 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
386 // multi-finger drags are not accepted
487 // multi-finger drags are not accepted
387 DDA_DEBUG("Multi-finger drags are not accepted");
488 DDA_DEBUG("Multi-finger drags are not accepted");
388− setStatus(WaitingForTouch);
392− m_previousDampedScenePos.setX(m_dampedScenePos.x());
490+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
393− m_previousDampedScenePos.setY(m_dampedScenePos.y());
491+ // We still wanna know when it ends for keeping the composition time window up-to-date
394− m_dampedScenePos.update(touchScenePos);
492+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
395− updateVelocityCalculator(touchScenePos);
397− if (!pointInsideAllowedArea()) {
398− DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
399− setStatus(WaitingForTouch);
403− if (!movingInRightDirection()) {
404− DDA_DEBUG("Rejecting gesture becauuse touch point is moving in the wrong direction.");
405 setStatus(WaitingForTouch);
494 setStatus(WaitingForTouch);
409− setPreviousPos(touchPoint->pos());
410− setPreviousScenePos(touchScenePos);
412− if (isWithinTouchCompositionWindow()) {
413− // There's still time for some new touch to appear and ruin our party as it would be combined
414− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
415− DDA_DEBUG("Sill within composition window. Let's wait more.");
419− if (movedFarEnough(touchScenePos)) {
420− setStatus(Recognized);
422− DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
@@ -437,11 +509,35 @@ void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
437 setPreviousScenePos(touchPoint->scenePos());
509 setPreviousScenePos(touchPoint->scenePos());
439 if (touchPoint->state() == Qt::TouchPointReleased) {
511 if (touchPoint->state() == Qt::TouchPointReleased) {
512+ emitSignalIfTapped();
440 setStatus(WaitingForTouch);
513 setStatus(WaitingForTouch);
518+void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
520+ for (int i = 0; i < touchPoints.count(); ++i) {
521+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
522+ if (touchPoint.state() == Qt::TouchPointPressed) {
523+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
528+bool DirectionalDragArea::recognitionIsDisabled() const
530+ return distanceThreshold() <= 0 && compositionTime() <= 0;
533+void DirectionalDragArea::emitSignalIfTapped()
535+ qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
536+ if (touchDuration <= maxTapDuration()) {
445const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
541const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
447 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
543 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
@@ -509,12 +605,16 @@ bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
510void DirectionalDragArea::checkSpeed()
606void DirectionalDragArea::checkSpeed()
608+ Q_ASSERT(m_status == Undecided);
512 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
610 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
513 qreal speed = qFabs(m_velocityCalculator->calculate());
611 qreal speed = qFabs(m_velocityCalculator->calculate());
514 qreal minSpeedMsecs = m_minSpeed / 1000.0;
612 qreal minSpeedMsecs = m_minSpeed / 1000.0;
516 if (speed < minSpeedMsecs) {
614 if (speed < minSpeedMsecs) {
517 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
615 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
616+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
617+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
518 setStatus(WaitingForTouch);
618 setStatus(WaitingForTouch);
@@ -523,7 +623,9 @@ void DirectionalDragArea::checkSpeed()
523 m_silenceTime += m_recognitionTimer->interval();
623 m_silenceTime += m_recognitionTimer->interval();
525 if (m_silenceTime > m_maxSilenceTime) {
625 if (m_silenceTime > m_maxSilenceTime) {
526− DDA_DEBUG("Rejecting gesture because it's silence time has been exceeded.");
626+ DDA_DEBUG("Rejecting gesture because its silence time has been exceeded.");
627+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
628+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
527 setStatus(WaitingForTouch);
629 setStatus(WaitingForTouch);
@@ -532,10 +634,19 @@ void DirectionalDragArea::checkSpeed()
532 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
634 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
535−void DirectionalDragArea::onEnabledChanged()
637+void DirectionalDragArea::giveUpIfDisabledOrInvisible()
639+ if (!isEnabled() || !isVisible()) {
640+ if (m_status == Undecided) {
641+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
642+ // We still wanna know when it ends for keeping the composition time window up-to-date
643+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
537− if (!isEnabled() && m_status != WaitingForTouch) {
646+ if (m_status != WaitingForTouch) {
647+ DDA_DEBUG("Resetting status because got disabled or made invisible");
538− setStatus(WaitingForTouch);
648+ setStatus(WaitingForTouch);
@@ -631,7 +742,9 @@ void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
632bool DirectionalDragArea::isWithinTouchCompositionWindow()
743bool DirectionalDragArea::isWithinTouchCompositionWindow()
746+ compositionTime() > 0 &&
634− return !m_activeTouches.isEmpty() &&
747+ !m_activeTouches.isEmpty() &&
635 m_timeSource->msecsSinceReference() <=
748 m_timeSource->msecsSinceReference() <=
636 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
749 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
@@ -639,18 +752,17 @@ bool DirectionalDragArea::isWithinTouchCompositionWindow()
639//************************** ActiveTouchesInfo **************************
752//************************** ActiveTouchesInfo **************************
641DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
754DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
642− : m_timeSource(timeSource), m_lastUsedIndex(-1)
755+ : m_timeSource(timeSource)
644− // Estimate of the maximum number of active touches we might reach.
645− // Not a problem if it ends up being an underestimate as this is just
650void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
759void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
652 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
761 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
653 // nothing to update
762 // nothing to update
763+ #if ACTIVETOUCHESINFO_DEBUG
764+ qDebug("[DDA::ActiveTouchesInfo] Nothing to Update");
@@ -658,74 +770,88 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
658 for (int i = 0; i < touchPoints.count(); ++i) {
770 for (int i = 0; i < touchPoints.count(); ++i) {
659 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
771 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
660 if (touchPoint.state() == Qt::TouchPointPressed) {
772 if (touchPoint.state() == Qt::TouchPointPressed) {
661− addTouchPoint(touchPoint);
773+ addTouchPoint(touchPoint.id());
662 } else if (touchPoint.state() == Qt::TouchPointReleased) {
774 } else if (touchPoint.state() == Qt::TouchPointReleased) {
663− removeTouchPoint(touchPoint);
775+ removeTouchPoint(touchPoint.id());
780+#if ACTIVETOUCHESINFO_DEBUG
668−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
781+QString DirectionalDragArea::ActiveTouchesInfo::toString()
670− ActiveTouchInfo &activeTouchInfo = getEmptySlot();
783+ QString string = "(";
671− activeTouchInfo.id = touchPoint.id();
784+
672− activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
785+ {
786+ QTextStream stream(&string);
787+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
788+ stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")";
797+#endif // ACTIVETOUCHESINFO_DEBUG
675−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
799+void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
677− for (int i = 0; i <= m_lastUsedIndex; ++i) {
801+ ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
678− if (touchPoint.id() == m_vector.at(i).id) {
802+ activeTouchInfo.id = touchId;
679− freeSlot(i);
803+ activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
681− }
805+ #if ACTIVETOUCHESINFO_DEBUG
682− }
806+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
683− Q_ASSERT(false); // shouldn't reach this point
807+ #endif
686−DirectionalDragArea::ActiveTouchInfo &DirectionalDragArea::ActiveTouchesInfo::getEmptySlot()
810+qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
688− Q_ASSERT(m_lastUsedIndex < m_vector.size());
812+ qint64 result = -1;
690− // Look for an in-between vacancy first
814+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
691− for (int i = 0; i < m_lastUsedIndex; ++i) {
815+ if (touchId == touchInfo->id) {
692− ActiveTouchInfo &activeTouchInfo = m_vector[i];
816+ result = touchInfo->startTime;
693− if (!activeTouchInfo.isValid()) {
817+ return false;
694− return activeTouchInfo;
818+ } else {
699− if (m_lastUsedIndex >= m_vector.size()) {
700− m_vector.resize(m_lastUsedIndex + 1);
703− return m_vector[m_lastUsedIndex];
823+ Q_ASSERT(result != -1);
706−void DirectionalDragArea::ActiveTouchesInfo::freeSlot(int index)
827+void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
708− m_vector[index].reset();
829+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
709− if (index == m_lastUsedIndex) {
830+ if (touchId == touchInfo->id) {
710− do {
831+ m_touchInfoPool.freeSlot(touchInfo);
711− --m_lastUsedIndex;
832+ return false;
712− } while (m_lastUsedIndex >= 0 && !m_vector.at(m_lastUsedIndex).isValid());
833+ } else {
838+ #if ACTIVETOUCHESINFO_DEBUG
839+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
716qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
843qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
718− Q_ASSERT(m_lastUsedIndex >= 0);
845+ Q_ASSERT(!m_touchInfoPool.isEmpty());
847+ qint64 highestStartTime = -1;
720− qint64 highestStartTime = m_vector.at(0).startTime;
723− const ActiveTouchInfo &activeTouchInfo = m_vector.at(i);
849+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) {
724− if (activeTouchInfo.isValid() && activeTouchInfo.startTime > highestStartTime) {
850+ if (activeTouchInfo->startTime > highestStartTime) {
725− highestStartTime = activeTouchInfo.startTime;
851+ highestStartTime = activeTouchInfo->startTime;
727− ++i;
853+ return true;
728− } while (i < m_lastUsedIndex);
854+ });
730 return highestStartTime;
856 return highestStartTime;