on xenial help sign in
DirectionalDragArea & friends: various improvements & API grooming
792c5f3Daniel d'Andrada
libs/UbuntuGestures/CMakeLists.txt+1
@@ -5,6 +5,7 @@ set(UbuntuGestures_SOURCES
5 CandidateInactivityTimer.cpp
5 CandidateInactivityTimer.cpp
6 DebugHelpers.cpp
6 DebugHelpers.cpp
7 Timer.cpp
7 Timer.cpp
8+ TimeSource.cpp
8 TouchOwnershipEvent.cpp
9 TouchOwnershipEvent.cpp
9 TouchRegistry.cpp
10 TouchRegistry.cpp
10 UnownedTouchEvent.cpp
11 UnownedTouchEvent.cpp
libs/UbuntuGestures/CandidateInactivityTimer.h+1−1
@@ -32,7 +32,7 @@ public:
32 AbstractTimerFactory &timerFactory,
32 AbstractTimerFactory &timerFactory,
33 QObject *parent = nullptr);
33 QObject *parent = nullptr);
34
34
35− const int durationMs = 350;
35+ const int durationMs = 1000;
36
36
37Q_SIGNALS:
37Q_SIGNALS:
38 void candidateDefaulted(int touchId, QQuickItem *candidate);
38 void candidateDefaulted(int touchId, QQuickItem *candidate);
plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpprenamed

The content is the same.

plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.hrenamed+14−4
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013,2015 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -21,14 +21,14 @@
21#ifndef UBUNTUGESTURES_TIMESOURCE_H
21#ifndef UBUNTUGESTURES_TIMESOURCE_H
22#define UBUNTUGESTURES_TIMESOURCE_H
22#define UBUNTUGESTURES_TIMESOURCE_H
23
23
24−#include "UbuntuGesturesQmlGlobal.h"
24+#include "UbuntuGesturesGlobal.h"
25#include <QSharedPointer>
25#include <QSharedPointer>
26
26
27namespace UbuntuGestures {
27namespace UbuntuGestures {
28/*
28/*
29 Interface for a time source.
29 Interface for a time source.
30 */
30 */
31−class UBUNTUGESTURESQML_EXPORT TimeSource {
31+class UBUNTUGESTURES_EXPORT TimeSource {
32public:
32public:
33 virtual ~TimeSource() {}
33 virtual ~TimeSource() {}
34 /* Returns the current time in milliseconds since some reference time in the past. */
34 /* Returns the current time in milliseconds since some reference time in the past. */
@@ -40,7 +40,7 @@ typedef QSharedPointer<TimeSource> SharedTimeSource;
40 Implementation of a time source
40 Implementation of a time source
41 */
41 */
42class RealTimeSourcePrivate;
42class RealTimeSourcePrivate;
43−class RealTimeSource : public TimeSource {
43+class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
44public:
44public:
45 RealTimeSource();
45 RealTimeSource();
46 virtual ~RealTimeSource();
46 virtual ~RealTimeSource();
@@ -49,6 +49,16 @@ private:
49 RealTimeSourcePrivate *d;
49 RealTimeSourcePrivate *d;
50};
50};
51
51
52+/*
53+ A fake time source, useful for tests
54+ */
55+class FakeTimeSource : public TimeSource {
56+public:
57+ FakeTimeSource() { m_msecsSinceReference = 0; }
58+ qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59+ qint64 m_msecsSinceReference;
60+};
61+
52} // namespace UbuntuGestures
62} // namespace UbuntuGestures
53
63
54#endif // UBUNTUGESTURES_TIMESOURCE_H
64#endif // UBUNTUGESTURES_TIMESOURCE_H
libs/UbuntuGestures/Timer.cpp+63−10
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -58,13 +58,36 @@ void Timer::setSingleShot(bool value)
58
58
59/////////////////////////////////// FakeTimer //////////////////////////////////
59/////////////////////////////////// FakeTimer //////////////////////////////////
60
60
61−FakeTimer::FakeTimer(QObject *parent)
61+FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
62 : UbuntuGestures::AbstractTimer(parent)
62 : UbuntuGestures::AbstractTimer(parent)
63 , m_interval(0)
63 , m_interval(0)
64 , m_singleShot(false)
64 , m_singleShot(false)
65+ , m_timeSource(timeSource)
65{
66{
66}
67}
67
68
69+void FakeTimer::update()
70+{
71+ if (!isRunning()) {
72+ return;
73+ }
74+
75+ if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76+ if (isSingleShot()) {
77+ stop();
78+ } else {
79+ m_nextTimeoutTime += interval();
80+ }
81+ Q_EMIT timeout();
82+ }
83+}
84+
85+void FakeTimer::start()
86+{
87+ AbstractTimer::start();
88+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
89+}
90+
68int FakeTimer::interval() const
91int FakeTimer::interval() const
69{
92{
70 return m_interval;
93 return m_interval;
@@ -87,23 +110,53 @@ void FakeTimer::setSingleShot(bool value)
87
110
88/////////////////////////////////// FakeTimerFactory //////////////////////////////////
111/////////////////////////////////// FakeTimerFactory //////////////////////////////////
89
112
90−AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
113+FakeTimerFactory::FakeTimerFactory()
91{
114{
92− FakeTimer *fakeTimer = new FakeTimer(parent);
115+ m_timeSource.reset(new FakeTimeSource);
93−
116+}
94− timers.append(fakeTimer);
95
117
96− return fakeTimer;
118+FakeTimerFactory::~FakeTimerFactory()
119+{
120+ for (int i = 0; i < timers.count(); ++i) {
121+ FakeTimer *timer = timers[i].data();
122+ if (timer) {
123+ delete timer;
124+ }
125+ }
97}
126}
98
127
99−void FakeTimerFactory::makeRunningTimersTimeout()
128+void FakeTimerFactory::updateTime(qint64 targetTime)
100{
129{
130+ qint64 minTimeoutTime = targetTime;
131+
132+ for (int i = 0; i < timers.count(); ++i) {
133+ FakeTimer *timer = timers[i].data();
134+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
135+ minTimeoutTime = timer->nextTimeoutTime();
136+ }
137+ }
138+
139+ m_timeSource->m_msecsSinceReference = minTimeoutTime;
140+
101 for (int i = 0; i < timers.count(); ++i) {
141 for (int i = 0; i < timers.count(); ++i) {
102 FakeTimer *timer = timers[i].data();
142 FakeTimer *timer = timers[i].data();
103− if (timer && timer->isRunning()) {
143+ if (timer) {
104− timer->emitTimeout();
144+ timer->update();
105 }
145 }
106 }
146 }
147+
148+ if (m_timeSource->msecsSinceReference() < targetTime) {
149+ updateTime(targetTime);
150+ }
151+}
152+
153+AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
154+{
155+ FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
156+
157+ timers.append(fakeTimer);
158+
159+ return fakeTimer;
107}
160}
108
161
109} // namespace UbuntuGestures
162} // namespace UbuntuGestures
libs/UbuntuGestures/Timer.h+15−3
@@ -18,6 +18,7 @@
18#define UBUNTUGESTURES_TIMER_H
18#define UBUNTUGESTURES_TIMER_H
19
19
20#include "UbuntuGesturesGlobal.h"
20#include "UbuntuGesturesGlobal.h"
21+#include "TimeSource.h"
21
22
22#include <QObject>
23#include <QObject>
23#include <QPointer>
24#include <QPointer>
@@ -66,17 +67,21 @@ class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer
66{
67{
67 Q_OBJECT
68 Q_OBJECT
68public:
69public:
69− FakeTimer(QObject *parent = nullptr);
70+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
70
71
71− virtual void emitTimeout() { Q_EMIT timeout(); }
72+ void update();
73+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
72
74
73 int interval() const override;
75 int interval() const override;
74 void setInterval(int msecs) override;
76 void setInterval(int msecs) override;
77+ void start() override;
75 bool isSingleShot() const override;
78 bool isSingleShot() const override;
76 void setSingleShot(bool value) override;
79 void setSingleShot(bool value) override;
77private:
80private:
78 int m_interval;
81 int m_interval;
79 bool m_singleShot;
82 bool m_singleShot;
83+ SharedTimeSource m_timeSource;
84+ qint64 m_nextTimeoutTime;
80};
85};
81
86
82class UBUNTUGESTURES_EXPORT AbstractTimerFactory
87class UBUNTUGESTURES_EXPORT AbstractTimerFactory
@@ -95,9 +100,16 @@ public:
95class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
100class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
96{
101{
97public:
102public:
103+ FakeTimerFactory();
104+ virtual ~FakeTimerFactory();
105+
106+ void updateTime(qint64 msecsSinceReference);
107+ QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108+
98 AbstractTimer *createTimer(QObject *parent = nullptr) override;
109 AbstractTimer *createTimer(QObject *parent = nullptr) override;
99− void makeRunningTimersTimeout();
100 QList<QPointer<FakeTimer>> timers;
110 QList<QPointer<FakeTimer>> timers;
111+private:
112+ QSharedPointer<FakeTimeSource> m_timeSource;
101};
113};
102
114
103} // namespace UbuntuGestures
115} // namespace UbuntuGestures
libs/UbuntuGestures/TouchRegistry.cpp+12−14
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -95,10 +95,6 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
95 // for each point and there should not be many active points at any given moment.
95 // for each point and there should not be many active points at any given moment.
96 // But having three nested for-loops does scare.
96 // But having three nested for-loops does scare.
97
97
98− // TODO: Don't send it to the object that is already receiving the regular event
99− // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
100− // Problem is, we cannnot easily get this information.
101−
102 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
98 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
103
99
104 // Maps an item to the touches in this event he should be informed about.
100 // Maps an item to the touches in this event he should be informed about.
@@ -118,7 +114,9 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
118 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
114 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119 CandidateInfo &candidate = touchInfo->candidates[i];
115 CandidateInfo &candidate = touchInfo->candidates[i];
120 Q_ASSERT(!candidate.item.isNull());
116 Q_ASSERT(!candidate.item.isNull());
117+ if (candidate.state != CandidateInfo::InterimOwner) {
121− touchIdsForItems[candidate.item.data()].append(touchInfo->id);
118+ touchIdsForItems[candidate.item.data()].append(touchInfo->id);
119+ }
122 }
120 }
123 }
121 }
124
122
@@ -260,7 +258,7 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
260 // TODO: Check if candidate already exists
258 // TODO: Check if candidate already exists
261
259
262 CandidateInfo candidateInfo;
260 CandidateInfo candidateInfo;
263− candidateInfo.undecided = true;
261+ candidateInfo.state = CandidateInfo::Undecided;
264 candidateInfo.item = candidate;
262 candidateInfo.item = candidate;
265 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
263 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
266 *m_timerFactory,
264 *m_timerFactory,
@@ -301,8 +299,8 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
301 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
299 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
302 CandidateInfo &candidateInfo = touchInfo->candidates[i];
300 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303 if (candidateInfo.item == candidate) {
301 if (candidateInfo.item == candidate) {
304− Q_ASSERT(i > 0 || candidateInfo.undecided);
302+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
305− if (i == 0 && !candidateInfo.undecided) {
303+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
306 qCritical("TouchRegistry: touch owner is being removed.");
304 qCritical("TouchRegistry: touch owner is being removed.");
307 }
305 }
308 delete candidateInfo.inactivityTimer;
306 delete candidateInfo.inactivityTimer;
@@ -340,7 +338,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
340 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
338 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
341 CandidateInfo &candidateInfo = touchInfo->candidates[i];
339 CandidateInfo &candidateInfo = touchInfo->candidates[i];
342 if (candidateInfo.item == candidate) {
340 if (candidateInfo.item == candidate) {
343− candidateInfo.undecided = false;
341+ candidateInfo.state = CandidateInfo::Requested;
344 delete candidateInfo.inactivityTimer;
342 delete candidateInfo.inactivityTimer;
345 candidateInfo.inactivityTimer = nullptr;
343 candidateInfo.inactivityTimer = nullptr;
346 candidateIndex = i;
344 candidateIndex = i;
@@ -351,7 +349,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
351 // add it as a candidate if not present yet
349 // add it as a candidate if not present yet
352 if (candidateIndex < 0) {
350 if (candidateIndex < 0) {
353 CandidateInfo candidateInfo;
351 CandidateInfo candidateInfo;
354− candidateInfo.undecided = false;
352+ candidateInfo.state = CandidateInfo::InterimOwner;
355 candidateInfo.item = candidate;
353 candidateInfo.item = candidate;
356 candidateInfo.inactivityTimer = nullptr;
354 candidateInfo.inactivityTimer = nullptr;
357 touchInfo->candidates.append(candidateInfo);
355 touchInfo->candidates.append(candidateInfo);
@@ -407,8 +405,8 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
407 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
405 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
408 CandidateInfo &candidateInfo = touchInfo->candidates[i];
406 CandidateInfo &candidateInfo = touchInfo->candidates[i];
409 if (candidateInfo.item == candidate) {
407 if (candidateInfo.item == candidate) {
410− Q_ASSERT(i > 0 || candidateInfo.undecided);
408+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
411− if (i == 0 && !candidateInfo.undecided) {
409+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
412 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
410 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
413 << ") as it already owns touch" << id;
411 << ") as it already owns touch" << id;
414 return;
412 return;
@@ -467,7 +465,7 @@ void TouchRegistry::TouchInfo::init(int id)
467
465
468bool TouchRegistry::TouchInfo::isOwned() const
466bool TouchRegistry::TouchInfo::isOwned() const
469{
467{
470− return !candidates.isEmpty() && !candidates.first().undecided;
468+ return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
471}
469}
472
470
473bool TouchRegistry::TouchInfo::ended() const
471bool TouchRegistry::TouchInfo::ended() const
libs/UbuntuGestures/TouchRegistry.h+18−4
@@ -79,9 +79,11 @@ namespace UbuntuGestures {
79 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
79 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
80 would be instead:
80 would be instead:
81 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
81 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82− return true;
82+ touchEvent->accept();
83+ He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
84+ will keep sending touch updates to him already). Eventually he will be notified once ownership has
83− He would then be notified once ownership has been granted to him, from which point onwards he could
85+ been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
84− safely assume other TouchRegistry users wouldn't snatch this touch away from him.
86+ other TouchRegistry users wouldn't snatch this touch away from him.
85
87
86 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
88 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
87
89
@@ -132,7 +134,19 @@ private Q_SLOTS:
132private:
134private:
133 class CandidateInfo {
135 class CandidateInfo {
134 public:
136 public:
135− bool undecided;
137+ enum {
138+ // A candidate owner that doesn't yet know for sure whether he wants the touch point
139+ // (gesture recognition is stilll going on)
140+ Undecided = 0,
141+ // A candidate owner that wants the touch but hasn't been granted it yet,
142+ // most likely because there's an undecided candidate with higher priority
143+ Requested = 1,
144+ // An item that is the interim owner of the touch, receiving QTouchEvents of it
145+ // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
146+ // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
147+ // yet because of undecided candidates higher up.
148+ InterimOwner = 2
149+ } state;
136 // TODO: Prune candidates that become null and resolve ownership accordingly.
150 // TODO: Prune candidates that become null and resolve ownership accordingly.
137 QPointer<QQuickItem> item;
151 QPointer<QQuickItem> item;
138 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
152 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
plugins/Ubuntu/Gestures/CMakeLists.txt+1−1
@@ -4,10 +4,10 @@ remove_definitions(-DQT_NO_KEYWORDS)
4set(UbuntuGesturesQml_SOURCES
4set(UbuntuGesturesQml_SOURCES
5 plugin.cpp
5 plugin.cpp
6 AxisVelocityCalculator.cpp
6 AxisVelocityCalculator.cpp
7+ Damper.cpp
7 Direction.cpp
8 Direction.cpp
8 DirectionalDragArea.cpp
9 DirectionalDragArea.cpp
9 PressedOutsideNotifier.cpp
10 PressedOutsideNotifier.cpp
10− TimeSource.cpp
11 TouchDispatcher.cpp
11 TouchDispatcher.cpp
12 TouchGate.cpp
12 TouchGate.cpp
13)
13)
plugins/Ubuntu/Gestures/Damper.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2015 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 "Damper.h"
18+#include <QDebug>
19+
20+QDebug operator<<(QDebug dbg, const DampedPointF &p)
21+{
22+ dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23+ return dbg.space();
24+}
plugins/Ubuntu/Gestures/Damper.h+2
@@ -84,4 +84,6 @@ private:
84 Damper<qreal> m_y;
84 Damper<qreal> m_y;
85};
85};
86
86
87+QDebug operator<<(QDebug dbg, const DampedPointF &p);
88+
87#endif // UBUNTU_GESTURES_DAMPER_H
89#endif // UBUNTU_GESTURES_DAMPER_H
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+309−323
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
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
@@ -21,6 +21,7 @@
21
21
22#include <QQuickWindow>
22#include <QQuickWindow>
23#include <QtCore/qmath.h>
23#include <QtCore/qmath.h>
24+#include <QScreen>
24#include <QDebug>
25#include <QDebug>
25
26
26#pragma GCC diagnostic push
27#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
33#include "TouchRegistry.h"
34#include "TouchRegistry.h"
34#include "UnownedTouchEvent.h"
35#include "UnownedTouchEvent.h"
35
36
37+#include "DirectionalDragArea_p.h"
38+
36using namespace UbuntuGestures;
39using namespace UbuntuGestures;
37
40
38#if DIRECTIONALDRAGAREA_DEBUG
41#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@ using namespace UbuntuGestures;
40#include "DebugHelpers.h"
43#include "DebugHelpers.h"
41
44
42namespace {
45namespace {
43−const char *statusToString(DirectionalDragArea::Status status)
46+const char *statusToString(DirectionalDragAreaPrivate::Status status)
44{
47{
45− if (status == DirectionalDragArea::WaitingForTouch) {
48+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
46 return "WaitingForTouch";
49 return "WaitingForTouch";
47− } else if (status == DirectionalDragArea::Undecided) {
50+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
48 return "Undecided";
51 return "Undecided";
49 } else {
52 } else {
50 return "Recognized";
53 return "Recognized";
@@ -56,205 +59,156 @@ const char *statusToString(DirectionalDragArea::Status status)
56#define ddaDebug(params) ((void)0)
59#define ddaDebug(params) ((void)0)
57#endif // DIRECTIONALDRAGAREA_DEBUG
60#endif // DIRECTIONALDRAGAREA_DEBUG
58
61
59−
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
62DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
61 : QQuickItem(parent)
63 : QQuickItem(parent)
62− , m_status(WaitingForTouch)
64+ , d(new DirectionalDragAreaPrivate(this))
63− , m_sceneDistance(0)
64− , m_touchId(-1)
65− , m_direction(Direction::Rightwards)
66− , m_wideningAngle(0)
67− , m_wideningFactor(0)
68− , m_distanceThreshold(0)
69− , m_distanceThresholdSquared(0.)
70− , m_minSpeed(0)
71− , m_maxSilenceTime(200)
72− , m_silenceTime(0)
73− , m_compositionTime(60)
74− , m_numSamplesOnLastSpeedCheck(0)
75− , m_recognitionTimer(0)
76− , m_velocityCalculator(0)
77− , m_timeSource(new RealTimeSource)
78− , m_activeTouches(m_timeSource)
79−{
80− setRecognitionTimer(new Timer(this));
81− m_recognitionTimer->setInterval(60);
82− m_recognitionTimer->setSingleShot(false);
83−
84− m_velocityCalculator = new AxisVelocityCalculator(this);
85−
86− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87− connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
88−}
89−
90−Direction::Type DirectionalDragArea::direction() const
91−{
92− return m_direction;
93−}
94−
95−void DirectionalDragArea::setDirection(Direction::Type direction)
96−{
97− if (direction != m_direction) {
98− m_direction = direction;
99− Q_EMIT directionChanged(m_direction);
100− }
101−}
102−
103−void DirectionalDragArea::setMaxDeviation(qreal value)
104{
65{
105− if (m_dampedScenePos.maxDelta() != value) {
66+ d->setRecognitionTimer(new Timer(this));
106− m_dampedScenePos.setMaxDelta(value);
67+ d->recognitionTimer->setInterval(d->maxTime);
107− Q_EMIT maxDeviationChanged(value);
68+ d->recognitionTimer->setSingleShot(true);
108− }
109−}
110
69
111−qreal DirectionalDragArea::wideningAngle() const
70+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
112−{
71+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
113− return m_wideningAngle;
114}
72}
115
73
116−void DirectionalDragArea::setWideningAngle(qreal angle)
74+Direction::Type DirectionalDragArea::direction() const
117{
75{
118− if (angle == m_wideningAngle)
119− return;
76+ return d->direction;
120−
121− m_wideningAngle = angle;
122−
123− // wideningFactor = pow(cosine(angle), 2)
124− {
125− qreal angleRadians = angle * M_PI / 180.0;
126− m_wideningFactor = qCos(angleRadians);
127− m_wideningFactor = m_wideningFactor * m_wideningFactor;
128− }
129−
130− Q_EMIT wideningAngleChanged(angle);
131}
77}
132
78
133−void DirectionalDragArea::setDistanceThreshold(qreal value)
79+void DirectionalDragArea::setDirection(Direction::Type direction)
134−{
135− if (m_distanceThreshold != value) {
136− m_distanceThreshold = value;
137− m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138− Q_EMIT distanceThresholdChanged(value);
139− }
140−}
141−
142−void DirectionalDragArea::setMinSpeed(qreal value)
143{
80{
144− if (m_minSpeed != value) {
81+ if (direction != d->direction) {
145− m_minSpeed = value;
82+ d->direction = direction;
146− Q_EMIT minSpeedChanged(value);
83+ Q_EMIT directionChanged(d->direction);
147 }
84 }
148}
85}
149
86
150−void DirectionalDragArea::setMaxSilenceTime(int value)
87+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
151{
88{
152− if (m_maxSilenceTime != value) {
89+ if (distanceThreshold != value) {
153− m_maxSilenceTime = value;
90+ distanceThreshold = value;
154− Q_EMIT maxSilenceTimeChanged(value);
91+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
155 }
92 }
156}
93}
157
94
158−void DirectionalDragArea::setCompositionTime(int value)
95+void DirectionalDragAreaPrivate::setMaxTime(int value)
159{
96{
160− if (m_compositionTime != value) {
97+ if (maxTime != value) {
161− m_compositionTime = value;
98+ maxTime = value;
162− Q_EMIT compositionTimeChanged(value);
99+ recognitionTimer->setInterval(maxTime);
163 }
100 }
164}
101}
165
102
166−void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
103+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
167{
104{
168 int interval = 0;
105 int interval = 0;
169 bool timerWasRunning = false;
106 bool timerWasRunning = false;
170 bool wasSingleShot = false;
107 bool wasSingleShot = false;
171
108
172 // can be null when called from the constructor
109 // can be null when called from the constructor
173− if (m_recognitionTimer) {
110+ if (recognitionTimer) {
174− interval = m_recognitionTimer->interval();
111+ interval = recognitionTimer->interval();
175− timerWasRunning = m_recognitionTimer->isRunning();
112+ timerWasRunning = recognitionTimer->isRunning();
176− if (m_recognitionTimer->parent() == this) {
113+ if (recognitionTimer->parent() == this) {
177− delete m_recognitionTimer;
114+ delete recognitionTimer;
178 }
115 }
179 }
116 }
180
117
181− m_recognitionTimer = timer;
118+ recognitionTimer = timer;
182 timer->setInterval(interval);
119 timer->setInterval(interval);
183 timer->setSingleShot(wasSingleShot);
120 timer->setSingleShot(wasSingleShot);
184 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
121 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
185− this, &DirectionalDragArea::checkSpeed);
122+ this, &DirectionalDragAreaPrivate::rejectGesture);
186 if (timerWasRunning) {
123 if (timerWasRunning) {
187− m_recognitionTimer->start();
124+ recognitionTimer->start();
188 }
125 }
189}
126}
190
127
191−void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
128+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
192{
129{
193− m_timeSource = timeSource;
130+ this->timeSource = timeSource;
194− m_velocityCalculator->setTimeSource(timeSource);
195− m_activeTouches.m_timeSource = timeSource;
131+ activeTouches.m_timeSource = timeSource;
196}
132}
197
133
198qreal DirectionalDragArea::distance() const
134qreal DirectionalDragArea::distance() const
199{
135{
200− if (Direction::isHorizontal(m_direction)) {
136+ if (Direction::isHorizontal(d->direction)) {
201− return m_previousPos.x() - m_startPos.x();
137+ return d->publicPos.x() - d->startPos.x();
202 } else {
138 } else {
203− return m_previousPos.y() - m_startPos.y();
139+ return d->publicPos.y() - d->startPos.y();
204 }
140 }
205}
141}
206
142
207−void DirectionalDragArea::updateSceneDistance()
143+void DirectionalDragAreaPrivate::updateSceneDistance()
208{
144{
209− QPointF totalMovement = m_previousScenePos - m_startScenePos;
145+ QPointF totalMovement = publicScenePos - startScenePos;
210− m_sceneDistance = projectOntoDirectionVector(totalMovement);
146+ sceneDistance = projectOntoDirectionVector(totalMovement);
211}
147}
212
148
213qreal DirectionalDragArea::sceneDistance() const
149qreal DirectionalDragArea::sceneDistance() const
214{
150{
215− return m_sceneDistance;
151+ return d->sceneDistance;
216}
152}
217
153
218qreal DirectionalDragArea::touchX() const
154qreal DirectionalDragArea::touchX() const
219{
155{
220− return m_previousPos.x();
156+ return d->publicPos.x();
221}
157}
222
158
223qreal DirectionalDragArea::touchY() const
159qreal DirectionalDragArea::touchY() const
224{
160{
225− return m_previousPos.y();
161+ return d->publicPos.y();
226}
162}
227
163
228qreal DirectionalDragArea::touchSceneX() const
164qreal DirectionalDragArea::touchSceneX() const
229{
165{
230− return m_previousScenePos.x();
166+ return d->publicScenePos.x();
231}
167}
232
168
233qreal DirectionalDragArea::touchSceneY() const
169qreal DirectionalDragArea::touchSceneY() const
234{
170{
235− return m_previousScenePos.y();
171+ return d->publicScenePos.y();
172+}
173+
174+bool DirectionalDragArea::dragging() const
175+{
176+ return d->status == DirectionalDragAreaPrivate::Recognized;
177+}
178+
179+bool DirectionalDragArea::immediateRecognition() const
180+{
181+ return d->immediateRecognition;
182+}
183+
184+void DirectionalDragArea::setImmediateRecognition(bool enabled)
185+{
186+ if (d->immediateRecognition != enabled) {
187+ d->immediateRecognition = enabled;
188+ Q_EMIT immediateRecognitionChanged(enabled);
189+ }
236}
190}
237
191
238bool DirectionalDragArea::event(QEvent *event)
192bool DirectionalDragArea::event(QEvent *event)
239{
193{
240 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
194 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241− touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
195+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242 return true;
196 return true;
243 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
197 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244− unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
198+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245 return true;
199 return true;
246 } else {
200 } else {
247 return QQuickItem::event(event);
201 return QQuickItem::event(event);
248 }
202 }
249}
203}
250
204
251−void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
205+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252{
206{
253 if (event->gained()) {
207 if (event->gained()) {
254 QVector<int> ids;
208 QVector<int> ids;
255 ids.append(event->touchId());
209 ids.append(event->touchId());
256 ddaDebug("grabbing touch");
210 ddaDebug("grabbing touch");
257− grabTouchPoints(ids);
211+ q->grabTouchPoints(ids);
258
212
259 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
213 // 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.
214 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +216,35 @@ void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
262 //
216 //
263 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
217 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264 // TODO: Remove this workaround once we start using Qt 5.4
218 // TODO: Remove this workaround once we start using Qt 5.4
265− if (window()) {
219+ if (q->window()) {
266− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
220+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
267− if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
221+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268 ddaDebug("removing mouse grabber");
222 ddaDebug("removing mouse grabber");
269− window()->mouseGrabberItem()->ungrabMouse();
223+ q->window()->mouseGrabberItem()->ungrabMouse();
270 }
224 }
271 }
225 }
272 } else {
226 } else {
273 // We still wanna know when it ends for keeping the composition time window up-to-date
227 // We still wanna know when it ends for keeping the composition time window up-to-date
274− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
228+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
275
229
276 setStatus(WaitingForTouch);
230 setStatus(WaitingForTouch);
277 }
231 }
278}
232}
279
233
280−void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
234+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281{
235{
282 QTouchEvent *event = unownedTouchEvent->touchEvent();
236 QTouchEvent *event = unownedTouchEvent->touchEvent();
283
237
284 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
238 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285
239
286− ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
240+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287
241
288− switch (m_status) {
242+ switch (status) {
289 case WaitingForTouch:
243 case WaitingForTouch:
290 // do nothing
244 // do nothing
291 break;
245 break;
292 case Undecided:
246 case Undecided:
293− Q_ASSERT(isEnabled() && isVisible());
247+ Q_ASSERT(q->isEnabled() && q->isVisible());
294 unownedTouchEvent_undecided(unownedTouchEvent);
248 unownedTouchEvent_undecided(unownedTouchEvent);
295 break;
249 break;
296 default: // Recognized:
250 default: // Recognized:
@@ -298,18 +252,18 @@ void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent
298 break;
252 break;
299 }
253 }
300
254
301− m_activeTouches.update(event);
255+ activeTouches.update(event);
302}
256}
303
257
304−void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
258+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305{
259{
306 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
260 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307 if (!touchPoint) {
261 if (!touchPoint) {
308− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
262+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
263 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310 "Considering it as released.";
264 "Considering it as released.";
311
265
312− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
266+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313 setStatus(WaitingForTouch);
267 setStatus(WaitingForTouch);
314 return;
268 return;
315 }
269 }
@@ -319,48 +273,42 @@ void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unowned
319 if (touchPoint->state() == Qt::TouchPointReleased) {
273 if (touchPoint->state() == Qt::TouchPointReleased) {
320 // touch has ended before recognition concluded
274 // touch has ended before recognition concluded
321 ddaDebug("Touch has ended before recognition concluded");
275 ddaDebug("Touch has ended before recognition concluded");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
276+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323− emitSignalIfTapped();
324 setStatus(WaitingForTouch);
277 setStatus(WaitingForTouch);
325 return;
278 return;
326 }
279 }
327
280
328− m_previousDampedScenePos.setX(m_dampedScenePos.x());
281+ previousDampedScenePos.setX(dampedScenePos.x());
329− m_previousDampedScenePos.setY(m_dampedScenePos.y());
282+ previousDampedScenePos.setY(dampedScenePos.y());
330− m_dampedScenePos.update(touchScenePos);
283+ dampedScenePos.update(touchScenePos);
331− updateVelocityCalculator(touchScenePos);
332−
333− if (!pointInsideAllowedArea()) {
334− ddaDebug("Rejecting gesture because touch point is outside allowed area.");
335− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
336− // We still wanna know when it ends for keeping the composition time window up-to-date
337− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
338− setStatus(WaitingForTouch);
339− return;
340− }
341
284
342 if (!movingInRightDirection()) {
285 if (!movingInRightDirection()) {
343 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
286 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
287+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345 // We still wanna know when it ends for keeping the composition time window up-to-date
288 // We still wanna know when it ends for keeping the composition time window up-to-date
346− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
289+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
347 setStatus(WaitingForTouch);
290 setStatus(WaitingForTouch);
348 return;
291 return;
349 }
292 }
350
293
351− setPreviousPos(touchPoint->pos());
352− setPreviousScenePos(touchScenePos);
353−
354 if (isWithinTouchCompositionWindow()) {
294 if (isWithinTouchCompositionWindow()) {
355 // There's still time for some new touch to appear and ruin our party as it would be combined
295 // There's still time for some new touch to appear and ruin our party as it would be combined
356− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
296+ // with our touchId one and therefore deny the possibility of a single-finger gesture.
357 ddaDebug("Sill within composition window. Let's wait more.");
297 ddaDebug("Sill within composition window. Let's wait more.");
358 return;
298 return;
359 }
299 }
360
300
361− if (movedFarEnough(touchScenePos)) {
301+ if (movedFarEnoughAlongGestureAxis()) {
362− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
302+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363 setStatus(Recognized);
303 setStatus(Recognized);
304+ setPublicPos(touchPoint->pos());
305+ setPublicScenePos(touchScenePos);
306+ } else if (isPastMaxDistance()) {
307+ ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
308+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
309+ // We still wanna know when it ends for keeping the composition time window up-to-date
310+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
311+ setStatus(WaitingForTouch);
364 } else {
312 } else {
365 ddaDebug("Didn't move far enough yet. Let's wait more.");
313 ddaDebug("Didn't move far enough yet. Let's wait more.");
366 }
314 }
@@ -371,29 +319,29 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
371 // TODO: Consider when more than one touch starts in the same event (although it's not possible
319 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
320 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373
321
374− ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
322+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375
323
376 if (!isEnabled() || !isVisible()) {
324 if (!isEnabled() || !isVisible()) {
377 QQuickItem::touchEvent(event);
325 QQuickItem::touchEvent(event);
378 return;
326 return;
379 }
327 }
380
328
381− switch (m_status) {
329+ switch (d->status) {
382− case WaitingForTouch:
330+ case DirectionalDragAreaPrivate::WaitingForTouch:
383− touchEvent_absent(event);
331+ d->touchEvent_absent(event);
384 break;
332 break;
385− case Undecided:
333+ case DirectionalDragAreaPrivate::Undecided:
386− touchEvent_undecided(event);
334+ d->touchEvent_undecided(event);
387 break;
335 break;
388 default: // Recognized:
336 default: // Recognized:
389− touchEvent_recognized(event);
337+ d->touchEvent_recognized(event);
390 break;
338 break;
391 }
339 }
392
340
393− m_activeTouches.update(event);
341+ d->activeTouches.update(event);
394}
342}
395
343
396−void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
344+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397{
345{
398 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
346 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399
347
@@ -423,36 +371,41 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
423 allGood = false;
371 allGood = false;
424 } else {
372 } else {
425 // that's our candidate
373 // that's our candidate
426− m_touchId = touchPoint.id();
374+ touchId = touchPoint.id();
427 newTouchPoint = &touchPoint;
375 newTouchPoint = &touchPoint;
428 }
376 }
429 }
377 }
430 }
378 }
431
379
380+ if (allGood) {
381+ allGood = sanityCheckRecognitionProperties();
382+ if (!allGood) {
383+ qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
384+ " is impossible");
385+ }
386+ }
387+
432 if (allGood) {
388 if (allGood) {
433 Q_ASSERT(newTouchPoint);
389 Q_ASSERT(newTouchPoint);
434
390
435− m_startPos = newTouchPoint->pos();
391+ startPos = newTouchPoint->pos();
436− m_startScenePos = newTouchPoint->scenePos();
392+ startScenePos = newTouchPoint->scenePos();
437− m_touchId = newTouchPoint->id();
393+ touchId = newTouchPoint->id();
438− m_dampedScenePos.reset(m_startScenePos);
394+ dampedScenePos.reset(startScenePos);
439− m_velocityCalculator->setTrackedPosition(0.);
395+ setPublicPos(startPos);
440− m_velocityCalculator->reset();
396+
441− m_numSamplesOnLastSpeedCheck = 0;
397+ setPublicScenePos(startScenePos);
442− m_silenceTime = 0;
443− setPreviousPos(m_startPos);
444− setPreviousScenePos(m_startScenePos);
445 updateSceneDirectionVector();
398 updateSceneDirectionVector();
446
399
447 if (recognitionIsDisabled()) {
400 if (recognitionIsDisabled()) {
448 // Behave like a dumb TouchArea
401 // Behave like a dumb TouchArea
449 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
402 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
403+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451 setStatus(Recognized);
404 setStatus(Recognized);
452 event->accept();
405 event->accept();
453 } else {
406 } else {
454 // just monitor the touch points for now.
407 // just monitor the touch points for now.
455− TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
408+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456
409
457 setStatus(Undecided);
410 setStatus(Undecided);
458 // Let the item below have it. We will monitor it and grab it later if a gesture
411 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +418,11 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
465 }
418 }
466}
419}
467
420
468−void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
421+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469{
422{
470− Q_ASSERT(event->type() == QEvent::TouchBegin);
471 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
423 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472
424
473− // We're not interested in new touch points. We already have our candidate (m_touchId).
425+ // We're not interested in new touch points. We already have our candidate (touchId).
474 // But we do want to know when those new touches end for keeping the composition time
426 // But we do want to know when those new touches end for keeping the composition time
475 // window up-to-date
427 // window up-to-date
476 event->ignore();
428 event->ignore();
@@ -480,63 +432,60 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
480 // multi-finger drags are not accepted
432 // multi-finger drags are not accepted
481 ddaDebug("Multi-finger drags are not accepted");
433 ddaDebug("Multi-finger drags are not accepted");
482
434
483− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
435+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484 // We still wanna know when it ends for keeping the composition time window up-to-date
436 // We still wanna know when it ends for keeping the composition time window up-to-date
485− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
437+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
486
438
487 setStatus(WaitingForTouch);
439 setStatus(WaitingForTouch);
488 }
440 }
489}
441}
490
442
491−void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
443+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492{
444{
493 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
445 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494
446
495 if (!touchPoint) {
447 if (!touchPoint) {
496− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
448+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
449 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498 "Considering it as released.";
450 "Considering it as released.";
499 setStatus(WaitingForTouch);
451 setStatus(WaitingForTouch);
500 } else {
452 } else {
501− setPreviousPos(touchPoint->pos());
453+ setPublicPos(touchPoint->pos());
502− setPreviousScenePos(touchPoint->scenePos());
454+ setPublicScenePos(touchPoint->scenePos());
503
455
504 if (touchPoint->state() == Qt::TouchPointReleased) {
456 if (touchPoint->state() == Qt::TouchPointReleased) {
505− emitSignalIfTapped();
506 setStatus(WaitingForTouch);
457 setStatus(WaitingForTouch);
507 }
458 }
508 }
459 }
509}
460}
510
461
511−void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
462+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512{
463{
513 for (int i = 0; i < touchPoints.count(); ++i) {
464 for (int i = 0; i < touchPoints.count(); ++i) {
514 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
465 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515 if (touchPoint.state() == Qt::TouchPointPressed) {
466 if (touchPoint.state() == Qt::TouchPointPressed) {
516− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
467+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517 }
468 }
518 }
469 }
519}
470}
520
471
521−bool DirectionalDragArea::recognitionIsDisabled() const
472+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522{
473{
523− return distanceThreshold() <= 0 && compositionTime() <= 0;
474+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524}
475}
525
476
526−void DirectionalDragArea::emitSignalIfTapped()
477+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527{
478{
528− qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
479+ return recognitionIsDisabled()
529− if (touchDuration <= maxTapDuration()) {
480+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
530− Q_EMIT tapped();
531− }
532}
481}
533
482
534−const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
483+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
535{
484{
536 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
485 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537 const QTouchEvent::TouchPoint *touchPoint = 0;
486 const QTouchEvent::TouchPoint *touchPoint = 0;
538 for (int i = 0; i < touchPoints.size(); ++i) {
487 for (int i = 0; i < touchPoints.size(); ++i) {
539− if (touchPoints.at(i).id() == m_touchId) {
488+ if (touchPoints.at(i).id() == touchId) {
540 touchPoint = &touchPoints.at(i);
489 touchPoint = &touchPoints.at(i);
541 break;
490 break;
542 }
491 }
@@ -544,39 +493,13 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
544 return touchPoint;
493 return touchPoint;
545}
494}
546
495
547−bool DirectionalDragArea::pointInsideAllowedArea() const
548−{
549− // NB: Using squared values to avoid computing the square root to find
550− // the length totalMovement
551−
552− QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
553− m_dampedScenePos.y() - m_startScenePos.y());
554−
555− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
556− totalMovement.y() * totalMovement.y();
557−
558− if (squaredTotalMovSize == 0.) {
559− // didn't move
560− return true;
561− }
562−
563− qreal projectedMovement = projectOntoDirectionVector(totalMovement);
564−
565−
566− qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
567−
568− // Same as:
569− // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
570− return cosineAngleSquared >= m_wideningFactor;
571−}
572−
573−bool DirectionalDragArea::movingInRightDirection() const
496+bool DirectionalDragAreaPrivate::movingInRightDirection() const
574{
497{
575− if (m_direction == Direction::Horizontal) {
498+ if (direction == Direction::Horizontal) {
576 return true;
499 return true;
577 } else {
500 } else {
578− QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
501+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
579− m_dampedScenePos.y() - m_previousDampedScenePos.y());
502+ dampedScenePos.y() - previousDampedScenePos.y());
580
503
581 qreal scalarProjection = projectOntoDirectionVector(movementVector);
504 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582
505
@@ -584,96 +507,93 @@ bool DirectionalDragArea::movingInRightDirection() const
584 }
507 }
585}
508}
586
509
587−bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
510+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588{
511{
589− if (m_distanceThreshold <= 0.) {
512+ if (distanceThreshold <= 0.) {
590 // distance threshold check is disabled
513 // distance threshold check is disabled
591 return true;
514 return true;
592 } else {
515 } else {
593− QPointF totalMovement(point.x() - m_startScenePos.x(),
516+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
594− point.y() - m_startScenePos.y());
517+ dampedScenePos.y() - startScenePos.y());
518+
519+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
595
520
596− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
521+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
597− totalMovement.y() * totalMovement.y();
522+ << ", distanceThreshold=" << distanceThreshold);
598
523
599− return squaredTotalMovSize > m_distanceThresholdSquared;
524+ if (direction == Direction::Horizontal) {
525+ return qAbs(scalarProjection) > distanceThreshold;
526+ } else {
527+ return scalarProjection > distanceThreshold;
528+ }
600 }
529 }
601}
530}
602
531
603−void DirectionalDragArea::checkSpeed()
532+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
604{
533{
605− Q_ASSERT(m_status == Undecided);
534+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
535+ dampedScenePos.y() - startScenePos.y());
606
536
607− if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
537+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
608− qreal speed = qFabs(m_velocityCalculator->calculate());
538+ return squaredDistance > maxDistance*maxDistance;
609− qreal minSpeedMsecs = m_minSpeed / 1000.0;
539+}
610
540
611− if (speed < minSpeedMsecs) {
541+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
612− ddaDebug("Rejecting gesture because it's below minimum speed.");
542+{
543+ if (!q->isEnabled() || !q->isVisible()) {
544+ if (status == Undecided) {
613− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
545+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
546+ // We still wanna know when it ends for keeping the composition time window up-to-date
614− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
547+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
615− setStatus(WaitingForTouch);
616 }
548 }
617− }
618−
619− if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
620− m_silenceTime += m_recognitionTimer->interval();
621
549
622− if (m_silenceTime > m_maxSilenceTime) {
550+ if (status != WaitingForTouch) {
623− ddaDebug("Rejecting gesture because its silence time has been exceeded.");
551+ ddaDebug("Resetting status because got disabled or made invisible");
624− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
626 setStatus(WaitingForTouch);
552 setStatus(WaitingForTouch);
627 }
553 }
628− } else {
629− m_silenceTime = 0;
630 }
554 }
631− m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
632}
555}
633
556
634−void DirectionalDragArea::giveUpIfDisabledOrInvisible()
557+void DirectionalDragAreaPrivate::rejectGesture()
635{
558{
636− if (!isEnabled() || !isVisible()) {
637− if (m_status == Undecided) {
559+ if (status == Undecided) {
638− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
560+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
639− // We still wanna know when it ends for keeping the composition time window up-to-date
640− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
641− }
642
561
643− if (m_status != WaitingForTouch) {
562+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
644− ddaDebug("Resetting status because got disabled or made invisible");
563+ // We still wanna know when it ends for keeping the composition time window up-to-date
564+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
565+
645− setStatus(WaitingForTouch);
566+ setStatus(WaitingForTouch);
646− }
647 }
567 }
648}
568}
649
569
650−void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
570+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
651{
571{
652− if (newStatus == m_status)
572+ if (newStatus == status)
653 return;
573 return;
654
574
655− DirectionalDragArea::Status oldStatus = m_status;
575+ Status oldStatus = status;
656
576
657 if (oldStatus == Undecided) {
577 if (oldStatus == Undecided) {
658− m_recognitionTimer->stop();
578+ recognitionTimer->stop();
659 }
579 }
660
580
661− m_status = newStatus;
581+ status = newStatus;
662− Q_EMIT statusChanged(m_status);
582+ Q_EMIT statusChanged(status);
663
583
664 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
584 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665
585
666 switch (newStatus) {
586 switch (newStatus) {
667 case WaitingForTouch:
587 case WaitingForTouch:
588+ if (oldStatus == Recognized) {
668− Q_EMIT draggingChanged(false);
589+ Q_EMIT q->draggingChanged(false);
590+ }
669 break;
591 break;
670 case Undecided:
592 case Undecided:
671− m_recognitionTimer->start();
593+ recognitionTimer->start();
672− Q_EMIT draggingChanged(true);
673 break;
594 break;
674 case Recognized:
595 case Recognized:
675− if (oldStatus == WaitingForTouch)
676− Q_EMIT draggingChanged(true);
596+ Q_EMIT q->draggingChanged(true);
677 break;
597 break;
678 default:
598 default:
679 // no-op
599 // no-op
@@ -681,77 +601,126 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
681 }
601 }
682}
602}
683
603
684−void DirectionalDragArea::setPreviousPos(const QPointF &point)
604+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685{
605{
686− bool xChanged = m_previousPos.x() != point.x();
606+ bool xChanged = publicPos.x() != point.x();
687− bool yChanged = m_previousPos.y() != point.y();
607+ bool yChanged = publicPos.y() != point.y();
608+
609+ // Public position should not get updated while the gesture is still being recognized
610+ // (ie, Undecided status).
611+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
688
612
689− m_previousPos = point;
613+ if (status == Recognized && !recognitionIsDisabled()) {
614+ // When the gesture finally gets recognized, the finger will likely be
615+ // reasonably far from the edge. If we made the contentX immediately
616+ // follow the finger position it would be visually unpleasant as it
617+ // would appear right next to the user's finger out of nowhere (ie,
618+ // it would jump). Instead, we make contentX go towards the user's
619+ // finger in several steps. ie., in an animated way.
620+ QPointF delta = point - publicPos;
621+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
622+ publicPos.rx() += 0.4 * delta.x();
623+ publicPos.ry() += 0.4 * delta.y();
624+ } else {
625+ // no smoothing when initializing or if gesture recognition was immediate as there will
626+ // be no jump.
627+ publicPos = point;
628+ }
690
629
691 if (xChanged) {
630 if (xChanged) {
692− Q_EMIT touchXChanged(point.x());
631+ Q_EMIT q->touchXChanged(publicPos.x());
693− if (Direction::isHorizontal(m_direction))
632+ if (Direction::isHorizontal(direction))
694− Q_EMIT distanceChanged(distance());
633+ Q_EMIT q->distanceChanged(q->distance());
695 }
634 }
696
635
697 if (yChanged) {
636 if (yChanged) {
698− Q_EMIT touchYChanged(point.y());
637+ Q_EMIT q->touchYChanged(publicPos.y());
699− if (Direction::isVertical(m_direction))
638+ if (Direction::isVertical(direction))
700− Q_EMIT distanceChanged(distance());
639+ Q_EMIT q->distanceChanged(q->distance());
701 }
640 }
702}
641}
703
642
704−void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
643+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705{
644{
706− bool xChanged = m_previousScenePos.x() != point.x();
645+ bool xChanged = publicScenePos.x() != point.x();
707− bool yChanged = m_previousScenePos.y() != point.y();
646+ bool yChanged = publicScenePos.y() != point.y();
708
647
709 if (!xChanged && !yChanged)
648 if (!xChanged && !yChanged)
710 return;
649 return;
711
650
651+ // Public position should not get updated while the gesture is still being recognized
652+ // (ie, Undecided status).
653+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
654+
712− qreal oldSceneDistance = sceneDistance();
655+ qreal oldSceneDistance = sceneDistance;
713− m_previousScenePos = point;
656+
657+ if (status == Recognized && !recognitionIsDisabled()) {
658+ // When the gesture finally gets recognized, the finger will likely be
659+ // reasonably far from the edge. If we made the contentX immediately
660+ // follow the finger position it would be visually unpleasant as it
661+ // would appear right next to the user's finger out of nowhere (ie,
662+ // it would jump). Instead, we make contentX go towards the user's
663+ // finger in several steps. ie., in an animated way.
664+ QPointF delta = point - publicScenePos;
665+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
666+ publicScenePos.rx() += 0.4 * delta.x();
667+ publicScenePos.ry() += 0.4 * delta.y();
668+ } else {
669+ // no smoothing when initializing or if gesture recognition was immediate as there will
670+ // be no jump.
671+ publicScenePos = point;
672+ }
673+
714 updateSceneDistance();
674 updateSceneDistance();
715
675
716− if (oldSceneDistance != sceneDistance()) {
676+ if (oldSceneDistance != sceneDistance) {
717− Q_EMIT sceneDistanceChanged(sceneDistance());
677+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
718 }
678 }
719
679
720 if (xChanged) {
680 if (xChanged) {
721− Q_EMIT touchSceneXChanged(point.x());
681+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722 }
682 }
723
683
724 if (yChanged) {
684 if (yChanged) {
725− Q_EMIT touchSceneYChanged(point.y());
685+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726 }
686 }
727}
687}
728
688
729−void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
689+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
730{
690{
731− QPointF totalSceneMovement = scenePos - m_startScenePos;
691+ return
732−
692+ compositionTime > 0 &&
733− qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
693+ !activeTouches.isEmpty() &&
694+ timeSource->msecsSinceReference() <=
695+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
696+}
734
697
735− m_velocityCalculator->setTrackedPosition(scalarProjection);
698+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
699+{
700+ if (change == QQuickItem::ItemSceneChange) {
701+ if (value.window != nullptr) {
702+ // TODO: Handle window->screen() changes (ie window changing screens)
703+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
704+ d->setPixelsPerMm(pixelsPerMm);
705+ }
706+ }
736}
707}
737
708
738−bool DirectionalDragArea::isWithinTouchCompositionWindow()
709+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
739{
710{
740− return
711+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
741− compositionTime() > 0 &&
712+ setDistanceThreshold(4. * pixelsPerMm);
742− !m_activeTouches.isEmpty() &&
713+ maxDistance = 10. * pixelsPerMm;
743− m_timeSource->msecsSinceReference() <=
744− m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
745}
714}
746
715
747//************************** ActiveTouchesInfo **************************
716//************************** ActiveTouchesInfo **************************
748
717
749−DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
718+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750 : m_timeSource(timeSource)
719 : m_timeSource(timeSource)
751{
720{
752}
721}
753
722
754−void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
723+void ActiveTouchesInfo::update(QTouchEvent *event)
755{
724{
756 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
725 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757 // nothing to update
726 // nothing to update
@@ -773,7 +742,7 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
773}
742}
774
743
775#if ACTIVETOUCHESINFO_DEBUG
744#if ACTIVETOUCHESINFO_DEBUG
776−QString DirectionalDragArea::ActiveTouchesInfo::toString()
745+QString ActiveTouchesInfo::toString()
777{
746{
778 QString string = "(";
747 QString string = "(";
779
748
@@ -791,7 +760,7 @@ QString DirectionalDragArea::ActiveTouchesInfo::toString()
791}
760}
792#endif // ACTIVETOUCHESINFO_DEBUG
761#endif // ACTIVETOUCHESINFO_DEBUG
793
762
794−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
763+void ActiveTouchesInfo::addTouchPoint(int touchId)
795{
764{
796 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
765 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797 activeTouchInfo.id = touchId;
766 activeTouchInfo.id = touchId;
@@ -802,7 +771,7 @@ void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
802 #endif
771 #endif
803}
772}
804
773
805−qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
774+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806{
775{
807 qint64 result = -1;
776 qint64 result = -1;
808
777
@@ -819,7 +788,7 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
819 return result;
788 return result;
820}
789}
821
790
822−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
791+void ActiveTouchesInfo::removeTouchPoint(int touchId)
823{
792{
824 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
793 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825 if (touchId == touchInfo->id) {
794 if (touchId == touchInfo->id) {
@@ -835,7 +804,7 @@ void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
835 #endif
804 #endif
836}
805}
837
806
838−qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
807+qint64 ActiveTouchesInfo::mostRecentStartTime()
839{
808{
840 Q_ASSERT(!m_touchInfoPool.isEmpty());
809 Q_ASSERT(!m_touchInfoPool.isEmpty());
841
810
@@ -851,11 +820,11 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
851 return highestStartTime;
820 return highestStartTime;
852}
821}
853
822
854−void DirectionalDragArea::updateSceneDirectionVector()
823+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855{
824{
856 QPointF localOrigin(0., 0.);
825 QPointF localOrigin(0., 0.);
857 QPointF localDirection;
826 QPointF localDirection;
858− switch (m_direction) {
827+ switch (direction) {
859 case Direction::Upwards:
828 case Direction::Upwards:
860 localDirection.rx() = 0.;
829 localDirection.rx() = 0.;
861 localDirection.ry() = -1.;
830 localDirection.ry() = -1.;
@@ -873,14 +842,31 @@ void DirectionalDragArea::updateSceneDirectionVector()
873 localDirection.ry() = 0.;
842 localDirection.ry() = 0.;
874 break;
843 break;
875 }
844 }
876− QPointF sceneOrigin = mapToScene(localOrigin);
845+ QPointF sceneOrigin = q->mapToScene(localOrigin);
877− QPointF sceneDirection = mapToScene(localDirection);
846+ QPointF sceneDirection = q->mapToScene(localDirection);
878− m_sceneDirectionVector = sceneDirection - sceneOrigin;
847+ sceneDirectionVector = sceneDirection - sceneOrigin;
848+}
849+
850+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
851+{
852+ // same as dot product as sceneDirectionVector is a unit vector
853+ return sceneVector.x() * sceneDirectionVector.x() +
854+ sceneVector.y() * sceneDirectionVector.y();
879}
855}
880
856
881−qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
857+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
858+ : q(q)
859+ , status(WaitingForTouch)
860+ , sceneDistance(0)
861+ , touchId(-1)
862+ , direction(Direction::Rightwards)
863+ , distanceThreshold(0)
864+ , distanceThresholdSquared(0.)
865+ , maxTime(400)
866+ , compositionTime(60)
867+ , immediateRecognition(false)
868+ , recognitionTimer(nullptr)
869+ , timeSource(new RealTimeSource)
870+ , activeTouches(timeSource)
882{
871{
883− // same as dot product as m_sceneDirectionVector is a unit vector
884− return sceneVector.x() * m_sceneDirectionVector.x() +
885− sceneVector.y() * m_sceneDirectionVector.y();
886}
872}
plugins/Ubuntu/Gestures/DirectionalDragArea.h+16−205
@@ -18,7 +18,6 @@
18#define DIRECTIONAL_DRAG_AREA_H
18#define DIRECTIONAL_DRAG_AREA_H
19
19
20#include <QtQuick/QQuickItem>
20#include <QtQuick/QQuickItem>
21−#include "AxisVelocityCalculator.h"
22#include "UbuntuGesturesQmlGlobal.h"
21#include "UbuntuGesturesQmlGlobal.h"
23#include "Damper.h"
22#include "Damper.h"
24#include "Direction.h"
23#include "Direction.h"
@@ -29,6 +28,7 @@
29
28
30class TouchOwnershipEvent;
29class TouchOwnershipEvent;
31class UnownedTouchEvent;
30class UnownedTouchEvent;
31+class DirectionalDragAreaPrivate;
32
32
33/*
33/*
34 An area that detects axis-aligned single-finger drag gestures
34 An area that detects axis-aligned single-finger drag gestures
@@ -61,95 +61,28 @@ class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
63
63
64− // The current status of the directional drag gesture area.
65− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
66−
67 // Whether a drag gesture is taking place
64 // Whether a drag gesture is taking place
68− // This will be true as long as status is Undecided or Recognized
69− // When a gesture gets rejected, dragging turns to false.
70 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
65 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
71
66
72− /////
67+ // Whether a gesture should be Recognized as soon a touch lands on the area.
73− // stuff that will be set in stone at some point
68+ // With this property enabled it will work pretty much like a MultiPointTouchArea,
74−
69+ // just with a different API.
75− // How far the touch point can move away from its expected position before
76− // it causes a rejection in the gesture recognition. This is to compensate
77− // for both noise in the touch input signal and for the natural irregularities
78− // in the finger movement.
79− // Proper value is likely device-specific.
80− Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
81−
82− // Widening angle, in degrees
83− // It's roughly the maximum angle a touch point can make relative to the
84− // axis defined by the compoment's direction for it to be recognized as a
85− // directional drag.
86− Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
87− NOTIFY wideningAngleChanged)
88−
89− // How far a touch point has to move from its initial position in order for
90− // it to be recognized as a directional drag.
91− Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
92− NOTIFY distanceThresholdChanged)
93−
94− // Minimum speed a gesture needs to have in order to be recognized as a
95− // directional drag.
96− // In pixels per second
97− Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
98−
99− // A gesture will be rejected if more than maxSilenceTime milliseconds has
100− // passed since we last got an input event from it (during Undecided state).
101− //
102− // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
103− // finger is still (zero drag speed). In some cases the finger might be moving but
104− // the driver's high noise filtering might cause those silence periods, specially
105− // in the moments succeeding a press (talking about Galaxy Nexus here).
106− Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
107− WRITE setMaxSilenceTime
108− NOTIFY maxSilenceTimeChanged)
109−
110 //
70 //
111− /////
71+ // It's false by default. In most cases you will not want that enabled.
112−
72+ Q_PROPERTY(bool immediateRecognition
113− // Maximum time (in milliseconds) after the start of a given touch point where
73+ READ immediateRecognition
114− // subsequent touch starts are grouped with the first one into an N-touches gesture
74+ WRITE setImmediateRecognition
115− // (e.g. a two-fingers tap or drag).
75+ NOTIFY immediateRecognitionChanged)
116− Q_PROPERTY(int compositionTime READ compositionTime
117− WRITE setCompositionTime
118− NOTIFY compositionTimeChanged)
119
76
120 Q_ENUMS(Direction)
77 Q_ENUMS(Direction)
121− Q_ENUMS(Status)
122public:
78public:
123 DirectionalDragArea(QQuickItem *parent = 0);
79 DirectionalDragArea(QQuickItem *parent = 0);
124
80
125 Direction::Type direction() const;
81 Direction::Type direction() const;
126 void setDirection(Direction::Type);
82 void setDirection(Direction::Type);
127
83
128− // Describes the state of the directional drag gesture.
129− enum Status {
130− // Waiting for a new touch point to land on this area. No gesture is being processed
131− // or tracked.
132− WaitingForTouch,
133−
134− // A touch point has landed on this area but it's not know yet whether it is
135− // performing a drag in the correct direction.
136− // If it's decided that the touch point is not performing a directional drag gesture,
137− // it will be rejected/ignored and status will return to WaitingForTouch.
138− Undecided, //Recognizing,
139−
140− // There's a touch point in this area and it performed a drag in the correct
141− // direction.
142− //
143− // Once recognized, the gesture state will move back to WaitingForTouch only once
144− // that touch point ends. The gesture will remain in the Recognized state even if
145− // the touch point starts moving in other directions or halts.
146− Recognized,
147− };
148− Status status() const { return m_status; }
149−
150 qreal distance() const;
84 qreal distance() const;
151 qreal sceneDistance() const;
85 qreal sceneDistance() const;
152− void updateSceneDistance();
153
86
154 qreal touchX() const;
87 qreal touchX() const;
155 qreal touchY() const;
88 qreal touchY() const;
@@ -157,152 +90,30 @@ public:
157 qreal touchSceneX() const;
90 qreal touchSceneX() const;
158 qreal touchSceneY() const;
91 qreal touchSceneY() const;
159
92
160− bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
93+ bool dragging() const;
161−
162− qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
163− void setMaxDeviation(qreal value);
164−
165− qreal wideningAngle() const;
166− void setWideningAngle(qreal value);
167−
168− qreal distanceThreshold() const { return m_distanceThreshold; }
169− void setDistanceThreshold(qreal value);
170−
171− qreal minSpeed() const { return m_minSpeed; }
172− void setMinSpeed(qreal value);
173
94
174− int maxSilenceTime() const { return m_maxSilenceTime; }
95+ bool immediateRecognition() const;
175− void setMaxSilenceTime(int value);
96+ void setImmediateRecognition(bool enabled);
176−
177− int compositionTime() const { return m_compositionTime; }
178− void setCompositionTime(int value);
179−
180− // Replaces the existing Timer with the given one.
181− //
182− // Useful for providing a fake timer when testing.
183− void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
184−
185− // Useful for testing, where a fake time source can be supplied
186− void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
187
97
188 bool event(QEvent *e) override;
98 bool event(QEvent *e) override;
189
99
190− // Maximum time, in milliseconds, between a press and a release, for a touch
191− // sequence to be considered a tap.
192− int maxTapDuration() const { return 300; }
193−
194Q_SIGNALS:
100Q_SIGNALS:
195 void directionChanged(Direction::Type direction);
101 void directionChanged(Direction::Type direction);
196− void statusChanged(Status value);
197 void draggingChanged(bool value);
102 void draggingChanged(bool value);
198 void distanceChanged(qreal value);
103 void distanceChanged(qreal value);
199 void sceneDistanceChanged(qreal value);
104 void sceneDistanceChanged(qreal value);
200− void maxDeviationChanged(qreal value);
201− void wideningAngleChanged(qreal value);
202− void distanceThresholdChanged(qreal value);
203− void minSpeedChanged(qreal value);
204− void maxSilenceTimeChanged(int value);
205− void compositionTimeChanged(int value);
206 void touchXChanged(qreal value);
105 void touchXChanged(qreal value);
207 void touchYChanged(qreal value);
106 void touchYChanged(qreal value);
208 void touchSceneXChanged(qreal value);
107 void touchSceneXChanged(qreal value);
209 void touchSceneYChanged(qreal value);
108 void touchSceneYChanged(qreal value);
210−
109+ void immediateRecognitionChanged(bool value);
211− // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212− // Remove when no longer used or move its implementation to the QML code that uses it
213− // See maxTapDuration()
214− void tapped();
215
110
216protected:
111protected:
217 virtual void touchEvent(QTouchEvent *event);
112 virtual void touchEvent(QTouchEvent *event);
113+ virtual void itemChange(ItemChange change, const ItemChangeData &value);
218
114
219−private Q_SLOTS:
220− void checkSpeed();
221− void giveUpIfDisabledOrInvisible();
222−
223−private:
224− void touchEvent_absent(QTouchEvent *event);
225− void touchEvent_undecided(QTouchEvent *event);
226− void touchEvent_recognized(QTouchEvent *event);
227− bool pointInsideAllowedArea() const;
228− bool movingInRightDirection() const;
229− bool movedFarEnough(const QPointF &point) const;
230− const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
231− void setStatus(Status newStatus);
232− void setPreviousPos(const QPointF &point);
233− void setPreviousScenePos(const QPointF &point);
234− void updateVelocityCalculator(const QPointF &point);
235− bool isWithinTouchCompositionWindow();
236− void updateSceneDirectionVector();
237− // returns the scalar projection between the given vector (in scene coordinates)
238− // and m_sceneDirectionVector
239− qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
240− void touchOwnershipEvent(TouchOwnershipEvent *event);
241− void unownedTouchEvent(UnownedTouchEvent *event);
242− void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
243− void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
244− bool recognitionIsDisabled() const;
245− void emitSignalIfTapped();
246−
247− Status m_status;
248−
249− QPointF m_startPos;
250− QPointF m_startScenePos;
251− QPointF m_previousPos;
252− QPointF m_previousScenePos;
253− qreal m_sceneDistance;
254− int m_touchId;
255−
256− // A movement damper is used in some of the gesture recognition calculations
257− // to get rid of noise or small oscillations in the touch position.
258− DampedPointF m_dampedScenePos;
259− QPointF m_previousDampedScenePos;
260−
261− // Unit vector in scene coordinates describing the direction of the gesture recognition
262− QPointF m_sceneDirectionVector;
263−
264− Direction::Type m_direction;
265− qreal m_wideningAngle; // in degrees
266− qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
267− qreal m_distanceThreshold;
268− qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
269− qreal m_minSpeed;
270− int m_maxSilenceTime; // in milliseconds
271− int m_silenceTime; // in milliseconds
272− int m_compositionTime; // in milliseconds
273− int m_numSamplesOnLastSpeedCheck;
274− UbuntuGestures::AbstractTimer *m_recognitionTimer;
275− AxisVelocityCalculator *m_velocityCalculator;
276−
277− UbuntuGestures::SharedTimeSource m_timeSource;
278−
279− // Information about an active touch point
280− struct ActiveTouchInfo {
281− ActiveTouchInfo() : id(-1), startTime(-1) {}
282− bool isValid() const { return id != -1; }
283− void reset() { id = -1; }
284− int id;
285− qint64 startTime;
286− };
287− class ActiveTouchesInfo {
288− public:
115+public: // so tests can access it
289− ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
116+ DirectionalDragAreaPrivate *d;
290− void update(QTouchEvent *event);
291− qint64 touchStartTime(int id);
292− bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
293− qint64 mostRecentStartTime();
294− UbuntuGestures::SharedTimeSource m_timeSource;
295− private:
296− void addTouchPoint(int touchId);
297− void removeTouchPoint(int touchId);
298− #if ACTIVETOUCHESINFO_DEBUG
299− QString toString();
300− #endif
301−
302− Pool<ActiveTouchInfo> m_touchInfoPool;
303− } m_activeTouches;
304−
305− friend class tst_DirectionalDragArea;
306};
117};
307
118
308#endif // DIRECTIONAL_DRAG_AREA_H
119#endif // DIRECTIONAL_DRAG_AREA_H
plugins/Ubuntu/Gestures/DirectionalDragArea_p.hnew file+167
@@ -0,0 +1,167 @@
1+/*
2+ * Copyright (C) 2015 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+#ifndef DIRECTIONAL_DRAG_AREA_PRIV_H
18+#define DIRECTIONAL_DRAG_AREA_PRIV_H
19+
20+// Information about an active touch point
21+struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
22+ ActiveTouchInfo() : id(-1), startTime(-1) {}
23+ bool isValid() const { return id != -1; }
24+ void reset() { id = -1; }
25+ int id;
26+ qint64 startTime;
27+};
28+class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
29+public:
30+ ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
31+ void update(QTouchEvent *event);
32+ qint64 touchStartTime(int id);
33+ bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
34+ qint64 mostRecentStartTime();
35+ UbuntuGestures::SharedTimeSource m_timeSource;
36+private:
37+ void addTouchPoint(int touchId);
38+ void removeTouchPoint(int touchId);
39+ #if ACTIVETOUCHESINFO_DEBUG
40+ QString toString();
41+ #endif
42+
43+ Pool<ActiveTouchInfo> m_touchInfoPool;
44+};
45+
46+class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
47+ Q_OBJECT
48+
49+ Q_ENUMS(Status)
50+public:
51+ DirectionalDragAreaPrivate(DirectionalDragArea *q);
52+
53+public Q_SLOTS:
54+ void giveUpIfDisabledOrInvisible();
55+ void rejectGesture();
56+
57+public:
58+ // Describes the state of the directional drag gesture.
59+ enum Status {
60+ // Waiting for a new touch point to land on this area. No gesture is being processed
61+ // or tracked.
62+ WaitingForTouch,
63+
64+ // A touch point has landed on this area but it's not know yet whether it is
65+ // performing a drag in the correct direction.
66+ // If it's decided that the touch point is not performing a directional drag gesture,
67+ // it will be rejected/ignored and status will return to WaitingForTouch.
68+ Undecided, //Recognizing,
69+
70+ // There's a touch point in this area and it performed a drag in the correct
71+ // direction.
72+ //
73+ // Once recognized, the gesture state will move back to WaitingForTouch only once
74+ // that touch point ends. The gesture will remain in the Recognized state even if
75+ // the touch point starts moving in other directions or halts.
76+ Recognized,
77+ };
78+
79+ void touchEvent_absent(QTouchEvent *event);
80+ void touchEvent_undecided(QTouchEvent *event);
81+ void touchEvent_recognized(QTouchEvent *event);
82+ bool movingInRightDirection() const;
83+ bool movedFarEnoughAlongGestureAxis() const;
84+ bool isPastMaxDistance() const;
85+ const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
86+ void setStatus(Status newStatus);
87+ void setPublicPos(const QPointF &point);
88+ void setPublicScenePos(const QPointF &point);
89+ bool isWithinTouchCompositionWindow();
90+ void updateSceneDirectionVector();
91+ // returns the scalar projection between the given vector (in scene coordinates)
92+ // and m_sceneDirectionVector
93+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
94+ void touchOwnershipEvent(TouchOwnershipEvent *event);
95+ void unownedTouchEvent(UnownedTouchEvent *event);
96+ void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
97+ void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
98+ bool recognitionIsDisabled() const;
99+ bool sanityCheckRecognitionProperties();
100+ void updateSceneDistance();
101+ void setMaxTime(int value);
102+ void setDistanceThreshold(qreal value);
103+ void setPixelsPerMm(qreal pixelsPerMm);
104+ QString objectName() const { return q->objectName(); }
105+
106+ // Replaces the existing Timer with the given one.
107+ //
108+ // Useful for providing a fake timer when testing.
109+ void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
110+
111+ // Useful for testing, where a fake time source can be supplied
112+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
113+
114+ DirectionalDragArea *q;
115+
116+ // The current status of the directional drag gesture area.
117+ Status status;
118+
119+ QPointF startPos;
120+ QPointF startScenePos;
121+ qreal sceneDistance;
122+ int touchId;
123+
124+ // The touch position exposed in the public API.
125+ // It only starts to move once the gesture gets recognized.
126+ QPointF publicPos;
127+ QPointF publicScenePos;
128+
129+ // A movement damper is used in some of the gesture recognition calculations
130+ // to get rid of noise or small oscillations in the touch position.
131+ DampedPointF dampedScenePos;
132+ QPointF previousDampedScenePos;
133+
134+ // Unit vector in scene coordinates describing the direction of the gesture recognition
135+ QPointF sceneDirectionVector;
136+
137+ Direction::Type direction;
138+
139+ // How far a touch point has to move from its initial position along the gesture axis in order
140+ // for it to be recognized as a directional drag.
141+ qreal distanceThreshold;
142+ qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
143+
144+ // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
145+ int maxTime;
146+
147+ // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
148+ qreal maxDistance;
149+
150+ // Maximum time (in milliseconds) after the start of a given touch point where
151+ // subsequent touch starts are grouped with the first one into an N-touches gesture
152+ // (e.g. a two-fingers tap or drag).
153+ int compositionTime;
154+
155+ bool immediateRecognition;
156+
157+ UbuntuGestures::AbstractTimer *recognitionTimer;
158+
159+ UbuntuGestures::SharedTimeSource timeSource;
160+
161+ ActiveTouchesInfo activeTouches;
162+
163+Q_SIGNALS:
164+ void statusChanged(Status value);
165+};
166+
167+#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -28,8 +28,11 @@
28#define TOUCHDISPATCHER_DEBUG 0
28#define TOUCHDISPATCHER_DEBUG 0
29
29
30#if TOUCHDISPATCHER_DEBUG
30#if TOUCHDISPATCHER_DEBUG
31+#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
31#include <DebugHelpers.h>
32#include <DebugHelpers.h>
32−#endif
33+#else // TOUCHDISPATCHER_DEBUG
34+#define ugDebug(params) ((void)0)
35+#endif // TOUCHDISPATCHER_DEBUG
33
36
34TouchDispatcher::TouchDispatcher()
37TouchDispatcher::TouchDispatcher()
35 : m_status(NoActiveTouch)
38 : m_status(NoActiveTouch)
@@ -44,13 +47,12 @@ void TouchDispatcher::setTargetItem(QQuickItem *target)
44 m_targetItem = target;
47 m_targetItem = target;
45 if (m_status != NoActiveTouch) {
48 if (m_status != NoActiveTouch) {
46 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
49 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47− m_status = TargetRejectedTouches;
50+ setStatus(TargetRejectedTouches);
48 }
51 }
49 }
52 }
50}
53}
51
54
52−void TouchDispatcher::dispatch(QEvent::Type eventType,
55+void TouchDispatcher::dispatch(QTouchDevice *device,
53− QTouchDevice *device,
54 Qt::KeyboardModifiers modifiers,
56 Qt::KeyboardModifiers modifiers,
55 const QList<QTouchEvent::TouchPoint> &touchPoints,
57 const QList<QTouchEvent::TouchPoint> &touchPoints,
56 QWindow *window,
58 QWindow *window,
@@ -61,6 +63,8 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
61 return;
63 return;
62 }
64 }
63
65
66+ QEvent::Type eventType = resolveEventType(touchPoints);
67+
64 if (eventType == QEvent::TouchBegin) {
68 if (eventType == QEvent::TouchBegin) {
65 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
69 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
66
70
@@ -72,15 +76,13 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
72 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
76 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
73 } else {
77 } else {
74 Q_ASSERT(m_status == TargetRejectedTouches);
78 Q_ASSERT(m_status == TargetRejectedTouches);
75− #if TOUCHDISPATCHER_DEBUG
76− qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
79+ ugDebug("Not dispatching touch event to " << m_targetItem.data()
77− << "because it already rejected the touch stream.";
80+ << "because it already rejected the touch stream.");
78− #endif
79 // Do nothing
81 // Do nothing
80 }
82 }
81
83
82 if (eventType == QEvent::TouchEnd) {
84 if (eventType == QEvent::TouchEnd) {
83− m_status = NoActiveTouch;
85+ setStatus(NoActiveTouch);
84 m_touchMouseId = -1;
86 m_touchMouseId = -1;
85 }
87 }
86
88
@@ -103,10 +105,7 @@ void TouchDispatcher::dispatchTouchBegin(
103 QQuickItem *targetItem = m_targetItem.data();
105 QQuickItem *targetItem = m_targetItem.data();
104
106
105 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
107 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106− #if TOUCHDISPATCHER_DEBUG
107− qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108− << "because it's disabled or invisible.";
108+ ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
109− #endif
110 return;
109 return;
111 }
110 }
112
111
@@ -118,62 +117,46 @@ void TouchDispatcher::dispatchTouchBegin(
118 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
117 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119
118
120
119
121− #if TOUCHDISPATCHER_DEBUG
122− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
120+ ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
123− << "to" << targetItem;
121+ << " to " << targetItem);
124− #endif
125 QCoreApplication::sendEvent(targetItem, touchEvent.data());
122 QCoreApplication::sendEvent(targetItem, touchEvent.data());
126
123
127
124
128 if (touchEvent->isAccepted()) {
125 if (touchEvent->isAccepted()) {
129− #if TOUCHDISPATCHER_DEBUG
130− qDebug() << "[TouchDispatcher] Item accepted the touch event.";
126+ ugDebug("Item accepted the touch event.");
131− #endif
132− m_status = DeliveringTouchEvents;
127+ setStatus(DeliveringTouchEvents);
133 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
128 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134− #if TOUCHDISPATCHER_DEBUG
135− qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
129+ ugDebug("Item rejected the touch event. Trying a QMouseEvent");
136− #endif
137 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
130 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138 QScopedPointer<QMouseEvent> mouseEvent(
131 QScopedPointer<QMouseEvent> mouseEvent(
139 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
132 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140 modifiers, false /* transformNeeded */));
133 modifiers, false /* transformNeeded */));
141 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
134 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142
135
143− #if TOUCHDISPATCHER_DEBUG
144− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
136+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
145− << "to" << m_targetItem.data();
137+ << " to " << m_targetItem.data());
146− #endif
147 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
138 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148 if (mouseEvent->isAccepted()) {
139 if (mouseEvent->isAccepted()) {
149− #if TOUCHDISPATCHER_DEBUG
150− qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
140+ ugDebug("Item accepted the QMouseEvent.");
151− #endif
152− m_status = DeliveringMouseEvents;
141+ setStatus(DeliveringMouseEvents);
153 m_touchMouseId = targetTouchPoints.at(0).id();
142 m_touchMouseId = targetTouchPoints.at(0).id();
154
143
155 if (checkIfDoubleClicked(timestamp)) {
144 if (checkIfDoubleClicked(timestamp)) {
156 QScopedPointer<QMouseEvent> doubleClickEvent(
145 QScopedPointer<QMouseEvent> doubleClickEvent(
157 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
146 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158 modifiers, false /* transformNeeded */));
147 modifiers, false /* transformNeeded */));
159− #if TOUCHDISPATCHER_DEBUG
160− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
148+ ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
161− << "to" << m_targetItem.data();
149+ << " to " << m_targetItem.data());
162− #endif
163 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
150 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164 }
151 }
165
152
166 } else {
153 } else {
167− #if TOUCHDISPATCHER_DEBUG
168− qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
154+ ugDebug("Item rejected the QMouseEvent.");
169− #endif
170− m_status = TargetRejectedTouches;
155+ setStatus(TargetRejectedTouches);
171 }
156 }
172 } else {
157 } else {
173− #if TOUCHDISPATCHER_DEBUG
174− qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
158+ ugDebug("Item rejected the touch event and does not accept mouse buttons.");
175− #endif
176− m_status = TargetRejectedTouches;
159+ setStatus(TargetRejectedTouches);
177 }
160 }
178}
161}
179
162
@@ -194,10 +177,8 @@ void TouchDispatcher::dispatchAsTouch(QEvent::Type eventType,
194 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
177 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195
178
196
179
197− #if TOUCHDISPATCHER_DEBUG
198− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
180+ ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
199− << "to" << targetItem;
181+ << " to " << targetItem);
200− #endif
201 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202}
183}
203
184
@@ -254,10 +235,8 @@ void TouchDispatcher::dispatchAsMouse(
254 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
235 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255 true /* transformNeeded */));
236 true /* transformNeeded */));
256
237
257− #if TOUCHDISPATCHER_DEBUG
258− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
238+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
259− << "to" << m_targetItem.data();
239+ << " to " << m_targetItem.data());
260− #endif
261 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
240 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262 }
241 }
263}
242}
@@ -365,3 +344,59 @@ bool TouchDispatcher::checkIfDoubleClicked(ulong newPressEventTimestamp)
365
344
366 return doubleClicked;
345 return doubleClicked;
367}
346}
347+
348+void TouchDispatcher::setStatus(Status status)
349+{
350+ if (status != m_status) {
351+ #if TOUCHDISPATCHER_DEBUG
352+ switch (status) {
353+ case NoActiveTouch:
354+ ugDebug("status = NoActiveTouch");
355+ break;
356+ case DeliveringTouchEvents:
357+ ugDebug("status = DeliveringTouchEvents");
358+ break;
359+ case DeliveringMouseEvents:
360+ ugDebug("status = DeliveringMouseEvents");
361+ break;
362+ case TargetRejectedTouches:
363+ ugDebug("status = TargetRejectedTouches");
364+ break;
365+ default:
366+ ugDebug("status = " << status);
367+ break;
368+ }
369+ #endif
370+ m_status = status;
371+ }
372+}
373+
374+void TouchDispatcher::reset()
375+{
376+ setStatus(NoActiveTouch);
377+ m_touchMouseId = -1;
378+ m_touchMousePressTimestamp =0;
379+}
380+
381+QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
382+{
383+ QEvent::Type eventType;
384+
385+ Qt::TouchPointStates eventStates = 0;
386+ for (int i = 0; i < touchPoints.count(); i++)
387+ eventStates |= touchPoints[i].state();
388+
389+ switch (eventStates) {
390+ case Qt::TouchPointPressed:
391+ eventType = QEvent::TouchBegin;
392+ break;
393+ case Qt::TouchPointReleased:
394+ eventType = QEvent::TouchEnd;
395+ break;
396+ default:
397+ eventType = QEvent::TouchUpdate;
398+ break;
399+ }
400+
401+ return eventType;
402+}
plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -36,12 +36,20 @@ public:
36 void setTargetItem(QQuickItem *target);
36 void setTargetItem(QQuickItem *target);
37 QQuickItem *targetItem() { return m_targetItem; }
37 QQuickItem *targetItem() { return m_targetItem; }
38
38
39− void dispatch(QEvent::Type eventType,
40− QTouchDevice *device,
39+ void dispatch(QTouchDevice *device,
41 Qt::KeyboardModifiers modifiers,
40 Qt::KeyboardModifiers modifiers,
42 const QList<QTouchEvent::TouchPoint> &touchPoints,
41 const QList<QTouchEvent::TouchPoint> &touchPoints,
43 QWindow *window,
42 QWindow *window,
44 ulong timestamp);
43 ulong timestamp);
44+
45+ void reset();
46+
47+ enum Status {
48+ NoActiveTouch,
49+ DeliveringTouchEvents,
50+ DeliveringMouseEvents,
51+ TargetRejectedTouches
52+ };
45private:
53private:
46 void dispatchTouchBegin(
54 void dispatchTouchBegin(
47 QTouchDevice *device,
55 QTouchDevice *device,
@@ -73,14 +81,13 @@ private:
73
81
74 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
82 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
75
83
84+ void setStatus(Status status);
85+
86+ static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
87+
76 QPointer<QQuickItem> m_targetItem;
88 QPointer<QQuickItem> m_targetItem;
77
89
78− enum {
79− NoActiveTouch,
80− DeliveringTouchEvents,
81− DeliveringMouseEvents,
82− TargetRejectedTouches
83− } m_status;
90+ Status m_status;
84
91
85 int m_touchMouseId;
92 int m_touchMouseId;
86 ulong m_touchMousePressTimestamp;
93 ulong m_touchMousePressTimestamp;
plugins/Ubuntu/Gestures/TouchGate.cpp+84−60
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -23,8 +23,18 @@
23#include <TouchRegistry.h>
23#include <TouchRegistry.h>
24
24
25#if TOUCHGATE_DEBUG
25#if TOUCHGATE_DEBUG
26+#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
26#include <DebugHelpers.h>
27#include <DebugHelpers.h>
28+#else // TOUCHGATE_DEBUG
29+#define ugDebug(params) ((void)0)
27−#endif
30+#endif // TOUCHGATE_DEBUG
31+
32+TouchGate::TouchGate(QQuickItem *parent)
33+ : QQuickItem(parent)
34+{
35+ connect(this, &QQuickItem::enabledChanged,
36+ this, &TouchGate::onEnabledChanged);
37+}
28
38
29bool TouchGate::event(QEvent *e)
39bool TouchGate::event(QEvent *e)
30{
40{
@@ -38,13 +48,12 @@ bool TouchGate::event(QEvent *e)
38
48
39void TouchGate::touchEvent(QTouchEvent *event)
49void TouchGate::touchEvent(QTouchEvent *event)
40{
50{
41− #if TOUCHGATE_DEBUG
42− qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
51+ ugDebug("got touch event" << qPrintable(touchEventToString(event)));
43− #endif
44 event->accept();
52 event->accept();
45
53
46 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
54 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
55+ QList<QTouchEvent::TouchPoint> validTouchPoints;
47− bool goodToGo = true;
56+ bool ownsAllTouches = true;
48 for (int i = 0; i < touchPoints.count(); ++i) {
57 for (int i = 0; i < touchPoints.count(); ++i) {
49 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
58 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
50
59
@@ -57,31 +66,41 @@ void TouchGate::touchEvent(QTouchEvent *event)
57 Q_EMIT pressed();
66 Q_EMIT pressed();
58 }
67 }
59
68
60− goodToGo &= m_touchInfoMap.contains(touchPoint.id())
69+ if (m_touchInfoMap.contains(touchPoint.id())) {
70+ validTouchPoints.append(touchPoint);
71+
61− && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
72+ ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
62
73
63− if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {
74+ if (touchPoint.state() == Qt::TouchPointReleased) {
64− m_touchInfoMap[touchPoint.id()].ended = true;
75+ m_touchInfoMap[touchPoint.id()].ended = true;
76+ }
65 }
77 }
66
78
67 }
79 }
68
80
69− if (goodToGo) {
81+ if (validTouchPoints.isEmpty()) {
82+ // nothing to do.
83+ return;
84+ }
85+
86+ if (ownsAllTouches) {
70 if (m_storedEvents.isEmpty()) {
87 if (m_storedEvents.isEmpty()) {
71 // let it pass through
88 // let it pass through
72− dispatchTouchEventToTarget(event);
89+ removeTouchInfoForEndedTouches(validTouchPoints);
90+ m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
91+ event->window(), event->timestamp());
73 } else {
92 } else {
74 // Retain the event to ensure TouchGate dispatches them in order.
93 // Retain the event to ensure TouchGate dispatches them in order.
75 // Otherwise the current event would come before the stored ones, which are older.
94 // Otherwise the current event would come before the stored ones, which are older.
76− #if TOUCHGATE_DEBUG
95+ ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
77− qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",
96+ << " are still pending ownership.");
78− qPrintable(oldestPendingTouchIdsString()));
79− #endif
80− storeTouchEvent(event);
97+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
98+ event->window(), event->timestamp());
81 }
99 }
82 } else {
100 } else {
83 // Retain events that have unowned touches
101 // Retain events that have unowned touches
84− storeTouchEvent(event);
102+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
103+ event->window(), event->timestamp());
85 }
104 }
86}
105}
87
106
@@ -90,24 +109,23 @@ void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
90 // TODO: Optimization: batch those actions as TouchOwnershipEvents
109 // TODO: Optimization: batch those actions as TouchOwnershipEvents
91 // might come one right after the other.
110 // might come one right after the other.
92
111
93− Q_ASSERT(m_touchInfoMap.contains(event->touchId()));
112+ if (m_touchInfoMap.contains(event->touchId())) {
113+ TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
94
114
95− TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
115+ if (event->gained()) {
116+ ugDebug("Got ownership of touch " << event->touchId());
117+ touchInfo.ownership = OwnershipGranted;
118+ } else {
119+ ugDebug("Lost ownership of touch " << event->touchId());
120+ m_touchInfoMap.remove(event->touchId());
121+ removeTouchFromStoredEvents(event->touchId());
122+ }
96
123
97− if (event->gained()) {
124+ dispatchFullyOwnedEvents();
98− #if TOUCHGATE_DEBUG
99− qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();
100− #endif
101− touchInfo.ownership = OwnershipGranted;
102 } else {
125 } else {
103− #if TOUCHGATE_DEBUG
126+ // Ignore it. It probably happened because the TouchGate got disabled
104− qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();
127+ // between the time it requested ownership and the time it got it.
105− #endif
106− m_touchInfoMap.remove(event->touchId());
107− removeTouchFromStoredEvents(event->touchId());
108 }
128 }
109−
110− dispatchFullyOwnedEvents();
111}
129}
112
130
113bool TouchGate::isTouchPointOwned(int touchId) const
131bool TouchGate::isTouchPointOwned(int touchId) const
@@ -115,14 +133,15 @@ bool TouchGate::isTouchPointOwned(int touchId) const
115 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
133 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
116}
134}
117
135
118−void TouchGate::storeTouchEvent(const QTouchEvent *event)
136+void TouchGate::storeTouchEvent(QTouchDevice *device,
137+ Qt::KeyboardModifiers modifiers,
138+ const QList<QTouchEvent::TouchPoint> &touchPoints,
139+ QWindow *window,
140+ ulong timestamp)
119{
141{
120− #if TOUCHGATE_DEBUG
142+ ugDebug("Storing" << touchPoints);
121− qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));
143+ TouchEvent event(device, modifiers, touchPoints, window, timestamp);
122− #endif
123−
124− TouchEvent clonedEvent(event);
125− m_storedEvents.append(std::move(clonedEvent));
144+ m_storedEvents.append(std::move(event));
126}
145}
127
146
128void TouchGate::removeTouchFromStoredEvents(int touchId)
147void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -195,25 +214,13 @@ void TouchGate::setTargetItem(QQuickItem *item)
195void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
214void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
196{
215{
197 removeTouchInfoForEndedTouches(event.touchPoints);
216 removeTouchInfoForEndedTouches(event.touchPoints);
198− m_dispatcher.dispatch(event.eventType,
217+ m_dispatcher.dispatch(event.device,
199− event.device,
200 event.modifiers,
218 event.modifiers,
201 event.touchPoints,
219 event.touchPoints,
202 event.window,
220 event.window,
203 event.timestamp);
221 event.timestamp);
204}
222}
205
223
206−void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
207−{
208− removeTouchInfoForEndedTouches(event->touchPoints());
209− m_dispatcher.dispatch(event->type(),
210− event->device(),
211− event->modifiers(),
212− event->touchPoints(),
213− event->window(),
214− event->timestamp());
215−}
216−
217void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
224void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
218{
225{
219 for (int i = 0; i < touchPoints.size(); ++i) {\
226 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -228,14 +235,31 @@ void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoi
228 }
235 }
229}
236}
230
237
238+void TouchGate::onEnabledChanged()
239+{
240+ ugDebug(" enabled = " << isEnabled());
241+ if (!isEnabled()) {
242+ reset();
243+ }
244+}
245+
246+void TouchGate::reset()
247+{
248+ m_storedEvents.clear();
249+ m_touchInfoMap.clear();
250+ m_dispatcher.reset();
251+}
252+
231−TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
253+TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
232− : eventType(event->type())
254+ Qt::KeyboardModifiers modifiers,
255+ const QList<QTouchEvent::TouchPoint> &touchPoints,
256+ QWindow *window,
257+ ulong timestamp)
233− , device(event->device())
258+ : device(device)
234− , modifiers(event->modifiers())
259+ , modifiers(modifiers)
235− , touchPoints(event->touchPoints())
260+ , touchPoints(touchPoints)
236− , target(qobject_cast<QQuickItem*>(event->target()))
237− , window(event->window())
261+ , window(window)
238− , timestamp(event->timestamp())
262+ , timestamp(timestamp)
239{
263{
240}
264}
241
265
plugins/Ubuntu/Gestures/TouchGate.h+18−5
@@ -45,6 +45,8 @@ class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem {
45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
46
46
47public:
47public:
48+ TouchGate(QQuickItem *parent = nullptr);
49+
48 bool event(QEvent *e) override;
50 bool event(QEvent *e) override;
49
51
50 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
52 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
@@ -57,31 +59,42 @@ Q_SIGNALS:
57
59
58protected:
60protected:
59 void touchEvent(QTouchEvent *event) override;
61 void touchEvent(QTouchEvent *event) override;
62+
63+private Q_SLOTS:
64+ void onEnabledChanged();
65+
60private:
66private:
67+ void reset();
68+
61 class TouchEvent {
69 class TouchEvent {
62 public:
70 public:
71+ TouchEvent(QTouchDevice *device,
72+ Qt::KeyboardModifiers modifiers,
63− TouchEvent(const QTouchEvent *event);
73+ const QList<QTouchEvent::TouchPoint> &touchPoints,
74+ QWindow *window,
75+ ulong timestamp);
64
76
65 bool removeTouch(int touchId);
77 bool removeTouch(int touchId);
66
78
67− QEvent::Type eventType;
68 QTouchDevice *device;
79 QTouchDevice *device;
69 Qt::KeyboardModifiers modifiers;
80 Qt::KeyboardModifiers modifiers;
70 QList<QTouchEvent::TouchPoint> touchPoints;
81 QList<QTouchEvent::TouchPoint> touchPoints;
71− QQuickItem *target;
72 QWindow *window;
82 QWindow *window;
73 ulong timestamp;
83 ulong timestamp;
74 };
84 };
75
85
76 void touchOwnershipEvent(TouchOwnershipEvent *event);
86 void touchOwnershipEvent(TouchOwnershipEvent *event);
77 bool isTouchPointOwned(int touchId) const;
87 bool isTouchPointOwned(int touchId) const;
78− void storeTouchEvent(const QTouchEvent *event);
88+ void storeTouchEvent(QTouchDevice *device,
89+ Qt::KeyboardModifiers modifiers,
90+ const QList<QTouchEvent::TouchPoint> &touchPoints,
91+ QWindow *window,
92+ ulong timestamp);
79 void removeTouchFromStoredEvents(int touchId);
93 void removeTouchFromStoredEvents(int touchId);
80 void dispatchFullyOwnedEvents();
94 void dispatchFullyOwnedEvents();
81 bool eventIsFullyOwned(const TouchEvent &event) const;
95 bool eventIsFullyOwned(const TouchEvent &event) const;
82
96
83 void dispatchTouchEventToTarget(const TouchEvent &event);
97 void dispatchTouchEventToTarget(const TouchEvent &event);
84− void dispatchTouchEventToTarget(QTouchEvent* event);
85
98
86 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
99 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
87
100
qml/Components/DragHandle.qml+18−33
@@ -43,16 +43,8 @@ import Ubuntu.Gestures 0.1
43 }
43 }
44
44
45 */
45 */
46−EdgeDragArea {
46+DirectionalDragArea {
47 id: dragArea
47 id: dragArea
48− objectName: "dragHandle"
49−
50− // Disable gesture recognition by default when hinting is used as
51− // it conflicts with the hinting idea.
52− distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53− maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54− maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55− compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
56
48
57 property bool stretch: false
49 property bool stretch: false
58
50
@@ -68,6 +60,9 @@ EdgeDragArea {
68 }
60 }
69
61
70 property real hintDisplacement: 0
62 property real hintDisplacement: 0
63+
64+ immediateRecognition: hintDisplacement > 0
65+
71 property var overrideStartValue: undefined
66 property var overrideStartValue: undefined
72 SmoothedAnimation {
67 SmoothedAnimation {
73 id: hintingAnimation
68 id: hintingAnimation
@@ -98,7 +93,6 @@ EdgeDragArea {
98 // Private stuff
93 // Private stuff
99 QtObject {
94 QtObject {
100 id: d
95 id: d
101− property var previousStatus: DirectionalDragArea.WaitingForTouch
102 property real startValue
96 property real startValue
103 property real minValue: {
97 property real minValue: {
104 if (direction == Direction.Horizontal) {
98 if (direction == Direction.Horizontal) {
@@ -183,7 +177,7 @@ EdgeDragArea {
183 }
177 }
184
178
185 onDistanceChanged: {
179 onDistanceChanged: {
186− if (status === DirectionalDragArea.Recognized) {
180+ if (dragging) {
187 // don't go the whole distance in order to smooth out the movement
181 // don't go the whole distance in order to smooth out the movement
188 var step = distance * 0.3;
182 var step = distance * 0.3;
189
183
@@ -193,31 +187,22 @@ EdgeDragArea {
193 }
187 }
194 }
188 }
195
189
196− onStatusChanged: {
190+ onDraggingChanged: {
197− if (status === DirectionalDragArea.WaitingForTouch) {
191+ if (dragging) {
198− hintingAnimation.stop();
192+ dragEvaluator.reset();
199− if (d.previousStatus === DirectionalDragArea.Recognized) {
193+ if (overrideStartValue !== undefined) {
200− d.onFinishedRecognizedGesture();
194+ d.startValue = overrideStartValue;
201− } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
195+ } else {
202− // Gesture was rejected.
196+ d.startValue = parent[d.targetProp];
203− d.rollbackDrag();
204 }
197 }
205− } else /* Undecided || Recognized */ {
206− if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207− dragEvaluator.reset();
208− if (overrideStartValue !== undefined) {
209− d.startValue = overrideStartValue;
210− } else {
211− d.startValue = parent[d.targetProp];
212− }
213
198
214− if (hintDisplacement > 0) {
199+ if (hintDisplacement > 0) {
215− hintingAnimation.targetValue = d.startValue;
200+ hintingAnimation.targetValue = d.startValue;
216− hintingAnimation.start();
201+ hintingAnimation.start();
217− }
218 }
202 }
203+ } else {
204+ hintingAnimation.stop();
205+ d.onFinishedRecognizedGesture();
219 }
206 }
220−
221− d.previousStatus = status;
222 }
207 }
223}
208}
qml/Components/EdgeDragArea.qmldeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−
21−/*
22− A DirectionalDragArea wrapper that provides some well-chosen defaults
23− for its gesture recognition parameters.
24−*/
25−DirectionalDragArea {
26−
27− // TODO: Re-evaluate those or even the recognition heuristics itself once
28− // we have gesture cancelling/forwarding in place.
29− //
30− // The idea here is that it's better having lax rules than false negatives.
31− // False negatives are very frustrating to the user.
32− maxDeviation: defaultMaxDeviation
33− wideningAngle: defaultWideningAngle
34− distanceThreshold: defaultDistanceThreshold
35− minSpeed: defaultMinSpeed
36− maxSilenceTime: defaultMaxSilenceTime
37− compositionTime: defaultCompositionTime
38−
39− readonly property real defaultMaxDeviation: units.gu(3)
40− readonly property real defaultWideningAngle: 50
41− readonly property real defaultDistanceThreshold: units.gu(1.5)
42− // some people were getting false negatives with it enabled.
43− readonly property real defaultMinSpeed: units.gu(0)
44− readonly property int defaultMaxSilenceTime: 200
45− readonly property int defaultCompositionTime: 60
46−}
qml/Dash/Dash.qml+5−12
@@ -140,7 +140,7 @@ Showable {
140 // (as expected) but would also cause the dash content flickable to move a bit, because
140 // (as expected) but would also cause the dash content flickable to move a bit, because
141 // that flickable was getting the touch events while overviewDragHandle was still undecided
141 // that flickable was getting the touch events while overviewDragHandle was still undecided
142 // about whether that touch was indeed performing a directional drag gesture.
142 // about whether that touch was indeed performing a directional drag gesture.
143− forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
143+ forceNonInteractive: overviewDragHandle.dragging
144
144
145 enabled: bottomEdgeController.progress == 0
145 enabled: bottomEdgeController.progress == 0
146 }
146 }
@@ -317,7 +317,7 @@ Showable {
317 }
317 }
318 }
318 }
319
319
320− EdgeDragArea {
320+ DirectionalDragArea {
321 id: overviewDragHandle
321 id: overviewDragHandle
322 objectName: "overviewDragHandle"
322 objectName: "overviewDragHandle"
323 z: 1
323 z: 1
@@ -333,21 +333,14 @@ Showable {
333 height: units.gu(2)
333 height: units.gu(2)
334
334
335 onSceneDistanceChanged: {
335 onSceneDistanceChanged: {
336− if (status == DirectionalDragArea.Recognized) {
336+ if (dragging) {
337 bottomEdgeController.enableAnimation = false;
337 bottomEdgeController.enableAnimation = false;
338 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
338 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
339 }
339 }
340 }
340 }
341
341
342− property int previousStatus: -1
342+ onDraggingChanged: {
343− property int currentStatus: DirectionalDragArea.WaitingForTouch
343+ if (!dragging) {
344−
345− onStatusChanged: {
346− previousStatus = currentStatus;
347− currentStatus = status;
348−
349− if (status == DirectionalDragArea.WaitingForTouch &&
350− previousStatus == DirectionalDragArea.Recognized) {
351 bottomEdgeController.enableAnimation = true;
344 bottomEdgeController.enableAnimation = true;
352 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
345 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
353 }
346 }
qml/Greeter/CoverPage.qml+1
@@ -129,6 +129,7 @@ Showable {
129
129
130 DragHandle {
130 DragHandle {
131 id: dragHandle
131 id: dragHandle
132+ objectName: "coverPageDragHandle"
132 anchors.fill: parent
133 anchors.fill: parent
133 anchors.leftMargin: root.dragHandleLeftMargin
134 anchors.leftMargin: root.dragHandleLeftMargin
134 enabled: root.draggable
135 enabled: root.draggable
qml/Launcher/Launcher.qml+6−14
@@ -210,7 +210,7 @@ Item {
210 bottom: parent.bottom
210 bottom: parent.bottom
211 }
211 }
212 x: -width
212 x: -width
213− visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width
214 model: LauncherModel
214 model: LauncherModel
215
215
216 property bool animate: true
216 property bool animate: true
@@ -272,7 +272,7 @@ Item {
272 }
272 }
273 }
273 }
274
274
275− EdgeDragArea {
275+ DirectionalDragArea {
276 id: dragArea
276 id: dragArea
277 objectName: "launcherDragArea"
277 objectName: "launcherDragArea"
278
278
@@ -284,19 +284,10 @@ Item {
284 height: root.height
284 height: root.height
285
285
286 onTouchXChanged: {
286 onTouchXChanged: {
287− if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
287+ if (!dragging || launcher.state == "visible")
288 return;
288 return;
289
289
290− // When the gesture finally gets recognized, the finger will likely be
291− // reasonably far from the edge. If we made the panel immediately
292− // follow the finger position it would be visually unpleasant as it
293− // would appear right next to the user's finger out of nowhere.
294− // Instead, we make the panel go towards the user's finger in several
295− // steps. ie., in an animated way.
296− var targetPanelX = Math.min(0, touchX - panel.width) - root.x
290+ panel.x = Math.min(0, touchX - panel.width) - root.x
297− var delta = targetPanelX - panel.x
298− // the trick is not to go all the way (1.0) as it would cause a sudden jump
299− panel.x += 0.4 * delta
300 }
291 }
301
292
302 onDraggingChanged: {
293 onDraggingChanged: {
@@ -306,7 +297,8 @@ Item {
306 if (distance > minimizeDistance) {
297 if (distance > minimizeDistance) {
307 root.dash();
298 root.dash();
308 }
299 }
309− } else {
300+ } else if (root.state === "") {
301+ // didn't drag far enough. rollback
310 root.switchToNextState("")
302 root.switchToNextState("")
311 }
303 }
312 }
304 }
qml/Panel/IndicatorsMenu.qml+12−2
@@ -182,9 +182,18 @@ Showable {
182 enabled: !root.shown && root.available
182 enabled: !root.shown && root.available
183 autoCompleteDragThreshold: maxTotalDragDistance / 2
183 autoCompleteDragThreshold: maxTotalDragDistance / 2
184 stretch: true
184 stretch: true
185− distanceThreshold: enableHint ? 0 : minimizedPanelHeight
186
185
186+ onDraggingChanged: {
187+ if (dragging) {
188+ touchPressTime = new Date().getTime();
189+ } else {
190+ var touchReleaseTime = new Date().getTime();
191+ if (touchReleaseTime - touchPressTime <= 300) {
187− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
192+ root.showTapped(Qt.point(touchSceneX, touchSceneY));
193+ }
194+ }
195+ }
196+ property var touchPressTime
188
197
189 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
198 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
190 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
199 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -200,6 +209,7 @@ Showable {
200
209
201 DragHandle {
210 DragHandle {
202 id: __hideDragHandle
211 id: __hideDragHandle
212+ objectName: "hideDragHandle"
203 anchors.fill: handle
213 anchors.fill: handle
204 direction: Direction.Upwards
214 direction: Direction.Upwards
205 enabled: root.shown && root.available
215 enabled: root.shown && root.available
qml/Stages/PhoneStage.qml+31−41
@@ -146,14 +146,13 @@ Rectangle {
146 id: spreadView
146 id: spreadView
147 objectName: "spreadView"
147 objectName: "spreadView"
148 anchors.fill: parent
148 anchors.fill: parent
149− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
150− && draggedDelegateCount === 0
149+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
151 contentWidth: spreadRow.width - shift
150 contentWidth: spreadRow.width - shift
152 contentX: -shift
151 contentX: -shift
153
152
154 // This indicates when the spreadView is active. That means, all the animations
153 // This indicates when the spreadView is active. That means, all the animations
155 // are activated and tiles need to line up for the spread.
154 // are activated and tiles need to line up for the spread.
156− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
155+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
157
156
158 // The flickable needs to fill the screen in order to get touch events all over.
157 // The flickable needs to fill the screen in order to get touch events all over.
159 // However, we don't want to the user to be able to scroll back all the way. For
158 // However, we don't want to the user to be able to scroll back all the way. For
@@ -450,7 +449,7 @@ Rectangle {
450 }
449 }
451 }
450 }
452
451
453− EdgeDragArea {
452+ DirectionalDragArea {
454 id: spreadDragArea
453 id: spreadDragArea
455 objectName: "spreadDragArea"
454 objectName: "spreadDragArea"
456 direction: Direction.Leftwards
455 direction: Direction.Leftwards
@@ -462,59 +461,50 @@ Rectangle {
462 property var gesturePoints: new Array()
461 property var gesturePoints: new Array()
463
462
464 onTouchXChanged: {
463 onTouchXChanged: {
465− if (!dragging) {
464+ if (dragging) {
466− // Initial touch. Let's reset the spreadView to the starting position.
467− spreadView.phase = 0;
468− spreadView.contentX = -spreadView.shift;
469− }
470− if (dragging && status == DirectionalDragArea.Recognized) {
471 // Gesture recognized. Let's move the spreadView with the finger
465 // Gesture recognized. Let's move the spreadView with the finger
472 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
466 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
473 dragX = -dragX + spreadDragArea.width - spreadView.shift;
467 dragX = -dragX + spreadDragArea.width - spreadView.shift;
474 // Don't allow dragging further than the animation crossing with phase2's animation
468 // Don't allow dragging further than the animation crossing with phase2's animation
475 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
469 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
470+
476 spreadView.contentX = Math.min(dragX, maxMovement);
471 spreadView.contentX = Math.min(dragX, maxMovement);
472+ } else {
473+ // Initial touch. Let's reset the spreadView to the starting position.
474+ spreadView.phase = 0;
475+ spreadView.contentX = -spreadView.shift;
477 }
476 }
478− gesturePoints.push(touchX);
479− }
480−
481− property int previousStatus: -1
482− property int currentStatus: DirectionalDragArea.WaitingForTouch
483
477
484− onStatusChanged: {
478+ gesturePoints.push(touchX);
485− previousStatus = currentStatus;
486− currentStatus = status;
487 }
479 }
488
480
489 onDraggingChanged: {
481 onDraggingChanged: {
490 if (dragging) {
482 if (dragging) {
491 // A potential edge-drag gesture has started. Start recording it
483 // A potential edge-drag gesture has started. Start recording it
492 gesturePoints = [];
484 gesturePoints = [];
493− return;
485+ } else {
494− }
486+ // Ok. The user released. Find out if it was a one-way movement.
487+ var oneWayFlick = true;
488+ var smallestX = spreadDragArea.width;
489+ for (var i = 0; i < gesturePoints.length; i++) {
490+ if (gesturePoints[i] >= smallestX) {
491+ oneWayFlick = false;
492+ break;
493+ }
494+ smallestX = gesturePoints[i];
495+ }
496+ gesturePoints = [];
495
497
498+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
499+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
496− // Ok. The user released. Find out if it was a one-way movement.
500+ // If it was a short one-way movement, do the Alt+Tab switch
497− var oneWayFlick = true;
501+ // no matter if we didn't cross positionMarker1 yet.
498− var smallestX = spreadDragArea.width;
502+ spreadView.snapTo(1);
499− for (var i = 0; i < gesturePoints.length; i++) {
503+ } else if (!dragging) {
500− if (gesturePoints[i] >= smallestX) {
504+ // otherwise snap to the closest snap position we can find
501− oneWayFlick = false;
505+ // (might be back to start, to app 1 or to spread)
502− break;
506+ spreadView.snap();
503 }
507 }
504− smallestX = gesturePoints[i];
505− }
506− gesturePoints = [];
507−
508− if (previousStatus == DirectionalDragArea.Recognized &&
509− oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
510− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
511− // If it was a short one-way movement, do the Alt+Tab switch
512− // no matter if we didn't cross positionMarker1 yet.
513− spreadView.snapTo(1);
514− } else if (!dragging) {
515− // otherwise snap to the closest snap position we can find
516− // (might be back to start, to app 1 or to spread)
517− spreadView.snap();
518 }
508 }
519 }
509 }
520 }
510 }
qml/Stages/TabletStage.qml+18−20
@@ -170,8 +170,7 @@ Rectangle {
170 Flickable {
170 Flickable {
171 id: spreadView
171 id: spreadView
172 anchors.fill: parent
172 anchors.fill: parent
173− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
174− && draggedDelegateCount === 0
173+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
175 contentWidth: spreadRow.width - shift
174 contentWidth: spreadRow.width - shift
176 contentX: -shift
175 contentX: -shift
177
176
@@ -598,7 +597,7 @@ Rectangle {
598 }
597 }
599 }
598 }
600
599
601− EdgeDragArea {
600+ DirectionalDragArea {
602 id: spreadDragArea
601 id: spreadDragArea
603 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
602 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
604 width: root.dragAreaWidth
603 width: root.dragAreaWidth
@@ -624,26 +623,25 @@ Rectangle {
624 if (dragging) {
623 if (dragging) {
625 // Gesture recognized. Start recording this gesture
624 // Gesture recognized. Start recording this gesture
626 gesturePoints = [];
625 gesturePoints = [];
627− return;
626+ } else {
628− }
629−
630− // Ok. The user released. Find out if it was a one-way movement.
627+ // Ok. The user released. Find out if it was a one-way movement.
631− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
628+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
632− gesturePoints = [];
629+ gesturePoints = [];
633
630
634− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
631+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
635− // If it was a short one-way movement, do the Alt+Tab switch
632+ // If it was a short one-way movement, do the Alt+Tab switch
636− // no matter if we didn't cross positionMarker1 yet.
633+ // no matter if we didn't cross positionMarker1 yet.
637− spreadView.snapTo(spreadView.nextInStack);
638− } else if (!dragging) {
639− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
640− spreadView.snap();
641− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
642 spreadView.snapTo(spreadView.nextInStack);
634 spreadView.snapTo(spreadView.nextInStack);
643 } else {
635 } else {
636+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
637+ spreadView.snap();
638+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
639+ spreadView.snapTo(spreadView.nextInStack);
640+ } else {
644− // otherwise snap to the closest snap position we can find
641+ // otherwise snap to the closest snap position we can find
645− // (might be back to start, to app 1 or to spread)
642+ // (might be back to start, to app 1 or to spread)
646− spreadView.snap();
643+ spreadView.snap();
644+ }
647 }
645 }
648 }
646 }
649 }
647 }
qml/Tutorial/TutorialBottom.qml+1−1
@@ -73,7 +73,7 @@ TutorialPage {
73 ]
73 ]
74 }
74 }
75
75
76− EdgeDragArea {
76+ DirectionalDragArea {
77 id: dragArea
77 id: dragArea
78 direction: Direction.Upwards
78 direction: Direction.Upwards
79 anchors {
79 anchors {
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+68−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -66,6 +66,7 @@ private Q_SLOTS:
66 void candidatesAndWatchers_2();
66 void candidatesAndWatchers_2();
67 void rejectingTouchfterItsEnd();
67 void rejectingTouchfterItsEnd();
68 void removeOldUndecidedCandidates();
68 void removeOldUndecidedCandidates();
69+ void interimOwnerWontGetUnownedTouchEvents();
69};
70};
70
71
71void tst_TouchRegistry::requestWithNoCandidates()
72void tst_TouchRegistry::requestWithNoCandidates()
@@ -742,7 +743,7 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
742
743
743 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744 // making TouchRegistry consider that undecidedCantidate defaulted.
745 // making TouchRegistry consider that undecidedCantidate defaulted.
745− fakeTimerFactory->makeRunningTimersTimeout();
746+ fakeTimerFactory->updateTime(10000);
746
747
747 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748 QVERIFY(undecidedCandidate.lostTouches.contains(0));
749 QVERIFY(undecidedCandidate.lostTouches.contains(0));
@@ -752,6 +753,71 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
752 delete touchRegistry;
753 delete touchRegistry;
753}
754}
754
755
756+/*
757+ An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
758+ is assumed to be the interim owner of the touch point, thus there's no point in sending
759+ UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
760+ he didn't ignore the first QTouchEvent with that touch point.
761+ */
762+void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
763+{
764+ FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
765+ TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
766+
767+ DummyCandidate undecidedCandidate;
768+ undecidedCandidate.setObjectName("undecided");
769+
770+ DummyCandidate interimOwner;
771+ interimOwner.setObjectName("interimOwner");
772+
773+ {
774+ QList<QTouchEvent::TouchPoint> touchPoints;
775+ touchPoints.append(QTouchEvent::TouchPoint(0));
776+ touchPoints[0].setState(Qt::TouchPointPressed);
777+ QTouchEvent touchEvent(QEvent::TouchBegin,
778+ 0 /* device */,
779+ Qt::NoModifier,
780+ Qt::TouchPointPressed,
781+ touchPoints);
782+ touchRegistry->update(&touchEvent);
783+ }
784+
785+ touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
786+ touchRegistry->requestTouchOwnership(0, &interimOwner);
787+
788+ {
789+ QList<QTouchEvent::TouchPoint> touchPoints;
790+ touchPoints.append(QTouchEvent::TouchPoint(0));
791+ touchPoints[0].setState(Qt::TouchPointMoved);
792+ QTouchEvent touchEvent(QEvent::TouchUpdate,
793+ 0 /* device */,
794+ Qt::NoModifier,
795+ Qt::TouchPointMoved,
796+ touchPoints);
797+ touchRegistry->update(&touchEvent);
798+ }
799+
800+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
801+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
802+
803+ {
804+ QList<QTouchEvent::TouchPoint> touchPoints;
805+ touchPoints.append(QTouchEvent::TouchPoint(0));
806+ touchPoints[0].setState(Qt::TouchPointMoved);
807+ QTouchEvent touchEvent(QEvent::TouchUpdate,
808+ 0 /* device */,
809+ Qt::NoModifier,
810+ Qt::TouchPointMoved,
811+ touchPoints);
812+ touchRegistry->update(&touchEvent);
813+ }
814+
815+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
816+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
817+
818+ delete touchRegistry;
819+}
820+
755////////////// TouchMemento //////////
821////////////// TouchMemento //////////
756
822
757TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
823TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−1
@@ -19,6 +19,7 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
19)
19)
20
20
21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
22+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
22
23
23macro(add_gesture_ui_test CLASSNAME)
24macro(add_gesture_ui_test CLASSNAME)
24 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
25 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
@@ -26,7 +27,8 @@ macro(add_gesture_ui_test CLASSNAME)
26 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
27 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
27
28
28 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29− add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
30+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
31+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
30endmacro(add_gesture_ui_test)
32endmacro(add_gesture_ui_test)
31
33
32macro(add_gesture_test CLASSNAME)
34macro(add_gesture_test CLASSNAME)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19
@@ -37,8 +37,8 @@ Item {
37
37
38 Rectangle {
38 Rectangle {
39 id: dragAreaRect
39 id: dragAreaRect
40− color: "yellow"
41− opacity: 0.0
40+ opacity: dragArea.dragging ? 0.5 : 0.0
41+ color: "green"
42 anchors.fill: dragArea
42 anchors.fill: dragArea
43 }
43 }
44
44
@@ -49,24 +49,10 @@ Item {
49 height: units.gu(5)
49 height: units.gu(5)
50
50
51 direction: Direction.Downwards
51 direction: Direction.Downwards
52− maxDeviation: units.gu(2)
53− wideningAngle: 10
54− distanceThreshold: units.gu(4)
55
52
56− onStatusChanged: {
53+ onDraggingChanged: {
57− switch (status) {
54+ if (dragging) {
58− case DirectionalDragArea.WaitingForTouch:
59− dragAreaRect.opacity = 0.0
60− break;
61− case DirectionalDragArea.Undecided:
62− dragAreaRect.color = "yellow"
63− dragAreaRect.opacity = 0.3
64− launcher.y = Qt.binding(launcher.followDragArea)
55+ launcher.y = Qt.binding(launcher.followDragArea)
65− break;
66− default: // DirectionalDragArea.Recognized:
67− dragAreaRect.color = "green"
68− dragAreaRect.opacity = 0.5
69− break;
70 }
56 }
71 }
57 }
72
58
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+13−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
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
@@ -21,8 +21,11 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QtTest>
22#include <QtTest>
23
23
24+#include <Timer.h>
24#include <TouchRegistry.h>
25#include <TouchRegistry.h>
25
26
27+using namespace UbuntuGestures;
28+
26GestureTest::GestureTest(const QString &qmlFilename)
29GestureTest::GestureTest(const QString &qmlFilename)
27 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
30 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
28{
31{
@@ -40,14 +43,17 @@ void GestureTest::initTestCase()
40void GestureTest::init()
43void GestureTest::init()
41{
44{
42 m_view = new QQuickView;
45 m_view = new QQuickView;
43− m_view->setResizeMode(QQuickView::SizeViewToRootObject);
46+ m_view->setResizeMode(QQuickView::SizeRootObjectToView);
44 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
47 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
48+ m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
45 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
49 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
46 m_view->show();
50 m_view->show();
47 QVERIFY(QTest::qWaitForWindowExposed(m_view));
51 QVERIFY(QTest::qWaitForWindowExposed(m_view));
48 QVERIFY(m_view->rootObject() != 0);
52 QVERIFY(m_view->rootObject() != 0);
49
53
54+ m_fakeTimerFactory = new FakeTimerFactory;
55+
50− m_touchRegistry = new TouchRegistry;
56+ m_touchRegistry = new TouchRegistry(nullptr, m_fakeTimerFactory);
51 m_view->installEventFilter(m_touchRegistry);
57 m_view->installEventFilter(m_touchRegistry);
52
58
53 qApp->processEvents();
59 qApp->processEvents();
@@ -59,6 +65,10 @@ void GestureTest::cleanup()
59 delete m_touchRegistry;
65 delete m_touchRegistry;
60 m_touchRegistry = nullptr;
66 m_touchRegistry = nullptr;
61
67
68+ // TouchRegistry will take down the timer factory along with him
69+ // delete m_fakeTimerFactory;
70+ m_fakeTimerFactory = nullptr;
71+
62 delete m_view;
72 delete m_view;
63 m_view = nullptr;
73 m_view = nullptr;
64}
74}
tests/plugins/Ubuntu/Gestures/GestureTest.h+4
@@ -23,6 +23,9 @@
23class QQuickView;
23class QQuickView;
24class QTouchDevice;
24class QTouchDevice;
25
25
26+namespace UbuntuGestures {
27+ class FakeTimerFactory;
28+}
26class TouchRegistry;
29class TouchRegistry;
27
30
28// C++ std lib
31// C++ std lib
@@ -81,6 +84,7 @@ protected:
81 QTouchDevice *m_device;
84 QTouchDevice *m_device;
82 QQuickView *m_view;
85 QQuickView *m_view;
83 TouchRegistry *m_touchRegistry;
86 TouchRegistry *m_touchRegistry;
87+ UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
84 QString m_qmlFilename;
88 QString m_qmlFilename;
85};
89};
86
90
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
21Item {
21Item {
22 id: root
22 id: root
23
23
24− function reset() { launcher.x = root.width }
24+ function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
25
25
26 Rectangle {
26 Rectangle {
27 id: launcher
27 id: launcher
@@ -41,7 +41,8 @@ Item {
41
41
42 Rectangle {
42 Rectangle {
43 id: dragAreaRect
43 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
45 anchors.fill: dragArea
46 anchors.fill: dragArea
46 }
47 }
47
48
@@ -52,24 +53,10 @@ Item {
52 width: units.gu(5)
53 width: units.gu(5)
53
54
54 direction: Direction.Leftwards
55 direction: Direction.Leftwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58
56
59− onStatusChanged: {
57+ onDraggingChanged: {
60− switch (status) {
58+ if (dragging) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.x = Qt.binding(launcher.followDragArea)
59+ launcher.x = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
73 }
60 }
74 }
61 }
75
62
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+5−19
@@ -38,7 +38,8 @@ Item {
38
38
39 Rectangle {
39 Rectangle {
40 id: dragAreaRect
40 id: dragAreaRect
41− opacity: 0.0
41+ opacity: dragArea.dragging ? 0.5 : 0.0
42+ color: "green"
42 anchors.fill: dragArea
43 anchors.fill: dragArea
43 }
44 }
44
45
@@ -52,25 +53,10 @@ Item {
52 width: units.gu(5)
53 width: units.gu(5)
53
54
54 direction: Direction.Rightwards
55 direction: Direction.Rightwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58− minSpeed: 50
59
56
60− onStatusChanged: {
57+ onDraggingChanged: {
61− switch (status) {
58+ if (dragging) {
62− case DirectionalDragArea.WaitingForTouch:
63− dragAreaRect.opacity = 0.0
64− break;
65− case DirectionalDragArea.Undecided:
66− dragAreaRect.color = "yellow"
67− dragAreaRect.opacity = 0.3
68− launcher.x = Qt.binding(launcher.followDragArea)
59+ launcher.x = Qt.binding(launcher.followDragArea)
69− break;
70− default: //case DirectionalDragArea.Recognized:
71− dragAreaRect.color = "green"
72− dragAreaRect.opacity = 0.5
73− break;
74 }
60 }
75 }
61 }
76
62
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
21Item {
21Item {
22 id: root
22 id: root
23
23
24− function reset() { launcher.y = root.height }
24+ function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
25
25
26 Rectangle {
26 Rectangle {
27 id: launcher
27 id: launcher
@@ -41,7 +41,8 @@ Item {
41
41
42 Rectangle {
42 Rectangle {
43 id: dragAreaRect
43 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
45 anchors.fill: dragArea
46 anchors.fill: dragArea
46 }
47 }
47
48
@@ -52,24 +53,10 @@ Item {
52 height: units.gu(5)
53 height: units.gu(5)
53
54
54 direction: Direction.Upwards
55 direction: Direction.Upwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58
56
59− onStatusChanged: {
57+ onDraggingChanged: {
60− switch (status) {
58+ if (dragging) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.y = Qt.binding(launcher.followDragArea)
59+ launcher.y = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
73 }
60 }
74 }
61 }
75
62
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+480−445
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
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
@@ -20,51 +20,38 @@
20#include <QtQml/QQmlEngine>
20#include <QtQml/QQmlEngine>
21#include <QPointer>
21#include <QPointer>
22#include <private/qquickmousearea_p.h>
22#include <private/qquickmousearea_p.h>
23−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
24− #include <private/qquickwindow_p.h>
23+#include <private/qquickwindow_p.h>
25−#endif
26
24
27
25
28#include <DirectionalDragArea.h>
26#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
29#include <TouchRegistry.h>
28#include <TouchRegistry.h>
30
29
31#include "GestureTest.h"
30#include "GestureTest.h"
32
31
33using namespace UbuntuGestures;
32using namespace UbuntuGestures;
34
33
35−
34+// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
36−class ComplexFakeTimer : public FakeTimer
35+// doesn't work
37−{
36+class StatusSpy : public QObject {
38 Q_OBJECT
37 Q_OBJECT
39public:
38public:
40− ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
39+ StatusSpy(DirectionalDragArea *edgeDragArea) {
41− : FakeTimer(parent),
40+ m_recognized = false;
42− m_timeSource(timeSource)
41+ connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
43− {}
42+ this, &StatusSpy::onStatusChanged);
44−
45− void start() override {
46− AbstractTimer::start();
47− m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
48 }
43 }
49−
44+ bool recognized() {
50− void emitTimeout() {
45+ return m_recognized;
51− m_nextTimeoutTime += interval();
52− Q_EMIT timeout();
53 }
46 }
54
47
55− qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
48+private Q_SLOTS:
49+ void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
50+ m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
51+ }
56
52
57private:
53private:
58− SharedTimeSource m_timeSource;
54+ bool m_recognized;
59− qint64 m_nextTimeoutTime;
60−};
61−
62−class FakeTimeSource : public UbuntuGestures::TimeSource
63−{
64−public:
65− FakeTimeSource() { m_msecsSinceReference = 0; }
66− virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
67− qint64 m_msecsSinceReference;
68};
55};
69
56
70/*
57/*
@@ -98,15 +85,9 @@ public:
98 tst_DirectionalDragArea();
85 tst_DirectionalDragArea();
99private Q_SLOTS:
86private Q_SLOTS:
100 void init(); // called right before each and every test function is executed
87 void init(); // called right before each and every test function is executed
101− void cleanup(); // called right after each and every test function is executed
102
88
103− void edgeDrag();
104− void edgeDrag_data();
105 void dragWithShortDirectionChange();
89 void dragWithShortDirectionChange();
106− void minSpeed();
107− void minSpeed_data();
108 void recognitionTimerUsage();
90 void recognitionTimerUsage();
109− void maxSilenceTime();
110 void sceneXAndX();
91 void sceneXAndX();
111 void sceneYAndY();
92 void sceneYAndY();
112 void twoFingerTap();
93 void twoFingerTap();
@@ -122,14 +103,24 @@ private Q_SLOTS:
122 void immediateRecognitionWhenConstraintsDisabled();
103 void immediateRecognitionWhenConstraintsDisabled();
123 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
104 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
124 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
105 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
125− void tappedSignal();
126− void tappedSignal_data();
127 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
106 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107+ void interleavedTouches();
108+ void makoRightEdgeDrag();
109+ void makoRightEdgeDrag_verticalDownwards();
110+ void makoLeftEdgeDrag_slowStart();
111+ void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
128
112
129private:
113private:
114+ // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115+ // lose precision due to rounding.
116+ // Besides, those helper functions lead to more compact code.
117+ void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118+ void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119+ void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120+ void sendTouch(qint64 timestamp, int id, QPointF pos,
121+ Qt::TouchPointState pointState, QEvent::Type eventType);
122+
130 void passTime(qint64 timeSpanMs);
123 void passTime(qint64 timeSpanMs);
131− ComplexFakeTimer *fakeTimer;
132− QSharedPointer<FakeTimeSource> fakeTimeSource;
133};
124};
134
125
135tst_DirectionalDragArea::tst_DirectionalDragArea()
126tst_DirectionalDragArea::tst_DirectionalDragArea()
@@ -148,185 +139,57 @@ void tst_DirectionalDragArea::init()
148 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
139 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
149 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
140 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
150 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
141 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
151−
152− fakeTimeSource.reset(new FakeTimeSource);
153− fakeTimer = new ComplexFakeTimer(fakeTimeSource);
154−}
155−
156−void tst_DirectionalDragArea::cleanup()
157−{
158− delete fakeTimer;
159− fakeTimer = 0;
160−
161− fakeTimeSource.reset();
162−
163− GestureTest::cleanup();
164−}
165−
166−void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
167−{
168− qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;
169−
170− if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
171− fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
172− fakeTimer->emitTimeout();
173−
174− qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
175− if (timeSpanRemainder > 0) {
176− passTime(timeSpanRemainder);
177− }
178− } else {
179− fakeTimeSource->m_msecsSinceReference = finalTime;
180− }
181}
142}
182
143
183−namespace {
144+void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
184−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
185{
145{
186− QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
146+ sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
187− return edgeDragArea->mapToScene(localCenter);
188}
147}
189
148
190−QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
149+void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
191− qreal wideningAngleMultiplier)
192{
150{
193− qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
151+ sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
194− * M_PI / 180.0;
195−
196− qreal angleCos = qCos(angleRadians);
197− qreal angleSin = qSin(angleRadians);
198−
199− switch (edgeDragArea->direction()) {
200− case Direction::Upwards:
201− return QPointF(angleSin, -angleCos);
202− case Direction::Downwards:
203− return QPointF(angleSin, angleCos);
204− case Direction::Leftwards:
205− return QPointF(-angleCos, angleSin);
206− default: // Direction::Rightwards:
207− return QPointF(angleCos, angleSin);
208− }
209}
152}
210
153
211−QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
154+void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
212{
155{
213− qreal deviation = edgeDragArea->maxDeviation() * 0.8;
156+ sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
214−
215− if (Direction::isHorizontal(edgeDragArea->direction())) {
216− return QPointF(0, deviation);
217− } else {
218− return QPointF(deviation, 0);
219− }
220−}
221}
157}
222
158
223−void tst_DirectionalDragArea::edgeDrag()
159+void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160+ Qt::TouchPointState pointState, QEvent::Type eventType)
224{
161{
225− QFETCH(QString, dragAreaObjectName);
162+ m_fakeTimerFactory->updateTime(timestamp);
226− QFETCH(qreal, wideningAngleMultiplier);
227− QFETCH(qreal, dragDistanceFactor);
228− QFETCH(bool, expectGestureRecognition);
229−
230− DirectionalDragArea *edgeDragArea =
231− m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
232− QVERIFY(edgeDragArea != 0);
233− edgeDragArea->setRecognitionTimer(fakeTimer);
234− edgeDragArea->setTimeSource(fakeTimeSource);
235−
236− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
237−
238− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
239− QPointF touchPoint = initialTouchPos;
240−
241− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
242− QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
243− wideningAngleMultiplier);
244− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
245− QPointF touchMovement = dragDirectionVector * movementStepDistance;
246− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
247− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
248−
249− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
250−
251− QCOMPARE(draggingSpy.count(), 1);
252− QCOMPARE(edgeDragArea->dragging(), true);
253−
254− if (wideningAngleMultiplier > 0) {
255− // go close to the border of the valid area for this touch point
256− // in order to make it easier to leave it by dragging at an angle
257− // slightly bigger than the widening angle
258− touchPoint += createTouchDeviation(edgeDragArea);
259− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
260− }
261
163
262− for (int i = 0; i < totalMovementSteps; ++i) {
164+ QTouchEvent::TouchPoint point;
263− touchPoint += touchMovement;
264− passTime(movementTimeStepMs);
265− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
266− }
267
165
268− if (expectGestureRecognition)
166+ point.setState(pointState);
269− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
167+ point.setId(id);
168+ point.setScenePos(pos);
169+ point.setPos(pos);
270
170
271− if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
171+ QList<QTouchEvent::TouchPoint> points;
272− QCOMPARE(edgeDragArea->dragging(), false);
172+ points << point;
273− QCOMPARE(draggingSpy.count(), 2);
274− }
275
173
276− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
174+ QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175+ QCoreApplication::sendEvent(m_view, &touchEvent);
277
176
278− QCOMPARE(draggingSpy.count(), 2);
177+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
279− QCOMPARE(edgeDragArea->dragging(), false);
178+ windowPrivate->flushDelayedTouchEvent();
280}
179}
281
180
282−void tst_DirectionalDragArea::edgeDrag_data()
181+void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
283{
182{
284− QTest::addColumn<QString>("dragAreaObjectName");
183+ qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
285− QTest::addColumn<qreal>("wideningAngleMultiplier");
184+ m_fakeTimerFactory->updateTime(finalTime);
286− QTest::addColumn<qreal>("dragDistanceFactor");
185+}
287− QTest::addColumn<bool>("expectGestureRecognition");
288−
289− QTest::newRow("rightwards, tiny drag")
290− << "hpDragArea" << 0.0 << 0.2 << false;
291−
292− QTest::newRow("rightwards, straight drag")
293− << "hpDragArea" << 0.0 << 3.0 << true;
294−
295− QTest::newRow("rightwards, diagonal drag")
296− << "hpDragArea" << 0.9 << 3.0 << true;
297−
298− QTest::newRow("rightwards, overly diagonal drag")
299− << "hpDragArea" << 2.0 << 3.0 << false;
300−
301− QTest::newRow("leftwards, tiny drag")
302− << "hnDragArea" << 0.0 << 0.2 << false;
303−
304− QTest::newRow("leftwards, straight drag")
305− << "hnDragArea" << 0.0 << 3.0 << true;
306−
307− QTest::newRow("leftwards, diagonal drag")
308− << "hnDragArea" << 0.9 << 3.0 << true;
309−
310− QTest::newRow("downwards, tiny drag")
311− << "vpDragArea" << 0.0 << 0.2 << false;
312−
313− QTest::newRow("downwards, straight drag")
314− << "vpDragArea" << 0.0 << 3.0 << true;
315−
316− QTest::newRow("downwards, diagonal drag")
317− << "vpDragArea" << 0.9 << 3.0 << true;
318−
319− QTest::newRow("upwards, tiny drag")
320− << "vnDragArea" << 0.0 << 0.2 << false;
321−
322− QTest::newRow("upwards, straight drag")
323− << "vnDragArea" << 0.0 << 3.0 << true;
324−
325− QTest::newRow("upwards, diagonal drag")
326− << "vnDragArea" << 0.9 << 3.0 << true;
327
186
328− QTest::newRow("upwards, overly diagonal drag")
187+namespace {
329− << "vnDragArea" << 2.0 << 3.0 << false;
188+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
189+{
190+ QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
191+ return edgeDragArea->mapToScene(localCenter);
192+}
330}
193}
331
194
332/*
195/*
@@ -339,17 +202,17 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
339 DirectionalDragArea *edgeDragArea =
202 DirectionalDragArea *edgeDragArea =
340 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
203 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
341 QVERIFY(edgeDragArea != 0);
204 QVERIFY(edgeDragArea != 0);
342− edgeDragArea->setRecognitionTimer(fakeTimer);
205+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
343− edgeDragArea->setTimeSource(fakeTimeSource);
206+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
344
207
345 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
208 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
346 QPointF touchPoint = initialTouchPos;
209 QPointF touchPoint = initialTouchPos;
347
210
348− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
211+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
349 QPointF dragDirectionVector(1.0, 0.0);
212 QPointF dragDirectionVector(1.0, 0.0);
350− qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
213+ qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
351− // make sure we are above minimum speed
214+ // make sure we are above maximum time
352− int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
215+ int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
353 QPointF touchMovement = dragDirectionVector * touchStepDistance;
216 QPointF touchMovement = dragDirectionVector * touchStepDistance;
354
217
355 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
218 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -373,65 +236,13 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
373 passTime(touchStepTimeMs);
236 passTime(touchStepTimeMs);
374 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
237 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
238 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
376− || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
239+ || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
377
240
378− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
241+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
379
242
380 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
243 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
381}
244}
382
245
383−/*
384− Checks that a gesture will be rejected if it's slower than minSpeed while
385− status is Undecided.
386− */
387−void tst_DirectionalDragArea::minSpeed()
388−{
389− QFETCH(qreal, minSpeed);
390− QFETCH(qreal, speed);
391− QFETCH(int, expectedStatusAfterSpeedCheck);
392−
393− DirectionalDragArea *edgeDragArea =
394− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
395− QVERIFY(edgeDragArea != 0);
396− edgeDragArea->setRecognitionTimer(fakeTimer);
397− edgeDragArea->setTimeSource(fakeTimeSource);
398−
399− // A really long, unattainable, number. We don't want it getting recognized before
400− // the speed checks we want have been performed
401− edgeDragArea->setDistanceThreshold(500000);
402−
403− edgeDragArea->setMinSpeed(minSpeed);
404−
405− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
406− QPointF touchPoint = initialTouchPos;
407−
408− QPointF dragDirectionVector(1.0, 0.0);
409− qint64 timeStepMsecs = 10;
410− qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
411− QPointF touchMovement = dragDirectionVector * distanceStep;
412−
413− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
414−
415− // Move for a while to ensure our speed check is performed a couple of times
416− for (int i=0; i < 20; ++i) {
417− touchPoint += touchMovement;
418− passTime(timeStepMsecs);
419− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
420− }
421−
422− QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
423−}
424−
425−void tst_DirectionalDragArea::minSpeed_data()
426−{
427− QTest::addColumn<qreal>("minSpeed");
428− QTest::addColumn<qreal>("speed");
429− QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
430−
431− QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
432− QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
433−}
434−
435/*
246/*
436 Checks that the recognition timer is started and stopped appropriately.
247 Checks that the recognition timer is started and stopped appropriately.
437 I.e., check that it's running only while gesture recognition is taking place
248 I.e., check that it's running only while gesture recognition is taking place
@@ -442,11 +253,9 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
442 DirectionalDragArea *edgeDragArea =
253 DirectionalDragArea *edgeDragArea =
443 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
254 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444 QVERIFY(edgeDragArea != 0);
255 QVERIFY(edgeDragArea != 0);
256+ AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
445− edgeDragArea->setRecognitionTimer(fakeTimer);
257+ edgeDragArea->d->setRecognitionTimer(fakeTimer);
446− edgeDragArea->setTimeSource(fakeTimeSource);
258+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
447−
448− // don't let it interfere with our test
449− edgeDragArea->setMinSpeed(0.0);
450
259
451 int timeStepMs = 5; // some arbitrary small value.
260 int timeStepMs = 5; // some arbitrary small value.
452
261
@@ -454,62 +263,31 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
454 QPointF touchPoint = initialTouchPos;
263 QPointF touchPoint = initialTouchPos;
455
264
456 QPointF dragDirectionVector(1.0, 0.0);
265 QPointF dragDirectionVector(1.0, 0.0);
457− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
266+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
458
267
459− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
268+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
460 QVERIFY(!fakeTimer->isRunning());
269 QVERIFY(!fakeTimer->isRunning());
461
270
462 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
271 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
463
272
464− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
273+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
465 QVERIFY(fakeTimer->isRunning());
274 QVERIFY(fakeTimer->isRunning());
466
275
467 // Move beyond distance threshold and composition time to ensure recognition
276 // Move beyond distance threshold and composition time to ensure recognition
468− while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
277+ while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
469− (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
278+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
470
279
471− QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
280+ QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
472
281
473 touchPoint += touchMovement;
282 touchPoint += touchMovement;
474 passTime(timeStepMs);
283 passTime(timeStepMs);
475 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
284 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
476 }
285 }
477
286
478− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
287+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
479 QVERIFY(!fakeTimer->isRunning());
288 QVERIFY(!fakeTimer->isRunning());
480}
289}
481
290
482−/*
483− A gesture should be rejected if too much time has passed without any new input
484− events from it.
485− */
486−void tst_DirectionalDragArea::maxSilenceTime()
487−{
488− DirectionalDragArea *edgeDragArea =
489− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
490− QVERIFY(edgeDragArea != 0);
491− edgeDragArea->setRecognitionTimer(fakeTimer);
492− edgeDragArea->setTimeSource(fakeTimeSource);
493−
494− // Make sure this property is not disabled
495− edgeDragArea->setMaxSilenceTime(100);
496−
497− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
498− QPointF touchPoint = initialTouchPos;
499−
500− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
501−
502− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
503− QVERIFY(fakeTimer->isRunning());
504−
505− // Force timer to timeout until after maxSilenceTime has been reached
506− while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
507− passTime(fakeTimer->interval());
508− }
509−
510− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
511−}
512−
513/*
291/*
514 Checks that it informs the X coordinate of the touch point in local and scene coordinates
292 Checks that it informs the X coordinate of the touch point in local and scene coordinates
515 correctly.
293 correctly.
@@ -519,18 +297,19 @@ void tst_DirectionalDragArea::sceneXAndX()
519 DirectionalDragArea *edgeDragArea =
297 DirectionalDragArea *edgeDragArea =
520 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
298 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
521 QVERIFY(edgeDragArea != 0);
299 QVERIFY(edgeDragArea != 0);
522− edgeDragArea->setRecognitionTimer(fakeTimer);
300+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
523− edgeDragArea->setTimeSource(fakeTimeSource);
301+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302+ edgeDragArea->setImmediateRecognition(true);
524
303
525 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
304 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
526
305
527− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
306+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
528
307
529 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
308 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
530 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
309 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
531
310
532 touchScenePos.rx() = m_view->width() / 2;
311 touchScenePos.rx() = m_view->width() / 2;
533− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
312+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
534
313
535 QCOMPARE(touchXSpy.count(), 1);
314 QCOMPARE(touchXSpy.count(), 1);
536 QCOMPARE(touchSceneXSpy.count(), 1);
315 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -547,18 +326,19 @@ void tst_DirectionalDragArea::sceneYAndY()
547 DirectionalDragArea *edgeDragArea =
326 DirectionalDragArea *edgeDragArea =
548 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
327 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
549 QVERIFY(edgeDragArea != 0);
328 QVERIFY(edgeDragArea != 0);
550− edgeDragArea->setRecognitionTimer(fakeTimer);
329+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
551− edgeDragArea->setTimeSource(fakeTimeSource);
330+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331+ edgeDragArea->setImmediateRecognition(true);
552
332
553 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
333 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
554
334
555− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
335+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
556
336
557 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
337 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
558 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
338 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
559
339
560 touchScenePos.ry() = m_view->height() / 2;
340 touchScenePos.ry() = m_view->height() / 2;
561− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
341+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
562
342
563 QCOMPARE(touchYSpy.count(), 1);
343 QCOMPARE(touchYSpy.count(), 1);
564 QCOMPARE(touchSceneYSpy.count(), 1);
344 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -577,8 +357,8 @@ void tst_DirectionalDragArea::twoFingerTap()
577 DirectionalDragArea *edgeDragArea =
357 DirectionalDragArea *edgeDragArea =
578 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
358 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
579 QVERIFY(edgeDragArea != 0);
359 QVERIFY(edgeDragArea != 0);
580− edgeDragArea->setRecognitionTimer(fakeTimer);
360+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
581− edgeDragArea->setTimeSource(fakeTimeSource);
361+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
582
362
583 // Make touches evenly spaced along the edgeDragArea
363 // Make touches evenly spaced along the edgeDragArea
584 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
364 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -592,7 +372,7 @@ void tst_DirectionalDragArea::twoFingerTap()
592 QTest::touchEvent(m_view, m_device)
372 QTest::touchEvent(m_view, m_device)
593 .press(0, touchAPos);
373 .press(0, touchAPos);
594
374
595− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
375+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
596
376
597 passTime(timeStepMsecs);
377 passTime(timeStepMsecs);
598 QTest::touchEvent(m_view, m_device)
378 QTest::touchEvent(m_view, m_device)
@@ -601,20 +381,20 @@ void tst_DirectionalDragArea::twoFingerTap()
601
381
602 // A second touch point appeared during recognition, reject immediately as this
382 // A second touch point appeared during recognition, reject immediately as this
603 // can't be a single-touch gesture anymore.
383 // can't be a single-touch gesture anymore.
604− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
384+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
605
385
606 passTime(timeStepMsecs);
386 passTime(timeStepMsecs);
607 QTest::touchEvent(m_view, m_device)
387 QTest::touchEvent(m_view, m_device)
608 .release(0, touchAPos)
388 .release(0, touchAPos)
609 .move(1, touchBPos);
389 .move(1, touchBPos);
610
390
611− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
391+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
612
392
613 passTime(timeStepMsecs);
393 passTime(timeStepMsecs);
614 QTest::touchEvent(m_view, m_device)
394 QTest::touchEvent(m_view, m_device)
615 .release(1, touchBPos);
395 .release(1, touchBPos);
616
396
617− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
397+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
618
398
619 // Perform the second two-finger tap
399 // Perform the second two-finger tap
620
400
@@ -622,27 +402,27 @@ void tst_DirectionalDragArea::twoFingerTap()
622 QTest::touchEvent(m_view, m_device)
402 QTest::touchEvent(m_view, m_device)
623 .press(0, touchAPos);
403 .press(0, touchAPos);
624
404
625− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
405+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
626
406
627 passTime(timeStepMsecs);
407 passTime(timeStepMsecs);
628 QTest::touchEvent(m_view, m_device)
408 QTest::touchEvent(m_view, m_device)
629 .move(0, touchAPos)
409 .move(0, touchAPos)
630 .press(1, touchBPos);
410 .press(1, touchBPos);
631
411
632− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
412+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
633
413
634 passTime(timeStepMsecs);
414 passTime(timeStepMsecs);
635 QTest::touchEvent(m_view, m_device)
415 QTest::touchEvent(m_view, m_device)
636 .release(0, touchAPos)
416 .release(0, touchAPos)
637 .move(1, touchBPos);
417 .move(1, touchBPos);
638
418
639− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
419+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
640
420
641 passTime(timeStepMsecs);
421 passTime(timeStepMsecs);
642 QTest::touchEvent(m_view, m_device)
422 QTest::touchEvent(m_view, m_device)
643 .release(1, touchBPos);
423 .release(1, touchBPos);
644
424
645− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
425+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
646}
426}
647
427
648/*
428/*
@@ -659,25 +439,25 @@ void tst_DirectionalDragArea::movingDDA()
659 DirectionalDragArea *edgeDragArea =
439 DirectionalDragArea *edgeDragArea =
660 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
440 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
661 Q_ASSERT(edgeDragArea != 0);
441 Q_ASSERT(edgeDragArea != 0);
662− edgeDragArea->setRecognitionTimer(fakeTimer);
442+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
663− edgeDragArea->setTimeSource(fakeTimeSource);
443+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
664
444
665 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
445 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
666 QPointF touchPoint = initialTouchPos;
446 QPointF touchPoint = initialTouchPos;
667
447
668− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
448+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
669 QPointF dragDirectionVector(1.0f, 0.0f);
449 QPointF dragDirectionVector(1.0f, 0.0f);
670
450
671− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
451+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
672 QPointF touchMovement = dragDirectionVector * movementStepDistance;
452 QPointF touchMovement = dragDirectionVector * movementStepDistance;
673 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
453 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
674− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
454+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
675
455
676 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
456 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
677
457
678 // Move it far ahead along the direction of the gesture
458 // Move it far ahead along the direction of the gesture
679 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
459 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
680− rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
460+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
681
461
682 for (int i = 0; i < totalMovementSteps; ++i) {
462 for (int i = 0; i < totalMovementSteps; ++i) {
683 touchPoint += touchMovement;
463 touchPoint += touchMovement;
@@ -685,7 +465,7 @@ void tst_DirectionalDragArea::movingDDA()
685 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
465 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
686 }
466 }
687
467
688− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
468+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
689
469
690 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
470 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
691}
471}
@@ -699,8 +479,8 @@ void tst_DirectionalDragArea::ignoreOldFinger()
699 DirectionalDragArea *edgeDragArea =
479 DirectionalDragArea *edgeDragArea =
700 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
480 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
701 Q_ASSERT(edgeDragArea != 0);
481 Q_ASSERT(edgeDragArea != 0);
702− edgeDragArea->setRecognitionTimer(fakeTimer);
482+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
703− edgeDragArea->setTimeSource(fakeTimeSource);
483+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
704
484
705 // Make touches evenly spaced along the edgeDragArea
485 // Make touches evenly spaced along the edgeDragArea
706 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
486 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -708,26 +488,26 @@ void tst_DirectionalDragArea::ignoreOldFinger()
708
488
709 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
489 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
710
490
711− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
491+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
712
492
713 // leave it lying around for some time
493 // leave it lying around for some time
714− passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
494+ passTime(edgeDragArea->d->maxTime * 10);
715
495
716− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
496+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
717
497
718− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
498+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
719 QPointF dragDirectionVector(1.0f, 0.0f);
499 QPointF dragDirectionVector(1.0f, 0.0f);
720
500
721− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
501+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
722 QPointF touchMovement = dragDirectionVector * movementStepDistance;
502 QPointF touchMovement = dragDirectionVector * movementStepDistance;
723 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
503 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
724− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
504+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
725
505
726 QTest::touchEvent(m_view, m_device)
506 QTest::touchEvent(m_view, m_device)
727 .move(0, touch0Pos)
507 .move(0, touch0Pos)
728 .press(1, touch1Pos);
508 .press(1, touch1Pos);
729
509
730− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
510+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731
511
732 for (int i = 0; i < totalMovementSteps; ++i) {
512 for (int i = 0; i < totalMovementSteps; ++i) {
733 touch1Pos += touchMovement.toPoint();
513 touch1Pos += touchMovement.toPoint();
@@ -737,13 +517,13 @@ void tst_DirectionalDragArea::ignoreOldFinger()
737 .move(1, touch1Pos);
517 .move(1, touch1Pos);
738 }
518 }
739
519
740− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
520+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
741
521
742 QTest::touchEvent(m_view, m_device)
522 QTest::touchEvent(m_view, m_device)
743 .move(0, touch0Pos)
523 .move(0, touch0Pos)
744 .release(1, touch1Pos);
524 .release(1, touch1Pos);
745
525
746− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
526+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
747}
527}
748
528
749/*
529/*
@@ -762,19 +542,19 @@ void tst_DirectionalDragArea::rotated()
762 DirectionalDragArea *edgeDragArea =
542 DirectionalDragArea *edgeDragArea =
763 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
543 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
764 Q_ASSERT(edgeDragArea != 0);
544 Q_ASSERT(edgeDragArea != 0);
765− edgeDragArea->setRecognitionTimer(fakeTimer);
545+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
766− edgeDragArea->setTimeSource(fakeTimeSource);
546+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
767
547
768 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
548 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
769 QPointF touchPoint = initialTouchPos;
549 QPointF touchPoint = initialTouchPos;
770
550
771− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
551+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
772 QPointF dragDirectionVector(0.0f, 1.0f);
552 QPointF dragDirectionVector(0.0f, 1.0f);
773
553
774− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
554+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
775 QPointF touchMovement = dragDirectionVector * movementStepDistance;
555 QPointF touchMovement = dragDirectionVector * movementStepDistance;
776 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
556 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
777− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
557+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
778
558
779 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
559 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
780
560
@@ -784,7 +564,7 @@ void tst_DirectionalDragArea::rotated()
784 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
564 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
785 }
565 }
786
566
787− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
567+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
788
568
789 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
569 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790}
570}
@@ -802,31 +582,30 @@ void tst_DirectionalDragArea::sceneDistance()
802 DirectionalDragArea *edgeDragArea =
582 DirectionalDragArea *edgeDragArea =
803 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
583 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
804 Q_ASSERT(edgeDragArea != 0);
584 Q_ASSERT(edgeDragArea != 0);
805− edgeDragArea->setRecognitionTimer(fakeTimer);
585+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
806− edgeDragArea->setTimeSource(fakeTimeSource);
586+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587+
588+ // to disable the position smoothing so that we can more easily check sceneDistance values
589+ edgeDragArea->setImmediateRecognition(true);
807
590
808 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
591 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
809 QPointF touchPoint = initialTouchPos;
592 QPointF touchPoint = initialTouchPos;
810
593
811− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
594+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
812
595
813− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
596+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
814 QPointF touchMovement = dragDirectionVector * movementStepDistance;
597 QPointF touchMovement = dragDirectionVector * movementStepDistance;
815 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
598 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
816− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
599+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
817
600
818− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
601+ qint64 timestamp = 0;
602+
603+ sendTouchPress(timestamp, 0, touchPoint);
819
604
820−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
821− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
822−#endif
823 for (int i = 0; i < totalMovementSteps; ++i) {
605 for (int i = 0; i < totalMovementSteps; ++i) {
824 touchPoint += touchMovement;
606 touchPoint += touchMovement;
825− passTime(movementTimeStepMs);
607+ timestamp += movementTimeStepMs;
826− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
608+ sendTouchUpdate(timestamp, 0, touchPoint);
827−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828− wp->flushDelayedTouchEvent();
829−#endif
830 }
609 }
831
610
832 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
611 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
@@ -836,7 +615,8 @@ void tst_DirectionalDragArea::sceneDistance()
836 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
615 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
837 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
616 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
838
617
839− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
618+ timestamp += movementTimeStepMs;
619+ sendTouchRelease(timestamp, 0, touchPoint);
840}
620}
841
621
842void tst_DirectionalDragArea::sceneDistance_data()
622void tst_DirectionalDragArea::sceneDistance_data()
@@ -860,18 +640,18 @@ void tst_DirectionalDragArea::disabledWhileDragging()
860 DirectionalDragArea *edgeDragArea =
640 DirectionalDragArea *edgeDragArea =
861 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
641 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
862 Q_ASSERT(edgeDragArea != 0);
642 Q_ASSERT(edgeDragArea != 0);
863− edgeDragArea->setRecognitionTimer(fakeTimer);
643+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
864− edgeDragArea->setTimeSource(fakeTimeSource);
644+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
865
645
866 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
646 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
867
647
868− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
648+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
869 QPointF dragDirectionVector(1., 0.); // horizontal positive
649 QPointF dragDirectionVector(1., 0.); // horizontal positive
870
650
871− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
651+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
872 QPointF touchMovement = dragDirectionVector * movementStepDistance;
652 QPointF touchMovement = dragDirectionVector * movementStepDistance;
873 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
653 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
874− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
654+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
875
655
876 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
656 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
877
657
@@ -881,13 +661,13 @@ void tst_DirectionalDragArea::disabledWhileDragging()
881 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
661 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
882 }
662 }
883
663
884− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
664+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
885 QCOMPARE(edgeDragArea->dragging(), true);
665 QCOMPARE(edgeDragArea->dragging(), true);
886
666
887 // disable the dragArea while it's being dragged.
667 // disable the dragArea while it's being dragged.
888 edgeDragArea->setEnabled(false);
668 edgeDragArea->setEnabled(false);
889
669
890− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
670+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
891 QCOMPARE(edgeDragArea->dragging(), false);
671 QCOMPARE(edgeDragArea->dragging(), false);
892
672
893 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
673 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -898,15 +678,14 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
898 DirectionalDragArea *edgeDragArea =
678 DirectionalDragArea *edgeDragArea =
899 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
679 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
900 Q_ASSERT(edgeDragArea != 0);
680 Q_ASSERT(edgeDragArea != 0);
901− edgeDragArea->setRecognitionTimer(fakeTimer);
681+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
902− edgeDragArea->setTimeSource(fakeTimeSource);
682+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
903
683
904 // Disable some constraints we're not interested in
684 // Disable some constraints we're not interested in
905− edgeDragArea->setMaxSilenceTime(60 * 1000);
685+ edgeDragArea->d->setMaxTime(60 * 1000);
906− edgeDragArea->setMinSpeed(0);
907
686
908 // And ensure others have the values we want
687 // And ensure others have the values we want
909− edgeDragArea->setCompositionTime(60);
688+ edgeDragArea->d->compositionTime = 60;
910
689
911 // Put an item right behind edgeDragArea to receive the touches ignored by it
690 // Put an item right behind edgeDragArea to receive the touches ignored by it
912 DummyItem *dummyItem = new DummyItem;
691 DummyItem *dummyItem = new DummyItem;
@@ -923,10 +702,10 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
923
702
924 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
703 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
925
704
926− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
705+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
927
706
928 // We are now going to be way beyond compositionTime
707 // We are now going to be way beyond compositionTime
929− passTime(edgeDragArea->compositionTime()*3);
708+ passTime(edgeDragArea->d->compositionTime*3);
930
709
931 QTest::touchEvent(m_view, m_device)
710 QTest::touchEvent(m_view, m_device)
932 .move(0, touch0Pos)
711 .move(0, touch0Pos)
@@ -948,7 +727,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
948 .move(0, touch0Pos)
727 .move(0, touch0Pos)
949 .move(1, touch1Pos);
728 .move(1, touch1Pos);
950
729
951− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
730+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
952
731
953 passTime(5);
732 passTime(5);
954
733
@@ -956,7 +735,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
956 .release(0, touch0Pos)
735 .release(0, touch0Pos)
957 .move(1, touch1Pos);
736 .move(1, touch1Pos);
958
737
959− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
738+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
960
739
961 passTime(5);
740 passTime(5);
962
741
@@ -964,7 +743,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
964 .release(1, touch1Pos);
743 .release(1, touch1Pos);
965
744
966 // Shouldn't be keepping info about touches that no longer exist or interest us
745 // Shouldn't be keepping info about touches that no longer exist or interest us
967− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
746+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
968
747
969 delete dummyItem;
748 delete dummyItem;
970}
749}
@@ -974,12 +753,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
974 DirectionalDragArea *edgeDragArea =
753 DirectionalDragArea *edgeDragArea =
975 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
754 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
976 Q_ASSERT(edgeDragArea != 0);
755 Q_ASSERT(edgeDragArea != 0);
977− edgeDragArea->setRecognitionTimer(fakeTimer);
756+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
978− edgeDragArea->setTimeSource(fakeTimeSource);
757+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
979
758
980 // Disable some constraints we're not interested in
759 // Disable some constraints we're not interested in
981− edgeDragArea->setMaxSilenceTime(60 * 1000);
760+ edgeDragArea->d->setMaxTime(60 * 1000);
982− edgeDragArea->setMinSpeed(0);
983
761
984 // Put an item right in front of edgeDragArea
762 // Put an item right in front of edgeDragArea
985 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
763 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
@@ -998,11 +776,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
998
776
999 QTest::touchEvent(m_view, m_device).press(0, touchPos);
777 QTest::touchEvent(m_view, m_device).press(0, touchPos);
1000
778
1001− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
779+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1002
780
1003 m_touchRegistry->requestTouchOwnership(0, dummyItem);
781 m_touchRegistry->requestTouchOwnership(0, dummyItem);
1004
782
1005− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
783+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1006
784
1007 dummyItem->grabTouchPoints({0});
785 dummyItem->grabTouchPoints({0});
1008 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
786 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
@@ -1010,9 +788,9 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1010 passTime(5);
788 passTime(5);
1011 QTest::touchEvent(m_view, m_device).release(0, touchPos);
789 QTest::touchEvent(m_view, m_device).release(0, touchPos);
1012
790
1013− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
791+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1014
792
1015− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
793+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1016}
794}
1017
795
1018void tst_DirectionalDragArea::threeFingerDrag()
796void tst_DirectionalDragArea::threeFingerDrag()
@@ -1020,15 +798,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1020 DirectionalDragArea *edgeDragArea =
798 DirectionalDragArea *edgeDragArea =
1021 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
799 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1022 Q_ASSERT(edgeDragArea != 0);
800 Q_ASSERT(edgeDragArea != 0);
1023− edgeDragArea->setRecognitionTimer(fakeTimer);
801+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1024− edgeDragArea->setTimeSource(fakeTimeSource);
802+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1025
803
1026 // Disable some constraints we're not interested in
804 // Disable some constraints we're not interested in
1027− edgeDragArea->setMaxSilenceTime(60 * 1000);
805+ edgeDragArea->d->setMaxTime(60 * 1000);
1028− edgeDragArea->setMinSpeed(0);
1029
806
1030 // And ensure others have the values we want
807 // And ensure others have the values we want
1031− edgeDragArea->setCompositionTime(60);
808+ edgeDragArea->d->compositionTime = 60;
1032
809
1033 // Make touches evenly spaced along the edgeDragArea
810 // Make touches evenly spaced along the edgeDragArea
1034 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
811 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
@@ -1038,14 +815,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1038 QTest::touchEvent(m_view, m_device)
815 QTest::touchEvent(m_view, m_device)
1039 .press(0, touch0Pos);
816 .press(0, touch0Pos);
1040
817
1041− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
818+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1042
819
1043 passTime(5);
820 passTime(5);
1044 QTest::touchEvent(m_view, m_device)
821 QTest::touchEvent(m_view, m_device)
1045 .move(0, touch0Pos)
822 .move(0, touch0Pos)
1046 .press(1, touch1Pos);
823 .press(1, touch1Pos);
1047
824
1048− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
825+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1049
826
1050 passTime(5);
827 passTime(5);
1051 QTest::touchEvent(m_view, m_device)
828 QTest::touchEvent(m_view, m_device)
@@ -1053,7 +830,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1053 .move(1, touch1Pos)
830 .move(1, touch1Pos)
1054 .press(2, touch2Pos);
831 .press(2, touch2Pos);
1055
832
1056− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
833+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1057
834
1058 passTime(10);
835 passTime(10);
1059 QTest::touchEvent(m_view, m_device)
836 QTest::touchEvent(m_view, m_device)
@@ -1077,7 +854,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1077 .release(0, touch0Pos);
854 .release(0, touch0Pos);
1078
855
1079 // Shouldn't be keepping info about touches that no longer exist or interest us
856 // Shouldn't be keepping info about touches that no longer exist or interest us
1080− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
857+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1081}
858}
1082
859
1083/*
860/*
@@ -1090,12 +867,12 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1090 DirectionalDragArea *edgeDragArea =
867 DirectionalDragArea *edgeDragArea =
1091 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
868 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1092 Q_ASSERT(edgeDragArea != 0);
869 Q_ASSERT(edgeDragArea != 0);
1093− edgeDragArea->setRecognitionTimer(fakeTimer);
870+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1094− edgeDragArea->setTimeSource(fakeTimeSource);
871+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1095
872
1096 // Disable the minimum amount of constraints to ensure immediate recognition
873 // Disable the minimum amount of constraints to ensure immediate recognition
1097− edgeDragArea->setDistanceThreshold(0);
874+ edgeDragArea->d->setDistanceThreshold(0);
1098− edgeDragArea->setCompositionTime(0);
875+ edgeDragArea->d->compositionTime = 0;
1099
876
1100 // Put an item right behind edgeDragArea to receive the touches ignored by it
877 // Put an item right behind edgeDragArea to receive the touches ignored by it
1101 DummyItem *dummyItem = new DummyItem;
878 DummyItem *dummyItem = new DummyItem;
@@ -1111,7 +888,7 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1111 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
888 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1112
889
1113 // check for immediate recognition
890 // check for immediate recognition
1114− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
891+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1115
892
1116 // and therefore it should have immediately grabbed the touch point,
893 // and therefore it should have immediately grabbed the touch point,
1117 // not letting it leak to items behind him.
894 // not letting it leak to items behind him.
@@ -1123,19 +900,19 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1123 DirectionalDragArea *edgeDragArea =
900 DirectionalDragArea *edgeDragArea =
1124 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
901 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1125 Q_ASSERT(edgeDragArea != 0);
902 Q_ASSERT(edgeDragArea != 0);
1126− edgeDragArea->setRecognitionTimer(fakeTimer);
903+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1127− edgeDragArea->setTimeSource(fakeTimeSource);
904+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1128
905
1129 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
906 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
1130
907
1131 // Move less than the minimum needed for the drag gesture recognition
908 // Move less than the minimum needed for the drag gesture recognition
1132− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;
909+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
1133 QPointF dragDirectionVector(1., 0.); // horizontal positive
910 QPointF dragDirectionVector(1., 0.); // horizontal positive
1134
911
1135− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
912+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1136 QPointF touchMovement = dragDirectionVector * movementStepDistance;
913 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1137 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
914 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1138− int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;
915+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
1139
916
1140 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
917 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1141
918
@@ -1145,14 +922,14 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1145 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
922 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1146 }
923 }
1147
924
1148− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
925+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1149
926
1150 // edgeDragArea should be an undecided candidate
927 // edgeDragArea should be an undecided candidate
1151 {
928 {
1152 auto touchInfo = m_touchRegistry->findTouchInfo(0);
929 auto touchInfo = m_touchRegistry->findTouchInfo(0);
1153 QCOMPARE(touchInfo->candidates.size(), 1);
930 QCOMPARE(touchInfo->candidates.size(), 1);
1154 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
931 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1155− QCOMPARE(touchInfo->candidates.at(0).undecided, true);
932+ QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
1156 }
933 }
1157
934
1158 // disable the dragArea while it's still recognizing a possible drag gesture.
935 // disable the dragArea while it's still recognizing a possible drag gesture.
@@ -1169,7 +946,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1169 QCOMPARE(touchInfo->candidates.size(), 0);
946 QCOMPARE(touchInfo->candidates.size(), 0);
1170 }
947 }
1171
948
1172− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
949+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1173
950
1174 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
951 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1175}
952}
@@ -1182,47 +959,13 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1182 QTest::newRow("invisible") << false;
959 QTest::newRow("invisible") << false;
1183}
960}
1184
961
1185−void tst_DirectionalDragArea::tappedSignal()
1186−{
1187− DirectionalDragArea *edgeDragArea =
1188− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1189− QVERIFY(edgeDragArea != 0);
1190− edgeDragArea->setRecognitionTimer(fakeTimer);
1191− edgeDragArea->setTimeSource(fakeTimeSource);
1192−
1193− QFETCH(bool, immediateGestureRecognition);
1194− if (immediateGestureRecognition) {
1195− // Disable the minimum amount of constraints to ensure immediate recognition
1196− edgeDragArea->setDistanceThreshold(0);
1197− edgeDragArea->setCompositionTime(0);
1198− }
1199−
1200− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1201−
1202− QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
1203−
1204− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1205− passTime(edgeDragArea->maxTapDuration() / 2);
1206− QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
1207−
1208− QCOMPARE(tappedSpy.count(), 1);
1209−}
1210−
1211−void tst_DirectionalDragArea::tappedSignal_data()
1212−{
1213− QTest::addColumn<bool>("immediateGestureRecognition");
1214−
1215− QTest::newRow("immediate gesture recognition") << true;
1216− QTest::newRow("default gesture recognition") << false;
1217−}
1218−
1219void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
962void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1220{
963{
1221 DirectionalDragArea *edgeDragArea =
964 DirectionalDragArea *edgeDragArea =
1222 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
965 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1223 QVERIFY(edgeDragArea != nullptr);
966 QVERIFY(edgeDragArea != nullptr);
1224− edgeDragArea->setRecognitionTimer(fakeTimer);
967+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1225− edgeDragArea->setTimeSource(fakeTimeSource);
968+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1226
969
1227 QQuickMouseArea *mouseArea =
970 QQuickMouseArea *mouseArea =
1228 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
971 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
@@ -1233,12 +976,12 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1233 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
976 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1234 QPointF touchPoint = initialTouchPos;
977 QPointF touchPoint = initialTouchPos;
1235
978
1236− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
979+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1237 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
980 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1238− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
981+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1239 QPointF touchMovement = dragDirectionVector * movementStepDistance;
982 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1240 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
983 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1241− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
984+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
1242
985
1243 QCOMPARE(mouseArea->pressed(), false);
986 QCOMPARE(mouseArea->pressed(), false);
1244
987
@@ -1259,13 +1002,305 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1259 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1002 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1260 // which should make the MouseArea get a cancelation event, which will then cause it to
1003 // which should make the MouseArea get a cancelation event, which will then cause it to
1261 // reset its state (going back to "unpressed"/"released").
1004 // reset its state (going back to "unpressed"/"released").
1262− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1005+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1263 QCOMPARE(mouseArea->pressed(), false);
1006 QCOMPARE(mouseArea->pressed(), false);
1264 QCOMPARE(mouseAreaSpy.canceledCount, 1);
1007 QCOMPARE(mouseAreaSpy.canceledCount, 1);
1265
1008
1266 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1009 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1267}
1010}
1268
1011
1012+void tst_DirectionalDragArea::interleavedTouches()
1013+{
1014+ DirectionalDragArea *edgeDragArea =
1015+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016+ QVERIFY(edgeDragArea != 0);
1017+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1018+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1019+
1020+ QPointF touch0 = edgeDragArea->mapToScene(
1021+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
1022+
1023+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1024+ QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1025+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1026+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
1027+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1028+ int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
1029+
1030+ QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
1031+ for (int i = 0; i < totalMovementSteps; ++i) {
1032+ touch0 += touchMovement;
1033+ passTime(movementTimeStepMs);
1034+ QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
1035+ }
1036+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1037+
1038+ QPointF touch1 = edgeDragArea->mapToScene(
1039+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
1040+
1041+ QTest::touchEvent(m_view, m_device)
1042+ .move(0, touch0.toPoint())
1043+ .press(1, touch1.toPoint());
1044+
1045+ touch1 += touchMovement;
1046+ passTime(movementTimeStepMs);
1047+ QTest::touchEvent(m_view, m_device)
1048+ .move(0, touch0.toPoint())
1049+ .move(1, touch1.toPoint());
1050+
1051+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1052+
1053+ QTest::touchEvent(m_view, m_device)
1054+ .release(0, touch0.toPoint())
1055+ .move(1, touch1.toPoint());
1056+
1057+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1058+
1059+ touch1 += touchMovement;
1060+ passTime(movementTimeStepMs);
1061+ QTest::touchEvent(m_view, m_device)
1062+ .move(1, touch1.toPoint());
1063+
1064+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1065+
1066+ QPointF touch2 = edgeDragArea->mapToScene(
1067+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
1068+
1069+ passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
1070+ QTest::touchEvent(m_view, m_device)
1071+ .move(1, touch1.toPoint())
1072+ .press(2, touch2.toPoint());
1073+
1074+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1075+ QCOMPARE(edgeDragArea->d->touchId, 2);
1076+
1077+ touch2 += touchMovement;
1078+ passTime(movementTimeStepMs);
1079+ QTest::touchEvent(m_view, m_device)
1080+ .move(1, touch1.toPoint())
1081+ .move(2, touch2.toPoint());
1082+
1083+ touch1 += touchMovement;
1084+ passTime(movementTimeStepMs);
1085+ QTest::touchEvent(m_view, m_device)
1086+ .move(1, touch1.toPoint())
1087+ .move(2, touch2.toPoint());
1088+
1089+ passTime(movementTimeStepMs);
1090+ QTest::touchEvent(m_view, m_device)
1091+ .release(1, touch1.toPoint())
1092+ .move(2, touch2.toPoint());
1093+
1094+ passTime(movementTimeStepMs);
1095+ QTest::touchEvent(m_view, m_device)
1096+ .release(2, touch2.toPoint());
1097+
1098+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1099+}
1100+
1101+/*
1102+ A valid right-edge drag performed on mako
1103+ */
1104+void tst_DirectionalDragArea::makoRightEdgeDrag()
1105+{
1106+ m_view->resize(768, 1280);
1107+ QTest::qWait(300);
1108+
1109+ DirectionalDragArea *edgeDragArea =
1110+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1111+ QVERIFY(edgeDragArea != nullptr);
1112+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1113+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1114+
1115+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1116+
1117+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1118+
1119+ sendTouchPress(319744, 0, QPointF(767.001, 719.719));
1120+ sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
1121+ sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
1122+ sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
1123+ sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
1124+ sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
1125+ sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
1126+ sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
1127+ sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
1128+ sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
1129+ sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
1130+ sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
1131+ sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
1132+ sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
1133+ sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
1134+ sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
1135+ sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
1136+ sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
1137+
1138+ QCOMPARE(statusSpy->recognized(), true);
1139+
1140+ delete statusSpy;
1141+}
1142+
1143+/*
1144+ A vertical, downwards swipe performed on mako near its right edge.
1145+
1146+ The DirectionalDragArea on the right edge must not recognize this
1147+ gesture.
1148+ */
1149+void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
1150+{
1151+ m_view->resize(768, 1280);
1152+ QTest::qWait(300);
1153+
1154+ DirectionalDragArea *edgeDragArea =
1155+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1156+ QVERIFY(edgeDragArea != nullptr);
1157+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1158+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1159+
1160+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1161+
1162+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1163+
1164+ sendTouchPress(12012445, 26, QPointF(767.001,461.82));
1165+ sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
1166+ sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
1167+ sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
1168+ sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
1169+ sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
1170+ sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
1171+ sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
1172+ sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
1173+ sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
1174+ sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
1175+ sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
1176+ sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
1177+ sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
1178+ sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
1179+ sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
1180+ sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
1181+ sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
1182+ sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
1183+ sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
1184+
1185+ QCOMPARE(statusSpy->recognized(), false);
1186+
1187+ delete statusSpy;
1188+}
1189+
1190+/*
1191+ A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
1192+ */
1193+void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
1194+{
1195+ m_view->resize(768, 1280);
1196+ QTest::qWait(300);
1197+
1198+ DirectionalDragArea *edgeDragArea =
1199+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1200+ QVERIFY(edgeDragArea != nullptr);
1201+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1202+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1203+
1204+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1205+
1206+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1207+
1208+ sendTouchPress(4002267, 77, QPointF(0,885.154));
1209+ sendTouchUpdate(4002275, 77, QPointF(0,886.214));
1210+ sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
1211+ sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
1212+ sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
1213+ sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
1214+ sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
1215+ sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
1216+ sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
1217+ sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
1218+ sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
1219+ sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
1220+ sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
1221+ sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
1222+ sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
1223+ sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
1224+ sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
1225+ sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
1226+ sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
1227+ sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
1228+ sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
1229+ sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
1230+ sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
1231+ sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
1232+ sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
1233+ sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
1234+ sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
1235+ sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
1236+ sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
1237+ sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
1238+ sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
1239+ sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
1240+ sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
1241+ sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
1242+ sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
1243+ sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
1244+ sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
1245+ sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
1246+ sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
1247+ sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
1248+ sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
1249+ sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
1250+ sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
1251+
1252+ QCOMPARE(statusSpy->recognized(), true);
1253+
1254+ delete statusSpy;
1255+}
1256+
1257+void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
1258+{
1259+ m_view->resize(768, 1280);
1260+ QTest::qWait(300);
1261+
1262+ DirectionalDragArea *edgeDragArea =
1263+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1264+ QVERIFY(edgeDragArea != nullptr);
1265+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1266+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267+
1268+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269+
1270+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271+
1272+ sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273+ sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274+ sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275+ sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276+ sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277+ sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278+ sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279+ sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280+ sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281+ sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282+ sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283+ sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284+ sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285+ sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286+ sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287+ sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288+ sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289+ sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290+ sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291+ sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292+ sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293+ sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294+ sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295+ sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296+ sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297+ sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298+
1299+ QCOMPARE(statusSpy->recognized(), true);
1300+
1301+ delete statusSpy;
1302+}
1303+
1269QTEST_MAIN(tst_DirectionalDragArea)
1304QTEST_MAIN(tst_DirectionalDragArea)
1270
1305
1271#include "tst_DirectionalDragArea.moc"
1306#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+8−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
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
@@ -16,12 +16,19 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Unity.Test 0.1
19
20
20Rectangle {
21Rectangle {
21 width: units.gu(60)
22 width: units.gu(60)
22 height: units.gu(60)
23 height: units.gu(60)
23 color: "white"
24 color: "white"
24
25
26+ Binding {
27+ target: MouseTouchAdaptor
28+ property: "enabled"
29+ value: true
30+ }
31+
25 MouseArea {
32 MouseArea {
26 id: mouseArea
33 id: mouseArea
27 objectName: "mouseArea"
34 objectName: "mouseArea"
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp+5−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -81,8 +81,7 @@ void tst_TouchDispatcher::sendMouseEventIfTouchIgnored()
81 event->accept();
81 event->accept();
82 };
82 };
83
83
84− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view,
84+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, 12345 /* timestamp */);
85− 12345 /* timestamp */);
86
85
87 QVERIFY(gotTouchEvent);
86 QVERIFY(gotTouchEvent);
88 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
87 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
@@ -128,18 +127,18 @@ void tst_TouchDispatcher::mouseDoubleClick()
128 }
127 }
129 ulong timestamp = 12345;
128 ulong timestamp = 12345;
130
129
131− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
130+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
132
131
133 touchPoints[0].setState(Qt::TouchPointReleased);
132 touchPoints[0].setState(Qt::TouchPointReleased);
134 timestamp += doubleClickInterval / 10;
133 timestamp += doubleClickInterval / 10;
135
134
136− touchDispatcher.dispatch(QEvent::TouchEnd, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
135+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
137
136
138 touchPoints[0].setId(1);
137 touchPoints[0].setId(1);
139 touchPoints[0].setState(Qt::TouchPointPressed);
138 touchPoints[0].setState(Qt::TouchPointPressed);
140 timestamp += timeBetweenClicks;
139 timestamp += timeBetweenClicks;
141
140
142− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
141+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
143
142
144 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
143 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
145}
144}
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+56−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
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
@@ -63,6 +63,7 @@ private Q_SLOTS:
63 void init(); // called right before each and every test function is executed
63 void init(); // called right before each and every test function is executed
64 void cleanup(); // called right after each and every test function is executed
64 void cleanup(); // called right after each and every test function is executed
65
65
66+private:
66 void onlyCandidate_passThrough();
67 void onlyCandidate_passThrough();
67 void holdsEventsUntilGainsOwnership();
68 void holdsEventsUntilGainsOwnership();
68 void holdsEventsUntilGainsOwnership_data();
69 void holdsEventsUntilGainsOwnership_data();
@@ -70,6 +71,9 @@ private Q_SLOTS:
70 void dontDispacthToDisabledOrInvisibleTarget();
71 void dontDispacthToDisabledOrInvisibleTarget();
71 void dontDispacthToDisabledOrInvisibleTarget_data();
72 void dontDispacthToDisabledOrInvisibleTarget_data();
72
73
74+private Q_SLOTS:
75+ void disabledWhileHoldingTouch();
76+
73private:
77private:
74 QQuickView *createView();
78 QQuickView *createView();
75 TouchRegistry *touchRegistry;
79 TouchRegistry *touchRegistry;
@@ -326,6 +330,57 @@ void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
326 QTest::newRow("disabled invisible") << false << false;
330 QTest::newRow("disabled invisible") << false << false;
327}
331}
328
332
333+void tst_TouchGate::disabledWhileHoldingTouch()
334+{
335+ TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
336+ Q_ASSERT(touchGate);
337+
338+ TestItem *testItem = new TestItem;
339+ testItem->setWidth(touchGate->width());
340+ testItem->setHeight(touchGate->height());
341+ testItem->setParentItem(view->rootObject());
342+ testItem->setZ(0.0);
343+
344+ touchGate->setZ(1.0);
345+ touchGate->setTargetItem(testItem);
346+
347+ QTest::touchEvent(view, device)
348+ .press(0, QPoint(100,100));
349+
350+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
351+ testItem->touchEventsReceived.clear();
352+
353+ touchGate->setEnabled(false);
354+
355+ QTest::touchEvent(view, device)
356+ .move(0, QPoint(101,101));
357+
358+ // Nothing new came as TouchGate didn't even get it because it's disabled
359+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
360+
361+ touchGate->setEnabled(true);
362+
363+ QTest::touchEvent(view, device)
364+ .move(0, QPoint(102,102));
365+
366+ QTest::touchEvent(view, device)
367+ .release(0, QPoint(102,102));
368+
369+ // Nothing new came because TouchGate has already discarded touch 0
370+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
371+
372+ QTest::touchEvent(view, device)
373+ .press(1, QPoint(200,200));
374+
375+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
376+ {
377+ // it got only the new touch point.
378+ QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
379+ QCOMPARE(touchEvent->touchPoints().count(), 1);
380+ QCOMPARE(touchEvent->touchPoints()[0].id(), 1);
381+ }
382+}
383+
329///////////// TestItem /////////////////////////////////////////////////////////////
384///////////// TestItem /////////////////////////////////////////////////////////////
330
385
331void TestItem::touchEvent(QTouchEvent *event)
386void TestItem::touchEvent(QTouchEvent *event)
tests/qmltests/Components/CMakeLists.txt+3−1
@@ -5,6 +5,7 @@ include_directories(
5 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
5 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
6 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
7 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
8+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
8 )
9 )
9
10
10add_executable(DragHandleTestExec tst_DragHandle.cpp)
11add_executable(DragHandleTestExec tst_DragHandle.cpp)
@@ -12,7 +13,8 @@ qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
12target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
13target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
13
14
14add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
15add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
15−add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
16+add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/utils/modules)
16
17
17add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
18add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
18add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
19add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
20+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
tests/qmltests/Components/tst_DragHandle.cpp+15−13
@@ -20,21 +20,16 @@
20#include <qpa/qwindowsysteminterface.h>
20#include <qpa/qwindowsysteminterface.h>
21#include <QtQuick/QQuickView>
21#include <QtQuick/QQuickView>
22#include <QtQml/QQmlEngine>
22#include <QtQml/QQmlEngine>
23+#include <private/qquickwindow_p.h>
23
24
25+#include <AxisVelocityCalculator.h>
24#include <DirectionalDragArea.h>
26#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
25#include <Timer.h>
28#include <Timer.h>
26#include <TouchRegistry.h>
29#include <TouchRegistry.h>
27
30
28using namespace UbuntuGestures;
31using namespace UbuntuGestures;
29
32
30−class FakeTimeSource : public TimeSource
31−{
32−public:
33− FakeTimeSource() { m_msecsSinceReference = 0; }
34− qint64 msecsSinceReference() override {return m_msecsSinceReference;}
35− qint64 m_msecsSinceReference;
36−};
37−
38class tst_DragHandle: public QObject
33class tst_DragHandle: public QObject
39{
34{
40 Q_OBJECT
35 Q_OBJECT
@@ -56,7 +51,6 @@ private Q_SLOTS:
56 void hintingAnimation();
51 void hintingAnimation();
57 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
52 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
58
53
59−
60private:
54private:
61 void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
55 void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
62 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
56 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
@@ -105,8 +99,8 @@ void tst_DragHandle::init()
105 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
99 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
106 controls->setVisible(false);
100 controls->setVisible(false);
107
101
108− m_fakeTimer = new FakeTimer;
109 m_fakeTimeSource.reset(new FakeTimeSource);
102 m_fakeTimeSource.reset(new FakeTimeSource);
103+ m_fakeTimer = new FakeTimer(m_fakeTimeSource);
110}
104}
111
105
112void tst_DragHandle::cleanup()
106void tst_DragHandle::cleanup()
@@ -129,6 +123,7 @@ QQuickView *tst_DragHandle::createView()
129 window->setResizeMode(QQuickView::SizeRootObjectToView);
123 window->setResizeMode(QQuickView::SizeRootObjectToView);
130 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
124 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
131 window->engine()->addImportPath(QLatin1String(TEST_DIR));
125 window->engine()->addImportPath(QLatin1String(TEST_DIR));
126+ window->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
132
127
133 return window;
128 return window;
134}
129}
@@ -190,17 +185,24 @@ void tst_DragHandle::flickAndHold(DirectionalDragArea *dragHandle,
190 // speed.
185 // speed.
191 m_fakeTimeSource->m_msecsSinceReference += 5000;
186 m_fakeTimeSource->m_msecsSinceReference += 5000;
192 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
187 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
188+
189+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
190+ windowPrivate->flushDelayedTouchEvent();
193}
191}
194
192
195void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
193void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
196 int numSteps, qint64 timeMs)
194 int numSteps, qint64 timeMs)
197{
195{
196+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
197+
198 qint64 timeStep = timeMs / numSteps;
198 qint64 timeStep = timeMs / numSteps;
199 QPointF touchMovement = direction * (distance / (qreal)numSteps);
199 QPointF touchMovement = direction * (distance / (qreal)numSteps);
200 for (int i = 0; i < numSteps; ++i) {
200 for (int i = 0; i < numSteps; ++i) {
201 touchPoint += touchMovement;
201 touchPoint += touchMovement;
202 m_fakeTimeSource->m_msecsSinceReference += timeStep;
202 m_fakeTimeSource->m_msecsSinceReference += timeStep;
203 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
203 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
204+
205+ windowPrivate->flushDelayedTouchEvent();
204 }
206 }
205}
207}
206
208
@@ -209,8 +211,8 @@ DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectN
209 DirectionalDragArea *dragHandle =
211 DirectionalDragArea *dragHandle =
210 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
212 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
211 Q_ASSERT(dragHandle != 0);
213 Q_ASSERT(dragHandle != 0);
212− dragHandle->setRecognitionTimer(m_fakeTimer);
214+ dragHandle->d->setRecognitionTimer(m_fakeTimer);
213− dragHandle->setTimeSource(m_fakeTimeSource);
215+ dragHandle->d->setTimeSource(m_fakeTimeSource);
214
216
215 AxisVelocityCalculator *edgeDragEvaluator =
217 AxisVelocityCalculator *edgeDragEvaluator =
216 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
218 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
@@ -242,6 +244,7 @@ void tst_DragHandle::dragThreshold_horizontal()
242 baseItem->setRotation(rotation);
244 baseItem->setRotation(rotation);
243
245
244 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
246 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
247+ QQuickItem *parentItem = dragHandle->parentItem();
245
248
246 qreal dragThreshold = fetchDragThreshold(dragHandle);
249 qreal dragThreshold = fetchDragThreshold(dragHandle);
247
250
@@ -249,7 +252,6 @@ void tst_DragHandle::dragThreshold_horizontal()
249 flickAndHold(dragHandle, dragThreshold * 0.7);
252 flickAndHold(dragHandle, dragThreshold * 0.7);
250
253
251 // should rollback
254 // should rollback
252− QQuickItem *parentItem = dragHandle->parentItem();
253 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
255 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
254 QCOMPARE(parentItem->property("shown").toBool(), false);
256 QCOMPARE(parentItem->property("shown").toBool(), false);
255
257
tests/qmltests/Components/tst_DragHandle.qml+7
@@ -19,6 +19,7 @@ import "../../../qml/Components"
19import "tst_DragHandle"
19import "tst_DragHandle"
20import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
21import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
22+import Unity.Test 0.1
22
23
23/*
24/*
24 Two blue fullscreen Showables (a vertical and a horizontal one) with red handles you
25 Two blue fullscreen Showables (a vertical and a horizontal one) with red handles you
@@ -33,6 +34,12 @@ Rectangle {
33 width: units.gu(70)
34 width: units.gu(70)
34 height: units.gu(70)
35 height: units.gu(70)
35
36
37+ Binding {
38+ target: MouseTouchAdaptor
39+ property: "enabled"
40+ value: true
41+ }
42+
36 property var dragHandle
43 property var dragHandle
37
44
38 property bool stretch: false
45 property bool stretch: false
tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml+2−2
@@ -60,8 +60,8 @@ Item {
60 maxTotalDragDistance: root.parent.width
60 maxTotalDragDistance: root.parent.width
61 autoCompleteDragThreshold: parent.width / 3
61 autoCompleteDragThreshold: parent.width / 3
62
62
63− onStatusChanged: {
63+ onDraggingChanged: {
64− if (status === DirectionalDragArea.Recognized) {
64+ if (dragging) {
65 dragHandleRecognizedGesture(dragHandle);
65 dragHandleRecognizedGesture(dragHandle);
66 }
66 }
67 }
67 }
tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+4−4
@@ -65,8 +65,8 @@ Showable {
65 maxTotalDragDistance: showable.parent.width
65 maxTotalDragDistance: showable.parent.width
66 hintDisplacement: showable.hintDisplacement
66 hintDisplacement: showable.hintDisplacement
67
67
68− onStatusChanged: {
68+ onDraggingChanged: {
69− if (status === DirectionalDragArea.Recognized) {
69+ if (dragging) {
70 dragHandleRecognizedGesture(leftwardsDragHandle);
70 dragHandleRecognizedGesture(leftwardsDragHandle);
71 }
71 }
72 }
72 }
@@ -88,8 +88,8 @@ Showable {
88 maxTotalDragDistance: showable.parent.width
88 maxTotalDragDistance: showable.parent.width
89 hintDisplacement: showable.hintDisplacement
89 hintDisplacement: showable.hintDisplacement
90
90
91− onStatusChanged: {
91+ onDraggingChanged: {
92− if (status === DirectionalDragArea.Recognized) {
92+ if (dragging) {
93 dragHandleRecognizedGesture(rightwardsDragHandle);
93 dragHandleRecognizedGesture(rightwardsDragHandle);
94 }
94 }
95 }
95 }
tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+4−4
@@ -65,8 +65,8 @@ Showable {
65 maxTotalDragDistance: showable.parent.height
65 maxTotalDragDistance: showable.parent.height
66 hintDisplacement: showable.hintDisplacement
66 hintDisplacement: showable.hintDisplacement
67
67
68− onStatusChanged: {
68+ onDraggingChanged: {
69− if (status === DirectionalDragArea.Recognized) {
69+ if (dragging) {
70 dragHandleRecognizedGesture(upwardsDragHandle);
70 dragHandleRecognizedGesture(upwardsDragHandle);
71 }
71 }
72 }
72 }
@@ -88,8 +88,8 @@ Showable {
88 maxTotalDragDistance: showable.parent.height
88 maxTotalDragDistance: showable.parent.height
89 hintDisplacement: showable.hintDisplacement
89 hintDisplacement: showable.hintDisplacement
90
90
91− onStatusChanged: {
91+ onDraggingChanged: {
92− if (status === DirectionalDragArea.Recognized) {
92+ if (dragging) {
93 dragHandleRecognizedGesture(downwardsDragHandle);
93 dragHandleRecognizedGesture(downwardsDragHandle);
94 }
94 }
95 }
95 }
tests/qmltests/Stages/tst_PhoneStage.qml+8−8
@@ -164,14 +164,14 @@ Item {
164
164
165 function test_enterSpread_data() {
165 function test_enterSpread_data() {
166 return [
166 return [
167− {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
167+ {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
168− {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
168+ {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
169− {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
169+ {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +5, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
170− {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
170+ {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
171− {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
171+ {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
172− {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
172+ {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +5, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
173− {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
173+ {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: 0, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
174− {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
174+ {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +5, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
175 ];
175 ];
176 }
176 }
177
177
tests/qmltests/Tutorial/tst_Tutorial.qml+5−1
@@ -226,7 +226,11 @@ Item {
226 checkLeftEdge();
226 checkLeftEdge();
227 checkBottomEdge();
227 checkBottomEdge();
228 if (name === "tutorialRight") return page;
228 if (name === "tutorialRight") return page;
229+ touchFlick(shell,
229− touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
230+ shell.width, halfHeight,
231+ halfWidth, halfHeight,
232+ true /* beginTouch */, true /* endTouch */,
233+ 20 /* speed */, 50 /* iterations */);
230 var overlay = findChild(page, "overlay");
234 var overlay = findChild(page, "overlay");
231 tryCompare(overlay, "shown", true);
235 tryCompare(overlay, "shown", true);
232 var tick = findChild(page, "tick");
236 var tick = findChild(page, "tick");
tests/utils/modules/Unity/Test/UnityTestCase.qml+2−5
@@ -403,11 +403,8 @@ TestCase {
403 function removeTimeConstraintsFromDirectionalDragAreas(item) {
403 function removeTimeConstraintsFromDirectionalDragAreas(item) {
404
404
405 // use duck-typing to identify a DirectionalDragArea
405 // use duck-typing to identify a DirectionalDragArea
406− if (item.minSpeed != undefined
407− && item.maxSilenceTime != undefined
408− && item.compositionTime != undefined) {
406+ if (item.maxTime != undefined && item.compositionTime != undefined) {
409− item.minSpeed = 0;
407+ item.maxTime = 60 * 60 * 1000;
410− item.maxSilenceTime = 60 * 60 * 1000;
411 item.compositionTime = 0;
408 item.compositionTime = 0;
412 } else {
409 } else {
413 for (var i in item.children) {
410 for (var i in item.children) {