@@ -1,932 +0,0 @@
2− * Copyright (C) 2013-2015 Canonical, Ltd.
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.
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.
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/>.
17−#define ACTIVETOUCHESINFO_DEBUG 0
18−#define DIRECTIONALDRAGAREA_DEBUG 0
20−#include "DirectionalDragArea.h"
22−#include <QQuickWindow>
23−#include <QtCore/qmath.h>
27−#pragma GCC diagnostic push
28−#pragma GCC diagnostic ignored "-pedantic"
29−#include <private/qquickwindow_p.h>
30−#pragma GCC diagnostic pop
33−#include "TouchOwnershipEvent.h"
34−#include "TouchRegistry.h"
35−#include "UnownedTouchEvent.h"
37−#include "DirectionalDragArea_p.h"
39−using namespace UbuntuGestures;
41−#if DIRECTIONALDRAGAREA_DEBUG
42−#define ddaDebug(params) qDebug().nospace() << "[DDA(" << qPrintable(objectName()) << ")] " << params
43−#include "DebugHelpers.h"
46−const char *statusToString(DirectionalDragAreaPrivate::Status status)
48− if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
49− return "WaitingForTouch";
50− } else if (status == DirectionalDragAreaPrivate::Undecided) {
58−#else // DIRECTIONALDRAGAREA_DEBUG
59−#define ddaDebug(params) ((void)0)
60−#endif // DIRECTIONALDRAGAREA_DEBUG
62−DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
64− , d(new DirectionalDragAreaPrivate(this))
66− d->setRecognitionTimer(new Timer(this));
67− d->recognitionTimer->setInterval(d->maxTime);
68− d->recognitionTimer->setSingleShot(true);
70− connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
71− connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
74−Direction::Type DirectionalDragArea::direction() const
79−void DirectionalDragArea::setDirection(Direction::Type direction)
81− if (direction != d->direction) {
82− d->direction = direction;
83− Q_EMIT directionChanged(d->direction);
87−void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
89− if (distanceThreshold != value) {
90− distanceThreshold = value;
91− distanceThresholdSquared = distanceThreshold * distanceThreshold;
95−void DirectionalDragAreaPrivate::setMaxTime(int value)
97− if (maxTime != value) {
99− recognitionTimer->setInterval(maxTime);
103−void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
106− bool timerWasRunning = false;
107− bool wasSingleShot = false;
109− // can be null when called from the constructor
110− if (recognitionTimer) {
111− interval = recognitionTimer->interval();
112− timerWasRunning = recognitionTimer->isRunning();
113− if (recognitionTimer->parent() == this) {
114− delete recognitionTimer;
118− recognitionTimer = timer;
119− timer->setInterval(interval);
120− timer->setSingleShot(wasSingleShot);
121− connect(timer, &UbuntuGestures::AbstractTimer::timeout,
122− this, &DirectionalDragAreaPrivate::rejectGesture);
123− if (timerWasRunning) {
124− recognitionTimer->start();
128−void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
130− this->timeSource = timeSource;
131− activeTouches.m_timeSource = timeSource;
134−qreal DirectionalDragArea::distance() const
136− if (Direction::isHorizontal(d->direction)) {
137− return d->publicPos.x() - d->startPos.x();
139− return d->publicPos.y() - d->startPos.y();
143−void DirectionalDragAreaPrivate::updateSceneDistance()
145− QPointF totalMovement = publicScenePos - startScenePos;
146− sceneDistance = projectOntoDirectionVector(totalMovement);
149−qreal DirectionalDragArea::sceneDistance() const
151− return d->sceneDistance;
154−qreal DirectionalDragArea::touchX() const
156− return d->publicPos.x();
159−qreal DirectionalDragArea::touchY() const
161− return d->publicPos.y();
164−qreal DirectionalDragArea::touchSceneX() const
166− return d->publicScenePos.x();
169−qreal DirectionalDragArea::touchSceneY() const
171− return d->publicScenePos.y();
174−bool DirectionalDragArea::dragging() const
176− return d->status == DirectionalDragAreaPrivate::Recognized;
179−bool DirectionalDragArea::pressed() const
181− return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
184−bool DirectionalDragArea::immediateRecognition() const
186− return d->immediateRecognition;
189−void DirectionalDragArea::setImmediateRecognition(bool enabled)
191− if (d->immediateRecognition != enabled) {
192− d->immediateRecognition = enabled;
193− Q_EMIT immediateRecognitionChanged(enabled);
197−bool DirectionalDragArea::monitorOnly() const
199− return d->monitorOnly;
202−void DirectionalDragArea::setMonitorOnly(bool monitorOnly)
204− if (d->monitorOnly != monitorOnly) {
205− d->monitorOnly = monitorOnly;
207− if (monitorOnly && d->status == DirectionalDragAreaPrivate::Undecided) {
208− TouchRegistry::instance()->removeCandidateOwnerForTouch(d->touchId, this);
209− // We still wanna know when it ends for keeping the composition time window up-to-date
210− TouchRegistry::instance()->addTouchWatcher(d->touchId, this);
213− Q_EMIT monitorOnlyChanged(monitorOnly);
217−void DirectionalDragArea::removeTimeConstraints()
219− d->setMaxTime(60 * 60 * 1000);
220− d->compositionTime = 0;
221− ddaDebug("removed time constraints");
224−bool DirectionalDragArea::event(QEvent *event)
226− if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
227− d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
229− } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
230− d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
233− return QQuickItem::event(event);
237−void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
239− if (event->gained()) {
241− ids.append(event->touchId());
242− ddaDebug("grabbing touch");
243− q->grabTouchPoints(ids);
245− // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
246− // emulation it will cause the emulation logic to go nuts.
247− // Thus we have to also grab the mouse in this case.
249− // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
250− // TODO: Remove this workaround once we start using Qt 5.4
252− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
253− if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
254− ddaDebug("removing mouse grabber");
255− q->window()->mouseGrabberItem()->ungrabMouse();
259− // We still wanna know when it ends for keeping the composition time window up-to-date
260− TouchRegistry::instance()->addTouchWatcher(touchId, q);
262− setStatus(WaitingForTouch);
266−void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
268− QTouchEvent *event = unownedTouchEvent->touchEvent();
270− Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
272− ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
275− case WaitingForTouch:
279− Q_ASSERT(q->isEnabled() && q->isVisible());
280− unownedTouchEvent_undecided(unownedTouchEvent);
282− default: // Recognized:
284− // Treat unowned event as if we owned it, but we are really just watching it
285− touchEvent_recognized(event);
290− activeTouches.update(event);
293−void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
295− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
297− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
298− << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
299− "Considering it as released.";
301− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
302− setStatus(WaitingForTouch);
306− const QPointF &touchScenePos = touchPoint->scenePos();
308− if (touchPoint->state() == Qt::TouchPointReleased) {
309− // touch has ended before recognition concluded
310− ddaDebug("Touch has ended before recognition concluded");
311− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
312− setStatus(WaitingForTouch);
316− previousDampedScenePos.setX(dampedScenePos.x());
317− previousDampedScenePos.setY(dampedScenePos.y());
318− dampedScenePos.update(touchScenePos);
320− if (!movingInRightDirection()) {
321− ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323− // We still wanna know when it ends for keeping the composition time window up-to-date
324− TouchRegistry::instance()->addTouchWatcher(touchId, q);
325− setStatus(WaitingForTouch);
329− if (isWithinTouchCompositionWindow()) {
330− // There's still time for some new touch to appear and ruin our party as it would be combined
331− // with our touchId one and therefore deny the possibility of a single-finger gesture.
332− ddaDebug("Sill within composition window. Let's wait more.");
336− if (movedFarEnoughAlongGestureAxis()) {
338− TouchRegistry::instance()->requestTouchOwnership(touchId, q);
340− setStatus(Recognized);
341− setPublicPos(touchPoint->pos());
342− setPublicScenePos(touchScenePos);
343− } else if (isPastMaxDistance()) {
344− ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
345− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
346− // We still wanna know when it ends for keeping the composition time window up-to-date
347− TouchRegistry::instance()->addTouchWatcher(touchId, q);
348− setStatus(WaitingForTouch);
350− ddaDebug("Didn't move far enough yet. Let's wait more.");
354−void DirectionalDragArea::touchEvent(QTouchEvent *event)
356− // TODO: Consider when more than one touch starts in the same event (although it's not possible
357− // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
359− ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
361− if (!isEnabled() || !isVisible()) {
362− QQuickItem::touchEvent(event);
366− switch (d->status) {
367− case DirectionalDragAreaPrivate::WaitingForTouch:
368− d->touchEvent_absent(event);
370− case DirectionalDragAreaPrivate::Undecided:
371− d->touchEvent_undecided(event);
373− default: // Recognized:
374− d->touchEvent_recognized(event);
378− d->activeTouches.update(event);
381−void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
383− // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
385− if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
386− // Nothing to see here. No touch starting in this event.
390− // to be proven wrong, if that's the case
391− bool allGood = true;
393− if (isWithinTouchCompositionWindow()) {
394− // too close to the last touch start. So we consider them as starting roughly at the same time.
395− // Can't be a single-touch gesture.
396− ddaDebug("A new touch point came in but we're still within time composition window. Ignoring it.");
400− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
402− const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
403− for (int i = 0; i < touchPoints.count() && allGood; ++i) {
404− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
405− if (touchPoint.state() == Qt::TouchPointPressed) {
406− if (newTouchPoint) {
407− // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
410− // that's our candidate
411− newTouchPoint = &touchPoint;
417− allGood = sanityCheckRecognitionProperties();
419− qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
425− Q_ASSERT(newTouchPoint);
427− startPos = newTouchPoint->pos();
428− startScenePos = newTouchPoint->scenePos();
429− touchId = newTouchPoint->id();
430− dampedScenePos.reset(startScenePos);
431− setPublicPos(startPos);
433− setPublicScenePos(startScenePos);
434− updateSceneDirectionVector();
436− if (recognitionIsDisabled()) {
437− // Behave like a dumb TouchArea
438− ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
439− setStatus(Recognized);
441− watchPressedTouchPoints(touchPoints);
444− TouchRegistry::instance()->requestTouchOwnership(touchId, q);
448− // just monitor the touch points for now.
450− watchPressedTouchPoints(touchPoints);
452− TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
455− setStatus(Undecided);
456− // Let the item below have it. We will monitor it and grab it later if a gesture
461− watchPressedTouchPoints(touchPoints);
466−void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
468− Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
470− // We're not interested in new touch points. We already have our candidate (touchId).
471− // But we do want to know when those new touches end for keeping the composition time
472− // window up-to-date
474− watchPressedTouchPoints(event->touchPoints());
476− if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
477− // multi-finger drags are not accepted
478− ddaDebug("Multi-finger drags are not accepted");
480− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
481− // We still wanna know when it ends for keeping the composition time window up-to-date
482− TouchRegistry::instance()->addTouchWatcher(touchId, q);
484− setStatus(WaitingForTouch);
488−void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
490− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
493− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
494− << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
495− "Considering it as released.";
496− setStatus(WaitingForTouch);
498− setPublicPos(touchPoint->pos());
499− setPublicScenePos(touchPoint->scenePos());
501− if (touchPoint->state() == Qt::TouchPointReleased) {
502− setStatus(WaitingForTouch);
507−void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
509− for (int i = 0; i < touchPoints.count(); ++i) {
510− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
511− if (touchPoint.state() == Qt::TouchPointPressed) {
512− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517−bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
519− return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
522−bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
524− return recognitionIsDisabled()
525− || (distanceThreshold < maxDistance && compositionTime < maxTime);
528−const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
530− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
531− const QTouchEvent::TouchPoint *touchPoint = 0;
532− for (int i = 0; i < touchPoints.size(); ++i) {
533− if (touchPoints.at(i).id() == touchId) {
534− touchPoint = &touchPoints.at(i);
541−bool DirectionalDragAreaPrivate::movingInRightDirection() const
543− if (direction == Direction::Horizontal || direction == Direction::Vertical) {
546− QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
547− dampedScenePos.y() - previousDampedScenePos.y());
549− qreal scalarProjection = projectOntoDirectionVector(movementVector);
551− return scalarProjection >= 0.;
555−bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
557− if (distanceThreshold <= 0.) {
558− // distance threshold check is disabled
561− QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
562− dampedScenePos.y() - startScenePos.y());
564− qreal scalarProjection = projectOntoDirectionVector(totalMovement);
566− ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
567− << ", distanceThreshold=" << distanceThreshold);
569− if (direction == Direction::Horizontal || direction == Direction::Vertical) {
570− return qAbs(scalarProjection) > distanceThreshold;
572− return scalarProjection > distanceThreshold;
577−bool DirectionalDragAreaPrivate::isPastMaxDistance() const
579− QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
580− dampedScenePos.y() - startScenePos.y());
582− qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
583− return squaredDistance > maxDistance*maxDistance;
586−void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
588− if (!q->isEnabled() || !q->isVisible()) {
589− if (status == Undecided) {
590− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
591− // We still wanna know when it ends for keeping the composition time window up-to-date
592− TouchRegistry::instance()->addTouchWatcher(touchId, q);
595− if (status != WaitingForTouch) {
596− ddaDebug("Resetting status because got disabled or made invisible");
597− setStatus(WaitingForTouch);
602−void DirectionalDragAreaPrivate::rejectGesture()
604− if (status == Undecided) {
605− ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
607− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
608− // We still wanna know when it ends for keeping the composition time window up-to-date
609− TouchRegistry::instance()->addTouchWatcher(touchId, q);
611− setStatus(WaitingForTouch);
615−void DirectionalDragAreaPrivate::setStatus(Status newStatus)
617− if (newStatus == status)
620− Status oldStatus = status;
622− if (oldStatus == Undecided) {
623− recognitionTimer->stop();
627− Q_EMIT statusChanged(status);
629− ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
631− switch (newStatus) {
632− case WaitingForTouch:
633− if (oldStatus == Recognized) {
634− Q_EMIT q->draggingChanged(false);
636− Q_EMIT q->pressedChanged(false);
639− recognitionTimer->start();
640− Q_EMIT q->pressedChanged(true);
643− if (oldStatus == WaitingForTouch) { // for immediate recognition
644− Q_EMIT q->pressedChanged(true);
646− Q_EMIT q->draggingChanged(true);
654−void DirectionalDragAreaPrivate::setPublicPos(const QPointF point)
656− bool xChanged = publicPos.x() != point.x();
657− bool yChanged = publicPos.y() != point.y();
659− // Public position should not get updated while the gesture is still being recognized
660− // (ie, Undecided status).
661− Q_ASSERT(status == WaitingForTouch || status == Recognized);
663− if (status == Recognized && !recognitionIsDisabled()) {
664− // When the gesture finally gets recognized, the finger will likely be
665− // reasonably far from the edge. If we made the contentX immediately
666− // follow the finger position it would be visually unpleasant as it
667− // would appear right next to the user's finger out of nowhere (ie,
668− // it would jump). Instead, we make contentX go towards the user's
669− // finger in several steps. ie., in an animated way.
670− QPointF delta = point - publicPos;
671− // the trick is not to go all the way (1.0) as it would cause a sudden jump
672− publicPos.rx() += 0.4 * delta.x();
673− publicPos.ry() += 0.4 * delta.y();
675− // no smoothing when initializing or if gesture recognition was immediate as there will
681− Q_EMIT q->touchXChanged(publicPos.x());
682− if (Direction::isHorizontal(direction))
683− Q_EMIT q->distanceChanged(q->distance());
687− Q_EMIT q->touchYChanged(publicPos.y());
688− if (Direction::isVertical(direction))
689− Q_EMIT q->distanceChanged(q->distance());
693−void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF point)
695− bool xChanged = publicScenePos.x() != point.x();
696− bool yChanged = publicScenePos.y() != point.y();
698− if (!xChanged && !yChanged)
701− // Public position should not get updated while the gesture is still being recognized
702− // (ie, Undecided status).
703− Q_ASSERT(status == WaitingForTouch || status == Recognized);
705− qreal oldSceneDistance = sceneDistance;
707− if (status == Recognized && !recognitionIsDisabled()) {
708− // When the gesture finally gets recognized, the finger will likely be
709− // reasonably far from the edge. If we made the contentX immediately
710− // follow the finger position it would be visually unpleasant as it
711− // would appear right next to the user's finger out of nowhere (ie,
712− // it would jump). Instead, we make contentX go towards the user's
713− // finger in several steps. ie., in an animated way.
714− QPointF delta = point - publicScenePos;
715− // the trick is not to go all the way (1.0) as it would cause a sudden jump
716− publicScenePos.rx() += 0.4 * delta.x();
717− publicScenePos.ry() += 0.4 * delta.y();
719− // no smoothing when initializing or if gesture recognition was immediate as there will
721− publicScenePos = point;
724− updateSceneDistance();
726− if (oldSceneDistance != sceneDistance) {
727− Q_EMIT q->sceneDistanceChanged(sceneDistance);
731− Q_EMIT q->touchSceneXChanged(publicScenePos.x());
735− Q_EMIT q->touchSceneYChanged(publicScenePos.y());
739−bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
742− compositionTime > 0 &&
743− !activeTouches.isEmpty() &&
744− timeSource->msecsSinceReference() <=
745− activeTouches.mostRecentStartTime() + (qint64)compositionTime;
748−void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
750− if (change == QQuickItem::ItemSceneChange) {
751− if (value.window != nullptr) {
752− value.window->installEventFilter(TouchRegistry::instance());
754− // TODO: Handle window->screen() changes (ie window changing screens)
756− qreal pixelsPerInch = value.window->screen()->physicalDotsPerInch();
757− if (pixelsPerInch < 0) {
758− // It can return garbage when run in a XVFB server (Virtual Framebuffer 'fake' X server)
762− d->setPixelsPerMm(pixelsPerInch / 25.4);
767−void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
769− dampedScenePos.setMaxDelta(1. * pixelsPerMm);
770− setDistanceThreshold(4. * pixelsPerMm);
771− maxDistance = 10. * pixelsPerMm;
774−//************************** ActiveTouchesInfo **************************
776−ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
777− : m_timeSource(timeSource)
781−void ActiveTouchesInfo::update(QTouchEvent *event)
783− if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
784− // nothing to update
785− #if ACTIVETOUCHESINFO_DEBUG
786− qDebug("[DDA::ActiveTouchesInfo] Nothing to Update");
791− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
792− for (int i = 0; i < touchPoints.count(); ++i) {
793− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
794− if (touchPoint.state() == Qt::TouchPointPressed) {
795− addTouchPoint(touchPoint.id());
796− } else if (touchPoint.state() == Qt::TouchPointReleased) {
797− removeTouchPoint(touchPoint.id());
802−#if ACTIVETOUCHESINFO_DEBUG
803−QString ActiveTouchesInfo::toString()
805− QString string = "(";
808− QTextStream stream(&string);
809− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
810− stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")";
819−#endif // ACTIVETOUCHESINFO_DEBUG
821−void ActiveTouchesInfo::addTouchPoint(int touchId)
823− ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
824− activeTouchInfo.id = touchId;
825− activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
827− #if ACTIVETOUCHESINFO_DEBUG
828− qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
832−qint64 ActiveTouchesInfo::touchStartTime(int touchId)
836− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
837− if (touchId == touchInfo->id) {
838− result = touchInfo->startTime;
845− Q_ASSERT(result != -1);
849−void ActiveTouchesInfo::removeTouchPoint(int touchId)
851− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
852− if (touchId == touchInfo->id) {
853− m_touchInfoPool.freeSlot(touchInfo);
860− #if ACTIVETOUCHESINFO_DEBUG
861− qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
865−qint64 ActiveTouchesInfo::mostRecentStartTime()
867− Q_ASSERT(!m_touchInfoPool.isEmpty());
869− qint64 highestStartTime = -1;
871− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) {
872− if (activeTouchInfo->startTime > highestStartTime) {
873− highestStartTime = activeTouchInfo->startTime;
878− return highestStartTime;
881−void DirectionalDragAreaPrivate::updateSceneDirectionVector()
883− QPointF localOrigin(0., 0.);
884− QPointF localDirection;
885− switch (direction) {
886− case Direction::Upwards:
887− localDirection.rx() = 0.;
888− localDirection.ry() = -1.;
890− case Direction::Downwards:
891− case Direction::Vertical:
892− localDirection.rx() = 0.;
893− localDirection.ry() = 1;
895− case Direction::Leftwards:
896− localDirection.rx() = -1.;
897− localDirection.ry() = 0.;
899− default: // Direction::Rightwards || Direction.Horizontal
900− localDirection.rx() = 1.;
901− localDirection.ry() = 0.;
904− QPointF sceneOrigin = q->mapToScene(localOrigin);
905− QPointF sceneDirection = q->mapToScene(localDirection);
906− sceneDirectionVector = sceneDirection - sceneOrigin;
909−qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF sceneVector) const
911− // same as dot product as sceneDirectionVector is a unit vector
912− return sceneVector.x() * sceneDirectionVector.x() +
913− sceneVector.y() * sceneDirectionVector.y();
916−DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
918− , status(WaitingForTouch)
921− , direction(Direction::Rightwards)
922− , distanceThreshold(0)
923− , distanceThresholdSquared(0.)
925− , compositionTime(60)
926− , immediateRecognition(false)
927− , recognitionTimer(nullptr)
928− , timeSource(new RealTimeSource)
929− , activeTouches(timeSource)
930− , monitorOnly(false)