Merge lp:~dandrader/unity8/ddaImprovements into lp:unity8

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/unity8/ddaImprovements
Merge into: lp:unity8
Diff against target: 5424 lines (+1721/-1505)
47 files modified
libs/UbuntuGestures/CMakeLists.txt (+1/-0)
libs/UbuntuGestures/CandidateInactivityTimer.h (+1/-1)
libs/UbuntuGestures/TimeSource.h (+14/-4)
libs/UbuntuGestures/Timer.cpp (+67/-14)
libs/UbuntuGestures/Timer.h (+15/-3)
libs/UbuntuGestures/TouchRegistry.cpp (+12/-14)
libs/UbuntuGestures/TouchRegistry.h (+18/-4)
plugins/Ubuntu/Gestures/CMakeLists.txt (+1/-1)
plugins/Ubuntu/Gestures/Damper.cpp (+24/-0)
plugins/Ubuntu/Gestures/Damper.h (+2/-0)
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+349/-363)
plugins/Ubuntu/Gestures/DirectionalDragArea.h (+19/-208)
plugins/Ubuntu/Gestures/DirectionalDragArea_p.h (+167/-0)
plugins/Ubuntu/Gestures/TouchDispatcher.cpp (+88/-53)
plugins/Ubuntu/Gestures/TouchDispatcher.h (+16/-9)
plugins/Ubuntu/Gestures/TouchGate.cpp (+87/-63)
plugins/Ubuntu/Gestures/TouchGate.h (+18/-5)
qml/Components/DragHandle.qml (+20/-35)
qml/Components/EdgeDragArea.qml (+0/-46)
qml/Dash/Dash.qml (+5/-12)
qml/Greeter/CoverPage.qml (+1/-0)
qml/Launcher/Launcher.qml (+6/-14)
qml/Panel/IndicatorsMenu.qml (+12/-2)
qml/Stages/PhoneStage.qml (+33/-43)
qml/Stages/TabletStage.qml (+19/-21)
qml/Tutorial/TutorialBottom.qml (+1/-1)
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp (+68/-2)
tests/plugins/Ubuntu/Gestures/CMakeLists.txt (+3/-1)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml (+5/-19)
tests/plugins/Ubuntu/Gestures/GestureTest.cpp (+13/-3)
tests/plugins/Ubuntu/Gestures/GestureTest.h (+4/-0)
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml (+6/-19)
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml (+5/-19)
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml (+6/-19)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+496/-461)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml (+8/-1)
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp (+5/-6)
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp (+56/-1)
tests/qmltests/Components/CMakeLists.txt (+3/-1)
tests/qmltests/Components/tst_DragHandle.cpp (+15/-13)
tests/qmltests/Components/tst_DragHandle.qml (+7/-0)
tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml (+2/-2)
tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml (+4/-4)
tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml (+4/-4)
tests/qmltests/Stages/tst_PhoneStage.qml (+8/-8)
tests/qmltests/Tutorial/tst_Tutorial.qml (+5/-1)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+2/-5)
To merge this branch: bzr merge lp:~dandrader/unity8/ddaImprovements
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Albert Astals Cid (community) Approve
Michał Sawicz Abstain
Review via email: mp+254964@code.launchpad.net

This proposal has been superseded by a proposal from 2015-04-10.

Commit message

Several improvements to Ubuntu.Gestures

-Simplified DirectionalDragArea gesture recognition
 - The widening angle property was dropped. Didn't really work
   as in real life swipes might start up in very different directions
   before finally turning to the final overall direction. That's specially
   bad when you have to conclude the recognition in a short amount
   of time so that you can still play animations to follow the user's finger
 - minSpeed as well. Went for a simpler way of expressing and evaluating it
   which is having maxTime and maxDistance.

-EdgeDragArea
 - Fine-tuned parameters and made them based on physical size.
 - Added tests with input from real gestures to catch those annoying
   false-negatives and false-positives introduced by badly set recognition parameters

-Improved debug output

-UnownedTouchEvents are no longer sent to interim owners

-Fixed issue when TouchGate got disabled while holding an active touch

Description of the change

Need for fixing bug 1417920.
The way parameters are currently set in EdgeDragArea, a gesture gets recognized regardless of its direction, it only have to be dragged far enough.

* Are there any related MPs required for this MP to build/function as expected? Please list.
No.
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
* Did you make sure that your branch does not contain spurious tags?
Yes.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.
* If you changed the UI, has there been a design review?
Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1705. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Michał Sawicz (saviq) wrote :

Failed QML tests:
    qmltestrunner.Launcher::test_clickFlick
    qmltestrunner.PhoneStage::test_shortFlick
    qmltestrunner.ShellWithPin::test_shortLeftEdgeSwipeMakesLauncherStayVisible

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Failed QML tests:
> qmltestrunner.Launcher::test_clickFlick
> qmltestrunner.PhoneStage::test_shortFlick
> qmltestrunner.ShellWithPin::test_shortLeftEdgeSwipeMakesLauncherStayVisible

Fixed.

Revision history for this message
Michał Sawicz (saviq) :
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1706. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1707. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1708. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1709. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Albert Astals Cid (aacid) wrote :

This does not actually fix the linked bug right?

Any way to test this change?

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> This does not actually fix the linked bug right?
>
> Any way to test this change?

It does, but you have to make Launcher's EdgeDragArea cover the whole launcher in order to notice it.

=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-07 12:19:02 +0000
@@ -280,7 +280,7 @@ Item {

         enabled: root.available
         x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
- width: root.dragAreaWidth
+ width: root.panelWidth
         height: root.height

         onTouchXChanged: {

Apply diff the diff above to trunk and try to scroll the launcher icons. It won't work.

Then merge this branch and try again. It will work.

And check that all DDAs still work normally
  - show and hide indicators panel
  - right-edge drag to show the spread
  - left-edge drag to show launcher
  - horizontal drag to dismiss the greeter
  - bottom edge drag to show the "manage dash" UI.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Can you confirm that doing http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/2103/artifact/work/output/*zip*/output.zip + width: root.panelWidth in /usr/share/unity8/Launcher/Launcher.qml in the phone it works for you?

I just tried and it still closes when i swipe vertically over an open Launcher.

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 07/04/15 11:08, Albert Astals Cid wrote:
> Review: Needs Information
>
> Can you confirm that doing http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/2103/artifact/work/output/*zip*/output.zip + width: root.panelWidth in /usr/share/unity8/Launcher/Launcher.qml in the phone it works for you?
>
> I just tried and it still closes when i swipe vertically over an open Launcher.

Sorry, this is the full patch:

"""
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-07 15:13:15 +0000
@@ -280,7 +280,7 @@ Item {

         enabled: root.available
         x: -root.x // so if launcher is adjusted relative to screen, we
stay put (like tutorial does when teasing)
- width: root.dragAreaWidth
+ width: root.panelWidth
         height: root.height

         onTouchXChanged: {
@@ -306,8 +306,9 @@ Item {
                     if (distance > minimizeDistance) {
                         root.dash();
                     }
- } else {
- root.switchToNextState("")
+ } else if (root.state === "") {
+ // didn't drag far enough. rollback
+ root.switchToNextState("");
                 }
             }
         }
"""

And you don't have to do it on the phone. Doing it in "make tryShell" is
enough. Apply this patch in trunk and you won't be able to scroll the
launcher as the DDA in front of it is recognizing even those vertical
swiped. Apply it on top of ddaImprovements branch and you will see you
can scroll launcher icons just fine as the DDA rejects vertical drags.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Hmmm, the code looks ok, can't see anything wrong, but i think some parameter changes need some tweaking, with the old code i can show the launcher with a small drag and it'll show the margin of the launcher and very few of the icons, with this i need to do a bigger drag and it'll in consequence show more part of the launcher.

On the other hand not sure this is problematic either. Can you try it yourself and see what you think?

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 08/04/15 08:18, Albert Astals Cid wrote:
> Review: Needs Information
>
> Hmmm, the code looks ok, can't see anything wrong, but i think some parameter changes need some tweaking, with the old code i can show the launcher with a small drag and it'll show the margin of the launcher and very few of the icons, with this i need to do a bigger drag and it'll in consequence show more part of the launcher.
>
> On the other hand not sure this is problematic either. Can you try it yourself and see what you think?
I get what you mean. But this is not relevant as you only do those very
short drags when you're testing the edge drag feature, ie when you're
checking how much drag it needs to kick in etc. In real usage no one
would do a very short edge drag expose the launcher just a little bit. :)

In any case I reduced the distance threshold a bit so that the gesture
kicks in a bit sooner, if that makes you feel better. :) But it does
help smoothing out the very beginning of the right edge drag (to show
the spread) as it is not animated like the left edge one. It just jumps
to the current drag position instead of animating towards it. Which
makes the larger drag threshold noticeable. But that's an issue with the
"show the spread with right-edge gesture" qml code, not with DDA per se.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Approving since code looks good and the behaviour change is acceptable to me but i'd like someone else to give it a try on the phone before top approving.

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

 * Did CI run pass?
known autopilot fixed elsewhere

 * Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

right-edge animation start is now smooth as silk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Broken tests again:
    qmltestrunner.PhoneStage::test_enterSpread
    qmltestrunner.Tutorial::test_bottomShortDrag
    qmltestrunner.Tutorial::test_walkthrough

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 09/04/15 15:35, Michał Sawicz wrote:
> Broken tests again:
> qmltestrunner.PhoneStage::test_enterSpread
> qmltestrunner.Tutorial::test_bottomShortDrag
> qmltestrunner.Tutorial::test_walkthrough
Fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1710. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> right-edge animation start is now smooth as silk

If this would just be unity8, I'd be totally happy with it (Except that you're missing TabletStage.qml). But given that the DDA should be an upstream component, I'm hesitant to make all the app developers do that magic as you added in the Phone stage. They won't, and as a result we'll have the jumpiness every time an app uses the DDA.

lp:~dandrader/unity8/ddaImprovements updated
1711. By Daniel d'Andrada

DirectionalDragArea & friends: various improvements & API grooming

-Simplified DirectionalDragArea gesture recognition
 * The widening angle property was dropped. Didn't really work
   as in real life swipes might start up in very different directions
   before finally turning to the final overall direction. That's specially
   bad when you have to conclude the recognition in a short amount
   of time so that you can still play animations to follow the user's finger
 * minSpeed as well. Went for a simpler way of expressing and evaluating it
   which is having maxTime and maxDistance.

-EdgeDragArea got absorbed by DirectionalDragArea

-Privatized all gesture recognition parameters.

-Privatized DirectionalDragArea.status property. All apps need is
 DirectionalDragArea.dragging. Modified qml code using DDA accordingly.

-Modified DirectionalDragArea.dragging semantics a bit. It goes to true only
 once a gesture is recognized

-Moved code that smooths out touch movement into DirectionalDragArea so all users
 get it for free.

-Cleaned up DirectionalDragArea.h of all implementation details, getting it ready
 to be moved out of unity8, into the SDK.

-Removed DirectionalDragArea.tapped() signal as it doesn't belong to it.

-Fine-tuned gesture recognition parameters and made them based on physical size.

-Added tests with input from real gestures to catch those annoying
  false-negatives and false-positives introduced by badly set recognition parameters

-Improved debug output

-UnownedTouchEvents are no longer sent to interim owners

-Fixed issue when TouchGate got disabled while holding an active touch

-Made "make tryDirectionalDragArea" and "make tryDragHandle" work with mouse again.

1712. By Daniel d'Andrada

Fix whitespace issues

1713. By Daniel d'Andrada

Fix UnityTestCase.removeTimeConstraintsFromDirectionalDragAreas()

1714. By Daniel d'Andrada

Fix tst_Shell::test_leftEdgeDragFullscreen

1715. By Daniel d'Andrada

fix shell tests

1716. By Daniel d'Andrada

Added DirectionalDragArea.pressed property

1717. By Daniel d'Andrada

Fix tst_ShellWithPin

1718. By Daniel d'Andrada

Tweak launcher animation

Unmerged revisions

1710. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libs/UbuntuGestures/CMakeLists.txt'
2--- libs/UbuntuGestures/CMakeLists.txt 2014-10-01 13:20:32 +0000
3+++ libs/UbuntuGestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
4@@ -5,6 +5,7 @@
5 CandidateInactivityTimer.cpp
6 DebugHelpers.cpp
7 Timer.cpp
8+ TimeSource.cpp
9 TouchOwnershipEvent.cpp
10 TouchRegistry.cpp
11 UnownedTouchEvent.cpp
12
13=== modified file 'libs/UbuntuGestures/CandidateInactivityTimer.h'
14--- libs/UbuntuGestures/CandidateInactivityTimer.h 2014-10-01 13:20:32 +0000
15+++ libs/UbuntuGestures/CandidateInactivityTimer.h 2015-04-10 21:17:56 +0000
16@@ -32,7 +32,7 @@
17 AbstractTimerFactory &timerFactory,
18 QObject *parent = nullptr);
19
20- const int durationMs = 350;
21+ const int durationMs = 1000;
22
23 Q_SIGNALS:
24 void candidateDefaulted(int touchId, QQuickItem *candidate);
25
26=== renamed file 'plugins/Ubuntu/Gestures/TimeSource.cpp' => 'libs/UbuntuGestures/TimeSource.cpp'
27=== renamed file 'plugins/Ubuntu/Gestures/TimeSource.h' => 'libs/UbuntuGestures/TimeSource.h'
28--- plugins/Ubuntu/Gestures/TimeSource.h 2014-10-01 13:20:32 +0000
29+++ libs/UbuntuGestures/TimeSource.h 2015-04-10 21:17:56 +0000
30@@ -1,5 +1,5 @@
31 /*
32- * Copyright (C) 2013 - Canonical Ltd.
33+ * Copyright (C) 2013,2015 Canonical Ltd.
34 *
35 * This program is free software: you can redistribute it and/or modify it
36 * under the terms of the GNU Lesser General Public License, as
37@@ -21,14 +21,14 @@
38 #ifndef UBUNTUGESTURES_TIMESOURCE_H
39 #define UBUNTUGESTURES_TIMESOURCE_H
40
41-#include "UbuntuGesturesQmlGlobal.h"
42+#include "UbuntuGesturesGlobal.h"
43 #include <QSharedPointer>
44
45 namespace UbuntuGestures {
46 /*
47 Interface for a time source.
48 */
49-class UBUNTUGESTURESQML_EXPORT TimeSource {
50+class UBUNTUGESTURES_EXPORT TimeSource {
51 public:
52 virtual ~TimeSource() {}
53 /* Returns the current time in milliseconds since some reference time in the past. */
54@@ -40,7 +40,7 @@
55 Implementation of a time source
56 */
57 class RealTimeSourcePrivate;
58-class RealTimeSource : public TimeSource {
59+class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
60 public:
61 RealTimeSource();
62 virtual ~RealTimeSource();
63@@ -49,6 +49,16 @@
64 RealTimeSourcePrivate *d;
65 };
66
67+/*
68+ A fake time source, useful for tests
69+ */
70+class FakeTimeSource : public TimeSource {
71+public:
72+ FakeTimeSource() { m_msecsSinceReference = 0; }
73+ qint64 msecsSinceReference() override { return m_msecsSinceReference; }
74+ qint64 m_msecsSinceReference;
75+};
76+
77 } // namespace UbuntuGestures
78
79 #endif // UBUNTUGESTURES_TIMESOURCE_H
80
81=== modified file 'libs/UbuntuGestures/Timer.cpp'
82--- libs/UbuntuGestures/Timer.cpp 2014-10-01 13:20:32 +0000
83+++ libs/UbuntuGestures/Timer.cpp 2015-04-10 21:17:56 +0000
84@@ -1,5 +1,5 @@
85 /*
86- * Copyright (C) 2014 Canonical, Ltd.
87+ * Copyright (C) 2014-2015 Canonical, Ltd.
88 *
89 * This program is free software; you can redistribute it and/or modify
90 * it under the terms of the GNU General Public License as published by
91@@ -58,11 +58,34 @@
92
93 /////////////////////////////////// FakeTimer //////////////////////////////////
94
95-FakeTimer::FakeTimer(QObject *parent)
96+FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
97 : UbuntuGestures::AbstractTimer(parent)
98 , m_interval(0)
99 , m_singleShot(false)
100-{
101+ , m_timeSource(timeSource)
102+{
103+}
104+
105+void FakeTimer::update()
106+{
107+ if (!isRunning()) {
108+ return;
109+ }
110+
111+ if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
112+ if (isSingleShot()) {
113+ stop();
114+ } else {
115+ m_nextTimeoutTime += interval();
116+ }
117+ Q_EMIT timeout();
118+ }
119+}
120+
121+void FakeTimer::start()
122+{
123+ AbstractTimer::start();
124+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
125 }
126
127 int FakeTimer::interval() const
128@@ -87,23 +110,53 @@
129
130 /////////////////////////////////// FakeTimerFactory //////////////////////////////////
131
132+FakeTimerFactory::FakeTimerFactory()
133+{
134+ m_timeSource.reset(new FakeTimeSource);
135+}
136+
137+FakeTimerFactory::~FakeTimerFactory()
138+{
139+ for (int i = 0; i < timers.count(); ++i) {
140+ FakeTimer *timer = timers[i].data();
141+ if (timer) {
142+ delete timer;
143+ }
144+ }
145+}
146+
147+void FakeTimerFactory::updateTime(qint64 targetTime)
148+{
149+ qint64 minTimeoutTime = targetTime;
150+
151+ for (int i = 0; i < timers.count(); ++i) {
152+ FakeTimer *timer = timers[i].data();
153+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
154+ minTimeoutTime = timer->nextTimeoutTime();
155+ }
156+ }
157+
158+ m_timeSource->m_msecsSinceReference = minTimeoutTime;
159+
160+ for (int i = 0; i < timers.count(); ++i) {
161+ FakeTimer *timer = timers[i].data();
162+ if (timer) {
163+ timer->update();
164+ }
165+ }
166+
167+ if (m_timeSource->msecsSinceReference() < targetTime) {
168+ updateTime(targetTime);
169+ }
170+}
171+
172 AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
173 {
174- FakeTimer *fakeTimer = new FakeTimer(parent);
175+ FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
176
177 timers.append(fakeTimer);
178
179 return fakeTimer;
180 }
181
182-void FakeTimerFactory::makeRunningTimersTimeout()
183-{
184- for (int i = 0; i < timers.count(); ++i) {
185- FakeTimer *timer = timers[i].data();
186- if (timer && timer->isRunning()) {
187- timer->emitTimeout();
188- }
189- }
190-}
191-
192 } // namespace UbuntuGestures
193
194=== modified file 'libs/UbuntuGestures/Timer.h'
195--- libs/UbuntuGestures/Timer.h 2014-10-01 13:20:32 +0000
196+++ libs/UbuntuGestures/Timer.h 2015-04-10 21:17:56 +0000
197@@ -18,6 +18,7 @@
198 #define UBUNTUGESTURES_TIMER_H
199
200 #include "UbuntuGesturesGlobal.h"
201+#include "TimeSource.h"
202
203 #include <QObject>
204 #include <QPointer>
205@@ -66,17 +67,21 @@
206 {
207 Q_OBJECT
208 public:
209- FakeTimer(QObject *parent = nullptr);
210+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
211
212- virtual void emitTimeout() { Q_EMIT timeout(); }
213+ void update();
214+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
215
216 int interval() const override;
217 void setInterval(int msecs) override;
218+ void start() override;
219 bool isSingleShot() const override;
220 void setSingleShot(bool value) override;
221 private:
222 int m_interval;
223 bool m_singleShot;
224+ SharedTimeSource m_timeSource;
225+ qint64 m_nextTimeoutTime;
226 };
227
228 class UBUNTUGESTURES_EXPORT AbstractTimerFactory
229@@ -95,9 +100,16 @@
230 class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
231 {
232 public:
233+ FakeTimerFactory();
234+ virtual ~FakeTimerFactory();
235+
236+ void updateTime(qint64 msecsSinceReference);
237+ QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
238+
239 AbstractTimer *createTimer(QObject *parent = nullptr) override;
240- void makeRunningTimersTimeout();
241 QList<QPointer<FakeTimer>> timers;
242+private:
243+ QSharedPointer<FakeTimeSource> m_timeSource;
244 };
245
246 } // namespace UbuntuGestures
247
248=== modified file 'libs/UbuntuGestures/TouchRegistry.cpp'
249--- libs/UbuntuGestures/TouchRegistry.cpp 2014-10-01 13:20:32 +0000
250+++ libs/UbuntuGestures/TouchRegistry.cpp 2015-04-10 21:17:56 +0000
251@@ -1,5 +1,5 @@
252 /*
253- * Copyright (C) 2014 Canonical, Ltd.
254+ * Copyright (C) 2014-2015 Canonical, Ltd.
255 *
256 * This program is free software; you can redistribute it and/or modify
257 * it under the terms of the GNU General Public License as published by
258@@ -95,10 +95,6 @@
259 // for each point and there should not be many active points at any given moment.
260 // But having three nested for-loops does scare.
261
262- // TODO: Don't send it to the object that is already receiving the regular event
263- // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
264- // Problem is, we cannnot easily get this information.
265-
266 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
267
268 // Maps an item to the touches in this event he should be informed about.
269@@ -118,7 +114,9 @@
270 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
271 CandidateInfo &candidate = touchInfo->candidates[i];
272 Q_ASSERT(!candidate.item.isNull());
273- touchIdsForItems[candidate.item.data()].append(touchInfo->id);
274+ if (candidate.state != CandidateInfo::InterimOwner) {
275+ touchIdsForItems[candidate.item.data()].append(touchInfo->id);
276+ }
277 }
278 }
279
280@@ -260,7 +258,7 @@
281 // TODO: Check if candidate already exists
282
283 CandidateInfo candidateInfo;
284- candidateInfo.undecided = true;
285+ candidateInfo.state = CandidateInfo::Undecided;
286 candidateInfo.item = candidate;
287 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
288 *m_timerFactory,
289@@ -301,8 +299,8 @@
290 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
291 CandidateInfo &candidateInfo = touchInfo->candidates[i];
292 if (candidateInfo.item == candidate) {
293- Q_ASSERT(i > 0 || candidateInfo.undecided);
294- if (i == 0 && !candidateInfo.undecided) {
295+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
296+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
297 qCritical("TouchRegistry: touch owner is being removed.");
298 }
299 delete candidateInfo.inactivityTimer;
300@@ -340,7 +338,7 @@
301 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
302 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303 if (candidateInfo.item == candidate) {
304- candidateInfo.undecided = false;
305+ candidateInfo.state = CandidateInfo::Requested;
306 delete candidateInfo.inactivityTimer;
307 candidateInfo.inactivityTimer = nullptr;
308 candidateIndex = i;
309@@ -351,7 +349,7 @@
310 // add it as a candidate if not present yet
311 if (candidateIndex < 0) {
312 CandidateInfo candidateInfo;
313- candidateInfo.undecided = false;
314+ candidateInfo.state = CandidateInfo::InterimOwner;
315 candidateInfo.item = candidate;
316 candidateInfo.inactivityTimer = nullptr;
317 touchInfo->candidates.append(candidateInfo);
318@@ -407,8 +405,8 @@
319 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
320 CandidateInfo &candidateInfo = touchInfo->candidates[i];
321 if (candidateInfo.item == candidate) {
322- Q_ASSERT(i > 0 || candidateInfo.undecided);
323- if (i == 0 && !candidateInfo.undecided) {
324+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
325+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
326 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
327 << ") as it already owns touch" << id;
328 return;
329@@ -467,7 +465,7 @@
330
331 bool TouchRegistry::TouchInfo::isOwned() const
332 {
333- return !candidates.isEmpty() && !candidates.first().undecided;
334+ return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
335 }
336
337 bool TouchRegistry::TouchInfo::ended() const
338
339=== modified file 'libs/UbuntuGestures/TouchRegistry.h'
340--- libs/UbuntuGestures/TouchRegistry.h 2014-10-02 12:47:07 +0000
341+++ libs/UbuntuGestures/TouchRegistry.h 2015-04-10 21:17:56 +0000
342@@ -79,9 +79,11 @@
343 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
344 would be instead:
345 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
346- return true;
347- He would then be notified once ownership has been granted to him, from which point onwards he could
348- safely assume other TouchRegistry users wouldn't snatch this touch away from him.
349+ touchEvent->accept();
350+ He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
351+ will keep sending touch updates to him already). Eventually he will be notified once ownership has
352+ been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
353+ other TouchRegistry users wouldn't snatch this touch away from him.
354
355 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
356
357@@ -132,7 +134,19 @@
358 private:
359 class CandidateInfo {
360 public:
361- bool undecided;
362+ enum {
363+ // A candidate owner that doesn't yet know for sure whether he wants the touch point
364+ // (gesture recognition is stilll going on)
365+ Undecided = 0,
366+ // A candidate owner that wants the touch but hasn't been granted it yet,
367+ // most likely because there's an undecided candidate with higher priority
368+ Requested = 1,
369+ // An item that is the interim owner of the touch, receiving QTouchEvents of it
370+ // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
371+ // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
372+ // yet because of undecided candidates higher up.
373+ InterimOwner = 2
374+ } state;
375 // TODO: Prune candidates that become null and resolve ownership accordingly.
376 QPointer<QQuickItem> item;
377 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
378
379=== modified file 'plugins/Ubuntu/Gestures/CMakeLists.txt'
380--- plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-17 11:01:53 +0000
381+++ plugins/Ubuntu/Gestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
382@@ -4,10 +4,10 @@
383 set(UbuntuGesturesQml_SOURCES
384 plugin.cpp
385 AxisVelocityCalculator.cpp
386+ Damper.cpp
387 Direction.cpp
388 DirectionalDragArea.cpp
389 PressedOutsideNotifier.cpp
390- TimeSource.cpp
391 TouchDispatcher.cpp
392 TouchGate.cpp
393 )
394
395=== added file 'plugins/Ubuntu/Gestures/Damper.cpp'
396--- plugins/Ubuntu/Gestures/Damper.cpp 1970-01-01 00:00:00 +0000
397+++ plugins/Ubuntu/Gestures/Damper.cpp 2015-04-10 21:17:56 +0000
398@@ -0,0 +1,24 @@
399+/*
400+ * Copyright (C) 2015 Canonical, Ltd.
401+ *
402+ * This program is free software; you can redistribute it and/or modify
403+ * it under the terms of the GNU General Public License as published by
404+ * the Free Software Foundation; version 3.
405+ *
406+ * This program is distributed in the hope that it will be useful,
407+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
408+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
409+ * GNU General Public License for more details.
410+ *
411+ * You should have received a copy of the GNU General Public License
412+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
413+ */
414+
415+#include "Damper.h"
416+#include <QDebug>
417+
418+QDebug operator<<(QDebug dbg, const DampedPointF &p)
419+{
420+ dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
421+ return dbg.space();
422+}
423
424=== modified file 'plugins/Ubuntu/Gestures/Damper.h'
425--- plugins/Ubuntu/Gestures/Damper.h 2013-08-06 13:18:34 +0000
426+++ plugins/Ubuntu/Gestures/Damper.h 2015-04-10 21:17:56 +0000
427@@ -84,4 +84,6 @@
428 Damper<qreal> m_y;
429 };
430
431+QDebug operator<<(QDebug dbg, const DampedPointF &p);
432+
433 #endif // UBUNTU_GESTURES_DAMPER_H
434
435=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp'
436--- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2014-12-09 11:00:37 +0000
437+++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2015-04-10 21:17:56 +0000
438@@ -1,5 +1,5 @@
439 /*
440- * Copyright (C) 2013-2014 Canonical, Ltd.
441+ * Copyright (C) 2013-2015 Canonical, Ltd.
442 *
443 * This program is free software; you can redistribute it and/or modify
444 * it under the terms of the GNU General Public License as published by
445@@ -21,6 +21,7 @@
446
447 #include <QQuickWindow>
448 #include <QtCore/qmath.h>
449+#include <QScreen>
450 #include <QDebug>
451
452 #pragma GCC diagnostic push
453@@ -33,6 +34,8 @@
454 #include "TouchRegistry.h"
455 #include "UnownedTouchEvent.h"
456
457+#include "DirectionalDragArea_p.h"
458+
459 using namespace UbuntuGestures;
460
461 #if DIRECTIONALDRAGAREA_DEBUG
462@@ -40,11 +43,11 @@
463 #include "DebugHelpers.h"
464
465 namespace {
466-const char *statusToString(DirectionalDragArea::Status status)
467+const char *statusToString(DirectionalDragAreaPrivate::Status status)
468 {
469- if (status == DirectionalDragArea::WaitingForTouch) {
470+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
471 return "WaitingForTouch";
472- } else if (status == DirectionalDragArea::Undecided) {
473+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
474 return "Undecided";
475 } else {
476 return "Recognized";
477@@ -56,205 +59,156 @@
478 #define ddaDebug(params) ((void)0)
479 #endif // DIRECTIONALDRAGAREA_DEBUG
480
481-
482 DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
483 : QQuickItem(parent)
484- , m_status(WaitingForTouch)
485- , m_sceneDistance(0)
486- , m_touchId(-1)
487- , m_direction(Direction::Rightwards)
488- , m_wideningAngle(0)
489- , m_wideningFactor(0)
490- , m_distanceThreshold(0)
491- , m_distanceThresholdSquared(0.)
492- , m_minSpeed(0)
493- , m_maxSilenceTime(200)
494- , m_silenceTime(0)
495- , m_compositionTime(60)
496- , m_numSamplesOnLastSpeedCheck(0)
497- , m_recognitionTimer(0)
498- , m_velocityCalculator(0)
499- , m_timeSource(new RealTimeSource)
500- , m_activeTouches(m_timeSource)
501+ , d(new DirectionalDragAreaPrivate(this))
502 {
503- setRecognitionTimer(new Timer(this));
504- m_recognitionTimer->setInterval(60);
505- m_recognitionTimer->setSingleShot(false);
506-
507- m_velocityCalculator = new AxisVelocityCalculator(this);
508-
509- connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
510- connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
511+ d->setRecognitionTimer(new Timer(this));
512+ d->recognitionTimer->setInterval(d->maxTime);
513+ d->recognitionTimer->setSingleShot(true);
514+
515+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
516+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
517 }
518
519 Direction::Type DirectionalDragArea::direction() const
520 {
521- return m_direction;
522+ return d->direction;
523 }
524
525 void DirectionalDragArea::setDirection(Direction::Type direction)
526 {
527- if (direction != m_direction) {
528- m_direction = direction;
529- Q_EMIT directionChanged(m_direction);
530- }
531-}
532-
533-void DirectionalDragArea::setMaxDeviation(qreal value)
534-{
535- if (m_dampedScenePos.maxDelta() != value) {
536- m_dampedScenePos.setMaxDelta(value);
537- Q_EMIT maxDeviationChanged(value);
538- }
539-}
540-
541-qreal DirectionalDragArea::wideningAngle() const
542-{
543- return m_wideningAngle;
544-}
545-
546-void DirectionalDragArea::setWideningAngle(qreal angle)
547-{
548- if (angle == m_wideningAngle)
549- return;
550-
551- m_wideningAngle = angle;
552-
553- // wideningFactor = pow(cosine(angle), 2)
554- {
555- qreal angleRadians = angle * M_PI / 180.0;
556- m_wideningFactor = qCos(angleRadians);
557- m_wideningFactor = m_wideningFactor * m_wideningFactor;
558- }
559-
560- Q_EMIT wideningAngleChanged(angle);
561-}
562-
563-void DirectionalDragArea::setDistanceThreshold(qreal value)
564-{
565- if (m_distanceThreshold != value) {
566- m_distanceThreshold = value;
567- m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
568- Q_EMIT distanceThresholdChanged(value);
569- }
570-}
571-
572-void DirectionalDragArea::setMinSpeed(qreal value)
573-{
574- if (m_minSpeed != value) {
575- m_minSpeed = value;
576- Q_EMIT minSpeedChanged(value);
577- }
578-}
579-
580-void DirectionalDragArea::setMaxSilenceTime(int value)
581-{
582- if (m_maxSilenceTime != value) {
583- m_maxSilenceTime = value;
584- Q_EMIT maxSilenceTimeChanged(value);
585- }
586-}
587-
588-void DirectionalDragArea::setCompositionTime(int value)
589-{
590- if (m_compositionTime != value) {
591- m_compositionTime = value;
592- Q_EMIT compositionTimeChanged(value);
593- }
594-}
595-
596-void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
597+ if (direction != d->direction) {
598+ d->direction = direction;
599+ Q_EMIT directionChanged(d->direction);
600+ }
601+}
602+
603+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
604+{
605+ if (distanceThreshold != value) {
606+ distanceThreshold = value;
607+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
608+ }
609+}
610+
611+void DirectionalDragAreaPrivate::setMaxTime(int value)
612+{
613+ if (maxTime != value) {
614+ maxTime = value;
615+ recognitionTimer->setInterval(maxTime);
616+ }
617+}
618+
619+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
620 {
621 int interval = 0;
622 bool timerWasRunning = false;
623 bool wasSingleShot = false;
624
625 // can be null when called from the constructor
626- if (m_recognitionTimer) {
627- interval = m_recognitionTimer->interval();
628- timerWasRunning = m_recognitionTimer->isRunning();
629- if (m_recognitionTimer->parent() == this) {
630- delete m_recognitionTimer;
631+ if (recognitionTimer) {
632+ interval = recognitionTimer->interval();
633+ timerWasRunning = recognitionTimer->isRunning();
634+ if (recognitionTimer->parent() == this) {
635+ delete recognitionTimer;
636 }
637 }
638
639- m_recognitionTimer = timer;
640+ recognitionTimer = timer;
641 timer->setInterval(interval);
642 timer->setSingleShot(wasSingleShot);
643 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
644- this, &DirectionalDragArea::checkSpeed);
645+ this, &DirectionalDragAreaPrivate::rejectGesture);
646 if (timerWasRunning) {
647- m_recognitionTimer->start();
648+ recognitionTimer->start();
649 }
650 }
651
652-void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
653+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
654 {
655- m_timeSource = timeSource;
656- m_velocityCalculator->setTimeSource(timeSource);
657- m_activeTouches.m_timeSource = timeSource;
658+ this->timeSource = timeSource;
659+ activeTouches.m_timeSource = timeSource;
660 }
661
662 qreal DirectionalDragArea::distance() const
663 {
664- if (Direction::isHorizontal(m_direction)) {
665- return m_previousPos.x() - m_startPos.x();
666+ if (Direction::isHorizontal(d->direction)) {
667+ return d->publicPos.x() - d->startPos.x();
668 } else {
669- return m_previousPos.y() - m_startPos.y();
670+ return d->publicPos.y() - d->startPos.y();
671 }
672 }
673
674-void DirectionalDragArea::updateSceneDistance()
675+void DirectionalDragAreaPrivate::updateSceneDistance()
676 {
677- QPointF totalMovement = m_previousScenePos - m_startScenePos;
678- m_sceneDistance = projectOntoDirectionVector(totalMovement);
679+ QPointF totalMovement = publicScenePos - startScenePos;
680+ sceneDistance = projectOntoDirectionVector(totalMovement);
681 }
682
683 qreal DirectionalDragArea::sceneDistance() const
684 {
685- return m_sceneDistance;
686+ return d->sceneDistance;
687 }
688
689 qreal DirectionalDragArea::touchX() const
690 {
691- return m_previousPos.x();
692+ return d->publicPos.x();
693 }
694
695 qreal DirectionalDragArea::touchY() const
696 {
697- return m_previousPos.y();
698+ return d->publicPos.y();
699 }
700
701 qreal DirectionalDragArea::touchSceneX() const
702 {
703- return m_previousScenePos.x();
704+ return d->publicScenePos.x();
705 }
706
707 qreal DirectionalDragArea::touchSceneY() const
708 {
709- return m_previousScenePos.y();
710+ return d->publicScenePos.y();
711+}
712+
713+bool DirectionalDragArea::dragging() const
714+{
715+ return d->status == DirectionalDragAreaPrivate::Recognized;
716+}
717+
718+bool DirectionalDragArea::immediateRecognition() const
719+{
720+ return d->immediateRecognition;
721+}
722+
723+void DirectionalDragArea::setImmediateRecognition(bool enabled)
724+{
725+ if (d->immediateRecognition != enabled) {
726+ d->immediateRecognition = enabled;
727+ Q_EMIT immediateRecognitionChanged(enabled);
728+ }
729 }
730
731 bool DirectionalDragArea::event(QEvent *event)
732 {
733 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
734- touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
735+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
736 return true;
737 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
738- unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
739+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
740 return true;
741 } else {
742 return QQuickItem::event(event);
743 }
744 }
745
746-void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
747+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
748 {
749 if (event->gained()) {
750 QVector<int> ids;
751 ids.append(event->touchId());
752 ddaDebug("grabbing touch");
753- grabTouchPoints(ids);
754+ q->grabTouchPoints(ids);
755
756 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
757 // emulation it will cause the emulation logic to go nuts.
758@@ -262,35 +216,35 @@
759 //
760 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
761 // TODO: Remove this workaround once we start using Qt 5.4
762- if (window()) {
763- QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
764- if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
765+ if (q->window()) {
766+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
767+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
768 ddaDebug("removing mouse grabber");
769- window()->mouseGrabberItem()->ungrabMouse();
770+ q->window()->mouseGrabberItem()->ungrabMouse();
771 }
772 }
773 } else {
774 // We still wanna know when it ends for keeping the composition time window up-to-date
775- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
776+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
777
778 setStatus(WaitingForTouch);
779 }
780 }
781
782-void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
783+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
784 {
785 QTouchEvent *event = unownedTouchEvent->touchEvent();
786
787 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
788
789- ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
790+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
791
792- switch (m_status) {
793+ switch (status) {
794 case WaitingForTouch:
795 // do nothing
796 break;
797 case Undecided:
798- Q_ASSERT(isEnabled() && isVisible());
799+ Q_ASSERT(q->isEnabled() && q->isVisible());
800 unownedTouchEvent_undecided(unownedTouchEvent);
801 break;
802 default: // Recognized:
803@@ -298,18 +252,18 @@
804 break;
805 }
806
807- m_activeTouches.update(event);
808+ activeTouches.update(event);
809 }
810
811-void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
812+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
813 {
814 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
815 if (!touchPoint) {
816- qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
817+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
818 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
819 "Considering it as released.";
820
821- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
822+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
823 setStatus(WaitingForTouch);
824 return;
825 }
826@@ -319,48 +273,42 @@
827 if (touchPoint->state() == Qt::TouchPointReleased) {
828 // touch has ended before recognition concluded
829 ddaDebug("Touch has ended before recognition concluded");
830- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
831- emitSignalIfTapped();
832- setStatus(WaitingForTouch);
833- return;
834- }
835-
836- m_previousDampedScenePos.setX(m_dampedScenePos.x());
837- m_previousDampedScenePos.setY(m_dampedScenePos.y());
838- m_dampedScenePos.update(touchScenePos);
839- updateVelocityCalculator(touchScenePos);
840-
841- if (!pointInsideAllowedArea()) {
842- ddaDebug("Rejecting gesture because touch point is outside allowed area.");
843- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
844- // We still wanna know when it ends for keeping the composition time window up-to-date
845- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
846- setStatus(WaitingForTouch);
847- return;
848- }
849+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
850+ setStatus(WaitingForTouch);
851+ return;
852+ }
853+
854+ previousDampedScenePos.setX(dampedScenePos.x());
855+ previousDampedScenePos.setY(dampedScenePos.y());
856+ dampedScenePos.update(touchScenePos);
857
858 if (!movingInRightDirection()) {
859 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
860- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
861+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
862 // We still wanna know when it ends for keeping the composition time window up-to-date
863- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
864+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
865 setStatus(WaitingForTouch);
866 return;
867 }
868
869- setPreviousPos(touchPoint->pos());
870- setPreviousScenePos(touchScenePos);
871-
872 if (isWithinTouchCompositionWindow()) {
873 // There's still time for some new touch to appear and ruin our party as it would be combined
874- // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
875+ // with our touchId one and therefore deny the possibility of a single-finger gesture.
876 ddaDebug("Sill within composition window. Let's wait more.");
877 return;
878 }
879
880- if (movedFarEnough(touchScenePos)) {
881- TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
882+ if (movedFarEnoughAlongGestureAxis()) {
883+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
884 setStatus(Recognized);
885+ setPublicPos(touchPoint->pos());
886+ setPublicScenePos(touchScenePos);
887+ } else if (isPastMaxDistance()) {
888+ ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
889+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
890+ // We still wanna know when it ends for keeping the composition time window up-to-date
891+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
892+ setStatus(WaitingForTouch);
893 } else {
894 ddaDebug("Didn't move far enough yet. Let's wait more.");
895 }
896@@ -371,29 +319,29 @@
897 // TODO: Consider when more than one touch starts in the same event (although it's not possible
898 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
899
900- ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
901+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
902
903 if (!isEnabled() || !isVisible()) {
904 QQuickItem::touchEvent(event);
905 return;
906 }
907
908- switch (m_status) {
909- case WaitingForTouch:
910- touchEvent_absent(event);
911+ switch (d->status) {
912+ case DirectionalDragAreaPrivate::WaitingForTouch:
913+ d->touchEvent_absent(event);
914 break;
915- case Undecided:
916- touchEvent_undecided(event);
917+ case DirectionalDragAreaPrivate::Undecided:
918+ d->touchEvent_undecided(event);
919 break;
920 default: // Recognized:
921- touchEvent_recognized(event);
922+ d->touchEvent_recognized(event);
923 break;
924 }
925
926- m_activeTouches.update(event);
927+ d->activeTouches.update(event);
928 }
929
930-void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
931+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
932 {
933 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
934
935@@ -423,36 +371,41 @@
936 allGood = false;
937 } else {
938 // that's our candidate
939- m_touchId = touchPoint.id();
940+ touchId = touchPoint.id();
941 newTouchPoint = &touchPoint;
942 }
943 }
944 }
945
946 if (allGood) {
947+ allGood = sanityCheckRecognitionProperties();
948+ if (!allGood) {
949+ qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
950+ " is impossible");
951+ }
952+ }
953+
954+ if (allGood) {
955 Q_ASSERT(newTouchPoint);
956
957- m_startPos = newTouchPoint->pos();
958- m_startScenePos = newTouchPoint->scenePos();
959- m_touchId = newTouchPoint->id();
960- m_dampedScenePos.reset(m_startScenePos);
961- m_velocityCalculator->setTrackedPosition(0.);
962- m_velocityCalculator->reset();
963- m_numSamplesOnLastSpeedCheck = 0;
964- m_silenceTime = 0;
965- setPreviousPos(m_startPos);
966- setPreviousScenePos(m_startScenePos);
967+ startPos = newTouchPoint->pos();
968+ startScenePos = newTouchPoint->scenePos();
969+ touchId = newTouchPoint->id();
970+ dampedScenePos.reset(startScenePos);
971+ setPublicPos(startPos);
972+
973+ setPublicScenePos(startScenePos);
974 updateSceneDirectionVector();
975
976 if (recognitionIsDisabled()) {
977 // Behave like a dumb TouchArea
978 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
979- TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
980+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
981 setStatus(Recognized);
982 event->accept();
983 } else {
984 // just monitor the touch points for now.
985- TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
986+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
987
988 setStatus(Undecided);
989 // Let the item below have it. We will monitor it and grab it later if a gesture
990@@ -465,12 +418,11 @@
991 }
992 }
993
994-void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
995+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
996 {
997- Q_ASSERT(event->type() == QEvent::TouchBegin);
998 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
999
1000- // We're not interested in new touch points. We already have our candidate (m_touchId).
1001+ // We're not interested in new touch points. We already have our candidate (touchId).
1002 // But we do want to know when those new touches end for keeping the composition time
1003 // window up-to-date
1004 event->ignore();
1005@@ -480,63 +432,60 @@
1006 // multi-finger drags are not accepted
1007 ddaDebug("Multi-finger drags are not accepted");
1008
1009- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
1010+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
1011 // We still wanna know when it ends for keeping the composition time window up-to-date
1012- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
1013+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
1014
1015 setStatus(WaitingForTouch);
1016 }
1017 }
1018
1019-void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
1020+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
1021 {
1022 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
1023
1024 if (!touchPoint) {
1025- qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
1026+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
1027 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
1028 "Considering it as released.";
1029 setStatus(WaitingForTouch);
1030 } else {
1031- setPreviousPos(touchPoint->pos());
1032- setPreviousScenePos(touchPoint->scenePos());
1033+ setPublicPos(touchPoint->pos());
1034+ setPublicScenePos(touchPoint->scenePos());
1035
1036 if (touchPoint->state() == Qt::TouchPointReleased) {
1037- emitSignalIfTapped();
1038 setStatus(WaitingForTouch);
1039 }
1040 }
1041 }
1042
1043-void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
1044+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
1045 {
1046 for (int i = 0; i < touchPoints.count(); ++i) {
1047 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1048 if (touchPoint.state() == Qt::TouchPointPressed) {
1049- TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
1050+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
1051 }
1052 }
1053 }
1054
1055-bool DirectionalDragArea::recognitionIsDisabled() const
1056-{
1057- return distanceThreshold() <= 0 && compositionTime() <= 0;
1058-}
1059-
1060-void DirectionalDragArea::emitSignalIfTapped()
1061-{
1062- qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
1063- if (touchDuration <= maxTapDuration()) {
1064- Q_EMIT tapped();
1065- }
1066-}
1067-
1068-const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
1069+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
1070+{
1071+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
1072+}
1073+
1074+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
1075+{
1076+ return recognitionIsDisabled()
1077+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
1078+}
1079+
1080+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
1081 {
1082 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1083 const QTouchEvent::TouchPoint *touchPoint = 0;
1084 for (int i = 0; i < touchPoints.size(); ++i) {
1085- if (touchPoints.at(i).id() == m_touchId) {
1086+ if (touchPoints.at(i).id() == touchId) {
1087 touchPoint = &touchPoints.at(i);
1088 break;
1089 }
1090@@ -544,39 +493,13 @@
1091 return touchPoint;
1092 }
1093
1094-bool DirectionalDragArea::pointInsideAllowedArea() const
1095-{
1096- // NB: Using squared values to avoid computing the square root to find
1097- // the length totalMovement
1098-
1099- QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
1100- m_dampedScenePos.y() - m_startScenePos.y());
1101-
1102- qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
1103- totalMovement.y() * totalMovement.y();
1104-
1105- if (squaredTotalMovSize == 0.) {
1106- // didn't move
1107- return true;
1108- }
1109-
1110- qreal projectedMovement = projectOntoDirectionVector(totalMovement);
1111-
1112-
1113- qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
1114-
1115- // Same as:
1116- // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
1117- return cosineAngleSquared >= m_wideningFactor;
1118-}
1119-
1120-bool DirectionalDragArea::movingInRightDirection() const
1121-{
1122- if (m_direction == Direction::Horizontal) {
1123+bool DirectionalDragAreaPrivate::movingInRightDirection() const
1124+{
1125+ if (direction == Direction::Horizontal) {
1126 return true;
1127 } else {
1128- QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
1129- m_dampedScenePos.y() - m_previousDampedScenePos.y());
1130+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
1131+ dampedScenePos.y() - previousDampedScenePos.y());
1132
1133 qreal scalarProjection = projectOntoDirectionVector(movementVector);
1134
1135@@ -584,96 +507,93 @@
1136 }
1137 }
1138
1139-bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
1140+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
1141 {
1142- if (m_distanceThreshold <= 0.) {
1143+ if (distanceThreshold <= 0.) {
1144 // distance threshold check is disabled
1145 return true;
1146 } else {
1147- QPointF totalMovement(point.x() - m_startScenePos.x(),
1148- point.y() - m_startScenePos.y());
1149-
1150- qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
1151- totalMovement.y() * totalMovement.y();
1152-
1153- return squaredTotalMovSize > m_distanceThresholdSquared;
1154- }
1155-}
1156-
1157-void DirectionalDragArea::checkSpeed()
1158-{
1159- Q_ASSERT(m_status == Undecided);
1160-
1161- if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
1162- qreal speed = qFabs(m_velocityCalculator->calculate());
1163- qreal minSpeedMsecs = m_minSpeed / 1000.0;
1164-
1165- if (speed < minSpeedMsecs) {
1166- ddaDebug("Rejecting gesture because it's below minimum speed.");
1167- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
1168- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
1169- setStatus(WaitingForTouch);
1170- }
1171- }
1172-
1173- if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
1174- m_silenceTime += m_recognitionTimer->interval();
1175-
1176- if (m_silenceTime > m_maxSilenceTime) {
1177- ddaDebug("Rejecting gesture because its silence time has been exceeded.");
1178- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
1179- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
1180- setStatus(WaitingForTouch);
1181- }
1182- } else {
1183- m_silenceTime = 0;
1184- }
1185- m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
1186-}
1187-
1188-void DirectionalDragArea::giveUpIfDisabledOrInvisible()
1189-{
1190- if (!isEnabled() || !isVisible()) {
1191- if (m_status == Undecided) {
1192- TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
1193+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
1194+ dampedScenePos.y() - startScenePos.y());
1195+
1196+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
1197+
1198+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
1199+ << ", distanceThreshold=" << distanceThreshold);
1200+
1201+ if (direction == Direction::Horizontal) {
1202+ return qAbs(scalarProjection) > distanceThreshold;
1203+ } else {
1204+ return scalarProjection > distanceThreshold;
1205+ }
1206+ }
1207+}
1208+
1209+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
1210+{
1211+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
1212+ dampedScenePos.y() - startScenePos.y());
1213+
1214+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
1215+ return squaredDistance > maxDistance*maxDistance;
1216+}
1217+
1218+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
1219+{
1220+ if (!q->isEnabled() || !q->isVisible()) {
1221+ if (status == Undecided) {
1222+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
1223 // We still wanna know when it ends for keeping the composition time window up-to-date
1224- TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
1225+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
1226 }
1227
1228- if (m_status != WaitingForTouch) {
1229+ if (status != WaitingForTouch) {
1230 ddaDebug("Resetting status because got disabled or made invisible");
1231 setStatus(WaitingForTouch);
1232 }
1233 }
1234 }
1235
1236-void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
1237-{
1238- if (newStatus == m_status)
1239+void DirectionalDragAreaPrivate::rejectGesture()
1240+{
1241+ if (status == Undecided) {
1242+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
1243+
1244+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
1245+ // We still wanna know when it ends for keeping the composition time window up-to-date
1246+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
1247+
1248+ setStatus(WaitingForTouch);
1249+ }
1250+}
1251+
1252+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
1253+{
1254+ if (newStatus == status)
1255 return;
1256
1257- DirectionalDragArea::Status oldStatus = m_status;
1258+ Status oldStatus = status;
1259
1260 if (oldStatus == Undecided) {
1261- m_recognitionTimer->stop();
1262+ recognitionTimer->stop();
1263 }
1264
1265- m_status = newStatus;
1266- Q_EMIT statusChanged(m_status);
1267+ status = newStatus;
1268+ Q_EMIT statusChanged(status);
1269
1270 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
1271
1272 switch (newStatus) {
1273 case WaitingForTouch:
1274- Q_EMIT draggingChanged(false);
1275+ if (oldStatus == Recognized) {
1276+ Q_EMIT q->draggingChanged(false);
1277+ }
1278 break;
1279 case Undecided:
1280- m_recognitionTimer->start();
1281- Q_EMIT draggingChanged(true);
1282+ recognitionTimer->start();
1283 break;
1284 case Recognized:
1285- if (oldStatus == WaitingForTouch)
1286- Q_EMIT draggingChanged(true);
1287+ Q_EMIT q->draggingChanged(true);
1288 break;
1289 default:
1290 // no-op
1291@@ -681,77 +601,126 @@
1292 }
1293 }
1294
1295-void DirectionalDragArea::setPreviousPos(const QPointF &point)
1296+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
1297 {
1298- bool xChanged = m_previousPos.x() != point.x();
1299- bool yChanged = m_previousPos.y() != point.y();
1300-
1301- m_previousPos = point;
1302+ bool xChanged = publicPos.x() != point.x();
1303+ bool yChanged = publicPos.y() != point.y();
1304+
1305+ // Public position should not get updated while the gesture is still being recognized
1306+ // (ie, Undecided status).
1307+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
1308+
1309+ if (status == Recognized && !recognitionIsDisabled()) {
1310+ // When the gesture finally gets recognized, the finger will likely be
1311+ // reasonably far from the edge. If we made the contentX immediately
1312+ // follow the finger position it would be visually unpleasant as it
1313+ // would appear right next to the user's finger out of nowhere (ie,
1314+ // it would jump). Instead, we make contentX go towards the user's
1315+ // finger in several steps. ie., in an animated way.
1316+ QPointF delta = point - publicPos;
1317+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
1318+ publicPos.rx() += 0.4 * delta.x();
1319+ publicPos.ry() += 0.4 * delta.y();
1320+ } else {
1321+ // no smoothing when initializing or if gesture recognition was immediate as there will
1322+ // be no jump.
1323+ publicPos = point;
1324+ }
1325
1326 if (xChanged) {
1327- Q_EMIT touchXChanged(point.x());
1328- if (Direction::isHorizontal(m_direction))
1329- Q_EMIT distanceChanged(distance());
1330+ Q_EMIT q->touchXChanged(publicPos.x());
1331+ if (Direction::isHorizontal(direction))
1332+ Q_EMIT q->distanceChanged(q->distance());
1333 }
1334
1335 if (yChanged) {
1336- Q_EMIT touchYChanged(point.y());
1337- if (Direction::isVertical(m_direction))
1338- Q_EMIT distanceChanged(distance());
1339+ Q_EMIT q->touchYChanged(publicPos.y());
1340+ if (Direction::isVertical(direction))
1341+ Q_EMIT q->distanceChanged(q->distance());
1342 }
1343 }
1344
1345-void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
1346+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
1347 {
1348- bool xChanged = m_previousScenePos.x() != point.x();
1349- bool yChanged = m_previousScenePos.y() != point.y();
1350+ bool xChanged = publicScenePos.x() != point.x();
1351+ bool yChanged = publicScenePos.y() != point.y();
1352
1353 if (!xChanged && !yChanged)
1354 return;
1355
1356- qreal oldSceneDistance = sceneDistance();
1357- m_previousScenePos = point;
1358+ // Public position should not get updated while the gesture is still being recognized
1359+ // (ie, Undecided status).
1360+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
1361+
1362+ qreal oldSceneDistance = sceneDistance;
1363+
1364+ if (status == Recognized && !recognitionIsDisabled()) {
1365+ // When the gesture finally gets recognized, the finger will likely be
1366+ // reasonably far from the edge. If we made the contentX immediately
1367+ // follow the finger position it would be visually unpleasant as it
1368+ // would appear right next to the user's finger out of nowhere (ie,
1369+ // it would jump). Instead, we make contentX go towards the user's
1370+ // finger in several steps. ie., in an animated way.
1371+ QPointF delta = point - publicScenePos;
1372+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
1373+ publicScenePos.rx() += 0.4 * delta.x();
1374+ publicScenePos.ry() += 0.4 * delta.y();
1375+ } else {
1376+ // no smoothing when initializing or if gesture recognition was immediate as there will
1377+ // be no jump.
1378+ publicScenePos = point;
1379+ }
1380+
1381 updateSceneDistance();
1382
1383- if (oldSceneDistance != sceneDistance()) {
1384- Q_EMIT sceneDistanceChanged(sceneDistance());
1385+ if (oldSceneDistance != sceneDistance) {
1386+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
1387 }
1388
1389 if (xChanged) {
1390- Q_EMIT touchSceneXChanged(point.x());
1391+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
1392 }
1393
1394 if (yChanged) {
1395- Q_EMIT touchSceneYChanged(point.y());
1396+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
1397 }
1398 }
1399
1400-void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
1401-{
1402- QPointF totalSceneMovement = scenePos - m_startScenePos;
1403-
1404- qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
1405-
1406- m_velocityCalculator->setTrackedPosition(scalarProjection);
1407-}
1408-
1409-bool DirectionalDragArea::isWithinTouchCompositionWindow()
1410+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
1411 {
1412 return
1413- compositionTime() > 0 &&
1414- !m_activeTouches.isEmpty() &&
1415- m_timeSource->msecsSinceReference() <=
1416- m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
1417+ compositionTime > 0 &&
1418+ !activeTouches.isEmpty() &&
1419+ timeSource->msecsSinceReference() <=
1420+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
1421+}
1422+
1423+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
1424+{
1425+ if (change == QQuickItem::ItemSceneChange) {
1426+ if (value.window != nullptr) {
1427+ // TODO: Handle window->screen() changes (ie window changing screens)
1428+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
1429+ d->setPixelsPerMm(pixelsPerMm);
1430+ }
1431+ }
1432+}
1433+
1434+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
1435+{
1436+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
1437+ setDistanceThreshold(4. * pixelsPerMm);
1438+ maxDistance = 10. * pixelsPerMm;
1439 }
1440
1441 //************************** ActiveTouchesInfo **************************
1442
1443-DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
1444+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
1445 : m_timeSource(timeSource)
1446 {
1447 }
1448
1449-void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
1450+void ActiveTouchesInfo::update(QTouchEvent *event)
1451 {
1452 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
1453 // nothing to update
1454@@ -773,7 +742,7 @@
1455 }
1456
1457 #if ACTIVETOUCHESINFO_DEBUG
1458-QString DirectionalDragArea::ActiveTouchesInfo::toString()
1459+QString ActiveTouchesInfo::toString()
1460 {
1461 QString string = "(";
1462
1463@@ -791,7 +760,7 @@
1464 }
1465 #endif // ACTIVETOUCHESINFO_DEBUG
1466
1467-void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
1468+void ActiveTouchesInfo::addTouchPoint(int touchId)
1469 {
1470 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
1471 activeTouchInfo.id = touchId;
1472@@ -802,7 +771,7 @@
1473 #endif
1474 }
1475
1476-qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
1477+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
1478 {
1479 qint64 result = -1;
1480
1481@@ -819,7 +788,7 @@
1482 return result;
1483 }
1484
1485-void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
1486+void ActiveTouchesInfo::removeTouchPoint(int touchId)
1487 {
1488 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
1489 if (touchId == touchInfo->id) {
1490@@ -835,7 +804,7 @@
1491 #endif
1492 }
1493
1494-qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
1495+qint64 ActiveTouchesInfo::mostRecentStartTime()
1496 {
1497 Q_ASSERT(!m_touchInfoPool.isEmpty());
1498
1499@@ -851,11 +820,11 @@
1500 return highestStartTime;
1501 }
1502
1503-void DirectionalDragArea::updateSceneDirectionVector()
1504+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
1505 {
1506 QPointF localOrigin(0., 0.);
1507 QPointF localDirection;
1508- switch (m_direction) {
1509+ switch (direction) {
1510 case Direction::Upwards:
1511 localDirection.rx() = 0.;
1512 localDirection.ry() = -1.;
1513@@ -873,14 +842,31 @@
1514 localDirection.ry() = 0.;
1515 break;
1516 }
1517- QPointF sceneOrigin = mapToScene(localOrigin);
1518- QPointF sceneDirection = mapToScene(localDirection);
1519- m_sceneDirectionVector = sceneDirection - sceneOrigin;
1520-}
1521-
1522-qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
1523-{
1524- // same as dot product as m_sceneDirectionVector is a unit vector
1525- return sceneVector.x() * m_sceneDirectionVector.x() +
1526- sceneVector.y() * m_sceneDirectionVector.y();
1527+ QPointF sceneOrigin = q->mapToScene(localOrigin);
1528+ QPointF sceneDirection = q->mapToScene(localDirection);
1529+ sceneDirectionVector = sceneDirection - sceneOrigin;
1530+}
1531+
1532+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
1533+{
1534+ // same as dot product as sceneDirectionVector is a unit vector
1535+ return sceneVector.x() * sceneDirectionVector.x() +
1536+ sceneVector.y() * sceneDirectionVector.y();
1537+}
1538+
1539+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
1540+ : q(q)
1541+ , status(WaitingForTouch)
1542+ , sceneDistance(0)
1543+ , touchId(-1)
1544+ , direction(Direction::Rightwards)
1545+ , distanceThreshold(0)
1546+ , distanceThresholdSquared(0.)
1547+ , maxTime(400)
1548+ , compositionTime(60)
1549+ , immediateRecognition(false)
1550+ , recognitionTimer(nullptr)
1551+ , timeSource(new RealTimeSource)
1552+ , activeTouches(timeSource)
1553+{
1554 }
1555
1556=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h'
1557--- plugins/Ubuntu/Gestures/DirectionalDragArea.h 2014-10-01 13:20:32 +0000
1558+++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2015-04-10 21:17:56 +0000
1559@@ -18,7 +18,6 @@
1560 #define DIRECTIONAL_DRAG_AREA_H
1561
1562 #include <QtQuick/QQuickItem>
1563-#include "AxisVelocityCalculator.h"
1564 #include "UbuntuGesturesQmlGlobal.h"
1565 #include "Damper.h"
1566 #include "Direction.h"
1567@@ -29,6 +28,7 @@
1568
1569 class TouchOwnershipEvent;
1570 class UnownedTouchEvent;
1571+class DirectionalDragAreaPrivate;
1572
1573 /*
1574 An area that detects axis-aligned single-finger drag gestures
1575@@ -61,95 +61,28 @@
1576 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
1577 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
1578
1579- // The current status of the directional drag gesture area.
1580- Q_PROPERTY(Status status READ status NOTIFY statusChanged)
1581-
1582 // Whether a drag gesture is taking place
1583- // This will be true as long as status is Undecided or Recognized
1584- // When a gesture gets rejected, dragging turns to false.
1585 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
1586
1587- /////
1588- // stuff that will be set in stone at some point
1589-
1590- // How far the touch point can move away from its expected position before
1591- // it causes a rejection in the gesture recognition. This is to compensate
1592- // for both noise in the touch input signal and for the natural irregularities
1593- // in the finger movement.
1594- // Proper value is likely device-specific.
1595- Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
1596-
1597- // Widening angle, in degrees
1598- // It's roughly the maximum angle a touch point can make relative to the
1599- // axis defined by the compoment's direction for it to be recognized as a
1600- // directional drag.
1601- Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
1602- NOTIFY wideningAngleChanged)
1603-
1604- // How far a touch point has to move from its initial position in order for
1605- // it to be recognized as a directional drag.
1606- Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
1607- NOTIFY distanceThresholdChanged)
1608-
1609- // Minimum speed a gesture needs to have in order to be recognized as a
1610- // directional drag.
1611- // In pixels per second
1612- Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
1613-
1614- // A gesture will be rejected if more than maxSilenceTime milliseconds has
1615- // passed since we last got an input event from it (during Undecided state).
1616- //
1617- // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
1618- // finger is still (zero drag speed). In some cases the finger might be moving but
1619- // the driver's high noise filtering might cause those silence periods, specially
1620- // in the moments succeeding a press (talking about Galaxy Nexus here).
1621- Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
1622- WRITE setMaxSilenceTime
1623- NOTIFY maxSilenceTimeChanged)
1624-
1625- //
1626- /////
1627-
1628- // Maximum time (in milliseconds) after the start of a given touch point where
1629- // subsequent touch starts are grouped with the first one into an N-touches gesture
1630- // (e.g. a two-fingers tap or drag).
1631- Q_PROPERTY(int compositionTime READ compositionTime
1632- WRITE setCompositionTime
1633- NOTIFY compositionTimeChanged)
1634+ // Whether a gesture should be Recognized as soon a touch lands on the area.
1635+ // With this property enabled it will work pretty much like a MultiPointTouchArea,
1636+ // just with a different API.
1637+ //
1638+ // It's false by default. In most cases you will not want that enabled.
1639+ Q_PROPERTY(bool immediateRecognition
1640+ READ immediateRecognition
1641+ WRITE setImmediateRecognition
1642+ NOTIFY immediateRecognitionChanged)
1643
1644 Q_ENUMS(Direction)
1645- Q_ENUMS(Status)
1646 public:
1647 DirectionalDragArea(QQuickItem *parent = 0);
1648
1649 Direction::Type direction() const;
1650 void setDirection(Direction::Type);
1651
1652- // Describes the state of the directional drag gesture.
1653- enum Status {
1654- // Waiting for a new touch point to land on this area. No gesture is being processed
1655- // or tracked.
1656- WaitingForTouch,
1657-
1658- // A touch point has landed on this area but it's not know yet whether it is
1659- // performing a drag in the correct direction.
1660- // If it's decided that the touch point is not performing a directional drag gesture,
1661- // it will be rejected/ignored and status will return to WaitingForTouch.
1662- Undecided, //Recognizing,
1663-
1664- // There's a touch point in this area and it performed a drag in the correct
1665- // direction.
1666- //
1667- // Once recognized, the gesture state will move back to WaitingForTouch only once
1668- // that touch point ends. The gesture will remain in the Recognized state even if
1669- // the touch point starts moving in other directions or halts.
1670- Recognized,
1671- };
1672- Status status() const { return m_status; }
1673-
1674 qreal distance() const;
1675 qreal sceneDistance() const;
1676- void updateSceneDistance();
1677
1678 qreal touchX() const;
1679 qreal touchY() const;
1680@@ -157,152 +90,30 @@
1681 qreal touchSceneX() const;
1682 qreal touchSceneY() const;
1683
1684- bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
1685-
1686- qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
1687- void setMaxDeviation(qreal value);
1688-
1689- qreal wideningAngle() const;
1690- void setWideningAngle(qreal value);
1691-
1692- qreal distanceThreshold() const { return m_distanceThreshold; }
1693- void setDistanceThreshold(qreal value);
1694-
1695- qreal minSpeed() const { return m_minSpeed; }
1696- void setMinSpeed(qreal value);
1697-
1698- int maxSilenceTime() const { return m_maxSilenceTime; }
1699- void setMaxSilenceTime(int value);
1700-
1701- int compositionTime() const { return m_compositionTime; }
1702- void setCompositionTime(int value);
1703-
1704- // Replaces the existing Timer with the given one.
1705- //
1706- // Useful for providing a fake timer when testing.
1707- void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
1708-
1709- // Useful for testing, where a fake time source can be supplied
1710- void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
1711+ bool dragging() const;
1712+
1713+ bool immediateRecognition() const;
1714+ void setImmediateRecognition(bool enabled);
1715
1716 bool event(QEvent *e) override;
1717
1718- // Maximum time, in milliseconds, between a press and a release, for a touch
1719- // sequence to be considered a tap.
1720- int maxTapDuration() const { return 300; }
1721-
1722 Q_SIGNALS:
1723 void directionChanged(Direction::Type direction);
1724- void statusChanged(Status value);
1725 void draggingChanged(bool value);
1726 void distanceChanged(qreal value);
1727 void sceneDistanceChanged(qreal value);
1728- void maxDeviationChanged(qreal value);
1729- void wideningAngleChanged(qreal value);
1730- void distanceThresholdChanged(qreal value);
1731- void minSpeedChanged(qreal value);
1732- void maxSilenceTimeChanged(int value);
1733- void compositionTimeChanged(int value);
1734 void touchXChanged(qreal value);
1735 void touchYChanged(qreal value);
1736 void touchSceneXChanged(qreal value);
1737 void touchSceneYChanged(qreal value);
1738-
1739- // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
1740- // Remove when no longer used or move its implementation to the QML code that uses it
1741- // See maxTapDuration()
1742- void tapped();
1743+ void immediateRecognitionChanged(bool value);
1744
1745 protected:
1746 virtual void touchEvent(QTouchEvent *event);
1747-
1748-private Q_SLOTS:
1749- void checkSpeed();
1750- void giveUpIfDisabledOrInvisible();
1751-
1752-private:
1753- void touchEvent_absent(QTouchEvent *event);
1754- void touchEvent_undecided(QTouchEvent *event);
1755- void touchEvent_recognized(QTouchEvent *event);
1756- bool pointInsideAllowedArea() const;
1757- bool movingInRightDirection() const;
1758- bool movedFarEnough(const QPointF &point) const;
1759- const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
1760- void setStatus(Status newStatus);
1761- void setPreviousPos(const QPointF &point);
1762- void setPreviousScenePos(const QPointF &point);
1763- void updateVelocityCalculator(const QPointF &point);
1764- bool isWithinTouchCompositionWindow();
1765- void updateSceneDirectionVector();
1766- // returns the scalar projection between the given vector (in scene coordinates)
1767- // and m_sceneDirectionVector
1768- qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
1769- void touchOwnershipEvent(TouchOwnershipEvent *event);
1770- void unownedTouchEvent(UnownedTouchEvent *event);
1771- void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
1772- void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
1773- bool recognitionIsDisabled() const;
1774- void emitSignalIfTapped();
1775-
1776- Status m_status;
1777-
1778- QPointF m_startPos;
1779- QPointF m_startScenePos;
1780- QPointF m_previousPos;
1781- QPointF m_previousScenePos;
1782- qreal m_sceneDistance;
1783- int m_touchId;
1784-
1785- // A movement damper is used in some of the gesture recognition calculations
1786- // to get rid of noise or small oscillations in the touch position.
1787- DampedPointF m_dampedScenePos;
1788- QPointF m_previousDampedScenePos;
1789-
1790- // Unit vector in scene coordinates describing the direction of the gesture recognition
1791- QPointF m_sceneDirectionVector;
1792-
1793- Direction::Type m_direction;
1794- qreal m_wideningAngle; // in degrees
1795- qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
1796- qreal m_distanceThreshold;
1797- qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
1798- qreal m_minSpeed;
1799- int m_maxSilenceTime; // in milliseconds
1800- int m_silenceTime; // in milliseconds
1801- int m_compositionTime; // in milliseconds
1802- int m_numSamplesOnLastSpeedCheck;
1803- UbuntuGestures::AbstractTimer *m_recognitionTimer;
1804- AxisVelocityCalculator *m_velocityCalculator;
1805-
1806- UbuntuGestures::SharedTimeSource m_timeSource;
1807-
1808- // Information about an active touch point
1809- struct ActiveTouchInfo {
1810- ActiveTouchInfo() : id(-1), startTime(-1) {}
1811- bool isValid() const { return id != -1; }
1812- void reset() { id = -1; }
1813- int id;
1814- qint64 startTime;
1815- };
1816- class ActiveTouchesInfo {
1817- public:
1818- ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
1819- void update(QTouchEvent *event);
1820- qint64 touchStartTime(int id);
1821- bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
1822- qint64 mostRecentStartTime();
1823- UbuntuGestures::SharedTimeSource m_timeSource;
1824- private:
1825- void addTouchPoint(int touchId);
1826- void removeTouchPoint(int touchId);
1827- #if ACTIVETOUCHESINFO_DEBUG
1828- QString toString();
1829- #endif
1830-
1831- Pool<ActiveTouchInfo> m_touchInfoPool;
1832- } m_activeTouches;
1833-
1834- friend class tst_DirectionalDragArea;
1835+ virtual void itemChange(ItemChange change, const ItemChangeData &value);
1836+
1837+public: // so tests can access it
1838+ DirectionalDragAreaPrivate *d;
1839 };
1840
1841 #endif // DIRECTIONAL_DRAG_AREA_H
1842
1843=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea_p.h'
1844--- plugins/Ubuntu/Gestures/DirectionalDragArea_p.h 1970-01-01 00:00:00 +0000
1845+++ plugins/Ubuntu/Gestures/DirectionalDragArea_p.h 2015-04-10 21:17:56 +0000
1846@@ -0,0 +1,167 @@
1847+/*
1848+ * Copyright (C) 2015 Canonical, Ltd.
1849+ *
1850+ * This program is free software; you can redistribute it and/or modify
1851+ * it under the terms of the GNU General Public License as published by
1852+ * the Free Software Foundation; version 3.
1853+ *
1854+ * This program is distributed in the hope that it will be useful,
1855+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1856+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1857+ * GNU General Public License for more details.
1858+ *
1859+ * You should have received a copy of the GNU General Public License
1860+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1861+ */
1862+
1863+#ifndef DIRECTIONAL_DRAG_AREA_PRIV_H
1864+#define DIRECTIONAL_DRAG_AREA_PRIV_H
1865+
1866+// Information about an active touch point
1867+struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
1868+ ActiveTouchInfo() : id(-1), startTime(-1) {}
1869+ bool isValid() const { return id != -1; }
1870+ void reset() { id = -1; }
1871+ int id;
1872+ qint64 startTime;
1873+};
1874+class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
1875+public:
1876+ ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
1877+ void update(QTouchEvent *event);
1878+ qint64 touchStartTime(int id);
1879+ bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
1880+ qint64 mostRecentStartTime();
1881+ UbuntuGestures::SharedTimeSource m_timeSource;
1882+private:
1883+ void addTouchPoint(int touchId);
1884+ void removeTouchPoint(int touchId);
1885+ #if ACTIVETOUCHESINFO_DEBUG
1886+ QString toString();
1887+ #endif
1888+
1889+ Pool<ActiveTouchInfo> m_touchInfoPool;
1890+};
1891+
1892+class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
1893+ Q_OBJECT
1894+
1895+ Q_ENUMS(Status)
1896+public:
1897+ DirectionalDragAreaPrivate(DirectionalDragArea *q);
1898+
1899+public Q_SLOTS:
1900+ void giveUpIfDisabledOrInvisible();
1901+ void rejectGesture();
1902+
1903+public:
1904+ // Describes the state of the directional drag gesture.
1905+ enum Status {
1906+ // Waiting for a new touch point to land on this area. No gesture is being processed
1907+ // or tracked.
1908+ WaitingForTouch,
1909+
1910+ // A touch point has landed on this area but it's not know yet whether it is
1911+ // performing a drag in the correct direction.
1912+ // If it's decided that the touch point is not performing a directional drag gesture,
1913+ // it will be rejected/ignored and status will return to WaitingForTouch.
1914+ Undecided, //Recognizing,
1915+
1916+ // There's a touch point in this area and it performed a drag in the correct
1917+ // direction.
1918+ //
1919+ // Once recognized, the gesture state will move back to WaitingForTouch only once
1920+ // that touch point ends. The gesture will remain in the Recognized state even if
1921+ // the touch point starts moving in other directions or halts.
1922+ Recognized,
1923+ };
1924+
1925+ void touchEvent_absent(QTouchEvent *event);
1926+ void touchEvent_undecided(QTouchEvent *event);
1927+ void touchEvent_recognized(QTouchEvent *event);
1928+ bool movingInRightDirection() const;
1929+ bool movedFarEnoughAlongGestureAxis() const;
1930+ bool isPastMaxDistance() const;
1931+ const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
1932+ void setStatus(Status newStatus);
1933+ void setPublicPos(const QPointF &point);
1934+ void setPublicScenePos(const QPointF &point);
1935+ bool isWithinTouchCompositionWindow();
1936+ void updateSceneDirectionVector();
1937+ // returns the scalar projection between the given vector (in scene coordinates)
1938+ // and m_sceneDirectionVector
1939+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
1940+ void touchOwnershipEvent(TouchOwnershipEvent *event);
1941+ void unownedTouchEvent(UnownedTouchEvent *event);
1942+ void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
1943+ void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
1944+ bool recognitionIsDisabled() const;
1945+ bool sanityCheckRecognitionProperties();
1946+ void updateSceneDistance();
1947+ void setMaxTime(int value);
1948+ void setDistanceThreshold(qreal value);
1949+ void setPixelsPerMm(qreal pixelsPerMm);
1950+ QString objectName() const { return q->objectName(); }
1951+
1952+ // Replaces the existing Timer with the given one.
1953+ //
1954+ // Useful for providing a fake timer when testing.
1955+ void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
1956+
1957+ // Useful for testing, where a fake time source can be supplied
1958+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
1959+
1960+ DirectionalDragArea *q;
1961+
1962+ // The current status of the directional drag gesture area.
1963+ Status status;
1964+
1965+ QPointF startPos;
1966+ QPointF startScenePos;
1967+ qreal sceneDistance;
1968+ int touchId;
1969+
1970+ // The touch position exposed in the public API.
1971+ // It only starts to move once the gesture gets recognized.
1972+ QPointF publicPos;
1973+ QPointF publicScenePos;
1974+
1975+ // A movement damper is used in some of the gesture recognition calculations
1976+ // to get rid of noise or small oscillations in the touch position.
1977+ DampedPointF dampedScenePos;
1978+ QPointF previousDampedScenePos;
1979+
1980+ // Unit vector in scene coordinates describing the direction of the gesture recognition
1981+ QPointF sceneDirectionVector;
1982+
1983+ Direction::Type direction;
1984+
1985+ // How far a touch point has to move from its initial position along the gesture axis in order
1986+ // for it to be recognized as a directional drag.
1987+ qreal distanceThreshold;
1988+ qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
1989+
1990+ // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
1991+ int maxTime;
1992+
1993+ // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
1994+ qreal maxDistance;
1995+
1996+ // Maximum time (in milliseconds) after the start of a given touch point where
1997+ // subsequent touch starts are grouped with the first one into an N-touches gesture
1998+ // (e.g. a two-fingers tap or drag).
1999+ int compositionTime;
2000+
2001+ bool immediateRecognition;
2002+
2003+ UbuntuGestures::AbstractTimer *recognitionTimer;
2004+
2005+ UbuntuGestures::SharedTimeSource timeSource;
2006+
2007+ ActiveTouchesInfo activeTouches;
2008+
2009+Q_SIGNALS:
2010+ void statusChanged(Status value);
2011+};
2012+
2013+#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
2014
2015=== modified file 'plugins/Ubuntu/Gestures/TouchDispatcher.cpp'
2016--- plugins/Ubuntu/Gestures/TouchDispatcher.cpp 2014-11-03 15:21:46 +0000
2017+++ plugins/Ubuntu/Gestures/TouchDispatcher.cpp 2015-04-10 21:17:56 +0000
2018@@ -1,5 +1,5 @@
2019 /*
2020- * Copyright (C) 2014 Canonical, Ltd.
2021+ * Copyright (C) 2014-2015 Canonical, Ltd.
2022 *
2023 * This program is free software; you can redistribute it and/or modify
2024 * it under the terms of the GNU General Public License as published by
2025@@ -28,8 +28,11 @@
2026 #define TOUCHDISPATCHER_DEBUG 0
2027
2028 #if TOUCHDISPATCHER_DEBUG
2029+#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
2030 #include <DebugHelpers.h>
2031-#endif
2032+#else // TOUCHDISPATCHER_DEBUG
2033+#define ugDebug(params) ((void)0)
2034+#endif // TOUCHDISPATCHER_DEBUG
2035
2036 TouchDispatcher::TouchDispatcher()
2037 : m_status(NoActiveTouch)
2038@@ -44,13 +47,12 @@
2039 m_targetItem = target;
2040 if (m_status != NoActiveTouch) {
2041 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
2042- m_status = TargetRejectedTouches;
2043+ setStatus(TargetRejectedTouches);
2044 }
2045 }
2046 }
2047
2048-void TouchDispatcher::dispatch(QEvent::Type eventType,
2049- QTouchDevice *device,
2050+void TouchDispatcher::dispatch(QTouchDevice *device,
2051 Qt::KeyboardModifiers modifiers,
2052 const QList<QTouchEvent::TouchPoint> &touchPoints,
2053 QWindow *window,
2054@@ -61,6 +63,8 @@
2055 return;
2056 }
2057
2058+ QEvent::Type eventType = resolveEventType(touchPoints);
2059+
2060 if (eventType == QEvent::TouchBegin) {
2061 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
2062
2063@@ -72,15 +76,13 @@
2064 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
2065 } else {
2066 Q_ASSERT(m_status == TargetRejectedTouches);
2067- #if TOUCHDISPATCHER_DEBUG
2068- qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
2069- << "because it already rejected the touch stream.";
2070- #endif
2071+ ugDebug("Not dispatching touch event to " << m_targetItem.data()
2072+ << "because it already rejected the touch stream.");
2073 // Do nothing
2074 }
2075
2076 if (eventType == QEvent::TouchEnd) {
2077- m_status = NoActiveTouch;
2078+ setStatus(NoActiveTouch);
2079 m_touchMouseId = -1;
2080 }
2081
2082@@ -103,10 +105,7 @@
2083 QQuickItem *targetItem = m_targetItem.data();
2084
2085 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
2086- #if TOUCHDISPATCHER_DEBUG
2087- qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
2088- << "because it's disabled or invisible.";
2089- #endif
2090+ ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
2091 return;
2092 }
2093
2094@@ -118,62 +117,46 @@
2095 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
2096
2097
2098- #if TOUCHDISPATCHER_DEBUG
2099- qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
2100- << "to" << targetItem;
2101- #endif
2102+ ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
2103+ << " to " << targetItem);
2104 QCoreApplication::sendEvent(targetItem, touchEvent.data());
2105
2106
2107 if (touchEvent->isAccepted()) {
2108- #if TOUCHDISPATCHER_DEBUG
2109- qDebug() << "[TouchDispatcher] Item accepted the touch event.";
2110- #endif
2111- m_status = DeliveringTouchEvents;
2112+ ugDebug("Item accepted the touch event.");
2113+ setStatus(DeliveringTouchEvents);
2114 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
2115- #if TOUCHDISPATCHER_DEBUG
2116- qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
2117- #endif
2118+ ugDebug("Item rejected the touch event. Trying a QMouseEvent");
2119 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
2120 QScopedPointer<QMouseEvent> mouseEvent(
2121 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
2122 modifiers, false /* transformNeeded */));
2123 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
2124
2125- #if TOUCHDISPATCHER_DEBUG
2126- qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
2127- << "to" << m_targetItem.data();
2128- #endif
2129+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
2130+ << " to " << m_targetItem.data());
2131 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
2132 if (mouseEvent->isAccepted()) {
2133- #if TOUCHDISPATCHER_DEBUG
2134- qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
2135- #endif
2136- m_status = DeliveringMouseEvents;
2137+ ugDebug("Item accepted the QMouseEvent.");
2138+ setStatus(DeliveringMouseEvents);
2139 m_touchMouseId = targetTouchPoints.at(0).id();
2140
2141 if (checkIfDoubleClicked(timestamp)) {
2142 QScopedPointer<QMouseEvent> doubleClickEvent(
2143 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
2144 modifiers, false /* transformNeeded */));
2145- #if TOUCHDISPATCHER_DEBUG
2146- qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
2147- << "to" << m_targetItem.data();
2148- #endif
2149+ ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
2150+ << " to " << m_targetItem.data());
2151 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
2152 }
2153
2154 } else {
2155- #if TOUCHDISPATCHER_DEBUG
2156- qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
2157- #endif
2158- m_status = TargetRejectedTouches;
2159+ ugDebug("Item rejected the QMouseEvent.");
2160+ setStatus(TargetRejectedTouches);
2161 }
2162 } else {
2163- #if TOUCHDISPATCHER_DEBUG
2164- qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
2165- #endif
2166- m_status = TargetRejectedTouches;
2167+ ugDebug("Item rejected the touch event and does not accept mouse buttons.");
2168+ setStatus(TargetRejectedTouches);
2169 }
2170 }
2171
2172@@ -194,10 +177,8 @@
2173 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
2174
2175
2176- #if TOUCHDISPATCHER_DEBUG
2177- qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
2178- << "to" << targetItem;
2179- #endif
2180+ ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
2181+ << " to " << targetItem);
2182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
2183 }
2184
2185@@ -254,10 +235,8 @@
2186 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
2187 true /* transformNeeded */));
2188
2189- #if TOUCHDISPATCHER_DEBUG
2190- qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
2191- << "to" << m_targetItem.data();
2192- #endif
2193+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
2194+ << " to " << m_targetItem.data());
2195 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
2196 }
2197 }
2198@@ -365,3 +344,59 @@
2199
2200 return doubleClicked;
2201 }
2202+
2203+void TouchDispatcher::setStatus(Status status)
2204+{
2205+ if (status != m_status) {
2206+ #if TOUCHDISPATCHER_DEBUG
2207+ switch (status) {
2208+ case NoActiveTouch:
2209+ ugDebug("status = NoActiveTouch");
2210+ break;
2211+ case DeliveringTouchEvents:
2212+ ugDebug("status = DeliveringTouchEvents");
2213+ break;
2214+ case DeliveringMouseEvents:
2215+ ugDebug("status = DeliveringMouseEvents");
2216+ break;
2217+ case TargetRejectedTouches:
2218+ ugDebug("status = TargetRejectedTouches");
2219+ break;
2220+ default:
2221+ ugDebug("status = " << status);
2222+ break;
2223+ }
2224+ #endif
2225+ m_status = status;
2226+ }
2227+}
2228+
2229+void TouchDispatcher::reset()
2230+{
2231+ setStatus(NoActiveTouch);
2232+ m_touchMouseId = -1;
2233+ m_touchMousePressTimestamp =0;
2234+}
2235+
2236+QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
2237+{
2238+ QEvent::Type eventType;
2239+
2240+ Qt::TouchPointStates eventStates = 0;
2241+ for (int i = 0; i < touchPoints.count(); i++)
2242+ eventStates |= touchPoints[i].state();
2243+
2244+ switch (eventStates) {
2245+ case Qt::TouchPointPressed:
2246+ eventType = QEvent::TouchBegin;
2247+ break;
2248+ case Qt::TouchPointReleased:
2249+ eventType = QEvent::TouchEnd;
2250+ break;
2251+ default:
2252+ eventType = QEvent::TouchUpdate;
2253+ break;
2254+ }
2255+
2256+ return eventType;
2257+}
2258
2259=== modified file 'plugins/Ubuntu/Gestures/TouchDispatcher.h'
2260--- plugins/Ubuntu/Gestures/TouchDispatcher.h 2014-11-03 15:21:46 +0000
2261+++ plugins/Ubuntu/Gestures/TouchDispatcher.h 2015-04-10 21:17:56 +0000
2262@@ -1,5 +1,5 @@
2263 /*
2264- * Copyright (C) 2014 Canonical, Ltd.
2265+ * Copyright (C) 2014-2015 Canonical, Ltd.
2266 *
2267 * This program is free software; you can redistribute it and/or modify
2268 * it under the terms of the GNU General Public License as published by
2269@@ -36,12 +36,20 @@
2270 void setTargetItem(QQuickItem *target);
2271 QQuickItem *targetItem() { return m_targetItem; }
2272
2273- void dispatch(QEvent::Type eventType,
2274- QTouchDevice *device,
2275+ void dispatch(QTouchDevice *device,
2276 Qt::KeyboardModifiers modifiers,
2277 const QList<QTouchEvent::TouchPoint> &touchPoints,
2278 QWindow *window,
2279 ulong timestamp);
2280+
2281+ void reset();
2282+
2283+ enum Status {
2284+ NoActiveTouch,
2285+ DeliveringTouchEvents,
2286+ DeliveringMouseEvents,
2287+ TargetRejectedTouches
2288+ };
2289 private:
2290 void dispatchTouchBegin(
2291 QTouchDevice *device,
2292@@ -73,14 +81,13 @@
2293
2294 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
2295
2296+ void setStatus(Status status);
2297+
2298+ static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
2299+
2300 QPointer<QQuickItem> m_targetItem;
2301
2302- enum {
2303- NoActiveTouch,
2304- DeliveringTouchEvents,
2305- DeliveringMouseEvents,
2306- TargetRejectedTouches
2307- } m_status;
2308+ Status m_status;
2309
2310 int m_touchMouseId;
2311 ulong m_touchMousePressTimestamp;
2312
2313=== modified file 'plugins/Ubuntu/Gestures/TouchGate.cpp'
2314--- plugins/Ubuntu/Gestures/TouchGate.cpp 2015-01-28 12:59:21 +0000
2315+++ plugins/Ubuntu/Gestures/TouchGate.cpp 2015-04-10 21:17:56 +0000
2316@@ -1,5 +1,5 @@
2317 /*
2318- * Copyright (C) 2014 Canonical, Ltd.
2319+ * Copyright (C) 2014-2015 Canonical, Ltd.
2320 *
2321 * This program is free software; you can redistribute it and/or modify
2322 * it under the terms of the GNU General Public License as published by
2323@@ -23,8 +23,18 @@
2324 #include <TouchRegistry.h>
2325
2326 #if TOUCHGATE_DEBUG
2327+#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
2328 #include <DebugHelpers.h>
2329-#endif
2330+#else // TOUCHGATE_DEBUG
2331+#define ugDebug(params) ((void)0)
2332+#endif // TOUCHGATE_DEBUG
2333+
2334+TouchGate::TouchGate(QQuickItem *parent)
2335+ : QQuickItem(parent)
2336+{
2337+ connect(this, &QQuickItem::enabledChanged,
2338+ this, &TouchGate::onEnabledChanged);
2339+}
2340
2341 bool TouchGate::event(QEvent *e)
2342 {
2343@@ -38,13 +48,12 @@
2344
2345 void TouchGate::touchEvent(QTouchEvent *event)
2346 {
2347- #if TOUCHGATE_DEBUG
2348- qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
2349- #endif
2350+ ugDebug("got touch event" << qPrintable(touchEventToString(event)));
2351 event->accept();
2352
2353 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
2354- bool goodToGo = true;
2355+ QList<QTouchEvent::TouchPoint> validTouchPoints;
2356+ bool ownsAllTouches = true;
2357 for (int i = 0; i < touchPoints.count(); ++i) {
2358 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
2359
2360@@ -57,31 +66,41 @@
2361 Q_EMIT pressed();
2362 }
2363
2364- goodToGo &= m_touchInfoMap.contains(touchPoint.id())
2365- && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
2366-
2367- if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {
2368- m_touchInfoMap[touchPoint.id()].ended = true;
2369+ if (m_touchInfoMap.contains(touchPoint.id())) {
2370+ validTouchPoints.append(touchPoint);
2371+
2372+ ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
2373+
2374+ if (touchPoint.state() == Qt::TouchPointReleased) {
2375+ m_touchInfoMap[touchPoint.id()].ended = true;
2376+ }
2377 }
2378
2379 }
2380
2381- if (goodToGo) {
2382+ if (validTouchPoints.isEmpty()) {
2383+ // nothing to do.
2384+ return;
2385+ }
2386+
2387+ if (ownsAllTouches) {
2388 if (m_storedEvents.isEmpty()) {
2389 // let it pass through
2390- dispatchTouchEventToTarget(event);
2391+ removeTouchInfoForEndedTouches(validTouchPoints);
2392+ m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
2393+ event->window(), event->timestamp());
2394 } else {
2395 // Retain the event to ensure TouchGate dispatches them in order.
2396 // Otherwise the current event would come before the stored ones, which are older.
2397- #if TOUCHGATE_DEBUG
2398- qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",
2399- qPrintable(oldestPendingTouchIdsString()));
2400- #endif
2401- storeTouchEvent(event);
2402+ ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
2403+ << " are still pending ownership.");
2404+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
2405+ event->window(), event->timestamp());
2406 }
2407 } else {
2408 // Retain events that have unowned touches
2409- storeTouchEvent(event);
2410+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
2411+ event->window(), event->timestamp());
2412 }
2413 }
2414
2415@@ -90,24 +109,23 @@
2416 // TODO: Optimization: batch those actions as TouchOwnershipEvents
2417 // might come one right after the other.
2418
2419- Q_ASSERT(m_touchInfoMap.contains(event->touchId()));
2420-
2421- TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
2422-
2423- if (event->gained()) {
2424- #if TOUCHGATE_DEBUG
2425- qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();
2426- #endif
2427- touchInfo.ownership = OwnershipGranted;
2428+ if (m_touchInfoMap.contains(event->touchId())) {
2429+ TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
2430+
2431+ if (event->gained()) {
2432+ ugDebug("Got ownership of touch " << event->touchId());
2433+ touchInfo.ownership = OwnershipGranted;
2434+ } else {
2435+ ugDebug("Lost ownership of touch " << event->touchId());
2436+ m_touchInfoMap.remove(event->touchId());
2437+ removeTouchFromStoredEvents(event->touchId());
2438+ }
2439+
2440+ dispatchFullyOwnedEvents();
2441 } else {
2442- #if TOUCHGATE_DEBUG
2443- qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();
2444- #endif
2445- m_touchInfoMap.remove(event->touchId());
2446- removeTouchFromStoredEvents(event->touchId());
2447+ // Ignore it. It probably happened because the TouchGate got disabled
2448+ // between the time it requested ownership and the time it got it.
2449 }
2450-
2451- dispatchFullyOwnedEvents();
2452 }
2453
2454 bool TouchGate::isTouchPointOwned(int touchId) const
2455@@ -115,14 +133,15 @@
2456 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
2457 }
2458
2459-void TouchGate::storeTouchEvent(const QTouchEvent *event)
2460+void TouchGate::storeTouchEvent(QTouchDevice *device,
2461+ Qt::KeyboardModifiers modifiers,
2462+ const QList<QTouchEvent::TouchPoint> &touchPoints,
2463+ QWindow *window,
2464+ ulong timestamp)
2465 {
2466- #if TOUCHGATE_DEBUG
2467- qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));
2468- #endif
2469-
2470- TouchEvent clonedEvent(event);
2471- m_storedEvents.append(std::move(clonedEvent));
2472+ ugDebug("Storing" << touchPoints);
2473+ TouchEvent event(device, modifiers, touchPoints, window, timestamp);
2474+ m_storedEvents.append(std::move(event));
2475 }
2476
2477 void TouchGate::removeTouchFromStoredEvents(int touchId)
2478@@ -195,25 +214,13 @@
2479 void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
2480 {
2481 removeTouchInfoForEndedTouches(event.touchPoints);
2482- m_dispatcher.dispatch(event.eventType,
2483- event.device,
2484+ m_dispatcher.dispatch(event.device,
2485 event.modifiers,
2486 event.touchPoints,
2487 event.window,
2488 event.timestamp);
2489 }
2490
2491-void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
2492-{
2493- removeTouchInfoForEndedTouches(event->touchPoints());
2494- m_dispatcher.dispatch(event->type(),
2495- event->device(),
2496- event->modifiers(),
2497- event->touchPoints(),
2498- event->window(),
2499- event->timestamp());
2500-}
2501-
2502 void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
2503 {
2504 for (int i = 0; i < touchPoints.size(); ++i) {\
2505@@ -228,14 +235,31 @@
2506 }
2507 }
2508
2509-TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
2510- : eventType(event->type())
2511- , device(event->device())
2512- , modifiers(event->modifiers())
2513- , touchPoints(event->touchPoints())
2514- , target(qobject_cast<QQuickItem*>(event->target()))
2515- , window(event->window())
2516- , timestamp(event->timestamp())
2517+void TouchGate::onEnabledChanged()
2518+{
2519+ ugDebug(" enabled = " << isEnabled());
2520+ if (!isEnabled()) {
2521+ reset();
2522+ }
2523+}
2524+
2525+void TouchGate::reset()
2526+{
2527+ m_storedEvents.clear();
2528+ m_touchInfoMap.clear();
2529+ m_dispatcher.reset();
2530+}
2531+
2532+TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
2533+ Qt::KeyboardModifiers modifiers,
2534+ const QList<QTouchEvent::TouchPoint> &touchPoints,
2535+ QWindow *window,
2536+ ulong timestamp)
2537+ : device(device)
2538+ , modifiers(modifiers)
2539+ , touchPoints(touchPoints)
2540+ , window(window)
2541+ , timestamp(timestamp)
2542 {
2543 }
2544
2545
2546=== modified file 'plugins/Ubuntu/Gestures/TouchGate.h'
2547--- plugins/Ubuntu/Gestures/TouchGate.h 2015-01-28 12:59:21 +0000
2548+++ plugins/Ubuntu/Gestures/TouchGate.h 2015-04-10 21:17:56 +0000
2549@@ -45,6 +45,8 @@
2550 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
2551
2552 public:
2553+ TouchGate(QQuickItem *parent = nullptr);
2554+
2555 bool event(QEvent *e) override;
2556
2557 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
2558@@ -57,31 +59,42 @@
2559
2560 protected:
2561 void touchEvent(QTouchEvent *event) override;
2562+
2563+private Q_SLOTS:
2564+ void onEnabledChanged();
2565+
2566 private:
2567+ void reset();
2568+
2569 class TouchEvent {
2570 public:
2571- TouchEvent(const QTouchEvent *event);
2572+ TouchEvent(QTouchDevice *device,
2573+ Qt::KeyboardModifiers modifiers,
2574+ const QList<QTouchEvent::TouchPoint> &touchPoints,
2575+ QWindow *window,
2576+ ulong timestamp);
2577
2578 bool removeTouch(int touchId);
2579
2580- QEvent::Type eventType;
2581 QTouchDevice *device;
2582 Qt::KeyboardModifiers modifiers;
2583 QList<QTouchEvent::TouchPoint> touchPoints;
2584- QQuickItem *target;
2585 QWindow *window;
2586 ulong timestamp;
2587 };
2588
2589 void touchOwnershipEvent(TouchOwnershipEvent *event);
2590 bool isTouchPointOwned(int touchId) const;
2591- void storeTouchEvent(const QTouchEvent *event);
2592+ void storeTouchEvent(QTouchDevice *device,
2593+ Qt::KeyboardModifiers modifiers,
2594+ const QList<QTouchEvent::TouchPoint> &touchPoints,
2595+ QWindow *window,
2596+ ulong timestamp);
2597 void removeTouchFromStoredEvents(int touchId);
2598 void dispatchFullyOwnedEvents();
2599 bool eventIsFullyOwned(const TouchEvent &event) const;
2600
2601 void dispatchTouchEventToTarget(const TouchEvent &event);
2602- void dispatchTouchEventToTarget(QTouchEvent* event);
2603
2604 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
2605
2606
2607=== modified file 'qml/Components/DragHandle.qml'
2608--- qml/Components/DragHandle.qml 2014-12-05 17:06:36 +0000
2609+++ qml/Components/DragHandle.qml 2015-04-10 21:17:56 +0000
2610@@ -43,16 +43,8 @@
2611 }
2612
2613 */
2614-EdgeDragArea {
2615+DirectionalDragArea {
2616 id: dragArea
2617- objectName: "dragHandle"
2618-
2619- // Disable gesture recognition by default when hinting is used as
2620- // it conflicts with the hinting idea.
2621- distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
2622- maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
2623- maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
2624- compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
2625
2626 property bool stretch: false
2627
2628@@ -68,6 +60,9 @@
2629 }
2630
2631 property real hintDisplacement: 0
2632+
2633+ immediateRecognition: hintDisplacement > 0
2634+
2635 property var overrideStartValue: undefined
2636 SmoothedAnimation {
2637 id: hintingAnimation
2638@@ -98,7 +93,6 @@
2639 // Private stuff
2640 QtObject {
2641 id: d
2642- property var previousStatus: DirectionalDragArea.WaitingForTouch
2643 property real startValue
2644 property real minValue: {
2645 if (direction == Direction.Horizontal) {
2646@@ -183,7 +177,7 @@
2647 }
2648
2649 onDistanceChanged: {
2650- if (status === DirectionalDragArea.Recognized) {
2651+ if (dragging) {
2652 // don't go the whole distance in order to smooth out the movement
2653 var step = distance * 0.3;
2654
2655@@ -193,31 +187,22 @@
2656 }
2657 }
2658
2659- onStatusChanged: {
2660- if (status === DirectionalDragArea.WaitingForTouch) {
2661+ onDraggingChanged: {
2662+ if (dragging) {
2663+ dragEvaluator.reset();
2664+ if (overrideStartValue !== undefined) {
2665+ d.startValue = overrideStartValue;
2666+ } else {
2667+ d.startValue = parent[d.targetProp];
2668+ }
2669+
2670+ if (hintDisplacement > 0) {
2671+ hintingAnimation.targetValue = d.startValue;
2672+ hintingAnimation.start();
2673+ }
2674+ } else {
2675 hintingAnimation.stop();
2676- if (d.previousStatus === DirectionalDragArea.Recognized) {
2677- d.onFinishedRecognizedGesture();
2678- } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
2679- // Gesture was rejected.
2680- d.rollbackDrag();
2681- }
2682- } else /* Undecided || Recognized */ {
2683- if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
2684- dragEvaluator.reset();
2685- if (overrideStartValue !== undefined) {
2686- d.startValue = overrideStartValue;
2687- } else {
2688- d.startValue = parent[d.targetProp];
2689- }
2690-
2691- if (hintDisplacement > 0) {
2692- hintingAnimation.targetValue = d.startValue;
2693- hintingAnimation.start();
2694- }
2695- }
2696+ d.onFinishedRecognizedGesture();
2697 }
2698-
2699- d.previousStatus = status;
2700 }
2701 }
2702
2703=== removed file 'qml/Components/EdgeDragArea.qml'
2704--- qml/Components/EdgeDragArea.qml 2014-10-01 13:20:32 +0000
2705+++ qml/Components/EdgeDragArea.qml 1970-01-01 00:00:00 +0000
2706@@ -1,46 +0,0 @@
2707-/*
2708- * Copyright (C) 2013 Canonical, Ltd.
2709- *
2710- * This program is free software; you can redistribute it and/or modify
2711- * it under the terms of the GNU General Public License as published by
2712- * the Free Software Foundation; version 3.
2713- *
2714- * This program is distributed in the hope that it will be useful,
2715- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2716- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2717- * GNU General Public License for more details.
2718- *
2719- * You should have received a copy of the GNU General Public License
2720- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2721- */
2722-
2723-import QtQuick 2.0
2724-import Ubuntu.Components 0.1
2725-import Ubuntu.Gestures 0.1
2726-
2727-/*
2728- A DirectionalDragArea wrapper that provides some well-chosen defaults
2729- for its gesture recognition parameters.
2730-*/
2731-DirectionalDragArea {
2732-
2733- // TODO: Re-evaluate those or even the recognition heuristics itself once
2734- // we have gesture cancelling/forwarding in place.
2735- //
2736- // The idea here is that it's better having lax rules than false negatives.
2737- // False negatives are very frustrating to the user.
2738- maxDeviation: defaultMaxDeviation
2739- wideningAngle: defaultWideningAngle
2740- distanceThreshold: defaultDistanceThreshold
2741- minSpeed: defaultMinSpeed
2742- maxSilenceTime: defaultMaxSilenceTime
2743- compositionTime: defaultCompositionTime
2744-
2745- readonly property real defaultMaxDeviation: units.gu(3)
2746- readonly property real defaultWideningAngle: 50
2747- readonly property real defaultDistanceThreshold: units.gu(1.5)
2748- // some people were getting false negatives with it enabled.
2749- readonly property real defaultMinSpeed: units.gu(0)
2750- readonly property int defaultMaxSilenceTime: 200
2751- readonly property int defaultCompositionTime: 60
2752-}
2753
2754=== modified file 'qml/Dash/Dash.qml'
2755--- qml/Dash/Dash.qml 2015-02-17 08:26:20 +0000
2756+++ qml/Dash/Dash.qml 2015-04-10 21:17:56 +0000
2757@@ -140,7 +140,7 @@
2758 // (as expected) but would also cause the dash content flickable to move a bit, because
2759 // that flickable was getting the touch events while overviewDragHandle was still undecided
2760 // about whether that touch was indeed performing a directional drag gesture.
2761- forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
2762+ forceNonInteractive: overviewDragHandle.dragging
2763
2764 enabled: bottomEdgeController.progress == 0
2765 }
2766@@ -317,7 +317,7 @@
2767 }
2768 }
2769
2770- EdgeDragArea {
2771+ DirectionalDragArea {
2772 id: overviewDragHandle
2773 objectName: "overviewDragHandle"
2774 z: 1
2775@@ -333,21 +333,14 @@
2776 height: units.gu(2)
2777
2778 onSceneDistanceChanged: {
2779- if (status == DirectionalDragArea.Recognized) {
2780+ if (dragging) {
2781 bottomEdgeController.enableAnimation = false;
2782 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
2783 }
2784 }
2785
2786- property int previousStatus: -1
2787- property int currentStatus: DirectionalDragArea.WaitingForTouch
2788-
2789- onStatusChanged: {
2790- previousStatus = currentStatus;
2791- currentStatus = status;
2792-
2793- if (status == DirectionalDragArea.WaitingForTouch &&
2794- previousStatus == DirectionalDragArea.Recognized) {
2795+ onDraggingChanged: {
2796+ if (!dragging) {
2797 bottomEdgeController.enableAnimation = true;
2798 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
2799 }
2800
2801=== modified file 'qml/Greeter/CoverPage.qml'
2802--- qml/Greeter/CoverPage.qml 2015-02-17 15:54:17 +0000
2803+++ qml/Greeter/CoverPage.qml 2015-04-10 21:17:56 +0000
2804@@ -129,6 +129,7 @@
2805
2806 DragHandle {
2807 id: dragHandle
2808+ objectName: "coverPageDragHandle"
2809 anchors.fill: parent
2810 anchors.leftMargin: root.dragHandleLeftMargin
2811 enabled: root.draggable
2812
2813=== modified file 'qml/Launcher/Launcher.qml'
2814--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
2815+++ qml/Launcher/Launcher.qml 2015-04-10 21:17:56 +0000
2816@@ -210,7 +210,7 @@
2817 bottom: parent.bottom
2818 }
2819 x: -width
2820- visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
2821+ visible: root.x > 0 || x > -width
2822 model: LauncherModel
2823
2824 property bool animate: true
2825@@ -272,7 +272,7 @@
2826 }
2827 }
2828
2829- EdgeDragArea {
2830+ DirectionalDragArea {
2831 id: dragArea
2832 objectName: "launcherDragArea"
2833
2834@@ -284,19 +284,10 @@
2835 height: root.height
2836
2837 onTouchXChanged: {
2838- if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
2839+ if (!dragging || launcher.state == "visible")
2840 return;
2841
2842- // When the gesture finally gets recognized, the finger will likely be
2843- // reasonably far from the edge. If we made the panel immediately
2844- // follow the finger position it would be visually unpleasant as it
2845- // would appear right next to the user's finger out of nowhere.
2846- // Instead, we make the panel go towards the user's finger in several
2847- // steps. ie., in an animated way.
2848- var targetPanelX = Math.min(0, touchX - panel.width) - root.x
2849- var delta = targetPanelX - panel.x
2850- // the trick is not to go all the way (1.0) as it would cause a sudden jump
2851- panel.x += 0.4 * delta
2852+ panel.x = Math.min(0, touchX - panel.width) - root.x
2853 }
2854
2855 onDraggingChanged: {
2856@@ -306,7 +297,8 @@
2857 if (distance > minimizeDistance) {
2858 root.dash();
2859 }
2860- } else {
2861+ } else if (root.state === "") {
2862+ // didn't drag far enough. rollback
2863 root.switchToNextState("")
2864 }
2865 }
2866
2867=== modified file 'qml/Panel/IndicatorsMenu.qml'
2868--- qml/Panel/IndicatorsMenu.qml 2015-03-18 10:17:21 +0000
2869+++ qml/Panel/IndicatorsMenu.qml 2015-04-10 21:17:56 +0000
2870@@ -182,9 +182,18 @@
2871 enabled: !root.shown && root.available
2872 autoCompleteDragThreshold: maxTotalDragDistance / 2
2873 stretch: true
2874- distanceThreshold: enableHint ? 0 : minimizedPanelHeight
2875
2876- onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
2877+ onDraggingChanged: {
2878+ if (dragging) {
2879+ touchPressTime = new Date().getTime();
2880+ } else {
2881+ var touchReleaseTime = new Date().getTime();
2882+ if (touchReleaseTime - touchPressTime <= 300) {
2883+ root.showTapped(Qt.point(touchSceneX, touchSceneY));
2884+ }
2885+ }
2886+ }
2887+ property var touchPressTime
2888
2889 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
2890 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
2891@@ -200,6 +209,7 @@
2892
2893 DragHandle {
2894 id: __hideDragHandle
2895+ objectName: "hideDragHandle"
2896 anchors.fill: handle
2897 direction: Direction.Upwards
2898 enabled: root.shown && root.available
2899
2900=== modified file 'qml/Stages/PhoneStage.qml'
2901--- qml/Stages/PhoneStage.qml 2015-02-18 18:29:03 +0000
2902+++ qml/Stages/PhoneStage.qml 2015-04-10 21:17:56 +0000
2903@@ -146,14 +146,13 @@
2904 id: spreadView
2905 objectName: "spreadView"
2906 anchors.fill: parent
2907- interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
2908- && draggedDelegateCount === 0
2909+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
2910 contentWidth: spreadRow.width - shift
2911 contentX: -shift
2912
2913 // This indicates when the spreadView is active. That means, all the animations
2914 // are activated and tiles need to line up for the spread.
2915- readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
2916+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
2917
2918 // The flickable needs to fill the screen in order to get touch events all over.
2919 // However, we don't want to the user to be able to scroll back all the way. For
2920@@ -450,7 +449,7 @@
2921 }
2922 }
2923
2924- EdgeDragArea {
2925+ DirectionalDragArea {
2926 id: spreadDragArea
2927 objectName: "spreadDragArea"
2928 direction: Direction.Leftwards
2929@@ -462,59 +461,50 @@
2930 property var gesturePoints: new Array()
2931
2932 onTouchXChanged: {
2933- if (!dragging) {
2934- // Initial touch. Let's reset the spreadView to the starting position.
2935- spreadView.phase = 0;
2936- spreadView.contentX = -spreadView.shift;
2937- }
2938- if (dragging && status == DirectionalDragArea.Recognized) {
2939+ if (dragging) {
2940 // Gesture recognized. Let's move the spreadView with the finger
2941 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
2942 dragX = -dragX + spreadDragArea.width - spreadView.shift;
2943 // Don't allow dragging further than the animation crossing with phase2's animation
2944 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
2945+
2946 spreadView.contentX = Math.min(dragX, maxMovement);
2947+ } else {
2948+ // Initial touch. Let's reset the spreadView to the starting position.
2949+ spreadView.phase = 0;
2950+ spreadView.contentX = -spreadView.shift;
2951 }
2952+
2953 gesturePoints.push(touchX);
2954 }
2955
2956- property int previousStatus: -1
2957- property int currentStatus: DirectionalDragArea.WaitingForTouch
2958-
2959- onStatusChanged: {
2960- previousStatus = currentStatus;
2961- currentStatus = status;
2962- }
2963-
2964 onDraggingChanged: {
2965 if (dragging) {
2966 // A potential edge-drag gesture has started. Start recording it
2967 gesturePoints = [];
2968- return;
2969- }
2970-
2971- // Ok. The user released. Find out if it was a one-way movement.
2972- var oneWayFlick = true;
2973- var smallestX = spreadDragArea.width;
2974- for (var i = 0; i < gesturePoints.length; i++) {
2975- if (gesturePoints[i] >= smallestX) {
2976- oneWayFlick = false;
2977- break;
2978- }
2979- smallestX = gesturePoints[i];
2980- }
2981- gesturePoints = [];
2982-
2983- if (previousStatus == DirectionalDragArea.Recognized &&
2984- oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
2985- spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
2986- // If it was a short one-way movement, do the Alt+Tab switch
2987- // no matter if we didn't cross positionMarker1 yet.
2988- spreadView.snapTo(1);
2989- } else if (!dragging) {
2990- // otherwise snap to the closest snap position we can find
2991- // (might be back to start, to app 1 or to spread)
2992- spreadView.snap();
2993+ } else {
2994+ // Ok. The user released. Find out if it was a one-way movement.
2995+ var oneWayFlick = true;
2996+ var smallestX = spreadDragArea.width;
2997+ for (var i = 0; i < gesturePoints.length; i++) {
2998+ if (gesturePoints[i] >= smallestX) {
2999+ oneWayFlick = false;
3000+ break;
3001+ }
3002+ smallestX = gesturePoints[i];
3003+ }
3004+ gesturePoints = [];
3005+
3006+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
3007+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
3008+ // If it was a short one-way movement, do the Alt+Tab switch
3009+ // no matter if we didn't cross positionMarker1 yet.
3010+ spreadView.snapTo(1);
3011+ } else if (!dragging) {
3012+ // otherwise snap to the closest snap position we can find
3013+ // (might be back to start, to app 1 or to spread)
3014+ spreadView.snap();
3015+ }
3016 }
3017 }
3018 }
3019
3020=== modified file 'qml/Stages/TabletStage.qml'
3021--- qml/Stages/TabletStage.qml 2015-02-02 16:28:03 +0000
3022+++ qml/Stages/TabletStage.qml 2015-04-10 21:17:56 +0000
3023@@ -170,8 +170,7 @@
3024 Flickable {
3025 id: spreadView
3026 anchors.fill: parent
3027- interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
3028- && draggedDelegateCount === 0
3029+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
3030 contentWidth: spreadRow.width - shift
3031 contentX: -shift
3032
3033@@ -598,7 +597,7 @@
3034 }
3035 }
3036
3037- EdgeDragArea {
3038+ DirectionalDragArea {
3039 id: spreadDragArea
3040 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
3041 width: root.dragAreaWidth
3042@@ -624,26 +623,25 @@
3043 if (dragging) {
3044 // Gesture recognized. Start recording this gesture
3045 gesturePoints = [];
3046- return;
3047- }
3048-
3049- // Ok. The user released. Find out if it was a one-way movement.
3050- var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
3051- gesturePoints = [];
3052-
3053- if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
3054- // If it was a short one-way movement, do the Alt+Tab switch
3055- // no matter if we didn't cross positionMarker1 yet.
3056- spreadView.snapTo(spreadView.nextInStack);
3057- } else if (!dragging) {
3058- if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
3059- spreadView.snap();
3060- } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
3061+ } else {
3062+ // Ok. The user released. Find out if it was a one-way movement.
3063+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
3064+ gesturePoints = [];
3065+
3066+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
3067+ // If it was a short one-way movement, do the Alt+Tab switch
3068+ // no matter if we didn't cross positionMarker1 yet.
3069 spreadView.snapTo(spreadView.nextInStack);
3070 } else {
3071- // otherwise snap to the closest snap position we can find
3072- // (might be back to start, to app 1 or to spread)
3073- spreadView.snap();
3074+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
3075+ spreadView.snap();
3076+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
3077+ spreadView.snapTo(spreadView.nextInStack);
3078+ } else {
3079+ // otherwise snap to the closest snap position we can find
3080+ // (might be back to start, to app 1 or to spread)
3081+ spreadView.snap();
3082+ }
3083 }
3084 }
3085 }
3086
3087=== modified file 'qml/Tutorial/TutorialBottom.qml'
3088--- qml/Tutorial/TutorialBottom.qml 2015-02-01 22:39:25 +0000
3089+++ qml/Tutorial/TutorialBottom.qml 2015-04-10 21:17:56 +0000
3090@@ -73,7 +73,7 @@
3091 ]
3092 }
3093
3094- EdgeDragArea {
3095+ DirectionalDragArea {
3096 id: dragArea
3097 direction: Direction.Upwards
3098 anchors {
3099
3100=== modified file 'tests/libs/UbuntuGestures/tst_TouchRegistry.cpp'
3101--- tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2014-10-01 13:20:32 +0000
3102+++ tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2015-04-10 21:17:56 +0000
3103@@ -1,5 +1,5 @@
3104 /*
3105- * Copyright (C) 2014 Canonical, Ltd.
3106+ * Copyright (C) 2014-2015 Canonical, Ltd.
3107 *
3108 * This program is free software; you can redistribute it and/or modify
3109 * it under the terms of the GNU General Public License as published by
3110@@ -66,6 +66,7 @@
3111 void candidatesAndWatchers_2();
3112 void rejectingTouchfterItsEnd();
3113 void removeOldUndecidedCandidates();
3114+ void interimOwnerWontGetUnownedTouchEvents();
3115 };
3116
3117 void tst_TouchRegistry::requestWithNoCandidates()
3118@@ -742,7 +743,7 @@
3119
3120 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
3121 // making TouchRegistry consider that undecidedCantidate defaulted.
3122- fakeTimerFactory->makeRunningTimersTimeout();
3123+ fakeTimerFactory->updateTime(10000);
3124
3125 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
3126 QVERIFY(undecidedCandidate.lostTouches.contains(0));
3127@@ -752,6 +753,71 @@
3128 delete touchRegistry;
3129 }
3130
3131+/*
3132+ An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
3133+ is assumed to be the interim owner of the touch point, thus there's no point in sending
3134+ UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
3135+ he didn't ignore the first QTouchEvent with that touch point.
3136+ */
3137+void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
3138+{
3139+ FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
3140+ TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
3141+
3142+ DummyCandidate undecidedCandidate;
3143+ undecidedCandidate.setObjectName("undecided");
3144+
3145+ DummyCandidate interimOwner;
3146+ interimOwner.setObjectName("interimOwner");
3147+
3148+ {
3149+ QList<QTouchEvent::TouchPoint> touchPoints;
3150+ touchPoints.append(QTouchEvent::TouchPoint(0));
3151+ touchPoints[0].setState(Qt::TouchPointPressed);
3152+ QTouchEvent touchEvent(QEvent::TouchBegin,
3153+ 0 /* device */,
3154+ Qt::NoModifier,
3155+ Qt::TouchPointPressed,
3156+ touchPoints);
3157+ touchRegistry->update(&touchEvent);
3158+ }
3159+
3160+ touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
3161+ touchRegistry->requestTouchOwnership(0, &interimOwner);
3162+
3163+ {
3164+ QList<QTouchEvent::TouchPoint> touchPoints;
3165+ touchPoints.append(QTouchEvent::TouchPoint(0));
3166+ touchPoints[0].setState(Qt::TouchPointMoved);
3167+ QTouchEvent touchEvent(QEvent::TouchUpdate,
3168+ 0 /* device */,
3169+ Qt::NoModifier,
3170+ Qt::TouchPointMoved,
3171+ touchPoints);
3172+ touchRegistry->update(&touchEvent);
3173+ }
3174+
3175+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
3176+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
3177+
3178+ {
3179+ QList<QTouchEvent::TouchPoint> touchPoints;
3180+ touchPoints.append(QTouchEvent::TouchPoint(0));
3181+ touchPoints[0].setState(Qt::TouchPointMoved);
3182+ QTouchEvent touchEvent(QEvent::TouchUpdate,
3183+ 0 /* device */,
3184+ Qt::NoModifier,
3185+ Qt::TouchPointMoved,
3186+ touchPoints);
3187+ touchRegistry->update(&touchEvent);
3188+ }
3189+
3190+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
3191+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
3192+
3193+ delete touchRegistry;
3194+}
3195+
3196 ////////////// TouchMemento //////////
3197
3198 TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
3199
3200=== modified file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt'
3201--- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-17 11:01:53 +0000
3202+++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
3203@@ -19,6 +19,7 @@
3204 )
3205
3206 add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
3207+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
3208
3209 macro(add_gesture_ui_test CLASSNAME)
3210 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
3211@@ -26,7 +27,8 @@
3212 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
3213
3214 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
3215- add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3216+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
3217+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
3218 endmacro(add_gesture_ui_test)
3219
3220 macro(add_gesture_test CLASSNAME)
3221
3222=== modified file 'tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml'
3223--- tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2014-02-11 20:21:24 +0000
3224+++ tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2015-04-10 21:17:56 +0000
3225@@ -37,8 +37,8 @@
3226
3227 Rectangle {
3228 id: dragAreaRect
3229- color: "yellow"
3230- opacity: 0.0
3231+ opacity: dragArea.dragging ? 0.5 : 0.0
3232+ color: "green"
3233 anchors.fill: dragArea
3234 }
3235
3236@@ -49,24 +49,10 @@
3237 height: units.gu(5)
3238
3239 direction: Direction.Downwards
3240- maxDeviation: units.gu(2)
3241- wideningAngle: 10
3242- distanceThreshold: units.gu(4)
3243
3244- onStatusChanged: {
3245- switch (status) {
3246- case DirectionalDragArea.WaitingForTouch:
3247- dragAreaRect.opacity = 0.0
3248- break;
3249- case DirectionalDragArea.Undecided:
3250- dragAreaRect.color = "yellow"
3251- dragAreaRect.opacity = 0.3
3252- launcher.y = Qt.binding(launcher.followDragArea)
3253- break;
3254- default: // DirectionalDragArea.Recognized:
3255- dragAreaRect.color = "green"
3256- dragAreaRect.opacity = 0.5
3257- break;
3258+ onDraggingChanged: {
3259+ if (dragging) {
3260+ launcher.y = Qt.binding(launcher.followDragArea)
3261 }
3262 }
3263
3264
3265=== modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.cpp'
3266--- tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2014-11-03 15:21:46 +0000
3267+++ tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2015-04-10 21:17:56 +0000
3268@@ -1,5 +1,5 @@
3269 /*
3270- * Copyright (C) 2013 Canonical, Ltd.
3271+ * Copyright (C) 2013,2015 Canonical, Ltd.
3272 *
3273 * This program is free software; you can redistribute it and/or modify
3274 * it under the terms of the GNU General Public License as published by
3275@@ -21,8 +21,11 @@
3276 #include <QQuickView>
3277 #include <QtTest>
3278
3279+#include <Timer.h>
3280 #include <TouchRegistry.h>
3281
3282+using namespace UbuntuGestures;
3283+
3284 GestureTest::GestureTest(const QString &qmlFilename)
3285 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
3286 {
3287@@ -40,14 +43,17 @@
3288 void GestureTest::init()
3289 {
3290 m_view = new QQuickView;
3291- m_view->setResizeMode(QQuickView::SizeViewToRootObject);
3292+ m_view->setResizeMode(QQuickView::SizeRootObjectToView);
3293 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
3294+ m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
3295 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
3296 m_view->show();
3297 QVERIFY(QTest::qWaitForWindowExposed(m_view));
3298 QVERIFY(m_view->rootObject() != 0);
3299
3300- m_touchRegistry = new TouchRegistry;
3301+ m_fakeTimerFactory = new FakeTimerFactory;
3302+
3303+ m_touchRegistry = new TouchRegistry(nullptr, m_fakeTimerFactory);
3304 m_view->installEventFilter(m_touchRegistry);
3305
3306 qApp->processEvents();
3307@@ -59,6 +65,10 @@
3308 delete m_touchRegistry;
3309 m_touchRegistry = nullptr;
3310
3311+ // TouchRegistry will take down the timer factory along with him
3312+ // delete m_fakeTimerFactory;
3313+ m_fakeTimerFactory = nullptr;
3314+
3315 delete m_view;
3316 m_view = nullptr;
3317 }
3318
3319=== modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.h'
3320--- tests/plugins/Ubuntu/Gestures/GestureTest.h 2014-11-03 15:21:46 +0000
3321+++ tests/plugins/Ubuntu/Gestures/GestureTest.h 2015-04-10 21:17:56 +0000
3322@@ -23,6 +23,9 @@
3323 class QQuickView;
3324 class QTouchDevice;
3325
3326+namespace UbuntuGestures {
3327+ class FakeTimerFactory;
3328+}
3329 class TouchRegistry;
3330
3331 // C++ std lib
3332@@ -81,6 +84,7 @@
3333 QTouchDevice *m_device;
3334 QQuickView *m_view;
3335 TouchRegistry *m_touchRegistry;
3336+ UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
3337 QString m_qmlFilename;
3338 };
3339
3340
3341=== modified file 'tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml'
3342--- tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2014-02-11 20:21:24 +0000
3343+++ tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2015-04-10 21:17:56 +0000
3344@@ -21,7 +21,7 @@
3345 Item {
3346 id: root
3347
3348- function reset() { launcher.x = root.width }
3349+ function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
3350
3351 Rectangle {
3352 id: launcher
3353@@ -41,7 +41,8 @@
3354
3355 Rectangle {
3356 id: dragAreaRect
3357- opacity: 0.0
3358+ opacity: dragArea.dragging ? 0.5 : 0.0
3359+ color: "green"
3360 anchors.fill: dragArea
3361 }
3362
3363@@ -52,24 +53,10 @@
3364 width: units.gu(5)
3365
3366 direction: Direction.Leftwards
3367- maxDeviation: units.gu(2)
3368- wideningAngle: 10
3369- distanceThreshold: units.gu(4)
3370
3371- onStatusChanged: {
3372- switch (status) {
3373- case DirectionalDragArea.WaitingForTouch:
3374- dragAreaRect.opacity = 0.0
3375- break;
3376- case DirectionalDragArea.Undecided:
3377- dragAreaRect.color = "yellow"
3378- dragAreaRect.opacity = 0.3
3379- launcher.x = Qt.binding(launcher.followDragArea)
3380- break;
3381- default: //case DirectionalDragArea.Recognized:
3382- dragAreaRect.color = "green"
3383- dragAreaRect.opacity = 0.5
3384- break;
3385+ onDraggingChanged: {
3386+ if (dragging) {
3387+ launcher.x = Qt.binding(launcher.followDragArea)
3388 }
3389 }
3390
3391
3392=== modified file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml'
3393--- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2014-10-01 13:20:32 +0000
3394+++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2015-04-10 21:17:56 +0000
3395@@ -38,7 +38,8 @@
3396
3397 Rectangle {
3398 id: dragAreaRect
3399- opacity: 0.0
3400+ opacity: dragArea.dragging ? 0.5 : 0.0
3401+ color: "green"
3402 anchors.fill: dragArea
3403 }
3404
3405@@ -52,25 +53,10 @@
3406 width: units.gu(5)
3407
3408 direction: Direction.Rightwards
3409- maxDeviation: units.gu(2)
3410- wideningAngle: 10
3411- distanceThreshold: units.gu(4)
3412- minSpeed: 50
3413
3414- onStatusChanged: {
3415- switch (status) {
3416- case DirectionalDragArea.WaitingForTouch:
3417- dragAreaRect.opacity = 0.0
3418- break;
3419- case DirectionalDragArea.Undecided:
3420- dragAreaRect.color = "yellow"
3421- dragAreaRect.opacity = 0.3
3422- launcher.x = Qt.binding(launcher.followDragArea)
3423- break;
3424- default: //case DirectionalDragArea.Recognized:
3425- dragAreaRect.color = "green"
3426- dragAreaRect.opacity = 0.5
3427- break;
3428+ onDraggingChanged: {
3429+ if (dragging) {
3430+ launcher.x = Qt.binding(launcher.followDragArea)
3431 }
3432 }
3433
3434
3435=== modified file 'tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml'
3436--- tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2014-02-11 20:21:24 +0000
3437+++ tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2015-04-10 21:17:56 +0000
3438@@ -21,7 +21,7 @@
3439 Item {
3440 id: root
3441
3442- function reset() { launcher.y = root.height }
3443+ function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
3444
3445 Rectangle {
3446 id: launcher
3447@@ -41,7 +41,8 @@
3448
3449 Rectangle {
3450 id: dragAreaRect
3451- opacity: 0.0
3452+ opacity: dragArea.dragging ? 0.5 : 0.0
3453+ color: "green"
3454 anchors.fill: dragArea
3455 }
3456
3457@@ -52,24 +53,10 @@
3458 height: units.gu(5)
3459
3460 direction: Direction.Upwards
3461- maxDeviation: units.gu(2)
3462- wideningAngle: 10
3463- distanceThreshold: units.gu(4)
3464
3465- onStatusChanged: {
3466- switch (status) {
3467- case DirectionalDragArea.WaitingForTouch:
3468- dragAreaRect.opacity = 0.0
3469- break;
3470- case DirectionalDragArea.Undecided:
3471- dragAreaRect.color = "yellow"
3472- dragAreaRect.opacity = 0.3
3473- launcher.y = Qt.binding(launcher.followDragArea)
3474- break;
3475- default: //case DirectionalDragArea.Recognized:
3476- dragAreaRect.color = "green"
3477- dragAreaRect.opacity = 0.5
3478- break;
3479+ onDraggingChanged: {
3480+ if (dragging) {
3481+ launcher.y = Qt.binding(launcher.followDragArea)
3482 }
3483 }
3484
3485
3486=== modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp'
3487--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2015-02-18 13:51:39 +0000
3488+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2015-04-10 21:17:56 +0000
3489@@ -1,5 +1,5 @@
3490 /*
3491- * Copyright (C) 2013-2014 Canonical, Ltd.
3492+ * Copyright (C) 2013-2015 Canonical, Ltd.
3493 *
3494 * This program is free software; you can redistribute it and/or modify
3495 * it under the terms of the GNU General Public License as published by
3496@@ -20,51 +20,38 @@
3497 #include <QtQml/QQmlEngine>
3498 #include <QPointer>
3499 #include <private/qquickmousearea_p.h>
3500-#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
3501- #include <private/qquickwindow_p.h>
3502-#endif
3503+#include <private/qquickwindow_p.h>
3504
3505
3506 #include <DirectionalDragArea.h>
3507+#include <DirectionalDragArea_p.h>
3508 #include <TouchRegistry.h>
3509
3510 #include "GestureTest.h"
3511
3512 using namespace UbuntuGestures;
3513
3514-
3515-class ComplexFakeTimer : public FakeTimer
3516-{
3517+// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
3518+// doesn't work
3519+class StatusSpy : public QObject {
3520 Q_OBJECT
3521 public:
3522- ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
3523- : FakeTimer(parent),
3524- m_timeSource(timeSource)
3525- {}
3526-
3527- void start() override {
3528- AbstractTimer::start();
3529- m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
3530- }
3531-
3532- void emitTimeout() {
3533- m_nextTimeoutTime += interval();
3534- Q_EMIT timeout();
3535- }
3536-
3537- qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
3538+ StatusSpy(DirectionalDragArea *edgeDragArea) {
3539+ m_recognized = false;
3540+ connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
3541+ this, &StatusSpy::onStatusChanged);
3542+ }
3543+ bool recognized() {
3544+ return m_recognized;
3545+ }
3546+
3547+private Q_SLOTS:
3548+ void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
3549+ m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
3550+ }
3551
3552 private:
3553- SharedTimeSource m_timeSource;
3554- qint64 m_nextTimeoutTime;
3555-};
3556-
3557-class FakeTimeSource : public UbuntuGestures::TimeSource
3558-{
3559-public:
3560- FakeTimeSource() { m_msecsSinceReference = 0; }
3561- virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
3562- qint64 m_msecsSinceReference;
3563+ bool m_recognized;
3564 };
3565
3566 /*
3567@@ -98,15 +85,9 @@
3568 tst_DirectionalDragArea();
3569 private Q_SLOTS:
3570 void init(); // called right before each and every test function is executed
3571- void cleanup(); // called right after each and every test function is executed
3572
3573- void edgeDrag();
3574- void edgeDrag_data();
3575 void dragWithShortDirectionChange();
3576- void minSpeed();
3577- void minSpeed_data();
3578 void recognitionTimerUsage();
3579- void maxSilenceTime();
3580 void sceneXAndX();
3581 void sceneYAndY();
3582 void twoFingerTap();
3583@@ -122,14 +103,24 @@
3584 void immediateRecognitionWhenConstraintsDisabled();
3585 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
3586 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
3587- void tappedSignal();
3588- void tappedSignal_data();
3589 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
3590+ void interleavedTouches();
3591+ void makoRightEdgeDrag();
3592+ void makoRightEdgeDrag_verticalDownwards();
3593+ void makoLeftEdgeDrag_slowStart();
3594+ void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
3595
3596 private:
3597+ // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
3598+ // lose precision due to rounding.
3599+ // Besides, those helper functions lead to more compact code.
3600+ void sendTouchPress(qint64 timestamp, int id, QPointF pos);
3601+ void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
3602+ void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
3603+ void sendTouch(qint64 timestamp, int id, QPointF pos,
3604+ Qt::TouchPointState pointState, QEvent::Type eventType);
3605+
3606 void passTime(qint64 timeSpanMs);
3607- ComplexFakeTimer *fakeTimer;
3608- QSharedPointer<FakeTimeSource> fakeTimeSource;
3609 };
3610
3611 tst_DirectionalDragArea::tst_DirectionalDragArea()
3612@@ -148,36 +139,49 @@
3613 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
3614 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
3615 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
3616-
3617- fakeTimeSource.reset(new FakeTimeSource);
3618- fakeTimer = new ComplexFakeTimer(fakeTimeSource);
3619-}
3620-
3621-void tst_DirectionalDragArea::cleanup()
3622-{
3623- delete fakeTimer;
3624- fakeTimer = 0;
3625-
3626- fakeTimeSource.reset();
3627-
3628- GestureTest::cleanup();
3629+}
3630+
3631+void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
3632+{
3633+ sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
3634+}
3635+
3636+void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
3637+{
3638+ sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
3639+}
3640+
3641+void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
3642+{
3643+ sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
3644+}
3645+
3646+void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
3647+ Qt::TouchPointState pointState, QEvent::Type eventType)
3648+{
3649+ m_fakeTimerFactory->updateTime(timestamp);
3650+
3651+ QTouchEvent::TouchPoint point;
3652+
3653+ point.setState(pointState);
3654+ point.setId(id);
3655+ point.setScenePos(pos);
3656+ point.setPos(pos);
3657+
3658+ QList<QTouchEvent::TouchPoint> points;
3659+ points << point;
3660+
3661+ QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
3662+ QCoreApplication::sendEvent(m_view, &touchEvent);
3663+
3664+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
3665+ windowPrivate->flushDelayedTouchEvent();
3666 }
3667
3668 void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
3669 {
3670- qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;
3671-
3672- if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
3673- fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
3674- fakeTimer->emitTimeout();
3675-
3676- qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
3677- if (timeSpanRemainder > 0) {
3678- passTime(timeSpanRemainder);
3679- }
3680- } else {
3681- fakeTimeSource->m_msecsSinceReference = finalTime;
3682- }
3683+ qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
3684+ m_fakeTimerFactory->updateTime(finalTime);
3685 }
3686
3687 namespace {
3688@@ -186,147 +190,6 @@
3689 QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
3690 return edgeDragArea->mapToScene(localCenter);
3691 }
3692-
3693-QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
3694- qreal wideningAngleMultiplier)
3695-{
3696- qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
3697- * M_PI / 180.0;
3698-
3699- qreal angleCos = qCos(angleRadians);
3700- qreal angleSin = qSin(angleRadians);
3701-
3702- switch (edgeDragArea->direction()) {
3703- case Direction::Upwards:
3704- return QPointF(angleSin, -angleCos);
3705- case Direction::Downwards:
3706- return QPointF(angleSin, angleCos);
3707- case Direction::Leftwards:
3708- return QPointF(-angleCos, angleSin);
3709- default: // Direction::Rightwards:
3710- return QPointF(angleCos, angleSin);
3711- }
3712-}
3713-
3714-QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
3715-{
3716- qreal deviation = edgeDragArea->maxDeviation() * 0.8;
3717-
3718- if (Direction::isHorizontal(edgeDragArea->direction())) {
3719- return QPointF(0, deviation);
3720- } else {
3721- return QPointF(deviation, 0);
3722- }
3723-}
3724-}
3725-
3726-void tst_DirectionalDragArea::edgeDrag()
3727-{
3728- QFETCH(QString, dragAreaObjectName);
3729- QFETCH(qreal, wideningAngleMultiplier);
3730- QFETCH(qreal, dragDistanceFactor);
3731- QFETCH(bool, expectGestureRecognition);
3732-
3733- DirectionalDragArea *edgeDragArea =
3734- m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
3735- QVERIFY(edgeDragArea != 0);
3736- edgeDragArea->setRecognitionTimer(fakeTimer);
3737- edgeDragArea->setTimeSource(fakeTimeSource);
3738-
3739- QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
3740-
3741- QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
3742- QPointF touchPoint = initialTouchPos;
3743-
3744- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
3745- QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
3746- wideningAngleMultiplier);
3747- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
3748- QPointF touchMovement = dragDirectionVector * movementStepDistance;
3749- int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
3750- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
3751-
3752- QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
3753-
3754- QCOMPARE(draggingSpy.count(), 1);
3755- QCOMPARE(edgeDragArea->dragging(), true);
3756-
3757- if (wideningAngleMultiplier > 0) {
3758- // go close to the border of the valid area for this touch point
3759- // in order to make it easier to leave it by dragging at an angle
3760- // slightly bigger than the widening angle
3761- touchPoint += createTouchDeviation(edgeDragArea);
3762- QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
3763- }
3764-
3765- for (int i = 0; i < totalMovementSteps; ++i) {
3766- touchPoint += touchMovement;
3767- passTime(movementTimeStepMs);
3768- QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
3769- }
3770-
3771- if (expectGestureRecognition)
3772- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
3773-
3774- if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
3775- QCOMPARE(edgeDragArea->dragging(), false);
3776- QCOMPARE(draggingSpy.count(), 2);
3777- }
3778-
3779- QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
3780-
3781- QCOMPARE(draggingSpy.count(), 2);
3782- QCOMPARE(edgeDragArea->dragging(), false);
3783-}
3784-
3785-void tst_DirectionalDragArea::edgeDrag_data()
3786-{
3787- QTest::addColumn<QString>("dragAreaObjectName");
3788- QTest::addColumn<qreal>("wideningAngleMultiplier");
3789- QTest::addColumn<qreal>("dragDistanceFactor");
3790- QTest::addColumn<bool>("expectGestureRecognition");
3791-
3792- QTest::newRow("rightwards, tiny drag")
3793- << "hpDragArea" << 0.0 << 0.2 << false;
3794-
3795- QTest::newRow("rightwards, straight drag")
3796- << "hpDragArea" << 0.0 << 3.0 << true;
3797-
3798- QTest::newRow("rightwards, diagonal drag")
3799- << "hpDragArea" << 0.9 << 3.0 << true;
3800-
3801- QTest::newRow("rightwards, overly diagonal drag")
3802- << "hpDragArea" << 2.0 << 3.0 << false;
3803-
3804- QTest::newRow("leftwards, tiny drag")
3805- << "hnDragArea" << 0.0 << 0.2 << false;
3806-
3807- QTest::newRow("leftwards, straight drag")
3808- << "hnDragArea" << 0.0 << 3.0 << true;
3809-
3810- QTest::newRow("leftwards, diagonal drag")
3811- << "hnDragArea" << 0.9 << 3.0 << true;
3812-
3813- QTest::newRow("downwards, tiny drag")
3814- << "vpDragArea" << 0.0 << 0.2 << false;
3815-
3816- QTest::newRow("downwards, straight drag")
3817- << "vpDragArea" << 0.0 << 3.0 << true;
3818-
3819- QTest::newRow("downwards, diagonal drag")
3820- << "vpDragArea" << 0.9 << 3.0 << true;
3821-
3822- QTest::newRow("upwards, tiny drag")
3823- << "vnDragArea" << 0.0 << 0.2 << false;
3824-
3825- QTest::newRow("upwards, straight drag")
3826- << "vnDragArea" << 0.0 << 3.0 << true;
3827-
3828- QTest::newRow("upwards, diagonal drag")
3829- << "vnDragArea" << 0.9 << 3.0 << true;
3830-
3831- QTest::newRow("upwards, overly diagonal drag")
3832- << "vnDragArea" << 2.0 << 3.0 << false;
3833 }
3834
3835 /*
3836@@ -339,17 +202,17 @@
3837 DirectionalDragArea *edgeDragArea =
3838 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
3839 QVERIFY(edgeDragArea != 0);
3840- edgeDragArea->setRecognitionTimer(fakeTimer);
3841- edgeDragArea->setTimeSource(fakeTimeSource);
3842+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
3843+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
3844
3845 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
3846 QPointF touchPoint = initialTouchPos;
3847
3848- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
3849+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
3850 QPointF dragDirectionVector(1.0, 0.0);
3851- qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
3852- // make sure we are above minimum speed
3853- int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
3854+ qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
3855+ // make sure we are above maximum time
3856+ int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
3857 QPointF touchMovement = dragDirectionVector * touchStepDistance;
3858
3859 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
3860@@ -373,66 +236,14 @@
3861 passTime(touchStepTimeMs);
3862 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
3863 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
3864- || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
3865+ || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
3866
3867- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
3868+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
3869
3870 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
3871 }
3872
3873 /*
3874- Checks that a gesture will be rejected if it's slower than minSpeed while
3875- status is Undecided.
3876- */
3877-void tst_DirectionalDragArea::minSpeed()
3878-{
3879- QFETCH(qreal, minSpeed);
3880- QFETCH(qreal, speed);
3881- QFETCH(int, expectedStatusAfterSpeedCheck);
3882-
3883- DirectionalDragArea *edgeDragArea =
3884- m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
3885- QVERIFY(edgeDragArea != 0);
3886- edgeDragArea->setRecognitionTimer(fakeTimer);
3887- edgeDragArea->setTimeSource(fakeTimeSource);
3888-
3889- // A really long, unattainable, number. We don't want it getting recognized before
3890- // the speed checks we want have been performed
3891- edgeDragArea->setDistanceThreshold(500000);
3892-
3893- edgeDragArea->setMinSpeed(minSpeed);
3894-
3895- QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
3896- QPointF touchPoint = initialTouchPos;
3897-
3898- QPointF dragDirectionVector(1.0, 0.0);
3899- qint64 timeStepMsecs = 10;
3900- qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
3901- QPointF touchMovement = dragDirectionVector * distanceStep;
3902-
3903- QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
3904-
3905- // Move for a while to ensure our speed check is performed a couple of times
3906- for (int i=0; i < 20; ++i) {
3907- touchPoint += touchMovement;
3908- passTime(timeStepMsecs);
3909- QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
3910- }
3911-
3912- QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
3913-}
3914-
3915-void tst_DirectionalDragArea::minSpeed_data()
3916-{
3917- QTest::addColumn<qreal>("minSpeed");
3918- QTest::addColumn<qreal>("speed");
3919- QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
3920-
3921- QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
3922- QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
3923-}
3924-
3925-/*
3926 Checks that the recognition timer is started and stopped appropriately.
3927 I.e., check that it's running only while gesture recognition is taking place
3928 (status == Undecided)
3929@@ -442,11 +253,9 @@
3930 DirectionalDragArea *edgeDragArea =
3931 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
3932 QVERIFY(edgeDragArea != 0);
3933- edgeDragArea->setRecognitionTimer(fakeTimer);
3934- edgeDragArea->setTimeSource(fakeTimeSource);
3935-
3936- // don't let it interfere with our test
3937- edgeDragArea->setMinSpeed(0.0);
3938+ AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
3939+ edgeDragArea->d->setRecognitionTimer(fakeTimer);
3940+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
3941
3942 int timeStepMs = 5; // some arbitrary small value.
3943
3944@@ -454,63 +263,32 @@
3945 QPointF touchPoint = initialTouchPos;
3946
3947 QPointF dragDirectionVector(1.0, 0.0);
3948- QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
3949+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
3950
3951- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
3952+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
3953 QVERIFY(!fakeTimer->isRunning());
3954
3955 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
3956
3957- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
3958+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
3959 QVERIFY(fakeTimer->isRunning());
3960
3961 // Move beyond distance threshold and composition time to ensure recognition
3962- while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
3963- (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
3964+ while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
3965+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
3966
3967- QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
3968+ QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
3969
3970 touchPoint += touchMovement;
3971 passTime(timeStepMs);
3972 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
3973 }
3974
3975- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
3976+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
3977 QVERIFY(!fakeTimer->isRunning());
3978 }
3979
3980 /*
3981- A gesture should be rejected if too much time has passed without any new input
3982- events from it.
3983- */
3984-void tst_DirectionalDragArea::maxSilenceTime()
3985-{
3986- DirectionalDragArea *edgeDragArea =
3987- m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
3988- QVERIFY(edgeDragArea != 0);
3989- edgeDragArea->setRecognitionTimer(fakeTimer);
3990- edgeDragArea->setTimeSource(fakeTimeSource);
3991-
3992- // Make sure this property is not disabled
3993- edgeDragArea->setMaxSilenceTime(100);
3994-
3995- QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
3996- QPointF touchPoint = initialTouchPos;
3997-
3998- QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
3999-
4000- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4001- QVERIFY(fakeTimer->isRunning());
4002-
4003- // Force timer to timeout until after maxSilenceTime has been reached
4004- while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
4005- passTime(fakeTimer->interval());
4006- }
4007-
4008- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4009-}
4010-
4011-/*
4012 Checks that it informs the X coordinate of the touch point in local and scene coordinates
4013 correctly.
4014 */
4015@@ -519,18 +297,19 @@
4016 DirectionalDragArea *edgeDragArea =
4017 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
4018 QVERIFY(edgeDragArea != 0);
4019- edgeDragArea->setRecognitionTimer(fakeTimer);
4020- edgeDragArea->setTimeSource(fakeTimeSource);
4021+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4022+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4023+ edgeDragArea->setImmediateRecognition(true);
4024
4025 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
4026
4027- QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
4028+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
4029
4030 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
4031 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
4032
4033 touchScenePos.rx() = m_view->width() / 2;
4034- QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
4035+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
4036
4037 QCOMPARE(touchXSpy.count(), 1);
4038 QCOMPARE(touchSceneXSpy.count(), 1);
4039@@ -547,18 +326,19 @@
4040 DirectionalDragArea *edgeDragArea =
4041 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
4042 QVERIFY(edgeDragArea != 0);
4043- edgeDragArea->setRecognitionTimer(fakeTimer);
4044- edgeDragArea->setTimeSource(fakeTimeSource);
4045+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4046+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4047+ edgeDragArea->setImmediateRecognition(true);
4048
4049 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
4050
4051- QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
4052+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
4053
4054 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
4055 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
4056
4057 touchScenePos.ry() = m_view->height() / 2;
4058- QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
4059+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
4060
4061 QCOMPARE(touchYSpy.count(), 1);
4062 QCOMPARE(touchSceneYSpy.count(), 1);
4063@@ -577,8 +357,8 @@
4064 DirectionalDragArea *edgeDragArea =
4065 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4066 QVERIFY(edgeDragArea != 0);
4067- edgeDragArea->setRecognitionTimer(fakeTimer);
4068- edgeDragArea->setTimeSource(fakeTimeSource);
4069+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4070+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4071
4072 // Make touches evenly spaced along the edgeDragArea
4073 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
4074@@ -592,7 +372,7 @@
4075 QTest::touchEvent(m_view, m_device)
4076 .press(0, touchAPos);
4077
4078- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4079+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4080
4081 passTime(timeStepMsecs);
4082 QTest::touchEvent(m_view, m_device)
4083@@ -601,20 +381,20 @@
4084
4085 // A second touch point appeared during recognition, reject immediately as this
4086 // can't be a single-touch gesture anymore.
4087- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4088+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4089
4090 passTime(timeStepMsecs);
4091 QTest::touchEvent(m_view, m_device)
4092 .release(0, touchAPos)
4093 .move(1, touchBPos);
4094
4095- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4096+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4097
4098 passTime(timeStepMsecs);
4099 QTest::touchEvent(m_view, m_device)
4100 .release(1, touchBPos);
4101
4102- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4103+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4104
4105 // Perform the second two-finger tap
4106
4107@@ -622,27 +402,27 @@
4108 QTest::touchEvent(m_view, m_device)
4109 .press(0, touchAPos);
4110
4111- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4112+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4113
4114 passTime(timeStepMsecs);
4115 QTest::touchEvent(m_view, m_device)
4116 .move(0, touchAPos)
4117 .press(1, touchBPos);
4118
4119- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4120+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4121
4122 passTime(timeStepMsecs);
4123 QTest::touchEvent(m_view, m_device)
4124 .release(0, touchAPos)
4125 .move(1, touchBPos);
4126
4127- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4128+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4129
4130 passTime(timeStepMsecs);
4131 QTest::touchEvent(m_view, m_device)
4132 .release(1, touchBPos);
4133
4134- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4135+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4136 }
4137
4138 /*
4139@@ -659,25 +439,25 @@
4140 DirectionalDragArea *edgeDragArea =
4141 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
4142 Q_ASSERT(edgeDragArea != 0);
4143- edgeDragArea->setRecognitionTimer(fakeTimer);
4144- edgeDragArea->setTimeSource(fakeTimeSource);
4145+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4146+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4147
4148 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
4149 QPointF touchPoint = initialTouchPos;
4150
4151- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
4152+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
4153 QPointF dragDirectionVector(1.0f, 0.0f);
4154
4155- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4156+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4157 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4158 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4159- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4160+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4161
4162 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
4163
4164 // Move it far ahead along the direction of the gesture
4165 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
4166- rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
4167+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
4168
4169 for (int i = 0; i < totalMovementSteps; ++i) {
4170 touchPoint += touchMovement;
4171@@ -685,7 +465,7 @@
4172 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
4173 }
4174
4175- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4176+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4177
4178 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4179 }
4180@@ -699,8 +479,8 @@
4181 DirectionalDragArea *edgeDragArea =
4182 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4183 Q_ASSERT(edgeDragArea != 0);
4184- edgeDragArea->setRecognitionTimer(fakeTimer);
4185- edgeDragArea->setTimeSource(fakeTimeSource);
4186+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4187+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4188
4189 // Make touches evenly spaced along the edgeDragArea
4190 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
4191@@ -708,26 +488,26 @@
4192
4193 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
4194
4195- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4196+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4197
4198 // leave it lying around for some time
4199- passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
4200-
4201- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4202-
4203- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
4204+ passTime(edgeDragArea->d->maxTime * 10);
4205+
4206+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4207+
4208+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
4209 QPointF dragDirectionVector(1.0f, 0.0f);
4210
4211- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4212+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4213 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4214 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4215- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4216+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4217
4218 QTest::touchEvent(m_view, m_device)
4219 .move(0, touch0Pos)
4220 .press(1, touch1Pos);
4221
4222- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4223+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4224
4225 for (int i = 0; i < totalMovementSteps; ++i) {
4226 touch1Pos += touchMovement.toPoint();
4227@@ -737,13 +517,13 @@
4228 .move(1, touch1Pos);
4229 }
4230
4231- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4232+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4233
4234 QTest::touchEvent(m_view, m_device)
4235 .move(0, touch0Pos)
4236 .release(1, touch1Pos);
4237
4238- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4239+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4240 }
4241
4242 /*
4243@@ -762,19 +542,19 @@
4244 DirectionalDragArea *edgeDragArea =
4245 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
4246 Q_ASSERT(edgeDragArea != 0);
4247- edgeDragArea->setRecognitionTimer(fakeTimer);
4248- edgeDragArea->setTimeSource(fakeTimeSource);
4249+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4250+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4251
4252 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
4253 QPointF touchPoint = initialTouchPos;
4254
4255- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
4256+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
4257 QPointF dragDirectionVector(0.0f, 1.0f);
4258
4259- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4260+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4261 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4262 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4263- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4264+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4265
4266 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
4267
4268@@ -784,7 +564,7 @@
4269 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
4270 }
4271
4272- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4273+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4274
4275 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4276 }
4277@@ -802,31 +582,30 @@
4278 DirectionalDragArea *edgeDragArea =
4279 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
4280 Q_ASSERT(edgeDragArea != 0);
4281- edgeDragArea->setRecognitionTimer(fakeTimer);
4282- edgeDragArea->setTimeSource(fakeTimeSource);
4283+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4284+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4285+
4286+ // to disable the position smoothing so that we can more easily check sceneDistance values
4287+ edgeDragArea->setImmediateRecognition(true);
4288
4289 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
4290 QPointF touchPoint = initialTouchPos;
4291
4292- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
4293+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
4294
4295- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4296+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4297 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4298 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4299- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4300-
4301- QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
4302-
4303-#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
4304- QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
4305-#endif
4306+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4307+
4308+ qint64 timestamp = 0;
4309+
4310+ sendTouchPress(timestamp, 0, touchPoint);
4311+
4312 for (int i = 0; i < totalMovementSteps; ++i) {
4313 touchPoint += touchMovement;
4314- passTime(movementTimeStepMs);
4315- QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
4316-#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
4317- wp->flushDelayedTouchEvent();
4318-#endif
4319+ timestamp += movementTimeStepMs;
4320+ sendTouchUpdate(timestamp, 0, touchPoint);
4321 }
4322
4323 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
4324@@ -836,7 +615,8 @@
4325 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
4326 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
4327
4328- QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4329+ timestamp += movementTimeStepMs;
4330+ sendTouchRelease(timestamp, 0, touchPoint);
4331 }
4332
4333 void tst_DirectionalDragArea::sceneDistance_data()
4334@@ -860,18 +640,18 @@
4335 DirectionalDragArea *edgeDragArea =
4336 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4337 Q_ASSERT(edgeDragArea != 0);
4338- edgeDragArea->setRecognitionTimer(fakeTimer);
4339- edgeDragArea->setTimeSource(fakeTimeSource);
4340+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4341+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4342
4343 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
4344
4345- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
4346+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
4347 QPointF dragDirectionVector(1., 0.); // horizontal positive
4348
4349- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4350+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4351 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4352 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4353- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4354+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4355
4356 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
4357
4358@@ -881,13 +661,13 @@
4359 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
4360 }
4361
4362- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4363+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4364 QCOMPARE(edgeDragArea->dragging(), true);
4365
4366 // disable the dragArea while it's being dragged.
4367 edgeDragArea->setEnabled(false);
4368
4369- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4370+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4371 QCOMPARE(edgeDragArea->dragging(), false);
4372
4373 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4374@@ -898,15 +678,14 @@
4375 DirectionalDragArea *edgeDragArea =
4376 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4377 Q_ASSERT(edgeDragArea != 0);
4378- edgeDragArea->setRecognitionTimer(fakeTimer);
4379- edgeDragArea->setTimeSource(fakeTimeSource);
4380+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4381+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4382
4383 // Disable some constraints we're not interested in
4384- edgeDragArea->setMaxSilenceTime(60 * 1000);
4385- edgeDragArea->setMinSpeed(0);
4386+ edgeDragArea->d->setMaxTime(60 * 1000);
4387
4388 // And ensure others have the values we want
4389- edgeDragArea->setCompositionTime(60);
4390+ edgeDragArea->d->compositionTime = 60;
4391
4392 // Put an item right behind edgeDragArea to receive the touches ignored by it
4393 DummyItem *dummyItem = new DummyItem;
4394@@ -923,10 +702,10 @@
4395
4396 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
4397
4398- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4399+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4400
4401 // We are now going to be way beyond compositionTime
4402- passTime(edgeDragArea->compositionTime()*3);
4403+ passTime(edgeDragArea->d->compositionTime*3);
4404
4405 QTest::touchEvent(m_view, m_device)
4406 .move(0, touch0Pos)
4407@@ -948,7 +727,7 @@
4408 .move(0, touch0Pos)
4409 .move(1, touch1Pos);
4410
4411- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4412+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4413
4414 passTime(5);
4415
4416@@ -956,7 +735,7 @@
4417 .release(0, touch0Pos)
4418 .move(1, touch1Pos);
4419
4420- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4421+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4422
4423 passTime(5);
4424
4425@@ -964,7 +743,7 @@
4426 .release(1, touch1Pos);
4427
4428 // Shouldn't be keepping info about touches that no longer exist or interest us
4429- QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
4430+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
4431
4432 delete dummyItem;
4433 }
4434@@ -974,12 +753,11 @@
4435 DirectionalDragArea *edgeDragArea =
4436 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4437 Q_ASSERT(edgeDragArea != 0);
4438- edgeDragArea->setRecognitionTimer(fakeTimer);
4439- edgeDragArea->setTimeSource(fakeTimeSource);
4440+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4441+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4442
4443 // Disable some constraints we're not interested in
4444- edgeDragArea->setMaxSilenceTime(60 * 1000);
4445- edgeDragArea->setMinSpeed(0);
4446+ edgeDragArea->d->setMaxTime(60 * 1000);
4447
4448 // Put an item right in front of edgeDragArea
4449 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
4450@@ -998,11 +776,11 @@
4451
4452 QTest::touchEvent(m_view, m_device).press(0, touchPos);
4453
4454- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4455+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4456
4457 m_touchRegistry->requestTouchOwnership(0, dummyItem);
4458
4459- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4460+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4461
4462 dummyItem->grabTouchPoints({0});
4463 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
4464@@ -1010,9 +788,9 @@
4465 passTime(5);
4466 QTest::touchEvent(m_view, m_device).release(0, touchPos);
4467
4468- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4469+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4470
4471- QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
4472+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
4473 }
4474
4475 void tst_DirectionalDragArea::threeFingerDrag()
4476@@ -1020,15 +798,14 @@
4477 DirectionalDragArea *edgeDragArea =
4478 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4479 Q_ASSERT(edgeDragArea != 0);
4480- edgeDragArea->setRecognitionTimer(fakeTimer);
4481- edgeDragArea->setTimeSource(fakeTimeSource);
4482+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4483+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4484
4485 // Disable some constraints we're not interested in
4486- edgeDragArea->setMaxSilenceTime(60 * 1000);
4487- edgeDragArea->setMinSpeed(0);
4488+ edgeDragArea->d->setMaxTime(60 * 1000);
4489
4490 // And ensure others have the values we want
4491- edgeDragArea->setCompositionTime(60);
4492+ edgeDragArea->d->compositionTime = 60;
4493
4494 // Make touches evenly spaced along the edgeDragArea
4495 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
4496@@ -1038,14 +815,14 @@
4497 QTest::touchEvent(m_view, m_device)
4498 .press(0, touch0Pos);
4499
4500- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4501+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4502
4503 passTime(5);
4504 QTest::touchEvent(m_view, m_device)
4505 .move(0, touch0Pos)
4506 .press(1, touch1Pos);
4507
4508- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4509+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4510
4511 passTime(5);
4512 QTest::touchEvent(m_view, m_device)
4513@@ -1053,7 +830,7 @@
4514 .move(1, touch1Pos)
4515 .press(2, touch2Pos);
4516
4517- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4518+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4519
4520 passTime(10);
4521 QTest::touchEvent(m_view, m_device)
4522@@ -1077,7 +854,7 @@
4523 .release(0, touch0Pos);
4524
4525 // Shouldn't be keepping info about touches that no longer exist or interest us
4526- QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
4527+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
4528 }
4529
4530 /*
4531@@ -1090,12 +867,12 @@
4532 DirectionalDragArea *edgeDragArea =
4533 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4534 Q_ASSERT(edgeDragArea != 0);
4535- edgeDragArea->setRecognitionTimer(fakeTimer);
4536- edgeDragArea->setTimeSource(fakeTimeSource);
4537+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4538+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4539
4540 // Disable the minimum amount of constraints to ensure immediate recognition
4541- edgeDragArea->setDistanceThreshold(0);
4542- edgeDragArea->setCompositionTime(0);
4543+ edgeDragArea->d->setDistanceThreshold(0);
4544+ edgeDragArea->d->compositionTime = 0;
4545
4546 // Put an item right behind edgeDragArea to receive the touches ignored by it
4547 DummyItem *dummyItem = new DummyItem;
4548@@ -1111,7 +888,7 @@
4549 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
4550
4551 // check for immediate recognition
4552- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4553+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4554
4555 // and therefore it should have immediately grabbed the touch point,
4556 // not letting it leak to items behind him.
4557@@ -1123,19 +900,19 @@
4558 DirectionalDragArea *edgeDragArea =
4559 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4560 Q_ASSERT(edgeDragArea != 0);
4561- edgeDragArea->setRecognitionTimer(fakeTimer);
4562- edgeDragArea->setTimeSource(fakeTimeSource);
4563+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4564+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4565
4566 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
4567
4568 // Move less than the minimum needed for the drag gesture recognition
4569- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;
4570+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
4571 QPointF dragDirectionVector(1., 0.); // horizontal positive
4572
4573- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4574+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4575 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4576 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4577- int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;
4578+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
4579
4580 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
4581
4582@@ -1145,14 +922,14 @@
4583 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
4584 }
4585
4586- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
4587+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4588
4589 // edgeDragArea should be an undecided candidate
4590 {
4591 auto touchInfo = m_touchRegistry->findTouchInfo(0);
4592 QCOMPARE(touchInfo->candidates.size(), 1);
4593 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
4594- QCOMPARE(touchInfo->candidates.at(0).undecided, true);
4595+ QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
4596 }
4597
4598 // disable the dragArea while it's still recognizing a possible drag gesture.
4599@@ -1169,7 +946,7 @@
4600 QCOMPARE(touchInfo->candidates.size(), 0);
4601 }
4602
4603- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
4604+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4605
4606 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4607 }
4608@@ -1182,47 +959,13 @@
4609 QTest::newRow("invisible") << false;
4610 }
4611
4612-void tst_DirectionalDragArea::tappedSignal()
4613-{
4614- DirectionalDragArea *edgeDragArea =
4615- m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4616- QVERIFY(edgeDragArea != 0);
4617- edgeDragArea->setRecognitionTimer(fakeTimer);
4618- edgeDragArea->setTimeSource(fakeTimeSource);
4619-
4620- QFETCH(bool, immediateGestureRecognition);
4621- if (immediateGestureRecognition) {
4622- // Disable the minimum amount of constraints to ensure immediate recognition
4623- edgeDragArea->setDistanceThreshold(0);
4624- edgeDragArea->setCompositionTime(0);
4625- }
4626-
4627- QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
4628-
4629- QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
4630-
4631- QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
4632- passTime(edgeDragArea->maxTapDuration() / 2);
4633- QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
4634-
4635- QCOMPARE(tappedSpy.count(), 1);
4636-}
4637-
4638-void tst_DirectionalDragArea::tappedSignal_data()
4639-{
4640- QTest::addColumn<bool>("immediateGestureRecognition");
4641-
4642- QTest::newRow("immediate gesture recognition") << true;
4643- QTest::newRow("default gesture recognition") << false;
4644-}
4645-
4646 void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
4647 {
4648 DirectionalDragArea *edgeDragArea =
4649 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4650 QVERIFY(edgeDragArea != nullptr);
4651- edgeDragArea->setRecognitionTimer(fakeTimer);
4652- edgeDragArea->setTimeSource(fakeTimeSource);
4653+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4654+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4655
4656 QQuickMouseArea *mouseArea =
4657 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
4658@@ -1233,12 +976,12 @@
4659 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
4660 QPointF touchPoint = initialTouchPos;
4661
4662- qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
4663+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
4664 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
4665- qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
4666+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4667 QPointF touchMovement = dragDirectionVector * movementStepDistance;
4668 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4669- int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
4670+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
4671
4672 QCOMPARE(mouseArea->pressed(), false);
4673
4674@@ -1259,13 +1002,305 @@
4675 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
4676 // which should make the MouseArea get a cancelation event, which will then cause it to
4677 // reset its state (going back to "unpressed"/"released").
4678- QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
4679+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4680 QCOMPARE(mouseArea->pressed(), false);
4681 QCOMPARE(mouseAreaSpy.canceledCount, 1);
4682
4683 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
4684 }
4685
4686+void tst_DirectionalDragArea::interleavedTouches()
4687+{
4688+ DirectionalDragArea *edgeDragArea =
4689+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4690+ QVERIFY(edgeDragArea != 0);
4691+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4692+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4693+
4694+ QPointF touch0 = edgeDragArea->mapToScene(
4695+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
4696+
4697+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
4698+ QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
4699+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
4700+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
4701+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
4702+ int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
4703+
4704+ QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
4705+ for (int i = 0; i < totalMovementSteps; ++i) {
4706+ touch0 += touchMovement;
4707+ passTime(movementTimeStepMs);
4708+ QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
4709+ }
4710+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4711+
4712+ QPointF touch1 = edgeDragArea->mapToScene(
4713+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
4714+
4715+ QTest::touchEvent(m_view, m_device)
4716+ .move(0, touch0.toPoint())
4717+ .press(1, touch1.toPoint());
4718+
4719+ touch1 += touchMovement;
4720+ passTime(movementTimeStepMs);
4721+ QTest::touchEvent(m_view, m_device)
4722+ .move(0, touch0.toPoint())
4723+ .move(1, touch1.toPoint());
4724+
4725+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
4726+
4727+ QTest::touchEvent(m_view, m_device)
4728+ .release(0, touch0.toPoint())
4729+ .move(1, touch1.toPoint());
4730+
4731+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4732+
4733+ touch1 += touchMovement;
4734+ passTime(movementTimeStepMs);
4735+ QTest::touchEvent(m_view, m_device)
4736+ .move(1, touch1.toPoint());
4737+
4738+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4739+
4740+ QPointF touch2 = edgeDragArea->mapToScene(
4741+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
4742+
4743+ passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
4744+ QTest::touchEvent(m_view, m_device)
4745+ .move(1, touch1.toPoint())
4746+ .press(2, touch2.toPoint());
4747+
4748+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
4749+ QCOMPARE(edgeDragArea->d->touchId, 2);
4750+
4751+ touch2 += touchMovement;
4752+ passTime(movementTimeStepMs);
4753+ QTest::touchEvent(m_view, m_device)
4754+ .move(1, touch1.toPoint())
4755+ .move(2, touch2.toPoint());
4756+
4757+ touch1 += touchMovement;
4758+ passTime(movementTimeStepMs);
4759+ QTest::touchEvent(m_view, m_device)
4760+ .move(1, touch1.toPoint())
4761+ .move(2, touch2.toPoint());
4762+
4763+ passTime(movementTimeStepMs);
4764+ QTest::touchEvent(m_view, m_device)
4765+ .release(1, touch1.toPoint())
4766+ .move(2, touch2.toPoint());
4767+
4768+ passTime(movementTimeStepMs);
4769+ QTest::touchEvent(m_view, m_device)
4770+ .release(2, touch2.toPoint());
4771+
4772+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
4773+}
4774+
4775+/*
4776+ A valid right-edge drag performed on mako
4777+ */
4778+void tst_DirectionalDragArea::makoRightEdgeDrag()
4779+{
4780+ m_view->resize(768, 1280);
4781+ QTest::qWait(300);
4782+
4783+ DirectionalDragArea *edgeDragArea =
4784+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
4785+ QVERIFY(edgeDragArea != nullptr);
4786+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4787+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4788+
4789+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
4790+
4791+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
4792+
4793+ sendTouchPress(319744, 0, QPointF(767.001, 719.719));
4794+ sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
4795+ sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
4796+ sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
4797+ sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
4798+ sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
4799+ sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
4800+ sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
4801+ sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
4802+ sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
4803+ sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
4804+ sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
4805+ sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
4806+ sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
4807+ sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
4808+ sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
4809+ sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
4810+ sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
4811+
4812+ QCOMPARE(statusSpy->recognized(), true);
4813+
4814+ delete statusSpy;
4815+}
4816+
4817+/*
4818+ A vertical, downwards swipe performed on mako near its right edge.
4819+
4820+ The DirectionalDragArea on the right edge must not recognize this
4821+ gesture.
4822+ */
4823+void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
4824+{
4825+ m_view->resize(768, 1280);
4826+ QTest::qWait(300);
4827+
4828+ DirectionalDragArea *edgeDragArea =
4829+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
4830+ QVERIFY(edgeDragArea != nullptr);
4831+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4832+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4833+
4834+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
4835+
4836+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
4837+
4838+ sendTouchPress(12012445, 26, QPointF(767.001,461.82));
4839+ sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
4840+ sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
4841+ sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
4842+ sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
4843+ sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
4844+ sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
4845+ sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
4846+ sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
4847+ sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
4848+ sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
4849+ sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
4850+ sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
4851+ sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
4852+ sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
4853+ sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
4854+ sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
4855+ sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
4856+ sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
4857+ sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
4858+
4859+ QCOMPARE(statusSpy->recognized(), false);
4860+
4861+ delete statusSpy;
4862+}
4863+
4864+/*
4865+ A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
4866+ */
4867+void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
4868+{
4869+ m_view->resize(768, 1280);
4870+ QTest::qWait(300);
4871+
4872+ DirectionalDragArea *edgeDragArea =
4873+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4874+ QVERIFY(edgeDragArea != nullptr);
4875+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4876+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4877+
4878+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
4879+
4880+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
4881+
4882+ sendTouchPress(4002267, 77, QPointF(0,885.154));
4883+ sendTouchUpdate(4002275, 77, QPointF(0,886.214));
4884+ sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
4885+ sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
4886+ sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
4887+ sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
4888+ sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
4889+ sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
4890+ sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
4891+ sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
4892+ sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
4893+ sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
4894+ sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
4895+ sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
4896+ sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
4897+ sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
4898+ sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
4899+ sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
4900+ sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
4901+ sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
4902+ sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
4903+ sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
4904+ sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
4905+ sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
4906+ sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
4907+ sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
4908+ sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
4909+ sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
4910+ sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
4911+ sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
4912+ sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
4913+ sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
4914+ sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
4915+ sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
4916+ sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
4917+ sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
4918+ sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
4919+ sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
4920+ sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
4921+ sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
4922+ sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
4923+ sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
4924+ sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
4925+
4926+ QCOMPARE(statusSpy->recognized(), true);
4927+
4928+ delete statusSpy;
4929+}
4930+
4931+void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
4932+{
4933+ m_view->resize(768, 1280);
4934+ QTest::qWait(300);
4935+
4936+ DirectionalDragArea *edgeDragArea =
4937+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
4938+ QVERIFY(edgeDragArea != nullptr);
4939+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
4940+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
4941+
4942+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
4943+
4944+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
4945+
4946+ sendTouchPress(41097, 24, QPointF(13.9909,827.177));
4947+ sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
4948+ sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
4949+ sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
4950+ sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
4951+ sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
4952+ sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
4953+ sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
4954+ sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
4955+ sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
4956+ sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
4957+ sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
4958+ sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
4959+ sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
4960+ sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
4961+ sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
4962+ sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
4963+ sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
4964+ sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
4965+ sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
4966+ sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
4967+ sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
4968+ sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
4969+ sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
4970+ sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
4971+ sendTouchRelease(41607, 24, QPointF(183.88,793.69));
4972+
4973+ QCOMPARE(statusSpy->recognized(), true);
4974+
4975+ delete statusSpy;
4976+}
4977+
4978 QTEST_MAIN(tst_DirectionalDragArea)
4979
4980 #include "tst_DirectionalDragArea.moc"
4981
4982=== modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml'
4983--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2014-10-07 13:02:29 +0000
4984+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2015-04-10 21:17:56 +0000
4985@@ -1,5 +1,5 @@
4986 /*
4987- * Copyright (C) 2013 Canonical, Ltd.
4988+ * Copyright (C) 2013,2015 Canonical, Ltd.
4989 *
4990 * This program is free software; you can redistribute it and/or modify
4991 * it under the terms of the GNU General Public License as published by
4992@@ -16,12 +16,19 @@
4993
4994 import QtQuick 2.0
4995 import Ubuntu.Components 0.1
4996+import Unity.Test 0.1
4997
4998 Rectangle {
4999 width: units.gu(60)
5000 height: units.gu(60)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches