on xenial help sign in
Merged with parent branch again and fixed conflicts.
commit78d490bd6680ea77bd81f2ef27bcf151a0759983 authorMirco Müller <mirco.mueller@ubuntu.com> dateMon 11 May 2015, 16:36 (11 years ago) parent773adbdbf5e0ed
95 files changed+3695−1895
cmake/modules/QmlTest.cmake+10−8 data/unity8-dash.conf−5 data/unity8.conf+1 debian/changelog+30−12 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/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/be.po+46−46 po/br.po+4−4 po/ca.po+25−25 po/da.po+53−51 po/en_GB.po+3−3 po/es.po+4−4 po/fr.po+4−4 po/gd.po+4−4 po/hu.po+13−13 po/ia.po+673 po/nl.po+4−4 po/pa.po+3−3 po/pl.po+10−10 po/pt_BR.po+9−9 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/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/Rotation/RotationStates.qml+7 qml/Shell.qml+2−1 qml/Stages/PhoneStage.qml+31−43 qml/Stages/SpreadDelegate.qml+15−7 qml/Stages/TabletStage.qml+19−24 qml/Stages/TransformedSpreadDelegate.qml+2−16 qml/Tutorial/TutorialBottom.qml+1−1 runtests.sh+3 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1 tests/autopilot/unity8/fixture_setup.py+66−1 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/listviewwithpageheadertest.cpp+8−2 tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−1 tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19 tests/plugins/Ubuntu/Gestures/GestureTest.cpp+13−3 tests/plugins/Ubuntu/Gestures/GestureTest.h+4 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+19−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_OrientedShell.qml+59−5 tests/qmltests/tst_Shell.qml+14−10 tests/qmltests/tst_ShellWithPin.qml+2−2 tests/utils/modules/Unity/Test/UnityTestCase.qml+45−7
cmake/modules/QmlTest.cmake+10−8
@@ -155,10 +155,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
155155 set(testCommand
156156 ${ENVVAR}
157157 LD_LIBRARY_PATH=${LD_PATH}
158− dbus-launch
159− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
160− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
161− -o -,txt)
158+ dbus-test-runner
159+ -m 300
160+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
161+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
162+ -p -o -p -,txt)
162163
163164 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
164165 add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
@@ -173,10 +174,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
173174 ${ENVVAR}
174175 LD_LIBRARY_PATH=${LD_PATH}
175176 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
176− dbus-launch
177− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
178− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
179− -o -,txt)
177+ dbus-test-runner
178+ -m 300
179+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
180+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
181+ -p -o -p -,txt)
180182
181183 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
182184 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
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+30−12
@@ -4,24 +4,42 @@ unity8 (8.10-0ubuntu1) UNRELEASED; urgency=medium
44
55 -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 15 Apr 2015 14:47:17 +0200
66
7−unity8 (8.02+15.04.20150423-0ubuntu1) vivid; urgency=medium
7+unity8 (8.02+15.04.20150505-0ubuntu1) vivid; urgency=medium
88
9− [ CI Train Bot ]
10− * New rebuild forced.
9+ [ Albert Astals Cid ]
10+ * Make runtests fake a test error if make fails
11+ * Make the test more stable
12+ * Use dbus-test-runner instead of dbus-launch
1113
1214 [ Daniel d'Andrada ]
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
15+ * DirectionalDragArea: improvements & API grooming (LP: #1417920)
16+ * Fix EdgeDragEvaluator when a drag can happen both ways
17+ (Direction.Horizontal)
18+
19+ [ Josh Arenson ]
20+ * Remove panelHeight property as it is unused.
21+
22+ [ Leo Arias ]
23+ * Initial clean up of the autopilot tests set up. Removed the touch
24+ device from the test case. Moved the restart of unity to a fixture.
25+ Removed the unused DragMixin. Updated the setUpClass to use
26+ process_helpers. Removed the workaround for bug #1238417, already
27+ fixed. Use the toolkit helper to set the testability environment
28+ variable. Fixed the indicators base class that was restarting unity
29+ twice. (LP: #1238417, #1447206)
30+ * Use the base class from the toolkit in autopilot tests.
31+
32+ [ Michael Zanetti ]
33+ * emit application-stop when we're going down (LP: #1326513)
1734
1835 [ Michał Sawicz ]
19− * Tapping home key shows unity8-dash home added:
20− plugins/Utils/ElapsedTimer.h plugins/Utils/HomeKeyWatcher.cpp
21− plugins/Utils/HomeKeyWatcher.h plugins/Utils/Timer.cpp
22− plugins/Utils/Timer.h tests/plugins/Utils/homekeywatchertest.cpp
36+ * UNITY_SCOPES_LIST is no more
37+
38+ [ handsome_feng<445865575@qq.com> ]
39+ * When click the favorite scope in Dash Manager , it just return to
40+ the corresponding scope page. (LP: #1447056)
2341
24− -- CI Train Bot <ci-train-bot@canonical.com> Thu, 23 Apr 2015 12:52:51 +0000
42+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 05 May 2015 14:47:24 +0000
2543
2644unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
2745
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/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/be.po+46−46
@@ -8,16 +8,16 @@ 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-02-07 19:04+0000\n"
12−"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
11+"PO-Revision-Date: 2015-04-27 14:34+0000\n"
12+"Last-Translator: Arsień Šachalevič <semen.shah@gmail.com>\n"
1313"Language-Team: Belarusian <be@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%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || 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-04-28 05:33+0000\n"
20+"X-Generator: Launchpad (build 17453)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -26,11 +26,11 @@ msgstr "Пароль: "
2626#: plugins/Unity/Launcher/launcheritem.cpp:45
2727#: plugins/Unity/Launcher/launcheritem.cpp:100
2828msgid "Pin shortcut"
29−msgstr "Заблакаваць кнопку"
29+msgstr "Замацаваць кнопку"
3030
3131#: plugins/Unity/Launcher/launcheritem.cpp:100
3232msgid "Unpin shortcut"
33−msgstr "Разблакаваць кнопку"
33+msgstr "Адмацаваць кнопку"
3434
3535#: qml/Components/DelayedLockscreen.qml:49
3636msgid "Device Locked"
@@ -58,21 +58,21 @@ msgstr[2] "Пачакайце %1 хвілінаў і паўтарыце спро
5858#: qml/Components/Dialogs.qml:69
5959msgctxt "Title: Lock/Log out dialog"
6060msgid "Log out"
61−msgstr ""
61+msgstr "Выйсці"
6262
6363#: qml/Components/Dialogs.qml:70
6464msgid "Are you sure you want to log out?"
65−msgstr "Вы насамрэч хочаце выйсці?"
65+msgstr "Вы ўпэўнены, што хочаце выйсці?"
6666
6767#: qml/Components/Dialogs.qml:72
6868msgctxt "Button: Lock the system"
6969msgid "Lock"
70−msgstr ""
70+msgstr "Заблакаваць"
7171
7272#: qml/Components/Dialogs.qml:79
7373msgctxt "Button: Log out from the system"
7474msgid "Log Out"
75−msgstr ""
75+msgstr "Выйсці"
7676
7777#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7878#: qml/Components/Dialogs.qml:173
@@ -82,7 +82,7 @@ msgstr "Скасаваць"
8282#: qml/Components/Dialogs.qml:98
8383msgctxt "Title: Reboot/Shut down dialog"
8484msgid "Shut down"
85−msgstr ""
85+msgstr "Выключыць"
8686
8787#: qml/Components/Dialogs.qml:99
8888msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Вы ўпэўненыя, што жадаеце выключыць пры
9191#: qml/Components/Dialogs.qml:101
9292msgctxt "Button: Reboot the system"
9393msgid "Reboot"
94−msgstr ""
94+msgstr "Перазагрузіць"
9595
9696#: qml/Components/Dialogs.qml:109
9797msgctxt "Button: Shut down the system"
9898msgid "Shut down"
99−msgstr ""
99+msgstr "Выключыць"
100100
101101#: qml/Components/Dialogs.qml:129
102102msgctxt "Title: Reboot dialog"
103103msgid "Reboot"
104−msgstr ""
104+msgstr "Перазагрузіць"
105105
106106#: qml/Components/Dialogs.qml:130
107107msgid "Are you sure you want to reboot?"
@@ -118,7 +118,7 @@ msgstr "Так"
118118#: qml/Components/Dialogs.qml:152
119119msgctxt "Title: Power off/Restart dialog"
120120msgid "Power"
121−msgstr ""
121+msgstr "Сілкаванне"
122122
123123#: qml/Components/Dialogs.qml:153
124124msgid ""
@@ -131,12 +131,12 @@ msgstr ""
131131#: qml/Components/Dialogs.qml:155
132132msgctxt "Button: Power off the system"
133133msgid "Power off"
134−msgstr ""
134+msgstr "Выключыць"
135135
136136#: qml/Components/Dialogs.qml:164
137137msgctxt "Button: Restart the system"
138138msgid "Restart"
139−msgstr ""
139+msgstr "Перазапуск"
140140
141141#: qml/Components/Lockscreen.qml:245
142142msgid "Return to Call"
@@ -161,22 +161,22 @@ msgstr "Больш падрабязна"
161161#: qml/Dash/GenericScopeView.qml:594
162162msgctxt "Label: Hint for dash search line edit"
163163msgid "Search"
164−msgstr ""
164+msgstr "Пошук"
165165
166166#: qml/Dash/PageHeader.qml:269
167167msgctxt "Button: Open the Ubuntu Store"
168168msgid "Store"
169−msgstr ""
169+msgstr "Крама"
170170
171171#: qml/Dash/PageHeader.qml:276
172172msgctxt "Button: Start a search in the current dash scope"
173173msgid "Search"
174−msgstr ""
174+msgstr "Пошук"
175175
176176#: qml/Dash/PageHeader.qml:286
177177msgctxt "Button: Show the current dash scope settings"
178178msgid "Settings"
179−msgstr ""
179+msgstr "Налады"
180180
181181#: qml/Dash/PageHeader.qml:293
182182msgid "Remove from Favorites"
@@ -216,7 +216,7 @@ msgstr "Адпусціце для абнаўлення ..."
216216
217217#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
218218msgid "Enable location data"
219−msgstr ""
219+msgstr "Уключыць дадзеныя аб месцазнаходжанні"
220220
221221#: qml/Dash/ScopesList.qml:66
222222msgid "Manage"
@@ -236,11 +236,11 @@ msgstr "Разблакаваць"
236236
237237#: qml/Greeter/LoginList.qml:236
238238msgid "Retry"
239−msgstr ""
239+msgstr "Паспрабаваць ізноў"
240240
241241#: qml/Greeter/LoginList.qml:237
242242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Націсніце, каб разблакаваць"
244244
245245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246246msgid "Sorry, incorrect passphrase."
@@ -343,7 +343,7 @@ msgstr "Памылка, націсніце для паўтору"
343343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344344msgctxt "Label: Hint in message indicator line edit"
345345msgid "Reply"
346−msgstr ""
346+msgstr "Адказаць"
347347
348348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349349msgid "Unlock SIM"
@@ -355,65 +355,65 @@ msgstr "Роўмінг"
355355
356356#: qml/Tutorial/TutorialBottomFinish.qml:24
357357msgid "This action does different things for different apps"
358−msgstr ""
358+msgstr "Гэта дзеянне мае розныя наступствы для розных праграм"
359359
360360#: qml/Tutorial/TutorialBottomFinish.qml:25
361361msgid "Tap here to finish."
362−msgstr ""
362+msgstr "Націсніце тут, каб завяршыць."
363363
364364#: qml/Tutorial/TutorialBottom.qml:28
365365msgid "Open special menus"
366−msgstr ""
366+msgstr "Адкрыць спецыяльныя меню"
367367
368368#: qml/Tutorial/TutorialBottom.qml:29
369369msgid "Swipe up from the bottom edge."
370−msgstr ""
370+msgstr "Правесці уверх ад ніжняга краю."
371371
372372#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
373373#: qml/Tutorial/TutorialRight.qml:144
374374msgid "Tap here to continue."
375−msgstr ""
375+msgstr "Націсніце тут, каб працягнуць."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Націсніце тут для завяршэння."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
383−msgstr ""
383+msgstr "Гэта скарачэнні для абраных праграм"
384384
385385#: qml/Tutorial/TutorialLeft.qml:26
386386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Адкрыць панэль запуску"
388388
389389#: qml/Tutorial/TutorialLeft.qml:27
390390msgid "Short swipe from the left edge."
391−msgstr ""
391+msgstr "Кароткі рух з левага краю."
392392
393393#: qml/Tutorial/TutorialPage.qml:178
394394msgid "You almost got it!"
395−msgstr ""
395+msgstr "Амаль усё!"
396396
397397#: qml/Tutorial/TutorialPage.qml:195
398398msgid "Try again."
399−msgstr ""
399+msgstr "Паспрабуйце яшчэ."
400400
401401#: qml/Tutorial/TutorialRight.qml:31
402402msgid "To view open apps"
403−msgstr ""
403+msgstr "Паглядзець адкрытыя праграмы"
404404
405405#: qml/Tutorial/TutorialRight.qml:32
406406msgid "Long swipe from the right edge."
407−msgstr ""
407+msgstr "Доўгі рух ад правага краю."
408408
409409#: qml/Tutorial/TutorialRight.qml:122
410410msgid "View all your running tasks."
411−msgstr ""
411+msgstr "Паказаць усе запушчаныя задачы."
412412
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Вярнуцца"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Калі ласка, абярыце спосаб разблакаван
457457#: qml/Wizard/Pages/30-passwd-type.qml:97
458458msgctxt "Label: Type of security method"
459459msgid "Swipe"
460−msgstr ""
460+msgstr "Рух"
461461
462462#: qml/Wizard/Pages/30-passwd-type.qml:98
463463msgctxt "Label: Description of security method"
464464msgid "No security"
465−msgstr ""
465+msgstr "Без бяспекі"
466466
467467#: qml/Wizard/Pages/30-passwd-type.qml:100
468468msgctxt "Label: Type of security method"
469469msgid "Passcode"
470−msgstr ""
470+msgstr "Пароль"
471471
472472#: qml/Wizard/Pages/30-passwd-type.qml:101
473473msgctxt "Label: Description of security method"
474474msgid "4 digits only"
475−msgstr ""
475+msgstr "Толькі 4 лічбы"
476476
477477#: qml/Wizard/Pages/30-passwd-type.qml:103
478478msgctxt "Label: Type of security method"
479479msgid "Passphrase"
480−msgstr ""
480+msgstr "Пароль"
481481
482482#: qml/Wizard/Pages/30-passwd-type.qml:104
483483msgctxt "Label: Description of security method"
484484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Літары і лічбы"
486486
487487#: qml/Wizard/Pages/40-wifi.qml:29
488488msgid "Connect to Wi‑Fi"
po/br.po+4−4
@@ -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-03-25 07:36+0000\n"
11+"PO-Revision-Date: 2015-04-27 07:03+0000\n"
1212"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
1313"Language-Team: Breton <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-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-04-28 05:33+0000\n"
19+"X-Generator: Launchpad (build 17453)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -375,7 +375,7 @@ msgstr "Pouzañ amañ evit kenderc'hel"
375375
376376#: qml/Tutorial/TutorialContent.qml:81
377377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Klikit amañ evit echuiñ."
379379
380380#: qml/Tutorial/TutorialLeftFinish.qml:24
381381msgid "These are the shortcuts to favorite apps"
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/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/en_GB.po+3−3
@@ -8,14 +8,14 @@ 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-01-22 12:14+0000\n"
11+"PO-Revision-Date: 2015-04-23 12:59+0000\n"
1212"Last-Translator: Andi Chandler <Unknown>\n"
1313"Language-Team: English (United Kingdom) <en_GB@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"
18+"X-Launchpad-Export-Date: 2015-04-24 05:39+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "Tap here to continue."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Click here to finish."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/es.po+4−4
@@ -8,14 +8,14 @@ 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-27 21:24+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-24 17:45+0000\n"
12+"Last-Translator: Paco Molinero <paco@byasl.com>\n"
1313"Language-Team: Spanish <es@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"
18+"X-Launchpad-Export-Date: 2015-04-25 05:38+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -375,7 +375,7 @@ msgstr "Toque aquí para continuar."
375375
376376#: qml/Tutorial/TutorialContent.qml:81
377377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Pulse aquí para finalizar."
379379
380380#: qml/Tutorial/TutorialLeftFinish.qml:24
381381msgid "These are the shortcuts to favorite apps"
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+13−13
@@ -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-25 20:55+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-23 06:48+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"
@@ -354,7 +354,7 @@ msgstr "Ez a művelet másként viselkedhet a különböző alkalmazásoknál"
354354
355355#: qml/Tutorial/TutorialBottomFinish.qml:25
356356msgid "Tap here to finish."
357−msgstr "Koppintson a befejezéshez."
357+msgstr "Koppintson ide a befejezéshez."
358358
359359#: qml/Tutorial/TutorialBottom.qml:28
360360msgid "Open special menus"
@@ -371,11 +371,11 @@ msgstr "Koppintson ide a folytatáshoz."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Kattintson ide a befejezéshez."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
378−msgstr "Ezek a kedvenc alkalmazások gyorsít-indítói"
378+msgstr "Ezek a kedvenc alkalmazások gyorsindítói"
379379
380380#: qml/Tutorial/TutorialLeft.qml:26
381381msgid "Open the launcher"
@@ -395,7 +395,7 @@ msgstr "Próbálja újra."
395395
396396#: qml/Tutorial/TutorialRight.qml:31
397397msgid "To view open apps"
398−msgstr "Nyitott alkalmazások megtekintéséhez"
398+msgstr "A nyitott alkalmazások megtekintéséhez"
399399
400400#: qml/Tutorial/TutorialRight.qml:32
401401msgid "Long swipe from the right edge."
@@ -403,7 +403,7 @@ msgstr "Húzza az ujját hosszan a kijelző jobboldalától indulva."
403403
404404#: qml/Tutorial/TutorialRight.qml:122
405405msgid "View all your running tasks."
406−msgstr "Minden futó feladat megtekintése."
406+msgstr "Az összes futó alkalmazás megtekinthető."
407407
408408#: qml/Wizard/Page.qml:89
409409msgctxt "Button: Go back one page in the Wizard"
po/ia.ponew file+673
@@ -0,0 +1,673 @@
1+# Interlingua 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-04-27 10:07+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Interlingua <ia@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-04-28 05:33+0000\n"
19+"X-Generator: Launchpad (build 17453)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Contrasigno: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr "Via-breve a pin"
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr "Via-breve levar pin"
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr "Dispositivo blocate"
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+"Tu ha essite blocate per multe tentativas de phrase-contrasigno fallite."
42+
43+#: qml/Components/DelayedLockscreen.qml:65
44+msgid "You have been locked out due to too many failed passcode attempts."
45+msgstr ""
46+"Tu ha essite blocate per multe tentativas de codice-contrasigno fallite."
47+
48+#: qml/Components/DelayedLockscreen.qml:74
49+#, qt-format
50+msgid "Please wait %1 minute and then try again…"
51+msgid_plural "Please wait %1 minutes and then try again…"
52+msgstr[0] "Per favor attende %1 minuta e postea tentar de nove"
53+msgstr[1] "Per favor attende %1 minutas e postea tentar de nove"
54+
55+#: qml/Components/Dialogs.qml:69
56+msgctxt "Title: Lock/Log out dialog"
57+msgid "Log out"
58+msgstr ""
59+
60+#: qml/Components/Dialogs.qml:70
61+msgid "Are you sure you want to log out?"
62+msgstr "Desira tu vermente Clauder le Session"
63+
64+#: qml/Components/Dialogs.qml:72
65+msgctxt "Button: Lock the system"
66+msgid "Lock"
67+msgstr ""
68+
69+#: qml/Components/Dialogs.qml:79
70+msgctxt "Button: Log out from the system"
71+msgid "Log Out"
72+msgstr ""
73+
74+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
75+#: qml/Components/Dialogs.qml:173
76+msgid "Cancel"
77+msgstr "Cancellar"
78+
79+#: qml/Components/Dialogs.qml:98
80+msgctxt "Title: Reboot/Shut down dialog"
81+msgid "Shut down"
82+msgstr ""
83+
84+#: qml/Components/Dialogs.qml:99
85+msgid "Are you sure you want to shut down?"
86+msgstr "Desira tu vermente Stoppar"
87+
88+#: qml/Components/Dialogs.qml:101
89+msgctxt "Button: Reboot the system"
90+msgid "Reboot"
91+msgstr ""
92+
93+#: qml/Components/Dialogs.qml:109
94+msgctxt "Button: Shut down the system"
95+msgid "Shut down"
96+msgstr ""
97+
98+#: qml/Components/Dialogs.qml:129
99+msgctxt "Title: Reboot dialog"
100+msgid "Reboot"
101+msgstr ""
102+
103+#: qml/Components/Dialogs.qml:130
104+msgid "Are you sure you want to reboot?"
105+msgstr "Desira tu vermente Reinitiar"
106+
107+#: qml/Components/Dialogs.qml:132
108+msgid "No"
109+msgstr "No"
110+
111+#: qml/Components/Dialogs.qml:138
112+msgid "Yes"
113+msgstr "Si"
114+
115+#: qml/Components/Dialogs.qml:152
116+msgctxt "Title: Power off/Restart dialog"
117+msgid "Power"
118+msgstr ""
119+
120+#: qml/Components/Dialogs.qml:153
121+msgid ""
122+"Are you sure you would like\n"
123+"to power off?"
124+msgstr ""
125+"Desira tu vermente \n"
126+"Stoppar le energia?"
127+
128+#: qml/Components/Dialogs.qml:155
129+msgctxt "Button: Power off the system"
130+msgid "Power off"
131+msgstr ""
132+
133+#: qml/Components/Dialogs.qml:164
134+msgctxt "Button: Restart the system"
135+msgid "Restart"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Return to Call"
140+msgstr "Retornar al appello"
141+
142+#: qml/Components/Lockscreen.qml:245
143+msgid "Emergency Call"
144+msgstr "Appello de emergentia"
145+
146+#: qml/Components/Lockscreen.qml:277
147+msgid "OK"
148+msgstr "OK"
149+
150+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
151+msgid "See less"
152+msgstr "Vider minus"
153+
154+#: qml/Dash/GenericScopeView.qml:532
155+msgid "See all"
156+msgstr "Vider toto"
157+
158+#: qml/Dash/GenericScopeView.qml:594
159+msgctxt "Label: Hint for dash search line edit"
160+msgid "Search"
161+msgstr ""
162+
163+#: qml/Dash/PageHeader.qml:269
164+msgctxt "Button: Open the Ubuntu Store"
165+msgid "Store"
166+msgstr ""
167+
168+#: qml/Dash/PageHeader.qml:276
169+msgctxt "Button: Start a search in the current dash scope"
170+msgid "Search"
171+msgstr ""
172+
173+#: qml/Dash/PageHeader.qml:286
174+msgctxt "Button: Show the current dash scope settings"
175+msgid "Settings"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Remove from Favorites"
180+msgstr "Remover ex le Favoritos"
181+
182+#: qml/Dash/PageHeader.qml:293
183+msgid "Add to Favorites"
184+msgstr "Adder al Favoritos"
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "More..."
188+msgstr "Plus..."
189+
190+#: qml/Dash/Previews/PreviewActionCombo.qml:35
191+msgid "Less..."
192+msgstr "Minus..."
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:81
195+msgid "Rate this"
196+msgstr "Evalutar isto"
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:126
199+msgid "Add a review"
200+msgstr "Adder un revision"
201+
202+#: qml/Dash/Previews/PreviewRatingInput.qml:166
203+msgid "Send"
204+msgstr "Mandar"
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:55
207+msgid "Pull to refresh…"
208+msgstr "Tirar pro renovar…"
209+
210+#: qml/Dash/PullToRefreshScopeStyle.qml:60
211+msgid "Release to refresh…"
212+msgstr "Relaxar pro renovar…"
213+
214+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
215+msgid "Enable location data"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:66
219+msgid "Manage"
220+msgstr "Tractar"
221+
222+#: qml/Dash/ScopesList.qml:112
223+msgid "Home"
224+msgstr "A domo"
225+
226+#: qml/Dash/ScopesList.qml:113
227+msgid "Also installed"
228+msgstr "Ja installate"
229+
230+#: qml/Greeter/CoverPage.qml:107
231+msgid "Unlock"
232+msgstr "Liberar"
233+
234+#: qml/Greeter/LoginList.qml:236
235+msgid "Retry"
236+msgstr ""
237+
238+#: qml/Greeter/LoginList.qml:237
239+msgid "Tap to unlock"
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
243+msgid "Sorry, incorrect passphrase."
244+msgstr "Displacente, incorrecte phrase-contrasigno."
245+
246+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
247+msgid "Sorry, incorrect passcode."
248+msgstr "Displacente, incorrecte codice-contrasigno."
249+
250+#: qml/Greeter/NarrowView.qml:55
251+msgid "This will be your last attempt."
252+msgstr "Isto essera tu ultime tentativa."
253+
254+#: qml/Greeter/NarrowView.qml:57
255+msgid ""
256+"If passphrase is entered incorrectly, your phone will conduct a factory "
257+"reset and all personal data will be deleted."
258+msgstr ""
259+"Si le phrase-contrasigno es inserite incorrectemente, tu telephono conducera "
260+"un prefixation al fabrica e omne le datos personal essera delite."
261+
262+#: qml/Greeter/NarrowView.qml:58
263+msgid ""
264+"If passcode is entered incorrectly, your phone will conduct a factory reset "
265+"and all personal data will be deleted."
266+msgstr ""
267+"Si le codice-contrasigno es inserite incorrectemente, tu telephono conducera "
268+"un prefixation a fabrica e omne le datos personal essera delite."
269+
270+#: qml/Greeter/NarrowView.qml:115
271+#, qt-format
272+msgid "Enter %1"
273+msgstr "Inserer %1"
274+
275+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
276+msgid "Enter passphrase"
277+msgstr "Inserer le phrase-contrasigno"
278+
279+#: qml/Greeter/NarrowView.qml:117
280+msgid "Enter passcode"
281+msgstr "Inserer le codice-contrasigno"
282+
283+#: qml/Greeter/NarrowView.qml:118
284+#, qt-format
285+msgid "Sorry, incorrect %1"
286+msgstr "Displacente, incorrecte %1"
287+
288+#: qml/Greeter/NarrowView.qml:119
289+msgid "Sorry, incorrect passphrase"
290+msgstr "Displacente, phrase-contrasigno incorrecte"
291+
292+#: qml/Greeter/NarrowView.qml:120
293+msgid "Please re-enter"
294+msgstr "Per favor re-inserer"
295+
296+#: qml/Greeter/NarrowView.qml:121
297+msgid "Sorry, incorrect passcode"
298+msgstr "Displacente, codice-contrasigno incorrecte"
299+
300+#: qml/Notifications/NotificationMenuItemFactory.qml:128
301+msgid "Show password"
302+msgstr "Monstra le contrasigno"
303+
304+#: qml/Panel/ActiveCallHint.qml:79
305+msgid "Tap to return to call..."
306+msgstr "Colpar pro retornar al appello..."
307+
308+#: qml/Panel/ActiveCallHint.qml:92
309+msgid "Conference"
310+msgstr "Conferentia"
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:639
313+msgid "Nothing is playing"
314+msgstr "Nil es jocante"
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:787
317+msgid "In queue…"
318+msgstr "In cauda…"
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:791
321+msgid "Downloading"
322+msgstr "Discargante"
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:793
325+msgid "Paused, tap to resume"
326+msgstr "In pausa, colpar pro reprender"
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:795
329+msgid "Canceled"
330+msgstr "Cancellate"
331+
332+#: qml/Panel/Indicators/MenuItemFactory.qml:797
333+msgid "Finished"
334+msgstr "Finite"
335+
336+#: qml/Panel/Indicators/MenuItemFactory.qml:799
337+msgid "Failed, tap to retry"
338+msgstr "Fallite, colpar pro re-provar"
339+
340+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
341+msgctxt "Label: Hint in message indicator line edit"
342+msgid "Reply"
343+msgstr ""
344+
345+#: qml/Panel/Indicators/ModemInfoItem.qml:105
346+msgid "Unlock SIM"
347+msgstr "Liberar le SIM"
348+
349+#: qml/Panel/Indicators/RoamingIndication.qml:27
350+msgid "Roaming"
351+msgstr "Roaming"
352+
353+#: qml/Tutorial/TutorialBottomFinish.qml:24
354+msgid "This action does different things for different apps"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottomFinish.qml:25
358+msgid "Tap here to finish."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialBottom.qml:28
362+msgid "Open special menus"
363+msgstr ""
364+
365+#: qml/Tutorial/TutorialBottom.qml:29
366+msgid "Swipe up from the bottom edge."
367+msgstr ""
368+
369+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
370+#: qml/Tutorial/TutorialRight.qml:144
371+msgid "Tap here to continue."
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialContent.qml:81
375+msgid "Click here to finish."
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeftFinish.qml:24
379+msgid "These are the shortcuts to favorite apps"
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialLeft.qml:26
383+msgid "Open the launcher"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialLeft.qml:27
387+msgid "Short swipe from the left edge."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialPage.qml:178
391+msgid "You almost got it!"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialPage.qml:195
395+msgid "Try again."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:31
399+msgid "To view open apps"
400+msgstr ""
401+
402+#: qml/Tutorial/TutorialRight.qml:32
403+msgid "Long swipe from the right edge."
404+msgstr ""
405+
406+#: qml/Tutorial/TutorialRight.qml:122
407+msgid "View all your running tasks."
408+msgstr ""
409+
410+#: qml/Wizard/Page.qml:89
411+msgctxt "Button: Go back one page in the Wizard"
412+msgid "Back"
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:27
416+msgid "Hi!"
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:44
420+msgid "Welcome to your Ubuntu phone."
421+msgstr ""
422+
423+#: qml/Wizard/Pages/10-welcome.qml:52
424+msgid "Let’s get started."
425+msgstr ""
426+
427+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
428+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
429+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
430+#: qml/Wizard/Pages/passwd-set.qml:90
431+msgid "Continue"
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:25
435+msgid "Add a SIM card and restart your device"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/20-sim.qml:59
439+msgid "Without it, you won’t be able to make calls or use text messaging."
440+msgstr ""
441+
442+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
443+msgid "Skip"
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:39
447+msgid "Lock security"
448+msgstr ""
449+
450+#: qml/Wizard/Pages/30-passwd-type.qml:74
451+msgid "Please select how you’d like to unlock your phone."
452+msgstr ""
453+
454+#: qml/Wizard/Pages/30-passwd-type.qml:97
455+msgctxt "Label: Type of security method"
456+msgid "Swipe"
457+msgstr ""
458+
459+#: qml/Wizard/Pages/30-passwd-type.qml:98
460+msgctxt "Label: Description of security method"
461+msgid "No security"
462+msgstr ""
463+
464+#: qml/Wizard/Pages/30-passwd-type.qml:100
465+msgctxt "Label: Type of security method"
466+msgid "Passcode"
467+msgstr ""
468+
469+#: qml/Wizard/Pages/30-passwd-type.qml:101
470+msgctxt "Label: Description of security method"
471+msgid "4 digits only"
472+msgstr ""
473+
474+#: qml/Wizard/Pages/30-passwd-type.qml:103
475+msgctxt "Label: Type of security method"
476+msgid "Passphrase"
477+msgstr ""
478+
479+#: qml/Wizard/Pages/30-passwd-type.qml:104
480+msgctxt "Label: Description of security method"
481+msgid "Numbers and letters"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:29
485+msgid "Connect to Wi‑Fi"
486+msgstr ""
487+
488+#: qml/Wizard/Pages/40-wifi.qml:168
489+msgid "Available networks…"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/40-wifi.qml:169
493+msgid "No available networks."
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:27
497+msgid "Location"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:62
501+msgid "Let the phone detect your location:"
502+msgstr ""
503+
504+#: qml/Wizard/Pages/50-location.qml:69
505+msgid "Using GPS only (less accurate)"
506+msgstr ""
507+
508+#: qml/Wizard/Pages/50-location.qml:86
509+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
510+msgstr ""
511+
512+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
513+#: qml/Wizard/Pages/50-location.qml:103
514+msgid ""
515+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
516+"conditions</a>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/50-location.qml:112
520+msgid "Not at all"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/50-location.qml:124
524+msgid "You can change your mind later in <b>System Settings</b>."
525+msgstr ""
526+
527+#: qml/Wizard/Pages/60-reporting.qml:24
528+msgid "Improving your experience"
529+msgstr ""
530+
531+#: qml/Wizard/Pages/60-reporting.qml:36
532+msgid ""
533+"Your phone is set up to automatically report errors to Canonical and its "
534+"partners, the makers of the operating system."
535+msgstr ""
536+
537+#: qml/Wizard/Pages/60-reporting.qml:43
538+msgid ""
539+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
540+"Privacy</b>"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:24
544+msgid "All done"
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:39
548+msgid "Nice work!"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/80-finished.qml:46
552+msgid "Your phone is now ready to use."
553+msgstr ""
554+
555+#: qml/Wizard/Pages/80-finished.qml:53
556+msgid "Finish"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/here-terms.qml:27
560+msgid "Terms & Conditions"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:49
564+msgid "Confirm passphrase"
565+msgstr ""
566+
567+#: qml/Wizard/Pages/passwd-confirm.qml:50
568+msgid "Confirm passcode"
569+msgstr ""
570+
571+#: qml/Wizard/Pages/passwd-confirm.qml:53
572+#: qml/Wizard/Pages/passwd-confirm.qml:54
573+msgid "Please try again."
574+msgstr ""
575+
576+#: qml/Wizard/Pages/passwd-set.qml:61
577+msgid "Choose your passcode"
578+msgstr ""
579+
580+#: qml/Wizard/Pages/passwd-set.qml:68
581+msgid "Passphrase must be 4 characters long"
582+msgstr ""
583+
584+#: qml/Wizard/StackButton.qml:39
585+#, qt-format
586+msgid "〈 %1"
587+msgstr ""
588+
589+#: qml/Wizard/StackButton.qml:42
590+#, qt-format
591+msgid "%1 〉"
592+msgstr ""
593+
594+#~ msgid "Log out"
595+#~ msgstr "Clauder le Session"
596+
597+#~ msgid "Reboot"
598+#~ msgstr "Reinitiar"
599+
600+#~ msgid "Shut down"
601+#~ msgstr "Stoppar"
602+
603+#~ msgid "Power"
604+#~ msgstr "Energia"
605+
606+#~ msgid "Restart"
607+#~ msgstr "Re-Initiar"
608+
609+#~ msgid "Power off"
610+#~ msgstr "Stoppar le energia"
611+
612+#~ msgid "Top edge"
613+#~ msgstr "Bordo superior"
614+
615+#~ msgid "Try swiping from the top edge to access the indicators"
616+#~ msgstr "Proba striar le bordo superior pro acceder le indicatores"
617+
618+#~ msgid "Close"
619+#~ msgstr "Clauder"
620+
621+#~ msgid "Left edge"
622+#~ msgstr "Bordo sinistre"
623+
624+#~ msgid "Try swiping from the right edge to unlock the phone"
625+#~ msgstr "Proba striar le bordo dextere pro liberar le telephono"
626+
627+#~ msgid "Skip intro"
628+#~ msgstr "Saltar le introduction"
629+
630+#~ msgid "Right edge"
631+#~ msgstr "Bordo dextere"
632+
633+#~ msgid "Swipe up again to close the settings screen"
634+#~ msgstr "Stria novemente pro clauder le pagina del preparationes"
635+
636+#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
637+#~ msgstr ""
638+#~ "Striar ab le sinistra pro revelar le lanceator pro un celere accesso al "
639+#~ "applicationes"
640+
641+#~ msgid "Well done"
642+#~ msgstr "Ben!"
643+
644+#~ msgid ""
645+#~ "You have now mastered the edge gestures and can start using the "
646+#~ "phone<br><br>Tap on the screen to start"
647+#~ msgstr ""
648+#~ "Tu ha ora maestrate le gestos del bordo e pote initiar usar le "
649+#~ "telephono<br><br>colpar sur le schermo pro initiar"
650+
651+#~ msgid "Search"
652+#~ msgstr "Cercar"
653+
654+#~ msgid "Store"
655+#~ msgstr "Salvar"
656+
657+#~ msgid "Settings"
658+#~ msgstr "Preparationes"
659+
660+#~ msgid "Confirm"
661+#~ msgstr "Confirmar"
662+
663+#~ msgid "Type or say a command"
664+#~ msgstr "Scriber o dicer un commando"
665+
666+#~ msgid "Loading. Please Wait..."
667+#~ msgstr "Cargante. Attender per favor..."
668+
669+#~ msgid "Speak Now..."
670+#~ msgstr "Parlar Ora..."
671+
672+#~ msgid "Speaking..."
673+#~ msgstr "Parlante..."
po/nl.po+4−4
@@ -8,14 +8,14 @@ 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-28 21:07+0000\n"
12−"Last-Translator: Sergi <philipsen.sergi@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-25 13:27+0000\n"
12+"Last-Translator: rob <linuxned@gmail.com>\n"
1313"Language-Team: Dutch <nl@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"
18+"X-Launchpad-Export-Date: 2015-04-26 05:47+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -377,7 +377,7 @@ msgstr "Tik hier om door te gaan."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Klik hier om te stoppen."
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
po/pa.po+3−3
@@ -8,14 +8,14 @@ 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-26 04:53+0000\n"
11+"PO-Revision-Date: 2015-04-24 04:29+0000\n"
1212"Last-Translator: Gursharnjit_Singh <ubuntuser13@gmail.com>\n"
1313"Language-Team: Punjabi <pa@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"
18+"X-Launchpad-Export-Date: 2015-04-25 05:38+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "ਜਾਰੀ ਰੱਖਣ ਲਈ ਇੱਥੇ ਛੂਹੋ।"
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "ਸਮਾਪਤੀ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।"
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
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_BR.po+9−9
@@ -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-06 19: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-23 06:48+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: "
@@ -122,8 +122,8 @@ msgid ""
122122"Are you sure you would like\n"
123123"to power off?"
124124msgstr ""
125−"Você tem certeza que gostaria \n"
126−"de desligar?"
125+"Você tem certeza que deseja\n"
126+"desligar?"
127127
128128#: qml/Components/Dialogs.qml:155
129129msgctxt "Button: Power off the system"
@@ -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/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/Rotation/RotationStates.qml+7
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 1.1
19+import Powerd 0.1
1920
2021// Why the state machine is done that way:
2122// We cannot use regular PropertyChanges{} inside the State elements as steps in the
@@ -121,6 +122,12 @@ StateGroup {
121122 // During start up, inital property values are still settling while we're still
122123 // to render the very first frame
123124 d.animationType = d.noAnimation;
125+ } else if (Powerd.status === Powerd.Off) {
126+ // There's no point in animating if the user can't see it (display is off).
127+ d.animationType = d.noAnimation;
128+ } else if (root.shell.showingGreeter) {
129+ // A rotating greeter looks weird.
130+ d.animationType = d.noAnimation;
124131 } else {
125132 if (!root.shell.mainApp) {
126133 // shouldn't happen but, anyway
qml/Shell.qml+2−1
@@ -70,6 +70,8 @@ Item {
7070 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
7171 && !greeter.animating
7272
73+ readonly property bool showingGreeter: greeter.shown
74+
7375 property bool startingUp: true
7476 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
7577
@@ -105,7 +107,6 @@ Item {
105107 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
106108 property url background: asImageTester.status == Image.Ready ? asImageTester.source
107109 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
108− readonly property real panelHeight: panel.panelHeight
109110
110111 // This is _only_ used to expose the property to autopilot tests
111112 readonly property string testShellMode: shellMode
qml/Stages/PhoneStage.qml+31−43
@@ -218,16 +218,13 @@ Rectangle {
218218 id: spreadView
219219 objectName: "spreadView"
220220 anchors.fill: parent
221− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
222− && draggedDelegateCount === 0
221+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
223222 contentWidth: spreadRow.width - shift
224223 contentX: -shift
225224
226225 // This indicates when the spreadView is active. That means, all the animations
227226 // are activated and tiles need to line up for the spread.
228− readonly property bool active: shiftedContentX > 0
229− || spreadDragArea.status === DirectionalDragArea.Recognized
230− || !root.focusFirstApp
227+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
231228
232229 // The flickable needs to fill the screen in order to get touch events all over.
233230 // However, we don't want to the user to be able to scroll back all the way. For
@@ -550,7 +547,7 @@ Rectangle {
550547 }
551548 }
552549
553− EdgeDragArea {
550+ DirectionalDragArea {
554551 id: spreadDragArea
555552 objectName: "spreadDragArea"
556553 direction: Direction.Leftwards
@@ -562,59 +559,50 @@ Rectangle {
562559 property var gesturePoints: new Array()
563560
564561 onTouchXChanged: {
565− if (!dragging) {
566− // Initial touch. Let's reset the spreadView to the starting position.
567− spreadView.phase = 0;
568− spreadView.contentX = -spreadView.shift;
569− }
570− if (dragging && status == DirectionalDragArea.Recognized) {
562+ if (dragging) {
571563 // Gesture recognized. Let's move the spreadView with the finger
572564 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
573565 dragX = -dragX + spreadDragArea.width - spreadView.shift;
574566 // Don't allow dragging further than the animation crossing with phase2's animation
575567 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
568+
576569 spreadView.contentX = Math.min(dragX, maxMovement);
570+ } else {
571+ // Initial touch. Let's reset the spreadView to the starting position.
572+ spreadView.phase = 0;
573+ spreadView.contentX = -spreadView.shift;
577574 }
578− gesturePoints.push(touchX);
579− }
580−
581− property int previousStatus: -1
582− property int currentStatus: DirectionalDragArea.WaitingForTouch
583575
584− onStatusChanged: {
585− previousStatus = currentStatus;
586− currentStatus = status;
576+ gesturePoints.push(touchX);
587577 }
588578
589579 onDraggingChanged: {
590580 if (dragging) {
591581 // A potential edge-drag gesture has started. Start recording it
592582 gesturePoints = [];
593− return;
594− }
583+ } else {
584+ // Ok. The user released. Find out if it was a one-way movement.
585+ var oneWayFlick = true;
586+ var smallestX = spreadDragArea.width;
587+ for (var i = 0; i < gesturePoints.length; i++) {
588+ if (gesturePoints[i] >= smallestX) {
589+ oneWayFlick = false;
590+ break;
591+ }
592+ smallestX = gesturePoints[i];
593+ }
594+ gesturePoints = [];
595595
596− // Ok. The user released. Find out if it was a one-way movement.
597− var oneWayFlick = true;
598− var smallestX = spreadDragArea.width;
599− for (var i = 0; i < gesturePoints.length; i++) {
600− if (gesturePoints[i] >= smallestX) {
601− oneWayFlick = false;
602− break;
596+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
597+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
598+ // If it was a short one-way movement, do the Alt+Tab switch
599+ // no matter if we didn't cross positionMarker1 yet.
600+ spreadView.snapTo(1);
601+ } else if (!dragging) {
602+ // otherwise snap to the closest snap position we can find
603+ // (might be back to start, to app 1 or to spread)
604+ spreadView.snap();
603605 }
604− smallestX = gesturePoints[i];
605− }
606− gesturePoints = [];
607−
608− if (previousStatus == DirectionalDragArea.Recognized &&
609− oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
610− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
611− // If it was a short one-way movement, do the Alt+Tab switch
612− // no matter if we didn't cross positionMarker1 yet.
613− spreadView.snapTo(1);
614− } else if (!dragging) {
615− // otherwise snap to the closest snap position we can find
616− // (might be back to start, to app 1 or to spread)
617− spreadView.snap();
618606 }
619607 }
620608 }
qml/Stages/SpreadDelegate.qml+15−7
@@ -131,6 +131,14 @@ FocusScope {
131131 }
132132 }
133133
134+ // Ensures the given angle is in the form (0,90,180,270)
135+ function normalizeAngle(angle) {
136+ while (angle < 0) {
137+ angle += 360;
138+ }
139+ return angle % 360;
140+ }
141+
134142 states: [
135143 // Sets the initial orientationAngle of the window, when it first slides into view
136144 // (with the splash screen likely being displayed). At that point we just try to
@@ -172,16 +180,16 @@ FocusScope {
172180 return Screen.angleBetween(root.nativeOrientation, chosenOrientation);
173181 }
174182
175− rotation: appWindowWithShadow.orientationAngle - root.shellOrientationAngle
183+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
176184 width: {
177− if (rotation == 0 || Math.abs(rotation) == 180) {
185+ if (rotation == 0 || rotation == 180) {
178186 return root.width;
179187 } else {
180188 return root.height;
181189 }
182190 }
183191 height: {
184− if (rotation == 0 || Math.abs(rotation) == 180)
192+ if (rotation == 0 || rotation == 180)
185193 return root.height;
186194 else
187195 return root.width;
@@ -201,16 +209,16 @@ FocusScope {
201209 PropertyChanges {
202210 target: appWindowWithShadow
203211 restoreEntryValues: false
204− rotation: appWindowWithShadow.orientationAngle - root.shellOrientationAngle
212+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
205213 width: {
206− if (rotation == 0 || Math.abs(rotation) == 180) {
214+ if (rotation == 0 || rotation == 180) {
207215 return root.width;
208216 } else {
209217 return root.height;
210218 }
211219 }
212220 height: {
213− if (rotation == 0 || Math.abs(rotation) == 180)
221+ if (rotation == 0 || rotation == 180)
214222 return root.height;
215223 else
216224 return root.width;
@@ -229,7 +237,7 @@ FocusScope {
229237 target: appWindowWithShadow
230238 width: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.width : root.height
231239 height: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.height : root.width
232− rotation: -root.shellOrientationAngle
240+ rotation: normalizeAngle(-root.shellOrientationAngle)
233241 }
234242 PropertyChanges {
235243 target: appWindow
qml/Stages/TabletStage.qml+19−24
@@ -245,8 +245,7 @@ Rectangle {
245245 id: spreadView
246246 objectName: "spreadView"
247247 anchors.fill: parent
248− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
249− && draggedDelegateCount === 0
248+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
250249 contentWidth: spreadRow.width - shift
251250 contentX: -shift
252251
@@ -727,8 +726,9 @@ Rectangle {
727726 }
728727 }
729728
730− EdgeDragArea {
729+ DirectionalDragArea {
731730 id: spreadDragArea
731+ objectName: "spreadDragArea"
732732 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
733733 width: root.dragAreaWidth
734734 direction: Direction.Leftwards
@@ -754,30 +754,25 @@ Rectangle {
754754 if (dragging) {
755755 // Gesture recognized. Start recording this gesture
756756 gesturePoints = [];
757− return;
758− }
759−
760− // Ok. The user released. Find out if it was a one-way movement.
761− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
762− gesturePoints = [];
763−
764− var nextAppInStackIsInMainStage =
765− spreadView.nextInStack !== -1 &&
766− ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage;
767−
768− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
769− // If it was a short one-way movement, do the Alt+Tab switch
770− // no matter if we didn't cross positionMarker1 yet.
771− spreadView.snapTo(spreadView.nextInStack);
772757 } else {
773− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
774− spreadView.snap();
775− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
758+ // Ok. The user released. Find out if it was a one-way movement.
759+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
760+ gesturePoints = [];
761+
762+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
763+ // If it was a short one-way movement, do the Alt+Tab switch
764+ // no matter if we didn't cross positionMarker1 yet.
776765 spreadView.snapTo(spreadView.nextInStack);
777766 } else {
778− // otherwise snap to the closest snap position we can find
779− // (might be back to start, to app 1 or to spread)
780− spreadView.snap();
767+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
768+ spreadView.snap();
769+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
770+ spreadView.snapTo(spreadView.nextInStack);
771+ } else {
772+ // otherwise snap to the closest snap position we can find
773+ // (might be back to start, to app 1 or to spread)
774+ spreadView.snap();
775+ }
781776 }
782777 }
783778 }
qml/Stages/TransformedSpreadDelegate.qml+2−16
@@ -312,27 +312,13 @@ SpreadDelegate {
312312 // non-fullscreen window when they're stacked on top of each other on the
313313 // far left of the spread.
314314 Translate {
315− y: !fullscreen && Math.abs(appWindowRotation) === 180
315+ y: !fullscreen && appWindowRotation === 180
316316 ? priv.topMarginProgress * maximizedAppTopMargin
317317 : 0
318318 },
319319 Scale {
320320 origin {
321− x: {
322− switch (appWindowRotation) {
323− case 0:
324− return 0;
325− break;
326− case 90:
327− return 0;
328− break;
329− case 270:
330− return spreadView.width;
331− break;
332− default: // 180
333− return 0;
334− }
335− }
321+ x: appWindowRotation === 270 ? spreadView.width : 0
336322 y: spreadView.height
337323 }
338324
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
@@ -42,6 +42,9 @@ sleep 1
4242cd -
4343
4444make -k qmltests
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
4548
4649cd -
4750
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+66−1
@@ -23,6 +23,7 @@ import threading
2323import fixtures
2424import logging
2525
26+import ubuntuuitoolkit
2627from autopilot import introspection
2728from autopilot.matchers import Eventually
2829from testtools.matchers import Equals
@@ -161,6 +162,70 @@ class LaunchUnityWithFakeSensors(fixtures.Fixture):
161162logger = logging.getLogger(__name__)
162163
163164
165+class RestartUnityWithTestability(fixtures.Fixture):
166+
167+ """Fixture to launch Unity8 with testability.
168+
169+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
170+
171+ """
172+
173+ unity_proxy = None
174+
175+ def __init__(self, binary_path, variables):
176+ """Initialize the fixture instance.
177+
178+ :param str binary_path: The path to the Dash app binary.
179+ :param cli_arguments: The arguments to pass when launching the
180+ :param variables: The variables to use when launching the app.
181+ :type variables: A dictionary.
182+
183+ """
184+ super().__init__()
185+ self.binary_path = binary_path
186+ self.variables = variables
187+
188+ def setUp(self):
189+ """Restart unity with testability when the fixture is used."""
190+ super().setUp()
191+ self.addCleanup(self.stop_unity)
192+ self.restart_unity()
193+
194+ def restart_unity(self):
195+ self.restart_unity_with_testability()
196+
197+ def restart_unity_with_testability(self):
198+ self._unlink_mir_socket()
199+
200+ binary_arg = 'BINARY={}'.format(self.binary_path)
201+ variable_args = [
202+ '{}={}'.format(key, value) for key, value in self.variables.items()
203+ ]
204+ all_args = [binary_arg] + variable_args
205+
206+ self.unity_proxy = process_helpers.restart_unity_with_testability(
207+ *all_args)
208+
209+ def _unlink_mir_socket(self):
210+ # FIXME: we shouldn't be doing this
211+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
212+ # /tmp/mir_socket as last resort
213+ try:
214+ os.unlink(
215+ os.getenv('MIR_SOCKET',
216+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
217+ "mir_socket")))
218+ except OSError:
219+ pass
220+ try:
221+ os.unlink("/tmp/mir_socket")
222+ except OSError:
223+ pass
224+
225+ def stop_unity(self):
226+ process_helpers.stop_job('unity8')
227+
228+
164229class LaunchDashApp(fixtures.Fixture):
165230
166231 """Fixture to launch the Dash app."""
@@ -194,7 +259,7 @@ class LaunchDashApp(fixtures.Fixture):
194259 pid = process_helpers.start_job('unity8-dash', *all_args)
195260 return introspection.get_proxy_object_for_existing_process(
196261 pid=pid,
197− emulator_base=emulators.UnityEmulatorBase,
262+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
198263 )
199264
200265 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/listviewwithpageheadertest.cpp+8−2
@@ -973,9 +973,15 @@ private Q_SLOTS:
973973 QCOMPARE(lvwph->contentY(), 1258.);
974974 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
975975
976− changeContentY(-1700);
977−
976+ changeContentY(-800);
977+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 5);
978+ changeContentY(-400);
979+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 8);
980+ changeContentY(-200);
981+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 10);
982+ changeContentY(-300);
978983 QTRY_COMPARE(lvwph->m_visibleItems.count(), 12);
984+
979985 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
980986 verifyItem(0, -308., 75., true);
981987 verifyItem(1, -233., 75., true);
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−1
@@ -19,6 +19,7 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
1919)
2020
2121add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
22+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
2223
2324macro(add_gesture_ui_test CLASSNAME)
2425 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
@@ -26,7 +27,8 @@ macro(add_gesture_ui_test CLASSNAME)
2627 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
2728
2829 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29− add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
30+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
31+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
3032endmacro(add_gesture_ui_test)
3133
3234macro(add_gesture_test CLASSNAME)
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+19−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,18 @@ qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
1217target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
1318
1419add_binary_qml_test(DragHandle ${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+########## tst_EdgeDragEvaluator
23+
24+include_directories(
25+ ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
26+ ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
27+ ${CMAKE_CURRENT_BINARY_DIR}
28+ )
29+
30+add_executable(EdgeDragEvaluatorTestExec tst_EdgeDragEvaluator.cpp)
31+qt5_use_modules(EdgeDragEvaluatorTestExec Test Core Qml Gui Quick)
32+target_link_libraries(EdgeDragEvaluatorTestExec UbuntuGesturesQml UbuntuGestures)
33+
34+add_binary_qml_test(EdgeDragEvaluator ${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
@@ -151,14 +151,14 @@ Item {
151151
152152 function test_enterSpread_data() {
153153 return [
154− {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
155− {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
156− {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
157− {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
158− {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
159− {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
160− {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
161− {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
154+ {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
155+ {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
156+ {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +5, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
157+ {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
158+ {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
159+ {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +5, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
160+ {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: 0, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
161+ {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +5, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
162162 ];
163163 }
164164
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_OrientedShell.qml+59−5
@@ -22,6 +22,7 @@ import Ubuntu.Components.ListItems 1.0 as ListItem
2222import Unity.Application 0.1
2323import Unity.Test 0.1
2424import LightDM 0.1 as LightDM
25+import Powerd 0.1
2526
2627import "../../qml"
2728
@@ -161,8 +162,16 @@ Rectangle {
161162 }
162163
163164 Rectangle {
164− id: controls
165+ width: controls.width
166+ anchors {
167+ top: parent.top
168+ bottom: parent.bottom
169+ right: parent.right
170+ }
165171 color: "darkgrey"
172+ }
173+ Flickable {
174+ id: controls
166175 width: units.gu(30)
167176 anchors {
168177 top: parent.top
@@ -170,7 +179,11 @@ Rectangle {
170179 right: parent.right
171180 }
172181
182+ boundsBehavior: Flickable.StopAtBounds
183+ contentHeight: controlsColumn.height
184+
173185 Column {
186+ id: controlsColumn
174187 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
175188 spacing: units.gu(1)
176189 Button {
@@ -281,6 +294,17 @@ Rectangle {
281294 anchors.verticalCenter: parent.verticalCenter
282295 }
283296 }
297+ Button {
298+ text: Powerd.status === Powerd.On ? "Display ON" : "Display OFF"
299+ activeFocusOnPress: false
300+ onClicked: {
301+ if (Powerd.status === Powerd.On) {
302+ Powerd.status = Powerd.Off;
303+ } else {
304+ Powerd.status = Powerd.On;
305+ }
306+ }
307+ }
284308 }
285309 }
286310
@@ -947,16 +971,31 @@ Rectangle {
947971 // If we swipe too much we will trigger the spread mode
948972 // and we don't want that.
949973 var spreadView = findChild(shell, "spreadView");
950− var swipeLength = (spreadView.positionMarker1 * spreadView.width) * 0.5;
974+ verify(spreadView);
951975
952976 verify(ApplicationManager.count >= 2);
953977 var previousApp = ApplicationManager.get(1);
954978
955979 var touchStartX = shell.width - 1;
956980 var touchStartY = shell.height / 2;
957− touchFlick(shell,
958− touchStartX, touchStartY,
959− touchStartX - swipeLength, touchStartY);
981+
982+ var condition;
983+ if (applicationArguments.deviceName === "phone") {
984+ condition = function() {
985+ return spreadView.shiftedContentX > units.gu(2) &&
986+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width;
987+ };
988+ } else {
989+ condition = function() {
990+ return spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker1
991+ && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2;
992+ };
993+ }
994+
995+ touchDragUntil(shell,
996+ touchStartX, touchStartY,
997+ -units.gu(1), 0,
998+ condition);
960999
9611000 // ensure the app switch animation has ended
9621001 tryCompare(spreadView, "shiftedContentX", 0);
@@ -1039,6 +1078,8 @@ Rectangle {
10391078
10401079 waitUntilShellIsInOrientation(root.physicalOrientation0);
10411080
1081+ waitForGreeterToStabilize();
1082+
10421083 swipeAwayGreeter();
10431084
10441085 var spreadRepeater = findChild(shell, "spreadRepeater");
@@ -1047,6 +1088,19 @@ Rectangle {
10471088 }
10481089 }
10491090
1091+ function waitForGreeterToStabilize() {
1092+ var greeter = findChild(shell, "greeter");
1093+ verify(greeter);
1094+
1095+ var loginList = findChild(greeter, "loginList");
1096+ // Only present in WideView
1097+ if (loginList) {
1098+ var userList = findChild(loginList, "userList");
1099+ verify(userList);
1100+ tryCompare(userList, "movingInternally", false);
1101+ }
1102+ }
1103+
10501104 // expectedAngle is in orientedShell's coordinate system
10511105 function checkAppSurfaceOrientation(app, expectedAngle) {
10521106 var surface = app.session.surface;
tests/qmltests/tst_Shell.qml+14−10
@@ -356,11 +356,11 @@ Rectangle {
356356 function test_phoneLeftEdgeDrag_data() {
357357 return [
358358 {tag: "without launcher",
359− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
359+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
360360 launcherHides: true, greeterShown: false},
361361
362362 {tag: "with launcher",
363− revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
363+ revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
364364 launcherHides: true, greeterShown: false},
365365
366366 {tag: "small swipe",
@@ -368,7 +368,7 @@ Rectangle {
368368 launcherHides: false, greeterShown: false},
369369
370370 {tag: "long swipe",
371− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
371+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
372372 launcherHides: true, greeterShown: false},
373373
374374 {tag: "small swipe with greeter",
@@ -376,11 +376,11 @@ Rectangle {
376376 launcherHides: false, greeterShown: true},
377377
378378 {tag: "long swipe with greeter",
379− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
379+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
380380 launcherHides: true, greeterShown: true},
381381
382382 {tag: "swipe over dash",
383− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash",
383+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
384384 launcherHides: false, greeterShown: false},
385385 ];
386386 }
@@ -585,7 +585,7 @@ Rectangle {
585585
586586 dashCommunicatorSpy.clear();
587587 // Minimize the application we just launched
588− swipeFromLeftEdge(units.gu(27));
588+ swipeFromLeftEdge(shell.width * 0.75);
589589
590590 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
591591
@@ -698,7 +698,8 @@ Rectangle {
698698 }
699699
700700 function dragLauncherIntoView() {
701− var launcherPanel = findChild(shell, "launcherPanel");
701+ var launcher = findChild(shell, "launcher");
702+ var launcherPanel = findChild(launcher, "launcherPanel");
702703 verify(launcherPanel.x = - launcherPanel.width);
703704
704705 var touchStartX = 2;
@@ -706,6 +707,7 @@ Rectangle {
706707 touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
707708
708709 tryCompare(launcherPanel, "x", 0);
710+ tryCompare(launcher, "state", "visible");
709711 }
710712
711713 function tapOnAppIconInLauncher() {
@@ -742,7 +744,9 @@ Rectangle {
742744 function swipeFromLeftEdge(swipeLength) {
743745 var touchStartX = 2;
744746 var touchStartY = shell.height / 2;
745− touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
747+ touchFlick(shell,
748+ touchStartX , touchStartY,
749+ touchStartX + swipeLength, touchStartY);
746750 }
747751
748752 function itemIsOnScreen(item) {
@@ -778,7 +782,7 @@ Rectangle {
778782 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
779783
780784 // Minimize the application we just launched
781− swipeFromLeftEdge(units.gu(26) + 1);
785+ swipeFromLeftEdge(shell.width * 0.75);
782786
783787 waitUntilDashIsFocused();
784788
@@ -860,7 +864,7 @@ Rectangle {
860864
861865 compare(panel.fullscreenMode, true);
862866
863− touchFlick(shell, units.gu(2), touchStartY, units.gu(10), touchStartY, false, false);
867+ touchFlick(shell, units.gu(2), touchStartY, shell.width * 0.5, touchStartY, false, false);
864868
865869 tryCompare(panel, "fullscreenMode", false);
866870
tests/qmltests/tst_ShellWithPin.qml+2−2
@@ -608,14 +608,14 @@ Item {
608608 tryCompare(coverPage, "showProgress", 1);
609609
610610 // Swipe cover page away
611− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
611+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
612612 tryCompare(launcher, "x", -launcher.width);
613613 tryCompare(coverPage, "showProgress", 0);
614614 compare(lockscreen.shown, true);
615615 compare(ApplicationManager.focusedApplicationId, "gallery-app");
616616
617617 // Now attempt a swipe on lockscreen
618− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
618+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
619619 tryCompare(launcher, "x", 0);
620620 compare(lockscreen.shown, true);
621621 compare(ApplicationManager.focusedApplicationId, "gallery-app");
tests/utils/modules/Unity/Test/UnityTestCase.qml+45−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 */
@@ -308,6 +308,48 @@ TestCase {
308308 }
309309 }
310310
311+ // perform a drag in the given direction until the given condition is true
312+ // The condition is a function to be evaluated after every step
313+ function touchDragUntil(item, startX, startY, stepX, stepY, condition) {
314+
315+ var root = fetchRootItem(item);
316+ var pos = item.mapToItem(root, startX, startY);
317+
318+ // convert step to scene coords
319+ {
320+ var stepStart = item.mapToItem(root, 0, 0);
321+ var stepEnd = item.mapToItem(root, stepX, stepY);
322+ }
323+ stepX = stepEnd.x - stepStart.x;
324+ stepY = stepEnd.y - stepStart.y;
325+
326+ var event = touchEvent(item)
327+ event.press(0 /* touchId */, pos.x, pos.y)
328+ event.commit()
329+
330+ // we have to stop at some point
331+ var maxSteps = 100;
332+ var stepsDone = 0;
333+
334+ while (!condition() && stepsDone < maxSteps) {
335+ wait(25);
336+ fakeDateTime.currentTimeMs += 25;
337+
338+ pos.x += stepX;
339+ pos.y += stepY;
340+
341+ event = touchEvent(item);
342+ event.move(0 /* touchId */, pos.x, pos.y);
343+ event.commit();
344+
345+ stepsDone += 1;
346+ }
347+
348+ event = touchEvent(item)
349+ event.release(0 /* touchId */, pos.x, pos.y)
350+ event.commit()
351+ }
352+
311353 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
312354 // Make sure the item is rendered
313355 waitForRendering(item);
@@ -403,12 +445,8 @@ TestCase {
403445 function removeTimeConstraintsFromDirectionalDragAreas(item) {
404446
405447 // 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;
448+ if (item.removeTimeConstraints != undefined) {
449+ item.removeTimeConstraints();
412450 } else {
413451 for (var i in item.children) {
414452 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);