on xenial help sign in
Merge lp:unity8
commita7041b5dda5cbe491982c608fd6b83ba55dbf4c0 authorAlbert Astals Cid <albert.astals@canonical.com> dateTue 19 May 2015, 21:05 (11 years ago) parent7ba8f770ea6e84
91 files changed+3675−1994
data/unity8-dash.conf−5 data/unity8.conf+1 debian/changelog+39−10 libs/UbuntuGestures/CMakeLists.txt+1 libs/UbuntuGestures/CandidateInactivityTimer.h+1−1 plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpp plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.h+14−4 libs/UbuntuGestures/Timer.cpp+63−10 libs/UbuntuGestures/Timer.h+15−3 libs/UbuntuGestures/TouchRegistry.cpp+12−14 libs/UbuntuGestures/TouchRegistry.h+18−4 plugins/Dash/CardCreator.js+18−5 plugins/Ubuntu/Gestures/CMakeLists.txt+1−1 plugins/Ubuntu/Gestures/Damper.cpp+24 plugins/Ubuntu/Gestures/Damper.h+2 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+322−323 plugins/Ubuntu/Gestures/DirectionalDragArea.h+29−202 plugins/Ubuntu/Gestures/DirectionalDragArea_p.h+167 plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53 plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9 plugins/Ubuntu/Gestures/TouchGate.cpp+84−60 plugins/Ubuntu/Gestures/TouchGate.h+18−5 po/ca.po+25−25 po/cs.po+136−119 po/da.po+53−51 po/de.po+30−30 po/fr.po+4−4 po/gd.po+4−4 po/hu.po+8−8 po/nn.po+584 po/pl.po+10−10 po/pt.po+7−7 po/pt_BR.po+7−7 po/ur.po+584 po/zh_CN.po+22−20 qml/Components/DragHandle.qml+18−33 qml/Components/EdgeDragArea.qml−46 qml/Components/EdgeDragEvaluator.qml+17−14 qml/Dash/Dash.qml+10−12 qml/Dash/GenericScopeView.qml+6−6 qml/Dash/ScopesList.qml+2 qml/Dash/ScopesListCategory.qml+5−1 qml/Greeter/CoverPage.qml+3−2 qml/Launcher/Launcher.qml+7−15 qml/Panel/IndicatorsMenu.qml+12−2 qml/Shell.qml−1 qml/Stages/PhoneStage.qml+31−41 qml/Stages/TabletStage.qml+18−20 qml/Tutorial/TutorialBottom.qml+1−1 runtests.sh+3−3 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1 tests/autopilot/unity8/fixture_setup.py+69−3 tests/autopilot/unity8/indicators/__init__.py+2−4 tests/autopilot/unity8/indicators/tests/__init__.py−2 tests/autopilot/unity8/process_helpers.py+2−1 tests/autopilot/unity8/shell/__init__.py−24 tests/autopilot/unity8/shell/emulators/__init__.py−6 tests/autopilot/unity8/shell/emulators/dash.py+6−6 tests/autopilot/unity8/shell/emulators/greeter.py+2−4 tests/autopilot/unity8/shell/emulators/launcher.py+3−2 tests/autopilot/unity8/shell/emulators/main_window.py+2−2 tests/autopilot/unity8/shell/tests/__init__.py+32−111 tests/autopilot/unity8/shell/tests/test_upstart.py+5−4 tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+68−2 tests/plugins/Dash/cardcreator/1.res+21−8 tests/plugins/Dash/cardcreator/3.res+31−18 tests/plugins/Dash/cardcreator/5.res+31−18 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 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+480−445 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+13−3 tests/qmltests/Components/tst_DragHandle.cpp+15−13 tests/qmltests/Components/tst_DragHandle.qml+7 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/Components/tst_EdgeDragEvaluator.cpp+165 tests/qmltests/Components/tst_EdgeDragEvaluator.qml+27 tests/qmltests/Stages/tst_PhoneStage.qml+8−8 tests/qmltests/Tutorial/tst_Tutorial.qml+5−1 tests/qmltests/tst_Shell.qml+14−10 tests/qmltests/tst_ShellWithPin.qml+2−2 tests/utils/modules/Unity/Test/UnityTestCase.qml+3−7
data/unity8-dash.conf−5
@@ -18,11 +18,6 @@ respawn
1818env APP_ID=unity8-dash
1919
2020pre-start script
21− if [ -z "$UNITY_SCOPES_LIST" ]; then
22− # FIXME: remove once we have this in dconf
23− initctl set-env UNITY_SCOPES_LIST="clickscope;musicaggregator;videoaggregator"
24− fi
25−
2621 initctl emit scope-ui-starting
2722end script
2823
data/unity8.conf+1
@@ -54,4 +54,5 @@ post-stop script
5454 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
5555 initctl unset-env --global UNITY_MIR_SOCKET
5656 initctl unset-env --global MIR_SERVER_PROMPT_FILE
57+ initctl emit application-end
5758end script
debian/changelog+39−10
@@ -1,21 +1,50 @@
1−unity8 (8.02+15.04.20150423-0ubuntu1) vivid; urgency=medium
1+unity8 (8.02+15.04.20150511-0ubuntu1) vivid; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Workarounds for concierge mode.
25
36 [ CI Train Bot ]
47 * New rebuild forced.
8+ * Resync trunk.
9+
10+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 11 May 2015 08:29:58 +0000
11+
12+unity8 (8.02+15.04.20150505-0ubuntu1) vivid; urgency=medium
13+
14+ [ Albert Astals Cid ]
15+ * Make runtests fake a test error if make fails
16+ * Make the test more stable
17+ * Use dbus-test-runner instead of dbus-launch
518
619 [ Daniel d'Andrada ]
7− * Tapping home key shows unity8-dash home added:
8− plugins/Utils/ElapsedTimer.h plugins/Utils/HomeKeyWatcher.cpp
9− plugins/Utils/HomeKeyWatcher.h plugins/Utils/Timer.cpp
10− plugins/Utils/Timer.h tests/plugins/Utils/homekeywatchertest.cpp
20+ * DirectionalDragArea: improvements & API grooming (LP: #1417920)
21+ * Fix EdgeDragEvaluator when a drag can happen both ways
22+ (Direction.Horizontal)
23+
24+ [ Josh Arenson ]
25+ * Remove panelHeight property as it is unused.
26+
27+ [ Leo Arias ]
28+ * Initial clean up of the autopilot tests set up. Removed the touch
29+ device from the test case. Moved the restart of unity to a fixture.
30+ Removed the unused DragMixin. Updated the setUpClass to use
31+ process_helpers. Removed the workaround for bug #1238417, already
32+ fixed. Use the toolkit helper to set the testability environment
33+ variable. Fixed the indicators base class that was restarting unity
34+ twice. (LP: #1238417, #1447206)
35+ * Use the base class from the toolkit in autopilot tests.
36+
37+ [ Michael Zanetti ]
38+ * emit application-stop when we're going down (LP: #1326513)
1139
1240 [ Michał Sawicz ]
13− * Tapping home key shows unity8-dash home added:
14− plugins/Utils/ElapsedTimer.h plugins/Utils/HomeKeyWatcher.cpp
15− plugins/Utils/HomeKeyWatcher.h plugins/Utils/Timer.cpp
16− plugins/Utils/Timer.h tests/plugins/Utils/homekeywatchertest.cpp
41+ * UNITY_SCOPES_LIST is no more
42+
43+ [ handsome_feng<445865575@qq.com> ]
44+ * When click the favorite scope in Dash Manager , it just return to
45+ the corresponding scope page. (LP: #1447056)
1746
18− -- CI Train Bot <ci-train-bot@canonical.com> Thu, 23 Apr 2015 12:52:51 +0000
47+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 05 May 2015 14:47:24 +0000
1948
2049unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
2150
libs/UbuntuGestures/CMakeLists.txt+1
@@ -5,6 +5,7 @@ set(UbuntuGestures_SOURCES
55 CandidateInactivityTimer.cpp
66 DebugHelpers.cpp
77 Timer.cpp
8+ TimeSource.cpp
89 TouchOwnershipEvent.cpp
910 TouchRegistry.cpp
1011 UnownedTouchEvent.cpp
libs/UbuntuGestures/CandidateInactivityTimer.h+1−1
@@ -32,7 +32,7 @@ public:
3232 AbstractTimerFactory &timerFactory,
3333 QObject *parent = nullptr);
3434
35− const int durationMs = 350;
35+ const int durationMs = 1000;
3636
3737Q_SIGNALS:
3838 void candidateDefaulted(int touchId, QQuickItem *candidate);
plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpprenamed

The content is the same.

plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.hrenamed+14−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013,2015 Canonical Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it
55 * under the terms of the GNU Lesser General Public License, as
@@ -21,14 +21,14 @@
2121#ifndef UBUNTUGESTURES_TIMESOURCE_H
2222#define UBUNTUGESTURES_TIMESOURCE_H
2323
24−#include "UbuntuGesturesQmlGlobal.h"
24+#include "UbuntuGesturesGlobal.h"
2525#include <QSharedPointer>
2626
2727namespace UbuntuGestures {
2828/*
2929 Interface for a time source.
3030 */
31−class UBUNTUGESTURESQML_EXPORT TimeSource {
31+class UBUNTUGESTURES_EXPORT TimeSource {
3232public:
3333 virtual ~TimeSource() {}
3434 /* Returns the current time in milliseconds since some reference time in the past. */
@@ -40,7 +40,7 @@ typedef QSharedPointer<TimeSource> SharedTimeSource;
4040 Implementation of a time source
4141 */
4242class RealTimeSourcePrivate;
43−class RealTimeSource : public TimeSource {
43+class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
4444public:
4545 RealTimeSource();
4646 virtual ~RealTimeSource();
@@ -49,6 +49,16 @@ private:
4949 RealTimeSourcePrivate *d;
5050};
5151
52+/*
53+ A fake time source, useful for tests
54+ */
55+class FakeTimeSource : public TimeSource {
56+public:
57+ FakeTimeSource() { m_msecsSinceReference = 0; }
58+ qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59+ qint64 m_msecsSinceReference;
60+};
61+
5262} // namespace UbuntuGestures
5363
5464#endif // UBUNTUGESTURES_TIMESOURCE_H
libs/UbuntuGestures/Timer.cpp+63−10
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -58,13 +58,36 @@ void Timer::setSingleShot(bool value)
5858
5959/////////////////////////////////// FakeTimer //////////////////////////////////
6060
61−FakeTimer::FakeTimer(QObject *parent)
61+FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
6262 : UbuntuGestures::AbstractTimer(parent)
6363 , m_interval(0)
6464 , m_singleShot(false)
65+ , m_timeSource(timeSource)
6566{
6667}
6768
69+void FakeTimer::update()
70+{
71+ if (!isRunning()) {
72+ return;
73+ }
74+
75+ if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76+ if (isSingleShot()) {
77+ stop();
78+ } else {
79+ m_nextTimeoutTime += interval();
80+ }
81+ Q_EMIT timeout();
82+ }
83+}
84+
85+void FakeTimer::start()
86+{
87+ AbstractTimer::start();
88+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
89+}
90+
6891int FakeTimer::interval() const
6992{
7093 return m_interval;
@@ -87,23 +110,53 @@ void FakeTimer::setSingleShot(bool value)
87110
88111/////////////////////////////////// FakeTimerFactory //////////////////////////////////
89112
90−AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
113+FakeTimerFactory::FakeTimerFactory()
91114{
92− FakeTimer *fakeTimer = new FakeTimer(parent);
93−
94− timers.append(fakeTimer);
115+ m_timeSource.reset(new FakeTimeSource);
116+}
95117
96− return fakeTimer;
118+FakeTimerFactory::~FakeTimerFactory()
119+{
120+ for (int i = 0; i < timers.count(); ++i) {
121+ FakeTimer *timer = timers[i].data();
122+ if (timer) {
123+ delete timer;
124+ }
125+ }
97126}
98127
99−void FakeTimerFactory::makeRunningTimersTimeout()
128+void FakeTimerFactory::updateTime(qint64 targetTime)
100129{
130+ qint64 minTimeoutTime = targetTime;
131+
132+ for (int i = 0; i < timers.count(); ++i) {
133+ FakeTimer *timer = timers[i].data();
134+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
135+ minTimeoutTime = timer->nextTimeoutTime();
136+ }
137+ }
138+
139+ m_timeSource->m_msecsSinceReference = minTimeoutTime;
140+
101141 for (int i = 0; i < timers.count(); ++i) {
102142 FakeTimer *timer = timers[i].data();
103− if (timer && timer->isRunning()) {
104− timer->emitTimeout();
143+ if (timer) {
144+ timer->update();
105145 }
106146 }
147+
148+ if (m_timeSource->msecsSinceReference() < targetTime) {
149+ updateTime(targetTime);
150+ }
151+}
152+
153+AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
154+{
155+ FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
156+
157+ timers.append(fakeTimer);
158+
159+ return fakeTimer;
107160}
108161
109162} // namespace UbuntuGestures
libs/UbuntuGestures/Timer.h+15−3
@@ -18,6 +18,7 @@
1818#define UBUNTUGESTURES_TIMER_H
1919
2020#include "UbuntuGesturesGlobal.h"
21+#include "TimeSource.h"
2122
2223#include <QObject>
2324#include <QPointer>
@@ -66,17 +67,21 @@ class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer
6667{
6768 Q_OBJECT
6869public:
69− FakeTimer(QObject *parent = nullptr);
70+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
7071
71− virtual void emitTimeout() { Q_EMIT timeout(); }
72+ void update();
73+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
7274
7375 int interval() const override;
7476 void setInterval(int msecs) override;
77+ void start() override;
7578 bool isSingleShot() const override;
7679 void setSingleShot(bool value) override;
7780private:
7881 int m_interval;
7982 bool m_singleShot;
83+ SharedTimeSource m_timeSource;
84+ qint64 m_nextTimeoutTime;
8085};
8186
8287class UBUNTUGESTURES_EXPORT AbstractTimerFactory
@@ -95,9 +100,16 @@ public:
95100class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
96101{
97102public:
103+ FakeTimerFactory();
104+ virtual ~FakeTimerFactory();
105+
106+ void updateTime(qint64 msecsSinceReference);
107+ QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108+
98109 AbstractTimer *createTimer(QObject *parent = nullptr) override;
99− void makeRunningTimersTimeout();
100110 QList<QPointer<FakeTimer>> timers;
111+private:
112+ QSharedPointer<FakeTimeSource> m_timeSource;
101113};
102114
103115} // namespace UbuntuGestures
libs/UbuntuGestures/TouchRegistry.cpp+12−14
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -95,10 +95,6 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
9595 // for each point and there should not be many active points at any given moment.
9696 // But having three nested for-loops does scare.
9797
98− // TODO: Don't send it to the object that is already receiving the regular event
99− // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
100− // Problem is, we cannnot easily get this information.
101−
10298 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
10399
104100 // Maps an item to the touches in this event he should be informed about.
@@ -118,7 +114,9 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
118114 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119115 CandidateInfo &candidate = touchInfo->candidates[i];
120116 Q_ASSERT(!candidate.item.isNull());
121− touchIdsForItems[candidate.item.data()].append(touchInfo->id);
117+ if (candidate.state != CandidateInfo::InterimOwner) {
118+ touchIdsForItems[candidate.item.data()].append(touchInfo->id);
119+ }
122120 }
123121 }
124122
@@ -260,7 +258,7 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
260258 // TODO: Check if candidate already exists
261259
262260 CandidateInfo candidateInfo;
263− candidateInfo.undecided = true;
261+ candidateInfo.state = CandidateInfo::Undecided;
264262 candidateInfo.item = candidate;
265263 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
266264 *m_timerFactory,
@@ -301,8 +299,8 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
301299 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
302300 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303301 if (candidateInfo.item == candidate) {
304− Q_ASSERT(i > 0 || candidateInfo.undecided);
305− if (i == 0 && !candidateInfo.undecided) {
302+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
303+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
306304 qCritical("TouchRegistry: touch owner is being removed.");
307305 }
308306 delete candidateInfo.inactivityTimer;
@@ -340,7 +338,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
340338 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
341339 CandidateInfo &candidateInfo = touchInfo->candidates[i];
342340 if (candidateInfo.item == candidate) {
343− candidateInfo.undecided = false;
341+ candidateInfo.state = CandidateInfo::Requested;
344342 delete candidateInfo.inactivityTimer;
345343 candidateInfo.inactivityTimer = nullptr;
346344 candidateIndex = i;
@@ -351,7 +349,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
351349 // add it as a candidate if not present yet
352350 if (candidateIndex < 0) {
353351 CandidateInfo candidateInfo;
354− candidateInfo.undecided = false;
352+ candidateInfo.state = CandidateInfo::InterimOwner;
355353 candidateInfo.item = candidate;
356354 candidateInfo.inactivityTimer = nullptr;
357355 touchInfo->candidates.append(candidateInfo);
@@ -407,8 +405,8 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
407405 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
408406 CandidateInfo &candidateInfo = touchInfo->candidates[i];
409407 if (candidateInfo.item == candidate) {
410− Q_ASSERT(i > 0 || candidateInfo.undecided);
411− if (i == 0 && !candidateInfo.undecided) {
408+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
409+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
412410 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
413411 << ") as it already owns touch" << id;
414412 return;
@@ -467,7 +465,7 @@ void TouchRegistry::TouchInfo::init(int id)
467465
468466bool TouchRegistry::TouchInfo::isOwned() const
469467{
470− return !candidates.isEmpty() && !candidates.first().undecided;
468+ return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
471469}
472470
473471bool TouchRegistry::TouchInfo::ended() const
libs/UbuntuGestures/TouchRegistry.h+18−4
@@ -79,9 +79,11 @@ namespace UbuntuGestures {
7979 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
8080 would be instead:
8181 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82− return true;
83− He would then be notified once ownership has been granted to him, from which point onwards he could
84− safely assume other TouchRegistry users wouldn't snatch this touch away from him.
82+ touchEvent->accept();
83+ He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
84+ will keep sending touch updates to him already). Eventually he will be notified once ownership has
85+ been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
86+ other TouchRegistry users wouldn't snatch this touch away from him.
8587
8688 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
8789
@@ -132,7 +134,19 @@ private Q_SLOTS:
132134private:
133135 class CandidateInfo {
134136 public:
135− bool undecided;
137+ enum {
138+ // A candidate owner that doesn't yet know for sure whether he wants the touch point
139+ // (gesture recognition is stilll going on)
140+ Undecided = 0,
141+ // A candidate owner that wants the touch but hasn't been granted it yet,
142+ // most likely because there's an undecided candidate with higher priority
143+ Requested = 1,
144+ // An item that is the interim owner of the touch, receiving QTouchEvents of it
145+ // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
146+ // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
147+ // yet because of undecided candidates higher up.
148+ InterimOwner = 2
149+ } state;
136150 // TODO: Prune candidates that become null and resolve ownership accordingly.
137151 QPointer<QQuickItem> item;
138152 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
plugins/Dash/CardCreator.js+18−5
@@ -64,11 +64,26 @@ var kArtShapeHolderCode = 'Item { \n\
6464 active: cardData && cardData["art"] || false; \n\
6565 asynchronous: root.asynchronous; \n\
6666 visible: status == Loader.Ready; \n\
67− sourceComponent: UbuntuShape { \n\
67+ sourceComponent: Item { \n\
6868 id: artShape; \n\
6969 objectName: "artShape"; \n\
70− radius: "medium"; \n\
70+ property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
7171 visible: image.status == Image.Ready; \n\
72+ readonly property alias image: artImage.image; \n\
73+ ShaderEffectSource { \n\
74+ id: artShapeSource; \n\
75+ sourceItem: artImage; \n\
76+ anchors.centerIn: parent; \n\
77+ width: 1; \n\
78+ height: 1; \n\
79+ hideSource: doShapeItem; \n\
80+ } \n\
81+ Shape { \n\
82+ image: artShapeSource; \n\
83+ anchors.fill: parent; \n\
84+ visible: doShapeItem; \n\
85+ radius: "medium"; \n\
86+ } \n\
7287 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
7388 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
7489 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
@@ -87,11 +102,9 @@ var kArtShapeHolderCode = 'Item { \n\
87102 objectName: "artImage"; \n\
88103 source: cardData && cardData["art"] || ""; \n\
89104 asynchronous: root.asynchronous; \n\
90− visible: false; \n\
91105 width: %2; \n\
92106 height: %3; \n\
93107 } \n\
94− image: artImage.image; \n\
95108 } \n\
96109 } \n\
97110 }\n';
@@ -322,7 +335,7 @@ function cardString(template, components) {
322335
323336 var hasArt = components["art"] && components["art"]["field"] || false;
324337 var hasSummary = components["summary"] || false;
325− var artAndSummary = hasArt && hasSummary;
338+ var artAndSummary = hasArt && hasSummary && components["art"]["conciergeMode"] !== true;
326339 var isHorizontal = template["card-layout"] === "horizontal";
327340 var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
328341 (hasSummary && (template["card-background"] || components["background"]));
plugins/Ubuntu/Gestures/CMakeLists.txt+1−1
@@ -4,10 +4,10 @@ remove_definitions(-DQT_NO_KEYWORDS)
44set(UbuntuGesturesQml_SOURCES
55 plugin.cpp
66 AxisVelocityCalculator.cpp
7+ Damper.cpp
78 Direction.cpp
89 DirectionalDragArea.cpp
910 PressedOutsideNotifier.cpp
10− TimeSource.cpp
1111 TouchDispatcher.cpp
1212 TouchGate.cpp
1313)
plugins/Ubuntu/Gestures/Damper.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Damper.h"
18+#include <QDebug>
19+
20+QDebug operator<<(QDebug dbg, const DampedPointF &p)
21+{
22+ dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23+ return dbg.space();
24+}
plugins/Ubuntu/Gestures/Damper.h+2
@@ -84,4 +84,6 @@ private:
8484 Damper<qreal> m_y;
8585};
8686
87+QDebug operator<<(QDebug dbg, const DampedPointF &p);
88+
8789#endif // UBUNTU_GESTURES_DAMPER_H
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+322−323
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
2121
2222#include <QQuickWindow>
2323#include <QtCore/qmath.h>
24+#include <QScreen>
2425#include <QDebug>
2526
2627#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
3334#include "TouchRegistry.h"
3435#include "UnownedTouchEvent.h"
3536
37+#include "DirectionalDragArea_p.h"
38+
3639using namespace UbuntuGestures;
3740
3841#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@ using namespace UbuntuGestures;
4043#include "DebugHelpers.h"
4144
4245namespace {
43−const char *statusToString(DirectionalDragArea::Status status)
46+const char *statusToString(DirectionalDragAreaPrivate::Status status)
4447{
45− if (status == DirectionalDragArea::WaitingForTouch) {
48+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
4649 return "WaitingForTouch";
47− } else if (status == DirectionalDragArea::Undecided) {
50+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
4851 return "Undecided";
4952 } else {
5053 return "Recognized";
@@ -56,205 +59,168 @@ const char *statusToString(DirectionalDragArea::Status status)
5659#define ddaDebug(params) ((void)0)
5760#endif // DIRECTIONALDRAGAREA_DEBUG
5861
59−
6062DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
6163 : QQuickItem(parent)
62− , m_status(WaitingForTouch)
63− , m_sceneDistance(0)
64− , m_touchId(-1)
65− , m_direction(Direction::Rightwards)
66− , m_wideningAngle(0)
67− , m_wideningFactor(0)
68− , m_distanceThreshold(0)
69− , m_distanceThresholdSquared(0.)
70− , m_minSpeed(0)
71− , m_maxSilenceTime(200)
72− , m_silenceTime(0)
73− , m_compositionTime(60)
74− , m_numSamplesOnLastSpeedCheck(0)
75− , m_recognitionTimer(0)
76− , m_velocityCalculator(0)
77− , m_timeSource(new RealTimeSource)
78− , m_activeTouches(m_timeSource)
79−{
80− setRecognitionTimer(new Timer(this));
81− m_recognitionTimer->setInterval(60);
82− m_recognitionTimer->setSingleShot(false);
83−
84− m_velocityCalculator = new AxisVelocityCalculator(this);
85−
86− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87− connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
88−}
89−
90−Direction::Type DirectionalDragArea::direction() const
91−{
92− return m_direction;
93−}
94−
95−void DirectionalDragArea::setDirection(Direction::Type direction)
96−{
97− if (direction != m_direction) {
98− m_direction = direction;
99− Q_EMIT directionChanged(m_direction);
100− }
101−}
102−
103−void DirectionalDragArea::setMaxDeviation(qreal value)
104−{
105− if (m_dampedScenePos.maxDelta() != value) {
106− m_dampedScenePos.setMaxDelta(value);
107− Q_EMIT maxDeviationChanged(value);
108− }
109−}
110−
111−qreal DirectionalDragArea::wideningAngle() const
64+ , d(new DirectionalDragAreaPrivate(this))
11265{
113− return m_wideningAngle;
114−}
115−
116−void DirectionalDragArea::setWideningAngle(qreal angle)
117−{
118− if (angle == m_wideningAngle)
119− return;
66+ d->setRecognitionTimer(new Timer(this));
67+ d->recognitionTimer->setInterval(d->maxTime);
68+ d->recognitionTimer->setSingleShot(true);
12069
121− m_wideningAngle = angle;
122−
123− // wideningFactor = pow(cosine(angle), 2)
124− {
125− qreal angleRadians = angle * M_PI / 180.0;
126− m_wideningFactor = qCos(angleRadians);
127− m_wideningFactor = m_wideningFactor * m_wideningFactor;
128− }
129−
130− Q_EMIT wideningAngleChanged(angle);
70+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
71+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
13172}
13273
133−void DirectionalDragArea::setDistanceThreshold(qreal value)
74+Direction::Type DirectionalDragArea::direction() const
13475{
135− if (m_distanceThreshold != value) {
136− m_distanceThreshold = value;
137− m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138− Q_EMIT distanceThresholdChanged(value);
139− }
76+ return d->direction;
14077}
14178
142−void DirectionalDragArea::setMinSpeed(qreal value)
79+void DirectionalDragArea::setDirection(Direction::Type direction)
14380{
144− if (m_minSpeed != value) {
145− m_minSpeed = value;
146− Q_EMIT minSpeedChanged(value);
81+ if (direction != d->direction) {
82+ d->direction = direction;
83+ Q_EMIT directionChanged(d->direction);
14784 }
14885}
14986
150−void DirectionalDragArea::setMaxSilenceTime(int value)
87+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
15188{
152− if (m_maxSilenceTime != value) {
153− m_maxSilenceTime = value;
154− Q_EMIT maxSilenceTimeChanged(value);
89+ if (distanceThreshold != value) {
90+ distanceThreshold = value;
91+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
15592 }
15693}
15794
158−void DirectionalDragArea::setCompositionTime(int value)
95+void DirectionalDragAreaPrivate::setMaxTime(int value)
15996{
160− if (m_compositionTime != value) {
161− m_compositionTime = value;
162− Q_EMIT compositionTimeChanged(value);
97+ if (maxTime != value) {
98+ maxTime = value;
99+ recognitionTimer->setInterval(maxTime);
163100 }
164101}
165102
166−void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
103+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
167104{
168105 int interval = 0;
169106 bool timerWasRunning = false;
170107 bool wasSingleShot = false;
171108
172109 // can be null when called from the constructor
173− if (m_recognitionTimer) {
174− interval = m_recognitionTimer->interval();
175− timerWasRunning = m_recognitionTimer->isRunning();
176− if (m_recognitionTimer->parent() == this) {
177− delete m_recognitionTimer;
110+ if (recognitionTimer) {
111+ interval = recognitionTimer->interval();
112+ timerWasRunning = recognitionTimer->isRunning();
113+ if (recognitionTimer->parent() == this) {
114+ delete recognitionTimer;
178115 }
179116 }
180117
181− m_recognitionTimer = timer;
118+ recognitionTimer = timer;
182119 timer->setInterval(interval);
183120 timer->setSingleShot(wasSingleShot);
184121 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
185− this, &DirectionalDragArea::checkSpeed);
122+ this, &DirectionalDragAreaPrivate::rejectGesture);
186123 if (timerWasRunning) {
187− m_recognitionTimer->start();
124+ recognitionTimer->start();
188125 }
189126}
190127
191−void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
128+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
192129{
193− m_timeSource = timeSource;
194− m_velocityCalculator->setTimeSource(timeSource);
195− m_activeTouches.m_timeSource = timeSource;
130+ this->timeSource = timeSource;
131+ activeTouches.m_timeSource = timeSource;
196132}
197133
198134qreal DirectionalDragArea::distance() const
199135{
200− if (Direction::isHorizontal(m_direction)) {
201− return m_previousPos.x() - m_startPos.x();
136+ if (Direction::isHorizontal(d->direction)) {
137+ return d->publicPos.x() - d->startPos.x();
202138 } else {
203− return m_previousPos.y() - m_startPos.y();
139+ return d->publicPos.y() - d->startPos.y();
204140 }
205141}
206142
207−void DirectionalDragArea::updateSceneDistance()
143+void DirectionalDragAreaPrivate::updateSceneDistance()
208144{
209− QPointF totalMovement = m_previousScenePos - m_startScenePos;
210− m_sceneDistance = projectOntoDirectionVector(totalMovement);
145+ QPointF totalMovement = publicScenePos - startScenePos;
146+ sceneDistance = projectOntoDirectionVector(totalMovement);
211147}
212148
213149qreal DirectionalDragArea::sceneDistance() const
214150{
215− return m_sceneDistance;
151+ return d->sceneDistance;
216152}
217153
218154qreal DirectionalDragArea::touchX() const
219155{
220− return m_previousPos.x();
156+ return d->publicPos.x();
221157}
222158
223159qreal DirectionalDragArea::touchY() const
224160{
225− return m_previousPos.y();
161+ return d->publicPos.y();
226162}
227163
228164qreal DirectionalDragArea::touchSceneX() const
229165{
230− return m_previousScenePos.x();
166+ return d->publicScenePos.x();
231167}
232168
233169qreal DirectionalDragArea::touchSceneY() const
234170{
235− return m_previousScenePos.y();
171+ return d->publicScenePos.y();
172+}
173+
174+bool DirectionalDragArea::dragging() const
175+{
176+ return d->status == DirectionalDragAreaPrivate::Recognized;
177+}
178+
179+bool DirectionalDragArea::pressed() const
180+{
181+ return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
182+}
183+
184+bool DirectionalDragArea::immediateRecognition() const
185+{
186+ return d->immediateRecognition;
187+}
188+
189+void DirectionalDragArea::setImmediateRecognition(bool enabled)
190+{
191+ if (d->immediateRecognition != enabled) {
192+ d->immediateRecognition = enabled;
193+ Q_EMIT immediateRecognitionChanged(enabled);
194+ }
195+}
196+
197+void DirectionalDragArea::removeTimeConstraints()
198+{
199+ d->setMaxTime(60 * 60 * 1000);
200+ d->compositionTime = 0;
201+ ddaDebug("removed time constraints");
236202}
237203
238204bool DirectionalDragArea::event(QEvent *event)
239205{
240206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241− touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
207+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242208 return true;
243209 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244− unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
210+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245211 return true;
246212 } else {
247213 return QQuickItem::event(event);
248214 }
249215}
250216
251−void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
217+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252218{
253219 if (event->gained()) {
254220 QVector<int> ids;
255221 ids.append(event->touchId());
256222 ddaDebug("grabbing touch");
257− grabTouchPoints(ids);
223+ q->grabTouchPoints(ids);
258224
259225 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260226 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +228,35 @@ void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
262228 //
263229 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264230 // TODO: Remove this workaround once we start using Qt 5.4
265− if (window()) {
266− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
267− if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
231+ if (q->window()) {
232+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
233+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268234 ddaDebug("removing mouse grabber");
269− window()->mouseGrabberItem()->ungrabMouse();
235+ q->window()->mouseGrabberItem()->ungrabMouse();
270236 }
271237 }
272238 } else {
273239 // We still wanna know when it ends for keeping the composition time window up-to-date
274− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
240+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
275241
276242 setStatus(WaitingForTouch);
277243 }
278244}
279245
280−void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
246+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281247{
282248 QTouchEvent *event = unownedTouchEvent->touchEvent();
283249
284250 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285251
286− ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
252+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287253
288− switch (m_status) {
254+ switch (status) {
289255 case WaitingForTouch:
290256 // do nothing
291257 break;
292258 case Undecided:
293− Q_ASSERT(isEnabled() && isVisible());
259+ Q_ASSERT(q->isEnabled() && q->isVisible());
294260 unownedTouchEvent_undecided(unownedTouchEvent);
295261 break;
296262 default: // Recognized:
@@ -298,18 +264,18 @@ void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent
298264 break;
299265 }
300266
301− m_activeTouches.update(event);
267+ activeTouches.update(event);
302268}
303269
304−void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
270+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305271{
306272 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307273 if (!touchPoint) {
308− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
274+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309275 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310276 "Considering it as released.";
311277
312− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
278+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313279 setStatus(WaitingForTouch);
314280 return;
315281 }
@@ -319,48 +285,42 @@ void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unowned
319285 if (touchPoint->state() == Qt::TouchPointReleased) {
320286 // touch has ended before recognition concluded
321287 ddaDebug("Touch has ended before recognition concluded");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
323− emitSignalIfTapped();
288+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
324289 setStatus(WaitingForTouch);
325290 return;
326291 }
327292
328− m_previousDampedScenePos.setX(m_dampedScenePos.x());
329− m_previousDampedScenePos.setY(m_dampedScenePos.y());
330− m_dampedScenePos.update(touchScenePos);
331− updateVelocityCalculator(touchScenePos);
332−
333− if (!pointInsideAllowedArea()) {
334− ddaDebug("Rejecting gesture because touch point is outside allowed area.");
335− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
336− // We still wanna know when it ends for keeping the composition time window up-to-date
337− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
338− setStatus(WaitingForTouch);
339− return;
340− }
293+ previousDampedScenePos.setX(dampedScenePos.x());
294+ previousDampedScenePos.setY(dampedScenePos.y());
295+ dampedScenePos.update(touchScenePos);
341296
342297 if (!movingInRightDirection()) {
343298 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
299+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345300 // We still wanna know when it ends for keeping the composition time window up-to-date
346− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
301+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
347302 setStatus(WaitingForTouch);
348303 return;
349304 }
350305
351− setPreviousPos(touchPoint->pos());
352− setPreviousScenePos(touchScenePos);
353−
354306 if (isWithinTouchCompositionWindow()) {
355307 // There's still time for some new touch to appear and ruin our party as it would be combined
356− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
308+ // with our touchId one and therefore deny the possibility of a single-finger gesture.
357309 ddaDebug("Sill within composition window. Let's wait more.");
358310 return;
359311 }
360312
361− if (movedFarEnough(touchScenePos)) {
362− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
313+ if (movedFarEnoughAlongGestureAxis()) {
314+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363315 setStatus(Recognized);
316+ setPublicPos(touchPoint->pos());
317+ setPublicScenePos(touchScenePos);
318+ } else if (isPastMaxDistance()) {
319+ ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
320+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
321+ // We still wanna know when it ends for keeping the composition time window up-to-date
322+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
323+ setStatus(WaitingForTouch);
364324 } else {
365325 ddaDebug("Didn't move far enough yet. Let's wait more.");
366326 }
@@ -371,29 +331,29 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
371331 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372332 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373333
374− ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
334+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375335
376336 if (!isEnabled() || !isVisible()) {
377337 QQuickItem::touchEvent(event);
378338 return;
379339 }
380340
381− switch (m_status) {
382− case WaitingForTouch:
383− touchEvent_absent(event);
341+ switch (d->status) {
342+ case DirectionalDragAreaPrivate::WaitingForTouch:
343+ d->touchEvent_absent(event);
384344 break;
385− case Undecided:
386− touchEvent_undecided(event);
345+ case DirectionalDragAreaPrivate::Undecided:
346+ d->touchEvent_undecided(event);
387347 break;
388348 default: // Recognized:
389− touchEvent_recognized(event);
349+ d->touchEvent_recognized(event);
390350 break;
391351 }
392352
393− m_activeTouches.update(event);
353+ d->activeTouches.update(event);
394354}
395355
396−void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
356+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397357{
398358 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399359
@@ -423,36 +383,40 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
423383 allGood = false;
424384 } else {
425385 // that's our candidate
426− m_touchId = touchPoint.id();
427386 newTouchPoint = &touchPoint;
428387 }
429388 }
430389 }
431390
391+ if (allGood) {
392+ allGood = sanityCheckRecognitionProperties();
393+ if (!allGood) {
394+ qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
395+ " is impossible");
396+ }
397+ }
398+
432399 if (allGood) {
433400 Q_ASSERT(newTouchPoint);
434401
435− m_startPos = newTouchPoint->pos();
436− m_startScenePos = newTouchPoint->scenePos();
437− m_touchId = newTouchPoint->id();
438− m_dampedScenePos.reset(m_startScenePos);
439− m_velocityCalculator->setTrackedPosition(0.);
440− m_velocityCalculator->reset();
441− m_numSamplesOnLastSpeedCheck = 0;
442− m_silenceTime = 0;
443− setPreviousPos(m_startPos);
444− setPreviousScenePos(m_startScenePos);
402+ startPos = newTouchPoint->pos();
403+ startScenePos = newTouchPoint->scenePos();
404+ touchId = newTouchPoint->id();
405+ dampedScenePos.reset(startScenePos);
406+ setPublicPos(startPos);
407+
408+ setPublicScenePos(startScenePos);
445409 updateSceneDirectionVector();
446410
447411 if (recognitionIsDisabled()) {
448412 // Behave like a dumb TouchArea
449413 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
414+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451415 setStatus(Recognized);
452416 event->accept();
453417 } else {
454418 // just monitor the touch points for now.
455− TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
419+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456420
457421 setStatus(Undecided);
458422 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +429,11 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
465429 }
466430}
467431
468−void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
432+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469433{
470− Q_ASSERT(event->type() == QEvent::TouchBegin);
471434 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472435
473− // We're not interested in new touch points. We already have our candidate (m_touchId).
436+ // We're not interested in new touch points. We already have our candidate (touchId).
474437 // But we do want to know when those new touches end for keeping the composition time
475438 // window up-to-date
476439 event->ignore();
@@ -480,63 +443,60 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
480443 // multi-finger drags are not accepted
481444 ddaDebug("Multi-finger drags are not accepted");
482445
483− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
446+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484447 // We still wanna know when it ends for keeping the composition time window up-to-date
485− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
448+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
486449
487450 setStatus(WaitingForTouch);
488451 }
489452}
490453
491−void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
454+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492455{
493456 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494457
495458 if (!touchPoint) {
496− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
459+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497460 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498461 "Considering it as released.";
499462 setStatus(WaitingForTouch);
500463 } else {
501− setPreviousPos(touchPoint->pos());
502− setPreviousScenePos(touchPoint->scenePos());
464+ setPublicPos(touchPoint->pos());
465+ setPublicScenePos(touchPoint->scenePos());
503466
504467 if (touchPoint->state() == Qt::TouchPointReleased) {
505− emitSignalIfTapped();
506468 setStatus(WaitingForTouch);
507469 }
508470 }
509471}
510472
511−void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
473+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512474{
513475 for (int i = 0; i < touchPoints.count(); ++i) {
514476 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515477 if (touchPoint.state() == Qt::TouchPointPressed) {
516− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
478+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517479 }
518480 }
519481}
520482
521−bool DirectionalDragArea::recognitionIsDisabled() const
483+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522484{
523− return distanceThreshold() <= 0 && compositionTime() <= 0;
485+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524486}
525487
526−void DirectionalDragArea::emitSignalIfTapped()
488+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527489{
528− qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
529− if (touchDuration <= maxTapDuration()) {
530− Q_EMIT tapped();
531− }
490+ return recognitionIsDisabled()
491+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
532492}
533493
534−const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
494+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
535495{
536496 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537497 const QTouchEvent::TouchPoint *touchPoint = 0;
538498 for (int i = 0; i < touchPoints.size(); ++i) {
539− if (touchPoints.at(i).id() == m_touchId) {
499+ if (touchPoints.at(i).id() == touchId) {
540500 touchPoint = &touchPoints.at(i);
541501 break;
542502 }
@@ -544,39 +504,13 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
544504 return touchPoint;
545505}
546506
547−bool DirectionalDragArea::pointInsideAllowedArea() const
507+bool DirectionalDragAreaPrivate::movingInRightDirection() const
548508{
549− // NB: Using squared values to avoid computing the square root to find
550− // the length totalMovement
551−
552− QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
553− m_dampedScenePos.y() - m_startScenePos.y());
554−
555− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
556− totalMovement.y() * totalMovement.y();
557−
558− if (squaredTotalMovSize == 0.) {
559− // didn't move
560− return true;
561− }
562−
563− qreal projectedMovement = projectOntoDirectionVector(totalMovement);
564−
565−
566− qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
567−
568− // Same as:
569− // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
570− return cosineAngleSquared >= m_wideningFactor;
571−}
572−
573−bool DirectionalDragArea::movingInRightDirection() const
574−{
575− if (m_direction == Direction::Horizontal) {
509+ if (direction == Direction::Horizontal) {
576510 return true;
577511 } else {
578− QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
579− m_dampedScenePos.y() - m_previousDampedScenePos.y());
512+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
513+ dampedScenePos.y() - previousDampedScenePos.y());
580514
581515 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582516
@@ -584,96 +518,95 @@ bool DirectionalDragArea::movingInRightDirection() const
584518 }
585519}
586520
587−bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
521+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588522{
589− if (m_distanceThreshold <= 0.) {
523+ if (distanceThreshold <= 0.) {
590524 // distance threshold check is disabled
591525 return true;
592526 } else {
593− QPointF totalMovement(point.x() - m_startScenePos.x(),
594− point.y() - m_startScenePos.y());
527+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
528+ dampedScenePos.y() - startScenePos.y());
529+
530+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
595531
596− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
597− totalMovement.y() * totalMovement.y();
532+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
533+ << ", distanceThreshold=" << distanceThreshold);
598534
599− return squaredTotalMovSize > m_distanceThresholdSquared;
535+ if (direction == Direction::Horizontal) {
536+ return qAbs(scalarProjection) > distanceThreshold;
537+ } else {
538+ return scalarProjection > distanceThreshold;
539+ }
600540 }
601541}
602542
603−void DirectionalDragArea::checkSpeed()
543+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
604544{
605− Q_ASSERT(m_status == Undecided);
545+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
546+ dampedScenePos.y() - startScenePos.y());
606547
607− if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
608− qreal speed = qFabs(m_velocityCalculator->calculate());
609− qreal minSpeedMsecs = m_minSpeed / 1000.0;
548+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
549+ return squaredDistance > maxDistance*maxDistance;
550+}
610551
611− if (speed < minSpeedMsecs) {
612− ddaDebug("Rejecting gesture because it's below minimum speed.");
613− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
614− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
615− setStatus(WaitingForTouch);
552+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
553+{
554+ if (!q->isEnabled() || !q->isVisible()) {
555+ if (status == Undecided) {
556+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
557+ // We still wanna know when it ends for keeping the composition time window up-to-date
558+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
616559 }
617− }
618−
619− if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
620− m_silenceTime += m_recognitionTimer->interval();
621560
622− if (m_silenceTime > m_maxSilenceTime) {
623− ddaDebug("Rejecting gesture because its silence time has been exceeded.");
624− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
561+ if (status != WaitingForTouch) {
562+ ddaDebug("Resetting status because got disabled or made invisible");
626563 setStatus(WaitingForTouch);
627564 }
628− } else {
629− m_silenceTime = 0;
630565 }
631− m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
632566}
633567
634−void DirectionalDragArea::giveUpIfDisabledOrInvisible()
568+void DirectionalDragAreaPrivate::rejectGesture()
635569{
636− if (!isEnabled() || !isVisible()) {
637− if (m_status == Undecided) {
638− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
639− // We still wanna know when it ends for keeping the composition time window up-to-date
640− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
641− }
570+ if (status == Undecided) {
571+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
642572
643− if (m_status != WaitingForTouch) {
644− ddaDebug("Resetting status because got disabled or made invisible");
645− setStatus(WaitingForTouch);
646− }
573+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
574+ // We still wanna know when it ends for keeping the composition time window up-to-date
575+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
576+
577+ setStatus(WaitingForTouch);
647578 }
648579}
649580
650−void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
581+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
651582{
652− if (newStatus == m_status)
583+ if (newStatus == status)
653584 return;
654585
655− DirectionalDragArea::Status oldStatus = m_status;
586+ Status oldStatus = status;
656587
657588 if (oldStatus == Undecided) {
658− m_recognitionTimer->stop();
589+ recognitionTimer->stop();
659590 }
660591
661− m_status = newStatus;
662− Q_EMIT statusChanged(m_status);
592+ status = newStatus;
593+ Q_EMIT statusChanged(status);
663594
664595 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665596
666597 switch (newStatus) {
667598 case WaitingForTouch:
668− Q_EMIT draggingChanged(false);
599+ if (oldStatus == Recognized) {
600+ Q_EMIT q->draggingChanged(false);
601+ }
602+ Q_EMIT q->pressedChanged(false);
669603 break;
670604 case Undecided:
671− m_recognitionTimer->start();
672− Q_EMIT draggingChanged(true);
605+ recognitionTimer->start();
606+ Q_EMIT q->pressedChanged(true);
673607 break;
674608 case Recognized:
675− if (oldStatus == WaitingForTouch)
676− Q_EMIT draggingChanged(true);
609+ Q_EMIT q->draggingChanged(true);
677610 break;
678611 default:
679612 // no-op
@@ -681,77 +614,126 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
681614 }
682615}
683616
684−void DirectionalDragArea::setPreviousPos(const QPointF &point)
617+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685618{
686− bool xChanged = m_previousPos.x() != point.x();
687− bool yChanged = m_previousPos.y() != point.y();
619+ bool xChanged = publicPos.x() != point.x();
620+ bool yChanged = publicPos.y() != point.y();
688621
689− m_previousPos = point;
622+ // Public position should not get updated while the gesture is still being recognized
623+ // (ie, Undecided status).
624+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
625+
626+ if (status == Recognized && !recognitionIsDisabled()) {
627+ // When the gesture finally gets recognized, the finger will likely be
628+ // reasonably far from the edge. If we made the contentX immediately
629+ // follow the finger position it would be visually unpleasant as it
630+ // would appear right next to the user's finger out of nowhere (ie,
631+ // it would jump). Instead, we make contentX go towards the user's
632+ // finger in several steps. ie., in an animated way.
633+ QPointF delta = point - publicPos;
634+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
635+ publicPos.rx() += 0.4 * delta.x();
636+ publicPos.ry() += 0.4 * delta.y();
637+ } else {
638+ // no smoothing when initializing or if gesture recognition was immediate as there will
639+ // be no jump.
640+ publicPos = point;
641+ }
690642
691643 if (xChanged) {
692− Q_EMIT touchXChanged(point.x());
693− if (Direction::isHorizontal(m_direction))
694− Q_EMIT distanceChanged(distance());
644+ Q_EMIT q->touchXChanged(publicPos.x());
645+ if (Direction::isHorizontal(direction))
646+ Q_EMIT q->distanceChanged(q->distance());
695647 }
696648
697649 if (yChanged) {
698− Q_EMIT touchYChanged(point.y());
699− if (Direction::isVertical(m_direction))
700− Q_EMIT distanceChanged(distance());
650+ Q_EMIT q->touchYChanged(publicPos.y());
651+ if (Direction::isVertical(direction))
652+ Q_EMIT q->distanceChanged(q->distance());
701653 }
702654}
703655
704−void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
656+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705657{
706− bool xChanged = m_previousScenePos.x() != point.x();
707− bool yChanged = m_previousScenePos.y() != point.y();
658+ bool xChanged = publicScenePos.x() != point.x();
659+ bool yChanged = publicScenePos.y() != point.y();
708660
709661 if (!xChanged && !yChanged)
710662 return;
711663
712− qreal oldSceneDistance = sceneDistance();
713− m_previousScenePos = point;
664+ // Public position should not get updated while the gesture is still being recognized
665+ // (ie, Undecided status).
666+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
667+
668+ qreal oldSceneDistance = sceneDistance;
669+
670+ if (status == Recognized && !recognitionIsDisabled()) {
671+ // When the gesture finally gets recognized, the finger will likely be
672+ // reasonably far from the edge. If we made the contentX immediately
673+ // follow the finger position it would be visually unpleasant as it
674+ // would appear right next to the user's finger out of nowhere (ie,
675+ // it would jump). Instead, we make contentX go towards the user's
676+ // finger in several steps. ie., in an animated way.
677+ QPointF delta = point - publicScenePos;
678+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
679+ publicScenePos.rx() += 0.4 * delta.x();
680+ publicScenePos.ry() += 0.4 * delta.y();
681+ } else {
682+ // no smoothing when initializing or if gesture recognition was immediate as there will
683+ // be no jump.
684+ publicScenePos = point;
685+ }
686+
714687 updateSceneDistance();
715688
716− if (oldSceneDistance != sceneDistance()) {
717− Q_EMIT sceneDistanceChanged(sceneDistance());
689+ if (oldSceneDistance != sceneDistance) {
690+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
718691 }
719692
720693 if (xChanged) {
721− Q_EMIT touchSceneXChanged(point.x());
694+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722695 }
723696
724697 if (yChanged) {
725− Q_EMIT touchSceneYChanged(point.y());
698+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726699 }
727700}
728701
729−void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
702+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
730703{
731− QPointF totalSceneMovement = scenePos - m_startScenePos;
732−
733− qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
704+ return
705+ compositionTime > 0 &&
706+ !activeTouches.isEmpty() &&
707+ timeSource->msecsSinceReference() <=
708+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
709+}
734710
735− m_velocityCalculator->setTrackedPosition(scalarProjection);
711+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
712+{
713+ if (change == QQuickItem::ItemSceneChange) {
714+ if (value.window != nullptr) {
715+ // TODO: Handle window->screen() changes (ie window changing screens)
716+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
717+ d->setPixelsPerMm(pixelsPerMm);
718+ }
719+ }
736720}
737721
738−bool DirectionalDragArea::isWithinTouchCompositionWindow()
722+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
739723{
740− return
741− compositionTime() > 0 &&
742− !m_activeTouches.isEmpty() &&
743− m_timeSource->msecsSinceReference() <=
744− m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
724+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
725+ setDistanceThreshold(4. * pixelsPerMm);
726+ maxDistance = 10. * pixelsPerMm;
745727}
746728
747729//************************** ActiveTouchesInfo **************************
748730
749−DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
731+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750732 : m_timeSource(timeSource)
751733{
752734}
753735
754−void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
736+void ActiveTouchesInfo::update(QTouchEvent *event)
755737{
756738 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757739 // nothing to update
@@ -773,7 +755,7 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
773755}
774756
775757#if ACTIVETOUCHESINFO_DEBUG
776−QString DirectionalDragArea::ActiveTouchesInfo::toString()
758+QString ActiveTouchesInfo::toString()
777759{
778760 QString string = "(";
779761
@@ -791,7 +773,7 @@ QString DirectionalDragArea::ActiveTouchesInfo::toString()
791773}
792774#endif // ACTIVETOUCHESINFO_DEBUG
793775
794−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
776+void ActiveTouchesInfo::addTouchPoint(int touchId)
795777{
796778 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797779 activeTouchInfo.id = touchId;
@@ -802,7 +784,7 @@ void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
802784 #endif
803785}
804786
805−qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
787+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806788{
807789 qint64 result = -1;
808790
@@ -819,7 +801,7 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
819801 return result;
820802}
821803
822−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
804+void ActiveTouchesInfo::removeTouchPoint(int touchId)
823805{
824806 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825807 if (touchId == touchInfo->id) {
@@ -835,7 +817,7 @@ void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
835817 #endif
836818}
837819
838−qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
820+qint64 ActiveTouchesInfo::mostRecentStartTime()
839821{
840822 Q_ASSERT(!m_touchInfoPool.isEmpty());
841823
@@ -851,11 +833,11 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
851833 return highestStartTime;
852834}
853835
854−void DirectionalDragArea::updateSceneDirectionVector()
836+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855837{
856838 QPointF localOrigin(0., 0.);
857839 QPointF localDirection;
858− switch (m_direction) {
840+ switch (direction) {
859841 case Direction::Upwards:
860842 localDirection.rx() = 0.;
861843 localDirection.ry() = -1.;
@@ -873,14 +855,31 @@ void DirectionalDragArea::updateSceneDirectionVector()
873855 localDirection.ry() = 0.;
874856 break;
875857 }
876− QPointF sceneOrigin = mapToScene(localOrigin);
877− QPointF sceneDirection = mapToScene(localDirection);
878− m_sceneDirectionVector = sceneDirection - sceneOrigin;
858+ QPointF sceneOrigin = q->mapToScene(localOrigin);
859+ QPointF sceneDirection = q->mapToScene(localDirection);
860+ sceneDirectionVector = sceneDirection - sceneOrigin;
861+}
862+
863+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
864+{
865+ // same as dot product as sceneDirectionVector is a unit vector
866+ return sceneVector.x() * sceneDirectionVector.x() +
867+ sceneVector.y() * sceneDirectionVector.y();
879868}
880869
881−qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
870+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
871+ : q(q)
872+ , status(WaitingForTouch)
873+ , sceneDistance(0)
874+ , touchId(-1)
875+ , direction(Direction::Rightwards)
876+ , distanceThreshold(0)
877+ , distanceThresholdSquared(0.)
878+ , maxTime(400)
879+ , compositionTime(60)
880+ , immediateRecognition(false)
881+ , recognitionTimer(nullptr)
882+ , timeSource(new RealTimeSource)
883+ , activeTouches(timeSource)
882884{
883− // same as dot product as m_sceneDirectionVector is a unit vector
884− return sceneVector.x() * m_sceneDirectionVector.x() +
885− sceneVector.y() * m_sceneDirectionVector.y();
886885}
plugins/Ubuntu/Gestures/DirectionalDragArea.h+29−202
@@ -18,7 +18,6 @@
1818#define DIRECTIONAL_DRAG_AREA_H
1919
2020#include <QtQuick/QQuickItem>
21−#include "AxisVelocityCalculator.h"
2221#include "UbuntuGesturesQmlGlobal.h"
2322#include "Damper.h"
2423#include "Direction.h"
@@ -29,6 +28,7 @@
2928
3029class TouchOwnershipEvent;
3130class UnownedTouchEvent;
31+class DirectionalDragAreaPrivate;
3232
3333/*
3434 An area that detects axis-aligned single-finger drag gestures
@@ -61,95 +61,31 @@ class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
6161 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
6262 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
6363
64− // The current status of the directional drag gesture area.
65− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
66−
6764 // Whether a drag gesture is taking place
68− // This will be true as long as status is Undecided or Recognized
69− // When a gesture gets rejected, dragging turns to false.
7065 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
7166
72− /////
73− // stuff that will be set in stone at some point
74−
75− // How far the touch point can move away from its expected position before
76− // it causes a rejection in the gesture recognition. This is to compensate
77− // for both noise in the touch input signal and for the natural irregularities
78− // in the finger movement.
79− // Proper value is likely device-specific.
80− Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
81−
82− // Widening angle, in degrees
83− // It's roughly the maximum angle a touch point can make relative to the
84− // axis defined by the compoment's direction for it to be recognized as a
85− // directional drag.
86− Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
87− NOTIFY wideningAngleChanged)
88−
89− // How far a touch point has to move from its initial position in order for
90− // it to be recognized as a directional drag.
91− Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
92− NOTIFY distanceThresholdChanged)
93−
94− // Minimum speed a gesture needs to have in order to be recognized as a
95− // directional drag.
96− // In pixels per second
97− Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
98−
99− // A gesture will be rejected if more than maxSilenceTime milliseconds has
100− // passed since we last got an input event from it (during Undecided state).
101− //
102− // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
103− // finger is still (zero drag speed). In some cases the finger might be moving but
104− // the driver's high noise filtering might cause those silence periods, specially
105− // in the moments succeeding a press (talking about Galaxy Nexus here).
106− Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
107− WRITE setMaxSilenceTime
108− NOTIFY maxSilenceTimeChanged)
67+ // Whether the drag area is pressed.
68+ Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
10969
70+ // Whether a gesture should be Recognized as soon a touch lands on the area.
71+ // With this property enabled it will work pretty much like a MultiPointTouchArea,
72+ // just with a different API.
11073 //
111− /////
112−
113− // Maximum time (in milliseconds) after the start of a given touch point where
114− // subsequent touch starts are grouped with the first one into an N-touches gesture
115− // (e.g. a two-fingers tap or drag).
116− Q_PROPERTY(int compositionTime READ compositionTime
117− WRITE setCompositionTime
118− NOTIFY compositionTimeChanged)
74+ // It's false by default. In most cases you will not want that enabled.
75+ Q_PROPERTY(bool immediateRecognition
76+ READ immediateRecognition
77+ WRITE setImmediateRecognition
78+ NOTIFY immediateRecognitionChanged)
11979
12080 Q_ENUMS(Direction)
121− Q_ENUMS(Status)
12281public:
12382 DirectionalDragArea(QQuickItem *parent = 0);
12483
12584 Direction::Type direction() const;
12685 void setDirection(Direction::Type);
12786
128− // Describes the state of the directional drag gesture.
129− enum Status {
130− // Waiting for a new touch point to land on this area. No gesture is being processed
131− // or tracked.
132− WaitingForTouch,
133−
134− // A touch point has landed on this area but it's not know yet whether it is
135− // performing a drag in the correct direction.
136− // If it's decided that the touch point is not performing a directional drag gesture,
137− // it will be rejected/ignored and status will return to WaitingForTouch.
138− Undecided, //Recognizing,
139−
140− // There's a touch point in this area and it performed a drag in the correct
141− // direction.
142− //
143− // Once recognized, the gesture state will move back to WaitingForTouch only once
144− // that touch point ends. The gesture will remain in the Recognized state even if
145− // the touch point starts moving in other directions or halts.
146− Recognized,
147− };
148− Status status() const { return m_status; }
149−
15087 qreal distance() const;
15188 qreal sceneDistance() const;
152− void updateSceneDistance();
15389
15490 qreal touchX() const;
15591 qreal touchY() const;
@@ -157,152 +93,43 @@ public:
15793 qreal touchSceneX() const;
15894 qreal touchSceneY() const;
15995
160− bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
161−
162− qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
163− void setMaxDeviation(qreal value);
164−
165− qreal wideningAngle() const;
166− void setWideningAngle(qreal value);
167−
168− qreal distanceThreshold() const { return m_distanceThreshold; }
169− void setDistanceThreshold(qreal value);
170−
171− qreal minSpeed() const { return m_minSpeed; }
172− void setMinSpeed(qreal value);
96+ bool dragging() const;
17397
174− int maxSilenceTime() const { return m_maxSilenceTime; }
175− void setMaxSilenceTime(int value);
98+ bool pressed() const;
17699
177− int compositionTime() const { return m_compositionTime; }
178− void setCompositionTime(int value);
179−
180− // Replaces the existing Timer with the given one.
181− //
182− // Useful for providing a fake timer when testing.
183− void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
184−
185− // Useful for testing, where a fake time source can be supplied
186− void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
100+ bool immediateRecognition() const;
101+ void setImmediateRecognition(bool enabled);
187102
188103 bool event(QEvent *e) override;
189104
190− // Maximum time, in milliseconds, between a press and a release, for a touch
191− // sequence to be considered a tap.
192− int maxTapDuration() const { return 300; }
105+ /*
106+ In qmltests, sequences of touch events are sent all at once, unlike in "real life".
107+ Also qmltests might run really slowly, e.g. when run from inside virtual machines.
108+ Thus to remove a variable that qmltests cannot really control, namely time, this
109+ function removes all constraints that are sensible to elapsed time.
110+
111+ This effectively makes the DirectionalDragArea easier to fool.
112+ */
113+ Q_INVOKABLE void removeTimeConstraints();
193114
194115Q_SIGNALS:
195116 void directionChanged(Direction::Type direction);
196− void statusChanged(Status value);
197117 void draggingChanged(bool value);
118+ void pressedChanged(bool value);
198119 void distanceChanged(qreal value);
199120 void sceneDistanceChanged(qreal value);
200− void maxDeviationChanged(qreal value);
201− void wideningAngleChanged(qreal value);
202− void distanceThresholdChanged(qreal value);
203− void minSpeedChanged(qreal value);
204− void maxSilenceTimeChanged(int value);
205− void compositionTimeChanged(int value);
206121 void touchXChanged(qreal value);
207122 void touchYChanged(qreal value);
208123 void touchSceneXChanged(qreal value);
209124 void touchSceneYChanged(qreal value);
210−
211− // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212− // Remove when no longer used or move its implementation to the QML code that uses it
213− // See maxTapDuration()
214− void tapped();
125+ void immediateRecognitionChanged(bool value);
215126
216127protected:
217128 virtual void touchEvent(QTouchEvent *event);
129+ virtual void itemChange(ItemChange change, const ItemChangeData &value);
218130
219−private Q_SLOTS:
220− void checkSpeed();
221− void giveUpIfDisabledOrInvisible();
222−
223−private:
224− void touchEvent_absent(QTouchEvent *event);
225− void touchEvent_undecided(QTouchEvent *event);
226− void touchEvent_recognized(QTouchEvent *event);
227− bool pointInsideAllowedArea() const;
228− bool movingInRightDirection() const;
229− bool movedFarEnough(const QPointF &point) const;
230− const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
231− void setStatus(Status newStatus);
232− void setPreviousPos(const QPointF &point);
233− void setPreviousScenePos(const QPointF &point);
234− void updateVelocityCalculator(const QPointF &point);
235− bool isWithinTouchCompositionWindow();
236− void updateSceneDirectionVector();
237− // returns the scalar projection between the given vector (in scene coordinates)
238− // and m_sceneDirectionVector
239− qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
240− void touchOwnershipEvent(TouchOwnershipEvent *event);
241− void unownedTouchEvent(UnownedTouchEvent *event);
242− void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
243− void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
244− bool recognitionIsDisabled() const;
245− void emitSignalIfTapped();
246−
247− Status m_status;
248−
249− QPointF m_startPos;
250− QPointF m_startScenePos;
251− QPointF m_previousPos;
252− QPointF m_previousScenePos;
253− qreal m_sceneDistance;
254− int m_touchId;
255−
256− // A movement damper is used in some of the gesture recognition calculations
257− // to get rid of noise or small oscillations in the touch position.
258− DampedPointF m_dampedScenePos;
259− QPointF m_previousDampedScenePos;
260−
261− // Unit vector in scene coordinates describing the direction of the gesture recognition
262− QPointF m_sceneDirectionVector;
263−
264− Direction::Type m_direction;
265− qreal m_wideningAngle; // in degrees
266− qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
267− qreal m_distanceThreshold;
268− qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
269− qreal m_minSpeed;
270− int m_maxSilenceTime; // in milliseconds
271− int m_silenceTime; // in milliseconds
272− int m_compositionTime; // in milliseconds
273− int m_numSamplesOnLastSpeedCheck;
274− UbuntuGestures::AbstractTimer *m_recognitionTimer;
275− AxisVelocityCalculator *m_velocityCalculator;
276−
277− UbuntuGestures::SharedTimeSource m_timeSource;
278−
279− // Information about an active touch point
280− struct ActiveTouchInfo {
281− ActiveTouchInfo() : id(-1), startTime(-1) {}
282− bool isValid() const { return id != -1; }
283− void reset() { id = -1; }
284− int id;
285− qint64 startTime;
286− };
287− class ActiveTouchesInfo {
288− public:
289− ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
290− void update(QTouchEvent *event);
291− qint64 touchStartTime(int id);
292− bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
293− qint64 mostRecentStartTime();
294− UbuntuGestures::SharedTimeSource m_timeSource;
295− private:
296− void addTouchPoint(int touchId);
297− void removeTouchPoint(int touchId);
298− #if ACTIVETOUCHESINFO_DEBUG
299− QString toString();
300− #endif
301−
302− Pool<ActiveTouchInfo> m_touchInfoPool;
303− } m_activeTouches;
304−
305− friend class tst_DirectionalDragArea;
131+public: // so tests can access it
132+ DirectionalDragAreaPrivate *d;
306133};
307134
308135#endif // DIRECTIONAL_DRAG_AREA_H
plugins/Ubuntu/Gestures/DirectionalDragArea_p.hnew file+167
@@ -0,0 +1,167 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DIRECTIONAL_DRAG_AREA_PRIV_H
18+#define DIRECTIONAL_DRAG_AREA_PRIV_H
19+
20+// Information about an active touch point
21+struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
22+ ActiveTouchInfo() : id(-1), startTime(-1) {}
23+ bool isValid() const { return id != -1; }
24+ void reset() { id = -1; }
25+ int id;
26+ qint64 startTime;
27+};
28+class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
29+public:
30+ ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
31+ void update(QTouchEvent *event);
32+ qint64 touchStartTime(int id);
33+ bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
34+ qint64 mostRecentStartTime();
35+ UbuntuGestures::SharedTimeSource m_timeSource;
36+private:
37+ void addTouchPoint(int touchId);
38+ void removeTouchPoint(int touchId);
39+ #if ACTIVETOUCHESINFO_DEBUG
40+ QString toString();
41+ #endif
42+
43+ Pool<ActiveTouchInfo> m_touchInfoPool;
44+};
45+
46+class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
47+ Q_OBJECT
48+
49+ Q_ENUMS(Status)
50+public:
51+ DirectionalDragAreaPrivate(DirectionalDragArea *q);
52+
53+public Q_SLOTS:
54+ void giveUpIfDisabledOrInvisible();
55+ void rejectGesture();
56+
57+public:
58+ // Describes the state of the directional drag gesture.
59+ enum Status {
60+ // Waiting for a new touch point to land on this area. No gesture is being processed
61+ // or tracked.
62+ WaitingForTouch,
63+
64+ // A touch point has landed on this area but it's not know yet whether it is
65+ // performing a drag in the correct direction.
66+ // If it's decided that the touch point is not performing a directional drag gesture,
67+ // it will be rejected/ignored and status will return to WaitingForTouch.
68+ Undecided, //Recognizing,
69+
70+ // There's a touch point in this area and it performed a drag in the correct
71+ // direction.
72+ //
73+ // Once recognized, the gesture state will move back to WaitingForTouch only once
74+ // that touch point ends. The gesture will remain in the Recognized state even if
75+ // the touch point starts moving in other directions or halts.
76+ Recognized,
77+ };
78+
79+ void touchEvent_absent(QTouchEvent *event);
80+ void touchEvent_undecided(QTouchEvent *event);
81+ void touchEvent_recognized(QTouchEvent *event);
82+ bool movingInRightDirection() const;
83+ bool movedFarEnoughAlongGestureAxis() const;
84+ bool isPastMaxDistance() const;
85+ const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
86+ void setStatus(Status newStatus);
87+ void setPublicPos(const QPointF &point);
88+ void setPublicScenePos(const QPointF &point);
89+ bool isWithinTouchCompositionWindow();
90+ void updateSceneDirectionVector();
91+ // returns the scalar projection between the given vector (in scene coordinates)
92+ // and m_sceneDirectionVector
93+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
94+ void touchOwnershipEvent(TouchOwnershipEvent *event);
95+ void unownedTouchEvent(UnownedTouchEvent *event);
96+ void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
97+ void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
98+ bool recognitionIsDisabled() const;
99+ bool sanityCheckRecognitionProperties();
100+ void updateSceneDistance();
101+ void setMaxTime(int value);
102+ void setDistanceThreshold(qreal value);
103+ void setPixelsPerMm(qreal pixelsPerMm);
104+ QString objectName() const { return q->objectName(); }
105+
106+ // Replaces the existing Timer with the given one.
107+ //
108+ // Useful for providing a fake timer when testing.
109+ void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
110+
111+ // Useful for testing, where a fake time source can be supplied
112+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
113+
114+ DirectionalDragArea *q;
115+
116+ // The current status of the directional drag gesture area.
117+ Status status;
118+
119+ QPointF startPos;
120+ QPointF startScenePos;
121+ qreal sceneDistance;
122+ int touchId;
123+
124+ // The touch position exposed in the public API.
125+ // It only starts to move once the gesture gets recognized.
126+ QPointF publicPos;
127+ QPointF publicScenePos;
128+
129+ // A movement damper is used in some of the gesture recognition calculations
130+ // to get rid of noise or small oscillations in the touch position.
131+ DampedPointF dampedScenePos;
132+ QPointF previousDampedScenePos;
133+
134+ // Unit vector in scene coordinates describing the direction of the gesture recognition
135+ QPointF sceneDirectionVector;
136+
137+ Direction::Type direction;
138+
139+ // How far a touch point has to move from its initial position along the gesture axis in order
140+ // for it to be recognized as a directional drag.
141+ qreal distanceThreshold;
142+ qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
143+
144+ // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
145+ int maxTime;
146+
147+ // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
148+ qreal maxDistance;
149+
150+ // Maximum time (in milliseconds) after the start of a given touch point where
151+ // subsequent touch starts are grouped with the first one into an N-touches gesture
152+ // (e.g. a two-fingers tap or drag).
153+ int compositionTime;
154+
155+ bool immediateRecognition;
156+
157+ UbuntuGestures::AbstractTimer *recognitionTimer;
158+
159+ UbuntuGestures::SharedTimeSource timeSource;
160+
161+ ActiveTouchesInfo activeTouches;
162+
163+Q_SIGNALS:
164+ void statusChanged(Status value);
165+};
166+
167+#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,11 @@
2828#define TOUCHDISPATCHER_DEBUG 0
2929
3030#if TOUCHDISPATCHER_DEBUG
31+#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
3132#include <DebugHelpers.h>
32−#endif
33+#else // TOUCHDISPATCHER_DEBUG
34+#define ugDebug(params) ((void)0)
35+#endif // TOUCHDISPATCHER_DEBUG
3336
3437TouchDispatcher::TouchDispatcher()
3538 : m_status(NoActiveTouch)
@@ -44,13 +47,12 @@ void TouchDispatcher::setTargetItem(QQuickItem *target)
4447 m_targetItem = target;
4548 if (m_status != NoActiveTouch) {
4649 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47− m_status = TargetRejectedTouches;
50+ setStatus(TargetRejectedTouches);
4851 }
4952 }
5053}
5154
52−void TouchDispatcher::dispatch(QEvent::Type eventType,
53− QTouchDevice *device,
55+void TouchDispatcher::dispatch(QTouchDevice *device,
5456 Qt::KeyboardModifiers modifiers,
5557 const QList<QTouchEvent::TouchPoint> &touchPoints,
5658 QWindow *window,
@@ -61,6 +63,8 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
6163 return;
6264 }
6365
66+ QEvent::Type eventType = resolveEventType(touchPoints);
67+
6468 if (eventType == QEvent::TouchBegin) {
6569 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
6670
@@ -72,15 +76,13 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
7276 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
7377 } else {
7478 Q_ASSERT(m_status == TargetRejectedTouches);
75− #if TOUCHDISPATCHER_DEBUG
76− qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
77− << "because it already rejected the touch stream.";
78− #endif
79+ ugDebug("Not dispatching touch event to " << m_targetItem.data()
80+ << "because it already rejected the touch stream.");
7981 // Do nothing
8082 }
8183
8284 if (eventType == QEvent::TouchEnd) {
83− m_status = NoActiveTouch;
85+ setStatus(NoActiveTouch);
8486 m_touchMouseId = -1;
8587 }
8688
@@ -103,10 +105,7 @@ void TouchDispatcher::dispatchTouchBegin(
103105 QQuickItem *targetItem = m_targetItem.data();
104106
105107 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106− #if TOUCHDISPATCHER_DEBUG
107− qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108− << "because it's disabled or invisible.";
109− #endif
108+ ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
110109 return;
111110 }
112111
@@ -118,62 +117,46 @@ void TouchDispatcher::dispatchTouchBegin(
118117 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119118
120119
121− #if TOUCHDISPATCHER_DEBUG
122− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
123− << "to" << targetItem;
124− #endif
120+ ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
121+ << " to " << targetItem);
125122 QCoreApplication::sendEvent(targetItem, touchEvent.data());
126123
127124
128125 if (touchEvent->isAccepted()) {
129− #if TOUCHDISPATCHER_DEBUG
130− qDebug() << "[TouchDispatcher] Item accepted the touch event.";
131− #endif
132− m_status = DeliveringTouchEvents;
126+ ugDebug("Item accepted the touch event.");
127+ setStatus(DeliveringTouchEvents);
133128 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134− #if TOUCHDISPATCHER_DEBUG
135− qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
136− #endif
129+ ugDebug("Item rejected the touch event. Trying a QMouseEvent");
137130 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138131 QScopedPointer<QMouseEvent> mouseEvent(
139132 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140133 modifiers, false /* transformNeeded */));
141134 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142135
143− #if TOUCHDISPATCHER_DEBUG
144− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
145− << "to" << m_targetItem.data();
146− #endif
136+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
137+ << " to " << m_targetItem.data());
147138 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148139 if (mouseEvent->isAccepted()) {
149− #if TOUCHDISPATCHER_DEBUG
150− qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
151− #endif
152− m_status = DeliveringMouseEvents;
140+ ugDebug("Item accepted the QMouseEvent.");
141+ setStatus(DeliveringMouseEvents);
153142 m_touchMouseId = targetTouchPoints.at(0).id();
154143
155144 if (checkIfDoubleClicked(timestamp)) {
156145 QScopedPointer<QMouseEvent> doubleClickEvent(
157146 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158147 modifiers, false /* transformNeeded */));
159− #if TOUCHDISPATCHER_DEBUG
160− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
161− << "to" << m_targetItem.data();
162− #endif
148+ ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
149+ << " to " << m_targetItem.data());
163150 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164151 }
165152
166153 } else {
167− #if TOUCHDISPATCHER_DEBUG
168− qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
169− #endif
170− m_status = TargetRejectedTouches;
154+ ugDebug("Item rejected the QMouseEvent.");
155+ setStatus(TargetRejectedTouches);
171156 }
172157 } else {
173− #if TOUCHDISPATCHER_DEBUG
174− qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
175− #endif
176− m_status = TargetRejectedTouches;
158+ ugDebug("Item rejected the touch event and does not accept mouse buttons.");
159+ setStatus(TargetRejectedTouches);
177160 }
178161}
179162
@@ -194,10 +177,8 @@ void TouchDispatcher::dispatchAsTouch(QEvent::Type eventType,
194177 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195178
196179
197− #if TOUCHDISPATCHER_DEBUG
198− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
199− << "to" << targetItem;
200− #endif
180+ ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
181+ << " to " << targetItem);
201182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202183}
203184
@@ -254,10 +235,8 @@ void TouchDispatcher::dispatchAsMouse(
254235 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255236 true /* transformNeeded */));
256237
257− #if TOUCHDISPATCHER_DEBUG
258− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
259− << "to" << m_targetItem.data();
260− #endif
238+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
239+ << " to " << m_targetItem.data());
261240 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262241 }
263242}
@@ -365,3 +344,59 @@ bool TouchDispatcher::checkIfDoubleClicked(ulong newPressEventTimestamp)
365344
366345 return doubleClicked;
367346}
347+
348+void TouchDispatcher::setStatus(Status status)
349+{
350+ if (status != m_status) {
351+ #if TOUCHDISPATCHER_DEBUG
352+ switch (status) {
353+ case NoActiveTouch:
354+ ugDebug("status = NoActiveTouch");
355+ break;
356+ case DeliveringTouchEvents:
357+ ugDebug("status = DeliveringTouchEvents");
358+ break;
359+ case DeliveringMouseEvents:
360+ ugDebug("status = DeliveringMouseEvents");
361+ break;
362+ case TargetRejectedTouches:
363+ ugDebug("status = TargetRejectedTouches");
364+ break;
365+ default:
366+ ugDebug("status = " << status);
367+ break;
368+ }
369+ #endif
370+ m_status = status;
371+ }
372+}
373+
374+void TouchDispatcher::reset()
375+{
376+ setStatus(NoActiveTouch);
377+ m_touchMouseId = -1;
378+ m_touchMousePressTimestamp =0;
379+}
380+
381+QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
382+{
383+ QEvent::Type eventType;
384+
385+ Qt::TouchPointStates eventStates = 0;
386+ for (int i = 0; i < touchPoints.count(); i++)
387+ eventStates |= touchPoints[i].state();
388+
389+ switch (eventStates) {
390+ case Qt::TouchPointPressed:
391+ eventType = QEvent::TouchBegin;
392+ break;
393+ case Qt::TouchPointReleased:
394+ eventType = QEvent::TouchEnd;
395+ break;
396+ default:
397+ eventType = QEvent::TouchUpdate;
398+ break;
399+ }
400+
401+ return eventType;
402+}
plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -36,12 +36,20 @@ public:
3636 void setTargetItem(QQuickItem *target);
3737 QQuickItem *targetItem() { return m_targetItem; }
3838
39− void dispatch(QEvent::Type eventType,
40− QTouchDevice *device,
39+ void dispatch(QTouchDevice *device,
4140 Qt::KeyboardModifiers modifiers,
4241 const QList<QTouchEvent::TouchPoint> &touchPoints,
4342 QWindow *window,
4443 ulong timestamp);
44+
45+ void reset();
46+
47+ enum Status {
48+ NoActiveTouch,
49+ DeliveringTouchEvents,
50+ DeliveringMouseEvents,
51+ TargetRejectedTouches
52+ };
4553private:
4654 void dispatchTouchBegin(
4755 QTouchDevice *device,
@@ -73,14 +81,13 @@ private:
7381
7482 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
7583
84+ void setStatus(Status status);
85+
86+ static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
87+
7688 QPointer<QQuickItem> m_targetItem;
7789
78− enum {
79− NoActiveTouch,
80− DeliveringTouchEvents,
81− DeliveringMouseEvents,
82− TargetRejectedTouches
83− } m_status;
90+ Status m_status;
8491
8592 int m_touchMouseId;
8693 ulong m_touchMousePressTimestamp;
plugins/Ubuntu/Gestures/TouchGate.cpp+84−60
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -23,8 +23,18 @@
2323#include <TouchRegistry.h>
2424
2525#if TOUCHGATE_DEBUG
26+#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
2627#include <DebugHelpers.h>
27−#endif
28+#else // TOUCHGATE_DEBUG
29+#define ugDebug(params) ((void)0)
30+#endif // TOUCHGATE_DEBUG
31+
32+TouchGate::TouchGate(QQuickItem *parent)
33+ : QQuickItem(parent)
34+{
35+ connect(this, &QQuickItem::enabledChanged,
36+ this, &TouchGate::onEnabledChanged);
37+}
2838
2939bool TouchGate::event(QEvent *e)
3040{
@@ -38,13 +48,12 @@ bool TouchGate::event(QEvent *e)
3848
3949void TouchGate::touchEvent(QTouchEvent *event)
4050{
41− #if TOUCHGATE_DEBUG
42− qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
43− #endif
51+ ugDebug("got touch event" << qPrintable(touchEventToString(event)));
4452 event->accept();
4553
4654 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
47− bool goodToGo = true;
55+ QList<QTouchEvent::TouchPoint> validTouchPoints;
56+ bool ownsAllTouches = true;
4857 for (int i = 0; i < touchPoints.count(); ++i) {
4958 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
5059
@@ -55,31 +64,41 @@ void TouchGate::touchEvent(QTouchEvent *event)
5564 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
5665 }
5766
58− goodToGo &= m_touchInfoMap.contains(touchPoint.id())
59− && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
67+ if (m_touchInfoMap.contains(touchPoint.id())) {
68+ validTouchPoints.append(touchPoint);
69+
70+ ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
6071
61− if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {
62− m_touchInfoMap[touchPoint.id()].ended = true;
72+ if (touchPoint.state() == Qt::TouchPointReleased) {
73+ m_touchInfoMap[touchPoint.id()].ended = true;
74+ }
6375 }
6476
6577 }
6678
67− if (goodToGo) {
79+ if (validTouchPoints.isEmpty()) {
80+ // nothing to do.
81+ return;
82+ }
83+
84+ if (ownsAllTouches) {
6885 if (m_storedEvents.isEmpty()) {
6986 // let it pass through
70− dispatchTouchEventToTarget(event);
87+ removeTouchInfoForEndedTouches(validTouchPoints);
88+ m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
89+ event->window(), event->timestamp());
7190 } else {
7291 // Retain the event to ensure TouchGate dispatches them in order.
7392 // Otherwise the current event would come before the stored ones, which are older.
74− #if TOUCHGATE_DEBUG
75− qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",
76− qPrintable(oldestPendingTouchIdsString()));
77− #endif
78− storeTouchEvent(event);
93+ ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
94+ << " are still pending ownership.");
95+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
96+ event->window(), event->timestamp());
7997 }
8098 } else {
8199 // Retain events that have unowned touches
82− storeTouchEvent(event);
100+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
101+ event->window(), event->timestamp());
83102 }
84103}
85104
@@ -88,24 +107,23 @@ void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
88107 // TODO: Optimization: batch those actions as TouchOwnershipEvents
89108 // might come one right after the other.
90109
91− Q_ASSERT(m_touchInfoMap.contains(event->touchId()));
110+ if (m_touchInfoMap.contains(event->touchId())) {
111+ TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
92112
93− TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
113+ if (event->gained()) {
114+ ugDebug("Got ownership of touch " << event->touchId());
115+ touchInfo.ownership = OwnershipGranted;
116+ } else {
117+ ugDebug("Lost ownership of touch " << event->touchId());
118+ m_touchInfoMap.remove(event->touchId());
119+ removeTouchFromStoredEvents(event->touchId());
120+ }
94121
95− if (event->gained()) {
96− #if TOUCHGATE_DEBUG
97− qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();
98− #endif
99− touchInfo.ownership = OwnershipGranted;
122+ dispatchFullyOwnedEvents();
100123 } else {
101− #if TOUCHGATE_DEBUG
102− qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();
103− #endif
104− m_touchInfoMap.remove(event->touchId());
105− removeTouchFromStoredEvents(event->touchId());
124+ // Ignore it. It probably happened because the TouchGate got disabled
125+ // between the time it requested ownership and the time it got it.
106126 }
107−
108− dispatchFullyOwnedEvents();
109127}
110128
111129bool TouchGate::isTouchPointOwned(int touchId) const
@@ -113,14 +131,15 @@ bool TouchGate::isTouchPointOwned(int touchId) const
113131 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
114132}
115133
116−void TouchGate::storeTouchEvent(const QTouchEvent *event)
134+void TouchGate::storeTouchEvent(QTouchDevice *device,
135+ Qt::KeyboardModifiers modifiers,
136+ const QList<QTouchEvent::TouchPoint> &touchPoints,
137+ QWindow *window,
138+ ulong timestamp)
117139{
118− #if TOUCHGATE_DEBUG
119− qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));
120− #endif
121−
122− TouchEvent clonedEvent(event);
123− m_storedEvents.append(std::move(clonedEvent));
140+ ugDebug("Storing" << touchPoints);
141+ TouchEvent event(device, modifiers, touchPoints, window, timestamp);
142+ m_storedEvents.append(std::move(event));
124143}
125144
126145void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -193,25 +212,13 @@ void TouchGate::setTargetItem(QQuickItem *item)
193212void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
194213{
195214 removeTouchInfoForEndedTouches(event.touchPoints);
196− m_dispatcher.dispatch(event.eventType,
197− event.device,
215+ m_dispatcher.dispatch(event.device,
198216 event.modifiers,
199217 event.touchPoints,
200218 event.window,
201219 event.timestamp);
202220}
203221
204−void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
205−{
206− removeTouchInfoForEndedTouches(event->touchPoints());
207− m_dispatcher.dispatch(event->type(),
208− event->device(),
209− event->modifiers(),
210− event->touchPoints(),
211− event->window(),
212− event->timestamp());
213−}
214−
215222void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
216223{
217224 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -226,14 +233,31 @@ void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoi
226233 }
227234}
228235
229−TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
230− : eventType(event->type())
231− , device(event->device())
232− , modifiers(event->modifiers())
233− , touchPoints(event->touchPoints())
234− , target(qobject_cast<QQuickItem*>(event->target()))
235− , window(event->window())
236− , timestamp(event->timestamp())
236+void TouchGate::onEnabledChanged()
237+{
238+ ugDebug(" enabled = " << isEnabled());
239+ if (!isEnabled()) {
240+ reset();
241+ }
242+}
243+
244+void TouchGate::reset()
245+{
246+ m_storedEvents.clear();
247+ m_touchInfoMap.clear();
248+ m_dispatcher.reset();
249+}
250+
251+TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
252+ Qt::KeyboardModifiers modifiers,
253+ const QList<QTouchEvent::TouchPoint> &touchPoints,
254+ QWindow *window,
255+ ulong timestamp)
256+ : device(device)
257+ , modifiers(modifiers)
258+ , touchPoints(touchPoints)
259+ , window(window)
260+ , timestamp(timestamp)
237261{
238262}
239263
plugins/Ubuntu/Gestures/TouchGate.h+18−5
@@ -45,6 +45,8 @@ class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem {
4545 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
4646
4747public:
48+ TouchGate(QQuickItem *parent = nullptr);
49+
4850 bool event(QEvent *e) override;
4951
5052 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
@@ -55,31 +57,42 @@ Q_SIGNALS:
5557
5658protected:
5759 void touchEvent(QTouchEvent *event) override;
60+
61+private Q_SLOTS:
62+ void onEnabledChanged();
63+
5864private:
65+ void reset();
66+
5967 class TouchEvent {
6068 public:
61− TouchEvent(const QTouchEvent *event);
69+ TouchEvent(QTouchDevice *device,
70+ Qt::KeyboardModifiers modifiers,
71+ const QList<QTouchEvent::TouchPoint> &touchPoints,
72+ QWindow *window,
73+ ulong timestamp);
6274
6375 bool removeTouch(int touchId);
6476
65− QEvent::Type eventType;
6677 QTouchDevice *device;
6778 Qt::KeyboardModifiers modifiers;
6879 QList<QTouchEvent::TouchPoint> touchPoints;
69− QQuickItem *target;
7080 QWindow *window;
7181 ulong timestamp;
7282 };
7383
7484 void touchOwnershipEvent(TouchOwnershipEvent *event);
7585 bool isTouchPointOwned(int touchId) const;
76− void storeTouchEvent(const QTouchEvent *event);
86+ void storeTouchEvent(QTouchDevice *device,
87+ Qt::KeyboardModifiers modifiers,
88+ const QList<QTouchEvent::TouchPoint> &touchPoints,
89+ QWindow *window,
90+ ulong timestamp);
7791 void removeTouchFromStoredEvents(int touchId);
7892 void dispatchFullyOwnedEvents();
7993 bool eventIsFullyOwned(const TouchEvent &event) const;
8094
8195 void dispatchTouchEventToTarget(const TouchEvent &event);
82− void dispatchTouchEventToTarget(QTouchEvent* event);
8396
8497 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
8598
po/ca.po+25−25
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-07 11:45+0000\n"
11+"PO-Revision-Date: 2015-05-02 07:52+0000\n"
1212"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-03 06:07+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -57,7 +57,7 @@ msgstr[1] "Espereu %1 minuts i torneu-ho a intentar..."
5757#: qml/Components/Dialogs.qml:69
5858msgctxt "Title: Lock/Log out dialog"
5959msgid "Log out"
60−msgstr ""
60+msgstr "Surt"
6161
6262#: qml/Components/Dialogs.qml:70
6363msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Segur que voleu sortir?"
6666#: qml/Components/Dialogs.qml:72
6767msgctxt "Button: Lock the system"
6868msgid "Lock"
69−msgstr ""
69+msgstr "Bloca"
7070
7171#: qml/Components/Dialogs.qml:79
7272msgctxt "Button: Log out from the system"
7373msgid "Log Out"
74−msgstr ""
74+msgstr "Surt"
7575
7676#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7777#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Cancel·la"
8181#: qml/Components/Dialogs.qml:98
8282msgctxt "Title: Reboot/Shut down dialog"
8383msgid "Shut down"
84−msgstr ""
84+msgstr "Apaga"
8585
8686#: qml/Components/Dialogs.qml:99
8787msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Segur que voleu apagar?"
9090#: qml/Components/Dialogs.qml:101
9191msgctxt "Button: Reboot the system"
9292msgid "Reboot"
93−msgstr ""
93+msgstr "Reinicia"
9494
9595#: qml/Components/Dialogs.qml:109
9696msgctxt "Button: Shut down the system"
9797msgid "Shut down"
98−msgstr ""
98+msgstr "Apaga"
9999
100100#: qml/Components/Dialogs.qml:129
101101msgctxt "Title: Reboot dialog"
102102msgid "Reboot"
103−msgstr ""
103+msgstr "Reinicia"
104104
105105#: qml/Components/Dialogs.qml:130
106106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Sí"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Energia"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130130#: qml/Components/Dialogs.qml:155
131131msgctxt "Button: Power off the system"
132132msgid "Power off"
133−msgstr ""
133+msgstr "Apaga"
134134
135135#: qml/Components/Dialogs.qml:164
136136msgctxt "Button: Restart the system"
137137msgid "Restart"
138−msgstr ""
138+msgstr "Reinicia"
139139
140140#: qml/Components/Lockscreen.qml:245
141141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Mostra-ho tot"
160160#: qml/Dash/GenericScopeView.qml:594
161161msgctxt "Label: Hint for dash search line edit"
162162msgid "Search"
163−msgstr ""
163+msgstr "Cerca"
164164
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Botiga"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Cerca"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Paràmetres"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -344,7 +344,7 @@ msgstr "Ha fallat, feu un toc per tornar-ho a intentar"
344344#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
345345msgctxt "Label: Hint in message indicator line edit"
346346msgid "Reply"
347−msgstr ""
347+msgstr "Respon"
348348
349349#: qml/Panel/Indicators/ModemInfoItem.qml:105
350350msgid "Unlock SIM"
@@ -377,7 +377,7 @@ msgstr "Feu un toc per continuar."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Feu un toc aquí per finalitzar"
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
@@ -414,7 +414,7 @@ msgstr "Mostra totes les tasques en execució."
414414#: qml/Wizard/Page.qml:89
415415msgctxt "Button: Go back one page in the Wizard"
416416msgid "Back"
417−msgstr ""
417+msgstr "Enrere"
418418
419419#: qml/Wizard/Pages/10-welcome.qml:27
420420msgid "Hi!"
@@ -458,17 +458,17 @@ msgstr "Seleccioneu com voleu desblocar el telèfon."
458458#: qml/Wizard/Pages/30-passwd-type.qml:97
459459msgctxt "Label: Type of security method"
460460msgid "Swipe"
461−msgstr ""
461+msgstr "Lliscament amb el dit"
462462
463463#: qml/Wizard/Pages/30-passwd-type.qml:98
464464msgctxt "Label: Description of security method"
465465msgid "No security"
466−msgstr ""
466+msgstr "Sense seguretat"
467467
468468#: qml/Wizard/Pages/30-passwd-type.qml:100
469469msgctxt "Label: Type of security method"
470470msgid "Passcode"
471−msgstr ""
471+msgstr "Codi d'accés"
472472
473473#: qml/Wizard/Pages/30-passwd-type.qml:101
474474msgctxt "Label: Description of security method"
@@ -478,12 +478,12 @@ msgstr "Només 4 dígits"
478478#: qml/Wizard/Pages/30-passwd-type.qml:103
479479msgctxt "Label: Type of security method"
480480msgid "Passphrase"
481−msgstr ""
481+msgstr "Contrasenya"
482482
483483#: qml/Wizard/Pages/30-passwd-type.qml:104
484484msgctxt "Label: Description of security method"
485485msgid "Numbers and letters"
486−msgstr ""
486+msgstr "Números i lletres"
487487
488488#: qml/Wizard/Pages/40-wifi.qml:29
489489msgid "Connect to Wi‑Fi"
po/cs.po+136−119
@@ -8,132 +8,138 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-01-08 10:11+0000\n"
11+"PO-Revision-Date: 2015-05-11 20:42+0000\n"
1212"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
1313"Language-Team: Czech <cs@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+"Language: cs\n"
2021
2122#: plugins/LightDM/Greeter.cpp:112
2223msgid "Password: "
23−msgstr ""
24+msgstr "Heslo: "
2425
2526#: plugins/Unity/Launcher/launcheritem.cpp:45
2627#: plugins/Unity/Launcher/launcheritem.cpp:100
2728msgid "Pin shortcut"
28−msgstr ""
29+msgstr "Připíchnout zkratku"
2930
3031#: plugins/Unity/Launcher/launcheritem.cpp:100
3132msgid "Unpin shortcut"
32−msgstr ""
33+msgstr "Odpíchnout zkratku"
3334
3435#: qml/Components/DelayedLockscreen.qml:49
3536msgid "Device Locked"
36−msgstr ""
37+msgstr "Zařízení uzamčeno"
3738
3839#: qml/Components/DelayedLockscreen.qml:64
3940msgid "You have been locked out due to too many failed passphrase attempts."
4041msgstr ""
42+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
4143
4244#: qml/Components/DelayedLockscreen.qml:65
4345msgid "You have been locked out due to too many failed passcode attempts."
4446msgstr ""
47+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
4548
4649#: qml/Components/DelayedLockscreen.qml:74
4750#, qt-format
4851msgid "Please wait %1 minute and then try again…"
4952msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
51−msgstr[1] ""
53+msgstr[0] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
54+msgstr[1] "Počkejte, prosím, %1 minuty a poté to zkuste znovu..."
55+msgstr[2] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
5256
5357#: qml/Components/Dialogs.qml:69
5458msgctxt "Title: Lock/Log out dialog"
5559msgid "Log out"
56−msgstr ""
60+msgstr "Odhlásit se"
5761
5862#: qml/Components/Dialogs.qml:70
5963msgid "Are you sure you want to log out?"
60−msgstr ""
64+msgstr "Jste si jistí, že se chcete odhlásit?"
6165
6266#: qml/Components/Dialogs.qml:72
6367msgctxt "Button: Lock the system"
6468msgid "Lock"
65−msgstr ""
69+msgstr "Uzamknout"
6670
6771#: qml/Components/Dialogs.qml:79
6872msgctxt "Button: Log out from the system"
6973msgid "Log Out"
70−msgstr ""
74+msgstr "Odhlásit"
7175
7276#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7377#: qml/Components/Dialogs.qml:173
7478msgid "Cancel"
75−msgstr ""
79+msgstr "Zrušit"
7680
7781#: qml/Components/Dialogs.qml:98
7882msgctxt "Title: Reboot/Shut down dialog"
7983msgid "Shut down"
80−msgstr ""
84+msgstr "Vypnout"
8185
8286#: qml/Components/Dialogs.qml:99
8387msgid "Are you sure you want to shut down?"
84−msgstr ""
88+msgstr "Jste si jistí, že chcete zařízení vypnout?"
8589
8690#: qml/Components/Dialogs.qml:101
8791msgctxt "Button: Reboot the system"
8892msgid "Reboot"
89−msgstr ""
93+msgstr "Restartovat"
9094
9195#: qml/Components/Dialogs.qml:109
9296msgctxt "Button: Shut down the system"
9397msgid "Shut down"
94−msgstr ""
98+msgstr "Vypnout"
9599
96100#: qml/Components/Dialogs.qml:129
97101msgctxt "Title: Reboot dialog"
98102msgid "Reboot"
99−msgstr ""
103+msgstr "Restartovat"
100104
101105#: qml/Components/Dialogs.qml:130
102106msgid "Are you sure you want to reboot?"
103−msgstr ""
107+msgstr "Jste si jistí, že chcete zařízení restartovat?"
104108
105109#: qml/Components/Dialogs.qml:132
106110msgid "No"
107−msgstr ""
111+msgstr "Ne"
108112
109113#: qml/Components/Dialogs.qml:138
110114msgid "Yes"
111−msgstr ""
115+msgstr "Ano"
112116
113117#: qml/Components/Dialogs.qml:152
114118msgctxt "Title: Power off/Restart dialog"
115119msgid "Power"
116−msgstr ""
120+msgstr "Vypnutí"
117121
118122#: qml/Components/Dialogs.qml:153
119123msgid ""
120124"Are you sure you would like\n"
121125"to power off?"
122126msgstr ""
127+"Jste si jistí, že chcete\n"
128+"telefon vypnout?"
123129
124130#: qml/Components/Dialogs.qml:155
125131msgctxt "Button: Power off the system"
126132msgid "Power off"
127−msgstr ""
133+msgstr "Vypnutí"
128134
129135#: qml/Components/Dialogs.qml:164
130136msgctxt "Button: Restart the system"
131137msgid "Restart"
132−msgstr ""
138+msgstr "Reboot"
133139
134140#: qml/Components/Lockscreen.qml:245
135141msgid "Return to Call"
136−msgstr ""
142+msgstr "Vrátit se k hovoru"
137143
138144#: qml/Components/Lockscreen.qml:245
139145msgid "Emergency Call"
@@ -141,51 +147,51 @@ msgstr "Tísňové volání"
141147
142148#: qml/Components/Lockscreen.qml:277
143149msgid "OK"
144−msgstr ""
150+msgstr "OK"
145151
146152#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147153msgid "See less"
148−msgstr ""
154+msgstr "Zobrazit méně"
149155
150156#: qml/Dash/GenericScopeView.qml:532
151157msgid "See all"
152−msgstr ""
158+msgstr "Zobrazit vše"
153159
154160#: qml/Dash/GenericScopeView.qml:594
155161msgctxt "Label: Hint for dash search line edit"
156162msgid "Search"
157−msgstr ""
163+msgstr "Hledat"
158164
159165#: qml/Dash/PageHeader.qml:269
160166msgctxt "Button: Open the Ubuntu Store"
161167msgid "Store"
162−msgstr ""
168+msgstr "Obchod"
163169
164170#: qml/Dash/PageHeader.qml:276
165171msgctxt "Button: Start a search in the current dash scope"
166172msgid "Search"
167−msgstr ""
173+msgstr "Hledat"
168174
169175#: qml/Dash/PageHeader.qml:286
170176msgctxt "Button: Show the current dash scope settings"
171177msgid "Settings"
172−msgstr ""
178+msgstr "Nastavení"
173179
174180#: qml/Dash/PageHeader.qml:293
175181msgid "Remove from Favorites"
176−msgstr ""
182+msgstr "Odstranit z oblíbených"
177183
178184#: qml/Dash/PageHeader.qml:293
179185msgid "Add to Favorites"
180−msgstr ""
186+msgstr "Přidat do oblíbených"
181187
182188#: qml/Dash/Previews/PreviewActionCombo.qml:35
183189msgid "More..."
184−msgstr ""
190+msgstr "Více..."
185191
186192#: qml/Dash/Previews/PreviewActionCombo.qml:35
187193msgid "Less..."
188−msgstr ""
194+msgstr "Méně..."
189195
190196#: qml/Dash/Previews/PreviewRatingInput.qml:81
191197msgid "Rate this"
@@ -201,93 +207,97 @@ msgstr "Odeslat"
201207
202208#: qml/Dash/PullToRefreshScopeStyle.qml:55
203209msgid "Pull to refresh…"
204−msgstr ""
210+msgstr "Vytáhněte pro obnovení..."
205211
206212#: qml/Dash/PullToRefreshScopeStyle.qml:60
207213msgid "Release to refresh…"
208−msgstr ""
214+msgstr "Pusťte pro obnovení..."
209215
210216#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211217msgid "Enable location data"
212−msgstr ""
218+msgstr "Povolit údaje umístění"
213219
214220#: qml/Dash/ScopesList.qml:66
215221msgid "Manage"
216−msgstr ""
222+msgstr "Spravovat"
217223
218224#: qml/Dash/ScopesList.qml:112
219225msgid "Home"
220−msgstr ""
226+msgstr "Domů"
221227
222228#: qml/Dash/ScopesList.qml:113
223229msgid "Also installed"
224−msgstr ""
230+msgstr "Také nainstalováno"
225231
226232#: qml/Greeter/CoverPage.qml:107
227233msgid "Unlock"
228−msgstr ""
234+msgstr "Odemknout"
229235
230236#: qml/Greeter/LoginList.qml:236
231237msgid "Retry"
232−msgstr ""
238+msgstr "Znovu"
233239
234240#: qml/Greeter/LoginList.qml:237
235241msgid "Tap to unlock"
236−msgstr ""
242+msgstr "Klepnout pro odemčení"
237243
238244#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239245msgid "Sorry, incorrect passphrase."
240−msgstr ""
246+msgstr "Zadali jste špatné heslo."
241247
242248#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243249msgid "Sorry, incorrect passcode."
244−msgstr ""
250+msgstr "Zadali jste špatné heslo."
245251
246252#: qml/Greeter/NarrowView.qml:55
247253msgid "This will be your last attempt."
248−msgstr ""
254+msgstr "Toto je váš poslední pokus."
249255
250256#: qml/Greeter/NarrowView.qml:57
251257msgid ""
252258"If passphrase is entered incorrectly, your phone will conduct a factory "
253259"reset and all personal data will be deleted."
254260msgstr ""
261+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
262+"nastavení a všechna osobní data budou smazána."
255263
256264#: qml/Greeter/NarrowView.qml:58
257265msgid ""
258266"If passcode is entered incorrectly, your phone will conduct a factory reset "
259267"and all personal data will be deleted."
260268msgstr ""
269+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
270+"nastavení a všechna osobní data budou smazána."
261271
262272#: qml/Greeter/NarrowView.qml:115
263273#, qt-format
264274msgid "Enter %1"
265−msgstr ""
275+msgstr "Vložte %1"
266276
267277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268278msgid "Enter passphrase"
269−msgstr ""
279+msgstr "Vložte heslo"
270280
271281#: qml/Greeter/NarrowView.qml:117
272282msgid "Enter passcode"
273−msgstr ""
283+msgstr "Vložte heslo"
274284
275285#: qml/Greeter/NarrowView.qml:118
276286#, qt-format
277287msgid "Sorry, incorrect %1"
278−msgstr ""
288+msgstr "Špatné %1"
279289
280290#: qml/Greeter/NarrowView.qml:119
281291msgid "Sorry, incorrect passphrase"
282−msgstr ""
292+msgstr "Špatné heslo"
283293
284294#: qml/Greeter/NarrowView.qml:120
285295msgid "Please re-enter"
286−msgstr ""
296+msgstr "Vložte znovu"
287297
288298#: qml/Greeter/NarrowView.qml:121
289299msgid "Sorry, incorrect passcode"
290−msgstr ""
300+msgstr "Špatné heslo"
291301
292302#: qml/Notifications/NotificationMenuItemFactory.qml:128
293303msgid "Show password"
@@ -295,211 +305,211 @@ msgstr "Zobrazit heslo"
295305
296306#: qml/Panel/ActiveCallHint.qml:79
297307msgid "Tap to return to call..."
298−msgstr ""
308+msgstr "Klepnutím se vrátíte k hovoru..."
299309
300310#: qml/Panel/ActiveCallHint.qml:92
301311msgid "Conference"
302−msgstr ""
312+msgstr "Konferenční hovor"
303313
304314#: qml/Panel/Indicators/MenuItemFactory.qml:639
305315msgid "Nothing is playing"
306−msgstr ""
316+msgstr "Nic se nepřehrává"
307317
308318#: qml/Panel/Indicators/MenuItemFactory.qml:787
309319msgid "In queue…"
310−msgstr ""
320+msgstr "Ve frontě..."
311321
312322#: qml/Panel/Indicators/MenuItemFactory.qml:791
313323msgid "Downloading"
314−msgstr ""
324+msgstr "Stahování"
315325
316326#: qml/Panel/Indicators/MenuItemFactory.qml:793
317327msgid "Paused, tap to resume"
318−msgstr ""
328+msgstr "Pauza, pro pokračování klepněte"
319329
320330#: qml/Panel/Indicators/MenuItemFactory.qml:795
321331msgid "Canceled"
322−msgstr ""
332+msgstr "Zrušeno"
323333
324334#: qml/Panel/Indicators/MenuItemFactory.qml:797
325335msgid "Finished"
326−msgstr ""
336+msgstr "Dokončeno"
327337
328338#: qml/Panel/Indicators/MenuItemFactory.qml:799
329339msgid "Failed, tap to retry"
330−msgstr ""
340+msgstr "Nezdařilo se, opakujte klepnutím"
331341
332342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333343msgctxt "Label: Hint in message indicator line edit"
334344msgid "Reply"
335−msgstr ""
345+msgstr "Odpovědět"
336346
337347#: qml/Panel/Indicators/ModemInfoItem.qml:105
338348msgid "Unlock SIM"
339−msgstr ""
349+msgstr "Odemknout SIM"
340350
341351#: qml/Panel/Indicators/RoamingIndication.qml:27
342352msgid "Roaming"
343−msgstr ""
353+msgstr "Roaming"
344354
345355#: qml/Tutorial/TutorialBottomFinish.qml:24
346356msgid "This action does different things for different apps"
347−msgstr ""
357+msgstr "Tato akce vyvolá různé reakce v rozdílných aplikacích"
348358
349359#: qml/Tutorial/TutorialBottomFinish.qml:25
350360msgid "Tap here to finish."
351−msgstr ""
361+msgstr "Klepněte zde pro dokončení."
352362
353363#: qml/Tutorial/TutorialBottom.qml:28
354364msgid "Open special menus"
355−msgstr ""
365+msgstr "Otevřít speciální menu"
356366
357367#: qml/Tutorial/TutorialBottom.qml:29
358368msgid "Swipe up from the bottom edge."
359−msgstr ""
369+msgstr "Potáhněte prstem z dola nahoru."
360370
361371#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362372#: qml/Tutorial/TutorialRight.qml:144
363373msgid "Tap here to continue."
364−msgstr ""
374+msgstr "Klepněte zde pro pokračování."
365375
366376#: qml/Tutorial/TutorialContent.qml:81
367377msgid "Click here to finish."
368−msgstr ""
378+msgstr "Klikněte zde pro dokončení."
369379
370380#: qml/Tutorial/TutorialLeftFinish.qml:24
371381msgid "These are the shortcuts to favorite apps"
372−msgstr ""
382+msgstr "Zde jsou odkazy na vaše oblíbené aplikace"
373383
374384#: qml/Tutorial/TutorialLeft.qml:26
375385msgid "Open the launcher"
376−msgstr ""
386+msgstr "Otevřete launcher"
377387
378388#: qml/Tutorial/TutorialLeft.qml:27
379389msgid "Short swipe from the left edge."
380−msgstr ""
390+msgstr "Potáhněte krátce z levého okraje."
381391
382392#: qml/Tutorial/TutorialPage.qml:178
383393msgid "You almost got it!"
384−msgstr ""
394+msgstr "Už to skoro máte!"
385395
386396#: qml/Tutorial/TutorialPage.qml:195
387397msgid "Try again."
388−msgstr ""
398+msgstr "Zkuste to znovu."
389399
390400#: qml/Tutorial/TutorialRight.qml:31
391401msgid "To view open apps"
392−msgstr ""
402+msgstr "K zobrazení otevřených aplikací"
393403
394404#: qml/Tutorial/TutorialRight.qml:32
395405msgid "Long swipe from the right edge."
396−msgstr ""
406+msgstr "Potáhněte dlouho z pravého okraje."
397407
398408#: qml/Tutorial/TutorialRight.qml:122
399409msgid "View all your running tasks."
400−msgstr ""
410+msgstr "Zobrazíte všechny běžící procesy."
401411
402412#: qml/Wizard/Page.qml:89
403413msgctxt "Button: Go back one page in the Wizard"
404414msgid "Back"
405−msgstr ""
415+msgstr "Zpět"
406416
407417#: qml/Wizard/Pages/10-welcome.qml:27
408418msgid "Hi!"
409−msgstr ""
419+msgstr "Ahoj!"
410420
411421#: qml/Wizard/Pages/10-welcome.qml:44
412422msgid "Welcome to your Ubuntu phone."
413−msgstr ""
423+msgstr "Vítejte ve vašem Ubuntu telefonu."
414424
415425#: qml/Wizard/Pages/10-welcome.qml:52
416426msgid "Let’s get started."
417−msgstr ""
427+msgstr "Začněme."
418428
419429#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420430#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421431#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422432#: qml/Wizard/Pages/passwd-set.qml:90
423433msgid "Continue"
424−msgstr ""
434+msgstr "Pokračovat"
425435
426436#: qml/Wizard/Pages/20-sim.qml:25
427437msgid "Add a SIM card and restart your device"
428−msgstr ""
438+msgstr "Vložte SIM kartu a restartujte své zařízení"
429439
430440#: qml/Wizard/Pages/20-sim.qml:59
431441msgid "Without it, you won’t be able to make calls or use text messaging."
432−msgstr ""
442+msgstr "Bez ni nebudete moci telefonovat nebo posílat textové zprávy."
433443
434444#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435445msgid "Skip"
436−msgstr ""
446+msgstr "Přeskočit"
437447
438448#: qml/Wizard/Pages/30-passwd-type.qml:39
439449msgid "Lock security"
440−msgstr ""
450+msgstr "Bezpečnostní uzamčení"
441451
442452#: qml/Wizard/Pages/30-passwd-type.qml:74
443453msgid "Please select how you’d like to unlock your phone."
444−msgstr ""
454+msgstr "Zvolte, prosím, jak chcete svůj telefon odemykat."
445455
446456#: qml/Wizard/Pages/30-passwd-type.qml:97
447457msgctxt "Label: Type of security method"
448458msgid "Swipe"
449−msgstr ""
459+msgstr "Potažením prstu"
450460
451461#: qml/Wizard/Pages/30-passwd-type.qml:98
452462msgctxt "Label: Description of security method"
453463msgid "No security"
454−msgstr ""
464+msgstr "Bez zabezpečení"
455465
456466#: qml/Wizard/Pages/30-passwd-type.qml:100
457467msgctxt "Label: Type of security method"
458468msgid "Passcode"
459−msgstr ""
469+msgstr "Číselné heslo"
460470
461471#: qml/Wizard/Pages/30-passwd-type.qml:101
462472msgctxt "Label: Description of security method"
463473msgid "4 digits only"
464−msgstr ""
474+msgstr "Pouze 4 čísla"
465475
466476#: qml/Wizard/Pages/30-passwd-type.qml:103
467477msgctxt "Label: Type of security method"
468478msgid "Passphrase"
469−msgstr ""
479+msgstr "Heslo"
470480
471481#: qml/Wizard/Pages/30-passwd-type.qml:104
472482msgctxt "Label: Description of security method"
473483msgid "Numbers and letters"
474−msgstr ""
484+msgstr "Čísla a písmena"
475485
476486#: qml/Wizard/Pages/40-wifi.qml:29
477487msgid "Connect to Wi‑Fi"
478−msgstr ""
488+msgstr "Připojte se k Wi-Fi"
479489
480490#: qml/Wizard/Pages/40-wifi.qml:168
481491msgid "Available networks…"
482−msgstr ""
492+msgstr "Dostupné sítě..."
483493
484494#: qml/Wizard/Pages/40-wifi.qml:169
485495msgid "No available networks."
486−msgstr ""
496+msgstr "Žádná dostupná síť."
487497
488498#: qml/Wizard/Pages/50-location.qml:27
489499msgid "Location"
490−msgstr ""
500+msgstr "Umístění"
491501
492502#: qml/Wizard/Pages/50-location.qml:62
493503msgid "Let the phone detect your location:"
494−msgstr ""
504+msgstr "Povolte telefonu zjistit, kde se zrovna nacházíte:"
495505
496506#: qml/Wizard/Pages/50-location.qml:69
497507msgid "Using GPS only (less accurate)"
498−msgstr ""
508+msgstr "Používat pouze GPS (méně přesné)"
499509
500510#: qml/Wizard/Pages/50-location.qml:86
501511msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502−msgstr ""
512+msgstr "Používat GPS, anonymní Wi-Fi a datovou síť (doporučeno)"
503513
504514#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505515#: qml/Wizard/Pages/50-location.qml:103
@@ -507,81 +517,88 @@ msgid ""
507517"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508518"conditions</a>."
509519msgstr ""
520+"Zvolením této možnosti souhlasíte s <a href='#'>pravidly a podmínkami</a> "
521+"Nokia HERE."
510522
511523#: qml/Wizard/Pages/50-location.qml:112
512524msgid "Not at all"
513−msgstr ""
525+msgstr "V žádném případě"
514526
515527#: qml/Wizard/Pages/50-location.qml:124
516528msgid "You can change your mind later in <b>System Settings</b>."
517529msgstr ""
530+"Pokud změníte názor, můžete nastavení změnit v <b>Nastavení systému</b>."
518531
519532#: qml/Wizard/Pages/60-reporting.qml:24
520533msgid "Improving your experience"
521−msgstr ""
534+msgstr "Zlepšete své zkušenosti"
522535
523536#: qml/Wizard/Pages/60-reporting.qml:36
524537msgid ""
525538"Your phone is set up to automatically report errors to Canonical and its "
526539"partners, the makers of the operating system."
527540msgstr ""
541+"Váš telefon je nastaven tak, aby automaticky odesílal informace o chybách "
542+"společnosti Canonical a partnerům, kteří stvořili tento operační systém."
528543
529544#: qml/Wizard/Pages/60-reporting.qml:43
530545msgid ""
531546"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532547"Privacy</b>"
533548msgstr ""
549+"To může být vypnuto v <b>Nastavení systému</b> v <b>Zabezpečení a "
550+"soukromí</b>"
534551
535552#: qml/Wizard/Pages/80-finished.qml:24
536553msgid "All done"
537−msgstr ""
554+msgstr "Vše je hotovo"
538555
539556#: qml/Wizard/Pages/80-finished.qml:39
540557msgid "Nice work!"
541−msgstr ""
558+msgstr "Skvělá práce!"
542559
543560#: qml/Wizard/Pages/80-finished.qml:46
544561msgid "Your phone is now ready to use."
545−msgstr ""
562+msgstr "Váš telefon je nyní připraven k použití."
546563
547564#: qml/Wizard/Pages/80-finished.qml:53
548565msgid "Finish"
549−msgstr ""
566+msgstr "Dokončit"
550567
551568#: qml/Wizard/Pages/here-terms.qml:27
552569msgid "Terms & Conditions"
553−msgstr ""
570+msgstr "Pravidla a podmínky"
554571
555572#: qml/Wizard/Pages/passwd-confirm.qml:49
556573msgid "Confirm passphrase"
557−msgstr ""
574+msgstr "Potvrdit heslo"
558575
559576#: qml/Wizard/Pages/passwd-confirm.qml:50
560577msgid "Confirm passcode"
561−msgstr ""
578+msgstr "Potvrdit číselné heslo"
562579
563580#: qml/Wizard/Pages/passwd-confirm.qml:53
564581#: qml/Wizard/Pages/passwd-confirm.qml:54
565582msgid "Please try again."
566−msgstr ""
583+msgstr "Zkuste to, prosím, znovu."
567584
568585#: qml/Wizard/Pages/passwd-set.qml:61
569586msgid "Choose your passcode"
570−msgstr ""
587+msgstr "Zvolte své heslo"
571588
572589#: qml/Wizard/Pages/passwd-set.qml:68
573590msgid "Passphrase must be 4 characters long"
574−msgstr ""
591+msgstr "Heslo musí být alespoň 4 znaky dlouhé"
575592
576593#: qml/Wizard/StackButton.qml:39
577594#, qt-format
578595msgid "〈 %1"
579−msgstr ""
596+msgstr "〈 %1"
580597
581598#: qml/Wizard/StackButton.qml:42
582599#, qt-format
583600msgid "%1 〉"
584−msgstr ""
601+msgstr "%1 〉"
585602
586603#~ msgid "Try swiping from the right edge to unlock the phone"
587604#~ msgstr "Pokuste se odemknout telefon potažením prstu z pravého okraje."
po/da.po+53−51
@@ -9,19 +9,19 @@ msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1111"POT-Creation-Date: 2015-04-14 16:13+0200\n"
12−"PO-Revision-Date: 2015-04-17 10:46+0000\n"
13−"Last-Translator: Bluegrassin <anderslgade@gmail.com>\n"
12+"PO-Revision-Date: 2015-04-28 19:14+0000\n"
13+"Last-Translator: Thomas Lundsgaard Hansen <thomaslhansen@live.dk>\n"
1414"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
1515"MIME-Version: 1.0\n"
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
20+"X-Generator: Launchpad (build 17453)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
24−msgstr ""
24+msgstr "Adgangskode: "
2525
2626#: plugins/Unity/Launcher/launcheritem.cpp:45
2727#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -58,7 +58,7 @@ msgstr[1] "Vent venligst %1 minutter og prøv igen..."
5858#: qml/Components/Dialogs.qml:69
5959msgctxt "Title: Lock/Log out dialog"
6060msgid "Log out"
61−msgstr ""
61+msgstr "Log af"
6262
6363#: qml/Components/Dialogs.qml:70
6464msgid "Are you sure you want to log out?"
@@ -67,22 +67,22 @@ msgstr "Vil du logge ud?"
6767#: qml/Components/Dialogs.qml:72
6868msgctxt "Button: Lock the system"
6969msgid "Lock"
70−msgstr ""
70+msgstr "Lås"
7171
7272#: qml/Components/Dialogs.qml:79
7373msgctxt "Button: Log out from the system"
7474msgid "Log Out"
75−msgstr ""
75+msgstr "Log af"
7676
7777#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7878#: qml/Components/Dialogs.qml:173
7979msgid "Cancel"
80−msgstr ""
80+msgstr "Annullér"
8181
8282#: qml/Components/Dialogs.qml:98
8383msgctxt "Title: Reboot/Shut down dialog"
8484msgid "Shut down"
85−msgstr ""
85+msgstr "Luk ned"
8686
8787#: qml/Components/Dialogs.qml:99
8888msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Er du sikker på du vil lukke ned?"
9191#: qml/Components/Dialogs.qml:101
9292msgctxt "Button: Reboot the system"
9393msgid "Reboot"
94−msgstr ""
94+msgstr "Genstart"
9595
9696#: qml/Components/Dialogs.qml:109
9797msgctxt "Button: Shut down the system"
9898msgid "Shut down"
99−msgstr ""
99+msgstr "Luk ned"
100100
101101#: qml/Components/Dialogs.qml:129
102102msgctxt "Title: Reboot dialog"
103103msgid "Reboot"
104−msgstr ""
104+msgstr "Genstart"
105105
106106#: qml/Components/Dialogs.qml:130
107107msgid "Are you sure you want to reboot?"
@@ -109,11 +109,11 @@ msgstr "Er du sikker på du vil genstarte?"
109109
110110#: qml/Components/Dialogs.qml:132
111111msgid "No"
112−msgstr ""
112+msgstr "Nej"
113113
114114#: qml/Components/Dialogs.qml:138
115115msgid "Yes"
116−msgstr ""
116+msgstr "Ja"
117117
118118#: qml/Components/Dialogs.qml:152
119119msgctxt "Title: Power off/Restart dialog"
@@ -131,12 +131,12 @@ msgstr ""
131131#: qml/Components/Dialogs.qml:155
132132msgctxt "Button: Power off the system"
133133msgid "Power off"
134−msgstr ""
134+msgstr "Luk ned"
135135
136136#: qml/Components/Dialogs.qml:164
137137msgctxt "Button: Restart the system"
138138msgid "Restart"
139−msgstr ""
139+msgstr "Genstart"
140140
141141#: qml/Components/Lockscreen.qml:245
142142msgid "Return to Call"
@@ -161,30 +161,30 @@ msgstr "Se alle"
161161#: qml/Dash/GenericScopeView.qml:594
162162msgctxt "Label: Hint for dash search line edit"
163163msgid "Search"
164−msgstr ""
164+msgstr "Søg"
165165
166166#: qml/Dash/PageHeader.qml:269
167167msgctxt "Button: Open the Ubuntu Store"
168168msgid "Store"
169−msgstr ""
169+msgstr "Butik"
170170
171171#: qml/Dash/PageHeader.qml:276
172172msgctxt "Button: Start a search in the current dash scope"
173173msgid "Search"
174−msgstr ""
174+msgstr "Søg"
175175
176176#: qml/Dash/PageHeader.qml:286
177177msgctxt "Button: Show the current dash scope settings"
178178msgid "Settings"
179−msgstr ""
179+msgstr "Indstillinger"
180180
181181#: qml/Dash/PageHeader.qml:293
182182msgid "Remove from Favorites"
183−msgstr ""
183+msgstr "Fjern fra Favoritter"
184184
185185#: qml/Dash/PageHeader.qml:293
186186msgid "Add to Favorites"
187−msgstr ""
187+msgstr "Tilføj til Favoritter"
188188
189189#: qml/Dash/Previews/PreviewActionCombo.qml:35
190190msgid "More..."
@@ -220,11 +220,11 @@ msgstr "Aktivér lokationsdata"
220220
221221#: qml/Dash/ScopesList.qml:66
222222msgid "Manage"
223−msgstr ""
223+msgstr "Administrér"
224224
225225#: qml/Dash/ScopesList.qml:112
226226msgid "Home"
227−msgstr ""
227+msgstr "Hjem"
228228
229229#: qml/Dash/ScopesList.qml:113
230230msgid "Also installed"
@@ -232,15 +232,15 @@ msgstr "Også installeret"
232232
233233#: qml/Greeter/CoverPage.qml:107
234234msgid "Unlock"
235−msgstr ""
235+msgstr "Lås op"
236236
237237#: qml/Greeter/LoginList.qml:236
238238msgid "Retry"
239−msgstr ""
239+msgstr "Forsøg igen"
240240
241241#: qml/Greeter/LoginList.qml:237
242242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Tap for at låse op"
244244
245245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246246msgid "Sorry, incorrect passphrase."
@@ -277,7 +277,7 @@ msgstr "Indtast %1"
277277
278278#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
279279msgid "Enter passphrase"
280−msgstr ""
280+msgstr "Angiv adgangsfrase"
281281
282282#: qml/Greeter/NarrowView.qml:117
283283msgid "Enter passcode"
@@ -314,7 +314,7 @@ msgstr "Konference"
314314
315315#: qml/Panel/Indicators/MenuItemFactory.qml:639
316316msgid "Nothing is playing"
317−msgstr ""
317+msgstr "Intet afspilles"
318318
319319#: qml/Panel/Indicators/MenuItemFactory.qml:787
320320msgid "In queue…"
@@ -343,7 +343,7 @@ msgstr "Mislykkedes - tryk for at prøve igen"
343343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344344msgctxt "Label: Hint in message indicator line edit"
345345msgid "Reply"
346−msgstr ""
346+msgstr "Svar"
347347
348348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349349msgid "Unlock SIM"
@@ -351,7 +351,7 @@ msgstr "Lås SIM-kort op"
351351
352352#: qml/Panel/Indicators/RoamingIndication.qml:27
353353msgid "Roaming"
354−msgstr ""
354+msgstr "Roaming"
355355
356356#: qml/Tutorial/TutorialBottomFinish.qml:24
357357msgid "This action does different things for different apps"
@@ -376,7 +376,7 @@ msgstr "Tryk her for at fortsætte."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Tryk her for at afslutte."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
@@ -384,7 +384,7 @@ msgstr "Disse er genveje til favorit-apps"
384384
385385#: qml/Tutorial/TutorialLeft.qml:26
386386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Åbn opstarteren"
388388
389389#: qml/Tutorial/TutorialLeft.qml:27
390390msgid "Short swipe from the left edge."
@@ -413,11 +413,11 @@ msgstr "Se alle dine kørende opgaver."
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Tilbage"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
420−msgstr ""
420+msgstr "Hej!"
421421
422422#: qml/Wizard/Pages/10-welcome.qml:44
423423msgid "Welcome to your Ubuntu phone."
@@ -432,7 +432,7 @@ msgstr "Lad os komme i gang."
432432#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
433433#: qml/Wizard/Pages/passwd-set.qml:90
434434msgid "Continue"
435−msgstr ""
435+msgstr "Fortsæt"
436436
437437#: qml/Wizard/Pages/20-sim.qml:25
438438msgid "Add a SIM card and restart your device"
@@ -441,14 +441,16 @@ msgstr "Indsæt et SIM-kort og genstart din enhed"
441441#: qml/Wizard/Pages/20-sim.qml:59
442442msgid "Without it, you won’t be able to make calls or use text messaging."
443443msgstr ""
444+"Uden denne vil du ikke være i stand til at foretage opkald eller sende "
445+"tekstbeskeder."
444446
445447#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
446448msgid "Skip"
447−msgstr ""
449+msgstr "Spring over"
448450
449451#: qml/Wizard/Pages/30-passwd-type.qml:39
450452msgid "Lock security"
451−msgstr ""
453+msgstr "Låsesikkerhed"
452454
453455#: qml/Wizard/Pages/30-passwd-type.qml:74
454456msgid "Please select how you’d like to unlock your phone."
@@ -457,32 +459,32 @@ msgstr "Vælg venligst hvordan du ønsker at låse din telefon op."
457459#: qml/Wizard/Pages/30-passwd-type.qml:97
458460msgctxt "Label: Type of security method"
459461msgid "Swipe"
460−msgstr ""
462+msgstr "Swipe"
461463
462464#: qml/Wizard/Pages/30-passwd-type.qml:98
463465msgctxt "Label: Description of security method"
464466msgid "No security"
465−msgstr ""
467+msgstr "Ingen sikkerhed"
466468
467469#: qml/Wizard/Pages/30-passwd-type.qml:100
468470msgctxt "Label: Type of security method"
469471msgid "Passcode"
470−msgstr ""
472+msgstr "Adgangskode"
471473
472474#: qml/Wizard/Pages/30-passwd-type.qml:101
473475msgctxt "Label: Description of security method"
474476msgid "4 digits only"
475−msgstr ""
477+msgstr "Kun 4 tal"
476478
477479#: qml/Wizard/Pages/30-passwd-type.qml:103
478480msgctxt "Label: Type of security method"
479481msgid "Passphrase"
480−msgstr ""
482+msgstr "Adgangsfrase"
481483
482484#: qml/Wizard/Pages/30-passwd-type.qml:104
483485msgctxt "Label: Description of security method"
484486msgid "Numbers and letters"
485−msgstr ""
487+msgstr "Tal og bogstaver"
486488
487489#: qml/Wizard/Pages/40-wifi.qml:29
488490msgid "Connect to Wi‑Fi"
@@ -498,7 +500,7 @@ msgstr "Ingen tilgængelige netværk."
498500
499501#: qml/Wizard/Pages/50-location.qml:27
500502msgid "Location"
501−msgstr ""
503+msgstr "Placering"
502504
503505#: qml/Wizard/Pages/50-location.qml:62
504506msgid "Let the phone detect your location:"
@@ -564,15 +566,15 @@ msgstr "Din telefon er nu klar til brug."
564566
565567#: qml/Wizard/Pages/80-finished.qml:53
566568msgid "Finish"
567−msgstr ""
569+msgstr "Afslut"
568570
569571#: qml/Wizard/Pages/here-terms.qml:27
570572msgid "Terms & Conditions"
571−msgstr ""
573+msgstr "Betingelser og vilkår"
572574
573575#: qml/Wizard/Pages/passwd-confirm.qml:49
574576msgid "Confirm passphrase"
575−msgstr ""
577+msgstr "Bekæft adgangsfrase"
576578
577579#: qml/Wizard/Pages/passwd-confirm.qml:50
578580msgid "Confirm passcode"
@@ -581,7 +583,7 @@ msgstr "Bekræft adgangskode"
581583#: qml/Wizard/Pages/passwd-confirm.qml:53
582584#: qml/Wizard/Pages/passwd-confirm.qml:54
583585msgid "Please try again."
584−msgstr ""
586+msgstr "Forsøg venligst igen."
585587
586588#: qml/Wizard/Pages/passwd-set.qml:61
587589msgid "Choose your passcode"
@@ -589,7 +591,7 @@ msgstr "Vælg din adgangskode"
589591
590592#: qml/Wizard/Pages/passwd-set.qml:68
591593msgid "Passphrase must be 4 characters long"
592−msgstr ""
594+msgstr "Adgangsfrasen skal være mindst 4 tegn"
593595
594596#: qml/Wizard/StackButton.qml:39
595597#, qt-format
po/de.po+30−30
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-06 14:24+0000\n"
12−"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
11+"PO-Revision-Date: 2015-05-14 15:49+0000\n"
12+"Last-Translator: Tobias Bannert <Unknown>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-15 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -51,13 +51,13 @@ msgstr ""
5151#, qt-format
5252msgid "Please wait %1 minute and then try again…"
5353msgid_plural "Please wait %1 minutes and then try again…"
54−msgstr[0] "Bitte warten Sie %1 Minute und versuchen Sie es dann erneut..."
55−msgstr[1] "Bitte warten Sie %1 Minuten und versuchen Sie es dann erneut..."
54+msgstr[0] "Bitte %1 Minute warten und es dann erneut versuchen …"
55+msgstr[1] "Bitte %1 Minuten warten und es dann erneut versuchen …"
5656
5757#: qml/Components/Dialogs.qml:69
5858msgctxt "Title: Lock/Log out dialog"
5959msgid "Log out"
60−msgstr ""
60+msgstr "Abmelden"
6161
6262#: qml/Components/Dialogs.qml:70
6363msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Sind Sie sicher, dass Sie sich abmelden wollen?"
6666#: qml/Components/Dialogs.qml:72
6767msgctxt "Button: Lock the system"
6868msgid "Lock"
69−msgstr ""
69+msgstr "Sperren"
7070
7171#: qml/Components/Dialogs.qml:79
7272msgctxt "Button: Log out from the system"
7373msgid "Log Out"
74−msgstr ""
74+msgstr "Abmelden"
7575
7676#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7777#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Abbrechen"
8181#: qml/Components/Dialogs.qml:98
8282msgctxt "Title: Reboot/Shut down dialog"
8383msgid "Shut down"
84−msgstr ""
84+msgstr "Ausschalten"
8585
8686#: qml/Components/Dialogs.qml:99
8787msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Sind Sie sicher, dass Sie herunterfahren wollen?"
9090#: qml/Components/Dialogs.qml:101
9191msgctxt "Button: Reboot the system"
9292msgid "Reboot"
93−msgstr ""
93+msgstr "Neustarten"
9494
9595#: qml/Components/Dialogs.qml:109
9696msgctxt "Button: Shut down the system"
9797msgid "Shut down"
98−msgstr ""
98+msgstr "Ausschalten"
9999
100100#: qml/Components/Dialogs.qml:129
101101msgctxt "Title: Reboot dialog"
102102msgid "Reboot"
103−msgstr ""
103+msgstr "Neustarten"
104104
105105#: qml/Components/Dialogs.qml:130
106106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Ja"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Leistung"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130130#: qml/Components/Dialogs.qml:155
131131msgctxt "Button: Power off the system"
132132msgid "Power off"
133−msgstr ""
133+msgstr "Ausschalten"
134134
135135#: qml/Components/Dialogs.qml:164
136136msgctxt "Button: Restart the system"
137137msgid "Restart"
138−msgstr ""
138+msgstr "Neustarten"
139139
140140#: qml/Components/Lockscreen.qml:245
141141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Alle anzeigen"
160160#: qml/Dash/GenericScopeView.qml:594
161161msgctxt "Label: Hint for dash search line edit"
162162msgid "Search"
163−msgstr ""
163+msgstr "Suchen"
164164
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Speichern"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Suchen"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Einstellungen"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -305,7 +305,7 @@ msgstr "Passwort anzeigen"
305305
306306#: qml/Panel/ActiveCallHint.qml:79
307307msgid "Tap to return to call..."
308−msgstr "Tippen um zum Anruf zurückzugehen"
308+msgstr "Tippen, um zum Anruf zurückzugehen …"
309309
310310#: qml/Panel/ActiveCallHint.qml:92
311311msgid "Conference"
@@ -342,7 +342,7 @@ msgstr "Fehlgeschlagen, tippen Sie zum Wiederholen"
342342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
343343msgctxt "Label: Hint in message indicator line edit"
344344msgid "Reply"
345−msgstr ""
345+msgstr "Antworten"
346346
347347#: qml/Panel/Indicators/ModemInfoItem.qml:105
348348msgid "Unlock SIM"
@@ -376,7 +376,7 @@ msgstr "Zum Fortfahren hier tippen."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Zum Abschließen bitte hier klicken."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
@@ -413,7 +413,7 @@ msgstr "Sehen Sie alle Ihre laufenden Anwendungen."
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Zurück"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Bitte wählen Sie aus, wie Sie Ihr Telefon entsperren wollen."
457457#: qml/Wizard/Pages/30-passwd-type.qml:97
458458msgctxt "Label: Type of security method"
459459msgid "Swipe"
460−msgstr ""
460+msgstr "Wischen"
461461
462462#: qml/Wizard/Pages/30-passwd-type.qml:98
463463msgctxt "Label: Description of security method"
464464msgid "No security"
465−msgstr ""
465+msgstr "Keine Sicherheitssperre"
466466
467467#: qml/Wizard/Pages/30-passwd-type.qml:100
468468msgctxt "Label: Type of security method"
469469msgid "Passcode"
470−msgstr ""
470+msgstr "PIN"
471471
472472#: qml/Wizard/Pages/30-passwd-type.qml:101
473473msgctxt "Label: Description of security method"
474474msgid "4 digits only"
475−msgstr ""
475+msgstr "nur vier Ziffern"
476476
477477#: qml/Wizard/Pages/30-passwd-type.qml:103
478478msgctxt "Label: Type of security method"
479479msgid "Passphrase"
480−msgstr ""
480+msgstr "Kennwort"
481481
482482#: qml/Wizard/Pages/30-passwd-type.qml:104
483483msgctxt "Label: Description of security method"
484484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Ziffern und Buchstaben"
486486
487487#: qml/Wizard/Pages/40-wifi.qml:29
488488msgid "Connect to Wi‑Fi"
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-19 21:55+0000\n"
11+"PO-Revision-Date: 2015-05-10 15:54+0000\n"
1212"Last-Translator: Anne <anneonyme017@gmail.com>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-11 05:41+0000\n"
19+"X-Generator: Launchpad (build 17487)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -377,7 +377,7 @@ msgstr "Appuyez ici pour continuer."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Cliquez ici pour terminer."
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
po/gd.po+4−4
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-27 09:50+0000\n"
11+"PO-Revision-Date: 2015-05-01 16:39+0000\n"
1212"Last-Translator: GunChleoc <Unknown>\n"
1313"Language-Team: Fòram na Gàidhlig\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-05-02 05:29+0000\n"
20+"X-Generator: Launchpad (build 17474)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:112
@@ -169,7 +169,7 @@ msgstr "Lorg"
169169#: qml/Dash/PageHeader.qml:269
170170msgctxt "Button: Open the Ubuntu Store"
171171msgid "Store"
172−msgstr "A' bhùth"
172+msgstr "Am bùth"
173173
174174#: qml/Dash/PageHeader.qml:276
175175msgctxt "Button: Start a search in the current dash scope"
po/hu.po+8−8
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-23 20:49+0000\n"
11+"PO-Revision-Date: 2015-05-04 18:02+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-24 05:39+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-05 05:47+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -239,11 +239,11 @@ msgstr "Koppintson a feloldáshoz"
239239
240240#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
241241msgid "Sorry, incorrect passphrase."
242−msgstr "Sajnálom, helytelen jelmondat."
242+msgstr "Elnézést, helytelen jelmondat."
243243
244244#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
245245msgid "Sorry, incorrect passcode."
246−msgstr "Sajnálom, helytelen jelkód."
246+msgstr "Elnézést, helytelen jelkód."
247247
248248#: qml/Greeter/NarrowView.qml:55
249249msgid "This will be your last attempt."
@@ -281,11 +281,11 @@ msgstr "Jelkód megadása"
281281#: qml/Greeter/NarrowView.qml:118
282282#, qt-format
283283msgid "Sorry, incorrect %1"
284−msgstr "Sajnálom, helytelen %1"
284+msgstr "Elnézést, helytelen %1"
285285
286286#: qml/Greeter/NarrowView.qml:119
287287msgid "Sorry, incorrect passphrase"
288−msgstr "Sajnálom, helytelen jelmondat"
288+msgstr "Elnézést, helytelen jelmondat"
289289
290290#: qml/Greeter/NarrowView.qml:120
291291msgid "Please re-enter"
@@ -293,7 +293,7 @@ msgstr "Próbálja újra"
293293
294294#: qml/Greeter/NarrowView.qml:121
295295msgid "Sorry, incorrect passcode"
296−msgstr "Sajnálom, helytelen jelkód"
296+msgstr "Elnézést, helytelen jelkód"
297297
298298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299299msgid "Show password"
po/nn.ponew file+584
@@ -0,0 +1,584 @@
1+# Norwegian Nynorsk translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-05-12 11:32+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Norwegian Nynorsk <nn@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:65
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:74
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:69
54+msgctxt "Title: Lock/Log out dialog"
55+msgid "Log out"
56+msgstr ""
57+
58+#: qml/Components/Dialogs.qml:70
59+msgid "Are you sure you want to log out?"
60+msgstr ""
61+
62+#: qml/Components/Dialogs.qml:72
63+msgctxt "Button: Lock the system"
64+msgid "Lock"
65+msgstr ""
66+
67+#: qml/Components/Dialogs.qml:79
68+msgctxt "Button: Log out from the system"
69+msgid "Log Out"
70+msgstr ""
71+
72+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73+#: qml/Components/Dialogs.qml:173
74+msgid "Cancel"
75+msgstr ""
76+
77+#: qml/Components/Dialogs.qml:98
78+msgctxt "Title: Reboot/Shut down dialog"
79+msgid "Shut down"
80+msgstr ""
81+
82+#: qml/Components/Dialogs.qml:99
83+msgid "Are you sure you want to shut down?"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:101
87+msgctxt "Button: Reboot the system"
88+msgid "Reboot"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:109
92+msgctxt "Button: Shut down the system"
93+msgid "Shut down"
94+msgstr ""
95+
96+#: qml/Components/Dialogs.qml:129
97+msgctxt "Title: Reboot dialog"
98+msgid "Reboot"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:130
102+msgid "Are you sure you want to reboot?"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:132
106+msgid "No"
107+msgstr ""
108+
109+#: qml/Components/Dialogs.qml:138
110+msgid "Yes"
111+msgstr ""
112+
113+#: qml/Components/Dialogs.qml:152
114+msgctxt "Title: Power off/Restart dialog"
115+msgid "Power"
116+msgstr ""
117+
118+#: qml/Components/Dialogs.qml:153
119+msgid ""
120+"Are you sure you would like\n"
121+"to power off?"
122+msgstr ""
123+
124+#: qml/Components/Dialogs.qml:155
125+msgctxt "Button: Power off the system"
126+msgid "Power off"
127+msgstr ""
128+
129+#: qml/Components/Dialogs.qml:164
130+msgctxt "Button: Restart the system"
131+msgid "Restart"
132+msgstr ""
133+
134+#: qml/Components/Lockscreen.qml:245
135+msgid "Return to Call"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Emergency Call"
140+msgstr ""
141+
142+#: qml/Components/Lockscreen.qml:277
143+msgid "OK"
144+msgstr ""
145+
146+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147+msgid "See less"
148+msgstr ""
149+
150+#: qml/Dash/GenericScopeView.qml:532
151+msgid "See all"
152+msgstr ""
153+
154+#: qml/Dash/GenericScopeView.qml:594
155+msgctxt "Label: Hint for dash search line edit"
156+msgid "Search"
157+msgstr ""
158+
159+#: qml/Dash/PageHeader.qml:269
160+msgctxt "Button: Open the Ubuntu Store"
161+msgid "Store"
162+msgstr ""
163+
164+#: qml/Dash/PageHeader.qml:276
165+msgctxt "Button: Start a search in the current dash scope"
166+msgid "Search"
167+msgstr ""
168+
169+#: qml/Dash/PageHeader.qml:286
170+msgctxt "Button: Show the current dash scope settings"
171+msgid "Settings"
172+msgstr ""
173+
174+#: qml/Dash/PageHeader.qml:293
175+msgid "Remove from Favorites"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Add to Favorites"
180+msgstr ""
181+
182+#: qml/Dash/Previews/PreviewActionCombo.qml:35
183+msgid "More..."
184+msgstr ""
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "Less..."
188+msgstr ""
189+
190+#: qml/Dash/Previews/PreviewRatingInput.qml:81
191+msgid "Rate this"
192+msgstr ""
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:126
195+msgid "Add a review"
196+msgstr ""
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:166
199+msgid "Send"
200+msgstr ""
201+
202+#: qml/Dash/PullToRefreshScopeStyle.qml:55
203+msgid "Pull to refresh…"
204+msgstr ""
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:60
207+msgid "Release to refresh…"
208+msgstr ""
209+
210+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211+msgid "Enable location data"
212+msgstr ""
213+
214+#: qml/Dash/ScopesList.qml:66
215+msgid "Manage"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:112
219+msgid "Home"
220+msgstr ""
221+
222+#: qml/Dash/ScopesList.qml:113
223+msgid "Also installed"
224+msgstr ""
225+
226+#: qml/Greeter/CoverPage.qml:107
227+msgid "Unlock"
228+msgstr ""
229+
230+#: qml/Greeter/LoginList.qml:236
231+msgid "Retry"
232+msgstr ""
233+
234+#: qml/Greeter/LoginList.qml:237
235+msgid "Tap to unlock"
236+msgstr ""
237+
238+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239+msgid "Sorry, incorrect passphrase."
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243+msgid "Sorry, incorrect passcode."
244+msgstr ""
245+
246+#: qml/Greeter/NarrowView.qml:55
247+msgid "This will be your last attempt."
248+msgstr ""
249+
250+#: qml/Greeter/NarrowView.qml:57
251+msgid ""
252+"If passphrase is entered incorrectly, your phone will conduct a factory "
253+"reset and all personal data will be deleted."
254+msgstr ""
255+
256+#: qml/Greeter/NarrowView.qml:58
257+msgid ""
258+"If passcode is entered incorrectly, your phone will conduct a factory reset "
259+"and all personal data will be deleted."
260+msgstr ""
261+
262+#: qml/Greeter/NarrowView.qml:115
263+#, qt-format
264+msgid "Enter %1"
265+msgstr ""
266+
267+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268+msgid "Enter passphrase"
269+msgstr ""
270+
271+#: qml/Greeter/NarrowView.qml:117
272+msgid "Enter passcode"
273+msgstr ""
274+
275+#: qml/Greeter/NarrowView.qml:118
276+#, qt-format
277+msgid "Sorry, incorrect %1"
278+msgstr ""
279+
280+#: qml/Greeter/NarrowView.qml:119
281+msgid "Sorry, incorrect passphrase"
282+msgstr ""
283+
284+#: qml/Greeter/NarrowView.qml:120
285+msgid "Please re-enter"
286+msgstr ""
287+
288+#: qml/Greeter/NarrowView.qml:121
289+msgid "Sorry, incorrect passcode"
290+msgstr ""
291+
292+#: qml/Notifications/NotificationMenuItemFactory.qml:128
293+msgid "Show password"
294+msgstr ""
295+
296+#: qml/Panel/ActiveCallHint.qml:79
297+msgid "Tap to return to call..."
298+msgstr ""
299+
300+#: qml/Panel/ActiveCallHint.qml:92
301+msgid "Conference"
302+msgstr ""
303+
304+#: qml/Panel/Indicators/MenuItemFactory.qml:639
305+msgid "Nothing is playing"
306+msgstr ""
307+
308+#: qml/Panel/Indicators/MenuItemFactory.qml:787
309+msgid "In queue…"
310+msgstr ""
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:791
313+msgid "Downloading"
314+msgstr ""
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:793
317+msgid "Paused, tap to resume"
318+msgstr ""
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:795
321+msgid "Canceled"
322+msgstr ""
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:797
325+msgid "Finished"
326+msgstr ""
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:799
329+msgid "Failed, tap to retry"
330+msgstr ""
331+
332+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333+msgctxt "Label: Hint in message indicator line edit"
334+msgid "Reply"
335+msgstr ""
336+
337+#: qml/Panel/Indicators/ModemInfoItem.qml:105
338+msgid "Unlock SIM"
339+msgstr ""
340+
341+#: qml/Panel/Indicators/RoamingIndication.qml:27
342+msgid "Roaming"
343+msgstr ""
344+
345+#: qml/Tutorial/TutorialBottomFinish.qml:24
346+msgid "This action does different things for different apps"
347+msgstr ""
348+
349+#: qml/Tutorial/TutorialBottomFinish.qml:25
350+msgid "Tap here to finish."
351+msgstr ""
352+
353+#: qml/Tutorial/TutorialBottom.qml:28
354+msgid "Open special menus"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottom.qml:29
358+msgid "Swipe up from the bottom edge."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362+#: qml/Tutorial/TutorialRight.qml:144
363+msgid "Tap here to continue."
364+msgstr ""
365+
366+#: qml/Tutorial/TutorialContent.qml:81
367+msgid "Click here to finish."
368+msgstr ""
369+
370+#: qml/Tutorial/TutorialLeftFinish.qml:24
371+msgid "These are the shortcuts to favorite apps"
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialLeft.qml:26
375+msgid "Open the launcher"
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeft.qml:27
379+msgid "Short swipe from the left edge."
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialPage.qml:178
383+msgid "You almost got it!"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialPage.qml:195
387+msgid "Try again."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialRight.qml:31
391+msgid "To view open apps"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialRight.qml:32
395+msgid "Long swipe from the right edge."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:122
399+msgid "View all your running tasks."
400+msgstr ""
401+
402+#: qml/Wizard/Page.qml:89
403+msgctxt "Button: Go back one page in the Wizard"
404+msgid "Back"
405+msgstr ""
406+
407+#: qml/Wizard/Pages/10-welcome.qml:27
408+msgid "Hi!"
409+msgstr ""
410+
411+#: qml/Wizard/Pages/10-welcome.qml:44
412+msgid "Welcome to your Ubuntu phone."
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:52
416+msgid "Let’s get started."
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422+#: qml/Wizard/Pages/passwd-set.qml:90
423+msgid "Continue"
424+msgstr ""
425+
426+#: qml/Wizard/Pages/20-sim.qml:25
427+msgid "Add a SIM card and restart your device"
428+msgstr ""
429+
430+#: qml/Wizard/Pages/20-sim.qml:59
431+msgid "Without it, you won’t be able to make calls or use text messaging."
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435+msgid "Skip"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/30-passwd-type.qml:39
439+msgid "Lock security"
440+msgstr ""
441+
442+#: qml/Wizard/Pages/30-passwd-type.qml:74
443+msgid "Please select how you’d like to unlock your phone."
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:97
447+msgctxt "Label: Type of security method"
448+msgid "Swipe"
449+msgstr ""
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:98
452+msgctxt "Label: Description of security method"
453+msgid "No security"
454+msgstr ""
455+
456+#: qml/Wizard/Pages/30-passwd-type.qml:100
457+msgctxt "Label: Type of security method"
458+msgid "Passcode"
459+msgstr ""
460+
461+#: qml/Wizard/Pages/30-passwd-type.qml:101
462+msgctxt "Label: Description of security method"
463+msgid "4 digits only"
464+msgstr ""
465+
466+#: qml/Wizard/Pages/30-passwd-type.qml:103
467+msgctxt "Label: Type of security method"
468+msgid "Passphrase"
469+msgstr ""
470+
471+#: qml/Wizard/Pages/30-passwd-type.qml:104
472+msgctxt "Label: Description of security method"
473+msgid "Numbers and letters"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/40-wifi.qml:29
477+msgid "Connect to Wi‑Fi"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/40-wifi.qml:168
481+msgid "Available networks…"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:169
485+msgid "No available networks."
486+msgstr ""
487+
488+#: qml/Wizard/Pages/50-location.qml:27
489+msgid "Location"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/50-location.qml:62
493+msgid "Let the phone detect your location:"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:69
497+msgid "Using GPS only (less accurate)"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:86
501+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502+msgstr ""
503+
504+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505+#: qml/Wizard/Pages/50-location.qml:103
506+msgid ""
507+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508+"conditions</a>."
509+msgstr ""
510+
511+#: qml/Wizard/Pages/50-location.qml:112
512+msgid "Not at all"
513+msgstr ""
514+
515+#: qml/Wizard/Pages/50-location.qml:124
516+msgid "You can change your mind later in <b>System Settings</b>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/60-reporting.qml:24
520+msgid "Improving your experience"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/60-reporting.qml:36
524+msgid ""
525+"Your phone is set up to automatically report errors to Canonical and its "
526+"partners, the makers of the operating system."
527+msgstr ""
528+
529+#: qml/Wizard/Pages/60-reporting.qml:43
530+msgid ""
531+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532+"Privacy</b>"
533+msgstr ""
534+
535+#: qml/Wizard/Pages/80-finished.qml:24
536+msgid "All done"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/80-finished.qml:39
540+msgid "Nice work!"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:46
544+msgid "Your phone is now ready to use."
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:53
548+msgid "Finish"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/here-terms.qml:27
552+msgid "Terms & Conditions"
553+msgstr ""
554+
555+#: qml/Wizard/Pages/passwd-confirm.qml:49
556+msgid "Confirm passphrase"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/passwd-confirm.qml:50
560+msgid "Confirm passcode"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:53
564+#: qml/Wizard/Pages/passwd-confirm.qml:54
565+msgid "Please try again."
566+msgstr ""
567+
568+#: qml/Wizard/Pages/passwd-set.qml:61
569+msgid "Choose your passcode"
570+msgstr ""
571+
572+#: qml/Wizard/Pages/passwd-set.qml:68
573+msgid "Passphrase must be 4 characters long"
574+msgstr ""
575+
576+#: qml/Wizard/StackButton.qml:39
577+#, qt-format
578+msgid "〈 %1"
579+msgstr ""
580+
581+#: qml/Wizard/StackButton.qml:42
582+#, qt-format
583+msgid "%1 〉"
584+msgstr ""
po/pl.po+10−10
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-04 13:00+0000\n"
11+"PO-Revision-Date: 2015-05-02 12:37+0000\n"
1212"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-05-03 06:07+0000\n"
20+"X-Generator: Launchpad (build 17474)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -123,7 +123,7 @@ msgid ""
123123"Are you sure you would like\n"
124124"to power off?"
125125msgstr ""
126−"Jesteś pewny że chcesz\n"
126+"Czy na pewno\n"
127127"wyłączyć?"
128128
129129#: qml/Components/Dialogs.qml:155
@@ -361,11 +361,11 @@ msgstr "Dotknij tutaj, by zakończyć."
361361
362362#: qml/Tutorial/TutorialBottom.qml:28
363363msgid "Open special menus"
364−msgstr ""
364+msgstr "Otwórz menu specjalne"
365365
366366#: qml/Tutorial/TutorialBottom.qml:29
367367msgid "Swipe up from the bottom edge."
368−msgstr ""
368+msgstr "Przeciągnij od dolnej krawędzi w górę ekranu"
369369
370370#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
371371#: qml/Tutorial/TutorialRight.qml:144
@@ -374,7 +374,7 @@ msgstr "Dotknij tutaj, by kontynuować."
374374
375375#: qml/Tutorial/TutorialContent.qml:81
376376msgid "Click here to finish."
377−msgstr ""
377+msgstr "Kliknij tutaj, by zakończyć."
378378
379379#: qml/Tutorial/TutorialLeftFinish.qml:24
380380msgid "These are the shortcuts to favorite apps"
@@ -382,11 +382,11 @@ msgstr "To są skróty do ulubionych aplikacji"
382382
383383#: qml/Tutorial/TutorialLeft.qml:26
384384msgid "Open the launcher"
385−msgstr ""
385+msgstr "Otwórz launcher"
386386
387387#: qml/Tutorial/TutorialLeft.qml:27
388388msgid "Short swipe from the left edge."
389−msgstr ""
389+msgstr "Krótkie przeciągnięcie od lewej krawędzi."
390390
391391#: qml/Tutorial/TutorialPage.qml:178
392392msgid "You almost got it!"
@@ -402,7 +402,7 @@ msgstr "By zobaczyć otwarte aplikacje"
402402
403403#: qml/Tutorial/TutorialRight.qml:32
404404msgid "Long swipe from the right edge."
405−msgstr ""
405+msgstr "Długie przeciągnięcie od prawej krawędzi."
406406
407407#: qml/Tutorial/TutorialRight.qml:122
408408msgid "View all your running tasks."
po/pt.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-18 02:15+0000\n"
11+"PO-Revision-Date: 2015-05-19 03:42+0000\n"
1212"Last-Translator: António Miranda <Unknown>\n"
1313"Language-Team: Portuguese <pt@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-19 05:44+0000\n"
19+"X-Generator: Launchpad (build 17508)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -262,7 +262,7 @@ msgid ""
262262"If passcode is entered incorrectly, your phone will conduct a factory reset "
263263"and all personal data will be deleted."
264264msgstr ""
265−"Se o código seja introduzido incorretamente, todos os dados pessoais do seu "
265+"Se o código não for introduzido corretamente, todos os dados pessoais do seu "
266266"telemóvel serão apagados."
267267
268268#: qml/Greeter/NarrowView.qml:115
@@ -276,7 +276,7 @@ msgstr "Indique a palavra-passe"
276276
277277#: qml/Greeter/NarrowView.qml:117
278278msgid "Enter passcode"
279−msgstr "Introduza código"
279+msgstr "Introduza o código"
280280
281281#: qml/Greeter/NarrowView.qml:118
282282#, qt-format
@@ -293,7 +293,7 @@ msgstr "Por favor, volte a introduzir"
293293
294294#: qml/Greeter/NarrowView.qml:121
295295msgid "Sorry, incorrect passcode"
296−msgstr "Código incorreta"
296+msgstr "Código incorreto"
297297
298298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299299msgid "Show password"
@@ -371,7 +371,7 @@ msgstr "Toque aqui para continuar."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Clique aqui para finalizar."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/pt_BR.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-27 03:02+0000\n"
11+"PO-Revision-Date: 2015-05-04 21:50+0000\n"
1212"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-28 05:33+0000\n"
19−"X-Generator: Launchpad (build 17453)\n"
18+"X-Launchpad-Export-Date: 2015-05-05 05:47+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -163,17 +163,17 @@ msgstr "Pesquisar"
163163#: qml/Dash/PageHeader.qml:269
164164msgctxt "Button: Open the Ubuntu Store"
165165msgid "Store"
166−msgstr ""
166+msgstr "Loja"
167167
168168#: qml/Dash/PageHeader.qml:276
169169msgctxt "Button: Start a search in the current dash scope"
170170msgid "Search"
171−msgstr ""
171+msgstr "Pesquisar"
172172
173173#: qml/Dash/PageHeader.qml:286
174174msgctxt "Button: Show the current dash scope settings"
175175msgid "Settings"
176−msgstr ""
176+msgstr "Configurações"
177177
178178#: qml/Dash/PageHeader.qml:293
179179msgid "Remove from Favorites"
@@ -373,7 +373,7 @@ msgstr "Toque aqui para continuar."
373373
374374#: qml/Tutorial/TutorialContent.qml:81
375375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Clique aqui para finalizar."
377377
378378#: qml/Tutorial/TutorialLeftFinish.qml:24
379379msgid "These are the shortcuts to favorite apps"
po/ur.ponew file+584
@@ -0,0 +1,584 @@
1+# Urdu translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-05-05 03:12+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Urdu <ur@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-05-09 05:49+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:65
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:74
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:69
54+msgctxt "Title: Lock/Log out dialog"
55+msgid "Log out"
56+msgstr ""
57+
58+#: qml/Components/Dialogs.qml:70
59+msgid "Are you sure you want to log out?"
60+msgstr ""
61+
62+#: qml/Components/Dialogs.qml:72
63+msgctxt "Button: Lock the system"
64+msgid "Lock"
65+msgstr ""
66+
67+#: qml/Components/Dialogs.qml:79
68+msgctxt "Button: Log out from the system"
69+msgid "Log Out"
70+msgstr ""
71+
72+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73+#: qml/Components/Dialogs.qml:173
74+msgid "Cancel"
75+msgstr ""
76+
77+#: qml/Components/Dialogs.qml:98
78+msgctxt "Title: Reboot/Shut down dialog"
79+msgid "Shut down"
80+msgstr ""
81+
82+#: qml/Components/Dialogs.qml:99
83+msgid "Are you sure you want to shut down?"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:101
87+msgctxt "Button: Reboot the system"
88+msgid "Reboot"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:109
92+msgctxt "Button: Shut down the system"
93+msgid "Shut down"
94+msgstr ""
95+
96+#: qml/Components/Dialogs.qml:129
97+msgctxt "Title: Reboot dialog"
98+msgid "Reboot"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:130
102+msgid "Are you sure you want to reboot?"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:132
106+msgid "No"
107+msgstr ""
108+
109+#: qml/Components/Dialogs.qml:138
110+msgid "Yes"
111+msgstr "ھاں"
112+
113+#: qml/Components/Dialogs.qml:152
114+msgctxt "Title: Power off/Restart dialog"
115+msgid "Power"
116+msgstr ""
117+
118+#: qml/Components/Dialogs.qml:153
119+msgid ""
120+"Are you sure you would like\n"
121+"to power off?"
122+msgstr ""
123+
124+#: qml/Components/Dialogs.qml:155
125+msgctxt "Button: Power off the system"
126+msgid "Power off"
127+msgstr ""
128+
129+#: qml/Components/Dialogs.qml:164
130+msgctxt "Button: Restart the system"
131+msgid "Restart"
132+msgstr ""
133+
134+#: qml/Components/Lockscreen.qml:245
135+msgid "Return to Call"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Emergency Call"
140+msgstr ""
141+
142+#: qml/Components/Lockscreen.qml:277
143+msgid "OK"
144+msgstr ""
145+
146+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147+msgid "See less"
148+msgstr ""
149+
150+#: qml/Dash/GenericScopeView.qml:532
151+msgid "See all"
152+msgstr ""
153+
154+#: qml/Dash/GenericScopeView.qml:594
155+msgctxt "Label: Hint for dash search line edit"
156+msgid "Search"
157+msgstr ""
158+
159+#: qml/Dash/PageHeader.qml:269
160+msgctxt "Button: Open the Ubuntu Store"
161+msgid "Store"
162+msgstr ""
163+
164+#: qml/Dash/PageHeader.qml:276
165+msgctxt "Button: Start a search in the current dash scope"
166+msgid "Search"
167+msgstr ""
168+
169+#: qml/Dash/PageHeader.qml:286
170+msgctxt "Button: Show the current dash scope settings"
171+msgid "Settings"
172+msgstr ""
173+
174+#: qml/Dash/PageHeader.qml:293
175+msgid "Remove from Favorites"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Add to Favorites"
180+msgstr ""
181+
182+#: qml/Dash/Previews/PreviewActionCombo.qml:35
183+msgid "More..."
184+msgstr ""
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "Less..."
188+msgstr ""
189+
190+#: qml/Dash/Previews/PreviewRatingInput.qml:81
191+msgid "Rate this"
192+msgstr ""
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:126
195+msgid "Add a review"
196+msgstr ""
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:166
199+msgid "Send"
200+msgstr ""
201+
202+#: qml/Dash/PullToRefreshScopeStyle.qml:55
203+msgid "Pull to refresh…"
204+msgstr ""
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:60
207+msgid "Release to refresh…"
208+msgstr ""
209+
210+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211+msgid "Enable location data"
212+msgstr ""
213+
214+#: qml/Dash/ScopesList.qml:66
215+msgid "Manage"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:112
219+msgid "Home"
220+msgstr ""
221+
222+#: qml/Dash/ScopesList.qml:113
223+msgid "Also installed"
224+msgstr ""
225+
226+#: qml/Greeter/CoverPage.qml:107
227+msgid "Unlock"
228+msgstr ""
229+
230+#: qml/Greeter/LoginList.qml:236
231+msgid "Retry"
232+msgstr ""
233+
234+#: qml/Greeter/LoginList.qml:237
235+msgid "Tap to unlock"
236+msgstr ""
237+
238+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239+msgid "Sorry, incorrect passphrase."
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243+msgid "Sorry, incorrect passcode."
244+msgstr ""
245+
246+#: qml/Greeter/NarrowView.qml:55
247+msgid "This will be your last attempt."
248+msgstr ""
249+
250+#: qml/Greeter/NarrowView.qml:57
251+msgid ""
252+"If passphrase is entered incorrectly, your phone will conduct a factory "
253+"reset and all personal data will be deleted."
254+msgstr ""
255+
256+#: qml/Greeter/NarrowView.qml:58
257+msgid ""
258+"If passcode is entered incorrectly, your phone will conduct a factory reset "
259+"and all personal data will be deleted."
260+msgstr ""
261+
262+#: qml/Greeter/NarrowView.qml:115
263+#, qt-format
264+msgid "Enter %1"
265+msgstr ""
266+
267+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268+msgid "Enter passphrase"
269+msgstr ""
270+
271+#: qml/Greeter/NarrowView.qml:117
272+msgid "Enter passcode"
273+msgstr ""
274+
275+#: qml/Greeter/NarrowView.qml:118
276+#, qt-format
277+msgid "Sorry, incorrect %1"
278+msgstr ""
279+
280+#: qml/Greeter/NarrowView.qml:119
281+msgid "Sorry, incorrect passphrase"
282+msgstr ""
283+
284+#: qml/Greeter/NarrowView.qml:120
285+msgid "Please re-enter"
286+msgstr ""
287+
288+#: qml/Greeter/NarrowView.qml:121
289+msgid "Sorry, incorrect passcode"
290+msgstr ""
291+
292+#: qml/Notifications/NotificationMenuItemFactory.qml:128
293+msgid "Show password"
294+msgstr ""
295+
296+#: qml/Panel/ActiveCallHint.qml:79
297+msgid "Tap to return to call..."
298+msgstr ""
299+
300+#: qml/Panel/ActiveCallHint.qml:92
301+msgid "Conference"
302+msgstr ""
303+
304+#: qml/Panel/Indicators/MenuItemFactory.qml:639
305+msgid "Nothing is playing"
306+msgstr ""
307+
308+#: qml/Panel/Indicators/MenuItemFactory.qml:787
309+msgid "In queue…"
310+msgstr ""
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:791
313+msgid "Downloading"
314+msgstr ""
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:793
317+msgid "Paused, tap to resume"
318+msgstr ""
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:795
321+msgid "Canceled"
322+msgstr ""
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:797
325+msgid "Finished"
326+msgstr ""
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:799
329+msgid "Failed, tap to retry"
330+msgstr ""
331+
332+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333+msgctxt "Label: Hint in message indicator line edit"
334+msgid "Reply"
335+msgstr ""
336+
337+#: qml/Panel/Indicators/ModemInfoItem.qml:105
338+msgid "Unlock SIM"
339+msgstr ""
340+
341+#: qml/Panel/Indicators/RoamingIndication.qml:27
342+msgid "Roaming"
343+msgstr ""
344+
345+#: qml/Tutorial/TutorialBottomFinish.qml:24
346+msgid "This action does different things for different apps"
347+msgstr ""
348+
349+#: qml/Tutorial/TutorialBottomFinish.qml:25
350+msgid "Tap here to finish."
351+msgstr ""
352+
353+#: qml/Tutorial/TutorialBottom.qml:28
354+msgid "Open special menus"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottom.qml:29
358+msgid "Swipe up from the bottom edge."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362+#: qml/Tutorial/TutorialRight.qml:144
363+msgid "Tap here to continue."
364+msgstr ""
365+
366+#: qml/Tutorial/TutorialContent.qml:81
367+msgid "Click here to finish."
368+msgstr ""
369+
370+#: qml/Tutorial/TutorialLeftFinish.qml:24
371+msgid "These are the shortcuts to favorite apps"
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialLeft.qml:26
375+msgid "Open the launcher"
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeft.qml:27
379+msgid "Short swipe from the left edge."
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialPage.qml:178
383+msgid "You almost got it!"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialPage.qml:195
387+msgid "Try again."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialRight.qml:31
391+msgid "To view open apps"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialRight.qml:32
395+msgid "Long swipe from the right edge."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:122
399+msgid "View all your running tasks."
400+msgstr ""
401+
402+#: qml/Wizard/Page.qml:89
403+msgctxt "Button: Go back one page in the Wizard"
404+msgid "Back"
405+msgstr ""
406+
407+#: qml/Wizard/Pages/10-welcome.qml:27
408+msgid "Hi!"
409+msgstr ""
410+
411+#: qml/Wizard/Pages/10-welcome.qml:44
412+msgid "Welcome to your Ubuntu phone."
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:52
416+msgid "Let’s get started."
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422+#: qml/Wizard/Pages/passwd-set.qml:90
423+msgid "Continue"
424+msgstr ""
425+
426+#: qml/Wizard/Pages/20-sim.qml:25
427+msgid "Add a SIM card and restart your device"
428+msgstr ""
429+
430+#: qml/Wizard/Pages/20-sim.qml:59
431+msgid "Without it, you won’t be able to make calls or use text messaging."
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435+msgid "Skip"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/30-passwd-type.qml:39
439+msgid "Lock security"
440+msgstr ""
441+
442+#: qml/Wizard/Pages/30-passwd-type.qml:74
443+msgid "Please select how you’d like to unlock your phone."
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:97
447+msgctxt "Label: Type of security method"
448+msgid "Swipe"
449+msgstr ""
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:98
452+msgctxt "Label: Description of security method"
453+msgid "No security"
454+msgstr ""
455+
456+#: qml/Wizard/Pages/30-passwd-type.qml:100
457+msgctxt "Label: Type of security method"
458+msgid "Passcode"
459+msgstr ""
460+
461+#: qml/Wizard/Pages/30-passwd-type.qml:101
462+msgctxt "Label: Description of security method"
463+msgid "4 digits only"
464+msgstr ""
465+
466+#: qml/Wizard/Pages/30-passwd-type.qml:103
467+msgctxt "Label: Type of security method"
468+msgid "Passphrase"
469+msgstr ""
470+
471+#: qml/Wizard/Pages/30-passwd-type.qml:104
472+msgctxt "Label: Description of security method"
473+msgid "Numbers and letters"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/40-wifi.qml:29
477+msgid "Connect to Wi‑Fi"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/40-wifi.qml:168
481+msgid "Available networks…"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:169
485+msgid "No available networks."
486+msgstr ""
487+
488+#: qml/Wizard/Pages/50-location.qml:27
489+msgid "Location"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/50-location.qml:62
493+msgid "Let the phone detect your location:"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:69
497+msgid "Using GPS only (less accurate)"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:86
501+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502+msgstr ""
503+
504+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505+#: qml/Wizard/Pages/50-location.qml:103
506+msgid ""
507+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508+"conditions</a>."
509+msgstr ""
510+
511+#: qml/Wizard/Pages/50-location.qml:112
512+msgid "Not at all"
513+msgstr ""
514+
515+#: qml/Wizard/Pages/50-location.qml:124
516+msgid "You can change your mind later in <b>System Settings</b>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/60-reporting.qml:24
520+msgid "Improving your experience"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/60-reporting.qml:36
524+msgid ""
525+"Your phone is set up to automatically report errors to Canonical and its "
526+"partners, the makers of the operating system."
527+msgstr ""
528+
529+#: qml/Wizard/Pages/60-reporting.qml:43
530+msgid ""
531+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532+"Privacy</b>"
533+msgstr ""
534+
535+#: qml/Wizard/Pages/80-finished.qml:24
536+msgid "All done"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/80-finished.qml:39
540+msgid "Nice work!"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:46
544+msgid "Your phone is now ready to use."
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:53
548+msgid "Finish"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/here-terms.qml:27
552+msgid "Terms & Conditions"
553+msgstr ""
554+
555+#: qml/Wizard/Pages/passwd-confirm.qml:49
556+msgid "Confirm passphrase"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/passwd-confirm.qml:50
560+msgid "Confirm passcode"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:53
564+#: qml/Wizard/Pages/passwd-confirm.qml:54
565+msgid "Please try again."
566+msgstr ""
567+
568+#: qml/Wizard/Pages/passwd-set.qml:61
569+msgid "Choose your passcode"
570+msgstr ""
571+
572+#: qml/Wizard/Pages/passwd-set.qml:68
573+msgid "Passphrase must be 4 characters long"
574+msgstr ""
575+
576+#: qml/Wizard/StackButton.qml:39
577+#, qt-format
578+msgid "〈 %1"
579+msgstr ""
580+
581+#: qml/Wizard/StackButton.qml:42
582+#, qt-format
583+msgid "%1 〉"
584+msgstr ""
po/zh_CN.po+22−20
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-12-04 11:57+0000\n"
12−"Last-Translator: Mo <alimo199x@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-28 20:51+0000\n"
12+"Last-Translator: Aron Xu <Unknown>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
19+"X-Generator: Launchpad (build 17453)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -53,7 +53,7 @@ msgstr[1] ""
5353#: qml/Components/Dialogs.qml:69
5454msgctxt "Title: Lock/Log out dialog"
5555msgid "Log out"
56−msgstr ""
56+msgstr "退出"
5757
5858#: qml/Components/Dialogs.qml:70
5959msgid "Are you sure you want to log out?"
@@ -62,12 +62,12 @@ msgstr ""
6262#: qml/Components/Dialogs.qml:72
6363msgctxt "Button: Lock the system"
6464msgid "Lock"
65−msgstr ""
65+msgstr "锁定"
6666
6767#: qml/Components/Dialogs.qml:79
6868msgctxt "Button: Log out from the system"
6969msgid "Log Out"
70−msgstr ""
70+msgstr "退出"
7171
7272#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7373#: qml/Components/Dialogs.qml:173
@@ -77,38 +77,38 @@ msgstr "取消"
7777#: qml/Components/Dialogs.qml:98
7878msgctxt "Title: Reboot/Shut down dialog"
7979msgid "Shut down"
80−msgstr ""
80+msgstr "关机"
8181
8282#: qml/Components/Dialogs.qml:99
8383msgid "Are you sure you want to shut down?"
84−msgstr ""
84+msgstr "您确定要关机?"
8585
8686#: qml/Components/Dialogs.qml:101
8787msgctxt "Button: Reboot the system"
8888msgid "Reboot"
89−msgstr ""
89+msgstr "重启"
9090
9191#: qml/Components/Dialogs.qml:109
9292msgctxt "Button: Shut down the system"
9393msgid "Shut down"
94−msgstr ""
94+msgstr "关机"
9595
9696#: qml/Components/Dialogs.qml:129
9797msgctxt "Title: Reboot dialog"
9898msgid "Reboot"
99−msgstr ""
99+msgstr "重启"
100100
101101#: qml/Components/Dialogs.qml:130
102102msgid "Are you sure you want to reboot?"
103−msgstr ""
103+msgstr "您确定要重启吗?"
104104
105105#: qml/Components/Dialogs.qml:132
106106msgid "No"
107−msgstr ""
107+msgstr "否"
108108
109109#: qml/Components/Dialogs.qml:138
110110msgid "Yes"
111−msgstr ""
111+msgstr "是"
112112
113113#: qml/Components/Dialogs.qml:152
114114msgctxt "Title: Power off/Restart dialog"
@@ -120,16 +120,18 @@ msgid ""
120120"Are you sure you would like\n"
121121"to power off?"
122122msgstr ""
123+"您确定要\n"
124+"关闭电源吗?"
123125
124126#: qml/Components/Dialogs.qml:155
125127msgctxt "Button: Power off the system"
126128msgid "Power off"
127−msgstr ""
129+msgstr "关机"
128130
129131#: qml/Components/Dialogs.qml:164
130132msgctxt "Button: Restart the system"
131133msgid "Restart"
132−msgstr ""
134+msgstr "重启"
133135
134136#: qml/Components/Lockscreen.qml:245
135137msgid "Return to Call"
@@ -154,7 +156,7 @@ msgstr "查看全部"
154156#: qml/Dash/GenericScopeView.qml:594
155157msgctxt "Label: Hint for dash search line edit"
156158msgid "Search"
157−msgstr ""
159+msgstr "搜索"
158160
159161#: qml/Dash/PageHeader.qml:269
160162msgctxt "Button: Open the Ubuntu Store"
@@ -164,12 +166,12 @@ msgstr ""
164166#: qml/Dash/PageHeader.qml:276
165167msgctxt "Button: Start a search in the current dash scope"
166168msgid "Search"
167−msgstr ""
169+msgstr "搜索"
168170
169171#: qml/Dash/PageHeader.qml:286
170172msgctxt "Button: Show the current dash scope settings"
171173msgid "Settings"
172−msgstr ""
174+msgstr "设置"
173175
174176#: qml/Dash/PageHeader.qml:293
175177msgid "Remove from Favorites"
qml/Components/DragHandle.qml+18−33
@@ -43,16 +43,8 @@ import Ubuntu.Gestures 0.1
4343 }
4444
4545 */
46−EdgeDragArea {
46+DirectionalDragArea {
4747 id: dragArea
48− objectName: "dragHandle"
49−
50− // Disable gesture recognition by default when hinting is used as
51− // it conflicts with the hinting idea.
52− distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53− maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54− maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55− compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
5648
5749 property bool stretch: false
5850
@@ -68,6 +60,9 @@ EdgeDragArea {
6860 }
6961
7062 property real hintDisplacement: 0
63+
64+ immediateRecognition: hintDisplacement > 0
65+
7166 property var overrideStartValue: undefined
7267 SmoothedAnimation {
7368 id: hintingAnimation
@@ -98,7 +93,6 @@ EdgeDragArea {
9893 // Private stuff
9994 QtObject {
10095 id: d
101− property var previousStatus: DirectionalDragArea.WaitingForTouch
10296 property real startValue
10397 property real minValue: {
10498 if (direction == Direction.Horizontal) {
@@ -183,7 +177,7 @@ EdgeDragArea {
183177 }
184178
185179 onDistanceChanged: {
186− if (status === DirectionalDragArea.Recognized) {
180+ if (dragging) {
187181 // don't go the whole distance in order to smooth out the movement
188182 var step = distance * 0.3;
189183
@@ -193,31 +187,22 @@ EdgeDragArea {
193187 }
194188 }
195189
196− onStatusChanged: {
197− if (status === DirectionalDragArea.WaitingForTouch) {
198− hintingAnimation.stop();
199− if (d.previousStatus === DirectionalDragArea.Recognized) {
200− d.onFinishedRecognizedGesture();
201− } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
202− // Gesture was rejected.
203− d.rollbackDrag();
190+ onDraggingChanged: {
191+ if (dragging) {
192+ dragEvaluator.reset();
193+ if (overrideStartValue !== undefined) {
194+ d.startValue = overrideStartValue;
195+ } else {
196+ d.startValue = parent[d.targetProp];
204197 }
205− } else /* Undecided || Recognized */ {
206− if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207− dragEvaluator.reset();
208− if (overrideStartValue !== undefined) {
209− d.startValue = overrideStartValue;
210− } else {
211− d.startValue = parent[d.targetProp];
212− }
213198
214− if (hintDisplacement > 0) {
215− hintingAnimation.targetValue = d.startValue;
216− hintingAnimation.start();
217− }
199+ if (hintDisplacement > 0) {
200+ hintingAnimation.targetValue = d.startValue;
201+ hintingAnimation.start();
218202 }
203+ } else {
204+ hintingAnimation.stop();
205+ d.onFinishedRecognizedGesture();
219206 }
220−
221− d.previousStatus = status;
222207 }
223208}
qml/Components/EdgeDragArea.qmldeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−
21−/*
22− A DirectionalDragArea wrapper that provides some well-chosen defaults
23− for its gesture recognition parameters.
24−*/
25−DirectionalDragArea {
26−
27− // TODO: Re-evaluate those or even the recognition heuristics itself once
28− // we have gesture cancelling/forwarding in place.
29− //
30− // The idea here is that it's better having lax rules than false negatives.
31− // False negatives are very frustrating to the user.
32− maxDeviation: defaultMaxDeviation
33− wideningAngle: defaultWideningAngle
34− distanceThreshold: defaultDistanceThreshold
35− minSpeed: defaultMinSpeed
36− maxSilenceTime: defaultMaxSilenceTime
37− compositionTime: defaultCompositionTime
38−
39− readonly property real defaultMaxDeviation: units.gu(3)
40− readonly property real defaultWideningAngle: 50
41− readonly property real defaultDistanceThreshold: units.gu(1.5)
42− // some people were getting false negatives with it enabled.
43− readonly property real defaultMinSpeed: units.gu(0)
44− readonly property int defaultMaxSilenceTime: 200
45− readonly property int defaultCompositionTime: 60
46−}
qml/Components/EdgeDragEvaluator.qml+17−14
@@ -61,18 +61,12 @@ AxisVelocityCalculator {
6161 }
6262
6363 velocity = calculate();
64+ minVelocity = __calculateMinimumVelocityForAutoCompletion();
6465
65− if (direction == Direction.Horizontal) {
66− minVelocity = __calculateMinimumVelocityForAutoCompletion(Math.abs(deltaPos));
67− return Math.abs(velocity) >= minVelocity;
66+ if (_dragDirectionIsPositive()) {
67+ return velocity >= minVelocity;
6868 } else {
69− minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos);
70− if (Direction.isPositive(direction) && (velocity >= minVelocity)
71− || !Direction.isPositive(direction) && (velocity <= minVelocity)) {
72− return true;
73− } else {
74− return false;
75− }
69+ return velocity <= minVelocity;
7670 }
7771 }
7872
@@ -81,11 +75,20 @@ AxisVelocityCalculator {
8175 // speedThreshold in pixels per millisecond
8276 property real __speedThresholdMs: speedThreshold / 1000.0
8377
84− // Minimum velocity when a drag total distance is zero
85− property real __v0: Direction.isPositive(direction) ? __speedThresholdMs : - __speedThresholdMs
78+ function _dragDirectionIsPositive() {
79+ if (direction === Direction.Horizontal) {
80+ return (trackedPosition - __startPosition) > 0;
81+ } else {
82+ return Direction.isPositive(direction);
83+ }
84+ }
85+
86+ function __calculateMinimumVelocityForAutoCompletion() {
87+ // Minimum velocity when a drag total distance is zero
88+ var v0 = _dragDirectionIsPositive() ? __speedThresholdMs : - __speedThresholdMs;
89+ var deltaPos = trackedPosition - __startPosition;
8690
87− function __calculateMinimumVelocityForAutoCompletion(distance) {
88− return __v0 - ((__speedThresholdMs / dragThreshold) * distance);
91+ return v0 - ((__speedThresholdMs / dragThreshold) * deltaPos);
8992 }
9093
9194 function reset() {
qml/Dash/Dash.qml+10−12
@@ -153,7 +153,7 @@ Showable {
153153 // (as expected) but would also cause the dash content flickable to move a bit, because
154154 // that flickable was getting the touch events while overviewDragHandle was still undecided
155155 // about whether that touch was indeed performing a directional drag gesture.
156− forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
156+ forceNonInteractive: overviewDragHandle.dragging
157157
158158 enabled: bottomEdgeController.progress == 0
159159 }
@@ -187,6 +187,11 @@ Showable {
187187 onRequestFavoriteMoveTo: {
188188 scopes.moveFavoriteTo(scopeId, index);
189189 }
190+ onRequestRestore: {
191+ bottomEdgeController.enableAnimation = true;
192+ bottomEdgeController.progress = 0;
193+ dash.setCurrentScope(scopeId, false, false);
194+ }
190195
191196 Binding {
192197 target: scopesList.scope
@@ -330,7 +335,7 @@ Showable {
330335 }
331336 }
332337
333− EdgeDragArea {
338+ DirectionalDragArea {
334339 id: overviewDragHandle
335340 objectName: "overviewDragHandle"
336341 z: 1
@@ -346,21 +351,14 @@ Showable {
346351 height: units.gu(2)
347352
348353 onSceneDistanceChanged: {
349− if (status == DirectionalDragArea.Recognized) {
354+ if (dragging) {
350355 bottomEdgeController.enableAnimation = false;
351356 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
352357 }
353358 }
354359
355− property int previousStatus: -1
356− property int currentStatus: DirectionalDragArea.WaitingForTouch
357−
358− onStatusChanged: {
359− previousStatus = currentStatus;
360− currentStatus = status;
361−
362− if (status == DirectionalDragArea.WaitingForTouch &&
363− previousStatus == DirectionalDragArea.Recognized) {
360+ onDraggingChanged: {
361+ if (!dragging) {
364362 bottomEdgeController.enableAnimation = true;
365363 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
366364 }
qml/Dash/GenericScopeView.qml+6−6
@@ -67,8 +67,8 @@ FocusScope {
6767 subPageLoader.closeSubPage()
6868 }
6969
70− function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
71− if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope") {
70+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
71+ if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope" || (scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
7272 // TODO Technically it is possible that calling activate() will make the scope emit
7373 // previewRequested so that we show a preview but there's no scope that does that yet
7474 // so it's not implemented
@@ -80,8 +80,8 @@ FocusScope {
8080 }
8181 }
8282
83− function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount) {
84− if (itemModel.uri.indexOf("scope://") !== 0) {
83+ function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
84+ if (itemModel.uri.indexOf("scope://") !== 0 && !(scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
8585 if (scope.preview(result)) {
8686 openPreview(index, resultsModel, limitedCategoryItemCount);
8787 }
@@ -346,11 +346,11 @@ FocusScope {
346346 Connections {
347347 target: rendererLoader.item
348348 onClicked: {
349− scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
349+ scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount(), baseItem.category);
350350 }
351351
352352 onPressAndHold: {
353− scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount());
353+ scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
354354 }
355355
356356 function categoryItemCount() {
qml/Dash/ScopesList.qml+2
@@ -31,6 +31,7 @@ Item {
3131 signal storeClicked()
3232 signal requestFavorite(string scopeId, bool favorite)
3333 signal requestFavoriteMoveTo(string scopeId, int index)
34+ signal requestRestore(string scopeId)
3435
3536 state: "browse"
3637
@@ -124,6 +125,7 @@ Item {
124125 onRequestEditMode: root.state = "edit";
125126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126127 onRequestActivate: root.scope.activate(result);
128+ onRequestRestore: root.requestRestore(scopeId);
127129 }
128130 }
129131 }
qml/Dash/ScopesListCategory.qml+5−1
@@ -35,6 +35,7 @@ Item {
3535 signal requestEditMode()
3636 signal requestScopeMoveTo(string scopeId, int index)
3737 signal requestActivate(var result)
38+ signal requestRestore(string scopeId)
3839
3940 implicitHeight: visible ? childrenRect.height : 0
4041
@@ -93,7 +94,10 @@ Item {
9394
9495 onClicked: {
9596 if (!editMode) {
96− root.requestActivate(result);
97+ if (root.isFavoritesFeed)
98+ root.requestRestore(model.scopeId);
99+ else
100+ root.requestActivate(result);
97101 }
98102 }
99103 onPressAndHold: {
qml/Greeter/CoverPage.qml+3−2
@@ -129,13 +129,14 @@ Showable {
129129
130130 DragHandle {
131131 id: dragHandle
132+ objectName: "coverPageDragHandle"
132133 anchors.fill: parent
133134 anchors.leftMargin: root.dragHandleLeftMargin
134135 enabled: root.draggable
135136 direction: Direction.Horizontal
136137
137− onDraggingChanged: {
138− if (dragging) {
138+ onPressedChanged: {
139+ if (pressed) {
139140 root.tease();
140141 showLabelAnimation.start();
141142 }
qml/Launcher/Launcher.qml+7−15
@@ -210,7 +210,7 @@ Item {
210210 bottom: parent.bottom
211211 }
212212 x: -width
213− visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.pressed
214214 model: LauncherModel
215215
216216 property bool animate: true
@@ -272,7 +272,7 @@ Item {
272272 }
273273 }
274274
275− EdgeDragArea {
275+ DirectionalDragArea {
276276 id: dragArea
277277 objectName: "launcherDragArea"
278278
@@ -283,20 +283,11 @@ Item {
283283 width: root.dragAreaWidth
284284 height: root.height
285285
286− onTouchXChanged: {
287− if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
286+ onDistanceChanged: {
287+ if (!dragging || launcher.state == "visible")
288288 return;
289289
290− // When the gesture finally gets recognized, the finger will likely be
291− // reasonably far from the edge. If we made the panel immediately
292− // follow the finger position it would be visually unpleasant as it
293− // would appear right next to the user's finger out of nowhere.
294− // Instead, we make the panel go towards the user's finger in several
295− // steps. ie., in an animated way.
296− var targetPanelX = Math.min(0, touchX - panel.width) - root.x
297− var delta = targetPanelX - panel.x
298− // the trick is not to go all the way (1.0) as it would cause a sudden jump
299− panel.x += 0.4 * delta
290+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
300291 }
301292
302293 onDraggingChanged: {
@@ -306,7 +297,8 @@ Item {
306297 if (distance > minimizeDistance) {
307298 root.dash();
308299 }
309− } else {
300+ } else if (root.state === "") {
301+ // didn't drag far enough. rollback
310302 root.switchToNextState("")
311303 }
312304 }
qml/Panel/IndicatorsMenu.qml+12−2
@@ -186,9 +186,18 @@ Showable {
186186 enabled: !root.shown && root.available
187187 autoCompleteDragThreshold: maxTotalDragDistance / 2
188188 stretch: true
189− distanceThreshold: enableHint ? 0 : minimizedPanelHeight
190189
191− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
190+ onPressedChanged: {
191+ if (pressed) {
192+ touchPressTime = new Date().getTime();
193+ } else {
194+ var touchReleaseTime = new Date().getTime();
195+ if (touchReleaseTime - touchPressTime <= 300) {
196+ root.showTapped(Qt.point(touchSceneX, touchSceneY));
197+ }
198+ }
199+ }
200+ property var touchPressTime
192201
193202 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
194203 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -204,6 +213,7 @@ Showable {
204213
205214 DragHandle {
206215 id: __hideDragHandle
216+ objectName: "hideDragHandle"
207217 anchors.fill: handle
208218 direction: Direction.Upwards
209219 enabled: root.shown && root.available
qml/Shell.qml−1
@@ -58,7 +58,6 @@ Item {
5858 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
5959 property url background: asImageTester.status == Image.Ready ? asImageTester.source
6060 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61− readonly property real panelHeight: panel.panelHeight
6261
6362 property bool sideStageEnabled: shell.width >= units.gu(100)
6463 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
qml/Stages/PhoneStage.qml+31−41
@@ -146,14 +146,13 @@ Rectangle {
146146 id: spreadView
147147 objectName: "spreadView"
148148 anchors.fill: parent
149− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
150− && draggedDelegateCount === 0
149+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
151150 contentWidth: spreadRow.width - shift
152151 contentX: -shift
153152
154153 // This indicates when the spreadView is active. That means, all the animations
155154 // are activated and tiles need to line up for the spread.
156− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
155+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
157156
158157 // The flickable needs to fill the screen in order to get touch events all over.
159158 // However, we don't want to the user to be able to scroll back all the way. For
@@ -450,7 +449,7 @@ Rectangle {
450449 }
451450 }
452451
453− EdgeDragArea {
452+ DirectionalDragArea {
454453 id: spreadDragArea
455454 objectName: "spreadDragArea"
456455 direction: Direction.Leftwards
@@ -462,59 +461,50 @@ Rectangle {
462461 property var gesturePoints: new Array()
463462
464463 onTouchXChanged: {
465− if (!dragging) {
466− // Initial touch. Let's reset the spreadView to the starting position.
467− spreadView.phase = 0;
468− spreadView.contentX = -spreadView.shift;
469− }
470− if (dragging && status == DirectionalDragArea.Recognized) {
464+ if (dragging) {
471465 // Gesture recognized. Let's move the spreadView with the finger
472466 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
473467 dragX = -dragX + spreadDragArea.width - spreadView.shift;
474468 // Don't allow dragging further than the animation crossing with phase2's animation
475469 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
470+
476471 spreadView.contentX = Math.min(dragX, maxMovement);
472+ } else {
473+ // Initial touch. Let's reset the spreadView to the starting position.
474+ spreadView.phase = 0;
475+ spreadView.contentX = -spreadView.shift;
477476 }
478− gesturePoints.push(touchX);
479− }
480−
481− property int previousStatus: -1
482− property int currentStatus: DirectionalDragArea.WaitingForTouch
483477
484− onStatusChanged: {
485− previousStatus = currentStatus;
486− currentStatus = status;
478+ gesturePoints.push(touchX);
487479 }
488480
489481 onDraggingChanged: {
490482 if (dragging) {
491483 // A potential edge-drag gesture has started. Start recording it
492484 gesturePoints = [];
493− return;
494− }
485+ } else {
486+ // Ok. The user released. Find out if it was a one-way movement.
487+ var oneWayFlick = true;
488+ var smallestX = spreadDragArea.width;
489+ for (var i = 0; i < gesturePoints.length; i++) {
490+ if (gesturePoints[i] >= smallestX) {
491+ oneWayFlick = false;
492+ break;
493+ }
494+ smallestX = gesturePoints[i];
495+ }
496+ gesturePoints = [];
495497
496− // Ok. The user released. Find out if it was a one-way movement.
497− var oneWayFlick = true;
498− var smallestX = spreadDragArea.width;
499− for (var i = 0; i < gesturePoints.length; i++) {
500− if (gesturePoints[i] >= smallestX) {
501− oneWayFlick = false;
502− break;
498+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
499+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
500+ // If it was a short one-way movement, do the Alt+Tab switch
501+ // no matter if we didn't cross positionMarker1 yet.
502+ spreadView.snapTo(1);
503+ } else if (!dragging) {
504+ // otherwise snap to the closest snap position we can find
505+ // (might be back to start, to app 1 or to spread)
506+ spreadView.snap();
503507 }
504− smallestX = gesturePoints[i];
505− }
506− gesturePoints = [];
507−
508− if (previousStatus == DirectionalDragArea.Recognized &&
509− oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
510− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
511− // If it was a short one-way movement, do the Alt+Tab switch
512− // no matter if we didn't cross positionMarker1 yet.
513− spreadView.snapTo(1);
514− } else if (!dragging) {
515− // otherwise snap to the closest snap position we can find
516− // (might be back to start, to app 1 or to spread)
517− spreadView.snap();
518508 }
519509 }
520510 }
qml/Stages/TabletStage.qml+18−20
@@ -170,8 +170,7 @@ Rectangle {
170170 Flickable {
171171 id: spreadView
172172 anchors.fill: parent
173− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
174− && draggedDelegateCount === 0
173+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
175174 contentWidth: spreadRow.width - shift
176175 contentX: -shift
177176
@@ -598,7 +597,7 @@ Rectangle {
598597 }
599598 }
600599
601− EdgeDragArea {
600+ DirectionalDragArea {
602601 id: spreadDragArea
603602 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
604603 width: root.dragAreaWidth
@@ -624,26 +623,25 @@ Rectangle {
624623 if (dragging) {
625624 // Gesture recognized. Start recording this gesture
626625 gesturePoints = [];
627− return;
628− }
629−
630− // Ok. The user released. Find out if it was a one-way movement.
631− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
632− gesturePoints = [];
626+ } else {
627+ // Ok. The user released. Find out if it was a one-way movement.
628+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
629+ gesturePoints = [];
633630
634− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
635− // If it was a short one-way movement, do the Alt+Tab switch
636− // no matter if we didn't cross positionMarker1 yet.
637− spreadView.snapTo(spreadView.nextInStack);
638− } else if (!dragging) {
639− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
640− spreadView.snap();
641− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
631+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
632+ // If it was a short one-way movement, do the Alt+Tab switch
633+ // no matter if we didn't cross positionMarker1 yet.
642634 spreadView.snapTo(spreadView.nextInStack);
643635 } else {
644− // otherwise snap to the closest snap position we can find
645− // (might be back to start, to app 1 or to spread)
646− spreadView.snap();
636+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
637+ spreadView.snap();
638+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
639+ spreadView.snapTo(spreadView.nextInStack);
640+ } else {
641+ // otherwise snap to the closest snap position we can find
642+ // (might be back to start, to app 1 or to spread)
643+ spreadView.snap();
644+ }
647645 }
648646 }
649647 }
qml/Tutorial/TutorialBottom.qml+1−1
@@ -73,7 +73,7 @@ TutorialPage {
7373 ]
7474 }
7575
76− EdgeDragArea {
76+ DirectionalDragArea {
7777 id: dragArea
7878 direction: Direction.Upwards
7979 anchors {
runtests.sh+3−3
@@ -42,7 +42,9 @@ sleep 1
4242cd -
4343
4444make -k xvfballtests
45−makeresult=$?
45+if [ $? -ne 0 ]; then
46+ echo '<?xml version="1.0" encoding="UTF-8" ?><testsuite errors="0" failures="1" tests="1" name="makeExitStatusTest"><properties/><testcase result="fail" name="makeExitStatus"><failure message="Make test did not suceed" result="fail"/></testcase><system-err/></testsuite>' > testMakeExitStatus.xml
47+fi
4648
4749cd -
4850
@@ -135,5 +137,3 @@ echo " </classes>" >> $coveragefile
135137echo " </package>" >> $coveragefile
136138echo " </packages>" >> $coveragefile
137139echo "</coverage>" >> $coveragefile
138−
139−exit $makeresult
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1
@@ -47,7 +47,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4747 end_y = start_y
4848
4949 logger.info("Swiping screen from the right edge")
50− self.touch.drag(start_x, start_y, end_x, end_y)
50+ self.main_window.pointing_device.drag(start_x, start_y, end_x, end_y)
5151
5252 def launch_fake_app(self):
5353 _, desktop_file_path = self.create_test_application()
tests/autopilot/unity8/fixture_setup.py+69−3
@@ -18,20 +18,86 @@
1818#
1919
2020import fixtures
21−import subprocess
2221import logging
22+import os
23+import subprocess
2324
25+import ubuntuuitoolkit
2426from autopilot import introspection
2527
2628from unity8 import (
2729 get_binary_path,
2830 process_helpers
2931)
30−from unity8.shell import emulators
32+
3133
3234logger = logging.getLogger(__name__)
3335
3436
37+class RestartUnityWithTestability(fixtures.Fixture):
38+
39+ """Fixture to launch Unity8 with testability.
40+
41+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
42+
43+ """
44+
45+ unity_proxy = None
46+
47+ def __init__(self, binary_path, variables):
48+ """Initialize the fixture instance.
49+
50+ :param str binary_path: The path to the Dash app binary.
51+ :param cli_arguments: The arguments to pass when launching the
52+ :param variables: The variables to use when launching the app.
53+ :type variables: A dictionary.
54+
55+ """
56+ super().__init__()
57+ self.binary_path = binary_path
58+ self.variables = variables
59+
60+ def setUp(self):
61+ """Restart unity with testability when the fixture is used."""
62+ super().setUp()
63+ self.addCleanup(self.stop_unity)
64+ self.restart_unity()
65+
66+ def restart_unity(self):
67+ self.restart_unity_with_testability()
68+
69+ def restart_unity_with_testability(self):
70+ self._unlink_mir_socket()
71+
72+ binary_arg = 'BINARY={}'.format(self.binary_path)
73+ variable_args = [
74+ '{}={}'.format(key, value) for key, value in self.variables.items()
75+ ]
76+ all_args = [binary_arg] + variable_args
77+
78+ self.unity_proxy = process_helpers.restart_unity_with_testability(
79+ *all_args)
80+
81+ def _unlink_mir_socket(self):
82+ # FIXME: we shouldn't be doing this
83+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
84+ # /tmp/mir_socket as last resort
85+ try:
86+ os.unlink(
87+ os.getenv('MIR_SOCKET',
88+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
89+ "mir_socket")))
90+ except OSError:
91+ pass
92+ try:
93+ os.unlink("/tmp/mir_socket")
94+ except OSError:
95+ pass
96+
97+ def stop_unity(self):
98+ process_helpers.stop_job('unity8')
99+
100+
35101class LaunchDashApp(fixtures.Fixture):
36102
37103 """Fixture to launch the Dash app."""
@@ -65,7 +131,7 @@ class LaunchDashApp(fixtures.Fixture):
65131 pid = process_helpers.start_job('unity8-dash', *all_args)
66132 return introspection.get_proxy_object_for_existing_process(
67133 pid=pid,
68− emulator_base=emulators.UnityEmulatorBase,
134+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
69135 )
70136
71137 def stop_application(self):
tests/autopilot/unity8/indicators/__init__.py+2−4
@@ -18,10 +18,8 @@
1818import ubuntuuitoolkit
1919from autopilot import introspection
2020
21−from unity8.shell import emulators
2221
23−
24−class IndicatorPage(emulators.UnityEmulatorBase):
22+class IndicatorPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2523
2624 """Autopilot helper for the IndicatorPage component."""
2725
@@ -166,7 +164,7 @@ class TestIndicatorPage(IndicatorPage):
166164 return self.select_single(objectName='indicator.action.slider')
167165
168166
169−class Slider(emulators.UnityEmulatorBase):
167+class Slider(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
170168
171169 """Autopilot helper for the Slider component."""
172170
tests/autopilot/unity8/indicators/tests/__init__.py−2
@@ -40,5 +40,3 @@ class DeviceIndicatorTestCase(IndicatorTestCase):
4040 if platform.model() == 'Desktop':
4141 self.skipTest('Test cannot be run on the desktop.')
4242 super().setUp()
43− self.unity_proxy = self.launch_unity()
44− process_helpers.unlock_unity(self.unity_proxy)
tests/autopilot/unity8/process_helpers.py+2−1
@@ -21,6 +21,7 @@ import logging
2121import subprocess
2222import dbus
2323
24+import ubuntuuitoolkit
2425from autopilot.exceptions import ProcessSearchError
2526from autopilot.introspection import get_proxy_object_for_existing_process
2627
@@ -271,5 +272,5 @@ def _get_unity_pid():
271272def _get_unity_proxy_object(pid):
272273 return get_proxy_object_for_existing_process(
273274 pid=pid,
274− emulator_base=emulators.UnityEmulatorBase,
275+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
275276 )
tests/autopilot/unity8/shell/__init__.py−24
@@ -19,7 +19,6 @@
1919
2020"""unity shell autopilot tests and emulators - sub level package."""
2121
22−from time import sleep
2322from functools import wraps
2423from gi.repository import Notify
2524
@@ -38,29 +37,6 @@ def disable_qml_mocking(fn):
3837 return wrapper
3938
4039
41−class DragMixin():
42− def _drag(self, x1, y1, x2, y2):
43− # XXX This ugly code is here just temporarily, waiting for drag
44− # improvements to land on autopilot so we don't have to access device
45− # private internal attributes. --elopio - 2014-02-12
46− cur_x = x1
47− cur_y = y1
48− dx = 1.0 * (x2 - x1) / 100
49− dy = 1.0 * (y2 - y1) / 100
50− for i in range(0, 100):
51− try:
52− self.touch._finger_move(int(cur_x), int(cur_y))
53− except AttributeError:
54− self.touch._device.finger_move(int(cur_x), int(cur_y))
55− sleep(0.002)
56− cur_x += dx
57− cur_y += dy
58− try:
59− self.touch._finger_move(int(x2), int(y2))
60− except AttributeError:
61− self.touch._device.finger_move(int(x2), int(y2))
62−
63−
6440def create_ephemeral_notification(
6541 summary='',
6642 body='',
tests/autopilot/unity8/shell/emulators/__init__.py−6
@@ -17,12 +17,6 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−import ubuntuuitoolkit
21−
22−
23−class UnityEmulatorBase(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
24− """A base class for all unity emulators."""
25−
2620
2721class UnityEmulatorException(Exception):
2822 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+6−6
@@ -18,13 +18,13 @@
1818#
1919
2020import logging
21−import ubuntuuitoolkit
22−
23−from unity8.shell import emulators
2421
22+import ubuntuuitoolkit
2523from autopilot import logging as autopilot_logging
2624from autopilot.introspection import dbus
2725
26+from unity8.shell import emulators
27+
2828
2929logger = logging.getLogger(__name__)
3030
@@ -40,7 +40,7 @@ class DashApp():
4040 self.dash = self.main_view.select_single(Dash)
4141
4242
43−class Dash(emulators.UnityEmulatorBase):
43+class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4444 """An emulator that understands the Dash."""
4545
4646 def __init__(self, *args):
@@ -188,7 +188,7 @@ class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
188188 pass
189189
190190
191−class GenericScopeView(emulators.UnityEmulatorBase):
191+class GenericScopeView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
192192 """Autopilot emulator for generic scopes."""
193193
194194 @autopilot_logging.log_action(logger.info)
@@ -255,5 +255,5 @@ class GenericScopeView(emulators.UnityEmulatorBase):
255255 return result
256256
257257
258−class Preview(emulators.UnityEmulatorBase):
258+class Preview(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
259259 """Autopilot custom proxy object for generic previews."""
tests/autopilot/unity8/shell/emulators/greeter.py+2−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -18,12 +18,10 @@
1818#
1919
2020import ubuntuuitoolkit
21−
2221from autopilot.utilities import sleep
23−from unity8.shell.emulators import UnityEmulatorBase
2422
2523
26−class Greeter(UnityEmulatorBase):
24+class Greeter(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2725 """An emulator that understands the greeter screen."""
2826
2927 def wait_swiped_away(self):
tests/autopilot/unity8/shell/emulators/launcher.py+3−2
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
2020import logging
2121
2222import autopilot.logging
23+import ubuntuuitoolkit
2324
2425from unity8.shell import emulators
2526
@@ -27,7 +28,7 @@ from unity8.shell import emulators
2728logger = logging.getLogger(__name__)
2829
2930
30−class Launcher(emulators.UnityEmulatorBase):
31+class Launcher(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3132
3233 """An emulator that understands the Launcher."""
3334
tests/autopilot/unity8/shell/emulators/main_window.py+2−2
@@ -19,17 +19,17 @@
1919
2020import logging
2121
22+import ubuntuuitoolkit
2223from autopilot import logging as autopilot_logging
2324from autopilot import input
2425
25−from unity8.shell import emulators
2626from unity8.shell.emulators.greeter import Greeter
2727from unity8.shell.emulators.launcher import Launcher
2828
2929logger = logging.getLogger(__name__)
3030
3131
32−class QQuickView(emulators.UnityEmulatorBase):
32+class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3333 """An emulator class that makes it easy to interact with the shell"""
3434
3535 def get_greeter(self):
tests/autopilot/unity8/shell/tests/__init__.py+32−111
@@ -24,17 +24,17 @@ try:
2424except ImportError:
2525 Gio = None
2626
27+import logging
28+import os
29+
2730from autopilot import introspection
2831from autopilot.platform import model
2932from autopilot.testcase import AutopilotTestCase
3033from autopilot.matchers import Eventually
31−from autopilot.input import Touch
3234from autopilot.display import Display
33−import logging
34−import os.path
35−import subprocess
36−import sys
3735from testtools.matchers import Equals
36+
37+import ubuntuuitoolkit
3838from ubuntuuitoolkit import (
3939 fixture_setup as toolkit_fixtures,
4040 ubuntu_scenarios
@@ -51,7 +51,6 @@ from unity8 import (
5151 fixture_setup,
5252 process_helpers
5353)
54−from unity8.shell import emulators
5554from unity8.shell.emulators import (
5655 dash as dash_helpers,
5756 main_window as main_window_emulator,
@@ -93,36 +92,25 @@ class UnityTestCase(AutopilotTestCase):
9392 @classmethod
9493 def setUpClass(cls):
9594 try:
96− output = subprocess.check_output(
97− ["/sbin/initctl", "status", "unity8"],
98− stderr=subprocess.STDOUT,
99− universal_newlines=True,
100− )
101− except subprocess.CalledProcessError as e:
102− sys.stderr.write(
103− "Error: `initctl status unity8` failed, most probably the "
104− "unity8 session could not be found:\n\n"
105− "{0}\n"
106− "Please install unity8 or copy data/unity8.conf to "
107− "{1}\n".format(
108− e.output,
109− os.path.join(os.getenv("XDG_CONFIG_HOME",
110− os.path.join(os.getenv("HOME"),
111− ".config")
112− ),
113− "upstart")
114− )
115− )
116− sys.exit(1)
117−
118− if "start/" in output:
119− sys.stderr.write(
120− "Error: Unity is currently running, these tests require it to "
121− "be 'stopped'.\n"
122− "Please run this command before running these tests: \n"
123− "initctl stop unity8\n"
95+ is_unity_running = process_helpers.is_job_running('unity8')
96+ except process_helpers.JobError as e:
97+ xdg_config_home = os.getenv(
98+ 'XDG_CONFIG_HOME', os.path.join(os.getenv('HOME'), '.config'))
99+ upstart_config_path = os.path.join(xdg_config_home, 'upstart')
100+ logger.error(
101+ '`initctl status unity8` failed, most probably the '
102+ 'unity8 session could not be found:\n\n'
103+ '{0}\n'
104+ 'Please install unity8 or copy data/unity8.conf to '
105+ '{1}\n'.format(e.output, upstart_config_path)
124106 )
125− sys.exit(2)
107+ raise e
108+ else:
109+ assert not is_unity_running, (
110+ 'Unity is currently running, these tests require it to be '
111+ 'stopped.\n'
112+ 'Please run this command before running these tests: \n'
113+ 'initctl stop unity8\n')
126114
127115 def setUp(self):
128116 super().setUp()
@@ -134,13 +122,6 @@ class UnityTestCase(AutopilotTestCase):
134122 self._data_dirs_mock_enabled = True
135123 self._environment = {}
136124
137− # FIXME: This is a work around re: lp:1238417
138− if model() != "Desktop":
139− from autopilot.input import _uinput
140− _uinput._touch_device = _uinput.create_touch_device()
141− self.addCleanup(_uinput._touch_device.close)
142−
143− self.touch = Touch.create()
144125 self._setup_display_details()
145126
146127 def _setup_display_details(self):
@@ -196,40 +177,6 @@ class UnityTestCase(AutopilotTestCase):
196177 divisor = divisor * 2
197178 return divisor
198179
199− def _patch_environment(self, key, value):
200− """Wrapper for patching env for upstart environment."""
201− try:
202− current_value = subprocess.check_output(
203− ["/sbin/initctl", "get-env", "--global", key],
204− stderr=subprocess.STDOUT,
205− universal_newlines=True,
206− ).rstrip()
207− except subprocess.CalledProcessError:
208− current_value = None
209−
210− subprocess.call([
211− "/sbin/initctl",
212− "set-env",
213− "--global",
214− "%s=%s" % (key, value)
215− ], stderr=subprocess.STDOUT)
216− self.addCleanup(self._upstart_reset_env, key, current_value)
217−
218− def _upstart_reset_env(self, key, value):
219− logger.info("Resetting upstart env %s to %s", key, value)
220− if value is None:
221− subprocess.call(
222− ["/sbin/initctl", "unset-env", key],
223− stderr=subprocess.STDOUT,
224− )
225− else:
226− subprocess.call([
227− "/sbin/initctl",
228− "set-env",
229− "--global",
230− "%s=%s" % (key, value)
231− ], stderr=subprocess.STDOUT)
232−
233180 def launch_unity(self, mode="full-greeter", *args):
234181 """
235182 Launch the unity shell, return a proxy object for it.
@@ -257,21 +204,6 @@ class UnityTestCase(AutopilotTestCase):
257204 if self._data_dirs_mock_enabled:
258205 self._patch_data_dirs()
259206
260− # FIXME: we shouldn't be doing this
261− # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
262− # /tmp/mir_socket as last resort
263− try:
264− os.unlink(
265− os.getenv('MIR_SOCKET',
266− os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
267− "mir_socket")))
268− except OSError:
269− pass
270− try:
271− os.unlink("/tmp/mir_socket")
272− except OSError:
273− pass
274−
275207 unity8_cli_args_list = ["--mode={}".format(mode)]
276208 if len(args) != 0:
277209 unity8_cli_args_list += args
@@ -297,26 +229,15 @@ class UnityTestCase(AutopilotTestCase):
297229
298230 def _launch_unity_with_upstart(self, binary_path, args):
299231 logger.info("Starting unity")
300− self._patch_environment("QT_LOAD_TESTABILITY", 1)
301−
302− binary_arg = "BINARY=%s" % binary_path
303− extra_args = "ARGS=%s" % " ".join(args)
304− env_args = ["%s=%s" % (k, v) for k, v in self._environment.items()]
305− all_args = [binary_arg, extra_args] + env_args
232+ self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
233+ global_=True, QT_LOAD_TESTABILITY=1))
306234
307− self.addCleanup(self._cleanup_launching_upstart_unity)
308−
309− return process_helpers.restart_unity_with_testability(*all_args)
310−
311− def _cleanup_launching_upstart_unity(self):
312− logger.info("Stopping unity")
313− try:
314− subprocess.check_output(
315− ["/sbin/initctl", "stop", "unity8"],
316− stderr=subprocess.STDOUT
317− )
318− except subprocess.CalledProcessError:
319− logger.warning("Appears unity was already stopped!")
235+ variables = self._environment
236+ variables['ARGS'] = " ".join(args)
237+ launch_unity_fixture = fixture_setup.RestartUnityWithTestability(
238+ binary_path, variables)
239+ self.useFixture(launch_unity_fixture)
240+ return launch_unity_fixture.unity_proxy
320241
321242 def _patch_data_dirs(self):
322243 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
@@ -369,7 +290,7 @@ class UnityTestCase(AutopilotTestCase):
369290 pid = process_helpers.get_job_pid('unity8-dash')
370291 dash_proxy = introspection.get_proxy_object_for_existing_process(
371292 pid=pid,
372− emulator_base=emulators.UnityEmulatorBase,
293+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
373294 )
374295 dash_app = dash_helpers.DashApp(dash_proxy)
375296 return dash_app.dash
tests/autopilot/unity8/shell/tests/test_upstart.py+5−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2013, 2014 Canonical
4+# Copyright (C) 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -26,13 +26,13 @@ import subprocess
2626import time
2727
2828import fixtures
29−from testtools.matchers import Equals, MismatchError
29+import ubuntuuitoolkit
3030from autopilot.matchers import Eventually
3131from autopilot.introspection import get_proxy_object_for_existing_process
32+from testtools.matchers import Equals, MismatchError
3233from ubuntuuitoolkit import ubuntu_scenarios
3334
3435from unity8 import get_binary_path
35−from unity8.shell.emulators import UnityEmulatorBase
3636from unity8.shell.tests import UnityTestCase
3737
3838import logging
@@ -90,10 +90,11 @@ class UpstartIntegrationTests(UnityTestCase):
9090 self.addCleanup(ensure_stopped)
9191
9292 def _set_proxy(self):
93+ proxy_base = ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
9394 super()._set_proxy(
9495 get_proxy_object_for_existing_process(
9596 pid=self.process.pid,
96− emulator_base=UnityEmulatorBase,))
97+ emulator_base=proxy_base))
9798
9899 def test_no_sigstop(self):
99100 self.useFixture(
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+68−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -66,6 +66,7 @@ private Q_SLOTS:
6666 void candidatesAndWatchers_2();
6767 void rejectingTouchfterItsEnd();
6868 void removeOldUndecidedCandidates();
69+ void interimOwnerWontGetUnownedTouchEvents();
6970};
7071
7172void tst_TouchRegistry::requestWithNoCandidates()
@@ -742,7 +743,7 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
742743
743744 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744745 // making TouchRegistry consider that undecidedCantidate defaulted.
745− fakeTimerFactory->makeRunningTimersTimeout();
746+ fakeTimerFactory->updateTime(10000);
746747
747748 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748749 QVERIFY(undecidedCandidate.lostTouches.contains(0));
@@ -752,6 +753,71 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
752753 delete touchRegistry;
753754}
754755
756+/*
757+ An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
758+ is assumed to be the interim owner of the touch point, thus there's no point in sending
759+ UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
760+ he didn't ignore the first QTouchEvent with that touch point.
761+ */
762+void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
763+{
764+ FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
765+ TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
766+
767+ DummyCandidate undecidedCandidate;
768+ undecidedCandidate.setObjectName("undecided");
769+
770+ DummyCandidate interimOwner;
771+ interimOwner.setObjectName("interimOwner");
772+
773+ {
774+ QList<QTouchEvent::TouchPoint> touchPoints;
775+ touchPoints.append(QTouchEvent::TouchPoint(0));
776+ touchPoints[0].setState(Qt::TouchPointPressed);
777+ QTouchEvent touchEvent(QEvent::TouchBegin,
778+ 0 /* device */,
779+ Qt::NoModifier,
780+ Qt::TouchPointPressed,
781+ touchPoints);
782+ touchRegistry->update(&touchEvent);
783+ }
784+
785+ touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
786+ touchRegistry->requestTouchOwnership(0, &interimOwner);
787+
788+ {
789+ QList<QTouchEvent::TouchPoint> touchPoints;
790+ touchPoints.append(QTouchEvent::TouchPoint(0));
791+ touchPoints[0].setState(Qt::TouchPointMoved);
792+ QTouchEvent touchEvent(QEvent::TouchUpdate,
793+ 0 /* device */,
794+ Qt::NoModifier,
795+ Qt::TouchPointMoved,
796+ touchPoints);
797+ touchRegistry->update(&touchEvent);
798+ }
799+
800+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
801+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
802+
803+ {
804+ QList<QTouchEvent::TouchPoint> touchPoints;
805+ touchPoints.append(QTouchEvent::TouchPoint(0));
806+ touchPoints[0].setState(Qt::TouchPointMoved);
807+ QTouchEvent touchEvent(QEvent::TouchUpdate,
808+ 0 /* device */,
809+ Qt::NoModifier,
810+ Qt::TouchPointMoved,
811+ touchPoints);
812+ touchRegistry->update(&touchEvent);
813+ }
814+
815+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
816+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
817+
818+ delete touchRegistry;
819+}
820+
755821////////////// TouchMemento //////////
756822
757823TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
tests/plugins/Dash/cardcreator/1.res+21−8
@@ -28,12 +28,27 @@ Item {
2828 active: cardData && cardData["art"] || false;
2929 asynchronous: root.asynchronous;
3030 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
31+ sourceComponent: Item {
32+ id: artShape;
33+ objectName: "artShape";
34+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35+ visible: image.status == Image.Ready;
36+ readonly property alias image: artImage.image;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ image: artShapeSource;
47+ anchors.fill: parent;
48+ visible: doShapeItem;
49+ radius: "medium";
50+ }
51+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3752 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
3853 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3954 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
@@ -51,11 +66,9 @@ Item {
5166 objectName: "artImage";
5267 source: cardData && cardData["art"] || "";
5368 asynchronous: root.asynchronous;
54− visible: false;
5569 width: root.width;
5670 height: width / artShape.aspect;
5771 }
58− image: artImage.image;
5972 }
6073 }
6174 }
tests/plugins/Dash/cardcreator/3.res+31−18
@@ -28,34 +28,47 @@ Item {
2828 active: cardData && cardData["art"] || false;
2929 asynchronous: root.asynchronous;
3030 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else {
31+ sourceComponent: Item {
32+ id: artShape;
33+ objectName: "artShape";
34+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35+ visible: image.status == Image.Ready;
36+ readonly property alias image: artImage.image;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ image: artShapeSource;
47+ anchors.fill: parent;
48+ visible: doShapeItem;
49+ radius: "medium";
50+ }
51+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
52+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
53+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
54+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
55+ function updateWidthHeightBindings() {
56+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
57+ width = root.fixedArtShapeSize.width;
58+ height = root.fixedArtShapeSize.height;
59+ } else {
4560 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
4661 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
4762 }
48− }
63+ }
4964 CroppedImageMinimumSourceSize {
5065 id: artImage;
5166 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
67+ source: cardData && cardData["art"] || "";
5368 asynchronous: root.asynchronous;
54− visible: false;
5569 width: root.width;
5670 height: width / artShape.aspect;
5771 }
58− image: artImage.image;
5972 }
6073 }
6174 }
tests/plugins/Dash/cardcreator/5.res+31−18
@@ -28,34 +28,47 @@ Item {
2828 active: cardData && cardData["art"] || false;
2929 asynchronous: root.asynchronous;
3030 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else {
31+ sourceComponent: Item {
32+ id: artShape;
33+ objectName: "artShape";
34+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35+ visible: image.status == Image.Ready;
36+ readonly property alias image: artImage.image;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ image: artShapeSource;
47+ anchors.fill: parent;
48+ visible: doShapeItem;
49+ radius: "medium";
50+ }
51+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
52+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
53+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
54+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
55+ function updateWidthHeightBindings() {
56+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
57+ width = root.fixedArtShapeSize.width;
58+ height = root.fixedArtShapeSize.height;
59+ } else {
4560 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
4661 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
4762 }
48− }
63+ }
4964 CroppedImageMinimumSourceSize {
5065 id: artImage;
5166 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
67+ source: cardData && cardData["art"] || "";
5368 asynchronous: root.asynchronous;
54− visible: false;
5569 width: root.width;
5670 height: width / artShape.aspect;
5771 }
58− image: artImage.image;
5972 }
6073 }
6174 }
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−1
@@ -15,6 +15,7 @@ include_directories(
1515export_qmlfiles(UbuntuGesturesTest .)
1616
1717add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
18+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
1819
1920macro(build_gesture_test CLASSNAME)
2021 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
@@ -38,7 +39,8 @@ macro(add_gesture_ui_test CLASSNAME)
3839 DEPENDS UbuntuGesturesTest-qmlfiles
3940 ${ARGN}
4041 )
41− add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
42+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
43+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
4244endmacro()
4345
4446add_gesture_ui_test(DirectionalDragArea)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19
@@ -37,8 +37,8 @@ Item {
3737
3838 Rectangle {
3939 id: dragAreaRect
40− color: "yellow"
41− opacity: 0.0
40+ opacity: dragArea.dragging ? 0.5 : 0.0
41+ color: "green"
4242 anchors.fill: dragArea
4343 }
4444
@@ -49,24 +49,10 @@ Item {
4949 height: units.gu(5)
5050
5151 direction: Direction.Downwards
52− maxDeviation: units.gu(2)
53− wideningAngle: 10
54− distanceThreshold: units.gu(4)
5552
56− onStatusChanged: {
57− switch (status) {
58− case DirectionalDragArea.WaitingForTouch:
59− dragAreaRect.opacity = 0.0
60− break;
61− case DirectionalDragArea.Undecided:
62− dragAreaRect.color = "yellow"
63− dragAreaRect.opacity = 0.3
64− launcher.y = Qt.binding(launcher.followDragArea)
65− break;
66− default: // DirectionalDragArea.Recognized:
67− dragAreaRect.color = "green"
68− dragAreaRect.opacity = 0.5
69− break;
53+ onDraggingChanged: {
54+ if (dragging) {
55+ launcher.y = Qt.binding(launcher.followDragArea)
7056 }
7157 }
7258
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+13−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -21,8 +21,11 @@
2121#include <QQuickView>
2222#include <QtTest>
2323
24+#include <Timer.h>
2425#include <TouchRegistry.h>
2526
27+using namespace UbuntuGestures;
28+
2629GestureTest::GestureTest(const QString &qmlFilename)
2730 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
2831{
@@ -40,14 +43,17 @@ void GestureTest::initTestCase()
4043void GestureTest::init()
4144{
4245 m_view = new QQuickView;
43− m_view->setResizeMode(QQuickView::SizeViewToRootObject);
46+ m_view->setResizeMode(QQuickView::SizeRootObjectToView);
4447 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
48+ m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
4549 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
4650 m_view->show();
4751 QVERIFY(QTest::qWaitForWindowExposed(m_view));
4852 QVERIFY(m_view->rootObject() != 0);
4953
50− m_touchRegistry = new TouchRegistry;
54+ m_fakeTimerFactory = new FakeTimerFactory;
55+
56+ m_touchRegistry = new TouchRegistry(nullptr, m_fakeTimerFactory);
5157 m_view->installEventFilter(m_touchRegistry);
5258
5359 qApp->processEvents();
@@ -59,6 +65,10 @@ void GestureTest::cleanup()
5965 delete m_touchRegistry;
6066 m_touchRegistry = nullptr;
6167
68+ // TouchRegistry will take down the timer factory along with him
69+ // delete m_fakeTimerFactory;
70+ m_fakeTimerFactory = nullptr;
71+
6272 delete m_view;
6373 m_view = nullptr;
6474}
tests/plugins/Ubuntu/Gestures/GestureTest.h+4
@@ -23,6 +23,9 @@
2323class QQuickView;
2424class QTouchDevice;
2525
26+namespace UbuntuGestures {
27+ class FakeTimerFactory;
28+}
2629class TouchRegistry;
2730
2831// C++ std lib
@@ -81,6 +84,7 @@ protected:
8184 QTouchDevice *m_device;
8285 QQuickView *m_view;
8386 TouchRegistry *m_touchRegistry;
87+ UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
8488 QString m_qmlFilename;
8589};
8690
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
2121Item {
2222 id: root
2323
24− function reset() { launcher.x = root.width }
24+ function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
2525
2626 Rectangle {
2727 id: launcher
@@ -41,7 +41,8 @@ Item {
4141
4242 Rectangle {
4343 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
4546 anchors.fill: dragArea
4647 }
4748
@@ -52,24 +53,10 @@ Item {
5253 width: units.gu(5)
5354
5455 direction: Direction.Leftwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
5856
59− onStatusChanged: {
60− switch (status) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.x = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.x = Qt.binding(launcher.followDragArea)
7360 }
7461 }
7562
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+5−19
@@ -38,7 +38,8 @@ Item {
3838
3939 Rectangle {
4040 id: dragAreaRect
41− opacity: 0.0
41+ opacity: dragArea.dragging ? 0.5 : 0.0
42+ color: "green"
4243 anchors.fill: dragArea
4344 }
4445
@@ -52,25 +53,10 @@ Item {
5253 width: units.gu(5)
5354
5455 direction: Direction.Rightwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58− minSpeed: 50
5956
60− onStatusChanged: {
61− switch (status) {
62− case DirectionalDragArea.WaitingForTouch:
63− dragAreaRect.opacity = 0.0
64− break;
65− case DirectionalDragArea.Undecided:
66− dragAreaRect.color = "yellow"
67− dragAreaRect.opacity = 0.3
68− launcher.x = Qt.binding(launcher.followDragArea)
69− break;
70− default: //case DirectionalDragArea.Recognized:
71− dragAreaRect.color = "green"
72− dragAreaRect.opacity = 0.5
73− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.x = Qt.binding(launcher.followDragArea)
7460 }
7561 }
7662
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
2121Item {
2222 id: root
2323
24− function reset() { launcher.y = root.height }
24+ function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
2525
2626 Rectangle {
2727 id: launcher
@@ -41,7 +41,8 @@ Item {
4141
4242 Rectangle {
4343 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
4546 anchors.fill: dragArea
4647 }
4748
@@ -52,24 +53,10 @@ Item {
5253 height: units.gu(5)
5354
5455 direction: Direction.Upwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
5856
59− onStatusChanged: {
60− switch (status) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.y = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.y = Qt.binding(launcher.followDragArea)
7360 }
7461 }
7562
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+480−445
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -20,51 +20,38 @@
2020#include <QtQml/QQmlEngine>
2121#include <QPointer>
2222#include <private/qquickmousearea_p.h>
23−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
24− #include <private/qquickwindow_p.h>
25−#endif
23+#include <private/qquickwindow_p.h>
2624
2725
2826#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
2928#include <TouchRegistry.h>
3029
3130#include "GestureTest.h"
3231
3332using namespace UbuntuGestures;
3433
35−
36−class ComplexFakeTimer : public FakeTimer
37−{
34+// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
35+// doesn't work
36+class StatusSpy : public QObject {
3837 Q_OBJECT
3938public:
40− ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
41− : FakeTimer(parent),
42− m_timeSource(timeSource)
43− {}
44−
45− void start() override {
46− AbstractTimer::start();
47− m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
39+ StatusSpy(DirectionalDragArea *edgeDragArea) {
40+ m_recognized = false;
41+ connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
42+ this, &StatusSpy::onStatusChanged);
4843 }
49−
50− void emitTimeout() {
51− m_nextTimeoutTime += interval();
52− Q_EMIT timeout();
44+ bool recognized() {
45+ return m_recognized;
5346 }
5447
55− qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
48+private Q_SLOTS:
49+ void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
50+ m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
51+ }
5652
5753private:
58− SharedTimeSource m_timeSource;
59− qint64 m_nextTimeoutTime;
60−};
61−
62−class FakeTimeSource : public UbuntuGestures::TimeSource
63−{
64−public:
65− FakeTimeSource() { m_msecsSinceReference = 0; }
66− virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
67− qint64 m_msecsSinceReference;
54+ bool m_recognized;
6855};
6956
7057/*
@@ -98,15 +85,9 @@ public:
9885 tst_DirectionalDragArea();
9986private Q_SLOTS:
10087 void init(); // called right before each and every test function is executed
101− void cleanup(); // called right after each and every test function is executed
10288
103− void edgeDrag();
104− void edgeDrag_data();
10589 void dragWithShortDirectionChange();
106− void minSpeed();
107− void minSpeed_data();
10890 void recognitionTimerUsage();
109− void maxSilenceTime();
11091 void sceneXAndX();
11192 void sceneYAndY();
11293 void twoFingerTap();
@@ -122,14 +103,24 @@ private Q_SLOTS:
122103 void immediateRecognitionWhenConstraintsDisabled();
123104 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
124105 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
125− void tappedSignal();
126− void tappedSignal_data();
127106 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107+ void interleavedTouches();
108+ void makoRightEdgeDrag();
109+ void makoRightEdgeDrag_verticalDownwards();
110+ void makoLeftEdgeDrag_slowStart();
111+ void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
128112
129113private:
114+ // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115+ // lose precision due to rounding.
116+ // Besides, those helper functions lead to more compact code.
117+ void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118+ void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119+ void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120+ void sendTouch(qint64 timestamp, int id, QPointF pos,
121+ Qt::TouchPointState pointState, QEvent::Type eventType);
122+
130123 void passTime(qint64 timeSpanMs);
131− ComplexFakeTimer *fakeTimer;
132− QSharedPointer<FakeTimeSource> fakeTimeSource;
133124};
134125
135126tst_DirectionalDragArea::tst_DirectionalDragArea()
@@ -148,185 +139,57 @@ void tst_DirectionalDragArea::init()
148139 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
149140 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
150141 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
151−
152− fakeTimeSource.reset(new FakeTimeSource);
153− fakeTimer = new ComplexFakeTimer(fakeTimeSource);
154−}
155−
156−void tst_DirectionalDragArea::cleanup()
157−{
158− delete fakeTimer;
159− fakeTimer = 0;
160−
161− fakeTimeSource.reset();
162−
163− GestureTest::cleanup();
164−}
165−
166−void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
167−{
168− qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;
169−
170− if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
171− fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
172− fakeTimer->emitTimeout();
173−
174− qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
175− if (timeSpanRemainder > 0) {
176− passTime(timeSpanRemainder);
177− }
178− } else {
179− fakeTimeSource->m_msecsSinceReference = finalTime;
180− }
181142}
182143
183−namespace {
184−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
144+void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
185145{
186− QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
187− return edgeDragArea->mapToScene(localCenter);
146+ sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
188147}
189148
190−QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
191− qreal wideningAngleMultiplier)
149+void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
192150{
193− qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
194− * M_PI / 180.0;
195−
196− qreal angleCos = qCos(angleRadians);
197− qreal angleSin = qSin(angleRadians);
198−
199− switch (edgeDragArea->direction()) {
200− case Direction::Upwards:
201− return QPointF(angleSin, -angleCos);
202− case Direction::Downwards:
203− return QPointF(angleSin, angleCos);
204− case Direction::Leftwards:
205− return QPointF(-angleCos, angleSin);
206− default: // Direction::Rightwards:
207− return QPointF(angleCos, angleSin);
208− }
151+ sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
209152}
210153
211−QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
154+void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
212155{
213− qreal deviation = edgeDragArea->maxDeviation() * 0.8;
214−
215− if (Direction::isHorizontal(edgeDragArea->direction())) {
216− return QPointF(0, deviation);
217− } else {
218− return QPointF(deviation, 0);
219− }
220−}
156+ sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
221157}
222158
223−void tst_DirectionalDragArea::edgeDrag()
159+void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160+ Qt::TouchPointState pointState, QEvent::Type eventType)
224161{
225− QFETCH(QString, dragAreaObjectName);
226− QFETCH(qreal, wideningAngleMultiplier);
227− QFETCH(qreal, dragDistanceFactor);
228− QFETCH(bool, expectGestureRecognition);
229−
230− DirectionalDragArea *edgeDragArea =
231− m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
232− QVERIFY(edgeDragArea != 0);
233− edgeDragArea->setRecognitionTimer(fakeTimer);
234− edgeDragArea->setTimeSource(fakeTimeSource);
235−
236− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
237−
238− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
239− QPointF touchPoint = initialTouchPos;
240−
241− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
242− QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
243− wideningAngleMultiplier);
244− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
245− QPointF touchMovement = dragDirectionVector * movementStepDistance;
246− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
247− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
248−
249− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
250−
251− QCOMPARE(draggingSpy.count(), 1);
252− QCOMPARE(edgeDragArea->dragging(), true);
253−
254− if (wideningAngleMultiplier > 0) {
255− // go close to the border of the valid area for this touch point
256− // in order to make it easier to leave it by dragging at an angle
257− // slightly bigger than the widening angle
258− touchPoint += createTouchDeviation(edgeDragArea);
259− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
260− }
162+ m_fakeTimerFactory->updateTime(timestamp);
261163
262− for (int i = 0; i < totalMovementSteps; ++i) {
263− touchPoint += touchMovement;
264− passTime(movementTimeStepMs);
265− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
266− }
164+ QTouchEvent::TouchPoint point;
267165
268− if (expectGestureRecognition)
269− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
166+ point.setState(pointState);
167+ point.setId(id);
168+ point.setScenePos(pos);
169+ point.setPos(pos);
270170
271− if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
272− QCOMPARE(edgeDragArea->dragging(), false);
273− QCOMPARE(draggingSpy.count(), 2);
274− }
171+ QList<QTouchEvent::TouchPoint> points;
172+ points << point;
275173
276− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
174+ QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175+ QCoreApplication::sendEvent(m_view, &touchEvent);
277176
278− QCOMPARE(draggingSpy.count(), 2);
279− QCOMPARE(edgeDragArea->dragging(), false);
177+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
178+ windowPrivate->flushDelayedTouchEvent();
280179}
281180
282−void tst_DirectionalDragArea::edgeDrag_data()
181+void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
283182{
284− QTest::addColumn<QString>("dragAreaObjectName");
285− QTest::addColumn<qreal>("wideningAngleMultiplier");
286− QTest::addColumn<qreal>("dragDistanceFactor");
287− QTest::addColumn<bool>("expectGestureRecognition");
288−
289− QTest::newRow("rightwards, tiny drag")
290− << "hpDragArea" << 0.0 << 0.2 << false;
291−
292− QTest::newRow("rightwards, straight drag")
293− << "hpDragArea" << 0.0 << 3.0 << true;
294−
295− QTest::newRow("rightwards, diagonal drag")
296− << "hpDragArea" << 0.9 << 3.0 << true;
297−
298− QTest::newRow("rightwards, overly diagonal drag")
299− << "hpDragArea" << 2.0 << 3.0 << false;
300−
301− QTest::newRow("leftwards, tiny drag")
302− << "hnDragArea" << 0.0 << 0.2 << false;
303−
304− QTest::newRow("leftwards, straight drag")
305− << "hnDragArea" << 0.0 << 3.0 << true;
306−
307− QTest::newRow("leftwards, diagonal drag")
308− << "hnDragArea" << 0.9 << 3.0 << true;
309−
310− QTest::newRow("downwards, tiny drag")
311− << "vpDragArea" << 0.0 << 0.2 << false;
312−
313− QTest::newRow("downwards, straight drag")
314− << "vpDragArea" << 0.0 << 3.0 << true;
315−
316− QTest::newRow("downwards, diagonal drag")
317− << "vpDragArea" << 0.9 << 3.0 << true;
318−
319− QTest::newRow("upwards, tiny drag")
320− << "vnDragArea" << 0.0 << 0.2 << false;
321−
322− QTest::newRow("upwards, straight drag")
323− << "vnDragArea" << 0.0 << 3.0 << true;
324−
325− QTest::newRow("upwards, diagonal drag")
326− << "vnDragArea" << 0.9 << 3.0 << true;
183+ qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
184+ m_fakeTimerFactory->updateTime(finalTime);
185+}
327186
328− QTest::newRow("upwards, overly diagonal drag")
329− << "vnDragArea" << 2.0 << 3.0 << false;
187+namespace {
188+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
189+{
190+ QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
191+ return edgeDragArea->mapToScene(localCenter);
192+}
330193}
331194
332195/*
@@ -339,17 +202,17 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
339202 DirectionalDragArea *edgeDragArea =
340203 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
341204 QVERIFY(edgeDragArea != 0);
342− edgeDragArea->setRecognitionTimer(fakeTimer);
343− edgeDragArea->setTimeSource(fakeTimeSource);
205+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
206+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
344207
345208 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
346209 QPointF touchPoint = initialTouchPos;
347210
348− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
211+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
349212 QPointF dragDirectionVector(1.0, 0.0);
350− qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
351− // make sure we are above minimum speed
352− int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
213+ qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
214+ // make sure we are above maximum time
215+ int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
353216 QPointF touchMovement = dragDirectionVector * touchStepDistance;
354217
355218 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -373,65 +236,13 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
373236 passTime(touchStepTimeMs);
374237 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375238 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
376− || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
239+ || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
377240
378− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
241+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
379242
380243 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
381244}
382245
383−/*
384− Checks that a gesture will be rejected if it's slower than minSpeed while
385− status is Undecided.
386− */
387−void tst_DirectionalDragArea::minSpeed()
388−{
389− QFETCH(qreal, minSpeed);
390− QFETCH(qreal, speed);
391− QFETCH(int, expectedStatusAfterSpeedCheck);
392−
393− DirectionalDragArea *edgeDragArea =
394− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
395− QVERIFY(edgeDragArea != 0);
396− edgeDragArea->setRecognitionTimer(fakeTimer);
397− edgeDragArea->setTimeSource(fakeTimeSource);
398−
399− // A really long, unattainable, number. We don't want it getting recognized before
400− // the speed checks we want have been performed
401− edgeDragArea->setDistanceThreshold(500000);
402−
403− edgeDragArea->setMinSpeed(minSpeed);
404−
405− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
406− QPointF touchPoint = initialTouchPos;
407−
408− QPointF dragDirectionVector(1.0, 0.0);
409− qint64 timeStepMsecs = 10;
410− qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
411− QPointF touchMovement = dragDirectionVector * distanceStep;
412−
413− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
414−
415− // Move for a while to ensure our speed check is performed a couple of times
416− for (int i=0; i < 20; ++i) {
417− touchPoint += touchMovement;
418− passTime(timeStepMsecs);
419− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
420− }
421−
422− QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
423−}
424−
425−void tst_DirectionalDragArea::minSpeed_data()
426−{
427− QTest::addColumn<qreal>("minSpeed");
428− QTest::addColumn<qreal>("speed");
429− QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
430−
431− QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
432− QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
433−}
434−
435246/*
436247 Checks that the recognition timer is started and stopped appropriately.
437248 I.e., check that it's running only while gesture recognition is taking place
@@ -442,11 +253,9 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
442253 DirectionalDragArea *edgeDragArea =
443254 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444255 QVERIFY(edgeDragArea != 0);
445− edgeDragArea->setRecognitionTimer(fakeTimer);
446− edgeDragArea->setTimeSource(fakeTimeSource);
447−
448− // don't let it interfere with our test
449− edgeDragArea->setMinSpeed(0.0);
256+ AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
257+ edgeDragArea->d->setRecognitionTimer(fakeTimer);
258+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
450259
451260 int timeStepMs = 5; // some arbitrary small value.
452261
@@ -454,62 +263,31 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
454263 QPointF touchPoint = initialTouchPos;
455264
456265 QPointF dragDirectionVector(1.0, 0.0);
457− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
266+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
458267
459− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
268+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
460269 QVERIFY(!fakeTimer->isRunning());
461270
462271 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
463272
464− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
273+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
465274 QVERIFY(fakeTimer->isRunning());
466275
467276 // Move beyond distance threshold and composition time to ensure recognition
468− while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
469− (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
277+ while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
278+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
470279
471− QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
280+ QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
472281
473282 touchPoint += touchMovement;
474283 passTime(timeStepMs);
475284 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
476285 }
477286
478− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
287+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
479288 QVERIFY(!fakeTimer->isRunning());
480289}
481290
482−/*
483− A gesture should be rejected if too much time has passed without any new input
484− events from it.
485− */
486−void tst_DirectionalDragArea::maxSilenceTime()
487−{
488− DirectionalDragArea *edgeDragArea =
489− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
490− QVERIFY(edgeDragArea != 0);
491− edgeDragArea->setRecognitionTimer(fakeTimer);
492− edgeDragArea->setTimeSource(fakeTimeSource);
493−
494− // Make sure this property is not disabled
495− edgeDragArea->setMaxSilenceTime(100);
496−
497− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
498− QPointF touchPoint = initialTouchPos;
499−
500− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
501−
502− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
503− QVERIFY(fakeTimer->isRunning());
504−
505− // Force timer to timeout until after maxSilenceTime has been reached
506− while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
507− passTime(fakeTimer->interval());
508− }
509−
510− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
511−}
512−
513291/*
514292 Checks that it informs the X coordinate of the touch point in local and scene coordinates
515293 correctly.
@@ -519,18 +297,19 @@ void tst_DirectionalDragArea::sceneXAndX()
519297 DirectionalDragArea *edgeDragArea =
520298 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
521299 QVERIFY(edgeDragArea != 0);
522− edgeDragArea->setRecognitionTimer(fakeTimer);
523− edgeDragArea->setTimeSource(fakeTimeSource);
300+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
301+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302+ edgeDragArea->setImmediateRecognition(true);
524303
525304 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
526305
527− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
306+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
528307
529308 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
530309 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
531310
532311 touchScenePos.rx() = m_view->width() / 2;
533− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
312+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
534313
535314 QCOMPARE(touchXSpy.count(), 1);
536315 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -547,18 +326,19 @@ void tst_DirectionalDragArea::sceneYAndY()
547326 DirectionalDragArea *edgeDragArea =
548327 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
549328 QVERIFY(edgeDragArea != 0);
550− edgeDragArea->setRecognitionTimer(fakeTimer);
551− edgeDragArea->setTimeSource(fakeTimeSource);
329+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
330+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331+ edgeDragArea->setImmediateRecognition(true);
552332
553333 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
554334
555− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
335+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
556336
557337 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
558338 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
559339
560340 touchScenePos.ry() = m_view->height() / 2;
561− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
341+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
562342
563343 QCOMPARE(touchYSpy.count(), 1);
564344 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -577,8 +357,8 @@ void tst_DirectionalDragArea::twoFingerTap()
577357 DirectionalDragArea *edgeDragArea =
578358 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
579359 QVERIFY(edgeDragArea != 0);
580− edgeDragArea->setRecognitionTimer(fakeTimer);
581− edgeDragArea->setTimeSource(fakeTimeSource);
360+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
361+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
582362
583363 // Make touches evenly spaced along the edgeDragArea
584364 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -592,7 +372,7 @@ void tst_DirectionalDragArea::twoFingerTap()
592372 QTest::touchEvent(m_view, m_device)
593373 .press(0, touchAPos);
594374
595− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
375+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
596376
597377 passTime(timeStepMsecs);
598378 QTest::touchEvent(m_view, m_device)
@@ -601,20 +381,20 @@ void tst_DirectionalDragArea::twoFingerTap()
601381
602382 // A second touch point appeared during recognition, reject immediately as this
603383 // can't be a single-touch gesture anymore.
604− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
384+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
605385
606386 passTime(timeStepMsecs);
607387 QTest::touchEvent(m_view, m_device)
608388 .release(0, touchAPos)
609389 .move(1, touchBPos);
610390
611− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
391+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
612392
613393 passTime(timeStepMsecs);
614394 QTest::touchEvent(m_view, m_device)
615395 .release(1, touchBPos);
616396
617− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
397+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
618398
619399 // Perform the second two-finger tap
620400
@@ -622,27 +402,27 @@ void tst_DirectionalDragArea::twoFingerTap()
622402 QTest::touchEvent(m_view, m_device)
623403 .press(0, touchAPos);
624404
625− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
405+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
626406
627407 passTime(timeStepMsecs);
628408 QTest::touchEvent(m_view, m_device)
629409 .move(0, touchAPos)
630410 .press(1, touchBPos);
631411
632− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
412+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
633413
634414 passTime(timeStepMsecs);
635415 QTest::touchEvent(m_view, m_device)
636416 .release(0, touchAPos)
637417 .move(1, touchBPos);
638418
639− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
419+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
640420
641421 passTime(timeStepMsecs);
642422 QTest::touchEvent(m_view, m_device)
643423 .release(1, touchBPos);
644424
645− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
425+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
646426}
647427
648428/*
@@ -659,25 +439,25 @@ void tst_DirectionalDragArea::movingDDA()
659439 DirectionalDragArea *edgeDragArea =
660440 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
661441 Q_ASSERT(edgeDragArea != 0);
662− edgeDragArea->setRecognitionTimer(fakeTimer);
663− edgeDragArea->setTimeSource(fakeTimeSource);
442+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
443+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
664444
665445 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
666446 QPointF touchPoint = initialTouchPos;
667447
668− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
448+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
669449 QPointF dragDirectionVector(1.0f, 0.0f);
670450
671− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
451+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
672452 QPointF touchMovement = dragDirectionVector * movementStepDistance;
673453 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
674− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
454+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
675455
676456 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
677457
678458 // Move it far ahead along the direction of the gesture
679459 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
680− rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
460+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
681461
682462 for (int i = 0; i < totalMovementSteps; ++i) {
683463 touchPoint += touchMovement;
@@ -685,7 +465,7 @@ void tst_DirectionalDragArea::movingDDA()
685465 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
686466 }
687467
688− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
468+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
689469
690470 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
691471}
@@ -699,8 +479,8 @@ void tst_DirectionalDragArea::ignoreOldFinger()
699479 DirectionalDragArea *edgeDragArea =
700480 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
701481 Q_ASSERT(edgeDragArea != 0);
702− edgeDragArea->setRecognitionTimer(fakeTimer);
703− edgeDragArea->setTimeSource(fakeTimeSource);
482+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
483+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
704484
705485 // Make touches evenly spaced along the edgeDragArea
706486 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -708,26 +488,26 @@ void tst_DirectionalDragArea::ignoreOldFinger()
708488
709489 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
710490
711− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
491+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
712492
713493 // leave it lying around for some time
714− passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
494+ passTime(edgeDragArea->d->maxTime * 10);
715495
716− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
496+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
717497
718− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
498+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
719499 QPointF dragDirectionVector(1.0f, 0.0f);
720500
721− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
501+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
722502 QPointF touchMovement = dragDirectionVector * movementStepDistance;
723503 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
724− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
504+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
725505
726506 QTest::touchEvent(m_view, m_device)
727507 .move(0, touch0Pos)
728508 .press(1, touch1Pos);
729509
730− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
510+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731511
732512 for (int i = 0; i < totalMovementSteps; ++i) {
733513 touch1Pos += touchMovement.toPoint();
@@ -737,13 +517,13 @@ void tst_DirectionalDragArea::ignoreOldFinger()
737517 .move(1, touch1Pos);
738518 }
739519
740− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
520+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
741521
742522 QTest::touchEvent(m_view, m_device)
743523 .move(0, touch0Pos)
744524 .release(1, touch1Pos);
745525
746− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
526+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
747527}
748528
749529/*
@@ -762,19 +542,19 @@ void tst_DirectionalDragArea::rotated()
762542 DirectionalDragArea *edgeDragArea =
763543 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
764544 Q_ASSERT(edgeDragArea != 0);
765− edgeDragArea->setRecognitionTimer(fakeTimer);
766− edgeDragArea->setTimeSource(fakeTimeSource);
545+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
546+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
767547
768548 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
769549 QPointF touchPoint = initialTouchPos;
770550
771− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
551+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
772552 QPointF dragDirectionVector(0.0f, 1.0f);
773553
774− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
554+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
775555 QPointF touchMovement = dragDirectionVector * movementStepDistance;
776556 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
777− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
557+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
778558
779559 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
780560
@@ -784,7 +564,7 @@ void tst_DirectionalDragArea::rotated()
784564 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
785565 }
786566
787− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
567+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
788568
789569 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790570}
@@ -802,31 +582,30 @@ void tst_DirectionalDragArea::sceneDistance()
802582 DirectionalDragArea *edgeDragArea =
803583 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
804584 Q_ASSERT(edgeDragArea != 0);
805− edgeDragArea->setRecognitionTimer(fakeTimer);
806− edgeDragArea->setTimeSource(fakeTimeSource);
585+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
586+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587+
588+ // to disable the position smoothing so that we can more easily check sceneDistance values
589+ edgeDragArea->setImmediateRecognition(true);
807590
808591 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
809592 QPointF touchPoint = initialTouchPos;
810593
811− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
594+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
812595
813− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
596+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
814597 QPointF touchMovement = dragDirectionVector * movementStepDistance;
815598 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
816− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
599+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
817600
818− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
601+ qint64 timestamp = 0;
602+
603+ sendTouchPress(timestamp, 0, touchPoint);
819604
820−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
821− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
822−#endif
823605 for (int i = 0; i < totalMovementSteps; ++i) {
824606 touchPoint += touchMovement;
825− passTime(movementTimeStepMs);
826− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
827−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828− wp->flushDelayedTouchEvent();
829−#endif
607+ timestamp += movementTimeStepMs;
608+ sendTouchUpdate(timestamp, 0, touchPoint);
830609 }
831610
832611 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
@@ -836,7 +615,8 @@ void tst_DirectionalDragArea::sceneDistance()
836615 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
837616 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
838617
839− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
618+ timestamp += movementTimeStepMs;
619+ sendTouchRelease(timestamp, 0, touchPoint);
840620}
841621
842622void tst_DirectionalDragArea::sceneDistance_data()
@@ -860,18 +640,18 @@ void tst_DirectionalDragArea::disabledWhileDragging()
860640 DirectionalDragArea *edgeDragArea =
861641 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
862642 Q_ASSERT(edgeDragArea != 0);
863− edgeDragArea->setRecognitionTimer(fakeTimer);
864− edgeDragArea->setTimeSource(fakeTimeSource);
643+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
644+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
865645
866646 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
867647
868− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
648+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
869649 QPointF dragDirectionVector(1., 0.); // horizontal positive
870650
871− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
651+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
872652 QPointF touchMovement = dragDirectionVector * movementStepDistance;
873653 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
874− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
654+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
875655
876656 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
877657
@@ -881,13 +661,13 @@ void tst_DirectionalDragArea::disabledWhileDragging()
881661 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
882662 }
883663
884− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
664+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
885665 QCOMPARE(edgeDragArea->dragging(), true);
886666
887667 // disable the dragArea while it's being dragged.
888668 edgeDragArea->setEnabled(false);
889669
890− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
670+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
891671 QCOMPARE(edgeDragArea->dragging(), false);
892672
893673 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -898,15 +678,14 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
898678 DirectionalDragArea *edgeDragArea =
899679 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
900680 Q_ASSERT(edgeDragArea != 0);
901− edgeDragArea->setRecognitionTimer(fakeTimer);
902− edgeDragArea->setTimeSource(fakeTimeSource);
681+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
682+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
903683
904684 // Disable some constraints we're not interested in
905− edgeDragArea->setMaxSilenceTime(60 * 1000);
906− edgeDragArea->setMinSpeed(0);
685+ edgeDragArea->d->setMaxTime(60 * 1000);
907686
908687 // And ensure others have the values we want
909− edgeDragArea->setCompositionTime(60);
688+ edgeDragArea->d->compositionTime = 60;
910689
911690 // Put an item right behind edgeDragArea to receive the touches ignored by it
912691 DummyItem *dummyItem = new DummyItem;
@@ -923,10 +702,10 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
923702
924703 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
925704
926− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
705+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
927706
928707 // We are now going to be way beyond compositionTime
929− passTime(edgeDragArea->compositionTime()*3);
708+ passTime(edgeDragArea->d->compositionTime*3);
930709
931710 QTest::touchEvent(m_view, m_device)
932711 .move(0, touch0Pos)
@@ -948,7 +727,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
948727 .move(0, touch0Pos)
949728 .move(1, touch1Pos);
950729
951− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
730+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
952731
953732 passTime(5);
954733
@@ -956,7 +735,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
956735 .release(0, touch0Pos)
957736 .move(1, touch1Pos);
958737
959− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
738+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
960739
961740 passTime(5);
962741
@@ -964,7 +743,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
964743 .release(1, touch1Pos);
965744
966745 // Shouldn't be keepping info about touches that no longer exist or interest us
967− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
746+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
968747
969748 delete dummyItem;
970749}
@@ -974,12 +753,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
974753 DirectionalDragArea *edgeDragArea =
975754 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
976755 Q_ASSERT(edgeDragArea != 0);
977− edgeDragArea->setRecognitionTimer(fakeTimer);
978− edgeDragArea->setTimeSource(fakeTimeSource);
756+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
757+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
979758
980759 // Disable some constraints we're not interested in
981− edgeDragArea->setMaxSilenceTime(60 * 1000);
982− edgeDragArea->setMinSpeed(0);
760+ edgeDragArea->d->setMaxTime(60 * 1000);
983761
984762 // Put an item right in front of edgeDragArea
985763 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
@@ -998,11 +776,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
998776
999777 QTest::touchEvent(m_view, m_device).press(0, touchPos);
1000778
1001− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
779+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1002780
1003781 m_touchRegistry->requestTouchOwnership(0, dummyItem);
1004782
1005− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
783+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1006784
1007785 dummyItem->grabTouchPoints({0});
1008786 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
@@ -1010,9 +788,9 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1010788 passTime(5);
1011789 QTest::touchEvent(m_view, m_device).release(0, touchPos);
1012790
1013− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
791+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1014792
1015− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
793+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1016794}
1017795
1018796void tst_DirectionalDragArea::threeFingerDrag()
@@ -1020,15 +798,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1020798 DirectionalDragArea *edgeDragArea =
1021799 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1022800 Q_ASSERT(edgeDragArea != 0);
1023− edgeDragArea->setRecognitionTimer(fakeTimer);
1024− edgeDragArea->setTimeSource(fakeTimeSource);
801+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
802+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1025803
1026804 // Disable some constraints we're not interested in
1027− edgeDragArea->setMaxSilenceTime(60 * 1000);
1028− edgeDragArea->setMinSpeed(0);
805+ edgeDragArea->d->setMaxTime(60 * 1000);
1029806
1030807 // And ensure others have the values we want
1031− edgeDragArea->setCompositionTime(60);
808+ edgeDragArea->d->compositionTime = 60;
1032809
1033810 // Make touches evenly spaced along the edgeDragArea
1034811 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
@@ -1038,14 +815,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1038815 QTest::touchEvent(m_view, m_device)
1039816 .press(0, touch0Pos);
1040817
1041− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
818+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1042819
1043820 passTime(5);
1044821 QTest::touchEvent(m_view, m_device)
1045822 .move(0, touch0Pos)
1046823 .press(1, touch1Pos);
1047824
1048− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
825+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1049826
1050827 passTime(5);
1051828 QTest::touchEvent(m_view, m_device)
@@ -1053,7 +830,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1053830 .move(1, touch1Pos)
1054831 .press(2, touch2Pos);
1055832
1056− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
833+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1057834
1058835 passTime(10);
1059836 QTest::touchEvent(m_view, m_device)
@@ -1077,7 +854,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1077854 .release(0, touch0Pos);
1078855
1079856 // Shouldn't be keepping info about touches that no longer exist or interest us
1080− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
857+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1081858}
1082859
1083860/*
@@ -1090,12 +867,12 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1090867 DirectionalDragArea *edgeDragArea =
1091868 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1092869 Q_ASSERT(edgeDragArea != 0);
1093− edgeDragArea->setRecognitionTimer(fakeTimer);
1094− edgeDragArea->setTimeSource(fakeTimeSource);
870+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
871+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1095872
1096873 // Disable the minimum amount of constraints to ensure immediate recognition
1097− edgeDragArea->setDistanceThreshold(0);
1098− edgeDragArea->setCompositionTime(0);
874+ edgeDragArea->d->setDistanceThreshold(0);
875+ edgeDragArea->d->compositionTime = 0;
1099876
1100877 // Put an item right behind edgeDragArea to receive the touches ignored by it
1101878 DummyItem *dummyItem = new DummyItem;
@@ -1111,7 +888,7 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1111888 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1112889
1113890 // check for immediate recognition
1114− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
891+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1115892
1116893 // and therefore it should have immediately grabbed the touch point,
1117894 // not letting it leak to items behind him.
@@ -1123,19 +900,19 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1123900 DirectionalDragArea *edgeDragArea =
1124901 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1125902 Q_ASSERT(edgeDragArea != 0);
1126− edgeDragArea->setRecognitionTimer(fakeTimer);
1127− edgeDragArea->setTimeSource(fakeTimeSource);
903+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
904+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1128905
1129906 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
1130907
1131908 // Move less than the minimum needed for the drag gesture recognition
1132− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;
909+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
1133910 QPointF dragDirectionVector(1., 0.); // horizontal positive
1134911
1135− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
912+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1136913 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1137914 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1138− int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;
915+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
1139916
1140917 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1141918
@@ -1145,14 +922,14 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1145922 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1146923 }
1147924
1148− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
925+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1149926
1150927 // edgeDragArea should be an undecided candidate
1151928 {
1152929 auto touchInfo = m_touchRegistry->findTouchInfo(0);
1153930 QCOMPARE(touchInfo->candidates.size(), 1);
1154931 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1155− QCOMPARE(touchInfo->candidates.at(0).undecided, true);
932+ QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
1156933 }
1157934
1158935 // disable the dragArea while it's still recognizing a possible drag gesture.
@@ -1169,7 +946,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1169946 QCOMPARE(touchInfo->candidates.size(), 0);
1170947 }
1171948
1172− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
949+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1173950
1174951 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1175952}
@@ -1182,47 +959,13 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1182959 QTest::newRow("invisible") << false;
1183960}
1184961
1185−void tst_DirectionalDragArea::tappedSignal()
1186−{
1187− DirectionalDragArea *edgeDragArea =
1188− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1189− QVERIFY(edgeDragArea != 0);
1190− edgeDragArea->setRecognitionTimer(fakeTimer);
1191− edgeDragArea->setTimeSource(fakeTimeSource);
1192−
1193− QFETCH(bool, immediateGestureRecognition);
1194− if (immediateGestureRecognition) {
1195− // Disable the minimum amount of constraints to ensure immediate recognition
1196− edgeDragArea->setDistanceThreshold(0);
1197− edgeDragArea->setCompositionTime(0);
1198− }
1199−
1200− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1201−
1202− QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
1203−
1204− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1205− passTime(edgeDragArea->maxTapDuration() / 2);
1206− QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
1207−
1208− QCOMPARE(tappedSpy.count(), 1);
1209−}
1210−
1211−void tst_DirectionalDragArea::tappedSignal_data()
1212−{
1213− QTest::addColumn<bool>("immediateGestureRecognition");
1214−
1215− QTest::newRow("immediate gesture recognition") << true;
1216− QTest::newRow("default gesture recognition") << false;
1217−}
1218−
1219962void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1220963{
1221964 DirectionalDragArea *edgeDragArea =
1222965 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1223966 QVERIFY(edgeDragArea != nullptr);
1224− edgeDragArea->setRecognitionTimer(fakeTimer);
1225− edgeDragArea->setTimeSource(fakeTimeSource);
967+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
968+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1226969
1227970 QQuickMouseArea *mouseArea =
1228971 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
@@ -1233,12 +976,12 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1233976 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1234977 QPointF touchPoint = initialTouchPos;
1235978
1236− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
979+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1237980 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1238− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
981+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1239982 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1240983 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1241− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
984+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
1242985
1243986 QCOMPARE(mouseArea->pressed(), false);
1244987
@@ -1259,13 +1002,305 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
12591002 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
12601003 // which should make the MouseArea get a cancelation event, which will then cause it to
12611004 // reset its state (going back to "unpressed"/"released").
1262− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1005+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
12631006 QCOMPARE(mouseArea->pressed(), false);
12641007 QCOMPARE(mouseAreaSpy.canceledCount, 1);
12651008
12661009 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
12671010}
12681011
1012+void tst_DirectionalDragArea::interleavedTouches()
1013+{
1014+ DirectionalDragArea *edgeDragArea =
1015+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016+ QVERIFY(edgeDragArea != 0);
1017+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1018+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1019+
1020+ QPointF touch0 = edgeDragArea->mapToScene(
1021+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
1022+
1023+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1024+ QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1025+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1026+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
1027+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1028+ int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
1029+
1030+ QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
1031+ for (int i = 0; i < totalMovementSteps; ++i) {
1032+ touch0 += touchMovement;
1033+ passTime(movementTimeStepMs);
1034+ QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
1035+ }
1036+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1037+
1038+ QPointF touch1 = edgeDragArea->mapToScene(
1039+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
1040+
1041+ QTest::touchEvent(m_view, m_device)
1042+ .move(0, touch0.toPoint())
1043+ .press(1, touch1.toPoint());
1044+
1045+ touch1 += touchMovement;
1046+ passTime(movementTimeStepMs);
1047+ QTest::touchEvent(m_view, m_device)
1048+ .move(0, touch0.toPoint())
1049+ .move(1, touch1.toPoint());
1050+
1051+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1052+
1053+ QTest::touchEvent(m_view, m_device)
1054+ .release(0, touch0.toPoint())
1055+ .move(1, touch1.toPoint());
1056+
1057+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1058+
1059+ touch1 += touchMovement;
1060+ passTime(movementTimeStepMs);
1061+ QTest::touchEvent(m_view, m_device)
1062+ .move(1, touch1.toPoint());
1063+
1064+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1065+
1066+ QPointF touch2 = edgeDragArea->mapToScene(
1067+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
1068+
1069+ passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
1070+ QTest::touchEvent(m_view, m_device)
1071+ .move(1, touch1.toPoint())
1072+ .press(2, touch2.toPoint());
1073+
1074+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1075+ QCOMPARE(edgeDragArea->d->touchId, 2);
1076+
1077+ touch2 += touchMovement;
1078+ passTime(movementTimeStepMs);
1079+ QTest::touchEvent(m_view, m_device)
1080+ .move(1, touch1.toPoint())
1081+ .move(2, touch2.toPoint());
1082+
1083+ touch1 += touchMovement;
1084+ passTime(movementTimeStepMs);
1085+ QTest::touchEvent(m_view, m_device)
1086+ .move(1, touch1.toPoint())
1087+ .move(2, touch2.toPoint());
1088+
1089+ passTime(movementTimeStepMs);
1090+ QTest::touchEvent(m_view, m_device)
1091+ .release(1, touch1.toPoint())
1092+ .move(2, touch2.toPoint());
1093+
1094+ passTime(movementTimeStepMs);
1095+ QTest::touchEvent(m_view, m_device)
1096+ .release(2, touch2.toPoint());
1097+
1098+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1099+}
1100+
1101+/*
1102+ A valid right-edge drag performed on mako
1103+ */
1104+void tst_DirectionalDragArea::makoRightEdgeDrag()
1105+{
1106+ m_view->resize(768, 1280);
1107+ QTest::qWait(300);
1108+
1109+ DirectionalDragArea *edgeDragArea =
1110+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1111+ QVERIFY(edgeDragArea != nullptr);
1112+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1113+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1114+
1115+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1116+
1117+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1118+
1119+ sendTouchPress(319744, 0, QPointF(767.001, 719.719));
1120+ sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
1121+ sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
1122+ sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
1123+ sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
1124+ sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
1125+ sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
1126+ sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
1127+ sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
1128+ sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
1129+ sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
1130+ sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
1131+ sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
1132+ sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
1133+ sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
1134+ sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
1135+ sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
1136+ sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
1137+
1138+ QCOMPARE(statusSpy->recognized(), true);
1139+
1140+ delete statusSpy;
1141+}
1142+
1143+/*
1144+ A vertical, downwards swipe performed on mako near its right edge.
1145+
1146+ The DirectionalDragArea on the right edge must not recognize this
1147+ gesture.
1148+ */
1149+void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
1150+{
1151+ m_view->resize(768, 1280);
1152+ QTest::qWait(300);
1153+
1154+ DirectionalDragArea *edgeDragArea =
1155+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1156+ QVERIFY(edgeDragArea != nullptr);
1157+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1158+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1159+
1160+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1161+
1162+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1163+
1164+ sendTouchPress(12012445, 26, QPointF(767.001,461.82));
1165+ sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
1166+ sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
1167+ sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
1168+ sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
1169+ sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
1170+ sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
1171+ sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
1172+ sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
1173+ sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
1174+ sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
1175+ sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
1176+ sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
1177+ sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
1178+ sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
1179+ sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
1180+ sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
1181+ sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
1182+ sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
1183+ sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
1184+
1185+ QCOMPARE(statusSpy->recognized(), false);
1186+
1187+ delete statusSpy;
1188+}
1189+
1190+/*
1191+ A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
1192+ */
1193+void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
1194+{
1195+ m_view->resize(768, 1280);
1196+ QTest::qWait(300);
1197+
1198+ DirectionalDragArea *edgeDragArea =
1199+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1200+ QVERIFY(edgeDragArea != nullptr);
1201+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1202+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1203+
1204+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1205+
1206+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1207+
1208+ sendTouchPress(4002267, 77, QPointF(0,885.154));
1209+ sendTouchUpdate(4002275, 77, QPointF(0,886.214));
1210+ sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
1211+ sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
1212+ sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
1213+ sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
1214+ sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
1215+ sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
1216+ sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
1217+ sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
1218+ sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
1219+ sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
1220+ sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
1221+ sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
1222+ sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
1223+ sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
1224+ sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
1225+ sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
1226+ sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
1227+ sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
1228+ sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
1229+ sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
1230+ sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
1231+ sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
1232+ sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
1233+ sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
1234+ sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
1235+ sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
1236+ sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
1237+ sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
1238+ sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
1239+ sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
1240+ sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
1241+ sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
1242+ sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
1243+ sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
1244+ sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
1245+ sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
1246+ sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
1247+ sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
1248+ sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
1249+ sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
1250+ sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
1251+
1252+ QCOMPARE(statusSpy->recognized(), true);
1253+
1254+ delete statusSpy;
1255+}
1256+
1257+void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
1258+{
1259+ m_view->resize(768, 1280);
1260+ QTest::qWait(300);
1261+
1262+ DirectionalDragArea *edgeDragArea =
1263+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1264+ QVERIFY(edgeDragArea != nullptr);
1265+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1266+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267+
1268+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269+
1270+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271+
1272+ sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273+ sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274+ sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275+ sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276+ sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277+ sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278+ sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279+ sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280+ sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281+ sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282+ sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283+ sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284+ sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285+ sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286+ sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287+ sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288+ sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289+ sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290+ sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291+ sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292+ sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293+ sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294+ sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295+ sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296+ sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297+ sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298+
1299+ QCOMPARE(statusSpy->recognized(), true);
1300+
1301+ delete statusSpy;
1302+}
1303+
12691304QTEST_MAIN(tst_DirectionalDragArea)
12701305
12711306#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+8−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -16,12 +16,19 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19+import Unity.Test 0.1
1920
2021Rectangle {
2122 width: units.gu(60)
2223 height: units.gu(60)
2324 color: "white"
2425
26+ Binding {
27+ target: MouseTouchAdaptor
28+ property: "enabled"
29+ value: true
30+ }
31+
2532 MouseArea {
2633 id: mouseArea
2734 objectName: "mouseArea"
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp+5−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -81,8 +81,7 @@ void tst_TouchDispatcher::sendMouseEventIfTouchIgnored()
8181 event->accept();
8282 };
8383
84− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view,
85− 12345 /* timestamp */);
84+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, 12345 /* timestamp */);
8685
8786 QVERIFY(gotTouchEvent);
8887 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
@@ -128,18 +127,18 @@ void tst_TouchDispatcher::mouseDoubleClick()
128127 }
129128 ulong timestamp = 12345;
130129
131− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
130+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
132131
133132 touchPoints[0].setState(Qt::TouchPointReleased);
134133 timestamp += doubleClickInterval / 10;
135134
136− touchDispatcher.dispatch(QEvent::TouchEnd, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
135+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
137136
138137 touchPoints[0].setId(1);
139138 touchPoints[0].setState(Qt::TouchPointPressed);
140139 timestamp += timeBetweenClicks;
141140
142− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
141+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
143142
144143 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
145144}
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+56−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -63,6 +63,7 @@ private Q_SLOTS:
6363 void init(); // called right before each and every test function is executed
6464 void cleanup(); // called right after each and every test function is executed
6565
66+private:
6667 void onlyCandidate_passThrough();
6768 void holdsEventsUntilGainsOwnership();
6869 void holdsEventsUntilGainsOwnership_data();
@@ -70,6 +71,9 @@ private Q_SLOTS:
7071 void dontDispacthToDisabledOrInvisibleTarget();
7172 void dontDispacthToDisabledOrInvisibleTarget_data();
7273
74+private Q_SLOTS:
75+ void disabledWhileHoldingTouch();
76+
7377private:
7478 QQuickView *createView();
7579 TouchRegistry *touchRegistry;
@@ -326,6 +330,57 @@ void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
326330 QTest::newRow("disabled invisible") << false << false;
327331}
328332
333+void tst_TouchGate::disabledWhileHoldingTouch()
334+{
335+ TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
336+ Q_ASSERT(touchGate);
337+
338+ TestItem *testItem = new TestItem;
339+ testItem->setWidth(touchGate->width());
340+ testItem->setHeight(touchGate->height());
341+ testItem->setParentItem(view->rootObject());
342+ testItem->setZ(0.0);
343+
344+ touchGate->setZ(1.0);
345+ touchGate->setTargetItem(testItem);
346+
347+ QTest::touchEvent(view, device)
348+ .press(0, QPoint(100,100));
349+
350+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
351+ testItem->touchEventsReceived.clear();
352+
353+ touchGate->setEnabled(false);
354+
355+ QTest::touchEvent(view, device)
356+ .move(0, QPoint(101,101));
357+
358+ // Nothing new came as TouchGate didn't even get it because it's disabled
359+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
360+
361+ touchGate->setEnabled(true);
362+
363+ QTest::touchEvent(view, device)
364+ .move(0, QPoint(102,102));
365+
366+ QTest::touchEvent(view, device)
367+ .release(0, QPoint(102,102));
368+
369+ // Nothing new came because TouchGate has already discarded touch 0
370+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
371+
372+ QTest::touchEvent(view, device)
373+ .press(1, QPoint(200,200));
374+
375+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
376+ {
377+ // it got only the new touch point.
378+ QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
379+ QCOMPARE(touchEvent->touchPoints().count(), 1);
380+ QCOMPARE(touchEvent->touchPoints()[0].id(), 1);
381+ }
382+}
383+
329384///////////// TestItem /////////////////////////////////////////////////////////////
330385
331386void TestItem::touchEvent(QTouchEvent *event)
tests/qmltests/Components/CMakeLists.txt+13−3
@@ -1,3 +1,7 @@
1+add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
2+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
3+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
4+
15########## tst_DragHandle
26
37include_directories(
@@ -5,6 +9,7 @@ include_directories(
59 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
610 ${CMAKE_CURRENT_BINARY_DIR}
711 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
12+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
813 )
914
1015add_executable(DragHandleTestExec tst_DragHandle.cpp)
@@ -12,7 +17,12 @@ qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
1217target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
1318
1419add_unity8_uitest(DragHandle DragHandleTestExec ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures)
15−add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
20+add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/utils/modules)
1621
17−add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
18−add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
22+
23+########## tst_EdgeDragEvaluator
24+add_executable(EdgeDragEvaluatorTestExec tst_EdgeDragEvaluator.cpp)
25+qt5_use_modules(EdgeDragEvaluatorTestExec Test Core Qml Gui Quick)
26+target_link_libraries(EdgeDragEvaluatorTestExec UbuntuGesturesQml UbuntuGestures)
27+
28+add_unity8_uitest(EdgeDragEvaluator EdgeDragEvaluatorTestExec ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures)
tests/qmltests/Components/tst_DragHandle.cpp+15−13
@@ -20,21 +20,16 @@
2020#include <qpa/qwindowsysteminterface.h>
2121#include <QtQuick/QQuickView>
2222#include <QtQml/QQmlEngine>
23+#include <private/qquickwindow_p.h>
2324
25+#include <AxisVelocityCalculator.h>
2426#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
2528#include <Timer.h>
2629#include <TouchRegistry.h>
2730
2831using namespace UbuntuGestures;
2932
30−class FakeTimeSource : public TimeSource
31−{
32−public:
33− FakeTimeSource() { m_msecsSinceReference = 0; }
34− qint64 msecsSinceReference() override {return m_msecsSinceReference;}
35− qint64 m_msecsSinceReference;
36−};
37−
3833class tst_DragHandle: public QObject
3934{
4035 Q_OBJECT
@@ -56,7 +51,6 @@ private Q_SLOTS:
5651 void hintingAnimation();
5752 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
5853
59−
6054private:
6155 void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
6256 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
@@ -105,8 +99,8 @@ void tst_DragHandle::init()
10599 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
106100 controls->setVisible(false);
107101
108− m_fakeTimer = new FakeTimer;
109102 m_fakeTimeSource.reset(new FakeTimeSource);
103+ m_fakeTimer = new FakeTimer(m_fakeTimeSource);
110104}
111105
112106void tst_DragHandle::cleanup()
@@ -129,6 +123,7 @@ QQuickView *tst_DragHandle::createView()
129123 window->setResizeMode(QQuickView::SizeRootObjectToView);
130124 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
131125 window->engine()->addImportPath(QLatin1String(TEST_DIR));
126+ window->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
132127
133128 return window;
134129}
@@ -190,17 +185,24 @@ void tst_DragHandle::flickAndHold(DirectionalDragArea *dragHandle,
190185 // speed.
191186 m_fakeTimeSource->m_msecsSinceReference += 5000;
192187 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
188+
189+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
190+ windowPrivate->flushDelayedTouchEvent();
193191}
194192
195193void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
196194 int numSteps, qint64 timeMs)
197195{
196+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
197+
198198 qint64 timeStep = timeMs / numSteps;
199199 QPointF touchMovement = direction * (distance / (qreal)numSteps);
200200 for (int i = 0; i < numSteps; ++i) {
201201 touchPoint += touchMovement;
202202 m_fakeTimeSource->m_msecsSinceReference += timeStep;
203203 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
204+
205+ windowPrivate->flushDelayedTouchEvent();
204206 }
205207}
206208
@@ -209,8 +211,8 @@ DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectN
209211 DirectionalDragArea *dragHandle =
210212 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
211213 Q_ASSERT(dragHandle != 0);
212− dragHandle->setRecognitionTimer(m_fakeTimer);
213− dragHandle->setTimeSource(m_fakeTimeSource);
214+ dragHandle->d->setRecognitionTimer(m_fakeTimer);
215+ dragHandle->d->setTimeSource(m_fakeTimeSource);
214216
215217 AxisVelocityCalculator *edgeDragEvaluator =
216218 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
@@ -242,6 +244,7 @@ void tst_DragHandle::dragThreshold_horizontal()
242244 baseItem->setRotation(rotation);
243245
244246 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
247+ QQuickItem *parentItem = dragHandle->parentItem();
245248
246249 qreal dragThreshold = fetchDragThreshold(dragHandle);
247250
@@ -249,7 +252,6 @@ void tst_DragHandle::dragThreshold_horizontal()
249252 flickAndHold(dragHandle, dragThreshold * 0.7);
250253
251254 // should rollback
252− QQuickItem *parentItem = dragHandle->parentItem();
253255 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
254256 QCOMPARE(parentItem->property("shown").toBool(), false);
255257
tests/qmltests/Components/tst_DragHandle.qml+7
@@ -19,6 +19,7 @@ import "../../../qml/Components"
1919import "tst_DragHandle"
2020import Ubuntu.Components 0.1
2121import Ubuntu.Gestures 0.1
22+import Unity.Test 0.1
2223
2324/*
2425 Two blue fullscreen Showables (a vertical and a horizontal one) with red handles you
@@ -33,6 +34,12 @@ Rectangle {
3334 width: units.gu(70)
3435 height: units.gu(70)
3536
37+ Binding {
38+ target: MouseTouchAdaptor
39+ property: "enabled"
40+ value: true
41+ }
42+
3643 property var dragHandle
3744
3845 property bool stretch: false
tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml+2−2
@@ -60,8 +60,8 @@ Item {
6060 maxTotalDragDistance: root.parent.width
6161 autoCompleteDragThreshold: parent.width / 3
6262
63− onStatusChanged: {
64− if (status === DirectionalDragArea.Recognized) {
63+ onDraggingChanged: {
64+ if (dragging) {
6565 dragHandleRecognizedGesture(dragHandle);
6666 }
6767 }
tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+4−4
@@ -65,8 +65,8 @@ Showable {
6565 maxTotalDragDistance: showable.parent.width
6666 hintDisplacement: showable.hintDisplacement
6767
68− onStatusChanged: {
69− if (status === DirectionalDragArea.Recognized) {
68+ onDraggingChanged: {
69+ if (dragging) {
7070 dragHandleRecognizedGesture(leftwardsDragHandle);
7171 }
7272 }
@@ -88,8 +88,8 @@ Showable {
8888 maxTotalDragDistance: showable.parent.width
8989 hintDisplacement: showable.hintDisplacement
9090
91− onStatusChanged: {
92− if (status === DirectionalDragArea.Recognized) {
91+ onDraggingChanged: {
92+ if (dragging) {
9393 dragHandleRecognizedGesture(rightwardsDragHandle);
9494 }
9595 }
tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+4−4
@@ -65,8 +65,8 @@ Showable {
6565 maxTotalDragDistance: showable.parent.height
6666 hintDisplacement: showable.hintDisplacement
6767
68− onStatusChanged: {
69− if (status === DirectionalDragArea.Recognized) {
68+ onDraggingChanged: {
69+ if (dragging) {
7070 dragHandleRecognizedGesture(upwardsDragHandle);
7171 }
7272 }
@@ -88,8 +88,8 @@ Showable {
8888 maxTotalDragDistance: showable.parent.height
8989 hintDisplacement: showable.hintDisplacement
9090
91− onStatusChanged: {
92− if (status === DirectionalDragArea.Recognized) {
91+ onDraggingChanged: {
92+ if (dragging) {
9393 dragHandleRecognizedGesture(downwardsDragHandle);
9494 }
9595 }
tests/qmltests/Components/tst_EdgeDragEvaluator.cppnew file+165
@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <functional>
18+#include <QtTest>
19+#include <QObject>
20+#include <QQuickView>
21+#include <QQmlEngine>
22+#include <QQuickItem>
23+
24+// Ubuntu.Gestures plugin
25+#include <AxisVelocityCalculator.h>
26+#include <Direction.h>
27+#include <Timer.h>
28+
29+using namespace UbuntuGestures;
30+
31+class tst_EdgeDragEvaluator: public QObject
32+{
33+ Q_OBJECT
34+public:
35+ tst_EdgeDragEvaluator() {}
36+private Q_SLOTS:
37+ void initTestCase(); // will be called before the first test function is executed
38+ void cleanupTestCase(); // will be called after the last test function was executed.
39+
40+ void init(); // called right before each and every test function is executed
41+ void cleanup(); // called right after each and every test function is executed
42+
43+ void horizontalDirection();
44+ void horizontalDirection_data();
45+
46+private:
47+ void drag(qreal &pos, qreal distance, qreal speed);
48+
49+ QQuickView *createView();
50+ QQuickView *m_view;
51+ FakeTimer *m_fakeTimer;
52+ QSharedPointer<FakeTimeSource> m_fakeTimeSource;
53+ AxisVelocityCalculator *m_dragEvaluator;
54+};
55+
56+
57+void tst_EdgeDragEvaluator::initTestCase()
58+{
59+ m_view = 0;
60+}
61+
62+void tst_EdgeDragEvaluator::cleanupTestCase()
63+{
64+}
65+
66+void tst_EdgeDragEvaluator::init()
67+{
68+
69+ m_view = createView();
70+ m_view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_EdgeDragEvaluator.qml"));
71+ m_view->show();
72+ QVERIFY(QTest::qWaitForWindowExposed(m_view));
73+ QVERIFY(m_view->rootObject() != 0);
74+ qApp->processEvents();
75+
76+ m_fakeTimeSource.reset(new FakeTimeSource);
77+ m_fakeTimer = new FakeTimer(m_fakeTimeSource);
78+
79+ m_dragEvaluator =
80+ m_view->rootObject()->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
81+ QVERIFY(m_dragEvaluator);
82+ m_dragEvaluator->setTimeSource(m_fakeTimeSource);
83+}
84+
85+void tst_EdgeDragEvaluator::cleanup()
86+{
87+ m_dragEvaluator = nullptr;
88+
89+ delete m_view;
90+ m_view = 0;
91+
92+ delete m_fakeTimer;
93+ m_fakeTimer = 0;
94+
95+ m_fakeTimeSource.reset();
96+}
97+
98+QQuickView *tst_EdgeDragEvaluator::createView()
99+{
100+ QQuickView *window = new QQuickView(0);
101+ window->setResizeMode(QQuickView::SizeRootObjectToView);
102+ window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
103+ window->engine()->addImportPath(QLatin1String(TEST_DIR));
104+
105+ return window;
106+}
107+
108+void tst_EdgeDragEvaluator::drag(qreal &pos, qreal distance, qreal speed)
109+{
110+ int numSteps = 10;
111+ qreal distanceStep = distance / ((qreal)numSteps);
112+ int timeStep = qAbs(distanceStep / speed);
113+
114+ for (int i = 0; i < numSteps; ++i) {
115+ m_fakeTimeSource->m_msecsSinceReference += timeStep;
116+ pos += distanceStep;
117+ m_dragEvaluator->setTrackedPosition(pos);
118+ }
119+}
120+
121+void tst_EdgeDragEvaluator::horizontalDirection()
122+{
123+ qreal maxDragDistance = 100.;
124+ qreal speedThreshold = 100.;
125+ qreal speedThresholdMs = speedThreshold / 1000.;
126+ QFETCH(qreal, dragDirection);
127+
128+ m_dragEvaluator->setProperty("maxDragDistance", QVariant::fromValue(maxDragDistance));
129+ m_dragEvaluator->setProperty("dragThreshold", QVariant::fromValue(maxDragDistance * 0.5));
130+ m_dragEvaluator->setProperty("speedThreshold", QVariant::fromValue(speedThreshold));
131+
132+ // If direction is "Horizontal" it means it can drag leftwards or rightwards
133+ m_dragEvaluator->setProperty("direction", QVariant::fromValue((int)Direction::Horizontal));
134+
135+ qreal pos = 0.;
136+ m_dragEvaluator->setTrackedPosition(pos);
137+
138+ // Go far beyond dragThreshold
139+ drag(pos, maxDragDistance * 0.75 * dragDirection /*distance*/, speedThresholdMs * 0.5 /*speed*/);
140+
141+ // Then come back a bit, getting close to dragThreshold
142+ drag(pos, -maxDragDistance * 0.20 * dragDirection /*distance*/, speedThresholdMs * 0.5 /*speed*/);
143+
144+ QVariant shouldAutoComplete;
145+ bool ok = QMetaObject::invokeMethod(m_dragEvaluator, "shouldAutoComplete", Qt::DirectConnection,
146+ Q_RETURN_ARG(QVariant, shouldAutoComplete));
147+ QVERIFY(ok);
148+
149+ // It should not autocomplete because the current negative velocity (speedThresholdMs * 0.5)
150+ // is bigger (in absolute terms) than the minimum negative velocity at this position needed to
151+ // cancel autocompletion (speedThreshold * 0.1)
152+ QCOMPARE(shouldAutoComplete.toBool(), false);
153+}
154+
155+void tst_EdgeDragEvaluator::horizontalDirection_data()
156+{
157+ QTest::addColumn<qreal>("dragDirection");
158+
159+ QTest::newRow("right") << 1.;
160+ QTest::newRow("left") << -1.;
161+}
162+
163+QTEST_MAIN(tst_EdgeDragEvaluator)
164+
165+#include "tst_EdgeDragEvaluator.moc"
tests/qmltests/Components/tst_EdgeDragEvaluator.qmlnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import "../../../qml/Components"
19+
20+Item {
21+ width: units.gu(70)
22+ height: units.gu(70)
23+
24+ EdgeDragEvaluator {
25+ objectName: "edgeDragEvaluator"
26+ }
27+}
tests/qmltests/Stages/tst_PhoneStage.qml+8−8
@@ -164,14 +164,14 @@ Item {
164164
165165 function test_enterSpread_data() {
166166 return [
167− {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
168− {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
169− {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
170− {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
171− {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
172− {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
173− {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
174− {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
167+ {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
168+ {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
169+ {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +5, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
170+ {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
171+ {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
172+ {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +5, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
173+ {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: 0, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
174+ {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +5, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
175175 ];
176176 }
177177
tests/qmltests/Tutorial/tst_Tutorial.qml+5−1
@@ -235,7 +235,11 @@ Item {
235235 checkLeftEdge();
236236 checkBottomEdge();
237237 if (name === "tutorialRight") return page;
238− touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
238+ touchFlick(shell,
239+ shell.width, halfHeight,
240+ halfWidth, halfHeight,
241+ true /* beginTouch */, true /* endTouch */,
242+ 20 /* speed */, 50 /* iterations */);
239243 var overlay = findChild(page, "overlay");
240244 tryCompare(overlay, "shown", true);
241245 var tick = findChild(page, "tick");
tests/qmltests/tst_Shell.qml+14−10
@@ -373,11 +373,11 @@ Rectangle {
373373 function test_phoneLeftEdgeDrag_data() {
374374 return [
375375 {tag: "without launcher",
376− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
376+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
377377 launcherHides: true, greeterShown: false},
378378
379379 {tag: "with launcher",
380− revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
380+ revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
381381 launcherHides: true, greeterShown: false},
382382
383383 {tag: "small swipe",
@@ -385,7 +385,7 @@ Rectangle {
385385 launcherHides: false, greeterShown: false},
386386
387387 {tag: "long swipe",
388− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
388+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
389389 launcherHides: true, greeterShown: false},
390390
391391 {tag: "small swipe with greeter",
@@ -393,11 +393,11 @@ Rectangle {
393393 launcherHides: false, greeterShown: true},
394394
395395 {tag: "long swipe with greeter",
396− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
396+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
397397 launcherHides: true, greeterShown: true},
398398
399399 {tag: "swipe over dash",
400− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash",
400+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
401401 launcherHides: false, greeterShown: false},
402402 ];
403403 }
@@ -602,7 +602,7 @@ Rectangle {
602602
603603 dashCommunicatorSpy.clear();
604604 // Minimize the application we just launched
605− swipeFromLeftEdge(units.gu(27));
605+ swipeFromLeftEdge(shell.width * 0.75);
606606
607607 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
608608
@@ -724,7 +724,8 @@ Rectangle {
724724 }
725725
726726 function dragLauncherIntoView() {
727− var launcherPanel = findChild(shell, "launcherPanel");
727+ var launcher = findChild(shell, "launcher");
728+ var launcherPanel = findChild(launcher, "launcherPanel");
728729 verify(launcherPanel.x = - launcherPanel.width);
729730
730731 var touchStartX = 2;
@@ -732,6 +733,7 @@ Rectangle {
732733 touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
733734
734735 tryCompare(launcherPanel, "x", 0);
736+ tryCompare(launcher, "state", "visible");
735737 }
736738
737739 function tapOnAppIconInLauncher() {
@@ -768,7 +770,9 @@ Rectangle {
768770 function swipeFromLeftEdge(swipeLength) {
769771 var touchStartX = 2;
770772 var touchStartY = shell.height / 2;
771− touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
773+ touchFlick(shell,
774+ touchStartX , touchStartY,
775+ touchStartX + swipeLength, touchStartY);
772776 }
773777
774778 function itemIsOnScreen(item) {
@@ -835,7 +839,7 @@ Rectangle {
835839 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
836840
837841 // Minimize the application we just launched
838− swipeFromLeftEdge(units.gu(26) + 1);
842+ swipeFromLeftEdge(shell.width * 0.75);
839843
840844 waitUntilDashIsFocused();
841845
@@ -917,7 +921,7 @@ Rectangle {
917921
918922 compare(panel.fullscreenMode, true);
919923
920− touchFlick(shell, units.gu(2), touchStartY, units.gu(10), touchStartY, false, false);
924+ touchFlick(shell, units.gu(2), touchStartY, shell.width * 0.5, touchStartY, false, false);
921925
922926 tryCompare(panel, "fullscreenMode", false);
923927
tests/qmltests/tst_ShellWithPin.qml+2−2
@@ -612,14 +612,14 @@ Item {
612612 tryCompare(coverPage, "showProgress", 1);
613613
614614 // Swipe cover page away
615− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
615+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
616616 tryCompare(launcher, "x", -launcher.width);
617617 tryCompare(coverPage, "showProgress", 0);
618618 compare(lockscreen.shown, true);
619619 compare(ApplicationManager.focusedApplicationId, "gallery-app");
620620
621621 // Now attempt a swipe on lockscreen
622− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
622+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
623623 tryCompare(launcher, "x", 0);
624624 compare(lockscreen.shown, true);
625625 compare(ApplicationManager.focusedApplicationId, "gallery-app");
tests/utils/modules/Unity/Test/UnityTestCase.qml+3−7
@@ -269,7 +269,7 @@ TestCase {
269269 speed = (speed !== undefined) ? speed : units.gu(10)
270270
271271 // Set a default iterations if not specified
272− var iterations = (iterations !== undefined) ? iterations : 5
272+ var iterations = (iterations !== undefined) ? iterations : 10
273273
274274 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
275275 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
@@ -403,12 +403,8 @@ TestCase {
403403 function removeTimeConstraintsFromDirectionalDragAreas(item) {
404404
405405 // use duck-typing to identify a DirectionalDragArea
406− if (item.minSpeed != undefined
407− && item.maxSilenceTime != undefined
408− && item.compositionTime != undefined) {
409− item.minSpeed = 0;
410− item.maxSilenceTime = 60 * 60 * 1000;
411− item.compositionTime = 0;
406+ if (item.removeTimeConstraints != undefined) {
407+ item.removeTimeConstraints();
412408 } else {
413409 for (var i in item.children) {
414410 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);